packages feed

marmalade-upload 0.6 → 0.7

raw patch · 10 files changed

+61/−406 lines, 10 filesdep −deepseqdep −processdep −unixPVP ok

version bump matches the API change (PVP)

Dependencies removed: deepseq, process, unix

API changes (from Hackage documentation)

- Web.Marmalade: verifyPackage :: String -> Marmalade ()
- Web.Marmalade.Magic: data MagicException
- Web.Marmalade.Magic: guessMimeType :: FilePath -> IO String
- Web.Marmalade.Magic: instance Exception MagicException
- Web.Marmalade.Magic: instance Show MagicException
- Web.Marmalade.Magic: instance Typeable MagicException

Files

CHANGES.md view
@@ -1,3 +1,9 @@+0.7 (Apr 28, 2014)+==================++- Improve the help output and the semantics of `-V`/`--version`+- Remove mimetype verification+ 0.6 (Apr 21, 2014) ================== 
main.hs view
@@ -30,10 +30,10 @@ import Control.Monad.IO.Class (liftIO) import Data.Version (showVersion) import Options.Applicative (Parser,execParser,-                            info,fullDesc,progDesc,-                            helper,argument,flag',long,short,str,metavar,help,-                            (<|>),(<$>),(<>),(<*>))-import System.Exit (ExitCode(ExitFailure),exitWith,exitSuccess)+                            info,fullDesc,progDesc,infoOption,+                            helper,argument,long,short,str,metavar,help,+                            (<$>),(<>),(<*>))+import System.Exit (ExitCode(ExitFailure),exitWith) import System.IO (hPutStrLn,stderr) import Text.Printf (printf) @@ -112,40 +112,33 @@ exitException :: SomeException -> IO () exitException = exitFailure.show -data UploadArguments =  UploadArguments { argUsername :: String-                                        , argPackageFile :: String }--data Arguments = ShowVersion |-                 UploadPackage UploadArguments+data Arguments =  Arguments { argUsername :: String+                            , argPackageFile :: String } -arguments :: Parser Arguments-arguments = flag' ShowVersion (long "version" <>-                               short 'V' <>-                               help "Show version number and exit") <|>-            (UploadPackage <$> uploadArguments)+parser :: Parser Arguments+parser =+  versionInfo <*> arguments   where-    uploadArguments = UploadArguments <$>-                      argument str (metavar "USERNAME" <> help "Marmalade username") <*>-                      argument str (metavar "PACKAGE" <> help "Package file")+    versionInfo = infoOption versionMessage (long "version" <>+                                             short 'V' <>+                                             help "Show version number and exit")+    arguments = Arguments <$>+                argument str (metavar "USERNAME" <> help "Marmalade username") <*>+                argument str (metavar "PACKAGE" <> help "Package file")+    versionMessage = unlines [(appName ++ " " ++ appVersion)+                             ,"Copyright (C) 2014 Sebastian Wiesner."+                             ,"You may redistribute marmalade-upload"+                             ,"under the terms of the MIT/X11 license."]  main :: IO () main = do-  args <- execParser (info (helper <*> arguments)+  args <- execParser (info (helper <*> parser)                       (fullDesc <> progDesc "Upload a package to Marmalade"))-  processArguments args-  where-    processArguments ShowVersion = putVersion >> exitSuccess-    processArguments (UploadPackage uploadArgs) = do-      (shallSaveToken, auth) <- getAuth (argUsername uploadArgs)-      handle exitException $ runMarmalade appUserAgent auth $ do-        (Username username, Token token) <- login-        -- Save the token now-        when shallSaveToken $-          liftIO (K.setPassword (K.Service appService) (K.Username username) (K.Password token))-        upload <- uploadPackage (argPackageFile uploadArgs)-        liftIO (putStrLn (uploadMessage upload))-    putVersion = do-      putStrLn (appName ++ " " ++ appVersion)-      putStrLn "Copyright (C) 2014 Sebastian Wiesner."-      putStrLn "You may redistribute marmalade-upload"-      putStrLn "under the terms of the MIT/X11 license."+  (shallSaveToken, auth) <- getAuth (argUsername args)+  handle exitException $ runMarmalade appUserAgent auth $ do+    (Username username, Token token) <- login+    -- Save the token now+    when shallSaveToken $+      liftIO (K.setPassword (K.Service appService) (K.Username username) (K.Password token))+    upload <- uploadPackage (argPackageFile args)+    liftIO (putStrLn (uploadMessage upload))
marmalade-upload.cabal view
@@ -1,5 +1,5 @@ name:                marmalade-upload-version:             0.6+version:             0.7 synopsis:            Upload packages to Marmalade description:   Upload Emacs packages to the <http://marmalade-repo.org/ Marmalade> ELPA@@ -8,9 +8,7 @@ license:             MIT license-file:        LICENSE extra-source-files:  README.md,-                     CHANGES.md,-                     test/resources/foo.el,-                     test/resources/foo.tar+                     CHANGES.md author:              Sebastian Wiesner maintainer:          lunaryorn@gmail.com copyright:           (C) 2014 Sebastian Wiesner@@ -27,7 +25,7 @@ source-repository this   type:              git   location:          https://github.com/lunaryorn/marmalade-upload.git-  tag:               0.6+  tag:               0.7  flag LibMagic   description:       Use libmagic to determine the mimetypes of packages@@ -36,7 +34,6 @@ library   hs-source-dirs:      src/   exposed-modules:     Web.Marmalade-                       Web.Marmalade.Magic   ghc-options:         -Wall   build-depends:       base >=4.6 && <4.8,                        mtl >=2.1 && <2.2,@@ -50,16 +47,6 @@                        http-client >=0.3 && <0.4   default-language:    Haskell2010 -  if flag(LibMagic)-     build-tools:      hsc2hs-     build-depends:    unix >=2.6 && <2.8-     extra-libraries:  magic-     cpp-options:      -DWITH_LIBMAGIC-     other-modules:    Web.Marmalade.Magic.Native-  else-     build-depends:    process >=1.1 && <1.3,-                       deepseq >=1.3 && <1.4- executable marmalade-upload   main-is:             main.hs   ghc-options:         -Wall@@ -69,17 +56,6 @@                        keyring >=0.1 && <0.2,                        marmalade-upload   default-language:    Haskell2010--test-suite magic-  type:                exitcode-stdio-1.0-  main-is:             magic-tests.hs-  hs-source-dirs:      test/-  build-depends:       base >=4.6 && <4.8,-                       tasty >=0.8 && <0.9,-                       tasty-hunit >= 0.8 && <0.9,-                       marmalade-upload-  default-language:    Haskell2010-  ghc-options:         -Wall  test-suite marmalade   type:                exitcode-stdio-1.0
src/Web/Marmalade.hs view
@@ -36,12 +36,10 @@          -- * Generic types        , Message(..)          -- * Package uploads-       , verifyPackage,uploadPackage,Upload(..)+       , Upload(..), uploadPackage        )        where -import qualified Web.Marmalade.Magic as Magic- import qualified Data.Aeson as JSON import qualified Data.ByteString.UTF8 as UTF8 import qualified Network as N@@ -49,7 +47,7 @@  import Control.Applicative (Applicative,(<$>)) import Control.Exception (Exception)-import Control.Monad (liftM,mzero,unless)+import Control.Monad (liftM,mzero) import Control.Monad.Catch (MonadThrow,MonadCatch,throwM) import Control.Monad.IO.Class (MonadIO,liftIO) import Control.Monad.State (StateT,MonadState,evalStateT,get,gets,put)@@ -233,29 +231,12 @@         status = C.responseStatus response         message = fmap messageContents (JSON.decode' body) --- |Permitted package mimetypes.-packageMimeTypes :: [String]-packageMimeTypes = ["application/x-tar", "text/x-lisp"]---- |@'verifyPackage' package@ checks whether @package@ is a valid package--- object.------ Throw an error if @package@ does not exist, or is not a valid package.-verifyPackage :: String -> Marmalade ()-verifyPackage packageFile = do-  -- Force early failure if the package doesn't exist-  mimeType <- liftIO (Magic.guessMimeType packageFile)-  unless (mimeType `elem` packageMimeTypes)-    (throwM (MarmaladeInvalidPackage packageFile-                     (printf "invalid mimetype %s" mimeType)))- -- |@'uploadPackage' package@ uploads a @package@ file to Marmalade. ----- Return the result of the upload, or throw an error if @package@ is not a--- valid package, or if Marmalade refused to accept the upload.+-- Return the result of the upload, or throw an error if Marmalade refused to+-- accept the upload. uploadPackage :: FilePath -> Marmalade Upload uploadPackage packageFile = do-  verifyPackage packageFile   (Username username, Token token) <- login   manager <- gets marmaladeManager   request <- makeRequest "/v1/packages" >>=
− src/Web/Marmalade/Magic.hs
@@ -1,121 +0,0 @@--- Copyright (c) 2014 Sebastian Wiesner <lunaryorn@gmail.com>---- Permission is hereby granted, free of charge, to any person obtaining a copy--- of this software and associated documentation files (the "Software"), to deal--- in the Software without restriction, including without limitation the rights--- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell--- copies of the Software, and to permit persons to whom the Software is--- furnished to do so, subject to the following conditions:---- The above copyright notice and this permission notice shall be included in--- all copies or substantial portions of the Software.---- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR--- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,--- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE--- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER--- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,--- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN--- THE SOFTWARE.--{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveDataTypeable #-}--module Web.Marmalade.Magic-       ( MagicException-       , guessMimeType)-       where--import Control.Exception (Exception,throwIO,bracket)-import Data.Typeable (Typeable)--#ifdef WITH_LIBMAGIC-import qualified Web.Marmalade.Magic.Native as N-import Control.Monad (when)-import Data.Bits ((.|.))-import Foreign.C (CInt,peekCString)-import Foreign.ForeignPtr (ForeignPtr,newForeignPtr,withForeignPtr)-import Foreign.Ptr (nullPtr)-import System.IO.Error (catchIOError)-import System.Posix.IO (OpenMode(ReadOnly),openFd,closeFd,defaultFileFlags)-import System.Posix.Types (Fd(Fd))-#else-import Control.DeepSeq (rnf)-import Control.Exception (finally,evaluate)-import System.Exit(ExitCode(..))-import System.IO (IOMode(ReadMode),Handle,withBinaryFile,hGetContents,hClose)-import System.Process (CreateProcess(std_out,std_err,std_in),-                       StdStream(CreatePipe,UseHandle),-                       proc,createProcess,waitForProcess)-#endif--newtype MagicException = MagicException String-                       deriving Typeable--instance Show MagicException where-  show (MagicException message) = message--instance Exception MagicException--#ifdef WITH_LIBMAGIC--type Magic = ForeignPtr ()--magicOpen :: CInt -> IO Magic-magicOpen flags = do-  raw <- N.magic_open flags-  when (raw == nullPtr) (throwIO (MagicException "Failed to allocate cookie"))-  newForeignPtr N.magic_close raw--throwCurrentMagicError :: Magic -> IO a-throwCurrentMagicError magic = do-  message <- withForeignPtr magic N.magic_error-  if message == nullPtr-    then throwIO (MagicException "Unknown error")-    else peekCString message >>= throwIO.MagicException--magicDescription :: Magic -> Fd -> IO String-magicDescription magic (Fd fd) = do-  buffer <- withForeignPtr magic $ \ptr -> N.magic_descriptor ptr fd-  when (buffer == nullPtr) (throwCurrentMagicError magic)-  peekCString buffer--withBinaryFileFd :: FilePath -> OpenMode -> (Fd -> IO a) -> IO a-withBinaryFileFd fileName mode =-  bracket (openFd fileName mode Nothing defaultFileFlags) closeFdSafe-  where-    closeFdSafe fd = catchIOError (closeFd fd) (const $ return ())--guessMimeType :: FilePath -> IO String-guessMimeType fileName = do-  cookie <- magicOpen (N.magicSymlink .|. N.magicMimeType .|. N.magicError)-  withForeignPtr cookie ((flip N.magic_load) nullPtr)-  withBinaryFileFd fileName ReadOnly (magicDescription cookie)--#else--hGuessMimeType :: Handle -> IO String-hGuessMimeType handle =-  bracket (createProcess process) closeHandles $ \(_, Just oh, Just eh, proch) -> do-    stdout <- hGetContents oh-    stderr <- hGetContents eh-    -- Force reading of the process handles-    evaluate $ rnf stdout-    evaluate $ rnf stderr-    exitStatus <- waitForProcess proch-    case exitStatus of-      ExitSuccess -> return (head (lines stdout))-      ExitFailure _ -> throwIO (MagicException (stdout ++ stderr))-  where closeHandles (_, Just outh, Just errh, _) =-          finally (hClose outh) (hClose errh)-        closeHandles _ = return ()-        process =-          (proc "file" ["--brief", "--mime-type", "-"]) {-            std_out = CreatePipe,-            std_err = CreatePipe,-            std_in = UseHandle handle }--guessMimeType :: FilePath -> IO String-guessMimeType fileName = withBinaryFile fileName ReadMode hGuessMimeType--#endif
− src/Web/Marmalade/Magic/Native.hsc
@@ -1,50 +0,0 @@--- Copyright (c) 2014 Sebastian Wiesner <lunaryorn@gmail.com>---- Permission is hereby granted, free of charge, to any person obtaining a copy--- of this software and associated documentation files (the "Software"), to deal--- in the Software without restriction, including without limitation the rights--- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell--- copies of the Software, and to permit persons to whom the Software is--- furnished to do so, subject to the following conditions:---- The above copyright notice and this permission notice shall be included in--- all copies or substantial portions of the Software.---- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR--- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,--- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE--- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER--- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,--- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN--- THE SOFTWARE.--{-# LANGUAGE ForeignFunctionInterface #-}--module Web.Marmalade.Magic.Native where--#include <magic.h>--import Foreign.C (CInt(..),CString)-import Foreign.Ptr (Ptr,FunPtr)--#{enum CInt, ,-  magicSymlink = MAGIC_SYMLINK,-  magicMimeType = MAGIC_MIME_TYPE,-  magicError = MAGIC_ERROR}--type Magic = Ptr ()--foreign import ccall unsafe "magic.h magic_open"-  magic_open :: CInt -> IO Magic--foreign import ccall unsafe "magic.h magic_load"-  magic_load :: Magic -> CString -> IO ()--foreign import ccall unsafe "magic.h &magic_close"-  magic_close :: FunPtr (Magic -> IO ())--foreign import ccall unsafe "magic.h magic_error"-  magic_error :: Magic -> IO CString--foreign import ccall unsafe "magic.h magic_descriptor"-  magic_descriptor :: Magic -> CInt -> IO CString
− test/magic-tests.hs
@@ -1,60 +0,0 @@--- Copyright (c) 2014 Sebastian Wiesner <lunaryorn@gmail.com>---- Permission is hereby granted, free of charge, to any person obtaining a copy--- of this software and associated documentation files (the "Software"), to deal--- in the Software without restriction, including without limitation the rights--- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell--- copies of the Software, and to permit persons to whom the Software is--- furnished to do so, subject to the following conditions:---- The above copyright notice and this permission notice shall be included in--- all copies or substantial portions of the Software.---- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR--- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,--- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE--- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER--- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,--- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN--- THE SOFTWARE.--import Web.Marmalade.Magic (guessMimeType)--import Control.Exception (handleJust)-import Control.Monad (guard)-import System.IO.Error (isDoesNotExistError)-import Test.Tasty-import Test.Tasty.HUnit--mimeTypeOfEmacsLispFile :: TestTree-mimeTypeOfEmacsLispFile = testCase "Emacs Lisp file" $ do-  mimeType <- guessMimeType "test/resources/foo.el"-  mimeType @?= "text/x-lisp"--mimeTypeOfTarFile :: TestTree-mimeTypeOfTarFile = testCase "TAR file" $ do-  mimeType <- guessMimeType "test/resources/foo.tar"-  mimeType @?= "application/x-tar"--mimeTypeOfTextFile :: TestTree-mimeTypeOfTextFile = testCase "Text file" $ do-  mimeType <- guessMimeType "README.md"-  mimeType @?= "text/plain"--fileDoesNotExist :: TestTree-fileDoesNotExist = testCase "File does not exist" $-  handleJust (guard.isDoesNotExistError) (const $ return ()) $ do-    _ <- guessMimeType "thisFileDoesNotExist"-    assertFailure "Expected IO error not thrown"--tests :: TestTree-tests = testGroup "Mimetype guessing"-        [-          mimeTypeOfEmacsLispFile-        , mimeTypeOfTarFile-        , mimeTypeOfTextFile-        , fileDoesNotExist-        ]--main :: IO ()-main = defaultMain tests
test/marmalade-tests.hs view
@@ -24,10 +24,9 @@ import Web.Marmalade  import qualified Data.Aeson as JSON-import Control.Exception (handleJust)-import Control.Monad (guard)+import Control.Exception (handle,handleJust) import Data.Version (showVersion)-import System.IO.Error (isDoesNotExistError)+import System.IO.Error (isDoesNotExistError,ioeGetFileName)  import Test.Tasty import Test.Tasty.HUnit@@ -36,37 +35,11 @@ testUserAgent = "marmalade-upload-tests/" ++ showVersion version  testAuth :: Auth-testAuth = BasicAuth (Username "marmalade-upload-test-user") (return "invalid password")+testAuth = TokenAuth (Username "marmalade-upload-test-user") (Token "test-token")  runMarmaladeTest :: Marmalade a -> IO a runMarmaladeTest = runMarmalade testUserAgent testAuth -verifyEmacsLispPackage :: TestTree-verifyEmacsLispPackage = testCase "Emacs Lisp package" $ runMarmaladeTest $-                         verifyPackage "test/resources/foo.el"--verifyTarPackage :: TestTree-verifyTarPackage = testCase "Tar package" $ runMarmaladeTest $-                   verifyPackage "test/resources/foo.tar"--verifyNonExistingPackage :: TestTree-verifyNonExistingPackage =-  testCase "Package file does not exist" $-  handleJust (guard.isDoesNotExistError) (const $ return ()) $ do-    runMarmaladeTest (verifyPackage "thisFileDoesNotExist")-    assertFailure "Expected IO error not thrown"--verifyInvalidPackage :: TestTree-verifyInvalidPackage =-  testCase "Invalid package file" $-  handleJust invalidPackageMessage assertInvalidPackageMessage $ do-   runMarmaladeTest (verifyPackage "README.md")-   assertFailure "Expected MarmaladeError not thrown"-  where-    invalidPackageMessage (MarmaladeInvalidPackage _ msg) = Just msg-    invalidPackageMessage _ = Nothing-    assertInvalidPackageMessage msg = msg @?= "invalid mimetype text/plain"- decodeToken :: TestTree decodeToken = testCase "Decode Token JSON" $               case JSON.decode' "{\"token\": \"fooBar\"}" of@@ -87,36 +60,43 @@  loginWithToken :: TestTree loginWithToken = testCase "Login with token" $ do-  (Username username, Token token) <- getLogin+  (Username username, Token token) <- runMarmaladeTest login   username @?= "marmalade-upload-test-user"   token @?= "test-token"-  where-    auth = TokenAuth (Username "marmalade-upload-test-user") (Token "test-token")-    getLogin = runMarmalade testUserAgent auth login  invalidUsernameAndPassword :: TestTree invalidUsernameAndPassword =   testCase "Invalid username and password" $   handleJust badRequestWithMessage assertMessage $ do-    _ <- runMarmaladeTest login+    _ <- runMarmalade testUserAgent auth login     assertFailure "Expected MarmaladeBadRequest not thrown"   where+    auth = BasicAuth (Username "marmalade-upload-test-user") (return "test-password")     badRequestWithMessage (MarmaladeBadRequest msg) = msg     badRequestWithMessage _ = Nothing     assertMessage msg = msg @?= "Username or password invalid" +uploadNonExistingPackage :: TestTree+uploadNonExistingPackage =+  testCase "Package file does not exist" $+  handle assertDoesNotExistError $ do+    _ <- runMarmaladeTest (uploadPackage fileName)+    assertFailure "Expected IOError not thrown"+  where+    fileName = "/this-file-does-not-exist"+    assertDoesNotExistError err = do+      isDoesNotExistError err @? ("Unexpected error type: " ++ show err)+      ioeGetFileName err @?= Just fileName+ tests :: TestTree tests = testGroup "Marmalade API"         [-          testGroup "verifyPackage" [ verifyEmacsLispPackage-                                    , verifyTarPackage-                                    , verifyNonExistingPackage-                                    , verifyInvalidPackage ]-        , testGroup "JSON decoding" [ decodeToken+          testGroup "JSON decoding" [ decodeToken                                     , decodeMessage                                     , decodeUpload ]         , testGroup "login" [ loginWithToken                             , invalidUsernameAndPassword ]+        , testGroup "upload" [ uploadNonExistingPackage ]         ]  main :: IO ()
− test/resources/foo.el
@@ -1,50 +0,0 @@-;;; foo.el --- marmalade-upload test dummy           -*- lexical-binding: t; -*---;; Copyright (C) 2014  Sebastian Wiesner--;; Author: Sebastian Wiesner <lunaryorn@gmail.com>-;; Package-Version: 0.1--;; This file is not part of GNU Emacs.--;; Permission is hereby granted, free of charge, to any person obtaining a copy-;; of this software and associated documentation files (the "Software"), to deal-;; in the Software without restriction, including without limitation the rights-;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell-;; copies of the Software, and to permit persons to whom the Software is-;; furnished to do so, subject to the following conditions:--;; The above copyright notice and this permission notice shall be included in-;; all copies or substantial portions of the Software.--;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR-;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,-;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE-;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER-;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,-;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE-;; SOFTWARE.--;; This program is free software; you can redistribute it and/or modify-;; it under the terms of the GNU General Public License as published by-;; the Free Software Foundation, either version 3 of the License, or-;; (at your option) any later version.--;; This program is distributed in the hope that it will be useful,-;; but WITHOUT ANY WARRANTY; without even the implied warranty of-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-;; GNU General Public License for more details.--;; You should have received a copy of the GNU General Public License-;; along with this program.  If not, see <http://www.gnu.org/licenses/>.--;;; Commentary:--;;; Code:--(defun foo ()-  (message "FOO"))--(provide 'foo)--;;; foo.el ends here
− test/resources/foo.tar

binary file changed (4096 → absent bytes)