Skip to content

chore: set homepage to https://www.edgeparse.com, bump to v0.2.5 (#38) #9

chore: set homepage to https://www.edgeparse.com, bump to v0.2.5 (#38)

chore: set homepage to https://www.edgeparse.com, bump to v0.2.5 (#38) #9

Workflow file for this run

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