mirror of
https://github.com/caddyserver/xcaddy.git
synced 2025-01-22 16:46:55 +01:00
Support replacements and customizing the core version; update readme
This commit is contained in:
parent
681037f5e1
commit
fd8e902223
5 changed files with 151 additions and 25 deletions
29
README.md
29
README.md
|
@ -13,8 +13,8 @@ Stay updated, be aware of changes, and please submit feedback! Thanks!
|
|||
|
||||
## Requirements
|
||||
|
||||
- Go installed
|
||||
- Go modules enabled
|
||||
- [Go installed](https://golang.org/doc/install)
|
||||
- [Go modules](https://github.com/golang/go/wiki/Modules) enabled
|
||||
|
||||
|
||||
## Command usage
|
||||
|
@ -30,31 +30,44 @@ Install the `xcaddy` command with:
|
|||
$ go get -u github.com/caddyserver/xcaddy/cmd/xcaddy
|
||||
```
|
||||
|
||||
The `xcaddy` command will use the latest version of Caddy by default. You can customize this for all invocations by setting the `CADDY_VERSION` environment variable.
|
||||
|
||||
As usual with `go` command, the `xcaddy` command will pass through the `GOOS`, `GOARCH`, and `GOARM` environment variables for cross-compilation.
|
||||
|
||||
|
||||
### Custom builds
|
||||
|
||||
Syntax:
|
||||
|
||||
```
|
||||
$ xcaddy build <caddy_version>
|
||||
$ xcaddy build [<caddy_version>]
|
||||
[--output <file>]
|
||||
[--with <module[@version]>...]
|
||||
[--with <module[@version][=replacement]>...]
|
||||
```
|
||||
|
||||
- `<caddy_version>` is the core Caddy version to build (required, for now).
|
||||
- `<caddy_version>` is the core Caddy version to build; defaults to `CADDY_VERSION` env variable or latest.
|
||||
- `--output` changes the output file.
|
||||
- `--with` can be used multiple times to add plugins by specifying the Go module name and optionally its version, similar to `go get`.
|
||||
|
||||
For example:
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
$ xcaddy build v2.0.0-rc.1 \
|
||||
$ xcaddy build \
|
||||
--with github.com/caddyserver/ntlm-transport
|
||||
|
||||
$ xcaddy build v2.0.1 \
|
||||
--with github.com/caddyserver/ntlm-transport@v0.1.0
|
||||
|
||||
$ xcaddy build \
|
||||
--with github.com/caddyserver/ntlm-transport=../../my-fork
|
||||
|
||||
$ xcaddy build \
|
||||
--with github.com/caddyserver/ntlm-transport=@v0.1.0=../../my-fork
|
||||
```
|
||||
|
||||
### For plugin development
|
||||
|
||||
If you run `xcaddy` from within the folder of the Caddy plugin you're working on without the `build` subcommand described above, it will build Caddy with your current module and run it, as if you manually plugged it in and ran `go run`.
|
||||
If you run `xcaddy` from within the folder of the Caddy plugin you're working on _without the `build` subcommand_, it will build Caddy with your current module and run it, as if you manually plugged it in and invoked `go run`.
|
||||
|
||||
The binary will be built and run from the current directory, then cleaned up.
|
||||
|
||||
|
|
|
@ -42,9 +42,6 @@ type Builder struct {
|
|||
// Build builds Caddy at the configured version with the
|
||||
// configured plugins and plops down a binary at outputFile.
|
||||
func (b Builder) Build(ctx context.Context, outputFile string) error {
|
||||
if b.CaddyVersion == "" {
|
||||
return fmt.Errorf("CaddyVersion must be set")
|
||||
}
|
||||
if outputFile == "" {
|
||||
return fmt.Errorf("output file path is required")
|
||||
}
|
||||
|
@ -141,7 +138,11 @@ func newTempFolder() (string, error) {
|
|||
// of "foo" and "v2.0.0" will return "foo/v2", for use in Go imports and go commands.
|
||||
// Inputs that conflict, like "foo/v2" and "v3.1.0" are an error. This function
|
||||
// returns the input if the moduleVersion is not a valid semantic version string.
|
||||
// If moduleVersion is empty string, the input modulePath is returned without error.
|
||||
func versionedModulePath(modulePath, moduleVersion string) (string, error) {
|
||||
if moduleVersion == "" {
|
||||
return modulePath, nil
|
||||
}
|
||||
ver, err := semver.StrictNewVersion(strings.TrimPrefix(moduleVersion, "v"))
|
||||
if err != nil {
|
||||
// only return the error if we know they were trying to use a semantic version
|
||||
|
|
|
@ -29,6 +29,8 @@ import (
|
|||
"github.com/caddyserver/xcaddy"
|
||||
)
|
||||
|
||||
var caddyVersion = os.Getenv("CADDY_VERSION")
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
@ -41,16 +43,16 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
// TODO: the caddy version needs to be settable by the user... maybe an env var?
|
||||
if err := runDev(ctx, "v2.0.0-rc.3", os.Args[1:]); err != nil {
|
||||
if err := runDev(ctx, os.Args[1:]); err != nil {
|
||||
log.Fatalf("[ERROR] %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func runBuild(ctx context.Context, args []string) error {
|
||||
// parse the command line args... rather primitively
|
||||
var caddyVersion, output string
|
||||
var argCaddyVersion, output string
|
||||
var plugins []xcaddy.Dependency
|
||||
var replacements []xcaddy.Replace
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch args[i] {
|
||||
case "--with":
|
||||
|
@ -58,17 +60,20 @@ func runBuild(ctx context.Context, args []string) error {
|
|||
return fmt.Errorf("expected value after --with flag")
|
||||
}
|
||||
i++
|
||||
var mod, ver string
|
||||
arg := args[i]
|
||||
parts := strings.SplitN(arg, "@", 2)
|
||||
mod = parts[0]
|
||||
if len(parts) == 2 {
|
||||
ver = parts[1]
|
||||
mod, ver, repl, err := splitWith(args[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plugins = append(plugins, xcaddy.Dependency{
|
||||
ModulePath: mod,
|
||||
Version: ver,
|
||||
})
|
||||
if repl != "" {
|
||||
replacements = append(replacements, xcaddy.Replace{
|
||||
Old: mod,
|
||||
New: repl,
|
||||
})
|
||||
}
|
||||
|
||||
case "--output":
|
||||
if i == len(args)-1 {
|
||||
|
@ -78,13 +83,18 @@ func runBuild(ctx context.Context, args []string) error {
|
|||
output = args[i]
|
||||
|
||||
default:
|
||||
if caddyVersion != "" {
|
||||
return fmt.Errorf("missing flag; caddy version already set at %s", caddyVersion)
|
||||
if argCaddyVersion != "" {
|
||||
return fmt.Errorf("missing flag; caddy version already set at %s", argCaddyVersion)
|
||||
}
|
||||
caddyVersion = args[i]
|
||||
argCaddyVersion = args[i]
|
||||
}
|
||||
}
|
||||
|
||||
// prefer caddy version from command line argument over env var
|
||||
if argCaddyVersion != "" {
|
||||
caddyVersion = argCaddyVersion
|
||||
}
|
||||
|
||||
// ensure an output file is always specified
|
||||
if output == "" {
|
||||
if runtime.GOOS == "windows" {
|
||||
|
@ -98,6 +108,7 @@ func runBuild(ctx context.Context, args []string) error {
|
|||
builder := xcaddy.Builder{
|
||||
CaddyVersion: caddyVersion,
|
||||
Plugins: plugins,
|
||||
Replacements: replacements,
|
||||
}
|
||||
err := builder.Build(ctx, output)
|
||||
if err != nil {
|
||||
|
@ -121,7 +132,7 @@ func runBuild(ctx context.Context, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func runDev(ctx context.Context, caddyVersion string, args []string) error {
|
||||
func runDev(ctx context.Context, args []string) error {
|
||||
const binOutput = "./caddy"
|
||||
|
||||
// get current/main module name
|
||||
|
@ -220,3 +231,31 @@ func trapSignals(ctx context.Context, cancel context.CancelFunc) {
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
func splitWith(arg string) (module, version, replace string, err error) {
|
||||
const versionSplit, replaceSplit = "@", "="
|
||||
|
||||
parts := strings.SplitN(arg, versionSplit, 2)
|
||||
module = parts[0]
|
||||
|
||||
if len(parts) == 1 {
|
||||
parts := strings.SplitN(module, replaceSplit, 2)
|
||||
if len(parts) > 1 {
|
||||
module = parts[0]
|
||||
replace = parts[1]
|
||||
}
|
||||
} else {
|
||||
version = parts[1]
|
||||
parts := strings.SplitN(version, replaceSplit, 2)
|
||||
if len(parts) > 1 {
|
||||
version = parts[0]
|
||||
replace = parts[1]
|
||||
}
|
||||
}
|
||||
|
||||
if module == "" {
|
||||
err = fmt.Errorf("module name is required")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
73
cmd/xcaddy/main_test.go
Normal file
73
cmd/xcaddy/main_test.go
Normal file
|
@ -0,0 +1,73 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSplitWith(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
input string
|
||||
expectModule string
|
||||
expectVersion string
|
||||
expectReplace string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
input: "module",
|
||||
expectModule: "module",
|
||||
},
|
||||
{
|
||||
input: "module@version",
|
||||
expectModule: "module",
|
||||
expectVersion: "version",
|
||||
},
|
||||
{
|
||||
input: "module@version=replace",
|
||||
expectModule: "module",
|
||||
expectVersion: "version",
|
||||
expectReplace: "replace",
|
||||
},
|
||||
{
|
||||
input: "module=replace",
|
||||
expectModule: "module",
|
||||
expectReplace: "replace",
|
||||
},
|
||||
{
|
||||
input: "=replace",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
input: "@version",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
input: "@version=replace",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
input: "",
|
||||
expectErr: true,
|
||||
},
|
||||
} {
|
||||
actualModule, actualVersion, actualReplace, actualErr := splitWith(tc.input)
|
||||
if actualModule != tc.expectModule {
|
||||
t.Errorf("Test %d: Expected module '%s' but got '%s' (input=%s)",
|
||||
i, tc.expectModule, actualModule, tc.input)
|
||||
}
|
||||
if tc.expectErr {
|
||||
if actualErr == nil {
|
||||
t.Errorf("Test %d: Expected error but did not get one (input='%s')", i, tc.input)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !tc.expectErr && actualErr != nil {
|
||||
t.Errorf("Test %d: Expected no error but got: %s (input='%s')", i, actualErr, tc.input)
|
||||
}
|
||||
if actualVersion != tc.expectVersion {
|
||||
t.Errorf("Test %d: Expected version '%s' but got '%s' (input='%s')",
|
||||
i, tc.expectVersion, actualVersion, tc.input)
|
||||
}
|
||||
if actualReplace != tc.expectReplace {
|
||||
t.Errorf("Test %d: Expected module '%s' but got '%s' (input='%s')",
|
||||
i, tc.expectReplace, actualReplace, tc.input)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -126,7 +126,7 @@ func (b Builder) newEnvironment(ctx context.Context) (*environment, error) {
|
|||
|
||||
// pin versions by populating go.mod, first for Caddy itself and then plugins
|
||||
log.Println("[INFO] Pinning versions")
|
||||
err = env.execGoGet(ctx, caddyModulePath, b.CaddyVersion)
|
||||
err = env.execGoGet(ctx, caddyModulePath, env.caddyVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue