Skip to content

fix(ci): make npm scripts Windows-safe #148

fix(ci): make npm scripts Windows-safe

fix(ci): make npm scripts Windows-safe #148

Workflow file for this run

name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch: {}
jobs:
release-notes:
name: Release Notes
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, '[skip ci]')
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Init required submodules
run: |
git submodule update --init --depth 1 external/vscode-test-playwright
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: npm
- name: Install dependencies
run: npm ci
- name: Determine next version
id: version
uses: actions/github-script@v7
with:
script: |
const latestRelease = await github.rest.repos.getLatestRelease({ owner: context.repo.owner, repo: context.repo.repo }).catch(()=>null);
let nextVersion;
let prevTagRaw = '';
if (latestRelease) {
prevTagRaw = latestRelease.data.tag_name || '';
const prev = prevTagRaw.replace(/^v/, '');
const parts = prev.split('.');
if (parts.length !== 3 || parts.some(p => isNaN(Number(p)))) {
core.setFailed(`Latest release tag '${prevTagRaw}' is not semver x.y.z`);
return;
}
parts[2] = String(Number(parts[2]) + 1);
nextVersion = parts.join('.');
} else {
nextVersion = '0.0.1';
}
core.info(`Computed next version: ${nextVersion}`);
core.setOutput('version', nextVersion);
core.setOutput('previous_tag', prevTagRaw);
- name: Generate Context
id: context
run: |
PREV_TAG="${{ steps.version.outputs.previous_tag }}"
if [ -n "$PREV_TAG" ]; then
RANGE="$PREV_TAG..HEAD"
echo "Generating context for range: $RANGE"
else
RANGE=""
echo "Generating context for initial release (all history)"
fi
{
echo 'commits<<EOF'
git log --pretty=format:"- %s (%an) [%h]" $RANGE
echo ''
echo 'EOF'
} >> $GITHUB_OUTPUT
{
echo 'stats<<EOF'
if [ -n "$RANGE" ]; then
git diff --stat $RANGE
else
git show --stat
fi
echo 'EOF'
} >> $GITHUB_OUTPUT
- name: Generate Release Notes
run: npm run release:notes
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
VERSION: ${{ steps.version.outputs.version }}
PREV_TAG: ${{ steps.version.outputs.previous_tag }}
COMMITS: ${{ steps.context.outputs.commits }}
STATS: ${{ steps.context.outputs.stats }}
- name: Verify RELEASE_NOTES.md
run: |
test -s RELEASE_NOTES.md
echo "--- RELEASE_NOTES.md (first 40 lines) ---"
head -n 40 RELEASE_NOTES.md
- name: Upload release notes artifact
uses: actions/upload-artifact@v4
with:
name: release-notes
path: |
RELEASE_NOTES.md
build-and-test:
name: Build and Test
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [20.x]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Init required submodules
run: |
git submodule update --init --depth 1 external/vscode-test-playwright
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
- name: Install dependencies
run: npm ci
- name: Run lint
run: npm run lint
- name: Compile TypeScript
run: npm run compile
- name: Setup virtual display (Linux only)
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get update && sudo apt-get install -y xvfb
- name: Run tests
shell: bash
run: |
if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then
xvfb-run --auto-servernum --server-args='-screen 0 1280x1024x24' npm test
else
npm test
fi
env:
CI: true
auto-release:
name: Auto Release
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, '[skip ci]')
runs-on: ubuntu-latest
needs: [build-and-test, release-notes]
permissions:
contents: write
outputs:
version: ${{ steps.version.outputs.version }}
env:
USE_CODEX: 'false' # Set to 'true' to use OpenAI Codex instead of Claude
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Init required submodules
run: |
git submodule update --init --depth 1 external/vscode-test-playwright
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: npm
- name: Install dependencies
run: npm ci
- name: Install system dependencies for Playwright demo
run: |
sudo apt-get update
sudo apt-get install -y xvfb ffmpeg
- name: Determine next version
id: version
uses: actions/github-script@v7
with:
script: |
const latestRelease = await github.rest.repos.getLatestRelease({ owner: context.repo.owner, repo: context.repo.repo }).catch(()=>null);
let nextVersion;
let prevTagRaw = '';
if (latestRelease) {
prevTagRaw = latestRelease.data.tag_name || '';
const prev = prevTagRaw.replace(/^v/, '');
const parts = prev.split('.');
if (parts.length !== 3 || parts.some(p => isNaN(Number(p)))) {
core.setFailed(`Latest release tag '${prevTagRaw}' is not semver x.y.z`);
return;
}
parts[2] = String(Number(parts[2]) + 1);
nextVersion = parts.join('.');
} else {
nextVersion = '0.0.1';
}
core.info(`Computed next version: ${nextVersion}`);
core.setOutput('version', nextVersion);
core.setOutput('previous_tag', prevTagRaw);
- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Generate Context
id: context
run: |
PREV_TAG="${{ steps.version.outputs.previous_tag }}"
if [ -n "$PREV_TAG" ]; then
RANGE="$PREV_TAG..HEAD"
echo "Generating context for range: $RANGE"
else
RANGE=""
echo "Generating context for initial release (all history)"
fi
{
echo 'commits<<EOF'
git log --pretty=format:"- %s (%an) [%h]" $RANGE
echo ''
echo 'EOF'
} >> $GITHUB_OUTPUT
{
echo 'stats<<EOF'
if [ -n "$RANGE" ]; then
git diff --stat $RANGE
else
git show --stat
fi
echo 'EOF'
} >> $GITHUB_OUTPUT
- name: Generate Release Notes
run: npm run release:notes
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
VERSION: ${{ steps.version.outputs.version }}
PREV_TAG: ${{ steps.version.outputs.previous_tag }}
COMMITS: ${{ steps.context.outputs.commits }}
STATS: ${{ steps.context.outputs.stats }}
- name: Init demo video submodule
run: |
git submodule update --init --depth 1 external/playwright-test-videos
- name: Update README demo video
shell: bash
run: |
xvfb-run --auto-servernum --server-args='-screen 0 1280x1024x24' npm run demo:pw:update-readme
- name: Commit and Push Changes
run: |
git add CHANGELOG.md README.md package.json docs/pw-videos/demo.mp4
if git diff --staged --quiet; then
echo "No changes to commit"
else
git commit -m "docs: update for release ${{ steps.version.outputs.version }} [skip ci]"
git push origin main
fi
- name: Create Tag
run: |
git tag ${{ steps.version.outputs.version }}
git push origin ${{ steps.version.outputs.version }}
- name: Create Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.version.outputs.version }}
body_path: RELEASE_NOTES.md
token: ${{ secrets.GITHUB_TOKEN }}
package:
name: Package Extension
runs-on: ubuntu-latest
needs: build-and-test
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Init required submodules
run: |
git submodule update --init --depth 1 external/vscode-test-playwright
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: npm
- name: Install dependencies
run: npm ci
- name: Install vsce
run: npm install -g @vscode/vsce
- name: Package extension
run: vsce package
- name: Upload VSIX artifact
uses: actions/upload-artifact@v4
with:
name: vsix-package
path: '*.vsix'
retention-days: 30
publish:
name: Publish to VS Code Marketplace
runs-on: ubuntu-latest
needs: [package, auto-release]
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, '[skip ci]')
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Init required submodules
run: |
git submodule update --init --depth 1 external/vscode-test-playwright
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: npm
- name: Install dependencies
run: npm ci
- name: Install vsce
run: npm install -g @vscode/vsce
- name: Sync package.json version to tag (no commit)
env:
TAG_VERSION: ${{ needs.auto-release.outputs.version }}
run: |
node -e "const fs=require('fs'); const tag=process.env.TAG_VERSION; const pkg=JSON.parse(fs.readFileSync('package.json','utf8')); if(pkg.version!==tag){ pkg.version=tag; fs.writeFileSync('package.json', JSON.stringify(pkg,null,2)+'\n'); console.log('Synced package.json version to tag', tag); } else { console.log('package.json version already matches tag', tag); }"
- name: Publish to VS Code Marketplace
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
run: vsce publish -p $VSCE_PAT --allow-proposed-apis terminalDataWriteEvent
- name: Download VSIX artifact
uses: actions/download-artifact@v4
with:
name: vsix-package
- name: Upload VSIX to GitHub Release
uses: softprops/action-gh-release@v1
with:
files: '*.vsix'
tag_name: ${{ needs.auto-release.outputs.version }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}