From 6b6618cca7d8d09f5b4cd9a314ecde2dc0e7ef7a Mon Sep 17 00:00:00 2001 From: MK Date: Sat, 28 Feb 2026 10:58:39 +0800 Subject: [PATCH 1/2] ci: add post-release smoke test workflow Adds automated testing of published npm packages after release.yml completes. Tests standalone install (Linux, macOS, Windows), npm install with NAPI binding verification, and package metadata checks. Creates a GitHub issue on failure. --- .github/workflows/post-release-test.yml | 454 ++++++++++++++++++++++++ 1 file changed, 454 insertions(+) create mode 100644 .github/workflows/post-release-test.yml diff --git a/.github/workflows/post-release-test.yml b/.github/workflows/post-release-test.yml new file mode 100644 index 0000000000..dc5c3be760 --- /dev/null +++ b/.github/workflows/post-release-test.yml @@ -0,0 +1,454 @@ +name: Post-Release Test + +permissions: {} + +on: + workflow_run: + workflows: ['Release'] + types: [completed] + +defaults: + run: + shell: bash + +jobs: + extract-version: + name: Extract release version + runs-on: ubuntu-latest + if: github.event.workflow_run.conclusion == 'success' + permissions: + contents: read + outputs: + version: ${{ steps.version.outputs.version }} + npm_tag: ${{ steps.version.outputs.npm_tag }} + steps: + - name: Find GitHub release for this commit + id: version + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + HEAD_SHA: ${{ github.event.workflow_run.head_sha }} + run: | + # Find the release whose target_commitish matches the workflow run's head SHA + RELEASE_JSON=$(gh api repos/$GH_REPO/releases --jq ".[] | select(.target_commitish == \"$HEAD_SHA\") | {tag_name, prerelease}" | head -1) + if [ -z "$RELEASE_JSON" ]; then + echo "Error: No GitHub release found for commit $HEAD_SHA" + exit 1 + fi + + TAG_NAME=$(echo "$RELEASE_JSON" | jq -r '.tag_name') + PRERELEASE=$(echo "$RELEASE_JSON" | jq -r '.prerelease') + + # Strip 'v' prefix to get version + VERSION="${TAG_NAME#v}" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + # Determine npm tag + if [ "$PRERELEASE" = "true" ]; then + echo "npm_tag=test" >> "$GITHUB_OUTPUT" + else + echo "npm_tag=latest" >> "$GITHUB_OUTPUT" + fi + + echo "Found release: tag=$TAG_NAME version=$VERSION prerelease=$PRERELEASE" + + wait-for-npm-propagation: + name: Wait for npm propagation + runs-on: ubuntu-latest + needs: extract-version + permissions: + contents: read + env: + VERSION: ${{ needs.extract-version.outputs.version }} + steps: + - name: Wait for all packages to be available on npm + run: | + PACKAGES=( + "vite-plus" + "@voidzero-dev/vite-plus-core" + "@voidzero-dev/vite-plus-test" + "@voidzero-dev/vite-plus-cli-darwin-arm64" + "@voidzero-dev/vite-plus-cli-darwin-x64" + "@voidzero-dev/vite-plus-cli-linux-x64-gnu" + "@voidzero-dev/vite-plus-cli-linux-arm64-gnu" + "@voidzero-dev/vite-plus-cli-win32-x64-msvc" + "@voidzero-dev/vite-plus-cli-win32-arm64-msvc" + ) + + MAX_ATTEMPTS=30 + INTERVAL=10 + + for pkg in "${PACKAGES[@]}"; do + echo "Waiting for $pkg@$VERSION on npm..." + for attempt in $(seq 1 $MAX_ATTEMPTS); do + HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://registry.npmjs.org/$pkg/$VERSION") + if [ "$HTTP_STATUS" = "200" ]; then + echo " $pkg@$VERSION is available (attempt $attempt)" + break + fi + if [ "$attempt" = "$MAX_ATTEMPTS" ]; then + echo "Error: $pkg@$VERSION not found on npm after $MAX_ATTEMPTS attempts" + exit 1 + fi + echo " Attempt $attempt/$MAX_ATTEMPTS: HTTP $HTTP_STATUS, retrying in ${INTERVAL}s..." + sleep $INTERVAL + done + done + + echo "All packages are available on npm." + + verify-npm-packages: + name: Verify npm package metadata + runs-on: ubuntu-latest + needs: [extract-version, wait-for-npm-propagation] + permissions: + contents: read + env: + VERSION: ${{ needs.extract-version.outputs.version }} + steps: + - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + with: + node-version: 22 + registry-url: 'https://registry.npmjs.org' + + - name: Verify vite-plus package + run: | + echo "Verifying vite-plus@$VERSION..." + PKG_JSON=$(npm view "vite-plus@$VERSION" --json) + + # Verify version + ACTUAL_VERSION=$(echo "$PKG_JSON" | jq -r '.version') + if [ "$ACTUAL_VERSION" != "$VERSION" ]; then + echo "Error: Expected version $VERSION, got $ACTUAL_VERSION" + exit 1 + fi + echo " Version: $ACTUAL_VERSION ✓" + + # Verify bin.vp exists + VP_BIN=$(echo "$PKG_JSON" | jq -r '.bin.vp // empty') + if [ -z "$VP_BIN" ]; then + echo "Error: bin.vp not found in package.json" + exit 1 + fi + echo " bin.vp: $VP_BIN ✓" + + # Verify dependencies exist + DEPS=$(echo "$PKG_JSON" | jq -r '.dependencies | keys[]' 2>/dev/null || echo "") + echo " Dependencies: $DEPS" + + - name: Verify core and test packages + run: | + for pkg in "@voidzero-dev/vite-plus-core" "@voidzero-dev/vite-plus-test"; do + echo "Verifying $pkg@$VERSION..." + ACTUAL_VERSION=$(npm view "$pkg@$VERSION" version --json 2>/dev/null | jq -r '.' 2>/dev/null || npm view "$pkg@$VERSION" version) + if [ "$ACTUAL_VERSION" != "$VERSION" ]; then + echo "Error: $pkg expected version $VERSION, got $ACTUAL_VERSION" + exit 1 + fi + echo " $pkg@$VERSION ✓" + done + + - name: Verify CLI platform packages + run: | + PLATFORMS=( + "darwin-arm64" + "darwin-x64" + "linux-x64-gnu" + "linux-arm64-gnu" + "win32-x64-msvc" + "win32-arm64-msvc" + ) + for platform in "${PLATFORMS[@]}"; do + pkg="@voidzero-dev/vite-plus-cli-$platform" + echo "Verifying $pkg@$VERSION..." + ACTUAL_VERSION=$(npm view "$pkg@$VERSION" version --json 2>/dev/null | jq -r '.' 2>/dev/null || npm view "$pkg@$VERSION" version) + if [ "$ACTUAL_VERSION" != "$VERSION" ]; then + echo "Error: $pkg expected version $VERSION, got $ACTUAL_VERSION" + exit 1 + fi + echo " $pkg@$VERSION ✓" + done + + test-standalone-install-sh: + name: Test install.sh (${{ matrix.name }}) + runs-on: ${{ matrix.os }} + needs: [extract-version, wait-for-npm-propagation] + permissions: + contents: read + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + name: Linux x64 glibc + - os: macos-15-intel + name: macOS x64 + - os: namespace-profile-mac-default + name: macOS ARM64 + env: + VITE_PLUS_VERSION: ${{ needs.extract-version.outputs.version }} + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - name: Run install.sh + run: cat packages/cli/install.sh | bash + + - name: Verify installation + working-directory: ${{ runner.temp }} + run: | + # Source shell config to get PATH updated + if [ -f ~/.zshenv ]; then + source ~/.zshenv + elif [ -f ~/.zshrc ]; then + source ~/.zshrc + elif [ -f ~/.bash_profile ]; then + source ~/.bash_profile + elif [ -f ~/.bashrc ]; then + source ~/.bashrc + else + export PATH="$HOME/.vite-plus/bin:$PATH" + fi + echo "PATH: $PATH" + + # Verify version + VP_VERSION=$(vp --version) + echo "vp --version: $VP_VERSION" + if [[ "$VP_VERSION" != *"$VITE_PLUS_VERSION"* ]]; then + echo "Error: Expected version to contain $VITE_PLUS_VERSION, got $VP_VERSION" + exit 1 + fi + + - name: Set PATH + run: | + echo "$HOME/.vite-plus/bin" >> $GITHUB_PATH + + - name: Verify bin setup + run: | + BIN_PATH="$HOME/.vite-plus/bin" + ls -al "$BIN_PATH" + + # Verify shim executables exist + for shim in node npm npx; do + if [ ! -f "$BIN_PATH/$shim" ]; then + echo "Error: Shim not found: $BIN_PATH/$shim" + exit 1 + fi + echo "Found shim: $BIN_PATH/$shim" + done + + vp env doctor + + - name: Test create and build + working-directory: ${{ runner.temp }} + run: | + vp create vite --no-interactive --no-agent -- hello --no-interactive -t vanilla + cd hello && vp run build + + - name: Test env install + run: | + vp env install 22 + node --version + + - name: Test global install + run: | + vp install -g typescript + tsc --version + vp uninstall -g typescript + + - name: Test dlx + run: | + vp dlx print-current-version + + - name: Test upgrade check + run: | + vp upgrade --check + + test-standalone-install-ps1: + name: Test install.ps1 (Windows x64) + runs-on: windows-latest + needs: [extract-version, wait-for-npm-propagation] + permissions: + contents: read + env: + VITE_PLUS_VERSION: ${{ needs.extract-version.outputs.version }} + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - name: Run install.ps1 + shell: pwsh + run: | + & ./packages/cli/install.ps1 + + - name: Set PATH + shell: bash + run: | + echo "$USERPROFILE\.vite-plus\bin" >> $GITHUB_PATH + + - name: Verify installation + shell: pwsh + working-directory: ${{ runner.temp }} + run: | + Write-Host "PATH: $env:Path" + + # Verify version + $vpVersion = vp --version + Write-Host "vp --version: $vpVersion" + if ($vpVersion -notmatch [regex]::Escape($env:VITE_PLUS_VERSION)) { + Write-Error "Expected version to contain $env:VITE_PLUS_VERSION, got $vpVersion" + exit 1 + } + + - name: Verify bin setup + shell: pwsh + run: | + $binPath = "$env:USERPROFILE\.vite-plus\bin" + Get-ChildItem -Force $binPath + + $expectedShims = @("node.cmd", "npm.cmd", "npx.cmd") + foreach ($shim in $expectedShims) { + $shimFile = Join-Path $binPath $shim + if (-not (Test-Path $shimFile)) { + Write-Error "Shim not found: $shimFile" + exit 1 + } + Write-Host "Found shim: $shimFile" + } + + $env:Path = "$env:USERPROFILE\.vite-plus\bin;$env:Path" + vp env doctor + + - name: Test create and build + shell: pwsh + working-directory: ${{ runner.temp }} + run: | + vp create vite --no-interactive --no-agent -- hello --no-interactive -t vanilla + cd hello + vp run build + + - name: Test env install + shell: pwsh + run: | + vp env install 22 + node --version + + - name: Test global install + shell: pwsh + run: | + vp install -g typescript + tsc --version + vp uninstall -g typescript + + - name: Test dlx + shell: pwsh + run: | + vp dlx print-current-version + + - name: Test upgrade check + shell: pwsh + run: | + vp upgrade --check + + test-npm-install: + name: Test npm install (${{ matrix.name }}) + runs-on: ${{ matrix.os }} + needs: [extract-version, wait-for-npm-propagation] + permissions: + contents: read + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + name: Linux x64 + - os: windows-latest + name: Windows x64 + env: + VERSION: ${{ needs.extract-version.outputs.version }} + steps: + - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + with: + node-version: 22 + registry-url: 'https://registry.npmjs.org' + + - name: Install vite-plus from npm + working-directory: ${{ runner.temp }} + run: | + mkdir test-npm-install && cd test-npm-install + npm init -y + npm install "vite-plus@$VERSION" + + - name: Verify NAPI binding loads + working-directory: ${{ runner.temp }}/test-npm-install + run: | + node -e "require('vite-plus/binding'); console.log('NAPI binding loaded successfully')" + + - name: Verify npx vp works + working-directory: ${{ runner.temp }}/test-npm-install + run: | + VP_VERSION=$(npx vp --version) + echo "npx vp --version: $VP_VERSION" + npx vp --help + + notify-failure: + name: Notify on failure + runs-on: ubuntu-latest + needs: + - verify-npm-packages + - test-standalone-install-sh + - test-standalone-install-ps1 + - test-npm-install + if: failure() + permissions: + contents: read + issues: write + steps: + - name: Create or update GitHub issue on failure + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + run: | + ISSUE_TITLE="Post-Release Test Failed" + ISSUE_LABEL="post-release-failure" + RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + RELEASE_SHA="${{ github.event.workflow_run.head_sha }}" + + # Create label if it doesn't exist + if ! gh label list --json name --jq '.[].name' | grep -q "^${ISSUE_LABEL}$"; then + CREATE_LABEL_OUTPUT=$(gh label create "$ISSUE_LABEL" --color "d73a4a" --description "Post-release test failure" 2>&1) + if [ $? -eq 0 ]; then + echo "Created label: $ISSUE_LABEL" + elif echo "$CREATE_LABEL_OUTPUT" | grep -qi "already exists"; then + echo "Label '$ISSUE_LABEL' already exists, continuing." + else + echo "Error: Failed to create label '$ISSUE_LABEL':" + echo "$CREATE_LABEL_OUTPUT" >&2 + exit 1 + fi + fi + + # Search for existing open issue with the label + EXISTING_ISSUE=$(gh issue list --label "$ISSUE_LABEL" --state open --json number --jq '.[0].number') + + if [ -z "$EXISTING_ISSUE" ]; then + # Create new issue if none exists + gh issue create \ + --title "$ISSUE_TITLE" \ + --label "$ISSUE_LABEL" \ + --body "The post-release smoke test has failed for published npm packages. + + **Failed Run:** $RUN_URL + **Release Commit:** $RELEASE_SHA + **Time:** $(date -u '+%Y-%m-%d %H:%M:%S UTC') + + Please investigate whether the published packages are functional." + echo "Created new issue" + else + # Add comment to existing issue + gh issue comment "$EXISTING_ISSUE" \ + --body "The post-release smoke test has failed again. + + **Failed Run:** $RUN_URL + **Release Commit:** $RELEASE_SHA + **Time:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')" + echo "Added comment to issue #$EXISTING_ISSUE" + fi From 6dfdffef89ce6acdd5658ff477d8b687f32ae6f8 Mon Sep 17 00:00:00 2001 From: MK Date: Sat, 28 Feb 2026 11:01:13 +0800 Subject: [PATCH 2/2] ci(post-release-test): add workflow_dispatch trigger for manual testing Allows manually triggering the post-release test with a specific version string to test previously released packages or conduct temporary script tests. --- .github/workflows/post-release-test.yml | 55 ++++++++++++++++--------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/.github/workflows/post-release-test.yml b/.github/workflows/post-release-test.yml index dc5c3be760..31b96ba26c 100644 --- a/.github/workflows/post-release-test.yml +++ b/.github/workflows/post-release-test.yml @@ -3,6 +3,12 @@ name: Post-Release Test permissions: {} on: + workflow_dispatch: + inputs: + version: + description: 'Version to test (e.g., 0.0.0-g12345678.20260228-1200)' + required: true + type: string workflow_run: workflows: ['Release'] types: [completed] @@ -15,42 +21,51 @@ jobs: extract-version: name: Extract release version runs-on: ubuntu-latest - if: github.event.workflow_run.conclusion == 'success' + # For workflow_run: only run if the release succeeded + # For workflow_dispatch: always run (manual trigger with explicit version) + if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' permissions: contents: read outputs: version: ${{ steps.version.outputs.version }} npm_tag: ${{ steps.version.outputs.npm_tag }} steps: - - name: Find GitHub release for this commit + - name: Resolve version id: version env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} + MANUAL_VERSION: ${{ inputs.version }} HEAD_SHA: ${{ github.event.workflow_run.head_sha }} run: | - # Find the release whose target_commitish matches the workflow run's head SHA - RELEASE_JSON=$(gh api repos/$GH_REPO/releases --jq ".[] | select(.target_commitish == \"$HEAD_SHA\") | {tag_name, prerelease}" | head -1) - if [ -z "$RELEASE_JSON" ]; then - echo "Error: No GitHub release found for commit $HEAD_SHA" - exit 1 - fi + if [ -n "$MANUAL_VERSION" ]; then + # Manual trigger: use the provided version directly + echo "version=$MANUAL_VERSION" >> "$GITHUB_OUTPUT" + echo "npm_tag=test" >> "$GITHUB_OUTPUT" + echo "Using manually specified version: $MANUAL_VERSION" + else + # Automatic trigger: find the release by commit SHA + RELEASE_JSON=$(gh api repos/$GH_REPO/releases --jq ".[] | select(.target_commitish == \"$HEAD_SHA\") | {tag_name, prerelease}" | head -1) + if [ -z "$RELEASE_JSON" ]; then + echo "Error: No GitHub release found for commit $HEAD_SHA" + exit 1 + fi - TAG_NAME=$(echo "$RELEASE_JSON" | jq -r '.tag_name') - PRERELEASE=$(echo "$RELEASE_JSON" | jq -r '.prerelease') + TAG_NAME=$(echo "$RELEASE_JSON" | jq -r '.tag_name') + PRERELEASE=$(echo "$RELEASE_JSON" | jq -r '.prerelease') - # Strip 'v' prefix to get version - VERSION="${TAG_NAME#v}" - echo "version=$VERSION" >> "$GITHUB_OUTPUT" + # Strip 'v' prefix to get version + VERSION="${TAG_NAME#v}" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" - # Determine npm tag - if [ "$PRERELEASE" = "true" ]; then - echo "npm_tag=test" >> "$GITHUB_OUTPUT" - else - echo "npm_tag=latest" >> "$GITHUB_OUTPUT" - fi + if [ "$PRERELEASE" = "true" ]; then + echo "npm_tag=test" >> "$GITHUB_OUTPUT" + else + echo "npm_tag=latest" >> "$GITHUB_OUTPUT" + fi - echo "Found release: tag=$TAG_NAME version=$VERSION prerelease=$PRERELEASE" + echo "Found release: tag=$TAG_NAME version=$VERSION prerelease=$PRERELEASE" + fi wait-for-npm-propagation: name: Wait for npm propagation