merge: wave 2 executor (01-02) into main

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 4.6 committed 2026-03-25 23:46:16 -04:00
1 parent a5b8fcf9d3
commit aa1a78d7d9
1081 files changed
+445341

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
# Replace @cryppadotta if a different maintainer or team should own release infrastructure.
.github/** @cryppadotta @devinfoley
scripts/release*.sh @cryppadotta @devinfoley
scripts/release-*.mjs @cryppadotta @devinfoley
scripts/create-github-release.sh @cryppadotta @devinfoley
scripts/rollback-latest.sh @cryppadotta @devinfoley
doc/RELEASING.md @cryppadotta @devinfoley
doc/PUBLISHING.md @cryppadotta @devinfoley
doc/RELEASE-AUTOMATION-SETUP.md @cryppadotta @devinfoley
+55
View File
@@ -0,0 +1,55 @@
name: Docker
on:
push:
branches:
- "master"
tags:
- "v*"
permissions:
contents: read
packages: write
jobs:
build-and-push:
runs-on: ubuntu-latest
timeout-minutes: 30
concurrency:
group: docker-${{ github.ref }}
cancel-in-progress: true
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+44
View File
@@ -0,0 +1,44 @@
name: E2E Tests
on:
workflow_dispatch:
inputs:
skip_llm:
description: "Skip LLM-dependent assertions (default: true)"
type: boolean
default: true
jobs:
e2e:
runs-on: ubuntu-latest
timeout-minutes: 30
env:
PAPERCLIP_E2E_SKIP_LLM: ${{ inputs.skip_llm && 'true' || 'false' }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm build
- run: npx playwright install --with-deps chromium
- name: Run e2e tests
run: pnpm run test:e2e
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: |
tests/e2e/playwright-report/
tests/e2e/test-results/
retention-days: 14
+146
View File
@@ -0,0 +1,146 @@
name: PR
on:
pull_request:
branches:
- master
concurrency:
group: pr-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
policy:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Block manual lockfile edits
if: github.head_ref != 'chore/refresh-lockfile'
run: |
changed="$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}")"
if printf '%s\n' "$changed" | grep -qx 'pnpm-lock.yaml'; then
echo "Do not commit pnpm-lock.yaml in pull requests. CI owns lockfile updates."
exit 1
fi
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.15.4
run_install: false
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
- name: Validate dependency resolution when manifests change
run: |
changed="$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}")"
manifest_pattern='(^|/)package\.json$|^pnpm-workspace\.yaml$|^\.npmrc$|^pnpmfile\.(cjs|js|mjs)$'
if printf '%s\n' "$changed" | grep -Eq "$manifest_pattern"; then
pnpm install --lockfile-only --ignore-scripts --no-frozen-lockfile
fi
verify:
needs: [policy]
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.15.4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck
run: pnpm -r typecheck
- name: Run tests
run: pnpm test:run
- name: Build
run: pnpm build
- name: Release canary dry run
run: |
git checkout -B master HEAD
git checkout -- pnpm-lock.yaml
./scripts/release.sh canary --skip-verify --dry-run
e2e:
needs: [policy]
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.15.4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm build
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Generate Paperclip config
run: |
mkdir -p ~/.paperclip/instances/default
cat > ~/.paperclip/instances/default/config.json << 'CONF'
{
"$meta": { "version": 1, "updatedAt": "2026-01-01T00:00:00.000Z", "source": "onboard" },
"database": { "mode": "embedded-postgres" },
"logging": { "mode": "file" },
"server": { "deploymentMode": "local_trusted", "host": "127.0.0.1", "port": 3100 },
"auth": { "baseUrlMode": "auto" },
"storage": { "provider": "local_disk" },
"secrets": { "provider": "local_encrypted", "strictMode": false }
}
CONF
- name: Run e2e tests
env:
PAPERCLIP_E2E_SKIP_LLM: "true"
run: pnpm run test:e2e
- name: Upload Playwright report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: |
tests/e2e/playwright-report/
tests/e2e/test-results/
retention-days: 14
@@ -0,0 +1,97 @@
name: Refresh Lockfile
on:
push:
branches:
- master
workflow_dispatch:
concurrency:
group: refresh-lockfile-master
cancel-in-progress: false
jobs:
refresh:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.15.4
run_install: false
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- name: Refresh pnpm lockfile
run: pnpm install --lockfile-only --ignore-scripts --no-frozen-lockfile
- name: Fail on unexpected file changes
run: |
changed="$(git status --porcelain)"
if [ -z "$changed" ]; then
echo "Lockfile is already up to date."
exit 0
fi
if printf '%s\n' "$changed" | grep -Fvq ' pnpm-lock.yaml'; then
echo "Unexpected files changed during lockfile refresh:"
echo "$changed"
exit 1
fi
- name: Create or update pull request
id: upsert-pr
env:
GH_TOKEN: ${{ github.token }}
run: |
if git diff --quiet -- pnpm-lock.yaml; then
echo "Lockfile unchanged, nothing to do."
echo "pr_created=false" >> "$GITHUB_OUTPUT"
exit 0
fi
BRANCH="chore/refresh-lockfile"
git config user.name "lockfile-bot"
git config user.email "lockfile-bot@users.noreply.github.com"
git checkout -B "$BRANCH"
git add pnpm-lock.yaml
git commit -m "chore(lockfile): refresh pnpm-lock.yaml"
git push --force origin "$BRANCH"
# Create PR if one doesn't already exist
existing=$(gh pr list --head "$BRANCH" --json number --jq '.[0].number')
if [ -z "$existing" ]; then
gh pr create \
--head "$BRANCH" \
--title "chore(lockfile): refresh pnpm-lock.yaml" \
--body "Auto-generated lockfile refresh after dependencies changed on master. This PR only updates pnpm-lock.yaml."
echo "Created new PR."
else
echo "PR #$existing already exists, branch updated via force push."
fi
echo "pr_created=true" >> "$GITHUB_OUTPUT"
- name: Enable auto-merge for lockfile PR
if: steps.upsert-pr.outputs.pr_created == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
pr_url="$(gh pr list --head chore/refresh-lockfile --json url --jq '.[0].url')"
if [ -z "$pr_url" ]; then
echo "Error: lockfile PR was not found." >&2
exit 1
fi
gh pr merge --auto --squash --delete-branch "$pr_url"
+118
View File
@@ -0,0 +1,118 @@
name: Release Smoke
on:
workflow_dispatch:
inputs:
paperclip_version:
description: Published Paperclip dist-tag to test
required: true
default: canary
type: choice
options:
- canary
- latest
host_port:
description: Host port for the Docker smoke container
required: false
default: "3232"
type: string
artifact_name:
description: Artifact name for uploaded diagnostics
required: false
default: release-smoke
type: string
workflow_call:
inputs:
paperclip_version:
required: true
type: string
host_port:
required: false
default: "3232"
type: string
artifact_name:
required: false
default: release-smoke
type: string
jobs:
smoke:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.15.4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install --no-frozen-lockfile
- name: Install Playwright browser
run: npx playwright install --with-deps chromium
- name: Launch Docker smoke harness
run: |
metadata_file="$RUNNER_TEMP/release-smoke.env"
HOST_PORT="${{ inputs.host_port }}" \
DATA_DIR="$RUNNER_TEMP/release-smoke-data" \
PAPERCLIPAI_VERSION="${{ inputs.paperclip_version }}" \
SMOKE_DETACH=true \
SMOKE_METADATA_FILE="$metadata_file" \
./scripts/docker-onboard-smoke.sh
set -a
source "$metadata_file"
set +a
{
echo "SMOKE_BASE_URL=$SMOKE_BASE_URL"
echo "SMOKE_ADMIN_EMAIL=$SMOKE_ADMIN_EMAIL"
echo "SMOKE_ADMIN_PASSWORD=$SMOKE_ADMIN_PASSWORD"
echo "SMOKE_CONTAINER_NAME=$SMOKE_CONTAINER_NAME"
echo "SMOKE_DATA_DIR=$SMOKE_DATA_DIR"
echo "SMOKE_IMAGE_NAME=$SMOKE_IMAGE_NAME"
echo "SMOKE_PAPERCLIPAI_VERSION=$SMOKE_PAPERCLIPAI_VERSION"
echo "SMOKE_METADATA_FILE=$metadata_file"
} >> "$GITHUB_ENV"
- name: Run release smoke Playwright suite
env:
PAPERCLIP_RELEASE_SMOKE_BASE_URL: ${{ env.SMOKE_BASE_URL }}
PAPERCLIP_RELEASE_SMOKE_EMAIL: ${{ env.SMOKE_ADMIN_EMAIL }}
PAPERCLIP_RELEASE_SMOKE_PASSWORD: ${{ env.SMOKE_ADMIN_PASSWORD }}
run: pnpm run test:release-smoke
- name: Capture Docker logs
if: always()
run: |
if [[ -n "${SMOKE_CONTAINER_NAME:-}" ]]; then
docker logs "$SMOKE_CONTAINER_NAME" >"$RUNNER_TEMP/docker-onboard-smoke.log" 2>&1 || true
fi
- name: Upload diagnostics
if: always()
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.artifact_name }}
path: |
${{ runner.temp }}/docker-onboard-smoke.log
${{ env.SMOKE_METADATA_FILE }}
tests/release-smoke/playwright-report/
tests/release-smoke/test-results/
retention-days: 14
- name: Stop Docker smoke container
if: always()
run: |
if [[ -n "${SMOKE_CONTAINER_NAME:-}" ]]; then
docker rm -f "$SMOKE_CONTAINER_NAME" >/dev/null 2>&1 || true
fi
+261
View File
@@ -0,0 +1,261 @@
name: Release
on:
push:
branches:
- master
workflow_dispatch:
inputs:
source_ref:
description: Commit SHA, branch, or tag to publish as stable
required: true
type: string
default: master
stable_date:
description: Enter a UTC date in YYYY-MM-DD format, for example 2026-03-18. Do not enter a version string. The workflow will resolve that date to a stable version such as 2026.318.0, then 2026.318.1 for the next same-day stable.
required: false
type: string
dry_run:
description: Preview the stable release without publishing
required: true
type: boolean
default: false
concurrency:
group: release-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: false
jobs:
verify_canary:
if: github.event_name == 'push'
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.15.4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install --no-frozen-lockfile
- name: Typecheck
run: pnpm -r typecheck
- name: Run tests
run: pnpm test:run
- name: Build
run: pnpm build
publish_canary:
if: github.event_name == 'push'
needs: verify_canary
runs-on: ubuntu-latest
timeout-minutes: 45
environment: npm-canary
permissions:
contents: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.15.4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install --no-frozen-lockfile
- name: Restore tracked install-time changes
run: git checkout -- pnpm-lock.yaml
- name: Configure git author
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Publish canary
env:
GITHUB_ACTIONS: "true"
run: ./scripts/release.sh canary --skip-verify
- name: Push canary tag
run: |
tag="$(git tag --points-at HEAD | grep '^canary/v' | head -1)"
if [ -z "$tag" ]; then
echo "Error: no canary tag points at HEAD after release." >&2
exit 1
fi
git push origin "refs/tags/${tag}"
verify_stable:
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.source_ref }}
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.15.4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install --no-frozen-lockfile
- name: Typecheck
run: pnpm -r typecheck
- name: Run tests
run: pnpm test:run
- name: Build
run: pnpm build
preview_stable:
if: github.event_name == 'workflow_dispatch' && inputs.dry_run
needs: verify_stable
runs-on: ubuntu-latest
timeout-minutes: 45
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.source_ref }}
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.15.4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install --no-frozen-lockfile
- name: Dry-run stable release
env:
GITHUB_ACTIONS: "true"
run: |
args=(stable --skip-verify --dry-run)
if [ -n "${{ inputs.stable_date }}" ]; then
args+=(--date "${{ inputs.stable_date }}")
fi
./scripts/release.sh "${args[@]}"
publish_stable:
if: github.event_name == 'workflow_dispatch' && !inputs.dry_run
needs: verify_stable
runs-on: ubuntu-latest
timeout-minutes: 45
environment: npm-stable
permissions:
contents: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.source_ref }}
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.15.4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install --no-frozen-lockfile
- name: Restore tracked install-time changes
run: git checkout -- pnpm-lock.yaml
- name: Configure git author
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Publish stable
env:
GITHUB_ACTIONS: "true"
run: |
args=(stable --skip-verify)
if [ -n "${{ inputs.stable_date }}" ]; then
args+=(--date "${{ inputs.stable_date }}")
fi
./scripts/release.sh "${args[@]}"
- name: Push stable tag
run: |
tag="$(git tag --points-at HEAD | grep '^v' | head -1)"
if [ -z "$tag" ]; then
echo "Error: no stable tag points at HEAD after release." >&2
exit 1
fi
git push origin "refs/tags/${tag}"
- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
PUBLISH_REMOTE: origin
run: |
version="$(git tag --points-at HEAD | grep '^v' | head -1 | sed 's/^v//')"
if [ -z "$version" ]; then
echo "Error: no v* tag points at HEAD after stable release." >&2
exit 1
fi
./scripts/create-github-release.sh "$version"