diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 28ee123..9b042ec 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -3,10 +3,6 @@ name: E2E Tests on: workflow_dispatch: inputs: - release_tag: - description: 'GitHub release tag to test (e.g., v0.4.2)' - required: true - type: string test_channel: description: 'TX3 channel to test' required: false @@ -16,278 +12,63 @@ on: - stable - nightly - beta + pull_request: + branches: + - main env: CARGO_TERM_COLOR: always jobs: - # Download tx3up binaries from GitHub releases - download-binaries: - name: Download tx3up (${{ matrix.os }}-${{ matrix.arch }}) - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - include: - # Ubuntu Intel - - os: ubuntu - arch: intel - asset_pattern: "*x86_64-unknown-linux-gnu*" - artifact_name: tx3up-ubuntu-intel - - # Ubuntu ARM - - os: ubuntu - arch: arm - asset_pattern: "*aarch64-unknown-linux-gnu*" - artifact_name: tx3up-ubuntu-arm - - # macOS Intel - - os: mac - arch: intel - asset_pattern: "*x86_64-apple-darwin*" - artifact_name: tx3up-mac-intel - - # macOS ARM - - os: mac - arch: arm - asset_pattern: "*aarch64-apple-darwin*" - artifact_name: tx3up-mac-arm - - steps: - - name: Download release asset - env: - GH_TOKEN: ${{ github.token }} - run: | - echo "Downloading tx3up ${{ inputs.release_tag }} for ${{ matrix.os }}-${{ matrix.arch }}" - - # Create target directory - mkdir -p target/release - - # List available assets first - echo "Available assets for ${{ inputs.release_tag }}:" - gh release view ${{ inputs.release_tag }} --repo ${{ github.repository }} --json assets --jq '.assets[].name' - - # Download the asset using GitHub CLI - echo "Downloading assets matching pattern: ${{ matrix.asset_pattern }}" - gh release download ${{ inputs.release_tag }} \ - --repo ${{ github.repository }} \ - --pattern "${{ matrix.asset_pattern }}" \ - --dir target/release/ - - # List what was downloaded - echo "Downloaded files:" - ls -la target/release/ - - # Find the downloaded file and extract if it's an archive - downloaded_file=$(find target/release -type f \( -name "*.tar.gz" -o -name "*.tar.xz" -o -name "*.zip" -o -name "*${{ matrix.os }}*${{ matrix.arch }}*" -o -name "*$(echo "${{ matrix.asset_pattern }}" | tr -d '*')*" \) | head -1) - - if [[ -z "$downloaded_file" ]]; then - echo "Error: No suitable file found for ${{ matrix.os }}-${{ matrix.arch }}" - echo "Available files:" - ls -la target/release/ - exit 1 - fi - - echo "Processing file: $downloaded_file" - - # Extract archives or handle direct binaries - if [[ "$downloaded_file" == *.tar.gz ]]; then - echo "Extracting tar.gz archive..." - tar -xzf "$downloaded_file" -C target/release/ - rm "$downloaded_file" - elif [[ "$downloaded_file" == *.tar.xz ]]; then - echo "Extracting tar.xz archive..." - tar -xJf "$downloaded_file" -C target/release/ - rm "$downloaded_file" - elif [[ "$downloaded_file" == *.zip ]]; then - echo "Extracting zip archive..." - unzip "$downloaded_file" -d target/release/ - rm "$downloaded_file" - else - echo "File appears to be a direct binary" - fi - - # Find the tx3up binary - tx3up_binary=$(find target/release -name "tx3up" -type f -executable 2>/dev/null | head -1) - if [[ -z "$tx3up_binary" ]]; then - # Try without executable check (some extracted files might not have exec bit set) - tx3up_binary=$(find target/release -name "tx3up" -type f | head -1) - fi - - if [[ -z "$tx3up_binary" ]]; then - echo "Error: tx3up binary not found after extraction" - echo "Contents after extraction/download:" - find target/release -type f -exec ls -la {} \; - exit 1 - fi - - echo "Found tx3up binary at: $tx3up_binary" - - # Move binary to standard location and make executable - if [[ "$tx3up_binary" != "target/release/tx3up" ]]; then - mv "$tx3up_binary" target/release/tx3up - fi - chmod +x target/release/tx3up - - echo "Successfully prepared tx3up binary" - - - name: Verify binary - run: | - ls -la target/release/tx3up - file target/release/tx3up - ./target/release/tx3up --version || echo "Version check failed, but binary exists" - - - name: Upload binary artifact - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.artifact_name }} - path: target/release/tx3up - retention-days: 1 - - # Run E2E tests using the downloaded binaries e2e-tests: - name: E2E Tests (${{ matrix.os }}-${{ matrix.arch }}, ${{ matrix.test }}) + name: E2E (${{ matrix.runner }}, ${{ matrix.test }}) runs-on: ${{ matrix.runner }} - needs: download-binaries - + strategy: fail-fast: false matrix: - include: - # Ubuntu Intel tests - - os: ubuntu - arch: intel - runner: ubuntu-latest - artifact_name: tx3up-ubuntu-intel - test: fresh_install - - os: ubuntu - arch: intel - runner: ubuntu-latest - artifact_name: tx3up-ubuntu-intel - test: update_install - - # Ubuntu ARM tests - - os: ubuntu - arch: arm - runner: ubuntu-latest - artifact_name: tx3up-ubuntu-arm - test: fresh_install - - os: ubuntu - arch: arm - runner: ubuntu-latest - artifact_name: tx3up-ubuntu-arm - test: update_install - - # macOS Intel tests - - os: mac - arch: intel - runner: macos-13 - artifact_name: tx3up-mac-intel - test: fresh_install - - os: mac - arch: intel - runner: macos-13 - artifact_name: tx3up-mac-intel - test: update_install - - # macOS ARM tests - - os: mac - arch: arm - runner: macos-latest - artifact_name: tx3up-mac-arm - test: fresh_install - - os: mac - arch: arm - runner: macos-latest - artifact_name: tx3up-mac-arm - test: update_install - + runner: + - ubuntu-latest + - ubuntu-24.04-arm + - macos-latest + test: + - fresh_install + - update_install + steps: - name: Checkout code uses: actions/checkout@v4 - - - name: Download tx3up binary - uses: actions/download-artifact@v4 - with: - name: ${{ matrix.artifact_name }} - path: target/release/ - - - name: Make binary executable - run: chmod +x target/release/tx3up - - - name: Verify binary - run: | - ls -la target/release/tx3up - file target/release/tx3up - - - name: Install jq (for JSON validation) + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Cache cargo build + uses: Swatinem/rust-cache@v2 + + - name: Build tx3up + run: cargo build --release + + - name: Install jq run: | - if [[ "${{ matrix.os }}" == "ubuntu" ]]; then + if [[ "$RUNNER_OS" == "Linux" ]]; then sudo apt-get update && sudo apt-get install -y jq - elif [[ "${{ matrix.os }}" == "mac" ]]; then - brew install jq + elif [[ "$RUNNER_OS" == "macOS" ]]; then + command -v jq >/dev/null 2>&1 || brew install jq fi - + - name: Run E2E test - ${{ matrix.test }} - run: | - echo "Running ${{ matrix.test }} test on ${{ matrix.os }}-${{ matrix.arch }}" - echo "Testing release: ${{ inputs.release_tag }}" - echo "Testing channel: ${{ inputs.test_channel }}" - ./tests/e2e/${{ matrix.test }}.sh env: - # Use a unique root directory for each test run - TX3_ROOT: ${{ github.workspace }}/test-root-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.test }} - TX3_CHANNEL: ${{ inputs.test_channel }} - + TX3_CHANNEL: ${{ inputs.test_channel || 'stable' }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: ./tests/e2e/${{ matrix.test }}.sh + - name: Upload test artifacts on failure if: failure() uses: actions/upload-artifact@v4 with: - name: test-artifacts-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.test }} + name: test-artifacts-${{ matrix.runner }}-${{ matrix.test }} path: | - ${{ github.workspace }}/test-root-*/ + /tmp/tmp.*/tx3_test/ target/release/tx3up retention-days: 7 - - - name: Display test summary - if: always() - run: | - echo "## E2E Test Summary" >> $GITHUB_STEP_SUMMARY - echo "- **Release**: ${{ inputs.release_tag }}" >> $GITHUB_STEP_SUMMARY - echo "- **OS**: ${{ matrix.os }}" >> $GITHUB_STEP_SUMMARY - echo "- **Architecture**: ${{ matrix.arch }}" >> $GITHUB_STEP_SUMMARY - echo "- **Test**: ${{ matrix.test }}" >> $GITHUB_STEP_SUMMARY - echo "- **Channel**: ${{ inputs.test_channel }}" >> $GITHUB_STEP_SUMMARY - echo "- **Runner**: ${{ matrix.runner }}" >> $GITHUB_STEP_SUMMARY - if [[ "${{ job.status }}" == "success" ]]; then - echo "- **Result**: ✅ PASSED" >> $GITHUB_STEP_SUMMARY - else - echo "- **Result**: ❌ FAILED" >> $GITHUB_STEP_SUMMARY - fi - - # Summary job that depends on all matrix jobs - e2e-summary: - name: E2E Tests Summary - runs-on: ubuntu-latest - needs: [download-binaries, e2e-tests] - if: always() - - steps: - - name: Check test results - run: | - echo "## Overall E2E Test Results" >> $GITHUB_STEP_SUMMARY - echo "**Tested Release**: ${{ inputs.release_tag }}" >> $GITHUB_STEP_SUMMARY - echo "**Tested Channel**: ${{ inputs.test_channel }}" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - if [[ "${{ needs.e2e-tests.result }}" == "success" ]]; then - echo "🎉 All E2E tests passed!" >> $GITHUB_STEP_SUMMARY - echo "📦 Downloaded binaries for all platforms successfully" >> $GITHUB_STEP_SUMMARY - exit 0 - else - echo "❌ Some tests failed. Check individual job results for details." >> $GITHUB_STEP_SUMMARY - if [[ "${{ needs.download-binaries.result }}" != "success" ]]; then - echo "📥 Binary download stage failed" >> $GITHUB_STEP_SUMMARY - fi - exit 1 - fi \ No newline at end of file + if-no-files-found: ignore diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..d6abe6e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,15 @@ +# Contributing to tx3up + +Thanks for your interest in contributing! + +## Before pushing + +Run the end-to-end install tests locally before pushing any change that could affect the install flow: + +```sh +cargo build --release +./tests/e2e/fresh_install.sh +./tests/e2e/update_install.sh +``` + +These are the same scripts CI runs against your pull request. Catching failures locally is much faster than waiting for the matrix in `.github/workflows/e2e.yml` to complete. See [`tests/e2e/README.md`](tests/e2e/README.md) for details on what each script checks and how to run them against a non-default channel. diff --git a/tests/e2e/README.md b/tests/e2e/README.md new file mode 100644 index 0000000..3027519 --- /dev/null +++ b/tests/e2e/README.md @@ -0,0 +1,40 @@ +# tx3up E2E tests + +End-to-end tests for `tx3up`. They drive a real `tx3up` binary against a temporary `TX3_ROOT` and assert the resulting toolchain layout. + +## Tests + +- `fresh_install.sh` — runs `tx3up` against an empty root and verifies the channel directory, `manifest.json`, and at least one executable in `bin/` are created. +- `update_install.sh` — installs once, runs `tx3up` again, and checks that the manifest is refreshed, no binaries are lost, and a third run is idempotent. + +Both scripts read `TX3_CHANNEL` from the environment (default: `stable`). They allocate their own `TX3_ROOT` via `mktemp -d` and clean it up on exit, so they will not touch your real `~/.tx3`. + +## Running locally + +From the repo root: + +```sh +cargo build --release +./tests/e2e/fresh_install.sh +./tests/e2e/update_install.sh +``` + +To exercise a non-default channel: + +```sh +TX3_CHANNEL=nightly ./tests/e2e/fresh_install.sh +``` + +Requirements: a working Rust toolchain and `jq` (the scripts fall back to a basic JSON check if `jq` is missing, but installing it is recommended). The scripts hit the real `tx3-lang/toolchain` GitHub releases, so the host needs network access. If you run into GitHub rate limits, export `GITHUB_TOKEN` before running. + +## How CI uses these tests + +`.github/workflows/e2e.yml` runs both scripts on every pull request to `main` and on manual `workflow_dispatch`. The workflow: + +1. Checks out the PR. +2. Builds `tx3up` from source with `cargo build --release` on each runner. +3. Runs `fresh_install.sh` and `update_install.sh` against the freshly-built binary. + +The matrix covers `ubuntu-latest`, `ubuntu-24.04-arm`, and `macos-latest`, with each script as a separate cell so failures are isolated. `workflow_dispatch` accepts a `test_channel` input (`stable`/`nightly`/`beta`); PR runs always use `stable`. + +Because CI builds from the PR's source, these tests gate the **current** code — not a previously-released binary. diff --git a/tests/e2e/fresh_install.sh b/tests/e2e/fresh_install.sh index 8969f79..4d1ff6d 100755 --- a/tests/e2e/fresh_install.sh +++ b/tests/e2e/fresh_install.sh @@ -113,7 +113,7 @@ main() { find "$TX3_ROOT" -type f -exec ls -la {} \; 2>/dev/null || true # Check if at least one binary was installed - bin_count=$(find "$TX3_ROOT/$TX3_CHANNEL/bin" -type f -executable 2>/dev/null | wc -l) + bin_count=$(find "$TX3_ROOT/$TX3_CHANNEL/bin" -type f -perm -u+x 2>/dev/null | wc -l) if [[ $bin_count -gt 0 ]]; then echo -e "${GREEN}✓ Found $bin_count executable binaries in bin directory${NC}" else diff --git a/tests/e2e/update_install.sh b/tests/e2e/update_install.sh index 3784144..e2c7c5d 100755 --- a/tests/e2e/update_install.sh +++ b/tests/e2e/update_install.sh @@ -181,7 +181,7 @@ main() { fi # Verify that at least some binaries are still executable - executable_count=$(find "$TX3_ROOT/$TX3_CHANNEL/bin" -type f -executable 2>/dev/null | wc -l) + executable_count=$(find "$TX3_ROOT/$TX3_CHANNEL/bin" -type f -perm -u+x 2>/dev/null | wc -l) if [[ $executable_count -gt 0 ]]; then echo -e "${GREEN}✓ Found $executable_count executable binaries after update${NC}" else