diff --git a/CHANGELOG.md b/CHANGELOG.md index c54cbe7f..ce2a33f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how * ci(build.yml): Try forked pkg to fix `arm64` build 2 (#292 and #294) * feat: Set error status when help is printed (#307) * feat: Add exit code specification (c49f53caa1f6ac94a9c8c884d70d0860f55728c1) +* feat(install): Add commands `install-file` and `install-vc` (#317) +* feat(install): Handle `--force` flag to overwrite installed packages (#317) ## 0.10.x > Released Jun 13, 2024 diff --git a/cmds/core/install-file.js b/cmds/core/install-file.js new file mode 100644 index 00000000..a33164bc --- /dev/null +++ b/cmds/core/install-file.js @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2025 the Eask authors. + * + * 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, 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 . + */ + +"use strict"; + +exports.command = ['install-file [files..]']; +exports.desc = 'Install packages from files, .tar files, or directories'; +exports.builder = yargs => yargs + .positional( + '[files..]', { + description: 'files to install as packages', + type: 'array', + }); + +exports.handler = async (argv) => { + await UTIL.e_call(argv, 'core/install-file', argv.files); +}; diff --git a/cmds/core/install-vc.js b/cmds/core/install-vc.js new file mode 100644 index 00000000..8e7f312b --- /dev/null +++ b/cmds/core/install-vc.js @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2025 the Eask authors. + * + * 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, 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 . + */ + +"use strict"; + +exports.command = ['install-vc [specs..]']; +exports.desc = 'Install packages directly from the version control'; +exports.builder = yargs => yargs + .positional( + '[specs..]', { + description: 'vc specification to install as packages', + type: 'array', + }); + +exports.handler = async (argv) => { + await UTIL.e_call(argv, 'core/install-vc', argv.specs); +}; diff --git a/docs/content/DSL/_index.en.md b/docs/content/DSL/_index.en.md index 8a6c4806..afd72e6a 100644 --- a/docs/content/DSL/_index.en.md +++ b/docs/content/DSL/_index.en.md @@ -56,7 +56,7 @@ Declare package's author. ## ๐Ÿ” **package-file** (`file` `version` `description`) -Define this package and its runtime dependencies from the package headers +Define this package and its runtime dependencies from the package headers of a file (used only for package development). ```elisp @@ -65,7 +65,7 @@ of a file (used only for package development). ## ๐Ÿ” **package-descriptor** (`pkg-file`) -Declare all package metadata directly by specifying a package descriptor +Declare all package metadata directly by specifying a package descriptor contained in file with name given by file. ```elisp @@ -149,14 +149,30 @@ Specify dependencies that are listed in **archives**: (depends-on "company") ``` +Specify dependencies in **file** format: + +```elisp +(depends-on "auto-rename-tag" :file "/path/to/auto-rename-tag") + +(depends-on "lsp-ui" :file "/path/to/lsp-ui") +``` + +Specify dependencies in **vc** format: + +```elisp +(depends-on "auto-rename-tag" :vc "jcs-elpa/auto-rename-tag") + +(depends-on "lsp-ui" :vc "emacs-lsp/lsp-ui") +``` + Specify dependencies in **recipe** format: ```elisp -(depends-on "auto-rename-tag" - :repo "jcs-elpa/auto-rename-tag" +(depends-on "auto-rename-tag" + :repo "jcs-elpa/auto-rename-tag" :fetcher 'github) -(depends-on "lsp-ui" +(depends-on "lsp-ui" :repo "emacs-lsp/lsp-ui" :fetcher 'github :files '(:defaults "lsp-ui-doc.html" "resources")) diff --git a/docs/content/DSL/_index.zh-tw.md b/docs/content/DSL/_index.zh-tw.md index 401924e4..4d6adcb2 100644 --- a/docs/content/DSL/_index.zh-tw.md +++ b/docs/content/DSL/_index.zh-tw.md @@ -145,14 +145,30 @@ weight: 200 (depends-on "company") ``` +ไปฅ **file** ๆ ผๅผๆŒ‡ๅฎšไพ่ณด้ …๏ผš + +```elisp +(depends-on "auto-rename-tag" :file "/path/to/auto-rename-tag") + +(depends-on "lsp-ui" :file "/path/to/lsp-ui") +``` + +ไปฅ **vc** ๆ ผๅผๆŒ‡ๅฎšไพ่ณด้ …๏ผš + +```elisp +(depends-on "auto-rename-tag" :vc "jcs-elpa/auto-rename-tag") + +(depends-on "lsp-ui" :vc "emacs-lsp/lsp-ui") +``` + ไปฅ **recipe** ๆ ผๅผๆŒ‡ๅฎšไพ่ณด้ …๏ผš ```elisp -(depends-on "auto-rename-tag" - :repo "jcs-elpa/auto-rename-tag" +(depends-on "auto-rename-tag" + :repo "jcs-elpa/auto-rename-tag" :fetcher 'github) -(depends-on "lsp-ui" +(depends-on "lsp-ui" :repo "emacs-lsp/lsp-ui" :fetcher 'github :files '(:defaults "lsp-ui-doc.html" "resources")) diff --git a/docs/content/Development-API/_index.en.md b/docs/content/Development-API/_index.en.md index 9ca7dd57..10a62013 100644 --- a/docs/content/Development-API/_index.en.md +++ b/docs/content/Development-API/_index.en.md @@ -129,6 +129,16 @@ then, (message "%s" (eask-command)) ; init ``` +## ๐Ÿ” Function: eask-command-check (`version`) + +Report error if the current command requires minimum `version`. + +```elisp +(eask-start + (eask-command-check "27.1") ; The command requires 27.1 and above! + ... +``` + ## ๐Ÿ” Function: eask-command-p (`commands`) Return t if COMMANDS is the current command. diff --git a/docs/content/Development-API/_index.zh-tw.md b/docs/content/Development-API/_index.zh-tw.md index cb43cfd4..cee586e6 100644 --- a/docs/content/Development-API/_index.zh-tw.md +++ b/docs/content/Development-API/_index.zh-tw.md @@ -128,6 +128,16 @@ eask init (message "%s" (eask-command)) ; init ``` +## ๐Ÿ” ๅ‡ฝๅผ: eask-command-check (`version`) + +ๅฆ‚ๆžœ็›ฎๅ‰็š„ๆŒ‡ไปค้œ€่ฆๆœ€ไฝŽ็š„ `version` ๅฐฑๆœƒๅ ฑ้Œฏใ€‚ + +```elisp +(eask-start + (eask-command-check "27.1") ; ๆญคๆŒ‡ไปค้œ€่ฆ 27.1 ๅŠไปฅไธŠ็‰ˆๆœฌ๏ผ + ... +``` + ## ๐Ÿ” ๅ‡ฝๅผ: eask-command-p (`commands`) ๅฆ‚ๆžœ COMMANDS ๆ˜ฏ็›ฎๅ‰ๅ‘ฝไปค๏ผŒๅ‰‡ๅ‚ณๅ›ž `t`ใ€‚ diff --git a/docs/content/Getting-Started/Basic-Usage/_index.en.md b/docs/content/Getting-Started/Basic-Usage/_index.en.md index 34a38e20..c34638f0 100644 --- a/docs/content/Getting-Started/Basic-Usage/_index.en.md +++ b/docs/content/Getting-Started/Basic-Usage/_index.en.md @@ -49,6 +49,8 @@ Commands: info Display information about the current package init [files..] Initialize project to use Eask install-deps Automatically install package dependencies [aliases: install-dependencies, prepare] + install-file [files..] Install packages from files, .tar files, or directories + install-vc [specs..] Install packages directly from the version control install [names..] Install packages keywords List available keywords that can be used in the header section link Manage links diff --git a/docs/content/Getting-Started/Basic-Usage/_index.zh-tw.md b/docs/content/Getting-Started/Basic-Usage/_index.zh-tw.md index f958ef7f..61f33585 100644 --- a/docs/content/Getting-Started/Basic-Usage/_index.zh-tw.md +++ b/docs/content/Getting-Started/Basic-Usage/_index.zh-tw.md @@ -46,6 +46,8 @@ Commands: info Display information about the current package init [files..] Initialize project to use Eask install-deps Automatically install package dependencies [aliases: install-dependencies, prepare] + install-file [files..] Install packages from files, .tar files, or directories + install-vc [specs..] Install packages directly from the version control install [names..] Install packages keywords List available keywords that can be used in the header section link Manage links diff --git a/docs/content/Getting-Started/Commands-and-options/_index.en.md b/docs/content/Getting-Started/Commands-and-options/_index.en.md index 9e485a35..23f4fbfe 100644 --- a/docs/content/Getting-Started/Commands-and-options/_index.en.md +++ b/docs/content/Getting-Started/Commands-and-options/_index.en.md @@ -114,6 +114,26 @@ Display the state of the workspace. eask [GLOBAL-OPTIONS] status ``` +## ๐Ÿ” eask install + +To install packages. + +```sh +eask [GLOBAL-OPTIONS] install [PACKAGES..] +``` + +Install packages by specifying arguments: + +```sh +eask install auto-complete helm magit +``` + +Or else, it will install the package from the current development: + +```sh +eask install +``` + ## ๐Ÿ” eask install-deps To install all dependencies. @@ -128,24 +148,20 @@ eask [GLOBAL-OPTIONS] install-deps [--dev] ๐Ÿ’ก Specify option [--dev] to install dependencies from the development scope. {{< /hint >}} -## ๐Ÿ” eask install +## ๐Ÿ” eask install-file -To install packages. +Install packages from files, `.tar` files, or directories. ```sh -eask [GLOBAL-OPTIONS] install [PACKAGES..] +eask [GLOBAL-OPTIONS] install-file [FILES..] ``` -Install packages by specifying arguments: +## ๐Ÿ” eask install-vc -```sh -eask install auto-complete helm magit -``` - -Or else, it will install the package from the current development: +Install packages directly from the version control. ```sh -eask install +eask [GLOBAL-OPTIONS] install-vc [SPECS..] ``` ## ๐Ÿ” eask uninstall diff --git a/docs/content/Getting-Started/Commands-and-options/_index.zh-tw.md b/docs/content/Getting-Started/Commands-and-options/_index.zh-tw.md index f70920b8..e1838d7d 100644 --- a/docs/content/Getting-Started/Commands-and-options/_index.zh-tw.md +++ b/docs/content/Getting-Started/Commands-and-options/_index.zh-tw.md @@ -112,6 +112,26 @@ eask [GLOBAL-OPTIONS] info eask [GLOBAL-OPTIONS] status ``` +## ๐Ÿ” eask install + +ๅฎ‰่ฃ่ปŸไปถๅŒ…ใ€‚ + +```sh +eask [GLOBAL-OPTIONS] install [PACKAGES..] +``` + +้€š้ŽๆŒ‡ๅฎšๅƒๆ•ธๅฎ‰่ฃๅŒ…๏ผš + +```sh +eask install auto-complete helm magit +``` + +ๅฆๅ‰‡๏ผŒๅฎƒๅฐ‡ๅฎ‰่ฃ็•ถๅ‰้–‹็™ผ็š„ๅŒ…๏ผš + +```sh +eask install +``` + ## ๐Ÿ” eask install-deps ๅฎ‰่ฃๆ‰€ๆœ‰ไพ่ณด้ …ใ€‚ @@ -126,24 +146,20 @@ eask [GLOBAL-OPTIONS] install-deps [--dev] ๐Ÿ’ก ๆŒ‡ๅฎš้ธ้ … [--dev] ๅพž้–‹็™ผ็ฏ„ๅœๅฎ‰่ฃไพ่ณด้ …ใ€‚ {{< /hint >}} -## ๐Ÿ” eask install +## ๐Ÿ” eask install-file -ๅฎ‰่ฃ่ปŸไปถๅŒ…ใ€‚ +ๅพžๆช”ๆกˆใ€`.tar` ๆช”ๆกˆๆˆ–็›ฎ้Œ„ๅฎ‰่ฃๅฅ—ไปถใ€‚ ```sh -eask [GLOBAL-OPTIONS] install [PACKAGES..] +eask [GLOBAL-OPTIONS] install-file [FILES..] ``` -้€š้ŽๆŒ‡ๅฎšๅƒๆ•ธๅฎ‰่ฃๅŒ…๏ผš +## ๐Ÿ” eask install-vc -```sh -eask install auto-complete helm magit -``` - -ๅฆๅ‰‡๏ผŒๅฎƒๅฐ‡ๅฎ‰่ฃ็•ถๅ‰้–‹็™ผ็š„ๅŒ…๏ผš +็›ดๆŽฅๅพž็‰ˆๆœฌๆŽงๅˆถๅฎ‰่ฃๅฅ—ไปถใ€‚ ```sh -eask install +eask [GLOBAL-OPTIONS] install-vc [SPECS..] ``` ## ๐Ÿ” eask uninstall diff --git a/lisp/_prepare.el b/lisp/_prepare.el index 003cfea3..e73ffb00 100644 --- a/lisp/_prepare.el +++ b/lisp/_prepare.el @@ -19,6 +19,7 @@ (require 'url-vars) (require 'cl-lib) +(require 'ffap) (require 'files) (require 'ls-lisp) (require 'pp) @@ -152,6 +153,12 @@ will return `lint/checkdoc' with a dash between two subcommands." (list script-file)) "/")))) +(defun eask-command-check (version) + "Report error if the current command requires minimum VERSION." + (when (version< emacs-version version) + (eask-error "The command `%s' requires Emacs %s and above!" + (eask-command) version))) + (defun eask-command-p (commands) "Return t if COMMANDS is the current command." (member (eask-command) (eask-listify commands))) @@ -542,10 +549,25 @@ For arguments FUNC and DEPS, see function `mapc' for more information." (len (length deps)) (fmt (eask--action-format len)) (count 0)) - (dolist (pkg deps) + (dolist (dep deps) (cl-incf count) (setq eask--action-prefix (format fmt count)) - (funcall func pkg)))) + (funcall func dep)))) + +(defun eask--install-dep (dep) + "Custom install DEP." + (let ((name (car dep))) + (cond + ;; Install through the function `package-install-file'. + ((memq :file dep) + (let ((file (nth 2 dep))) + (eask-package-install-file name file))) + ;; Install through the function `package-vc-install'. + ((memq :vc dep) + (let ((spec (cdr (memq :vc dep)))) + (eask-package-vc-install name spec))) + ;; Fallback to archive install. + (t (eask-package-install name))))) (defun eask--install-deps (dependencies msg) "Install DEPENDENCIES. @@ -557,10 +579,11 @@ scope of the dependencies (it's either `production' or `development')." (len (length dependencies)) (ies (eask--sinr len "y" "ies")) (pkg-installed (cl-remove-if #'package-installed-p names)) - (installed (length pkg-installed)) (skipped (- len installed))) + (installed (length pkg-installed)) + (skipped (- len installed))) (eask-log "Installing %s %s dependenc%s..." len msg ies) (eask-msg "") - (eask--package-mapc #'eask-package-install names) + (eask--package-mapc #'eask--install-dep dependencies) (eask-msg "") (eask-info "(Total of %s dependenc%s installed, %s skipped)" installed ies skipped))) @@ -629,10 +652,12 @@ Argument PKG is the name of the package." (defmacro eask--pkg-process (pkg &rest body) "Execute BODY with PKG's related variables." (declare (indent 1) (debug t)) - `(let* ((pkg-info (eask--pkg-transaction-vars ,pkg)) - (pkg (nth 0 pkg-info)) - (name (nth 1 pkg-info)) - (version (nth 2 pkg-info))) + `(let* ((pkg-info (eask--pkg-transaction-vars ,pkg)) + (pkg (nth 0 pkg-info)) + (name (nth 1 pkg-info)) + (version (nth 2 pkg-info)) + (installed-p (package-installed-p pkg)) + (should-reinstall-p (and installed-p (eask-force-p)))) ,@body)) (defmacro eask-with-archives (archives &rest body) @@ -669,12 +694,54 @@ Argument BODY are forms for execution." "Return non-nil if package (PKG) is installable." (assq (eask-intern pkg) package-archive-contents)) +(defun eask-package-vc-install (pkg spec) + "To vc install the package (PKG) by argument SPEC." + (eask-defvc< 27 (eask-pkg-init)) ; XXX: remove this after we drop 26.x + (eask--pkg-process pkg + (cond + ((and installed-p (not should-reinstall-p)) + (eask-msg " - %sSkipping %s (%s)... already installed โœ—" + eask--action-prefix + name version)) + (t + (eask-with-progress + (format " - %s%snstalling %s (%s)... " eask--action-prefix + (if should-reinstall-p "Rei" "I") + name version) + (eask-with-verbosity 'debug + ;; Handle `--force` flag. + (when should-reinstall-p (package-delete (eask-package-desc pkg t) t)) + ;; Install it. + (apply #'package-vc-install spec)) + "done โœ“"))))) + +(defun eask-package-install-file (pkg file) + "To FILE install the package (PKG)." + (eask-defvc< 27 (eask-pkg-init)) ; XXX: remove this after we drop 26.x + (eask--pkg-process pkg + (cond + ((and installed-p (not should-reinstall-p)) + (eask-msg " - %sSkipping %s (%s)... already installed โœ—" + eask--action-prefix + name version)) + (t + (eask-with-progress + (format " - %s%snstalling %s (%s)... " eask--action-prefix + (if should-reinstall-p "Rei" "I") + name version) + (eask-with-verbosity 'debug + ;; Handle `--force` flag. + (when should-reinstall-p (package-delete (eask-package-desc pkg t) t)) + ;; Install it. + (package-install-file (expand-file-name file))) + "done โœ“"))))) + (defun eask-package-install (pkg) "Install the package (PKG)." (eask-defvc< 27 (eask-pkg-init)) ; XXX: remove this after we drop 26.x (eask--pkg-process pkg (cond - ((package-installed-p pkg) + ((and installed-p (not should-reinstall-p)) (eask-msg " - %sSkipping %s (%s)... already installed โœ—" eask--action-prefix name version)) @@ -683,25 +750,30 @@ Argument BODY are forms for execution." (unless (eask-package-installable-p pkg) (eask-error "Package not installable `%s'; make sure the package archive (source) is included" pkg)))) (t - (eask--pkg-process pkg - (eask-with-progress - (format " - %sInstalling %s (%s)... " eask--action-prefix name version) - (eask-with-verbosity 'debug - ;; XXX: Without ignore-errors guard, it will trigger error - ;; - ;; Can't find library xxxxxxx.el - ;; - ;; But we can remove this after Emacs 28, since function `find-library-name' - ;; has replaced the function `signal' instead of the `error'. - (eask-ignore-errors (package-install pkg))) - "done โœ“")))))) + (eask-with-progress + (format " - %s%snstalling %s (%s)... " eask--action-prefix + (if should-reinstall-p "Rei" "I") + name version) + (eask-with-verbosity 'debug + ;; Handle `--force` flag. + (when should-reinstall-p (package-delete (eask-package-desc pkg t) t)) + ;; XXX: Without ignore-errors guard, it will trigger error + ;; + ;; Can't find library xxxxxxx.el + ;; + ;; But we can remove this after Emacs 28, since function `find-library-name' + ;; has replaced the function `signal' instead of the `error'. + ;; + ;; Install it. + (eask-ignore-errors (package-install pkg))) + "done โœ“"))))) (defun eask-package-delete (pkg) "Delete the package (PKG)." (eask-defvc< 27 (eask-pkg-init)) ; XXX: remove this after we drop 26.x (eask--pkg-process pkg (cond - ((not (package-installed-p pkg)) + ((not installed-p) (eask-msg " - %sSkipping %s (%s)... not installed โœ—" eask--action-prefix name version)) (t (eask--pkg-process pkg @@ -716,17 +788,17 @@ Argument BODY are forms for execution." (eask-defvc< 27 (eask-pkg-init)) ; XXX: remove this after we drop 26.x (eask--pkg-process pkg (cond - ((not (package-installed-p pkg)) + ;; You cannot reinstall the package that are not installed. + ((not installed-p) (eask-msg " - %sSkipping %s (%s)... not installed โœ—" eask--action-prefix name version)) (t (eask-pkg-init) - (eask--pkg-process pkg - (eask-with-progress - (format " - %sReinstalling %s (%s)... " eask--action-prefix name version) - (eask-with-verbosity 'debug - (package-delete (eask-package-desc pkg t) t) - (eask-ignore-errors (package-install pkg))) - "done โœ“")))))) + (eask-with-progress + (format " - %sReinstalling %s (%s)... " eask--action-prefix name version) + (eask-with-verbosity 'debug + (package-delete (eask-package-desc pkg t) t) + (eask-ignore-errors (package-install pkg))) + "done โœ“"))))) (defun eask-package-desc (name &optional current) "Build package description by its NAME. @@ -1487,6 +1559,17 @@ argument COMMAND." (add-hook 'eask-file-loaded-hook #'eask--setup-dependencies) +(defun eask--check-depends-on (recipe) + "Return non-nil if RECIPE is invalid." + (let ((pkg (car recipe)) + (minimum-version (cdr recipe))) + (cond ((member recipe eask-depends-on) + (eask-error "Define dependencies with the same name `%s'" pkg)) + ((cl-some (lambda (rcp) + (string= (car rcp) pkg)) + eask-depends-on) + (eask-error "Define dependencies with the same name `%s' with different version" pkg))))) + (defun eask-f-depends-on (pkg &rest args) "Specify a dependency (PKG) of this package. @@ -1505,17 +1588,27 @@ ELPA)." recipe))) ;; No argument specify ((<= (length args) 1) - (let* ((minimum-version (or (car args) "0")) + (let* ((minimum-version (car args)) (recipe (list pkg minimum-version))) - (if (member recipe eask-depends-on) - (eask-error "Define dependencies with the same name `%s'" pkg) + (unless (eask--check-depends-on recipe) + (push recipe eask-depends-on)) + recipe)) + ;; File packages + ((memq :file args) + (let* ((recipe (append (list (intern pkg)) args))) + (unless (eask--check-depends-on recipe) + (push recipe eask-depends-on)) + recipe)) + ;; VC packages + ((memq :vc args) + (let* ((recipe (append (list (intern pkg)) args))) + (unless (eask--check-depends-on recipe) (push recipe eask-depends-on)) recipe)) ;; recipe are entered (t (let ((recipe (append (list (intern pkg)) args))) - (if (member recipe eask-depends-on) - (eask-error "Define dependencies with the same name `%s'" pkg) + (unless (eask--check-depends-on recipe) (push recipe eask-depends-on) (eask-load "extern/github-elpa") (eask-with-verbosity 'debug diff --git a/lisp/core/info.el b/lisp/core/info.el index a89fa3c0..4225098a 100644 --- a/lisp/core/info.el +++ b/lisp/core/info.el @@ -27,9 +27,12 @@ offset (eask-2str eask-info--max-offset)) (dolist (dep dependencies) (let* ((target-version (cdr dep)) - (target-version (if (= (length target-version) 1) - (nth 0 target-version) - "specified"))) + (target-version (cond ((= (length target-version) 1) + (or (nth 0 target-version) ; verison number + "archive")) + ((memq :file dep) "file") + ((memq :vc dep) "vc") + (t "recipe")))) (eask-println (concat " %-" offset "s (%s)") (car dep) target-version) (eask-debug " Recipe: %s" (car dep))))))) diff --git a/lisp/core/install-file.el b/lisp/core/install-file.el new file mode 100644 index 00000000..a542b3a6 --- /dev/null +++ b/lisp/core/install-file.el @@ -0,0 +1,55 @@ +;;; core/install-file.el --- Install packages from files, .tar files, or directories -*- lexical-binding: t; -*- + +;;; Commentary: +;; +;; Command use to install packages from files, .tar files, or directories +;; +;; $ eask install-file [files..] +;; +;; +;; Positionals: +;; +;; [files..] files to install as packages; it will install through the +;; function `package-install-file' +;; + +;;; Code: + +(let ((dir (file-name-directory (nth 1 (member "-scriptload" command-line-args))))) + (load (expand-file-name "_prepare.el" + (locate-dominating-file dir "_prepare.el")) + nil t)) + +(defun eask-install-file--guess-name (file) + "Guess the package name of the install FILE." + (file-name-sans-extension (file-name-nondirectory (directory-file-name file)))) + +(defun eask-install-file--packages (files) + "The file install packages with FILES." + (let* ((deps (mapcar (lambda (file) + (list (eask-install-file--guess-name file) file)) + files)) + (names (mapcar #'car deps)) + (len (length deps)) + (s (eask--sinr len "" "s")) + (pkg-not-installed (cl-remove-if #'package-installed-p names)) + (installed (length pkg-not-installed)) (skipped (- len installed))) + (eask-log "Installing %s specified file package%s..." len s) + (eask-msg "") + (eask--package-mapc (lambda (dep &rest _) + (apply #'eask-package-install-file dep)) + deps) + (eask-msg "") + (eask-info "(Total of %s file package%s installed, %s skipped)" + installed s skipped))) + +(eask-start + (eask-pkg-init) + (if-let* ((files (eask-args))) + ;; If package [files..] are specified, we try to install it + (eask-install-file--packages files) + ;; Otherwise, report error. + (eask-info "(No file packages have been intalled)") + (eask-help "core/install-file"))) + +;;; core/install-file.el ends here diff --git a/lisp/core/install-vc.el b/lisp/core/install-vc.el new file mode 100644 index 00000000..02834853 --- /dev/null +++ b/lisp/core/install-vc.el @@ -0,0 +1,71 @@ +;;; core/install-vc.el --- Install packages directly from the version control -*- lexical-binding: t; -*- + +;;; Commentary: +;; +;; Command use to install packages directly from the version control +;; +;; $ eask install-vc [specs..] +;; +;; +;; Positionals: +;; +;; [specs..] specs to install as packages; it will install through the +;; function `package-vc-install' +;; + +;;; Code: + +(let ((dir (file-name-directory (nth 1 (member "-scriptload" command-line-args))))) + (load (expand-file-name "_prepare.el" + (locate-dominating-file dir "_prepare.el")) + nil t)) + +(eask-load "core/install-file") + +(defun eask-install-vc--split-sepcs (specs) + "Split the SPECS and return a list of specification." + (let ((new-specs) + (current-spec)) + (dolist (spec specs) + ;; Detect new specification. + (cond ((ffap-url-p spec) + (push (reverse current-spec) new-specs) + ;; We're using the push, so the order is reversed. + (setq current-spec (list spec (eask-install-file--guess-name spec)))) + (t + (push spec current-spec)))) + ;; Push thes rest of the specification. + (push (reverse current-spec) new-specs) + (cl-remove-if #'null (reverse new-specs)))) + +(defun eask-install-vc--packages (specs) + "The vc install packages with SPECS." + (let* ((deps (eask-install-vc--split-sepcs specs)) + (names (mapcar #'car deps)) + (len (length deps)) + (s (eask--sinr len "" "s")) + (pkg-not-installed (cl-remove-if #'package-installed-p names)) + (installed (length pkg-not-installed)) (skipped (- len installed))) + (eask-log "Installing %s specified vc package%s..." len s) + (eask-msg "") + (eask--package-mapc (lambda (dep &rest _) + (let ((name (car dep)) + (spec (cdr dep))) + (eask-package-vc-install name spec))) + deps) + (eask-msg "") + (eask-info "(Total of %s vc package%s installed, %s skipped)" + installed s skipped))) + +(eask-start + (eask-command-check "29.1") + + (eask-pkg-init) + (if-let* ((specs (eask-args))) + ;; If package [specs..] are specified, we try to install it + (eask-install-vc--packages specs) + ;; Otherwise, report error. + (eask-info "(No vc packages have been intalled)") + (eask-help "core/install-vc"))) + +;;; core/install-vc.el ends here diff --git a/lisp/core/loc.el b/lisp/core/loc.el index 62c82cd7..5beb242e 100644 --- a/lisp/core/loc.el +++ b/lisp/core/loc.el @@ -35,6 +35,8 @@ (insert (format "| %s | %s | %s |\n" file lines chars))))) (eask-start + (eask-command-check "28.1") + ;; Preparation (eask-archive-install-packages '("gnu" "melpa") 'markdown-mode) diff --git a/lisp/format/elisp-autofmt.el b/lisp/format/elisp-autofmt.el index 9a729c58..3f3d9364 100644 --- a/lisp/format/elisp-autofmt.el +++ b/lisp/format/elisp-autofmt.el @@ -45,6 +45,8 @@ (kill-buffer)))) (eask-start + (eask-command-check "28.1") + ;; Preparation (eask-archive-install-packages '("gnu" "melpa") 'elisp-autofmt) diff --git a/lisp/help/core/install-file b/lisp/help/core/install-file new file mode 100644 index 00000000..4556d96c --- /dev/null +++ b/lisp/help/core/install-file @@ -0,0 +1,4 @@ + +๐Ÿ’ก You can add dependencies by using the specifier [depends-on] + + [+] (depends-on "PACKAGE-NAME" :file "/path/to/PACKAGE-NAME") diff --git a/lisp/help/core/install-vc b/lisp/help/core/install-vc new file mode 100644 index 00000000..4c6f6844 --- /dev/null +++ b/lisp/help/core/install-vc @@ -0,0 +1,4 @@ + +๐Ÿ’ก You can add dependencies by using the specifier [depends-on] + + [+] (depends-on "PACKAGE-NAME" :vc "/url/to/PACKAGE-NAME") diff --git a/lisp/lint/regexps.el b/lisp/lint/regexps.el index 78d46b34..1c4657af 100644 --- a/lisp/lint/regexps.el +++ b/lisp/lint/regexps.el @@ -59,6 +59,8 @@ (kill-this-buffer)))) (eask-start + (eask-command-check "27.1") + ;; Preparation (eask-archive-install-packages '("gnu") 'relint) diff --git a/test/jest/__snapshots__/analyze.test.js.snap b/test/jest/__snapshots__/analyze.test.js.snap index cebc9b2f..7ebf4945 100644 --- a/test/jest/__snapshots__/analyze.test.js.snap +++ b/test/jest/__snapshots__/analyze.test.js.snap @@ -18,7 +18,9 @@ exports[`analyze in ./dsl matches snapshot 1`] = ` ~/Eask:35:15 Error: Unknown package archive \`local' ~/Eask:38:20 Error: Define dependencies with the same name \`emacs' ~/Eask:41:19 Error: Define dependencies with the same name \`dash' -~/Eask:46:2 Error: Define dependencies with the same name \`f' +~/Eask:42:27 Error: Define dependencies with the same name \`dash' with different version +~/Eask:48:2 Error: Define dependencies with the same name \`f' +~/Eask:48:2 Error: Define dependencies with the same name \`f' with different version ", "stdout": "", } diff --git a/test/jest/docker.test.js b/test/jest/docker.test.js index 3d122a76..797bc792 100644 --- a/test/jest/docker.test.js +++ b/test/jest/docker.test.js @@ -1,6 +1,6 @@ const { TestContext } = require("./helpers"); -jest.setTimeout(60000); +jest.setTimeout(1000 * 60); describe("docker", () => { const ctx = new TestContext("./test/jest/docker"); diff --git a/test/jest/dsl/Eask b/test/jest/dsl/Eask index 67341f37..ec5dc11e 100644 --- a/test/jest/dsl/Eask +++ b/test/jest/dsl/Eask @@ -39,8 +39,10 @@ (depends-on "dash") ; duplicate dependency (depends-on "dash") +(depends-on "dash" "9.9.9") ; duplicate dependency; different version (development (depends-on "f") ; duplicate dependency (depends-on "f") + (depends-on "f" "9.9.9") ; duplicate dependency; different version ) diff --git a/test/jest/install.test.js b/test/jest/install.test.js index 8883da4b..ea2f2849 100644 --- a/test/jest/install.test.js +++ b/test/jest/install.test.js @@ -1,4 +1,6 @@ -const { TestContext } = require("./helpers"); +const { emacsVersion, TestContext } = require("./helpers"); + +jest.setTimeout(1000 * 60 * 10000); describe("install and uninstall", () => { describe("in ./install", () => { @@ -7,7 +9,6 @@ describe("install and uninstall", () => { beforeAll(async () => { await ctx.runEask("clean all"); - await ctx.runEask("install-deps"); }); afterAll(() => ctx.cleanUp()); @@ -41,6 +42,30 @@ describe("install and uninstall", () => { const { stderr } = await ctx.runEask("list"); expect(stderr).not.toMatch(packageName); }); + + it("installs dependencies", async () => { + const { stderr } = await ctx.runEask("install-deps"); + expect(stderr).not.toMatch(packageName); + }); + + it("installs dev dependencies", async () => { + const { stderr } = await ctx.runEask("install-deps --dev"); + expect(stderr).not.toMatch(packageName); + }); + + it("installs file directly", async () => { + const { stderr } = await ctx.runEask("install-file ./mini.pkg.2"); + expect(stderr).toMatch("mini.pkg.2"); + }); + + test.skip("installs vc directly", async () => { + if ((await emacsVersion()) >= "29.1") { + const { stderr } = await ctx.runEask( + "install-vc https://github.com/jcs-elpa/msgu" + ); + expect(stderr).toMatch("msgu"); + } + }); }); describe("in an empty project", () => { diff --git a/test/jest/install/Eask b/test/jest/install/Eask index aa3c6aeb..8f132288 100644 --- a/test/jest/install/Eask +++ b/test/jest/install/Eask @@ -26,12 +26,26 @@ (depends-on "emacs" "26.1") (depends-on "f") (depends-on "s") -;; (depends-on "fringe-helper") -;; (depends-on "watch-cursor" -;; :repo "jcs-elpa/watch-cursor" :fetcher 'github) +(depends-on "fringe-helper") + +;; File install +(depends-on "mini.pkg.2" :file "./mini.pkg.2") + +;; VC install +(when (version<= "29.1" emacs-version) + ;; XXX: This takes too long to test, disable for now. + ;;(depends-on "msgu" :vc "https://github.com/jcs-elpa/msgu") + ) + +;; Recipe install +(depends-on "watch-cursor" + :repo "jcs-elpa/watch-cursor" :fetcher 'github) ;; (depends-on "organize-imports-java" ;; :repo "jcs-elpa/organize-imports-java" ;; :fetcher 'github ;; :files '(:defaults "sdk" "default")) +(development + (depends-on "ert-runner")) + (setq network-security-level 'low) ; see https://github.com/jcs090218/setup-emacs-windows/issues/156#issuecomment-932956432 diff --git a/test/jest/install/mini.pkg.2/mini.pkg.2.el b/test/jest/install/mini.pkg.2/mini.pkg.2.el new file mode 100644 index 00000000..95a1d786 --- /dev/null +++ b/test/jest/install/mini.pkg.2/mini.pkg.2.el @@ -0,0 +1,39 @@ +;;; mini.pkg.2.el --- Minimal test package 2 -*- lexical-binding: t; -*- + +;; Copyright (C) 2022-2025 the Eask authors. +;; Created date 2022-03-29 01:52:58 + +;; Author: Shen, Jen-Chieh +;; URL: https://github.com/emacs-eask/cli/tree/master/test/fixtures/mini.pkg.2 +;; Version: 0.0.1 +;; Package-Requires: ((emacs "24.3") (s "1.12.0") (fringe-helper "1.0.1")) +;; Keywords: test local + +;; This file is NOT part of GNU Emacs. + +;; 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 . + +;;; Commentary: +;; +;; Minimal Emacs package to simulate development environment; only for testing +;; purposes! +;; + +;;; Code: + +(require 's) +(require 'fringe-helper) + +(provide 'mini.pkg.2) +;;; mini.pkg.2.el ends here diff --git a/test/jest/local.test.js b/test/jest/local.test.js index d8de59aa..8b455750 100644 --- a/test/jest/local.test.js +++ b/test/jest/local.test.js @@ -6,7 +6,7 @@ const { emacsVersion, TestContext } = require("./helpers"); -jest.setTimeout(60000); +jest.setTimeout(1000 * 60); describe("local", () => { const cwd = "./test/jest/local"; @@ -130,7 +130,7 @@ describe("local", () => { await ctx.runEask("clean pkg-file").catch(() => {}); await ctx.removeFiles( - "recipes", // from generate recipes + "recipes", // from generate recipes ".gitignore", // from generate ignore elisp ); }); @@ -168,21 +168,21 @@ describe("local", () => { describe("Generating workflow", () => { beforeEach( async () => - await ctx.removeFiles( - ".circleci", - ".gitlab-ci.yml", - ".github", - ".travis.yml", - ), + await ctx.removeFiles( + ".circleci", + ".gitlab-ci.yml", + ".github", + ".travis.yml", + ), ); afterAll( async () => - await ctx.removeFiles( - ".circleci", - ".gitlab-ci.yml", - ".github", - ".travis.yml", - ), + await ctx.removeFiles( + ".circleci", + ".gitlab-ci.yml", + ".github", + ".travis.yml", + ), ); it.each([ @@ -211,7 +211,7 @@ describe("local", () => { await ctx.runEask(cmd); }); it("lint regexps", async () => { - if ((await emacsVersion()) > "27.1") { + if ((await emacsVersion()) >= "27.1") { await ctx.runEask("lint regexps"); } }); @@ -230,7 +230,7 @@ describe("local", () => { describe("Formatting", () => { // installs elisp-autofmt it("format elisp-autofmt", async () => { - if ((await emacsVersion()) > "29.1") { + if ((await emacsVersion()) >= "28.1") { await ctx.runEask("format elisp-autofmt"); } });