Split npm release authentication #130
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: | |
| - "**" | |
| tags-ignore: | |
| - "v*" | |
| pull_request: | |
| permissions: | |
| contents: read | |
| jobs: | |
| test-and-build: | |
| name: Test and build (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: | |
| - ubuntu-latest | |
| - macos-latest | |
| - windows-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v6 | |
| - name: Set up Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Run tests | |
| # -count=1 disables Go's test result cache so flaky CI nodes never | |
| # surface a stale "PASS" from an earlier run on the same content. | |
| run: go test -count=1 ./... | |
| - name: Check Go formatting | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| run: | | |
| files="$(gofmt -l .)" | |
| if [ -n "$files" ]; then | |
| echo "$files" | |
| exit 1 | |
| fi | |
| - name: Audit error code coverage | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| run: ./scripts/check-error-codes.sh | |
| - name: Check docs/commands.md is up to date | |
| if: runner.os == 'Linux' | |
| shell: bash | |
| run: | | |
| # Generated reference must track the cobra tree. If this fails, | |
| # run `make docs-commands` locally and commit the result. | |
| go run ./cmd/gendocs -check | |
| # Prebuilt golangci-lint binaries may be compiled with an older Go than | |
| # go.mod; loading .golangci.yml then fails ("Go language version used to | |
| # build golangci-lint is lower than the targeted Go version"). Building | |
| # from source with setup-go matches the project's toolchain. | |
| - name: golangci-lint | |
| if: runner.os == 'Linux' | |
| uses: golangci/golangci-lint-action@v6 | |
| with: | |
| version: v1.64.8 | |
| install-mode: goinstall | |
| args: --timeout=5m | |
| - name: Build CLI | |
| shell: bash | |
| run: | | |
| mkdir -p dist | |
| ext="" | |
| if [ "${{ runner.os }}" = "Windows" ]; then | |
| ext=".exe" | |
| fi | |
| go build -trimpath -o "dist/agora${ext}" . | |
| - name: Check Unix installer syntax | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| run: sh -n install.sh | |
| - name: Smoke test installer messages | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| run: sh scripts/test-installer-messages.sh | |
| - name: Check PowerShell installer syntax | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $tokens = $null | |
| $errors = $null | |
| [void][System.Management.Automation.Language.Parser]::ParseFile( | |
| (Join-Path $PWD 'install.ps1'), | |
| [ref]$tokens, | |
| [ref]$errors | |
| ) | |
| if ($errors.Count -gt 0) { | |
| $errors | ForEach-Object { Write-Error $_.Message } | |
| exit 1 | |
| } | |
| - name: Smoke test POSIX installer on Windows | |
| if: runner.os == 'Windows' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| VERSION="0.0.0-ci" \ | |
| INSTALL_DIR="$PWD/.tmp/install-bin-sh" \ | |
| sh ./install.sh --dry-run --force | |
| - name: Smoke test Unix installer | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| version="0.0.0-ci" | |
| goos="$(uname -s | tr '[:upper:]' '[:lower:]')" | |
| case "$(uname -m)" in | |
| x86_64|amd64) goarch="amd64" ;; | |
| aarch64|arm64) goarch="arm64" ;; | |
| *) echo "Unsupported CI arch: $(uname -m)" >&2; exit 1 ;; | |
| esac | |
| fixture_root="$PWD/.tmp/install-fixture" | |
| download_dir="$fixture_root/download/v${version}" | |
| install_dir="$PWD/.tmp/install-bin" | |
| bad_install_dir="$PWD/.tmp/install-bin-bad" | |
| archive="agora-cli_v${version}_${goos}_${goarch}.tar.gz" | |
| rm -rf "$fixture_root" "$install_dir" "$bad_install_dir" | |
| mkdir -p "$download_dir" | |
| tar -C dist -czf "$download_dir/$archive" agora | |
| if command -v sha256sum >/dev/null 2>&1; then | |
| (cd "$download_dir" && sha256sum "$archive" > checksums.txt) | |
| else | |
| (cd "$download_dir" && shasum -a 256 "$archive" > checksums.txt) | |
| fi | |
| python3 -m http.server 18080 --directory "$fixture_root" >/tmp/agora-install-server.log 2>&1 & | |
| server_pid=$! | |
| trap 'kill "$server_pid"' EXIT | |
| sleep 2 | |
| # The installer enforces --proto =https in production. The smoke test | |
| # serves the fixture from a local HTTP server, so we relax the proto | |
| # restriction (test only). | |
| export INSTALLER_CURL_PROTO_OPTS="--proto =http,https" | |
| VERSION="$version" \ | |
| RELEASES_DOWNLOAD_BASE_URL="http://127.0.0.1:18080/download" \ | |
| RELEASES_PAGE_URL="http://127.0.0.1:18080" \ | |
| INSTALL_DIR="$install_dir" \ | |
| sh ./install.sh --force | |
| "$install_dir/agora" --help >/dev/null | |
| printf '%064d %s\n' 0 "$archive" > "$download_dir/checksums.txt" | |
| if VERSION="$version" \ | |
| RELEASES_DOWNLOAD_BASE_URL="http://127.0.0.1:18080/download" \ | |
| RELEASES_PAGE_URL="http://127.0.0.1:18080" \ | |
| INSTALL_DIR="$bad_install_dir" \ | |
| sh ./install.sh --force; then | |
| echo "Expected checksum verification to fail" >&2 | |
| exit 1 | |
| fi | |
| - name: Smoke test PowerShell installer | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $version = '0.0.0-ci' | |
| $arch = switch ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant()) { | |
| 'x64' { 'amd64' } | |
| 'arm64' { 'arm64' } | |
| default { throw "Unsupported CI arch: $([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)" } | |
| } | |
| $fixtureRoot = Join-Path $PWD '.tmp/install-fixture' | |
| $downloadDir = Join-Path $fixtureRoot "download/v$version" | |
| $installDir = Join-Path $PWD '.tmp/install-bin' | |
| $badInstallDir = Join-Path $PWD '.tmp/install-bin-bad' | |
| $archive = "agora-cli_v$version" + "_windows_${arch}.zip" | |
| Remove-Item -Recurse -Force $fixtureRoot, $installDir, $badInstallDir -ErrorAction SilentlyContinue | |
| New-Item -ItemType Directory -Force -Path $downloadDir | Out-Null | |
| Compress-Archive -Path (Join-Path $PWD 'dist/agora.exe') -DestinationPath (Join-Path $downloadDir $archive) -Force | |
| $hash = (Get-FileHash -Path (Join-Path $downloadDir $archive) -Algorithm SHA256).Hash.ToLowerInvariant() | |
| Set-Content -Path (Join-Path $downloadDir 'checksums.txt') -Value "$hash $archive" | |
| $serverOutLog = Join-Path $fixtureRoot 'http-server.out.log' | |
| $serverErrLog = Join-Path $fixtureRoot 'http-server.err.log' | |
| $server = Start-Process -FilePath python -ArgumentList '-m', 'http.server', '18081', '--directory', $fixtureRoot -RedirectStandardOutput $serverOutLog -RedirectStandardError $serverErrLog -PassThru | |
| $archiveUrl = "http://127.0.0.1:18081/download/v$version/$archive" | |
| $serverReady = $false | |
| for ($attempt = 1; $attempt -le 20; $attempt++) { | |
| if ($server.HasExited) { | |
| if (Test-Path -LiteralPath $serverOutLog) { Get-Content -Path $serverOutLog | ForEach-Object { Write-Host $_ } } | |
| if (Test-Path -LiteralPath $serverErrLog) { Get-Content -Path $serverErrLog | ForEach-Object { Write-Host $_ } } | |
| throw "Fixture HTTP server exited before serving $archiveUrl." | |
| } | |
| try { | |
| $response = Invoke-WebRequest -Uri $archiveUrl -Method Head -UseBasicParsing | |
| if ($response.StatusCode -eq 200) { | |
| $serverReady = $true | |
| break | |
| } | |
| } catch { | |
| Start-Sleep -Milliseconds 500 | |
| } | |
| } | |
| if (-not $serverReady) { | |
| if (Test-Path -LiteralPath $serverOutLog) { Get-Content -Path $serverOutLog | ForEach-Object { Write-Host $_ } } | |
| if (Test-Path -LiteralPath $serverErrLog) { Get-Content -Path $serverErrLog | ForEach-Object { Write-Host $_ } } | |
| throw "Fixture HTTP server did not serve $archiveUrl." | |
| } | |
| try { | |
| $env:VERSION = $version | |
| $env:RELEASES_DOWNLOAD_BASE_URL = 'http://127.0.0.1:18081/download' | |
| $env:RELEASES_PAGE_URL = 'http://127.0.0.1:18081' | |
| & ./install.ps1 -InstallDir $installDir | |
| if ($LASTEXITCODE -ne 0) { | |
| throw "install.ps1 failed with exit code $LASTEXITCODE." | |
| } | |
| & (Join-Path $installDir 'agora.exe') --help *> $null | |
| Set-Content -Path (Join-Path $downloadDir 'checksums.txt') -Value ('0' * 64 + " $archive") | |
| $previousNativePreference = $PSNativeCommandUseErrorActionPreference | |
| $PSNativeCommandUseErrorActionPreference = $false | |
| pwsh -NoProfile -ExecutionPolicy Bypass -File ./install.ps1 -InstallDir $badInstallDir | |
| $badExitCode = $LASTEXITCODE | |
| $PSNativeCommandUseErrorActionPreference = $previousNativePreference | |
| if ($badExitCode -eq 0) { | |
| throw 'Expected checksum verification to fail with a non-zero exit code.' | |
| } | |
| # The expected non-zero $LASTEXITCODE from install.ps1 is good news | |
| # for this assertion, but GitHub Actions' pwsh wrapper appends | |
| # `if (Test-Path variable:\LASTEXITCODE) { exit $LASTEXITCODE }`, | |
| # which would otherwise propagate that non-zero exit and fail the | |
| # whole step. Clear it now that we have what we need. | |
| $global:LASTEXITCODE = 0 | |
| } finally { | |
| Stop-Process -Id $server.Id -Force -ErrorAction SilentlyContinue | |
| Remove-Item Env:VERSION, Env:RELEASES_DOWNLOAD_BASE_URL, Env:RELEASES_PAGE_URL -ErrorAction SilentlyContinue | |
| } |