diff --git a/.github/workflows/installer.yml b/.github/workflows/installer.yml index 45d2ad62..83f3e3fc 100644 --- a/.github/workflows/installer.yml +++ b/.github/workflows/installer.yml @@ -10,12 +10,14 @@ on: paths: - bin/installer.sh - .github/workflows/installer.yml + - .github/workflows/release.yml push: branches: - main paths: - bin/installer.sh - .github/workflows/installer.yml + - .github/workflows/release.yml concurrency: group: ${{ github.workflow }}-${{ github.ref || github.event.pull_request.number || github.sha }} @@ -76,6 +78,17 @@ jobs: which midenup midenup --version + # Exercise the opt-out flags: the binary must land in the requested directory, no + # verification may run, and `midenup init` must not create MIDENUP_HOME. + - name: Install with all opt-out flags + run: | + install_path="$(mktemp -d 2>/dev/null || mktemp -d -t 'install-path')" + export MIDENUP_HOME="${RUNNER_TEMP}/no-init-home" + bin/installer.sh --install-path "${install_path}" --no-init --no-verify --ignore-attestation --no-cargo-fallback 2>&1 | tee "${RUNNER_TEMP}/installer.log" + test -x "${install_path}/midenup" + if grep -q 'Verifying' "${RUNNER_TEMP}/installer.log"; then echo "verification ran despite opt-out flags"; exit 1; fi + if [ -e "${MIDENUP_HOME}" ]; then echo "init ran despite --no-init"; exit 1; fi + # Dummy job to have a stable name for the "all tests pass" requirement tests-pass: name: Install scripts test pass diff --git a/.github/workflows/publish-manifest.yml b/.github/workflows/publish-manifest.yml index 76afe130..3af1a60a 100644 --- a/.github/workflows/publish-manifest.yml +++ b/.github/workflows/publish-manifest.yml @@ -9,6 +9,7 @@ on: # Grant GITHUB_TOKEN the permissions required to make a Pages deployment permissions: + contents: read pages: write id-token: write @@ -37,3 +38,18 @@ jobs: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 + + smoke: + name: Run smoke tests + needs: deploy + if: ${{ needs.deploy.result == 'success' }} + # A called workflow can only *downgrade* the calling job's token, so this + # grant is the ceiling for every job in `smoke.yml`. Without it the + # job inherits the top-level permissions and the call is rejected as an escalation + # when the run graph is built -- which fails the whole run before any job + # starts, rather than failing this one. + permissions: + contents: read + uses: ./.github/workflows/smoke.yml + with: + release: latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ffc4d7d8..219d5fa4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,6 +44,7 @@ jobs: contents: read outputs: subject: ${{ steps.subject.outputs.sha }} + tag: ${{ steps.intent.outputs.tag }} steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: @@ -91,10 +92,14 @@ jobs: run: cargo make release lint - name: Generate the intent + id: intent run: | + set -euo pipefail cargo make release plan \ --subject "${GITHUB_SHA}" --output "${RUNNER_TEMP}/intent.json" cat "${RUNNER_TEMP}/intent.json" + tag_name=$(jq -r '.tags[0].name' < "${RUNNER_TEMP}/intent.json") + echo "tag=${tag_name}" >> "${GITHUB_OUTPUT}" - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: @@ -367,3 +372,18 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | cargo make release finalize --plan "${RUNNER_TEMP}/plan.json" + + smoke: + name: Run smoke tests + needs: [plan, finalize] + if: ${{ needs.plan.result == 'success' && needs.finalize.result == 'success' }} + # A called workflow can only *downgrade* the calling job's token, so this + # grant is the ceiling for every job in `smoke.yml`. Without it the + # job inherits the top-level permissions and the call is rejected as an escalation + # when the run graph is built -- which fails the whole run before any job + # starts, rather than failing this one. + permissions: + contents: read + uses: ./.github/workflows/smoke.yml + with: + release: ${{ needs.plan.outputs.tag }} diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml new file mode 100644 index 00000000..9c19b500 --- /dev/null +++ b/.github/workflows/smoke.yml @@ -0,0 +1,49 @@ +# Install midenup and a toolchain the way the README tells users to, from the published +# release assets and the live channel manifest. +name: smoke + +on: + workflow_call: + inputs: + release: + description: The release tag to test, or latest + required: true + type: string + +permissions: {} + +jobs: + install: + name: install from release + strategy: + fail-fast: false + matrix: + os: [macos-latest, ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: + # The `release` input is either a tag name, or the string 'latest' + - name: Run the published installer + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + if [ "${{ inputs.release }}" != "latest" ]; then + tag="${{ inputs.release }}" + url="https://github.com/0xMiden/midenup/releases/download/${tag}/installer.sh" + else + tag= + url="https://github.com/0xMiden/midenup/releases/latest/download/installer.sh" + fi + echo "Installing midenup ${tag}" + curl --retry 10 -L --proto '=https' --tlsv1.2 -sSf -O "${url}" + chmod +x installer.sh + ./installer.sh ${tag:+--version "${tag}"} + + - name: Verify `midenup` installation + run: | + which midenup + midenup --version + + - name: Install the mainnet toolchain from the live manifest + run: | + midenup install mainnet + midenup show list diff --git a/.release/config.toml b/.release/config.toml index 2fac2b84..d89dd86e 100644 --- a/.release/config.toml +++ b/.release/config.toml @@ -23,9 +23,10 @@ changelog = "CHANGELOG.md" changelog-headings = ["Added", "Changed", "Fixed", "Migration and breaking changes"] latest = true # The release workflow's matrix builds `${binary}-${target}.tar.gz` for the midenup binary -# across two targets. They route here. -assets = ["midenup-*.tar.gz"] -required-assets = ["midenup-*.tar.gz"] +# across two targets, and uploads `bin/installer.sh` so users can fetch it from the release. +# They route here. +assets = ["midenup-*.tar.gz", "installer.sh"] +required-assets = ["midenup-*.tar.gz", "installer.sh"] # Repository infrastructure, never published. diff --git a/bin/installer.sh b/bin/installer.sh index df257a0d..5d78382d 100755 --- a/bin/installer.sh +++ b/bin/installer.sh @@ -91,9 +91,9 @@ do_verify_attestation() { } install_via_cargo() { - case "${0:-}" in + case "${1:-}" in "") cargo install --force --locked --bin midenup --no-track midenup ;; - *) cargo install --force --locked --bin midenup --no-track --version "$0" midenup;; + *) cargo install --force --locked --bin midenup --no-track --version "$1" midenup;; esac }