Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
deploy-lab:
name: Deploy Reader to lab
needs: check
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
if: github.workflow == 'CI' && github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-24.04
timeout-minutes: 15
concurrency:
Expand Down
33 changes: 25 additions & 8 deletions .github/workflows/deploy-reader.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
name: Deploy Reader

# Lab deploys from CI on every push to main. Staging and production are explicit.
# Dependency merges use the existing production publisher; manual paths remain.
on:
push:
branches: [main]
paths:
- apps/reader/package.json
- apps/zotero-exporter/package.json
- packages/connect/package.json
- packages/core/package.json
- packages/i18n/package.json
- packages/markdown-editor/package.json
- packages/migration/package.json
- packages/platform/package.json
- packages/reading-surface/package.json
- packages/renderer-epub/package.json
- packages/renderer-html/package.json
- packages/renderer-pdf/package.json
- packages/ui/package.json
- packages/web-capture/package.json
workflow_dispatch:
inputs:
target:
Expand All @@ -27,18 +44,18 @@ jobs:

deploy:
if: inputs.source_sha == ''
name: Deploy Reader to ${{ inputs.target }}
name: Deploy Reader to ${{ inputs.target || 'production' }}
needs: ci
runs-on: ubuntu-24.04
timeout-minutes: 15
concurrency:
group: deploy-reader-${{ inputs.target }}
group: deploy-reader-${{ inputs.target || 'production' }}
cancel-in-progress: false
environment:
name: reader-${{ inputs.target }}
url: ${{ inputs.target == 'production' && 'https://reader.mdbase.dev/' || 'https://staging.mdbase-reader.pages.dev/' }}
name: reader-${{ inputs.target || 'production' }}
url: ${{ inputs.target != 'staging' && 'https://reader.mdbase.dev/' || 'https://staging.mdbase-reader.pages.dev/' }}
env:
MDBASE_ENV: ${{ inputs.target }}
MDBASE_ENV: ${{ inputs.target || 'production' }}
steps:
- name: Require main
if: github.ref != 'refs/heads/main'
Expand All @@ -62,15 +79,15 @@ jobs:
cache: pnpm
- run: pnpm install --frozen-lockfile
# deploy-pages.mjs refuses a dirty tree for staging and production.
- run: pnpm ${{ inputs.target == 'production' && 'deploy:prod' || 'deploy:dev' }}
- run: pnpm ${{ inputs.target != 'staging' && 'deploy:prod' || 'deploy:dev' }}
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
VITE_MDBASE_FEEDBACK_URL: ${{ vars.MDBASE_FEEDBACK_URL }}
VITE_MDBASE_FEEDBACK_TURNSTILE_SITE_KEY: ${{ vars.MDBASE_FEEDBACK_TURNSTILE_SITE_KEY }}
- name: Verify deployed manifest
env:
ORIGIN: ${{ inputs.target == 'production' && 'https://reader.mdbase.dev' || 'https://staging.mdbase-reader.pages.dev' }}
ORIGIN: ${{ inputs.target != 'staging' && 'https://reader.mdbase.dev' || 'https://staging.mdbase-reader.pages.dev' }}
run: |
for attempt in 1 2 3 4 5 6 7 8 9 10 11 12; do
homepage=$(curl -fsS "$ORIGIN/.well-known/mdbase-app.json" | jq -r .homepage || true)
Expand Down
94 changes: 94 additions & 0 deletions .github/workflows/sdk-repin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Repin production Connect packages

on:
workflow_dispatch:
inputs:
connect_release:
description: Production-verified Connect tag (v0.1.0-beta.N)
required: true
type: string

permissions:
contents: read

concurrency:
group: sdk-repin
cancel-in-progress: false

