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");
}
});