From c72185ed0b8db805ac75109dac56e72cfb391247 Mon Sep 17 00:00:00 2001 From: alam00000 Date: Fri, 21 Aug 2026 12:50:08 +0530 Subject: [PATCH] ci: gate builds on the test suite and automate engine bumps Runs typecheck and tests on every push and before publishing. A daily job rebuilds both vendored tarballs from one commit of the engine fork, verifies a single wasm ends up on disk, and opens a pull request. --- .github/workflows/build-and-publish.yml | 3 + .github/workflows/tests.yml | 36 +++ .github/workflows/update-bentopdf-viewer.yml | 221 ++++++++++++------- 3 files changed, 177 insertions(+), 83 deletions(-) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index f62c22b9..c45724e8 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -29,6 +29,9 @@ jobs: - name: Install dependencies run: npm ci + - name: Run tests + run: npm run test:run + - name: Build distribution run: npm run build diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..0acc8b6f --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,36 @@ +name: Tests + +on: + push: + branches: + - 'main' + pull_request: + workflow_dispatch: + +concurrency: + group: tests-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + runs-on: ubuntu-latest + env: + HUSKY: 0 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Typecheck + run: npx tsc --noEmit + + - name: Run tests + run: npm run test:run diff --git a/.github/workflows/update-bentopdf-viewer.yml b/.github/workflows/update-bentopdf-viewer.yml index eafe9f0c..7f0136ca 100644 --- a/.github/workflows/update-bentopdf-viewer.yml +++ b/.github/workflows/update-bentopdf-viewer.yml @@ -1,60 +1,54 @@ -name: Update BentoPDF Viewer +name: Update PDF Engine and Viewer on: workflow_dispatch: + schedule: + - cron: '17 4 * * *' permissions: contents: write pull-requests: write +concurrency: + group: update-engine + cancel-in-progress: false + +env: + FORK_URL: https://github.com/alam00000/bentopdf-pdfium-viewer + HUSKY: 0 + jobs: - update-snippet: + update: runs-on: ubuntu-latest + if: github.repository == 'alam00000/bentopdf' steps: - name: Checkout repo uses: actions/checkout@v4 - - name: Read current upstream version marker - id: current-version + - name: Read current upstream marker + id: current run: | if [ -f vendor/bentopdf-viewer/.upstream-version ]; then - CUR=$(cat vendor/bentopdf-viewer/.upstream-version) + echo "version=$(cat vendor/bentopdf-viewer/.upstream-version)" >> "$GITHUB_OUTPUT" else - CUR="" + echo "version=" >> "$GITHUB_OUTPUT" fi - echo "version=$CUR" >> "$GITHUB_OUTPUT" - - name: Read latest commit from our viewer fork - id: upstream-version - run: | - LATEST=$(git ls-remote https://github.com/alam00000/bentopdf-pdfium-viewer refs/heads/main | cut -c1-12) - echo "version=$LATEST" >> "$GITHUB_OUTPUT" + - 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 run: | - if [ "${{ steps.upstream-version.outputs.version }}" = "${{ steps.current-version.outputs.version }}" ]; then + if [ "${{ steps.upstream.outputs.version }}" = "${{ steps.current.outputs.version }}" ]; then echo "run=false" >> "$GITHUB_OUTPUT" - echo "No upstream version change detected." + echo "Already on ${{ steps.current.outputs.version }}." else echo "run=true" >> "$GITHUB_OUTPUT" - echo "Updating from '${{ steps.current-version.outputs.version }}' to '${{ steps.upstream-version.outputs.version }}'" + echo "Updating ${{ steps.current.outputs.version }} -> ${{ steps.upstream.outputs.version }}" fi - - name: Enable corepack (pnpm) - if: steps.gate.outputs.run == 'true' - run: corepack enable - - - name: Prepare workspace - if: steps.gate.outputs.run == 'true' - run: | - mkdir -p vendor/bentopdf-viewer - npm config set cache ./\.npm-cache - - - name: Clone our viewer fork - if: steps.gate.outputs.run == 'true' - run: git clone --depth 1 --branch main https://github.com/alam00000/bentopdf-pdfium-viewer ./_fork/bentopdf-pdfium-viewer - - name: Setup Node if: steps.gate.outputs.run == 'true' uses: actions/setup-node@v4 @@ -62,82 +56,143 @@ jobs: node-version: 22 cache: npm - - name: Install upstream deps + - name: Enable corepack (pnpm) if: steps.gate.outputs.run == 'true' - working-directory: ./_fork/bentopdf-pdfium-viewer - run: pnpm install --no-frozen-lockfile + run: corepack enable - - name: Build snippet + - name: Clone the viewer fork if: steps.gate.outputs.run == 'true' - working-directory: ./_fork/bentopdf-pdfium-viewer - run: pnpm run build:snippet + run: git clone --depth 1 --branch main "$FORK_URL" ./_fork - - name: Pack snippet tarball + - name: Require a published engine if: steps.gate.outputs.run == 'true' - working-directory: ./_fork/bentopdf-pdfium-viewer run: | - npm pack ./viewers/snippet --pack-destination ../../vendor/bentopdf-viewer - ls -l ../../vendor/bentopdf-viewer + test -s ./_fork/packages/pdfium/src/vendor/pdfium.wasm + test -s ./_fork/packages/pdfium/src/vendor/pdfium.js - - name: Sanitize tarball (rename pkg and pin deps) + - name: Pack the engine if: steps.gate.outputs.run == 'true' - env: - UPSTREAM_VERSION: ${{ steps.upstream-version.outputs.version }} + id: engine run: | - TARBALL=$(ls vendor/bentopdf-viewer/*.tgz | sort | tail -n1) + set -euo pipefail + SHA=${{ steps.upstream.outputs.version }} + 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 + run: | + set -euo pipefail + SHA=${{ steps.upstream.outputs.version }} + WORK=$(mktemp -d) + (cd ./_fork && npm pack ./viewers/snippet --pack-destination "$WORK" >/dev/null) TMP=$(mktemp -d) - tar -xzf "$TARBALL" -C "$TMP" - PKG="$TMP/package/package.json" - PKG="$PKG" node - <<'NODE' - const fs = require("fs"); - const path = process.env.PKG; - const pkg = JSON.parse(fs.readFileSync(path, "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(path, JSON.stringify(pkg, null, 2) + "\n"); + 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 - PKG_VERSION=$(node -p "require('./$TMP/package/package.json').version" 2>/dev/null || node -p "require('$TMP/package/package.json').version") - NEW=vendor/bentopdf-viewer/bentopdf-viewer-${PKG_VERSION}.tgz - tar -czf "$NEW" -C "$TMP" package - # Remove any older snippet tarballs, keep only the new one - find vendor/bentopdf-viewer -maxdepth 1 -name 'bentopdf-viewer-*.tgz' ! -name "$(basename "$NEW")" -delete - rm -rf "$TMP" - ls -l vendor/bentopdf-viewer + 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: Update package.json dependency path + - name: Point dependencies at the new tarballs if: steps.gate.outputs.run == 'true' run: | - TARBALL=$(ls vendor/bentopdf-viewer/bentopdf-viewer-*.tgz | sort | tail -n1) - node -e "const fs=require('fs');const pkg=require('./package.json');const tar=process.argv[1];pkg.dependencies['bentopdf-viewer']='file:'+tar;fs.writeFileSync('package.json',JSON.stringify(pkg,null,2)+'\n');" "$TARBALL" - - - name: Refresh lockfile - if: steps.gate.outputs.run == 'true' - run: npm install --package-lock-only --ignore-scripts - - - name: Write upstream version marker - if: steps.gate.outputs.run == 'true' - run: | - echo "${{ steps.upstream-version.outputs.version }}" > vendor/bentopdf-viewer/.upstream-version + node -e " + const fs = require('fs'); + const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8')); + pkg.dependencies['bentopdf-pdfium'] = 'file:' + process.argv[1]; + pkg.dependencies['bentopdf-viewer'] = 'file:' + process.argv[2]; + fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n'); + " "${{ steps.engine.outputs.tarball }}" "${{ steps.viewer.outputs.tarball }}" - 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' + run: | + echo "${{ steps.upstream.outputs.version }}" > vendor/bentopdf-viewer/.upstream-version + echo "${{ steps.upstream.outputs.version }}" > vendor/bentopdf-pdfium/.upstream-version + - name: Create Pull Request if: steps.gate.outputs.run == 'true' uses: peter-evans/create-pull-request@v6 with: - commit-message: "build(deps): bump bentopdf-viewer from ${{ steps.current-version.outputs.version }} to ${{ steps.upstream-version.outputs.version }}" - title: "build(deps): bump bentopdf-viewer from ${{ steps.current-version.outputs.version }} to ${{ steps.upstream-version.outputs.version }}" + commit-message: 'build(deps): bump PDF engine and viewer to ${{ steps.upstream.outputs.version }}' + title: 'build(deps): bump PDF engine and viewer to ${{ steps.upstream.outputs.version }}' body: | - - Build snippet from the BentoPDF viewer fork via `pnpm run build:snippet` - - Pack tarball into `vendor/bentopdf-viewer/` and point dependency to it - - Refresh package-lock.json - branch: chore/update-bentopdf-viewer + Both vendored packages built from one clone of the viewer fork at + `${{ steps.upstream.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. + branch: chore/update-pdf-engine delete-branch: true