name: Update PDF Engine and Viewer on: workflow_dispatch: schedule: - cron: '17 4 * * *' permissions: contents: read concurrency: group: update-engine cancel-in-progress: false env: FORK_URL: https://github.com/alam00000/bentopdf-pdfium-viewer HUSKY: 0 jobs: build: runs-on: ubuntu-latest if: github.repository == 'alam00000/bentopdf' permissions: contents: read outputs: run: ${{ steps.gate.outputs.run }} version: ${{ steps.gate.outputs.version }} steps: - name: Checkout repo uses: actions/checkout@v4 with: persist-credentials: false - name: Read current upstream marker id: current run: | if [ -f vendor/bentopdf-viewer/.upstream-version ]; then echo "version=$(cat vendor/bentopdf-viewer/.upstream-version)" >> "$GITHUB_OUTPUT" else echo "version=" >> "$GITHUB_OUTPUT" fi - name: Read fork HEAD id: upstream run: echo "version=$(git ls-remote "$FORK_URL" refs/heads/main | cut -c1-12)" >> "$GITHUB_OUTPUT" - name: Should update? id: gate env: CURRENT: ${{ steps.current.outputs.version }} UPSTREAM: ${{ steps.upstream.outputs.version }} run: | if ! [[ "$UPSTREAM" =~ ^[0-9a-f]{12}$ ]]; then echo "::error::Unexpected fork HEAD: $UPSTREAM" exit 1 fi echo "version=$UPSTREAM" >> "$GITHUB_OUTPUT" if [ "$UPSTREAM" = "$CURRENT" ]; then echo "run=false" >> "$GITHUB_OUTPUT" echo "Already on $CURRENT." else echo "run=true" >> "$GITHUB_OUTPUT" echo "Updating $CURRENT -> $UPSTREAM" fi - name: Setup Node if: steps.gate.outputs.run == 'true' uses: actions/setup-node@v4 with: node-version: 22 cache: npm - name: Enable corepack (pnpm) if: steps.gate.outputs.run == 'true' run: corepack enable - name: Clone the viewer fork if: steps.gate.outputs.run == 'true' run: git clone --depth 1 --branch main "$FORK_URL" ./_fork - name: Require a published engine if: steps.gate.outputs.run == 'true' run: | test -s ./_fork/packages/pdfium/src/vendor/pdfium.wasm test -s ./_fork/packages/pdfium/src/vendor/pdfium.js - name: Pack the engine if: steps.gate.outputs.run == 'true' id: engine env: SHA: ${{ steps.gate.outputs.version }} run: | set -euo pipefail VENDOR=./_fork/packages/pdfium/src/vendor STAGE=$(mktemp -d)/package mkdir -p "$STAGE" cp "$VENDOR/pdfium.js" "$STAGE/editcore.js" cp "$VENDOR/pdfium.wasm" "$STAGE/editcore.wasm" cat > "$STAGE/package.json" <> "$GITHUB_OUTPUT" - name: Build the viewer if: steps.gate.outputs.run == 'true' working-directory: ./_fork run: | pnpm install --no-frozen-lockfile pnpm run build:snippet - name: Pack the viewer without a second engine if: steps.gate.outputs.run == 'true' id: viewer env: SHA: ${{ steps.gate.outputs.version }} run: | set -euo pipefail WORK=$(mktemp -d) (cd ./_fork && npm pack ./viewers/snippet --pack-destination "$WORK" >/dev/null) TMP=$(mktemp -d) tar -xzf "$WORK"/*.tgz -C "$TMP" rm -f "$TMP/package/dist/pdfium.wasm" PKG="$TMP/package/package.json" node - <<'NODE' const fs = require('fs'); const p = process.env.PKG; const pkg = JSON.parse(fs.readFileSync(p, 'utf8')); pkg.name = 'bentopdf-viewer'; pkg.dependencies = pkg.dependencies || {}; // The rollup bundle inlines every @embedpdf/* package, so these // declared deps would only pull unused copies from upstream npm. for (const k of Object.keys(pkg.dependencies)) { if (k.startsWith('@embedpdf/')) delete pkg.dependencies[k]; if (k === 'preact') pkg.dependencies[k] = '^10.17.0'; } fs.writeFileSync(p, JSON.stringify(pkg, null, 2) + '\n'); NODE mkdir -p vendor/bentopdf-viewer OUT="vendor/bentopdf-viewer/bentopdf-viewer-$SHA.tgz" tar -czf "$OUT" -C "$TMP" package find vendor/bentopdf-viewer -maxdepth 1 -name 'bentopdf-viewer-*.tgz' ! -name "$(basename "$OUT")" -delete echo "tarball=$OUT" >> "$GITHUB_OUTPUT" - name: Point dependencies at the new tarballs if: steps.gate.outputs.run == 'true' env: ENGINE_TARBALL: ${{ steps.engine.outputs.tarball }} VIEWER_TARBALL: ${{ steps.viewer.outputs.tarball }} run: | node -e " const fs = require('fs'); const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8')); pkg.dependencies['bentopdf-pdfium'] = 'file:' + process.env.ENGINE_TARBALL; pkg.dependencies['bentopdf-viewer'] = 'file:' + process.env.VIEWER_TARBALL; fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n'); " - name: Cleanup upstream clone if: steps.gate.outputs.run == 'true' run: rm -rf ./_fork - name: Install and verify one shared engine if: steps.gate.outputs.run == 'true' run: | set -euo pipefail npm install test -s node_modules/bentopdf-pdfium/editcore.wasm if find node_modules/bentopdf-viewer -name '*.wasm' | grep -q .; then echo "::error::viewer package still ships a wasm; the engine would be duplicated" exit 1 fi if ! grep -q "bentopdf-pdfium/editcore.wasm" src/js/logic/edit-pdf-page.ts; then echo "::error::the PDF editor no longer points at the shared engine" exit 1 fi echo "engine sha256: $(sha256sum node_modules/bentopdf-pdfium/editcore.wasm | cut -c1-16)" - name: Typecheck and test if: steps.gate.outputs.run == 'true' run: | npx tsc --noEmit npm run test:run - name: Write upstream marker if: steps.gate.outputs.run == 'true' env: SHA: ${{ steps.gate.outputs.version }} run: | echo "$SHA" > vendor/bentopdf-viewer/.upstream-version echo "$SHA" > vendor/bentopdf-pdfium/.upstream-version - name: Upload update if: steps.gate.outputs.run == 'true' uses: actions/upload-artifact@v4 with: name: engine-update include-hidden-files: true retention-days: 1 path: | package.json package-lock.json vendor/bentopdf-pdfium/ vendor/bentopdf-viewer/ pull-request: runs-on: ubuntu-latest needs: build if: needs.build.outputs.run == 'true' permissions: contents: write pull-requests: write steps: - name: Checkout repo uses: actions/checkout@v4 - name: Download update uses: actions/download-artifact@v4 with: name: engine-update path: ${{ runner.temp }}/engine-update - name: Apply update id: apply env: UPDATE: ${{ runner.temp }}/engine-update run: | set -euo pipefail rm -rf vendor/bentopdf-pdfium vendor/bentopdf-viewer mkdir -p vendor cp -R "$UPDATE/vendor/bentopdf-pdfium" "$UPDATE/vendor/bentopdf-viewer" vendor/ cp "$UPDATE/package.json" "$UPDATE/package-lock.json" . { echo 'checksums<> "$GITHUB_OUTPUT" - name: Create Pull Request uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6.1.0 with: commit-message: 'build(deps): bump PDF engine and viewer to ${{ needs.build.outputs.version }}' title: 'build(deps): bump PDF engine and viewer to ${{ needs.build.outputs.version }}' body: | Both vendored packages built from one clone of the viewer fork at `${{ needs.build.outputs.version }}`, so the text editor and the PDF editor run the same engine. - `bentopdf-pdfium` — the engine wasm, packed from the fork's published `src/vendor` - `bentopdf-viewer` — the snippet bundle, packed **without** `dist/pdfium.wasm` The viewer loads the shared engine through `EmbedPDF.init({ wasmUrl })`. CI verified a single wasm on disk, typecheck, and the test suite. Built in a read-only job. Tarball checksums: ``` ${{ steps.apply.outputs.checksums }} ``` branch: chore/update-pdf-engine delete-branch: true