chore: set homepage to https://www.edgeparse.com, bump to v0.2.5 (#38) #9
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: Release — WASM SDK (npm + GitHub Packages) | |
| on: | |
| push: | |
| tags: ['v[0-9]+.[0-9]+.[0-9]+'] | |
| workflow_dispatch: | |
| inputs: | |
| tag_name: | |
| description: 'Tag name to publish (e.g. v0.2.4) — used for version sync' | |
| required: true | |
| default: 'v0.2.4' | |
| permissions: | |
| contents: write # upload GitHub Release assets | |
| packages: write # publish to GitHub Packages npm registry | |
| id-token: write # required for npm Trusted Publisher (OIDC tokenless publish) | |
| jobs: | |
| publish-wasm: | |
| name: Build & publish WASM package | |
| runs-on: ubuntu-latest | |
| environment: npm | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # Node.js is needed for npm pack / publish and wasm-pack post-processing. | |
| # Registry URL is overridden per-publish step via .npmrc; the value here | |
| # merely ensures `NODE_AUTH_TOKEN` is wired into the environment. | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| registry-url: 'https://registry.npmjs.org' | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: wasm32-unknown-unknown | |
| - uses: Swatinem/rust-cache@v2 | |
| - uses: taiki-e/install-action@wasm-pack | |
| # ───────────────────────────────────────────────────────────────────── | |
| # 1. Verify that the Git tag matches the Cargo workspace version. | |
| # ───────────────────────────────────────────────────────────────────── | |
| - name: Verify version consistency | |
| env: | |
| INPUT_TAG_NAME: ${{ inputs.tag_name }} | |
| run: | | |
| TAG_NAME="${INPUT_TAG_NAME:-$GITHUB_REF_NAME}" | |
| TAG_VERSION="${TAG_NAME#v}" | |
| CARGO_VERSION=$(cargo metadata --no-deps --format-version 1 \ | |
| | jq -r '.packages[] | select(.name=="edgeparse-wasm") | .version') | |
| if [[ "$TAG_VERSION" != "$CARGO_VERSION" ]]; then | |
| echo "ERROR: tag $TAG_VERSION ≠ Cargo.toml $CARGO_VERSION" | |
| exit 1 | |
| fi | |
| echo "VERSION=$TAG_VERSION" >> "$GITHUB_ENV" | |
| echo "TAG_NAME=$TAG_NAME" >> "$GITHUB_ENV" | |
| # ───────────────────────────────────────────────────────────────────── | |
| # 2. Compile the Rust crate to WebAssembly and generate JS/TS glue. | |
| # ───────────────────────────────────────────────────────────────────── | |
| - name: Build WASM package | |
| run: | | |
| cd crates/edgeparse-wasm | |
| wasm-pack build --target web --release | |
| # ───────────────────────────────────────────────────────────────────── | |
| # 3. Patch pkg/package.json with release metadata (shared across both | |
| # registries; the name/scope is adjusted per publish step below). | |
| # ───────────────────────────────────────────────────────────────────── | |
| - name: Sync package metadata | |
| run: | | |
| node -e " | |
| const fs = require('fs'); | |
| const version = process.env.VERSION; | |
| const pkgPath = 'crates/edgeparse-wasm/pkg/package.json'; | |
| const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); | |
| pkg.version = version; | |
| pkg.description = 'EdgeParse PDF parser — WebAssembly build for browsers'; | |
| pkg.homepage = 'https://www.edgeparse.com'; | |
| pkg.keywords = ['pdf', 'parser', 'wasm', 'webassembly', 'browser', 'extraction', 'markdown']; | |
| pkg.repository = { | |
| type: 'git', | |
| url: 'git+https://github.com/raphaelmansuy/edgeparse.git', | |
| directory: 'crates/edgeparse-wasm' | |
| }; | |
| pkg.exports = { | |
| '.': { import: './edgeparse_wasm.js', types: './edgeparse_wasm.d.ts' } | |
| }; | |
| pkg.files = [ | |
| 'edgeparse_wasm_bg.wasm', | |
| 'edgeparse_wasm.js', | |
| 'edgeparse_wasm.d.ts', | |
| 'edgeparse_wasm_bg.wasm.d.ts', | |
| 'README.md', | |
| 'LICENSE' | |
| ]; | |
| pkg.publishConfig = { | |
| access: 'public', | |
| registry: 'https://registry.npmjs.org', | |
| provenance: true | |
| }; | |
| fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n'); | |
| console.log('Metadata synced to version: ' + version); | |
| " | |
| - name: Stage README and LICENSE | |
| run: | | |
| cp README.md crates/edgeparse-wasm/pkg/README.md | |
| cp LICENSE crates/edgeparse-wasm/pkg/LICENSE | |
| # ───────────────────────────────────────────────────────────────────── | |
| # 4. Pack the tarball once (reused by both registries and the Release). | |
| # ───────────────────────────────────────────────────────────────────── | |
| - name: Pack npm tarball | |
| run: | | |
| node -e " | |
| const fs = require('fs'); | |
| const pkgPath = 'crates/edgeparse-wasm/pkg/package.json'; | |
| const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); | |
| pkg.name = 'edgeparse-wasm'; | |
| pkg.publishConfig = { access: 'public', registry: 'https://registry.npmjs.org', provenance: true }; | |
| fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n'); | |
| " | |
| cd crates/edgeparse-wasm/pkg | |
| npm pack | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: wasm-package | |
| path: crates/edgeparse-wasm/pkg/*.tgz | |
| retention-days: 30 | |
| # ───────────────────────────────────────────────────────────────────── | |
| # 5a. Publish to npm via Trusted Publisher (OIDC — no access token). | |
| # Requires id-token:write permission (set at top of this file) and | |
| # the package configured at: | |
| # https://www.npmjs.com/package/edgeparse-wasm | |
| # → Settings → Trusted Publisher → workflow: release-wasm.yml | |
| # "already-published" is treated as idempotent. | |
| # ───────────────────────────────────────────────────────────────────── | |
| - name: Publish to npm registry | |
| run: | | |
| node -e " | |
| const fs = require('fs'); | |
| const pkgPath = 'crates/edgeparse-wasm/pkg/package.json'; | |
| const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); | |
| pkg.name = 'edgeparse-wasm'; | |
| pkg.publishConfig = { access: 'public', registry: 'https://registry.npmjs.org', provenance: true }; | |
| fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n'); | |
| " | |
| cd crates/edgeparse-wasm/pkg | |
| npm publish --provenance --access public --registry https://registry.npmjs.org \ | |
| || { CODE=$?; npm info edgeparse-wasm@${{ env.VERSION }} version >/dev/null 2>&1 \ | |
| && echo "Already published — skipping." || exit $CODE; } | |
| # ───────────────────────────────────────────────────────────────────── | |
| # 5b. Publish to GitHub Packages (secondary registry — useful for | |
| # enterprise / GitHub-native consumers). | |
| # The package is scoped as @raphaelmansuy/edgeparse-wasm. | |
| # Uses the built-in GITHUB_TOKEN — no extra secret required. | |
| # ───────────────────────────────────────────────────────────────────── | |
| - name: Publish to GitHub Packages | |
| env: | |
| NODE_AUTH_TOKEN: ${{ github.token }} | |
| run: | | |
| node -e " | |
| const fs = require('fs'); | |
| const pkgPath = 'crates/edgeparse-wasm/pkg/package.json'; | |
| const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); | |
| pkg.name = '@raphaelmansuy/edgeparse-wasm'; | |
| pkg.publishConfig = { | |
| access: 'public', | |
| registry: 'https://npm.pkg.github.com' | |
| }; | |
| fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n'); | |
| " | |
| # Write a scoped .npmrc so npm uses the right registry for this scope. | |
| # actions/setup-node sets NPM_CONFIG_USERCONFIG to a temp file; append there, | |
| # not to ~/.npmrc, so npm actually picks up the GitHub Packages token. | |
| NPMRC="${NPM_CONFIG_USERCONFIG:-$HOME/.npmrc}" | |
| echo "@raphaelmansuy:registry=https://npm.pkg.github.com" >> "$NPMRC" | |
| echo "//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}" >> "$NPMRC" | |
| cd crates/edgeparse-wasm/pkg | |
| npm publish --registry https://npm.pkg.github.com \ | |
| || { CODE=$?; echo "GitHub Packages publish exited $CODE — may already exist for this version." ; } | |
| # ───────────────────────────────────────────────────────────────────── | |
| # 6. Attach the tarball to the GitHub Release. | |
| # ───────────────────────────────────────────────────────────────────── | |
| - name: Ensure GitHub Release exists | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh release view "$TAG_NAME" --repo "${{ github.repository }}" \ | |
| || gh release create "$TAG_NAME" \ | |
| --repo "${{ github.repository }}" \ | |
| --title "$TAG_NAME" \ | |
| --generate-notes | |
| - name: Upload tarball to GitHub Release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh release upload "$TAG_NAME" crates/edgeparse-wasm/pkg/*.tgz \ | |
| --repo "${{ github.repository }}" --clobber | |
| # ─────────────────────────────────────────────────────────────────────────── | |
| # Post-publish verification — runs after publish-wasm completes. | |
| # Verifies every distribution channel independently. Failures here do NOT | |
| # roll back the publish but they do fail the workflow so the release is | |
| # flagged visibly in GitHub UI and the team is alerted. | |
| # ─────────────────────────────────────────────────────────────────────────── | |
| verify-wasm: | |
| name: Verify — ${{ matrix.channel }} | |
| needs: publish-wasm | |
| runs-on: ubuntu-latest | |
| # A fresh Node.js environment; no Rust toolchain needed. | |
| strategy: | |
| fail-fast: false # check every channel even if one fails | |
| matrix: | |
| include: | |
| - channel: npm-registry | |
| description: "edgeparse-wasm published on registry.npmjs.org" | |
| - channel: github-packages | |
| description: "@raphaelmansuy/edgeparse-wasm on npm.pkg.github.com" | |
| - channel: jsdelivr-cdn | |
| description: "WASM binary reachable via jsDelivr CDN" | |
| - channel: unpkg-cdn | |
| description: "WASM binary reachable via unpkg CDN" | |
| - channel: github-release | |
| description: "npm tarball attached to GitHub Release" | |
| steps: | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| # ── Resolve the version to check (supports both tag push and manual dispatch) ── | |
| - name: Resolve version | |
| env: | |
| INPUT_TAG_NAME: ${{ inputs.tag_name }} | |
| run: | | |
| TAG_NAME="${INPUT_TAG_NAME:-$GITHUB_REF_NAME}" | |
| VERSION="${TAG_NAME#v}" | |
| echo "VERSION=$VERSION" >> "$GITHUB_ENV" | |
| echo "TAG_NAME=$TAG_NAME" >> "$GITHUB_ENV" | |
| echo "Verifying channel: ${{ matrix.channel }} for version $VERSION" | |
| # ── npm registry — poll with retries (index propagation can take ~60 s) ── | |
| - name: Check npm registry | |
| if: matrix.channel == 'npm-registry' | |
| run: | | |
| MAX=12 | |
| DELAY=15 | |
| for i in $(seq 1 $MAX); do | |
| echo "Attempt $i/$MAX — checking npm for edgeparse-wasm@$VERSION" | |
| PUBLISHED=$(npm view "edgeparse-wasm@$VERSION" version 2>/dev/null || true) | |
| if [[ "$PUBLISHED" == "$VERSION" ]]; then | |
| echo "✓ npm: edgeparse-wasm@$VERSION is live" | |
| # Verify the expected files are present in the package manifest. | |
| npm view "edgeparse-wasm@$VERSION" --json | \ | |
| python3 -c " | |
| import json, sys | |
| pkg = json.load(sys.stdin) | |
| files = pkg.get('dist', {}) | |
| assert pkg['version'] == '${VERSION}', f'version mismatch: {pkg[\"version\"]}' | |
| assert pkg.get('dist', {}).get('tarball'), 'missing tarball URL' | |
| print(' version :', pkg['version']) | |
| print(' tarball :', pkg['dist']['tarball']) | |
| print(' integrity:', pkg['dist'].get('integrity', 'n/a')) | |
| " | |
| exit 0 | |
| fi | |
| echo " Not yet visible — waiting ${DELAY}s..." | |
| sleep $DELAY | |
| done | |
| echo "ERROR: edgeparse-wasm@$VERSION not found on npm after $((MAX * DELAY))s" | |
| exit 1 | |
| # ── GitHub Packages — check via npm.pkg.github.com REST endpoint ── | |
| - name: Check GitHub Packages | |
| if: matrix.channel == 'github-packages' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| MAX=8 | |
| DELAY=20 | |
| SCOPE="raphaelmansuy" | |
| PKG="edgeparse-wasm" | |
| for i in $(seq 1 $MAX); do | |
| echo "Attempt $i/$MAX — checking GitHub Packages for @${SCOPE}/${PKG}@$VERSION" | |
| HTTP=$(curl -s -o /dev/null -w "%{http_code}" \ | |
| -H "Authorization: Bearer $GH_TOKEN" \ | |
| -H "Accept: application/vnd.npm.install-v1+json" \ | |
| "https://npm.pkg.github.com/@${SCOPE}%2F${PKG}") | |
| if [[ "$HTTP" == "200" ]]; then | |
| # Fetch full metadata and confirm the version exists. | |
| META=$(curl -s \ | |
| -H "Authorization: Bearer $GH_TOKEN" \ | |
| -H "Accept: application/vnd.npm.install-v1+json" \ | |
| "https://npm.pkg.github.com/@${SCOPE}%2F${PKG}") | |
| FOUND=$(echo "$META" | python3 -c " | |
| import json, sys | |
| d = json.load(sys.stdin) | |
| vs = list(d.get('versions', {}).keys()) | |
| print('found' if '${VERSION}' in vs else 'missing') | |
| " 2>/dev/null || echo "error") | |
| if [[ "$FOUND" == "found" ]]; then | |
| echo "✓ GitHub Packages: @${SCOPE}/${PKG}@$VERSION is live" | |
| exit 0 | |
| fi | |
| fi | |
| echo " HTTP $HTTP — waiting ${DELAY}s..." | |
| sleep $DELAY | |
| done | |
| echo "ERROR: @${SCOPE}/${PKG}@$VERSION not found on GitHub Packages after $((MAX * DELAY))s" | |
| exit 1 | |
| # ── jsDelivr — the package must be available once npm index has propagated ── | |
| - name: Check jsDelivr CDN | |
| if: matrix.channel == 'jsdelivr-cdn' | |
| run: | | |
| # jsDelivr mirrors npm; it may take a few minutes after npm publish. | |
| MAX=10 | |
| DELAY=30 | |
| WASM_URL="https://cdn.jsdelivr.net/npm/edgeparse-wasm@${VERSION}/edgeparse_wasm_bg.wasm" | |
| JS_URL="https://cdn.jsdelivr.net/npm/edgeparse-wasm@${VERSION}/edgeparse_wasm.js" | |
| DTS_URL="https://cdn.jsdelivr.net/npm/edgeparse-wasm@${VERSION}/edgeparse_wasm.d.ts" | |
| for i in $(seq 1 $MAX); do | |
| echo "Attempt $i/$MAX — checking jsDelivr" | |
| WASM_HTTP=$(curl -s -o /dev/null -w "%{http_code}" --max-time 30 "$WASM_URL") | |
| JS_HTTP=$(curl -s -o /dev/null -w "%{http_code}" --max-time 30 "$JS_URL") | |
| DTS_HTTP=$(curl -s -o /dev/null -w "%{http_code}" --max-time 30 "$DTS_URL") | |
| echo " .wasm=$WASM_HTTP .js=$JS_HTTP .d.ts=$DTS_HTTP" | |
| if [[ "$WASM_HTTP" == "200" && "$JS_HTTP" == "200" && "$DTS_HTTP" == "200" ]]; then | |
| echo "✓ jsDelivr: all three assets reachable for edgeparse-wasm@$VERSION" | |
| exit 0 | |
| fi | |
| echo " Not yet available — waiting ${DELAY}s..." | |
| sleep $DELAY | |
| done | |
| echo "ERROR: jsDelivr assets not reachable after $((MAX * DELAY))s" | |
| echo " WASM: $WASM_URL" | |
| echo " JS: $JS_URL" | |
| exit 1 | |
| # ── unpkg — same content, different CDN ── | |
| - name: Check unpkg CDN | |
| if: matrix.channel == 'unpkg-cdn' | |
| run: | | |
| MAX=10 | |
| DELAY=30 | |
| WASM_URL="https://unpkg.com/edgeparse-wasm@${VERSION}/edgeparse_wasm_bg.wasm" | |
| JS_URL="https://unpkg.com/edgeparse-wasm@${VERSION}/edgeparse_wasm.js" | |
| for i in $(seq 1 $MAX); do | |
| echo "Attempt $i/$MAX — checking unpkg" | |
| WASM_HTTP=$(curl -s -o /dev/null -w "%{http_code}" --max-time 30 -L "$WASM_URL") | |
| JS_HTTP=$(curl -s -o /dev/null -w "%{http_code}" --max-time 30 -L "$JS_URL") | |
| echo " .wasm=$WASM_HTTP .js=$JS_HTTP" | |
| if [[ "$WASM_HTTP" == "200" && "$JS_HTTP" == "200" ]]; then | |
| echo "✓ unpkg: assets reachable for edgeparse-wasm@$VERSION" | |
| exit 0 | |
| fi | |
| echo " Not yet available — waiting ${DELAY}s..." | |
| sleep $DELAY | |
| done | |
| echo "ERROR: unpkg assets not reachable after $((MAX * DELAY))s" | |
| exit 1 | |
| # ── GitHub Release — confirm the .tgz tarball is attached ── | |
| - name: Check GitHub Release asset | |
| if: matrix.channel == 'github-release' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| MAX=6 | |
| DELAY=15 | |
| for i in $(seq 1 $MAX); do | |
| echo "Attempt $i/$MAX — checking GitHub Release $TAG_NAME for .tgz asset" | |
| ASSET=$(gh release view "$TAG_NAME" \ | |
| --repo "${{ github.repository }}" \ | |
| --json assets \ | |
| --jq '.assets[].name' 2>/dev/null \ | |
| | grep '\.tgz$' || true) | |
| if [[ -n "$ASSET" ]]; then | |
| echo "✓ GitHub Release: found tarball — $ASSET" | |
| # Also confirm the release is not a draft. | |
| DRAFT=$(gh release view "$TAG_NAME" \ | |
| --repo "${{ github.repository }}" \ | |
| --json isDraft --jq '.isDraft') | |
| echo " isDraft: $DRAFT" | |
| exit 0 | |
| fi | |
| echo " No .tgz yet — waiting ${DELAY}s..." | |
| sleep $DELAY | |
| done | |
| echo "ERROR: no .tgz asset found on GitHub Release $TAG_NAME" | |
| exit 1 | |