Smoke Tests #151
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: Smoke Tests | |
| on: | |
| schedule: | |
| - cron: "0 6 * * *" | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| smoke: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Get latest release tag | |
| id: release | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| TAG=$(gh release view --json tagName -q .tagName) | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| echo "Latest release: $TAG" | |
| - name: Map runner to archive name | |
| id: archive | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| case "$RUNNER_OS" in | |
| Linux) OS=linux ;; | |
| macOS) OS=darwin ;; | |
| Windows) OS=windows ;; | |
| *) echo "Unsupported OS: $RUNNER_OS"; exit 1 ;; | |
| esac | |
| case "$RUNNER_ARCH" in | |
| X64) ARCH=amd64 ;; | |
| ARM64) ARCH=arm64 ;; | |
| *) echo "Unsupported arch: $RUNNER_ARCH"; exit 1 ;; | |
| esac | |
| if [ "$OS" = "windows" ]; then | |
| NAME="aitutor_${OS}_${ARCH}.zip" | |
| else | |
| NAME="aitutor_${OS}_${ARCH}.tar.gz" | |
| fi | |
| echo "name=$NAME" >> "$GITHUB_OUTPUT" | |
| echo "os=$OS" >> "$GITHUB_OUTPUT" | |
| echo "Archive: $NAME" | |
| - name: Download release archive | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: gh release download ${{ steps.release.outputs.tag }} -p "${{ steps.archive.outputs.name }}" | |
| - name: Extract binary (Linux/macOS) | |
| if: runner.os != 'Windows' | |
| run: tar xzf ${{ steps.archive.outputs.name }} | |
| - name: Extract binary (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: Expand-Archive -Path ${{ steps.archive.outputs.name }} -DestinationPath . -Force | |
| - name: Smoke test (Linux) | |
| if: runner.os == 'Linux' | |
| run: | | |
| chmod +x ./aitutor | |
| timeout 5 script -qec "./aitutor" output.txt || true | |
| # Strip ANSI escape sequences (CSI, private modes, mode sets) | |
| perl -pe 's/\e[\[\(][?]?[0-9;]*[a-zA-Z~]//g; s/\e[=>]//g; s/\r//g' output.txt > clean.txt | |
| echo "--- Captured output ---" | |
| cat clean.txt | |
| echo "--- Checking content ---" | |
| grep -q "Beginner" clean.txt | |
| grep -qi "theory" clean.txt | |
| grep -q "quit" clean.txt | |
| echo "All checks passed" | |
| - name: Smoke test (macOS) | |
| if: runner.os == 'macOS' | |
| run: | | |
| chmod +x ./aitutor | |
| # Run app inside script session with internal timeout so PTY is allocated properly | |
| script -q output.txt bash -c './aitutor & PID=$!; sleep 5; kill $PID 2>/dev/null; exit 0' || true | |
| # Strip ANSI escape sequences (CSI, private modes, mode sets) | |
| perl -pe 's/\e[\[\(][?]?[0-9;]*[a-zA-Z~]//g; s/\e[=>]//g; s/\r//g' output.txt > clean.txt | |
| echo "--- Captured output ---" | |
| cat clean.txt | |
| echo "--- Checking content ---" | |
| grep -q "Beginner" clean.txt | |
| grep -qi "theory" clean.txt | |
| grep -q "quit" clean.txt | |
| echo "All checks passed" | |
| - name: Smoke test (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $proc = Start-Process -FilePath .\aitutor.exe -PassThru -NoNewWindow -RedirectStandardOutput output.txt -RedirectStandardError stderr.txt | |
| Start-Sleep -Seconds 5 | |
| if ($proc.HasExited -and $proc.ExitCode -ne 0) { | |
| Write-Error "Binary crashed with exit code $($proc.ExitCode)" | |
| exit 1 | |
| } | |
| if (-not $proc.HasExited) { | |
| Stop-Process -Id $proc.Id -Force | |
| } | |
| Write-Output "Binary started successfully without crashing" | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| - name: Smoke test npm package (Linux) | |
| if: runner.os == 'Linux' | |
| run: | | |
| npm install -g @aitutor/cli | |
| BINARY=$(which aitutor) | |
| echo "npm binary: $BINARY" | |
| timeout 5 script -qec "$BINARY" npm_output.txt || true | |
| perl -pe 's/\e[\[\(][?]?[0-9;]*[a-zA-Z~]//g; s/\e[=>]//g; s/\r//g' npm_output.txt > npm_clean.txt | |
| grep -q "Beginner" npm_clean.txt | |
| grep -q "quit" npm_clean.txt | |
| echo "npm smoke test passed" | |
| - name: Smoke test npm package (macOS) | |
| if: runner.os == 'macOS' | |
| run: | | |
| npm install -g @aitutor/cli | |
| BINARY=$(which aitutor) | |
| echo "npm binary: $BINARY" | |
| export AITUTOR_BIN="$BINARY" | |
| script -q npm_output.txt bash -c '$AITUTOR_BIN & PID=$!; sleep 5; kill $PID 2>/dev/null; exit 0' || true | |
| perl -pe 's/\e[\[\(][?]?[0-9;]*[a-zA-Z~]//g; s/\e[=>]//g; s/\r//g' npm_output.txt > npm_clean.txt | |
| grep -q "Beginner" npm_clean.txt | |
| grep -q "quit" npm_clean.txt | |
| echo "npm smoke test passed" | |
| - name: Smoke test npm package (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| npm install -g @aitutor/cli | |
| # Get-Command returns the .ps1 wrapper; find the actual .exe in the package dir | |
| $npmRoot = (npm root -g) | |
| $binary = Join-Path $npmRoot "@aitutor\cli\aitutor.exe" | |
| Write-Output "npm binary: $binary" | |
| $proc = Start-Process -FilePath $binary -PassThru -NoNewWindow | |
| Start-Sleep -Seconds 5 | |
| if ($proc.HasExited -and $proc.ExitCode -ne 0) { | |
| Write-Error "npm binary crashed with exit code $($proc.ExitCode)" | |
| exit 1 | |
| } | |
| if (-not $proc.HasExited) { | |
| Stop-Process -Id $proc.Id -Force | |
| } | |
| Write-Output "npm smoke test passed" |