feat: support Android quickstart init #245
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@v7 | |
| - 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: Unit test installer fallback logic | |
| if: runner.os == 'Linux' | |
| shell: bash | |
| run: sh scripts/test-installer-fallback.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 | |
| # Fixed-port fixture server (matches the S3 fallback steps below). | |
| # Avoids the ephemeral-port + port-file write race that intermittently | |
| # hung before writing its port on macOS runners. | |
| server_log="$fixture_root/http-server.log" | |
| server_url="http://127.0.0.1:18080" | |
| archive_url="${server_url}/download/v${version}/${archive}" | |
| python3 -m http.server 18080 --directory "$fixture_root" >"$server_log" 2>&1 & | |
| server_pid=$! | |
| trap 'kill "$server_pid" 2>/dev/null || true' EXIT | |
| server_ready=0 | |
| for attempt in $(seq 1 120); do | |
| if ! kill -0 "$server_pid" 2>/dev/null; then | |
| echo "Fixture HTTP server exited before serving the test archive." >&2 | |
| sed 's/^/ /' "$server_log" >&2 || true | |
| exit 1 | |
| fi | |
| if curl -fsS --head --connect-timeout 2 "$archive_url" >/dev/null 2>&1; then | |
| server_ready=1 | |
| break | |
| fi | |
| sleep 0.25 | |
| done | |
| if [ "$server_ready" != "1" ]; then | |
| echo "Fixture HTTP server did not serve $archive_url." >&2 | |
| sed 's/^/ /' "$server_log" >&2 || true | |
| exit 1 | |
| fi | |
| # 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" | |
| # Pin the source to github so this test only exercises the fixture | |
| # (RELEASES_DOWNLOAD_BASE_URL) path; auto mode would fast-fail a | |
| # transient fixture hiccup and divert to the real dl.agora.io mirror. | |
| # github mode also uses the retry profile, restoring pre-fallback | |
| # resilience. | |
| VERSION="$version" \ | |
| AGORA_INSTALL_SOURCE=github \ | |
| RELEASES_DOWNLOAD_BASE_URL="${server_url}/download" \ | |
| RELEASES_PAGE_URL="$server_url" \ | |
| 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" \ | |
| AGORA_INSTALL_SOURCE=github \ | |
| RELEASES_DOWNLOAD_BASE_URL="${server_url}/download" \ | |
| RELEASES_PAGE_URL="$server_url" \ | |
| INSTALL_DIR="$bad_install_dir" \ | |
| sh ./install.sh --force; then | |
| echo "Expected checksum verification to fail" >&2 | |
| exit 1 | |
| fi | |
| - name: Smoke test installer S3 fallback (Unix) | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| version='0.0.0-ci' | |
| arch="$(uname -m)"; case "$arch" in x86_64|amd64) arch=amd64;; aarch64|arm64) arch=arm64;; esac | |
| os="$(uname -s | tr '[:upper:]' '[:lower:]')" | |
| archive="agora-cli_v${version}_${os}_${arch}.tar.gz" | |
| root=".tmp/fallback-fixture" | |
| rel="${root}/releases/v${version}" | |
| install_dir="$PWD/.tmp/fallback-bin" | |
| rm -rf "$root" "$install_dir" | |
| mkdir -p "$rel" "$install_dir" | |
| cp dist/agora "$root/agora" | |
| tar -C "$root" -czf "${rel}/${archive}" agora | |
| if command -v sha256sum >/dev/null 2>&1; then | |
| ( cd "$rel" && sha256sum "$archive" > checksums.txt ) | |
| else | |
| ( cd "$rel" && shasum -a 256 "$archive" > checksums.txt ) | |
| fi | |
| printf '{"tag_name":"v%s","version":"%s"}' "$version" "$version" > "${root}/latest.json" | |
| server_log="${root}/http.log" | |
| python3 -m http.server 18082 --directory "$root" >"$server_log" 2>&1 & | |
| server_pid=$! | |
| trap 'kill "$server_pid" 2>/dev/null || true' EXIT | |
| server_ready=0 | |
| for _ in $(seq 1 120); do | |
| if ! kill -0 "$server_pid" 2>/dev/null; then | |
| echo "Fixture HTTP server exited before becoming ready." >&2 | |
| sed 's/^/ /' "$server_log" >&2 || true | |
| exit 1 | |
| fi | |
| if curl -fsS --head --connect-timeout 2 "http://127.0.0.1:18082/latest.json" >/dev/null 2>&1; then | |
| server_ready=1; break | |
| fi | |
| sleep 0.25 | |
| done | |
| if [ "$server_ready" != "1" ]; then | |
| echo "Fixture HTTP server did not become ready." >&2 | |
| sed 's/^/ /' "$server_log" >&2 || true | |
| exit 1 | |
| fi | |
| export INSTALLER_CURL_PROTO_OPTS="--proto =http,https" | |
| GITHUB_API_URL="http://127.0.0.1:1/api" \ | |
| RELEASES_DOWNLOAD_BASE_URL="http://127.0.0.1:1/download" \ | |
| S3_DOWNLOAD_BASE_URL="http://127.0.0.1:18082/releases" \ | |
| S3_LATEST_URL="http://127.0.0.1:18082/latest.json" \ | |
| RELEASES_PAGE_URL="http://127.0.0.1:18082" \ | |
| INSTALL_DIR="$install_dir" \ | |
| sh ./install.sh --force | |
| "$install_dir/agora" --help >/dev/null | |
| echo "Fallback install succeeded via mock S3." | |
| printf '%064d %s\n' 0 "$archive" > "${rel}/checksums.txt" | |
| if GITHUB_API_URL="http://127.0.0.1:1/api" \ | |
| RELEASES_DOWNLOAD_BASE_URL="http://127.0.0.1:1/download" \ | |
| S3_DOWNLOAD_BASE_URL="http://127.0.0.1:18082/releases" \ | |
| S3_LATEST_URL="http://127.0.0.1:18082/latest.json" \ | |
| RELEASES_PAGE_URL="http://127.0.0.1:18082" \ | |
| INSTALL_DIR="$PWD/.tmp/fallback-bin-bad" \ | |
| sh ./install.sh --force; then | |
| echo "Expected checksum verification to fail on the mirrored archive." >&2 | |
| exit 1 | |
| fi | |
| echo "Checksum verification correctly rejected the tampered mirror archive." | |
| - 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 60; $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 | |
| # Pin source to github so this test only exercises the fixture path | |
| # (auto mode could divert a transient hiccup to the real mirror). | |
| $env:AGORA_INSTALL_SOURCE = 'github' | |
| $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:AGORA_INSTALL_SOURCE, Env:RELEASES_DOWNLOAD_BASE_URL, Env:RELEASES_PAGE_URL -ErrorAction SilentlyContinue | |
| } | |
| - name: Smoke test PowerShell installer S3 fallback | |
| 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" } | |
| } | |
| $root = Join-Path $PWD '.tmp/fallback-fixture' | |
| $rel = Join-Path $root "releases/v$version" | |
| $installDir = Join-Path $PWD '.tmp/fallback-bin' | |
| $archive = "agora-cli_v$version" + "_windows_${arch}.zip" | |
| Remove-Item -Recurse -Force $root, $installDir -ErrorAction SilentlyContinue | |
| New-Item -ItemType Directory -Force -Path $rel, $installDir | Out-Null | |
| Compress-Archive -Path (Join-Path $PWD 'dist/agora.exe') -DestinationPath (Join-Path $rel $archive) -Force | |
| $hash = (Get-FileHash -Path (Join-Path $rel $archive) -Algorithm SHA256).Hash.ToLowerInvariant() | |
| Set-Content -Path (Join-Path $rel 'checksums.txt') -Value "$hash $archive" | |
| Set-Content -NoNewline -Path (Join-Path $root 'latest.json') -Value ('{"tag_name":"v' + $version + '","version":"' + $version + '"}') | |
| $server = Start-Process -FilePath python -ArgumentList '-m', 'http.server', '18083', '--directory', $root -PassThru | |
| try { | |
| $ready = $false | |
| for ($i = 0; $i -lt 120; $i++) { | |
| if ($server.HasExited) { throw 'Fixture HTTP server exited before becoming ready.' } | |
| try { if ((Invoke-WebRequest -Uri 'http://127.0.0.1:18083/latest.json' -UseBasicParsing).StatusCode -eq 200) { $ready = $true; break } } catch { Start-Sleep -Milliseconds 250 } | |
| } | |
| if (-not $ready) { throw 'fixture server not ready at http://127.0.0.1:18083/latest.json' } | |
| $env:GITHUB_API_URL = 'http://127.0.0.1:1/api' | |
| $env:RELEASES_DOWNLOAD_BASE_URL = 'http://127.0.0.1:1/download' | |
| $env:S3_DOWNLOAD_BASE_URL = 'http://127.0.0.1:18083/releases' | |
| $env:S3_LATEST_URL = 'http://127.0.0.1:18083/latest.json' | |
| $env:RELEASES_PAGE_URL = 'http://127.0.0.1:18083' | |
| & ./install.ps1 -InstallDir $installDir | |
| if ($LASTEXITCODE -ne 0) { throw "install.ps1 fallback failed: $LASTEXITCODE" } | |
| & (Join-Path $installDir 'agora.exe') --help *> $null | |
| Write-Host 'Fallback install succeeded via mock S3.' | |
| # Checksum verification must still fire on S3-served bytes: corrupt | |
| # the mirror's checksums.txt and assert install.ps1 refuses. | |
| Set-Content -Path (Join-Path $rel 'checksums.txt') -Value ('0' * 64 + " $archive") | |
| $badInstallDir = Join-Path $PWD '.tmp/fallback-bin-bad' | |
| $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 on the mirrored archive.' | |
| } | |
| $global:LASTEXITCODE = 0 | |
| Write-Host 'Checksum verification correctly rejected the tampered mirror archive.' | |
| } finally { | |
| Stop-Process -Id $server.Id -Force -ErrorAction SilentlyContinue | |
| Remove-Item Env:GITHUB_API_URL, Env:RELEASES_DOWNLOAD_BASE_URL, Env:S3_DOWNLOAD_BASE_URL, Env:S3_LATEST_URL, Env:RELEASES_PAGE_URL -ErrorAction SilentlyContinue | |
| } |