Merge pull request #48 from quantfive/github-in-js #194
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: Release and Publish | |
| on: | |
| push: | |
| branches: ["**"] | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| NODE_VERSION: "20" | |
| REGISTRY_URL: "https://registry.npmjs.org" | |
| SCOPE: "@codepress" | |
| jobs: | |
| check-version: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: read | |
| outputs: | |
| version_changed: ${{ steps.release_info.outputs.changed }} | |
| release_version: ${{ steps.release_info.outputs.release_version }} | |
| release_tag: ${{ steps.release_info.outputs.release_tag }} | |
| branch_name: ${{ steps.release_info.outputs.branch_name }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| # Configure npmjs.org as registry (scoped for @codepress) | |
| - name: Setup Node (scoped registry) | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| registry-url: ${{ env.REGISTRY_URL }} | |
| scope: ${{ env.SCOPE }} | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.22.0 | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Compute release metadata | |
| id: release_info | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: | | |
| CURRENT_VERSION=$(node -p "require('./package.json').version") | |
| BRANCH_NAME=${GITHUB_REF#refs/heads/} | |
| TIMESTAMP=$(date +%Y%m%d%H%M%S) | |
| if [[ "$BRANCH_NAME" == "main" ]]; then | |
| RELEASE_VERSION="$CURRENT_VERSION" | |
| RELEASE_TAG="latest" | |
| elif [[ "$BRANCH_NAME" == "develop" ]]; then | |
| RELEASE_VERSION="$CURRENT_VERSION-beta.$TIMESTAMP" | |
| RELEASE_TAG="beta" | |
| elif [[ "$BRANCH_NAME" =~ ^feature/ ]]; then | |
| FEATURE_NAME=$(echo "$BRANCH_NAME" | sed 's/feature\///' | sed 's/[^a-zA-Z0-9]/-/g') | |
| RELEASE_VERSION="$CURRENT_VERSION-alpha.$FEATURE_NAME.$TIMESTAMP" | |
| RELEASE_TAG="alpha" | |
| else | |
| SAFE_BRANCH=$(echo "$BRANCH_NAME" | sed 's/[^a-zA-Z0-9]/-/g') | |
| RELEASE_VERSION="$CURRENT_VERSION-dev.$SAFE_BRANCH.$TIMESTAMP" | |
| RELEASE_TAG="dev" | |
| fi | |
| echo "release_version=$RELEASE_VERSION" >> $GITHUB_OUTPUT | |
| echo "release_tag=$RELEASE_TAG" >> $GITHUB_OUTPUT | |
| echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT | |
| if git tag | grep -q "^v$RELEASE_VERSION$"; then | |
| echo "Version v$RELEASE_VERSION already tagged" | |
| echo "changed=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| if [[ "$BRANCH_NAME" == "main" ]]; then | |
| LATEST_PUBLISHED=$(npm view @codepress/codepress-engine version 2>/dev/null || echo "0.0.0") | |
| if [ "$RELEASE_VERSION" != "$LATEST_PUBLISHED" ]; then | |
| echo "changed=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "changed=false" >> $GITHUB_OUTPUT | |
| fi | |
| else | |
| echo "changed=true" >> $GITHUB_OUTPUT | |
| fi | |
| # Build each WASM band in parallel using matrix strategy | |
| build-wasm: | |
| needs: check-version | |
| if: needs.check-version.outputs.version_changed == 'true' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| band: [v0_82_87, v26, v42, v48] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.22.0 | |
| - name: Setup Rust (stable + WASI) | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: wasm32-wasip1 | |
| - name: Cache Cargo registry | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| key: ${{ runner.os }}-cargo-${{ matrix.band }}-${{ hashFiles('codepress-swc-plugin/src/**', 'scripts/build-swc.mjs') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-${{ matrix.band }}- | |
| ${{ runner.os }}-cargo- | |
| - name: Install deps | |
| run: pnpm install --frozen-lockfile | |
| - name: Build WASM band ${{ matrix.band }} | |
| run: node scripts/build-swc.mjs | |
| env: | |
| BAND: ${{ matrix.band }} | |
| - name: Verify WASM build | |
| run: | | |
| echo "Built WASM files:" | |
| ls -la swc/*.wasm | |
| for f in swc/*.wasm; do | |
| echo "MD5 of $f:" | |
| md5sum "$f" || md5 "$f" | |
| done | |
| - name: Upload WASM artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wasm-${{ matrix.band }} | |
| path: swc/*.wasm | |
| retention-days: 1 | |
| build-and-publish: | |
| needs: [check-version, build-wasm] | |
| if: needs.check-version.outputs.version_changed == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| packages: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Node (scoped registry) | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| registry-url: ${{ env.REGISTRY_URL }} | |
| scope: ${{ env.SCOPE }} | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.22.0 | |
| - name: Install deps | |
| run: pnpm install --frozen-lockfile | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Download all WASM artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: wasm-* | |
| path: swc-artifacts | |
| merge-multiple: false | |
| - name: Combine WASM artifacts | |
| run: | | |
| mkdir -p swc | |
| for dir in swc-artifacts/wasm-*; do | |
| cp "$dir"/*.wasm swc/ 2>/dev/null || true | |
| done | |
| ls -la swc/ | |
| - name: Verify all WASM files present | |
| run: | | |
| echo "Verifying WASM artifacts for all bands..." | |
| MISSING_BANDS="" | |
| BANDS=("v0_82_87" "v26" "v42" "v48") | |
| for band in "${BANDS[@]}"; do | |
| if ! ls swc/*${band}*.wasm 1>/dev/null 2>&1; then | |
| echo "ERROR: Missing WASM file for band: $band" | |
| MISSING_BANDS="$MISSING_BANDS $band" | |
| else | |
| echo "✓ Found WASM file(s) for band: $band" | |
| ls swc/*${band}*.wasm | |
| fi | |
| done | |
| if [ -n "$MISSING_BANDS" ]; then | |
| echo "" | |
| echo "FATAL: The following bands are missing WASM artifacts:$MISSING_BANDS" | |
| echo "This indicates one or more matrix build jobs failed." | |
| echo "Cannot proceed with publish - package would be incomplete." | |
| exit 1 | |
| fi | |
| echo "" | |
| echo "✓ All WASM bands verified successfully" | |
| echo "Total WASM files: $(ls swc/*.wasm | wc -l)" | |
| - name: Update package version for release | |
| env: | |
| RELEASE_VERSION: ${{ needs.check-version.outputs.release_version }} | |
| run: | | |
| node -e " | |
| const fs = require('fs'); | |
| const version = process.env.RELEASE_VERSION; | |
| const pkgPath = './package.json'; | |
| const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); | |
| pkg.version = version; | |
| fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n'); | |
| " | |
| - name: Build JS | |
| run: pnpm run build | |
| - name: Run tests | |
| run: pnpm test | |
| # Optional: verify the pack contents before publishing | |
| - name: Dry-run pack | |
| run: npm pack --dry-run --ignore-scripts | |
| # Race safety: re-check just before publish | |
| - name: Ensure version not yet published | |
| id: recheck | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| TARGET_VERSION: ${{ needs.check-version.outputs.release_version }} | |
| run: | | |
| PUBLISHED=$(npm view @codepress/codepress-engine versions --json 2>/dev/null || echo '[]') | |
| if printf '%s' "$PUBLISHED" | grep -F "\"$TARGET_VERSION\"" >/dev/null; then | |
| echo "already=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "already=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Publish to npm | |
| if: steps.recheck.outputs.already == 'false' | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| RELEASE_TAG: ${{ needs.check-version.outputs.release_tag }} | |
| run: | | |
| if [ "$RELEASE_TAG" = "latest" ]; then | |
| npm publish --ignore-scripts | |
| else | |
| npm publish --ignore-scripts --tag "$RELEASE_TAG" | |
| fi | |
| - name: Create Git tag (after successful publish) | |
| if: steps.recheck.outputs.already == 'false' | |
| run: | | |
| VERSION=${{ needs.check-version.outputs.release_version }} | |
| BRANCH_NAME=${{ needs.check-version.outputs.branch_name }} | |
| git config --local user.email "action@github.com" | |
| git config --local user.name "GitHub Action" | |
| git tag -a "v$VERSION" -m "Release v$VERSION from $BRANCH_NAME" | |
| git push origin "v$VERSION" | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: v${{ needs.check-version.outputs.release_version }} | |
| name: Engine v${{ needs.check-version.outputs.release_version }} | |
| prerelease: ${{ needs.check-version.outputs.release_tag != 'latest' }} | |
| generate_release_notes: true | |
| body: | | |
| ## CodePress Engine Release | |
| **Branch**: `${{ needs.check-version.outputs.branch_name }}` | |
| **Dist Tag**: `${{ needs.check-version.outputs.release_tag }}` | |
| ```bash | |
| # Install the published dist-tag from npm (defaults to latest on main) | |
| npm install @codepress/codepress-engine@${{ needs.check-version.outputs.release_tag }} | |
| # Pin to this exact release version from npm | |
| npm install @codepress/codepress-engine@${{ needs.check-version.outputs.release_version }} | |
| ``` | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |