From 11ea8cf77b1d2a106ef4f85ef3d0f0e8d3c31a7a Mon Sep 17 00:00:00 2001 From: Austin Burdine Date: Fri, 9 Oct 2026 17:28:14 -0400 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=8E=A8=20Made=20the=20piped=20launche?= =?UTF-8?q?r=20install=20when=20no=20command=20is=20given?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The install command repeated itself: `curl .../install.sh | bash -s -- install --local` names install twice. Piped from curl, the launcher has no site of its own, so install is the only thing a first run does; the command word can default to it. Renaming install.sh would not help, as `-s --` is bash's syntax for a piped script's arguments, not ours. The default applies only when the script is read from a pipe and no command word was given. A site's own ./ghost-docker never gets one, and piped commands named explicitly, such as self-update, are unchanged. - ghost-docker: find whether the script is piped before choosing the command, and prepend install, keeping the --import bundle's index. - tests/e2e/launcher.sh: a piped run with no command installs and still mounts its bundle; a run from a file is not given a command. - .github/workflows/launcher.yml: the newest-beta check pipes with no command, so the default is exercised against the served launcher. - README.md, docs/install.md, docs/architecture.md, pages/index.html: drop install from the curl examples and describe the default. --- .github/workflows/launcher.yml | 2 +- README.md | 2 +- docs/architecture.md | 1 + docs/install.md | 21 ++++++++++++--------- ghost-docker | 25 +++++++++++++++++++------ pages/index.html | 6 +++--- tests/e2e/launcher.sh | 19 +++++++++++++++++++ 7 files changed, 56 insertions(+), 20 deletions(-) diff --git a/.github/workflows/launcher.yml b/.github/workflows/launcher.yml index d239758b..18efb185 100644 --- a/.github/workflows/launcher.yml +++ b/.github/workflows/launcher.yml @@ -145,7 +145,7 @@ jobs: run: | set -euo pipefail site=$(mktemp -d) - curl -fsSL "https://$DOMAIN/install.sh" | bash -s -- --dir "$site" install --local --no-prompt + curl -fsSL "https://$DOMAIN/install.sh" | bash -s -- --dir "$site" --local --no-prompt version=$(jq -r .stack.version "$site/.ghost-docker.json") channel=$(jq -r .channel "$site/.ghost-docker.json") [[ $version == "$TAG" && $channel == beta ]] || { diff --git a/README.md b/README.md index 34fe068b..441d80f4 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Configuration to run Ghost and its services with Docker Compose. ```sh # Linux, macOS, and Windows through WSL2; or --domain example.com --email ops@example.com -curl -fsSL https://docker.ghost.org/install.sh | bash -s -- install --local +curl -fsSL https://docker.ghost.org/install.sh | bash -s -- --local ./ghost-docker check ./ghost-docker self-update ``` diff --git a/docs/architecture.md b/docs/architecture.md index 58601651..16a3e48e 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -59,6 +59,7 @@ use native clients; bulk database operations use version-matched MySQL tools. would refer to a different machine. - External import bundles and restore backups are mounted read-only at their own paths. Prompts use the terminal even when the launcher is piped from curl. +- Piped from curl with no command word, the launcher runs `install`. - `doctor` writes a file and reads it through a sibling container mounting the same host path. Seeing the directory inside the manager alone cannot establish that the daemon sees the same files. diff --git a/docs/install.md b/docs/install.md index 138f7bfe..0e534d98 100644 --- a/docs/install.md +++ b/docs/install.md @@ -2,13 +2,16 @@ ```bash mkdir ~/my-site && cd ~/my-site -curl -fsSL https://docker.ghost.org/install.sh | bash -s -- install --local # or: --domain example.com +curl -fsSL https://docker.ghost.org/install.sh | bash -s -- --local # or: --domain example.com ``` `install.sh` is the launcher, `ghost-docker`, from the newest release. Piped -into bash it installs the newest release on the `beta` channel; `install` -writes a copy of it into the site, and every later command is -`./ghost-docker ...` from there. +into bash with no command, it runs `install` with the options given, from the +newest release on the `beta` channel; plain `curl -fsSL +https://docker.ghost.org/install.sh | bash` asks for the site mode. `install` +writes a copy of the launcher into the site, and every later command is +`./ghost-docker ...` from there. Other commands can still be piped by name: +`bash -s -- self-update`. ## Releases @@ -26,7 +29,7 @@ a beta. | The development image | `GD_CHANNEL=edge` | `edge`, built from every push to `next-docker`. Not a release. | These go to the launcher with the command: `curl -fsSL -https://docker.ghost.org/install.sh | bash -s -- install --local --release +https://docker.ghost.org/install.sh | bash -s -- --local --release v0.1.0-beta.1`. A site always runs one image digest, never a channel: `install` pins the site's launcher to the digest it ran as, and records the channel the site follows, which `self-update` uses. @@ -341,7 +344,7 @@ cd ~/sites/my-blog ghost stop ghost migrate-export --output ../my-blog-bundle mkdir ../my-blog-docker && cd ../my-blog-docker -curl -fsSL https://docker.ghost.org/install.sh | bash -s -- install --import ../my-blog-bundle --port 2368 +curl -fsSL https://docker.ghost.org/install.sh | bash -s -- --import ../my-blog-bundle --port 2368 ``` - **Stop first.** The exporter stops a running site while it copies, and @@ -399,7 +402,7 @@ ghost stop ghost migrate-export --output ~/my-site-bundle sudo systemctl stop nginx mkdir ~/my-site && cd ~/my-site -curl -fsSL https://docker.ghost.org/install.sh | bash -s -- install --import ~/my-site-bundle +curl -fsSL https://docker.ghost.org/install.sh | bash -s -- --import ~/my-site-bundle ``` - **Stop nginx yourself.** Nothing in the import stops it; it may serve other @@ -426,7 +429,7 @@ scp ~/my-site-bundle.tgz new-host: # On the new host mkdir ~/my-site && cd ~/my-site -curl -fsSL https://docker.ghost.org/install.sh | bash -s -- install --import ~/my-site-bundle.tgz +curl -fsSL https://docker.ghost.org/install.sh | bash -s -- --import ~/my-site-bundle.tgz ``` Then point the domain's DNS at the new host. Until it moves, `check` reports @@ -448,7 +451,7 @@ domain of its own (the source's admin domain is not carried to it; name the copy's own with `--admin-domain` if it needs one): ```bash -curl -fsSL https://docker.ghost.org/install.sh | bash -s -- install --import ~/my-site-bundle.tgz --domain staging.example.com --no-start +curl -fsSL https://docker.ghost.org/install.sh | bash -s -- --import ~/my-site-bundle.tgz --domain staging.example.com --no-start ``` **A copied database sends real mail.** Before starting the copy, remove the diff --git a/ghost-docker b/ghost-docker index 044864e8..3ee83ea3 100755 --- a/ghost-docker +++ b/ghost-docker @@ -3,6 +3,9 @@ # # ghost-docker [--dir PATH] [options] # ghost-docker help +# curl -fsSL https://docker.ghost.org/install.sh | bash -s -- [install options] +# +# Piped into bash with no command, the command is `install`. # # Everything ghost-docker does happens in a container, the manager image. This # script is the only part that runs on the host, and it does three things: @@ -139,6 +142,22 @@ while (($#)); do shift done +# Empty when this script is being read from a pipe (`curl ... | bash`): there is +# no file, so there is no checkout to find beside it. +script_dir="" +if [[ -n ${BASH_SOURCE[0]:-} && -f ${BASH_SOURCE[0]} ]]; then + script_dir=$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P) +fi + +# Piped with no command, it installs: `curl ... | bash` and +# `curl ... | bash -s -- --local` mean `install`, the only command that has no +# site's own copy of this script to run instead. +if [[ -z $script_dir && -z $command_word ]]; then + command_word=install + args=(install ${args[@]+"${args[@]}"}) + [[ -z $bundle_at ]] || bundle_at=$((bundle_at + 1)) +fi + # Only the release formats that are published; the manager checks them again. case $channel in '' | stable | beta) ;; @@ -241,12 +260,6 @@ fi # --- Which image ------------------------------------------------------------ -# Empty when this script is being read from a pipe (`curl ... | bash`): there is -# no file, so there is no checkout to find beside it. -script_dir="" -if [[ -n ${BASH_SOURCE[0]:-} && -f ${BASH_SOURCE[0]} ]]; then - script_dir=$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P) -fi source_kind=image # A channel is a tag that moves, and `docker run` would otherwise keep using # whichever copy of it this host pulled first. Docker's progress goes to diff --git a/pages/index.html b/pages/index.html index 4df25483..b06a0daf 100644 --- a/pages/index.html +++ b/pages/index.html @@ -76,13 +76,13 @@

Requirements

A production site

Ports 80 and 443 must be free. Make an empty directory for the site and run, from inside it:

-
curl -fsSL https://docker.ghost.org/install.sh | bash -s -- install --domain example.com --email you@example.com
+
curl -fsSL https://docker.ghost.org/install.sh | bash -s -- --domain example.com --email you@example.com

Then point the domain's DNS at this host. Caddy obtains a certificate once it does, and ./ghost-docker check reports it.

A local site

For themes, and for trying Ghost, on 127.0.0.1:

mkdir ~/my-site && cd ~/my-site
-curl -fsSL https://docker.ghost.org/install.sh | bash -s -- install --local
+curl -fsSL https://docker.ghost.org/install.sh | bash -s -- --local

Moving a Ghost-CLI site

A local Ghost-CLI site (one made with ghost install local) moves to Docker with its posts, members, staff accounts, settings, themes, images and configuration. You need Ghost-CLI 1.33.4 or later, and Ghost 6.61.0 or later in the site: the import runs the exact version the site did, and 6.61.0 is the first with a Docker image the import can use. Older sites are refused, so update first.

@@ -93,7 +93,7 @@

Moving a Ghost-CLI site

ghost stop ghost migrate-export --output ../my-blog-bundle mkdir ../my-blog-docker && cd ../my-blog-docker -curl -fsSL https://docker.ghost.org/install.sh | bash -s -- install --import ../my-blog-bundle --port 2368 +curl -fsSL https://docker.ghost.org/install.sh | bash -s -- --import ../my-blog-bundle --port 2368