From 3bd37b598cbe54c57dd60d6af6207eee19f3d458 Mon Sep 17 00:00:00 2001 From: Patrick Dawkins Date: Sun, 15 Mar 2026 21:52:49 +0000 Subject: [PATCH 1/6] ci: add installer.sh test automation Add a GitHub Actions workflow that tests installer.sh across distros: - Linux: Debian (apt), Ubuntu (apt), Fedora (yum), Alpine (apk), Debian (raw) - macOS: homebrew and raw methods Also add a local Docker-based test script (scripts/test/installer.sh) for quick iteration without pushing to CI. Tests run against published packages/releases. The installer script itself comes from the current commit so changes to it are tested even though the installed packages come from live repos. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/test-installer.yml | 88 ++++++++++++++++++++++++++++ scripts/test/installer.sh | 61 +++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 .github/workflows/test-installer.yml create mode 100755 scripts/test/installer.sh diff --git a/.github/workflows/test-installer.yml b/.github/workflows/test-installer.yml new file mode 100644 index 000000000..1f6425352 --- /dev/null +++ b/.github/workflows/test-installer.yml @@ -0,0 +1,88 @@ +name: Test installer.sh + +on: + workflow_dispatch: + push: + branches: + - main + paths: + - installer.sh + pull_request: + paths: + - installer.sh + +permissions: + contents: read + +jobs: + linux: + runs-on: ubuntu-latest + container: ${{ matrix.image }} + + strategy: + fail-fast: false + matrix: + include: + - image: debian:bookworm + method: apt + - image: ubuntu:24.04 + method: apt + - image: fedora:41 + method: yum + - image: alpine:3.21 + method: apk + - image: debian:bookworm + method: raw + + steps: + - name: Install prerequisites + run: | + case "${{ matrix.method }}" in + apt) + apt-get update && apt-get install -y curl ca-certificates + ;; + yum) + yum install -y curl + ;; + apk) + apk add --no-cache curl ca-certificates + ;; + raw) + apt-get update && apt-get install -y curl ca-certificates gzip + ;; + esac + + - name: Check out repository + uses: actions/checkout@v4 + + - name: Run installer + env: + INSTALL_METHOD: ${{ matrix.method }} + GITHUB_TOKEN: ${{ github.token }} + run: sh installer.sh + + - name: Verify installation + run: upsun --version + + macos: + runs-on: macos-latest + + strategy: + fail-fast: false + matrix: + method: + - homebrew + - raw + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Run installer + env: + INSTALL_METHOD: ${{ matrix.method }} + GITHUB_TOKEN: ${{ github.token }} + run: sh installer.sh + + - name: Verify installation + run: upsun --version diff --git a/scripts/test/installer.sh b/scripts/test/installer.sh new file mode 100755 index 000000000..fb7a4c777 --- /dev/null +++ b/scripts/test/installer.sh @@ -0,0 +1,61 @@ +#!/bin/sh +# Test installer.sh across Linux distros using Docker. +# Requires Docker to be installed and running. + +set -eu + +cd "$(dirname "$0")/../.." + +pass=0 +fail=0 +errors="" + +run_test() { + image="$1" + method="$2" + prereqs="$3" + + label="${image} (${method})" + printf "Testing %-35s " "$label" + + if docker run --rm \ + -v "$(pwd)/installer.sh:/installer.sh:ro" \ + -e INSTALL_METHOD="$method" \ + "$image" \ + sh -c "${prereqs} sh /installer.sh && upsun --version" \ + >/dev/null 2>&1; then + printf "PASS\n" + pass=$((pass + 1)) + else + printf "FAIL\n" + fail=$((fail + 1)) + errors="${errors} ${label}\n" + fi +} + +echo "installer.sh test suite" +echo "=======================" +echo "" + +run_test "debian:bookworm" "apt" \ + "apt-get update && apt-get install -y curl ca-certificates && " + +run_test "ubuntu:24.04" "apt" \ + "apt-get update && apt-get install -y curl ca-certificates && " + +run_test "fedora:41" "yum" \ + "yum install -y curl && " + +run_test "alpine:3.21" "apk" \ + "apk add --no-cache curl ca-certificates && " + +run_test "debian:bookworm" "raw" \ + "apt-get update && apt-get install -y curl ca-certificates gzip && " + +echo "" +echo "Results: ${pass} passed, ${fail} failed" + +if [ "$fail" -gt 0 ]; then + printf "\nFailed:\n%b" "$errors" + exit 1 +fi From 86cbbf0e32b98a64a26c5dbd1b41aa00c01ac125 Mon Sep 17 00:00:00 2001 From: Patrick Dawkins Date: Sun, 15 Mar 2026 21:55:22 +0000 Subject: [PATCH 2/6] fix(scripts): show Docker output in installer test script Show all Docker output (installer progress and errors) instead of suppressing it. Use -qq/-q flags on package managers to reduce noise from prerequisite installs while keeping installer output visible. Add a comment noting that macOS methods are CI-only. Co-Authored-By: Claude Opus 4.6 (1M context) --- scripts/test/installer.sh | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/scripts/test/installer.sh b/scripts/test/installer.sh index fb7a4c777..09bbab86d 100755 --- a/scripts/test/installer.sh +++ b/scripts/test/installer.sh @@ -1,6 +1,7 @@ #!/bin/sh # Test installer.sh across Linux distros using Docker. # Requires Docker to be installed and running. +# macOS methods (homebrew, raw) are only tested in CI (GitHub Actions). set -eu @@ -16,18 +17,18 @@ run_test() { prereqs="$3" label="${image} (${method})" - printf "Testing %-35s " "$label" + echo "" + echo "=== ${label} ===" if docker run --rm \ -v "$(pwd)/installer.sh:/installer.sh:ro" \ -e INSTALL_METHOD="$method" \ "$image" \ - sh -c "${prereqs} sh /installer.sh && upsun --version" \ - >/dev/null 2>&1; then - printf "PASS\n" + sh -c "${prereqs}sh /installer.sh && upsun --version"; then + echo "--- PASS: ${label} ---" pass=$((pass + 1)) else - printf "FAIL\n" + echo "--- FAIL: ${label} ---" fail=$((fail + 1)) errors="${errors} ${label}\n" fi @@ -35,24 +36,24 @@ run_test() { echo "installer.sh test suite" echo "=======================" -echo "" run_test "debian:bookworm" "apt" \ - "apt-get update && apt-get install -y curl ca-certificates && " + "apt-get update -qq && apt-get install -y -qq curl ca-certificates && " run_test "ubuntu:24.04" "apt" \ - "apt-get update && apt-get install -y curl ca-certificates && " + "apt-get update -qq && apt-get install -y -qq curl ca-certificates && " run_test "fedora:41" "yum" \ - "yum install -y curl && " + "yum install -y -q curl && " run_test "alpine:3.21" "apk" \ - "apk add --no-cache curl ca-certificates && " + "apk add -q --no-cache curl ca-certificates && " run_test "debian:bookworm" "raw" \ - "apt-get update && apt-get install -y curl ca-certificates gzip && " + "apt-get update -qq && apt-get install -y -qq curl ca-certificates gzip && " echo "" +echo "=======================" echo "Results: ${pass} passed, ${fail} failed" if [ "$fail" -gt 0 ]; then From 2899ea1eb7bbb8fbe177865c844393785955105e Mon Sep 17 00:00:00 2001 From: Patrick Dawkins Date: Sun, 15 Mar 2026 21:57:27 +0000 Subject: [PATCH 3/6] feat: add VERSION option to installer test script and workflow The raw install method needs /releases/latest on GitHub, which only returns non-prerelease versions. When only pre-releases exist, this fails. Allow passing a specific version to work around this. Local script: VERSION=5.0.4 scripts/test/installer.sh Workflow: workflow_dispatch input field Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/test-installer.yml | 7 +++++++ scripts/test/installer.sh | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/.github/workflows/test-installer.yml b/.github/workflows/test-installer.yml index 1f6425352..03366dd9d 100644 --- a/.github/workflows/test-installer.yml +++ b/.github/workflows/test-installer.yml @@ -2,6 +2,11 @@ name: Test installer.sh on: workflow_dispatch: + inputs: + version: + description: 'CLI version to install (leave empty for latest release)' + required: false + type: string push: branches: - main @@ -59,6 +64,7 @@ jobs: env: INSTALL_METHOD: ${{ matrix.method }} GITHUB_TOKEN: ${{ github.token }} + VERSION: ${{ inputs.version }} run: sh installer.sh - name: Verify installation @@ -82,6 +88,7 @@ jobs: env: INSTALL_METHOD: ${{ matrix.method }} GITHUB_TOKEN: ${{ github.token }} + VERSION: ${{ inputs.version }} run: sh installer.sh - name: Verify installation diff --git a/scripts/test/installer.sh b/scripts/test/installer.sh index 09bbab86d..6d4e7801f 100755 --- a/scripts/test/installer.sh +++ b/scripts/test/installer.sh @@ -7,6 +7,8 @@ set -eu cd "$(dirname "$0")/../.." +: "${VERSION:=}" + pass=0 fail=0 errors="" @@ -20,9 +22,15 @@ run_test() { echo "" echo "=== ${label} ===" + version_flag="" + if [ -n "$VERSION" ]; then + version_flag="-e VERSION=$VERSION" + fi + if docker run --rm \ -v "$(pwd)/installer.sh:/installer.sh:ro" \ -e INSTALL_METHOD="$method" \ + $version_flag \ "$image" \ sh -c "${prereqs}sh /installer.sh && upsun --version"; then echo "--- PASS: ${label} ---" From 537e76e0512c30e74d7badaa9fc6302c68e825cd Mon Sep 17 00:00:00 2001 From: Patrick Dawkins Date: Fri, 10 Apr 2026 11:46:57 +0200 Subject: [PATCH 4/6] fix(ci): address review comments on installer test PR - Set shell: sh for linux job (Alpine lacks bash) - Quote VERSION env var properly in Docker script - Only pass GITHUB_TOKEN for the raw install method Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/test-installer.yml | 8 +++++--- scripts/test/installer.sh | 7 +------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-installer.yml b/.github/workflows/test-installer.yml index 03366dd9d..fb7b8b502 100644 --- a/.github/workflows/test-installer.yml +++ b/.github/workflows/test-installer.yml @@ -23,7 +23,9 @@ jobs: linux: runs-on: ubuntu-latest container: ${{ matrix.image }} - + defaults: + run: + shell: sh strategy: fail-fast: false matrix: @@ -63,7 +65,7 @@ jobs: - name: Run installer env: INSTALL_METHOD: ${{ matrix.method }} - GITHUB_TOKEN: ${{ github.token }} + GITHUB_TOKEN: ${{ matrix.method == 'raw' && github.token || '' }} VERSION: ${{ inputs.version }} run: sh installer.sh @@ -87,7 +89,7 @@ jobs: - name: Run installer env: INSTALL_METHOD: ${{ matrix.method }} - GITHUB_TOKEN: ${{ github.token }} + GITHUB_TOKEN: ${{ matrix.method == 'raw' && github.token || '' }} VERSION: ${{ inputs.version }} run: sh installer.sh diff --git a/scripts/test/installer.sh b/scripts/test/installer.sh index 6d4e7801f..5129abfcb 100755 --- a/scripts/test/installer.sh +++ b/scripts/test/installer.sh @@ -22,15 +22,10 @@ run_test() { echo "" echo "=== ${label} ===" - version_flag="" - if [ -n "$VERSION" ]; then - version_flag="-e VERSION=$VERSION" - fi - if docker run --rm \ -v "$(pwd)/installer.sh:/installer.sh:ro" \ -e INSTALL_METHOD="$method" \ - $version_flag \ + -e "VERSION=${VERSION}" \ "$image" \ sh -c "${prereqs}sh /installer.sh && upsun --version"; then echo "--- PASS: ${label} ---" From fb9b12254bae551162b2ca3aabd7f02def6d4131 Mon Sep 17 00:00:00 2001 From: Patrick Dawkins Date: Fri, 10 Apr 2026 12:10:02 +0200 Subject: [PATCH 5/6] refactor(ci): test default installer flow per distro Restructure tests to focus on the default (auto-detected) install method for each distro, rather than forcing INSTALL_METHOD. This better reflects what users actually experience. - Linux default tests: override CI="" so the installer auto-detects the package manager (apt/yum/apk) instead of always selecting raw - Separate linux-raw and macos-raw jobs for the raw download path - Raw tests accept a VERSION input for testing prereleases - Local test script: default tests have no INSTALL_METHOD; raw test runs only when VERSION is set Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/test-installer.yml | 83 +++++++++++++++++----------- scripts/test/installer.sh | 32 +++++++---- 2 files changed, 72 insertions(+), 43 deletions(-) diff --git a/.github/workflows/test-installer.yml b/.github/workflows/test-installer.yml index fb7b8b502..3ea7fdb22 100644 --- a/.github/workflows/test-installer.yml +++ b/.github/workflows/test-installer.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: inputs: version: - description: 'CLI version to install (leave empty for latest release)' + description: 'CLI version to install for raw tests (leave empty for latest release)' required: false type: string push: @@ -12,14 +12,19 @@ on: - main paths: - installer.sh + - .github/workflows/test-installer.yml pull_request: paths: - installer.sh + - .github/workflows/test-installer.yml permissions: contents: read jobs: + # Default install flow: the installer auto-detects the package manager. + # CI is overridden to empty so that is_ci() returns false (otherwise + # the installer would always select the "raw" method on Linux). linux: runs-on: ubuntu-latest container: ${{ matrix.image }} @@ -31,66 +36,80 @@ jobs: matrix: include: - image: debian:bookworm - method: apt + prereqs: apt-get update && apt-get install -y curl ca-certificates - image: ubuntu:24.04 - method: apt + prereqs: apt-get update && apt-get install -y curl ca-certificates - image: fedora:41 - method: yum + prereqs: yum install -y curl - image: alpine:3.21 - method: apk - - image: debian:bookworm - method: raw + prereqs: apk add --no-cache curl ca-certificates + + steps: + - name: Install prerequisites + run: ${{ matrix.prereqs }} + - name: Check out repository + uses: actions/checkout@v4 + + - name: Run installer + env: + CI: "" + run: sh installer.sh + + - name: Verify installation + run: upsun --version + + # Raw install on Linux: setting VERSION triggers the raw method. + # Without a VERSION input the installer queries the GitHub API for the + # latest release. + linux-raw: + runs-on: ubuntu-latest + container: debian:bookworm + defaults: + run: + shell: sh steps: - name: Install prerequisites - run: | - case "${{ matrix.method }}" in - apt) - apt-get update && apt-get install -y curl ca-certificates - ;; - yum) - yum install -y curl - ;; - apk) - apk add --no-cache curl ca-certificates - ;; - raw) - apt-get update && apt-get install -y curl ca-certificates gzip - ;; - esac + run: apt-get update && apt-get install -y curl ca-certificates gzip - name: Check out repository uses: actions/checkout@v4 - name: Run installer env: - INSTALL_METHOD: ${{ matrix.method }} - GITHUB_TOKEN: ${{ matrix.method == 'raw' && github.token || '' }} VERSION: ${{ inputs.version }} + GITHUB_TOKEN: ${{ github.token }} run: sh installer.sh - name: Verify installation run: upsun --version + # Default install flow on macOS (auto-detects homebrew). macos: runs-on: macos-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 - strategy: - fail-fast: false - matrix: - method: - - homebrew - - raw + - name: Run installer + run: sh installer.sh + - name: Verify installation + run: upsun --version + + # Raw install on macOS (INSTALL_METHOD is required because VERSION + # does not affect macOS method selection). + macos-raw: + runs-on: macos-latest steps: - name: Check out repository uses: actions/checkout@v4 - name: Run installer env: - INSTALL_METHOD: ${{ matrix.method }} - GITHUB_TOKEN: ${{ matrix.method == 'raw' && github.token || '' }} + INSTALL_METHOD: raw VERSION: ${{ inputs.version }} + GITHUB_TOKEN: ${{ github.token }} run: sh installer.sh - name: Verify installation diff --git a/scripts/test/installer.sh b/scripts/test/installer.sh index 5129abfcb..a76384bad 100755 --- a/scripts/test/installer.sh +++ b/scripts/test/installer.sh @@ -13,19 +13,20 @@ pass=0 fail=0 errors="" +# Run a test in a Docker container. +# Arguments: label, image, prereqs, [extra docker args...] run_test() { - image="$1" - method="$2" + label="$1" + image="$2" prereqs="$3" + shift 3 - label="${image} (${method})" echo "" echo "=== ${label} ===" if docker run --rm \ -v "$(pwd)/installer.sh:/installer.sh:ro" \ - -e INSTALL_METHOD="$method" \ - -e "VERSION=${VERSION}" \ + "$@" \ "$image" \ sh -c "${prereqs}sh /installer.sh && upsun --version"; then echo "--- PASS: ${label} ---" @@ -40,20 +41,29 @@ run_test() { echo "installer.sh test suite" echo "=======================" -run_test "debian:bookworm" "apt" \ +# Default flow tests: let the installer auto-detect the install method. + +run_test "debian:bookworm (default)" "debian:bookworm" \ "apt-get update -qq && apt-get install -y -qq curl ca-certificates && " -run_test "ubuntu:24.04" "apt" \ +run_test "ubuntu:24.04 (default)" "ubuntu:24.04" \ "apt-get update -qq && apt-get install -y -qq curl ca-certificates && " -run_test "fedora:41" "yum" \ +run_test "fedora:41 (default)" "fedora:41" \ "yum install -y -q curl && " -run_test "alpine:3.21" "apk" \ +run_test "alpine:3.21 (default)" "alpine:3.21" \ "apk add -q --no-cache curl ca-certificates && " -run_test "debian:bookworm" "raw" \ - "apt-get update -qq && apt-get install -y -qq curl ca-certificates gzip && " +# Raw install test: setting VERSION triggers the raw method on Linux. +if [ -n "$VERSION" ]; then + run_test "debian:bookworm (raw, VERSION=$VERSION)" "debian:bookworm" \ + "apt-get update -qq && apt-get install -y -qq curl ca-certificates gzip && " \ + -e "VERSION=${VERSION}" +else + echo "" + echo "Skipping raw install test (set VERSION to enable)" +fi echo "" echo "=======================" From 863253a754bf961f0c4a427e5dcea974ad6ce244 Mon Sep 17 00:00:00 2001 From: Patrick Dawkins Date: Fri, 10 Apr 2026 12:15:06 +0200 Subject: [PATCH 6/6] ci: update actions/checkout to v6 Resolves Node.js 20 deprecation warnings. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/test-installer.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-installer.yml b/.github/workflows/test-installer.yml index 3ea7fdb22..b645cc5f3 100644 --- a/.github/workflows/test-installer.yml +++ b/.github/workflows/test-installer.yml @@ -49,7 +49,7 @@ jobs: run: ${{ matrix.prereqs }} - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Run installer env: @@ -73,7 +73,7 @@ jobs: run: apt-get update && apt-get install -y curl ca-certificates gzip - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Run installer env: @@ -89,7 +89,7 @@ jobs: runs-on: macos-latest steps: - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Run installer run: sh installer.sh @@ -103,7 +103,7 @@ jobs: runs-on: macos-latest steps: - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Run installer env: