feat(sunrise): print the PATH hint for the user's login shell
- new.sh reads the basename of SHELL (via ${SHELL:-} under set -u) and
prints fish_add_path "<dir>" for fish, which keeps it for new sessions,
export PATH="<dir>:$PATH" naming ~/.zshrc or ~/.bashrc for zsh and
bash, and today's generic export line with "your shell profile" for
any other, empty or unset shell
- dq_escape escapes the install directory for the target shell's double
quotes: backslash, double quote and dollar always, plus backtick and a
single-quoted ! for POSIX shells; a per-character loop, so bash 3.2
and 5.2 behave the same
- the behaviour harness defaults SHELL to empty so cases do not depend on
the developer's login shell, adds an unset-SHELL mode and covers fish,
zsh, bash, empty, unset, unknown and two metacharacter directories
- TestBootstrapEndToEnd pins SHELL=/bin/bash
- sunrise README and docs/setup/installation.md describe the per-shell
hint; the manual PATH section gains fish_add_path (go env GOPATH)/bin
This commit is contained in:
@@ -30,7 +30,7 @@ Run the one-liner from the directory that should hold both the framework checkou
|
|||||||
|
|
||||||
1. It checks the prerequisites. Go older than 1.27 or missing, or a missing git, stops the script with an install hint before anything is cloned. A missing Docker or Docker Compose plugin is only a warning, because the application needs them to run its database, not to be created.
|
1. It checks the prerequisites. Go older than 1.27 or missing, or a missing git, stops the script with an install hint before anything is cloned. A missing Docker or Docker Compose plugin is only a warning, because the application needs them to run its database, not to be created.
|
||||||
2. It clones the framework into `./summercms` and checks out the newest `vX.Y.Z` release tag, detached. When `./summercms` already exists, the script fetches tags and moves the checkout forward to the newest release only when that is a fast-forward. It refuses, without changing anything, a directory that is not a framework checkout, a checkout with changes to tracked files and a checkout that is on a branch.
|
2. It clones the framework into `./summercms` and checks out the newest `vX.Y.Z` release tag, detached. When `./summercms` already exists, the script fetches tags and moves the checkout forward to the newest release only when that is a fast-forward. It refuses, without changing anything, a directory that is not a framework checkout, a checkout with changes to tracked files and a checkout that is on a branch.
|
||||||
3. It installs the `summer` CLI from that checkout with `go install ./cmd/summer`. When the Go install directory is not on your `PATH`, the script ends with the `export PATH=...` line to run and to add to your shell profile.
|
3. It installs the `summer` CLI from that checkout with `go install ./cmd/summer`. When the Go install directory is not on your `PATH`, the script reads your login shell from `SHELL` and ends with `fish_add_path <dir>` in fish, which keeps the directory for new sessions, or with `export PATH="<dir>:$PATH"`, to add to `~/.zshrc` in zsh, `~/.bashrc` in bash or your shell profile in any other shell.
|
||||||
4. It runs `summer new demo` next to the checkout, with `--framework` pointing at it, which writes `sm-demo-app` and prints the next steps. The script itself ends with the `cd` line into the new application.
|
4. It runs `summer new demo` next to the checkout, with `--framework` pointing at it, which writes `sm-demo-app` and prints the next steps. The script itself ends with the `cd` line into the new application.
|
||||||
|
|
||||||
The script stops before cloning when the name is invalid or `sm-demo-app` already exists. Three environment variables change its defaults:
|
The script stops before cloning when the name is invalid or `sm-demo-app` already exists. Three environment variables change its defaults:
|
||||||
@@ -99,12 +99,18 @@ go install ./cmd/summer
|
|||||||
summer --help
|
summer --help
|
||||||
```
|
```
|
||||||
|
|
||||||
`go install` writes the binary to `$(go env GOPATH)/bin`, usually `~/go/bin`. If `summer --help` reports `command not found`, that directory is not on your `PATH`. Add it for the current shell, and to your shell profile to keep it:
|
`go install` writes the binary to `$(go env GOPATH)/bin`, usually `~/go/bin`. If `summer --help` reports `command not found`, that directory is not on your `PATH`. In bash or zsh, add it for the current shell, and to `~/.bashrc` or `~/.zshrc` to keep it:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
export PATH="$(go env GOPATH)/bin:$PATH"
|
export PATH="$(go env GOPATH)/bin:$PATH"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
In fish, `fish_add_path` adds it and keeps it for new sessions without a profile edit:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
fish_add_path (go env GOPATH)/bin
|
||||||
|
```
|
||||||
|
|
||||||
The tool creates, builds and watches applications, scaffolds plugins, models, migrations and admin controllers, and builds this documentation. Check that the framework compiles and its unit tests pass:
|
The tool creates, builds and watches applications, scaffolds plugins, models, migrations and admin controllers, and builds this documentation. Check that the framework compiles and its unit tests pass:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Bootstrap script that installs the summer CLI and creates a new SummerCMS applic
|
|||||||
curl -fsSL https://summercms.io/new | bash -s -- blog
|
curl -fsSL https://summercms.io/new | bash -s -- blog
|
||||||
```
|
```
|
||||||
|
|
||||||
The script checks for Go 1.27 or newer, git and Docker, clones the framework into `./summercms` at its newest release tag (or moves an existing release checkout forward to it), installs the `summer` CLI with `go install ./cmd/summer` and runs `summer new blog --framework <checkout>` next to the checkout, which writes `./sm-blog-app`. `summer new` prints the next steps; the script adds the `cd` line and, when the Go install directory is not on `PATH`, an `export PATH=...` hint.
|
The script checks for Go 1.27 or newer, git and Docker, clones the framework into `./summercms` at its newest release tag (or moves an existing release checkout forward to it), installs the `summer` CLI with `go install ./cmd/summer` and runs `summer new blog --framework <checkout>` next to the checkout, which writes `./sm-blog-app`. `summer new` prints the next steps; the script adds the `cd` line and, when the Go install directory is not on `PATH`, a hint for the login shell named by `SHELL`: `fish_add_path <dir>` for fish (which keeps it for new sessions), an `export PATH=...` line naming `~/.zshrc` or `~/.bashrc` for zsh and bash, and the generic `export PATH=...` line with "your shell profile" for any other shell.
|
||||||
|
|
||||||
The script lives inside this package so `//go:embed` can reach it, and a web server serves the bytes of the framework release it was built against. `sunrise.Script` returns them.
|
The script lives inside this package so `//go:embed` can reach it, and a web server serves the bytes of the framework release it was built against. `sunrise.Script` returns them.
|
||||||
|
|
||||||
@@ -24,6 +24,7 @@ The script lives inside this package so `//go:embed` can reach it, and a web ser
|
|||||||
- Fast-forward-only updates: an existing checkout is fetched (tags are never force-updated) and moved only when its `HEAD` is an ancestor of the target.
|
- Fast-forward-only updates: an existing checkout is fetched (tags are never force-updated) and moved only when its `HEAD` is an ancestor of the target.
|
||||||
- Refusals that protect local work: an existing directory that is not a git repository, whose `go.mod` is not the framework module, that has changes to tracked files, or that is on a branch is left untouched and the script stops with a message.
|
- Refusals that protect local work: an existing directory that is not a git repository, whose `go.mod` is not the framework module, that has changes to tracked files, or that is on a branch is left untouched and the script stops with a message.
|
||||||
- Early name check: a name that is not lower-case letters and digits starting with a letter, or an existing `sm-<name>-app`, stops the script before any clone or install.
|
- Early name check: a name that is not lower-case letters and digits starting with a letter, or an existing `sm-<name>-app`, stops the script before any clone or install.
|
||||||
|
- Login-shell-aware PATH hint: the closing hint is one correct line for fish, zsh or bash, with a generic `export PATH=...` line for any other shell; the directory is escaped for the target shell's double quotes and `$PATH` is printed literally.
|
||||||
- No privilege escalation and no dynamic code; only bash builtins plus `git`, `go` and `docker` are run, with stdin closed and `GIT_TERMINAL_PROMPT=0` so nothing waits for input.
|
- No privilege escalation and no dynamic code; only bash builtins plus `git`, `go` and `docker` are run, with stdin closed and `GIT_TERMINAL_PROMPT=0` so nothing waits for input.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -62,7 +63,7 @@ func newScript(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
The script reads three environment variables. It never reads or sets `SUMMER_`-prefixed variables, which the framework reads as configuration.
|
The script reads three environment variables, plus `SHELL`, which only picks the form of the PATH hint. It never reads or sets `SUMMER_`-prefixed variables, which the framework reads as configuration.
|
||||||
|
|
||||||
| Variable | Default | Purpose |
|
| Variable | Default | Purpose |
|
||||||
|----------|---------|---------|
|
|----------|---------|---------|
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
# application is created next to it
|
# application is created next to it
|
||||||
# SUMMERCMS_REF tag or commit to check out instead of the newest release
|
# SUMMERCMS_REF tag or commit to check out instead of the newest release
|
||||||
#
|
#
|
||||||
|
# The login shell named by SHELL only picks the form of the PATH hint printed
|
||||||
|
# at the end (fish, zsh, bash, or a generic export line).
|
||||||
|
#
|
||||||
# Everything below is a function definition and the last line calls main, so
|
# Everything below is a function definition and the last line calls main, so
|
||||||
# a download cut short runs nothing. Only bash builtins and the git, go and
|
# a download cut short runs nothing. Only bash builtins and the git, go and
|
||||||
# docker commands are used.
|
# docker commands are used.
|
||||||
@@ -199,6 +202,23 @@ go_bin_dir() {
|
|||||||
printf '%s\n' "$gobin"
|
printf '%s\n' "$gobin"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# dq_escape prints $1 escaped for the double quotes of the shell named by $2:
|
||||||
|
# backslash, double quote and dollar always; for shells other than fish also
|
||||||
|
# backtick, and ! moved into single quotes against history expansion.
|
||||||
|
dq_escape() {
|
||||||
|
local s="$1" sh="$2" out="" c i
|
||||||
|
for ((i = 0; i < ${#s}; i++)); do
|
||||||
|
c="${s:i:1}"
|
||||||
|
case "$c" in
|
||||||
|
\\ | \" | \$) out+="\\$c" ;;
|
||||||
|
\`) if [[ $sh == fish ]]; then out+="$c"; else out+="\\$c"; fi ;;
|
||||||
|
!) if [[ $sh == fish ]]; then out+="$c"; else out+="\"'!'\""; fi ;;
|
||||||
|
*) out+="$c" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
printf '%s\n' "$out"
|
||||||
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
export GIT_TERMINAL_PROMPT=0
|
export GIT_TERMINAL_PROMPT=0
|
||||||
@@ -247,10 +267,24 @@ main() {
|
|||||||
|
|
||||||
printf '\n==> Done. Start with:\n cd %s\n' "$app"
|
printf '\n==> Done. Start with:\n cd %s\n' "$app"
|
||||||
if [[ :${PATH:-}: != *":$gobin:"* ]]; then
|
if [[ :${PATH:-}: != *":$gobin:"* ]]; then
|
||||||
printf '\n==> summer is installed in %s, which is not on your PATH. Run this (and add it to your shell profile):\n' "$gobin"
|
# Under curl | bash this is always bash; SHELL still names the
|
||||||
|
# user's login shell, which picks the hint form.
|
||||||
|
local login="${SHELL:-}" note
|
||||||
|
login="${login##*/}"
|
||||||
|
case "$login" in
|
||||||
|
fish) note='fish_add_path also keeps it for new fish sessions' ;;
|
||||||
|
zsh) note='and add it to ~/.zshrc' ;;
|
||||||
|
bash) note='and add it to ~/.bashrc' ;;
|
||||||
|
*) note='and add it to your shell profile' ;;
|
||||||
|
esac
|
||||||
|
printf '\n==> summer is installed in %s, which is not on your PATH. Run this (%s):\n' "$gobin" "$note"
|
||||||
|
if [[ $login == fish ]]; then
|
||||||
|
printf ' fish_add_path "%s"\n' "$(dq_escape "$gobin" fish)"
|
||||||
|
else
|
||||||
# $PATH stays literal: the user's shell expands it.
|
# $PATH stays literal: the user's shell expands it.
|
||||||
# shellcheck disable=SC2016
|
# shellcheck disable=SC2016
|
||||||
printf ' export PATH="%s:$PATH"\n' "$gobin"
|
printf ' export PATH="%s:$PATH"\n' "$(dq_escape "$gobin" "$login")"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ type scriptHarness struct {
|
|||||||
goVersion string // go env GOVERSION
|
goVersion string // go env GOVERSION
|
||||||
useGOBIN bool // go env GOBIN prints gobin; otherwise empty, GOPATH/bin is used
|
useGOBIN bool // go env GOBIN prints gobin; otherwise empty, GOPATH/bin is used
|
||||||
gobinOnPath bool
|
gobinOnPath bool
|
||||||
|
unsetShell bool // run the script with SHELL unset instead of the empty default
|
||||||
env []string // extra environment
|
env []string // extra environment
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,6 +244,10 @@ func (h *scriptHarness) run(args ...string) scriptResult {
|
|||||||
"STUB_GOVERSION="+h.goVersion,
|
"STUB_GOVERSION="+h.goVersion,
|
||||||
"STUB_GOBIN="+gobin,
|
"STUB_GOBIN="+gobin,
|
||||||
"STUB_GOPATH="+h.gopath,
|
"STUB_GOPATH="+h.gopath,
|
||||||
|
// bash fills an unset SHELL from the passwd entry, so this default
|
||||||
|
// keeps cases independent of the developer's login shell; a case
|
||||||
|
// sets its own SHELL through h.env, which comes later and wins.
|
||||||
|
"SHELL=",
|
||||||
)
|
)
|
||||||
// The script must not depend on the harness identity: drop it.
|
// The script must not depend on the harness identity: drop it.
|
||||||
filtered := env[:0]
|
filtered := env[:0]
|
||||||
@@ -254,6 +259,11 @@ func (h *scriptHarness) run(args ...string) scriptResult {
|
|||||||
}
|
}
|
||||||
env = append(filtered, h.env...)
|
env = append(filtered, h.env...)
|
||||||
cmd := exec.Command(h.bash, append([]string{h.script}, args...)...)
|
cmd := exec.Command(h.bash, append([]string{h.script}, args...)...)
|
||||||
|
if h.unsetShell {
|
||||||
|
// Unset SHELL inside bash, after startup filled it, then source the
|
||||||
|
// script ($0) with the case arguments so it runs with SHELL unset.
|
||||||
|
cmd = exec.Command(h.bash, append([]string{"-c", `unset SHELL; . "$0" "$@"`, h.script}, args...)...)
|
||||||
|
}
|
||||||
cmd.Dir = h.work
|
cmd.Dir = h.work
|
||||||
cmd.Env = env
|
cmd.Env = env
|
||||||
cmd.Stdin = strings.NewReader(stdinProbe)
|
cmd.Stdin = strings.NewReader(stdinProbe)
|
||||||
@@ -319,6 +329,57 @@ func expectContains(t *testing.T, what, got string, wants ...string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func expectLacks(t *testing.T, what, got string, unwanted ...string) {
|
||||||
|
t.Helper()
|
||||||
|
for _, u := range unwanted {
|
||||||
|
if strings.Contains(got, u) {
|
||||||
|
t.Fatalf("%s contains %q:\n%s", what, u, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hintDir returns dir escaped the way new.sh escapes it inside the double
|
||||||
|
// quotes of the printed PATH hint: backslash, double quote and dollar for
|
||||||
|
// every shell, plus backtick and a single-quoted ! for POSIX shells.
|
||||||
|
func hintDir(dir string, fish bool) string {
|
||||||
|
if fish {
|
||||||
|
return strings.NewReplacer(`\`, `\\`, `"`, `\"`, `$`, `\$`).Replace(dir)
|
||||||
|
}
|
||||||
|
return strings.NewReplacer(`\`, `\\`, `"`, `\"`, `$`, `\$`, "`", "\\`", `!`, `"'!'"`).Replace(dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hint is the PATH hint new.sh prints at the end of stdout.
|
||||||
|
func hint(gobin, note, line string) string {
|
||||||
|
return "\n==> summer is installed in " + gobin + ", which is not on your PATH. Run this (" + note + "):\n" + line + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
// exportHint and fishHint are the two hint forms for gobin.
|
||||||
|
func exportHint(gobin, note string) string {
|
||||||
|
return hint(gobin, note, ` export PATH="`+hintDir(gobin, false)+`:$PATH"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fishHint(gobin string) string {
|
||||||
|
return hint(gobin, "fish_add_path also keeps it for new fish sessions", ` fish_add_path "`+hintDir(gobin, true)+`"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// expectHintSuffix asserts stdout ends with the hint.
|
||||||
|
func expectHintSuffix(t *testing.T, stdout, want string) {
|
||||||
|
t.Helper()
|
||||||
|
if !strings.HasSuffix(stdout, want) {
|
||||||
|
t.Fatalf("stdout does not end with the hint\ngot:\n%q\nwant suffix:\n%q", stdout, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// metaGobin creates and selects a Go bin directory whose name holds a
|
||||||
|
// space, backslash, double quote, dollar, backtick and !.
|
||||||
|
func metaGobin(t *testing.T, h *scriptHarness) {
|
||||||
|
t.Helper()
|
||||||
|
h.gobin = filepath.Join(h.root, "go bin\\x\"y$HOME`id`!z")
|
||||||
|
if err := os.MkdirAll(h.gobin, 0o755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// expectNothingStarted asserts no clone and no go or summer call happened.
|
// expectNothingStarted asserts no clone and no go or summer call happened.
|
||||||
func expectNothingStarted(t *testing.T, h *scriptHarness) {
|
func expectNothingStarted(t *testing.T, h *scriptHarness) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
@@ -583,16 +644,61 @@ func TestScriptBehavior(t *testing.T) {
|
|||||||
t.Fatalf("the script created .git: %v", err)
|
t.Fatalf("the script created .git: %v", err)
|
||||||
}
|
}
|
||||||
}},
|
}},
|
||||||
{"GOBIN off PATH prints the hint", func(t *testing.T, h *scriptHarness) {
|
{"empty SHELL prints the generic hint", func(t *testing.T, h *scriptHarness) {
|
||||||
res := h.mustRun("demo")
|
res := h.mustRun("demo")
|
||||||
expectContains(t, "stdout", res.stdout, "not on your PATH", `export PATH="`+h.gobin+`:$PATH"`)
|
expectHintSuffix(t, res.stdout, "\n==> summer is installed in "+h.gobin+", which is not on your PATH. Run this (and add it to your shell profile):\n export PATH=\""+h.gobin+":$PATH\"\n")
|
||||||
|
}},
|
||||||
|
{"unset SHELL prints the generic hint", func(t *testing.T, h *scriptHarness) {
|
||||||
|
h.unsetShell = true
|
||||||
|
res := h.mustRun("demo")
|
||||||
|
expectLacks(t, "stderr", res.stderr, "unbound variable")
|
||||||
|
expectHintSuffix(t, res.stdout, exportHint(h.gobin, "and add it to your shell profile"))
|
||||||
|
}},
|
||||||
|
{"unknown SHELL prints the generic hint", func(t *testing.T, h *scriptHarness) {
|
||||||
|
h.env = append(h.env, "SHELL=/bin/tcsh")
|
||||||
|
res := h.mustRun("demo")
|
||||||
|
expectHintSuffix(t, res.stdout, exportHint(h.gobin, "and add it to your shell profile"))
|
||||||
|
}},
|
||||||
|
{"fish SHELL prints fish_add_path", func(t *testing.T, h *scriptHarness) {
|
||||||
|
h.env = append(h.env, "SHELL=/usr/bin/fish")
|
||||||
|
res := h.mustRun("demo")
|
||||||
|
expectHintSuffix(t, res.stdout, fishHint(h.gobin))
|
||||||
|
expectContains(t, "stdout", res.stdout, "fish_add_path also keeps it for new fish sessions", ` fish_add_path "`+h.gobin+`"`)
|
||||||
|
expectLacks(t, "stdout", res.stdout, "export PATH", "shell profile")
|
||||||
|
}},
|
||||||
|
{"zsh SHELL names ~/.zshrc", func(t *testing.T, h *scriptHarness) {
|
||||||
|
h.env = append(h.env, "SHELL=/bin/zsh")
|
||||||
|
res := h.mustRun("demo")
|
||||||
|
expectHintSuffix(t, res.stdout, exportHint(h.gobin, "and add it to ~/.zshrc"))
|
||||||
|
expectLacks(t, "stdout", res.stdout, "fish_add_path")
|
||||||
|
}},
|
||||||
|
{"bash SHELL names ~/.bashrc", func(t *testing.T, h *scriptHarness) {
|
||||||
|
h.env = append(h.env, "SHELL=/usr/local/bin/bash")
|
||||||
|
res := h.mustRun("demo")
|
||||||
|
expectHintSuffix(t, res.stdout, exportHint(h.gobin, "and add it to ~/.bashrc"))
|
||||||
|
expectLacks(t, "stdout", res.stdout, "fish_add_path")
|
||||||
|
}},
|
||||||
|
{"metacharacters in the dir are escaped for zsh", func(t *testing.T, h *scriptHarness) {
|
||||||
|
metaGobin(t, h)
|
||||||
|
h.env = append(h.env, "SHELL=/bin/zsh")
|
||||||
|
res := h.mustRun("demo")
|
||||||
|
want := " export PATH=\"" + hintDir(h.root, false) + "/" + (`go bin\\x\"y\$HOME\` + "`" + `id\` + "`" + `"'!'"z`) + ":$PATH\"\n"
|
||||||
|
expectContains(t, "stdout", res.stdout, want)
|
||||||
|
expectHintSuffix(t, res.stdout, exportHint(h.gobin, "and add it to ~/.zshrc"))
|
||||||
|
}},
|
||||||
|
{"metacharacters in the dir are escaped for fish", func(t *testing.T, h *scriptHarness) {
|
||||||
|
metaGobin(t, h)
|
||||||
|
h.env = append(h.env, "SHELL=/usr/bin/fish")
|
||||||
|
res := h.mustRun("demo")
|
||||||
|
want := " fish_add_path \"" + hintDir(h.root, true) + "/" + (`go bin\\x\"y\$HOME` + "`id`!z") + "\"\n"
|
||||||
|
expectContains(t, "stdout", res.stdout, want)
|
||||||
|
expectHintSuffix(t, res.stdout, fishHint(h.gobin))
|
||||||
}},
|
}},
|
||||||
{"GOBIN on PATH prints no hint", func(t *testing.T, h *scriptHarness) {
|
{"GOBIN on PATH prints no hint", func(t *testing.T, h *scriptHarness) {
|
||||||
h.gobinOnPath = true
|
h.gobinOnPath = true
|
||||||
|
h.env = append(h.env, "SHELL=/usr/bin/fish")
|
||||||
res := h.mustRun("demo")
|
res := h.mustRun("demo")
|
||||||
if strings.Contains(res.stdout, "export PATH") || strings.Contains(res.stdout, "not on your PATH") {
|
expectLacks(t, "stdout", res.stdout, "export PATH", "fish_add_path", "not on your PATH")
|
||||||
t.Fatalf("hint printed although GOBIN is on PATH:\n%s", res.stdout)
|
|
||||||
}
|
|
||||||
}},
|
}},
|
||||||
{"empty GOBIN installs into GOPATH/bin", func(t *testing.T, h *scriptHarness) {
|
{"empty GOBIN installs into GOPATH/bin", func(t *testing.T, h *scriptHarness) {
|
||||||
h.useGOBIN = false
|
h.useGOBIN = false
|
||||||
@@ -605,7 +711,7 @@ func TestScriptBehavior(t *testing.T) {
|
|||||||
expectContains(t, "stdout", res.stdout, `export PATH="`+bin+`:$PATH"`)
|
expectContains(t, "stdout", res.stdout, `export PATH="`+bin+`:$PATH"`)
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
if len(cases) < 18 {
|
if len(cases) < 32 {
|
||||||
t.Fatalf("only %d cases", len(cases))
|
t.Fatalf("only %d cases", len(cases))
|
||||||
}
|
}
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
|
|||||||
@@ -154,12 +154,13 @@ func TestBootstrapEndToEnd(t *testing.T) {
|
|||||||
|
|
||||||
work := t.TempDir()
|
work := t.TempDir()
|
||||||
gobin := t.TempDir()
|
gobin := t.TempDir()
|
||||||
env := []string{"SUMMERCMS_REPO=" + root, "SUMMERCMS_REF=" + head, "GOBIN=" + gobin}
|
// The PATH hint form follows SHELL: pin bash so the export line is checked.
|
||||||
|
env := []string{"SUMMERCMS_REPO=" + root, "SUMMERCMS_REF=" + head, "GOBIN=" + gobin, "SHELL=/bin/bash"}
|
||||||
for _, kv := range os.Environ() {
|
for _, kv := range os.Environ() {
|
||||||
key, _, _ := strings.Cut(kv, "=")
|
key, _, _ := strings.Cut(kv, "=")
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(key, "SUMMER_"), strings.HasPrefix(key, "SUMMERCMS_"),
|
case strings.HasPrefix(key, "SUMMER_"), strings.HasPrefix(key, "SUMMERCMS_"),
|
||||||
key == "GOWORK", key == "GOBIN", key == "PWD", key == "GOFLAGS":
|
key == "GOWORK", key == "GOBIN", key == "PWD", key == "GOFLAGS", key == "SHELL":
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
env = append(env, kv)
|
env = append(env, kv)
|
||||||
@@ -197,6 +198,9 @@ func TestBootstrapEndToEnd(t *testing.T) {
|
|||||||
if !strings.Contains(string(out), `export PATH="`+gobin+`:$PATH"`) {
|
if !strings.Contains(string(out), `export PATH="`+gobin+`:$PATH"`) {
|
||||||
t.Fatalf("output lacks the PATH hint for %s", gobin)
|
t.Fatalf("output lacks the PATH hint for %s", gobin)
|
||||||
}
|
}
|
||||||
|
if !strings.Contains(string(out), "(and add it to ~/.bashrc):") {
|
||||||
|
t.Fatal("output lacks the ~/.bashrc note of the bash PATH hint")
|
||||||
|
}
|
||||||
|
|
||||||
b := exec.CommandContext(ctx, filepath.Join(gobin, "summer"), "build")
|
b := exec.CommandContext(ctx, filepath.Join(gobin, "summer"), "build")
|
||||||
b.Dir = app
|
b.Dir = app
|
||||||
|
|||||||
Reference in New Issue
Block a user