refactor: optimize GitHub Actions workflows with composite action #159
Workflow file for this run
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: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| jobs: | |
| lint: | |
| name: Lint | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Setup Go and checkout | |
| uses: ./.github/actions/setup-go-wt | |
| with: | |
| bot-app-id: ${{ secrets.BOT_APP_ID }} | |
| bot-private-key: ${{ secrets.BOT_PRIVATE_KEY }} | |
| - name: Run golangci-lint | |
| uses: golangci/golangci-lint-action@v4 | |
| with: | |
| version: latest | |
| test: | |
| name: Test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Setup Go and checkout | |
| uses: ./.github/actions/setup-go-wt | |
| with: | |
| bot-app-id: ${{ secrets.BOT_APP_ID }} | |
| bot-private-key: ${{ secrets.BOT_PRIVATE_KEY }} | |
| - name: Verify dependencies | |
| run: go mod verify | |
| - name: Install shells for PTY tests | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y bash zsh | |
| # Fix zsh permissions to avoid compinit security warnings | |
| sudo chmod -R 755 /usr/share/zsh | |
| sudo chown -R root:root /usr/share/zsh | |
| - name: Run tests | |
| run: go test -v -race -coverprofile=coverage.out ./... | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v5 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| slug: timvw/wt | |
| files: ./coverage.out | |
| fail_ci_if_error: false | |
| build: | |
| name: Build and Cross-Compile | |
| runs-on: ubuntu-latest | |
| needs: [lint, test] | |
| steps: | |
| - name: Setup Go and checkout | |
| uses: ./.github/actions/setup-go-wt | |
| with: | |
| bot-app-id: ${{ secrets.BOT_APP_ID }} | |
| bot-private-key: ${{ secrets.BOT_PRIVATE_KEY }} | |
| - name: Build for all platforms | |
| run: | | |
| mkdir -p bin | |
| # Native build | |
| go build -o bin/wt . | |
| # Cross-compile for all supported platforms | |
| GOOS=linux GOARCH=amd64 go build -o bin/wt-linux-amd64 . | |
| GOOS=linux GOARCH=arm64 go build -o bin/wt-linux-arm64 . | |
| GOOS=darwin GOARCH=amd64 go build -o bin/wt-darwin-amd64 . | |
| GOOS=darwin GOARCH=arm64 go build -o bin/wt-darwin-arm64 . | |
| GOOS=windows GOARCH=amd64 go build -o bin/wt-windows-amd64.exe . | |
| - name: Verify binaries | |
| run: | | |
| ./bin/wt --help | |
| ls -lh bin/ | |
| file bin/* | |
| - name: Upload Linux binary | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wt-linux-amd64 | |
| path: bin/wt-linux-amd64 | |
| retention-days: 1 | |
| - name: Upload macOS ARM64 binary | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wt-darwin-arm64 | |
| path: bin/wt-darwin-arm64 | |
| retention-days: 1 | |
| - name: Upload Windows binary | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wt-windows-amd64 | |
| path: bin/wt-windows-amd64.exe | |
| retention-days: 1 | |
| e2e-macos: | |
| name: E2E Tests (macOS) | |
| runs-on: macos-latest | |
| needs: build | |
| strategy: | |
| matrix: | |
| shell: ['bash', 'zsh'] | |
| steps: | |
| - name: Setup Go and checkout | |
| uses: ./.github/actions/setup-go-wt | |
| with: | |
| bot-app-id: ${{ secrets.BOT_APP_ID }} | |
| bot-private-key: ${{ secrets.BOT_PRIVATE_KEY }} | |
| - name: Download macOS binary | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: wt-darwin-arm64 | |
| path: bin | |
| - name: Prepare binary | |
| run: | | |
| mv bin/wt-darwin-arm64 bin/wt | |
| chmod +x bin/wt | |
| ./bin/wt --help | |
| file bin/wt | |
| - name: Run e2e tests for ${{ matrix.shell }} | |
| run: | | |
| # Set up isolated test environment | |
| export ISOLATED_TMPDIR=$(mktemp -d) | |
| export HOME=$ISOLATED_TMPDIR | |
| # Run specific e2e tests (not ./... to avoid redundancy) | |
| if [ "${{ matrix.shell }}" = "bash" ]; then | |
| # Run both old e2e tests and new PTY interactive tests for bash | |
| go test -v -run 'TestE2EAutoCdWithNonInteractiveCommand|TestE2EAutoCdWithCreate|TestInteractiveCheckoutWithoutArgsBash|TestNonInteractiveCheckoutWithArgsBash' . | |
| elif [ "${{ matrix.shell }}" = "zsh" ]; then | |
| # Run both old e2e test and new PTY interactive tests for zsh | |
| go test -v -run 'TestE2EAutoCdInZsh|TestInteractiveCheckoutWithoutArgs|TestNonInteractiveCheckoutWithArgs' . | |
| fi | |
| # Cleanup (best effort - ignore errors from Go cache files) | |
| rm -rf "$ISOLATED_TMPDIR" 2>/dev/null || true | |
| - name: Test shellenv output | |
| run: | | |
| # Capture shellenv output for inspection | |
| ./bin/wt shellenv > shellenv-${{ matrix.shell }}.txt | |
| # Verify shellenv produces output | |
| if [ ! -s shellenv-${{ matrix.shell }}.txt ]; then | |
| echo "ERROR: shellenv produced no output" | |
| exit 1 | |
| fi | |
| # Check for key components (wt function) | |
| if ! grep -q "wt()" shellenv-${{ matrix.shell }}.txt; then | |
| echo "ERROR: wt function not found in shellenv output" | |
| exit 1 | |
| fi | |
| # Verify shell-specific features | |
| if [ "${{ matrix.shell }}" = "bash" ]; then | |
| grep -q "BASH_VERSION" shellenv-${{ matrix.shell }}.txt || (echo "ERROR: bash-specific code not found" && exit 1) | |
| elif [ "${{ matrix.shell }}" = "zsh" ]; then | |
| grep -q "ZSH_VERSION" shellenv-${{ matrix.shell }}.txt || (echo "ERROR: zsh-specific code not found" && exit 1) | |
| fi | |
| echo "shellenv validation passed for ${{ matrix.shell }}" | |
| - name: Test worktree CRUD operations with auto-cd | |
| run: | | |
| # Save absolute paths BEFORE changing directories | |
| WORK_DIR=$(pwd) | |
| WT_BIN="$WORK_DIR/bin/wt" | |
| WT_BIN_DIR="$WORK_DIR/bin" | |
| # Create isolated test environment with separate HOME | |
| ISOLATED_HOME=$(mktemp -d) | |
| WORKTREE_ROOT=$(mktemp -d)/worktrees | |
| TEST_REPO=$(mktemp -d)/test-repo | |
| # Generate shellenv script | |
| $WT_BIN shellenv > /tmp/wt-shellenv-${{ matrix.shell }}.sh | |
| # Create test git repo | |
| mkdir -p $TEST_REPO | |
| cd $TEST_REPO | |
| export HOME=$ISOLATED_HOME | |
| git init | |
| git config user.email "test@example.com" | |
| git config user.name "Test User" | |
| git commit --allow-empty -m "initial commit" | |
| git branch -M main | |
| # Create a test branch | |
| git checkout -b test-branch | |
| git commit --allow-empty -m "test commit" | |
| git checkout main | |
| # Determine shell command | |
| if [ "${{ matrix.shell }}" = "bash" ]; then | |
| SHELL_CMD="bash" | |
| else | |
| SHELL_CMD="zsh" | |
| fi | |
| # Save the current PATH so git is available in the subshell | |
| CURRENT_PATH="$PATH" | |
| # Run test in a subshell with shellenv sourced | |
| $SHELL_CMD -c " | |
| export HOME='$ISOLATED_HOME' | |
| export WORKTREE_ROOT='$WORKTREE_ROOT' | |
| export PATH='$WT_BIN_DIR:$CURRENT_PATH' | |
| cd '$TEST_REPO' | |
| source /tmp/wt-shellenv-${{ matrix.shell }}.sh | |
| echo 'Testing: wt checkout test-branch' | |
| wt checkout test-branch | |
| # Verify we're in the worktree directory | |
| CURRENT_DIR=\$(pwd) | |
| EXPECTED_DIR='$WORKTREE_ROOT/test-repo/test-branch' | |
| if [[ \"\$CURRENT_DIR\" != \"\$EXPECTED_DIR\" ]]; then | |
| echo \"ERROR: Auto-cd failed. Expected: \$EXPECTED_DIR, Got: \$CURRENT_DIR\" | |
| exit 1 | |
| fi | |
| echo \"✓ Auto-cd to worktree verified: \$CURRENT_DIR\" | |
| # Verify worktree was created | |
| wt list | grep test-branch || (echo 'ERROR: worktree not in list' && exit 1) | |
| echo '✓ Worktree created and listed' | |
| # Go back to original repo to test create | |
| cd '$TEST_REPO' | |
| # Test create command (from main repo) | |
| echo 'Testing: wt create feature-test' | |
| wt create feature-test | |
| CURRENT_DIR=\$(pwd) | |
| EXPECTED_DIR='$WORKTREE_ROOT/test-repo/feature-test' | |
| if [[ \"\$CURRENT_DIR\" != \"\$EXPECTED_DIR\" ]]; then | |
| echo \"ERROR: Auto-cd failed for create. Expected: \$EXPECTED_DIR, Got: \$CURRENT_DIR\" | |
| exit 1 | |
| fi | |
| echo \"✓ Auto-cd to new worktree verified: \$CURRENT_DIR\" | |
| # Go back to original repo to test remove | |
| cd '$TEST_REPO' | |
| # Test remove command | |
| echo 'Testing: wt remove test-branch' | |
| wt remove test-branch | |
| # Verify worktree was removed | |
| if wt list | grep test-branch; then | |
| echo 'ERROR: worktree not removed' | |
| exit 1 | |
| fi | |
| echo '✓ Worktree removed successfully' | |
| echo 'CRUD operations with auto-cd test passed!' | |
| " | |
| # Cleanup all temporary directories (best effort) | |
| cd "$WORK_DIR" | |
| rm -rf "$ISOLATED_HOME" "$WORKTREE_ROOT" "$TEST_REPO" /tmp/wt-shellenv-${{ matrix.shell }}.sh 2>/dev/null || true | |
| - name: Upload shellenv output as artifact | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: shellenv-output-linux-${{ matrix.shell }} | |
| path: shellenv-${{ matrix.shell }}.txt | |
| retention-days: 7 | |
| if-no-files-found: ignore | |
| - name: Upload test logs | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: e2e-logs-linux-${{ matrix.shell }} | |
| path: | | |
| /tmp/*.log | |
| /var/tmp/*.log | |
| retention-days: 7 | |
| if-no-files-found: ignore | |
| e2e-linux: | |
| name: E2E Tests (Linux) | |
| runs-on: ubuntu-latest | |
| needs: build | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shell: [bash, zsh] | |
| # Note: fish is excluded for now as wt shellenv doesn't support fish yet | |
| steps: | |
| - name: Setup Go and checkout | |
| uses: ./.github/actions/setup-go-wt | |
| with: | |
| bot-app-id: ${{ secrets.BOT_APP_ID }} | |
| bot-private-key: ${{ secrets.BOT_PRIVATE_KEY }} | |
| - name: Install zsh | |
| if: matrix.shell == 'zsh' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y zsh | |
| - name: Download Linux binary | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: wt-linux-amd64 | |
| path: bin | |
| - name: Prepare binary | |
| run: | | |
| mv bin/wt-linux-amd64 bin/wt | |
| chmod +x bin/wt | |
| ./bin/wt --help | |
| file bin/wt | |
| - name: Run e2e tests for ${{ matrix.shell }} | |
| run: | | |
| # Set up isolated test environment | |
| export TMPDIR=$(mktemp -d) | |
| export HOME=$TMPDIR | |
| # Run e2e tests with verbose output | |
| if [ "${{ matrix.shell }}" = "bash" ]; then | |
| go test -v -run TestE2EAutoCdWithNonInteractiveCommand ./... | |
| go test -v -run TestE2EAutoCdWithCreate ./... | |
| elif [ "${{ matrix.shell }}" = "zsh" ]; then | |
| go test -v -run TestE2EAutoCdInZsh ./... | |
| fi | |
| - name: Test shellenv output | |
| run: | | |
| # Capture shellenv output for inspection | |
| ./bin/wt shellenv > shellenv-${{ matrix.shell }}.txt | |
| # Verify shellenv produces output | |
| if [ ! -s shellenv-${{ matrix.shell }}.txt ]; then | |
| echo "ERROR: shellenv produced no output" | |
| exit 1 | |
| fi | |
| # Check for key components (wt function) | |
| if ! grep -q "wt()" shellenv-${{ matrix.shell }}.txt; then | |
| echo "ERROR: wt function not found in shellenv output" | |
| exit 1 | |
| fi | |
| # Verify shell-specific features | |
| if [ "${{ matrix.shell }}" = "bash" ]; then | |
| grep -q "BASH_VERSION" shellenv-${{ matrix.shell }}.txt || (echo "ERROR: bash-specific code not found" && exit 1) | |
| elif [ "${{ matrix.shell }}" = "zsh" ]; then | |
| grep -q "ZSH_VERSION" shellenv-${{ matrix.shell }}.txt || (echo "ERROR: zsh-specific code not found" && exit 1) | |
| fi | |
| echo "shellenv validation passed for ${{ matrix.shell }}" | |
| - name: Test worktree CRUD operations with auto-cd | |
| run: | | |
| # Save workspace directory before creating test repo | |
| WORKSPACE_DIR=$(pwd) | |
| WT_BIN="$WORKSPACE_DIR/bin/wt" | |
| WT_BIN_DIR="$WORKSPACE_DIR/bin" | |
| # Create isolated test environment | |
| export WORKTREE_ROOT=$(mktemp -d)/worktrees | |
| export PATH=$WT_BIN_DIR:$PATH | |
| # Create a test git repo | |
| TEST_REPO=$(mktemp -d)/test-repo | |
| mkdir -p $TEST_REPO | |
| cd $TEST_REPO | |
| git init | |
| git config user.email "test@example.com" | |
| git config user.name "Test User" | |
| git commit --allow-empty -m "initial commit" | |
| git branch -M main | |
| # Create a test branch | |
| git checkout -b test-branch | |
| git commit --allow-empty -m "test commit" | |
| git checkout main | |
| # Test with shell integration | |
| if [ "${{ matrix.shell }}" = "bash" ]; then | |
| SHELL_CMD="bash" | |
| else | |
| SHELL_CMD="zsh" | |
| fi | |
| # Generate shellenv script for the shell | |
| $WT_BIN shellenv > /tmp/wt-shellenv-${{ matrix.shell }}.sh | |
| # Run test in a subshell with shellenv sourced | |
| $SHELL_CMD -c " | |
| export WORKTREE_ROOT=$WORKTREE_ROOT | |
| export PATH=$WT_BIN_DIR:\$PATH | |
| cd $TEST_REPO | |
| source /tmp/wt-shellenv-${{ matrix.shell }}.sh | |
| echo 'Testing: wt checkout test-branch' | |
| wt checkout test-branch | |
| # Verify we're in the worktree directory | |
| CURRENT_DIR=\$(pwd) | |
| EXPECTED_DIR=$WORKTREE_ROOT/test-repo/test-branch | |
| if [[ \"\$CURRENT_DIR\" != \"\$EXPECTED_DIR\" ]]; then | |
| echo \"ERROR: Auto-cd failed. Expected: \$EXPECTED_DIR, Got: \$CURRENT_DIR\" | |
| exit 1 | |
| fi | |
| echo \"✓ Auto-cd to worktree verified: \$CURRENT_DIR\" | |
| # Verify worktree was created | |
| wt list | grep test-branch || (echo 'ERROR: worktree not in list' && exit 1) | |
| echo '✓ Worktree created and listed' | |
| # Go back to original repo to test create | |
| cd $TEST_REPO | |
| # Test create command | |
| echo 'Testing: wt create feature-test' | |
| wt create feature-test | |
| CURRENT_DIR=\$(pwd) | |
| EXPECTED_DIR=$WORKTREE_ROOT/test-repo/feature-test | |
| if [[ \"\$CURRENT_DIR\" != \"\$EXPECTED_DIR\" ]]; then | |
| echo \"ERROR: Auto-cd failed for create. Expected: \$EXPECTED_DIR, Got: \$CURRENT_DIR\" | |
| exit 1 | |
| fi | |
| echo \"✓ Auto-cd to new worktree verified: \$CURRENT_DIR\" | |
| # Go back to original repo to test remove | |
| cd $TEST_REPO | |
| # Test remove command | |
| echo 'Testing: wt remove test-branch' | |
| wt remove test-branch | |
| # Verify worktree was removed | |
| if wt list | grep test-branch; then | |
| echo 'ERROR: worktree not removed' | |
| exit 1 | |
| fi | |
| echo '✓ Worktree removed successfully' | |
| echo 'CRUD operations with auto-cd test passed!' | |
| " | |
| - name: Upload shellenv output as artifact | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: shellenv-output-${{ matrix.shell }} | |
| path: shellenv-${{ matrix.shell }}.txt | |
| retention-days: 7 | |
| - name: Upload test logs | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: e2e-logs-${{ matrix.shell }} | |
| path: | | |
| /tmp/*.log | |
| /var/tmp/*.log | |
| retention-days: 7 | |
| if-no-files-found: ignore | |
| e2e-windows: | |
| name: E2E Tests (Windows) | |
| runs-on: windows-latest | |
| needs: build | |
| strategy: | |
| matrix: | |
| shell: ['powershell', 'pwsh'] | |
| steps: | |
| - name: Setup Go and checkout | |
| uses: ./.github/actions/setup-go-wt | |
| with: | |
| bot-app-id: ${{ secrets.BOT_APP_ID }} | |
| bot-private-key: ${{ secrets.BOT_PRIVATE_KEY }} | |
| - name: Download Windows binary | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: wt-windows-amd64 | |
| path: bin | |
| - name: Prepare binary | |
| run: | | |
| mv bin/wt-windows-amd64.exe bin/wt.exe | |
| ./bin/wt.exe --help | |
| file bin/wt.exe | |
| - name: Run e2e tests for ${{ matrix.shell }} | |
| shell: pwsh | |
| run: | | |
| # Set up isolated test environment | |
| $env:ISOLATED_TMPDIR = New-TemporaryFile | ForEach-Object { Remove-Item $_; New-Item -ItemType Directory -Path $_ } | |
| $env:HOME = $env:ISOLATED_TMPDIR | |
| # Run specific e2e tests based on shell | |
| # Note: Interactive tests are skipped because promptui doesn't support Windows | |
| if ('${{ matrix.shell }}' -eq 'powershell') { | |
| # Run non-interactive tests for PowerShell 5.1 | |
| go test -v -run 'TestNonInteractiveCheckoutWithArgsPowerShell' . | |
| } elseif ('${{ matrix.shell }}' -eq 'pwsh') { | |
| # Run non-interactive tests for PowerShell 7 (promptui doesn't support Windows interactive prompts) | |
| go test -v -run 'TestNonInteractiveCheckoutWithArgsPowerShell' . | |
| } | |
| - name: Upload test logs | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: e2e-logs-windows-${{ matrix.shell }} | |
| path: | | |
| ${{ env.TEMP }}/*.log | |
| retention-days: 7 | |
| if-no-files-found: ignore |