cmd: Allow add-package to select version of package (#6665)

* feat: allow versioning of packages

* docs: remove xcaddy issue reference
This commit is contained in:
Lucas VerÍssimo Botelho 2024-10-30 13:48:36 -03:00 committed by GitHub
parent eaaa2e5872
commit d398898b35
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 53 additions and 12 deletions

View File

@ -409,12 +409,13 @@ latest versions. EXPERIMENTAL: May be changed or removed.
RegisterCommand(Command{ RegisterCommand(Command{
Name: "add-package", Name: "add-package",
Usage: "<packages...>", Usage: "<package[@version]...>",
Short: "Adds Caddy packages (EXPERIMENTAL)", Short: "Adds Caddy packages (EXPERIMENTAL)",
Long: ` Long: `
Downloads an updated Caddy binary with the specified packages (module/plugin) Downloads an updated Caddy binary with the specified packages (module/plugin)
added. Retains existing packages. Returns an error if the any of packages are added, with an optional version specified (e.g., "package@version"). Retains
already included. EXPERIMENTAL: May be changed or removed. existing packages. Returns an error if any of the specified packages are already
included. EXPERIMENTAL: May be changed or removed.
`, `,
CobraFunc: func(cmd *cobra.Command) { CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().BoolP("keep-backup", "k", false, "Keep the backed up binary, instead of deleting it") cmd.Flags().BoolP("keep-backup", "k", false, "Keep the backed up binary, instead of deleting it")

View File

@ -46,6 +46,25 @@ func cmdUpgrade(fl Flags) (int, error) {
return upgradeBuild(pluginPkgs, fl) return upgradeBuild(pluginPkgs, fl)
} }
func splitModule(arg string) (module, version string, err error) {
const versionSplit = "@"
// accommodate module paths that have @ in them, but we can only tolerate that if there's also
// a version, otherwise we don't know if it's a version separator or part of the file path
lastVersionSplit := strings.LastIndex(arg, versionSplit)
if lastVersionSplit < 0 {
module = arg
} else {
module, version = arg[:lastVersionSplit], arg[lastVersionSplit+1:]
}
if module == "" {
err = fmt.Errorf("module name is required")
}
return
}
func cmdAddPackage(fl Flags) (int, error) { func cmdAddPackage(fl Flags) (int, error) {
if len(fl.Args()) == 0 { if len(fl.Args()) == 0 {
return caddy.ExitCodeFailedStartup, fmt.Errorf("at least one package name must be specified") return caddy.ExitCodeFailedStartup, fmt.Errorf("at least one package name must be specified")
@ -60,10 +79,15 @@ func cmdAddPackage(fl Flags) (int, error) {
} }
for _, arg := range fl.Args() { for _, arg := range fl.Args() {
if _, ok := pluginPkgs[arg]; ok { module, version, err := splitModule(arg)
if err != nil {
return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid module name: %v", err)
}
// only allow a version to be specified if it's different from the existing version
if _, ok := pluginPkgs[module]; ok && !(version != "" && pluginPkgs[module].Version != version) {
return caddy.ExitCodeFailedStartup, fmt.Errorf("package is already added") return caddy.ExitCodeFailedStartup, fmt.Errorf("package is already added")
} }
pluginPkgs[arg] = struct{}{} pluginPkgs[module] = pluginPackage{Version: version, Path: module}
} }
return upgradeBuild(pluginPkgs, fl) return upgradeBuild(pluginPkgs, fl)
@ -83,7 +107,11 @@ func cmdRemovePackage(fl Flags) (int, error) {
} }
for _, arg := range fl.Args() { for _, arg := range fl.Args() {
if _, ok := pluginPkgs[arg]; !ok { module, _, err := splitModule(arg)
if err != nil {
return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid module name: %v", err)
}
if _, ok := pluginPkgs[module]; !ok {
// package does not exist // package does not exist
return caddy.ExitCodeFailedStartup, fmt.Errorf("package is not added") return caddy.ExitCodeFailedStartup, fmt.Errorf("package is not added")
} }
@ -93,7 +121,7 @@ func cmdRemovePackage(fl Flags) (int, error) {
return upgradeBuild(pluginPkgs, fl) return upgradeBuild(pluginPkgs, fl)
} }
func upgradeBuild(pluginPkgs map[string]struct{}, fl Flags) (int, error) { func upgradeBuild(pluginPkgs map[string]pluginPackage, fl Flags) (int, error) {
l := caddy.Log() l := caddy.Log()
thisExecPath, err := os.Executable() thisExecPath, err := os.Executable()
@ -120,8 +148,8 @@ func upgradeBuild(pluginPkgs map[string]struct{}, fl Flags) (int, error) {
"os": {runtime.GOOS}, "os": {runtime.GOOS},
"arch": {runtime.GOARCH}, "arch": {runtime.GOARCH},
} }
for pkg := range pluginPkgs { for _, pkgInfo := range pluginPkgs {
qs.Add("p", pkg) qs.Add("p", pkgInfo.String())
} }
// initiate the build // initiate the build
@ -276,14 +304,14 @@ func downloadBuild(qs url.Values) (*http.Response, error) {
return resp, nil return resp, nil
} }
func getPluginPackages(modules []moduleInfo) (map[string]struct{}, error) { func getPluginPackages(modules []moduleInfo) (map[string]pluginPackage, error) {
pluginPkgs := make(map[string]struct{}) pluginPkgs := make(map[string]pluginPackage)
for _, mod := range modules { for _, mod := range modules {
if mod.goModule.Replace != nil { if mod.goModule.Replace != nil {
return nil, fmt.Errorf("cannot auto-upgrade when Go module has been replaced: %s => %s", return nil, fmt.Errorf("cannot auto-upgrade when Go module has been replaced: %s => %s",
mod.goModule.Path, mod.goModule.Replace.Path) mod.goModule.Path, mod.goModule.Replace.Path)
} }
pluginPkgs[mod.goModule.Path] = struct{}{} pluginPkgs[mod.goModule.Path] = pluginPackage{Version: mod.goModule.Version, Path: mod.goModule.Path}
} }
return pluginPkgs, nil return pluginPkgs, nil
} }
@ -312,3 +340,15 @@ func writeCaddyBinary(path string, body *io.ReadCloser, fileInfo os.FileInfo) er
} }
const downloadPath = "https://caddyserver.com/api/download" const downloadPath = "https://caddyserver.com/api/download"
type pluginPackage struct {
Version string
Path string
}
func (p pluginPackage) String() string {
if p.Version == "" {
return p.Path
}
return p.Path + "@" + p.Version
}