jobs:
repin:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- name: Mint the release automation App token
id: app
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.RELEASE_AUTOMATION_CLIENT_ID }}
private-key: ${{ secrets.RELEASE_AUTOMATION_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: ${{ github.event.repository.name }}
permission-contents: write
permission-pull-requests: write
- name: Mint read-only access to the production record
id: ops
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.RELEASE_AUTOMATION_CLIENT_ID }}
private-key: ${{ secrets.RELEASE_AUTOMATION_APP_PRIVATE_KEY }}
owner: mdbase-dev
repositories: mdbase-cloud-ops
permission-contents: read
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: main
token: ${{ steps.app.outputs.token }}
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 22
package-manager-cache: false
- name: Validate production identity and plan exact pins
id: pins
env:
GH_TOKEN: ${{ steps.ops.outputs.token }}
CONNECT_RELEASE: ${{ inputs.connect_release }}
REPIN_INTEGRITIES: ${{ runner.temp }}/sdk-repin-integrities.json
run: |
pnpm install --frozen-lockfile --ignore-scripts
node scripts/sdk-repin.mjs
- name: Refresh the existing lockfile
if: steps.pins.outputs.changed == 'true'
env:
REPIN_INTEGRITIES: ${{ runner.temp }}/sdk-repin-integrities.json
run: |
pnpm install --lockfile-only --ignore-scripts
node scripts/sdk-repin.mjs verify-lock
- name: Open or update the single release PR
if: steps.pins.outputs.changed == 'true'
env:
GH_TOKEN: ${{ steps.app.outputs.token }}
APP_SLUG: ${{ steps.app.outputs.app-slug }}
CONNECT_RELEASE: ${{ inputs.connect_release }}
CONNECT_COMMIT: ${{ steps.pins.outputs.commit }}
run: |
if git diff --quiet; then exit 0; fi
branch=automation/sdk-repin
previous=$(git ls-remote origin "refs/heads/$branch" | awk '{print $1}')
git switch --create "$branch"
git config user.name "${APP_SLUG}[bot]"
git config user.email "${APP_SLUG}[bot]@users.noreply.github.com"
git ls-files -z -- '*package.json' pnpm-lock.yaml | xargs -0 git add --
git diff --cached --quiet && exit 0
git commit -m "Pin production Connect packages to $CONNECT_RELEASE"
git push --force-with-lease="refs/heads/$branch:$previous" origin "HEAD:refs/heads/$branch"
title="Pin production Connect packages to $CONNECT_RELEASE"
body=$(printf 'Connect: %s, commit %s.\n\nTrain run: https://github.com/%s/actions/runs/%s\n\nExact npm pins and pnpm lockfile only. App CI and normal branch protections gate auto-merge. Reader web uses the existing production publisher on dependency merges; extension version/tag/store handoff remains separate.\n' "$CONNECT_RELEASE" "$CONNECT_COMMIT" "$GITHUB_REPOSITORY" "$GITHUB_RUN_ID")
pull=$(gh pr list --head "$branch" --base main --state open --json number --jq '.[0].number // empty')
if [ -n "$pull" ]; then
gh pr edit "$pull" --title "$title" --body "$body"
else
pull=$(gh pr create --head "$branch" --base main --title "$title" --body "$body")
fi
gh pr merge "$pull" --auto --squash
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"test:workspaces": "pnpm -r --if-present test",
"test:architecture": "node --test scripts/lib/architecture-check.test.mjs",
"test:publisher": "node --test scripts/tests/*.test.mjs",
"test:sdk-repin": "node --test scripts/tests/sdk-repin.test.mjs scripts/tests/npm-provenance.test.mjs",
"test:publisher:evidence": "node --test scripts/tests/reader-evidence.test.mjs",
"test:publisher:review": "node --test scripts/review-tests/*.test.mjs",
"test:publisher:offline": "node --test scripts/tests/reader-offline.test.mjs",
Expand Down Expand Up @@ -63,6 +64,7 @@
"eslint-plugin-react-hooks": "^7.1.1",
"globals": "^17.13.0",
"prettier": "^3.9.10",
"sigstore": "4.1.1",
"typescript": "^6.0.3",
"typescript-eslint": "^8.72.1",
"vitest": "^5.0.5",
Expand Down
Loading