feat(build): plugin:install --source and git URLs as submodules

- --source clones each registry repository (https only) at the chosen tag,
  adds it as a submodule under plugins/<vendor>/<name>, absorbs its git
  dir and registers it with the tag as the go.mod require
- git URLs (https, ssh, user@host:path) are cloned with the user's git
  setup and never sent to the registry; the newest tag whose plugin.yaml
  framework minimum fits is checked out, else the default branch head
- go.work replace overrides for every local replace in workspace modules'
  go.mod files (app target, versioned workspace module, or version pin)
- GIT_ALLOW_PROTOCOL=https:ssh, '--' before URLs and paths; non-git apps
  are refused before cloning; plugin:add keeps require v0.0.0
This commit is contained in:
Jakub Zych
2026-10-29 18:39:22 +01:00
parent a462bf12c9
commit 8093587696
8 changed files with 1191 additions and 29 deletions

View File

@@ -114,7 +114,7 @@ A required plugin the application already has is not touched, but its version mu
### Confirmation
Before changing anything, the command prints each plugin it is about to add, one line per plugin:
Before changing anything, the command prints each plugin it is about to add, one line per plugin, with `module` or `submodule` as the mode:
```text
Installing 2 plugins into /srv/acme (framework v0.6.2):
@@ -124,6 +124,34 @@ Installing 2 plugins into /srv/acme (framework v0.6.2):
Then it asks for confirmation, with "no" as the default. `--yes` skips the question. A run that cannot ask (no terminal, `-n` or `-q`) stops with an error naming `--yes` unless it is given. Declining changes nothing.
### Installing from source
`--source` installs the plugin, and every required plugin it brings, as git submodules instead of Go modules:
```sh
summer plugin:install acme.blog --source
```
Each repository (the `repository` the registry lists, which must be an `https` URL) is cloned, checked out at the chosen release tag and added as a submodule in `plugins/<vendor>/<name>`, named after the ID in the repository root's `plugin.yaml`. A plugin in a subdirectory of its repository lives inside that directory. The `plugin.yaml` in the checkout must describe the plugin the registry listed (same ID and module), or the install is refused. The plugin is then registered like `summer plugin:add` does it, with the tag as the version `go.mod` requires, and `go.work` gets the replace overrides the plugin's own `go.mod` needs to build inside the application (see [Plugins installed as submodules](../architecture/go-modules-and-workspaces.md#plugins-installed-as-submodules)). Commit `.gitmodules`, the submodule, `go.mod`, `go.work` and `summer.yaml` together.
A submodule needs a git repository: in an application that is not one, `--source` stops before cloning anything.
### Installing from a git URL
A plugin that is not listed, such as a private one, installs from its repository:
```sh
summer plugin:install https://git.example.com/acme/sm-blog-plugin.git
summer plugin:install git@git.example.com:acme/sm-blog-plugin.git
summer plugin:install ssh://git@git.example.com/acme/sm-blog-plugin.git
```
The repository is cloned with your own git setup (credential helpers, SSH keys, `insteadOf` rules) and never sent to the registry. The command reads `plugin.yaml` at the repository's release tags, newest first, and checks out the newest tag whose framework minimum the application meets. When no tag fits, it takes the default branch head and prints a warning. A git URL install is always a submodule install, with the same layout and `go.work` overrides as `--source`.
The plugin's required plugins are looked up in the registry by ID and installed as submodules too. With `--offline` no registry call is made, so every required plugin must already be in `summer.yaml`; a missing one stops the install and is named.
Only `https://`, `ssh://` and scp-like `user@host:path` addresses are accepted. Git runs with `GIT_ALLOW_PROTOCOL=https:ssh`, so `ext::` and `file::` transports, `file://` and plain `http://` are refused, as is an address that starts with `-` or carries a password (use a credential helper instead).
### The registry setting
The registry is `https://summercms.io` unless the application's `plugins.registry_url` setting names another one: