Skip to content

Commit

Permalink
Update CLI; change command to 'xcaddy'; update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
mholt committed Mar 25, 2020
1 parent 40c0796 commit 1cc8e08
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 105 deletions.
47 changes: 39 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ This package and associated CLI tool make it easy to perform custom builds of th

Supports Caddy 2 and up.

⚠️ Still in early development. Works; but no guarantees and prone to changes at this point. Stay updated and please submit feedback!

## Requirements

- Go installed
Expand All @@ -14,9 +16,9 @@ Supports Caddy 2 and up.
## Library usage

```go
caddyVersion := "v2.0.0-beta.19"
plugins := []builder.CaddyPlugin{
builder.CaddyPlugin{
caddyVersion := "v2.0.0-beta.20"
plugins := []builder.Dependency{
builder.Dependency{
ModulePath: "github.com/caddyserver/nginx-adapter",
Version: "6c484552e630ccac384d2d9c43c9d14c4e8d2e56",
},
Expand All @@ -31,23 +33,52 @@ Versions can be anything compatible with `go get`.

## CLI usage

The CLI can be used both to make custom builds of Caddy, but also as a replacement for `go run` while developing Caddy plugins.

### For custom builds

Syntax:

```
builder --version <version> [--output <file>] [<plugins...>]
xcaddy build <version>
[--output <file>]
[--with <module[@version]>...]
```

Where:

- `--version` is the core Caddy version to build (required).
- `--version` is the core Caddy version to build (required, for now).
- `--output` changes the output file.
- `<plugins...>` are extra plugins and their versions, in `go get` module syntax: `module@version`
- `--with` can be used multiple times to add plugins by specifying the module name and optionally its version, in a way similar to `go get`.

For example:

```bash
$ xcaddy build v2.0.0-beta.20 \
--with github.com/caddyserver/nginx-adapter@6c484552e630ccac384d2d9c43c9d14c4e8d2e56
```

### For plugin development

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, similar to if you manually plugged it in and ran `go run`.

The binary will be built, run from the current directory, then cleaned up.

Syntax:

```
xcaddy <args...>
```

Where:

- `<args...>` are passed through to the `caddy` command.

For example:

```bash
$ builder --version v2.0.0-beta.19 \
github.com/caddyserver/nginx-adapter@6c484552e630ccac384d2d9c43c9d14c4e8d2e56
$ xcaddy list-modules
$ xcaddy run --config caddy.json
```


Expand Down
15 changes: 9 additions & 6 deletions builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"fmt"
"io/ioutil"
"log"
"os"
"path"
"path/filepath"
"regexp"
Expand All @@ -29,9 +30,9 @@ import (
"github.com/Masterminds/semver/v3"
)

// Build builds Caddy at the given version with the given plugins and plops
// binary down at outputFile.
func Build(caddyVersion string, plugins []CaddyPlugin, outputFile string) error {
// Build builds Caddy at the given version with the given plugins and
// plops a binary down at outputFile.
func Build(caddyVersion string, deps []Dependency, outputFile string) error {
if caddyVersion == "" {
return fmt.Errorf("caddy version is required")
}
Expand All @@ -47,7 +48,7 @@ func Build(caddyVersion string, plugins []CaddyPlugin, outputFile string) error
return err
}

env, err := newEnvironment(caddyVersion, plugins)
env, err := newEnvironment(caddyVersion, deps)
if err != nil {
return err
}
Expand All @@ -60,6 +61,7 @@ func Build(caddyVersion string, plugins []CaddyPlugin, outputFile string) error
"-ldflags", "-w -s", // trim debug symbols
"-trimpath",
)
cmd.Env = append(os.Environ(), "CGO_ENABLED=0")
err = env.runCommand(cmd, 5*time.Minute)
if err != nil {
return err
Expand All @@ -70,10 +72,11 @@ func Build(caddyVersion string, plugins []CaddyPlugin, outputFile string) error
return nil
}

// CaddyPlugin pairs a Go module path with a version.
type CaddyPlugin struct {
// Dependency pairs a Go module path with a version.
type Dependency struct {
ModulePath string
Version string
Replace string
}

// newTempFolder creates a new folder in a temporary location.
Expand Down
89 changes: 0 additions & 89 deletions cmd/builder/main.go

This file was deleted.

168 changes: 168 additions & 0 deletions cmd/xcaddy/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// Copyright 2020 Matthew Holt
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"

"github.com/caddyserver/builder"
)

func main() {
if len(os.Args) > 1 && os.Args[1] == "build" {
if err := runBuild(os.Args[2:]); err != nil {
log.Fatalf("[ERROR] %v", err)
}
return
}

// TODO: the caddy version needs to be settable by the user... maybe an env var?
if err := runDev("v2.0.0-beta.20", os.Args[1:]); err != nil {
log.Fatalf("[ERROR] %v", err)
}
}

func runBuild(args []string) error {
// parse the command line args... rather primitively
var caddyVersion, output string
var plugins []builder.Dependency
for i := 0; i < len(args); i++ {
switch args[i] {
case "--with":
if i == len(args)-1 {
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]
}
plugins = append(plugins, builder.Dependency{
ModulePath: mod,
Version: ver,
})

case "--output":
if i == len(args)-1 {
return fmt.Errorf("expected value after --output flag")
}
i++
output = args[i+1]

default:
if caddyVersion != "" {
return fmt.Errorf("missing flag; caddy version already set at %s", caddyVersion)
}
caddyVersion = args[i]
}
}

// ensure an output file is always specified
if output == "" {
if runtime.GOOS == "windows" {
output = "caddy.exe"
} else {
output = "caddy"
}
}

// perform the build
err := builder.Build(caddyVersion, plugins, output)
if err != nil {
log.Fatalf("[FATAL] %v", err)
}

// prove the build is working by printing the version
if !filepath.IsAbs(output) {
output = "." + string(filepath.Separator) + output
}
fmt.Println()
fmt.Printf("%s version\n", output)
cmd := exec.Command(output, "version")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
log.Fatalf("[FATAL] %v", err)
}

return nil
}

func runDev(caddyVersion string, args []string) error {
const binOutput = "./caddy"

// get current/main module name
cmd := exec.Command("go", "list", "-m")
out, err := cmd.Output()
if err != nil {
return err
}
currentModule := strings.TrimSpace(string(out))

// get its root directory
cmd = exec.Command("go", "list", "-m", "-f={{.Dir}}")
out, err = cmd.Output()
if err != nil {
return err
}
moduleDir := strings.TrimSpace(string(out))

// build caddy with this module plugged in
err = builder.Build(caddyVersion, []builder.Dependency{
{
ModulePath: currentModule,
Replace: moduleDir,
},
}, binOutput)
if err != nil {
return err
}

log.Printf("[INFO] Running %v\n\n", append([]string{binOutput}, args...))

cmd = exec.Command(binOutput, args...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Start()
if err != nil {
return err
}

cleanup := func() {
err = os.Remove(binOutput)
if err != nil && !os.IsNotExist(err) {
log.Printf("[ERROR] Deleting temporary binary %s: %v", binOutput, err)
}
}
defer cleanup()
go func() {
time.Sleep(2 * time.Second)
cleanup()
}()

return cmd.Wait()
}
Loading

0 comments on commit 1cc8e08

Please sign in to comment.