Skip to content

fix(super-converter): normalize single-paragraph BIBLIOGRAPHY/INDEX/T… #648

fix(super-converter): normalize single-paragraph BIBLIOGRAPHY/INDEX/T…

fix(super-converter): normalize single-paragraph BIBLIOGRAPHY/INDEX/T… #648

Workflow file for this run

# Auto-releases SDK on push to main (@next channel).
# Stable releases are orchestrated centrally by release-stable.yml.
# Also supports manual dispatch as a fallback for one-off releases.
#
# Python publish routing:
# - push to main (auto-release) -> TestPyPI (Python @next .devN, ephemeral)
# - workflow_dispatch, smoke=true -> TestPyPI only (Python; no npm, no semantic-release)
# - workflow_dispatch, smoke=false -> production PyPI (manual production recovery for stable)
# TestPyPI auth uses an account-scoped API token stored as TEST_PYPI_TOKEN in
# the `testpypi` GitHub environment (not OIDC). The smoke job lives in this
# file so the same token + env scope cover both auto and smoke paths.
name: "\U0001F4E6 Release SDK"
on:
push:
branches:
- main
paths:
# Keep in sync with packages/sdk/.releaserc.cjs includePaths (patch-commit-filter).
- 'packages/sdk/**'
- 'apps/cli/**'
- 'packages/document-api/**'
- 'packages/superdoc/**'
- 'packages/super-editor/**'
- 'packages/layout-engine/**'
- 'packages/word-layout/**'
- 'packages/preset-geometry/**'
- 'shared/**'
- 'scripts/semantic-release/**'
- 'pnpm-workspace.yaml'
- '!**/*.md'
workflow_dispatch:
inputs:
version:
description: 'Version to release (e.g. 1.0.0-next.7). Leave empty to publish the current repo version.'
required: false
type: string
dry-run:
description: 'Dry run — build and validate without publishing'
required: false
type: boolean
default: false
npm-tag:
description: 'npm dist-tag for Node SDK publish (e.g. latest, next). Only used for manual version override.'
required: false
type: string
default: latest
smoke:
description: 'TestPyPI smoke test (Python-only, no npm, no semantic-release). Uses 0.0.0-next.${run_number}.'
required: false
type: boolean
default: false
permissions:
contents: write
packages: write
id-token: write # Production PyPI trusted publishing (OIDC) on manual-release; TestPyPI uses TEST_PYPI_TOKEN instead.
concurrency:
# Release runs never cancel an in-progress release (each merge is a release-worthy
# state). queue: max keeps GitHub from dropping older pending stable releases when
# a stable push touches multiple wrapper packages in the shared release-stable group;
# default queue: single only allows one pending. queue: max requires
# cancel-in-progress: false (cannot be combined with true).
group: ${{ github.ref_name == 'stable' && 'release-stable' || format('{0}-{1}', github.workflow, github.ref) }}
cancel-in-progress: false
queue: max
jobs:
# -------------------------------------------------------------------
# Auto-release via semantic-release (push trigger)
# -------------------------------------------------------------------
auto-release:
if: github.event_name == 'push'
runs-on: ubuntu-24.04
# Auto path publishes only to TestPyPI to keep production PyPI storage
# reserved for stable X.Y.Z releases. Auth uses TEST_PYPI_TOKEN from the
# `testpypi` GitHub environment (account-scoped TestPyPI API token).
environment: testpypi
outputs:
released: ${{ steps.detect.outputs.released }}
version: ${{ steps.detect.outputs.version }}
steps:
- name: Generate token
id: generate_token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- uses: actions/checkout@v6
with:
fetch-depth: 0
token: ${{ steps.generate_token.outputs.token }}
- name: Refresh branch head
# Queued release runs may start against a stale checkout (queue: max
# plus cancel-in-progress: false). Refresh to the current branch head
# so @semantic-release/git pushes fast-forward; semantic-release no-ops
# if no new commits were added since the previous queued run released.
run: |
git fetch origin "${{ github.ref_name }}" --tags
git checkout -B "${{ github.ref_name }}" "origin/${{ github.ref_name }}"
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
cache: pnpm
registry-url: 'https://registry.npmjs.org'
- uses: oven-sh/setup-bun@v2
with:
# DO NOT BUMP without verifying macOS darwin-arm64 binary signing.
# Bun 1.3.12+ regressed `bun build --compile` macOS code signing
# (oven-sh/bun#29120, oven-sh/bun#29361), which causes the embedded
# CLI in the Python SDK wheel to be SIGKILL'd by Gatekeeper on macOS.
# Tracked in SD-2784 — unpin once explicit codesign step lands.
bun-version: 1.3.11
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install canvas system dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential libcairo2-dev libpango1.0-dev \
libjpeg-dev libgif-dev librsvg2-dev libpixman-1-dev
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Install Python build tools
run: pip install build
- name: Build packages
run: pnpm run build
- name: Snapshot tags before release
id: tags_before
run: echo "tags=$(git tag --list 'sdk-v*' | sort | tr '\n' ',')" >> "$GITHUB_OUTPUT"
- name: Run semantic-release
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
LINEAR_TOKEN: ${{ secrets.LINEAR_TOKEN }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
working-directory: packages/sdk
run: pnpx semantic-release
- name: Detect whether a release was created
id: detect
run: |
BEFORE="${{ steps.tags_before.outputs.tags }}"
AFTER=$(git tag --list 'sdk-v*' | sort | tr '\n' ',')
RELEASE_TAG=$(git tag --points-at HEAD --list 'sdk-v*' --sort=-version:refname | head -n 1)
if [ -z "$RELEASE_TAG" ]; then
echo "release_present=false" >> "$GITHUB_OUTPUT"
echo "released=false" >> "$GITHUB_OUTPUT"
echo "version=" >> "$GITHUB_OUTPUT"
echo "dist_tag=" >> "$GITHUB_OUTPUT"
echo "No SDK release tag at HEAD."
else
echo "release_present=true" >> "$GITHUB_OUTPUT"
if [ "$BEFORE" = "$AFTER" ]; then
echo "released=false" >> "$GITHUB_OUTPUT"
else
echo "released=true" >> "$GITHUB_OUTPUT"
fi
VERSION="${RELEASE_TAG#sdk-v}"
if [[ "$VERSION" == *-next.* ]]; then
DIST_TAG="next"
else
DIST_TAG="latest"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "dist_tag=$DIST_TAG" >> "$GITHUB_OUTPUT"
if [ "$BEFORE" = "$AFTER" ]; then
echo "SDK release tag already present at HEAD: $RELEASE_TAG"
else
echo "Released SDK v$VERSION"
fi
fi
- name: Resume Node SDK publish for existing release tag
if: steps.detect.outputs.release_present == 'true' && steps.detect.outputs.released != 'true'
run: node packages/sdk/scripts/sdk-release-publish.mjs --tag "${{ steps.detect.outputs.dist_tag }}" --npm-only
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
# Build + publish Python whenever this commit corresponds to an SDK release
- name: Build and verify Python SDK
if: steps.detect.outputs.release_present == 'true'
run: node packages/sdk/scripts/build-python-sdk.mjs
- name: Publish companion Python packages to TestPyPI
if: steps.detect.outputs.release_present == 'true'
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.TEST_PYPI_TOKEN }}
repository-url: https://test.pypi.org/legacy/
packages-dir: packages/sdk/langs/python/companion-dist/
skip-existing: true
- name: Publish main Python SDK to TestPyPI
if: steps.detect.outputs.release_present == 'true'
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.TEST_PYPI_TOKEN }}
repository-url: https://test.pypi.org/legacy/
packages-dir: packages/sdk/langs/python/dist/
skip-existing: true
# -------------------------------------------------------------------
# Sync labs agent with newly released SDK (main/@next only)
# -------------------------------------------------------------------
sync-labs-agent:
needs: auto-release
if: >-
always()
&& github.event_name == 'push'
&& github.ref_name == 'main'
&& needs.auto-release.outputs.version != ''
runs-on: ubuntu-24.04
steps:
- name: Generate token for superdoc-labs
id: labs_token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
owner: superdoc-dev
repositories: labs
- name: Dispatch labs SDK update
env:
GH_TOKEN: ${{ steps.labs_token.outputs.token }}
run: |
VERSION="${{ needs.auto-release.outputs.version }}"
echo "Dispatching update-agent-sdk.yml on superdoc-dev/labs main for @superdoc-dev/sdk@$VERSION"
gh workflow run update-agent-sdk.yml \
--repo superdoc-dev/labs \
--ref main \
--field sdk_version="$VERSION" \
--field deploy_after_update=true
# -------------------------------------------------------------------
# Manual fallback (workflow_dispatch)
# -------------------------------------------------------------------
manual-release:
if: github.event_name == 'workflow_dispatch' && !inputs.smoke
runs-on: ubuntu-24.04
environment: ${{ inputs.dry-run && '' || 'pypi' }}
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
cache: pnpm
registry-url: 'https://registry.npmjs.org'
- uses: oven-sh/setup-bun@v2
with:
# DO NOT BUMP without verifying macOS darwin-arm64 binary signing.
# Bun 1.3.12+ regressed `bun build --compile` macOS code signing
# (oven-sh/bun#29120, oven-sh/bun#29361), which causes the embedded
# CLI in the Python SDK wheel to be SIGKILL'd by Gatekeeper on macOS.
# Tracked in SD-2784 — unpin once explicit codesign step lands.
bun-version: 1.3.11
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Install Python build tools
run: pip install build
- name: Show current version
run: |
CURRENT=$(node -p "require('./packages/sdk/package.json').version")
echo "### SDK Release (manual)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| | Version |" >> $GITHUB_STEP_SUMMARY
echo "|---|---|" >> $GITHUB_STEP_SUMMARY
echo "| **Current (in repo)** | \`${CURRENT}\` |" >> $GITHUB_STEP_SUMMARY
if [ -n "${{ inputs.version }}" ]; then
echo "| **Releasing** | \`${{ inputs.version }}\` |" >> $GITHUB_STEP_SUMMARY
else
echo "| **Releasing** | \`${CURRENT}\` (unchanged) |" >> $GITHUB_STEP_SUMMARY
fi
echo "| **Dry run** | \`${{ inputs.dry-run }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| **npm tag** | \`${{ inputs.npm-tag }}\` |" >> $GITHUB_STEP_SUMMARY
- name: Set version
if: inputs.version != ''
run: node packages/sdk/scripts/sync-sdk-version.mjs --set "${{ inputs.version }}"
- name: Generate all artifacts
run: pnpm run generate:all
- name: Build Node SDK
run: pnpm --prefix packages/sdk/langs/node run build
- name: Build superdoc package for CLI native bundling
run: pnpm --prefix packages/superdoc run build:es
- name: Verify superdoc build output exists
run: |
test -f packages/superdoc/dist/super-editor.es.js \
|| (echo "FATAL: packages/superdoc/dist/super-editor.es.js missing — build:es likely failed silently" && exit 1)
- name: Validate SDK
run: node packages/sdk/scripts/sdk-validate.mjs
- name: Publish Node SDK packages (platforms + root)
if: ${{ !inputs.dry-run }}
run: node packages/sdk/scripts/sdk-release-publish.mjs --tag "${{ inputs.npm-tag }}" --npm-only
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish Node SDK packages (dry run)
if: ${{ inputs.dry-run }}
run: node packages/sdk/scripts/sdk-release-publish.mjs --tag "${{ inputs.npm-tag }}" --npm-only --dry-run
- name: Build and verify Python SDK
run: node packages/sdk/scripts/build-python-sdk.mjs
- name: Publish companion Python packages to PyPI
if: ${{ !inputs.dry-run }}
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: packages/sdk/langs/python/companion-dist/
skip-existing: true
- name: Publish main Python SDK to PyPI
if: ${{ !inputs.dry-run }}
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: packages/sdk/langs/python/dist/
skip-existing: true
- name: Publish Python SDK (dry run)
if: ${{ inputs.dry-run }}
run: |
echo "=== Companion wheels ==="
ls -la packages/sdk/langs/python/companion-dist/
echo "=== Root wheel ==="
ls -la packages/sdk/langs/python/dist/
# -------------------------------------------------------------------
# TestPyPI smoke (workflow_dispatch, smoke=true)
#
# Publishes all six Python projects to TestPyPI at a unique
# 0.0.0.dev${run_number} version to validate the publish wiring end to
# end (account-scoped TEST_PYPI_TOKEN from the `testpypi` environment,
# wheel build, all six project names) in one shot. No npm publish, no
# semantic-release tag, no labs dispatch. Use before the first real
# merge on the auto path, and any time the publish wiring changes.
#
# First successful run also creates the six TestPyPI projects under
# the token owner's account.
#
# The 0.0.0.dev* versions left on TestPyPI are swept by the retention
# workflow (PR1b); they have no stable predecessor so the retention
# policy must include them explicitly.
# -------------------------------------------------------------------
testpypi-smoke:
if: github.event_name == 'workflow_dispatch' && inputs.smoke && github.ref_name == 'main'
runs-on: ubuntu-24.04
environment: testpypi
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
cache: pnpm
- uses: oven-sh/setup-bun@v2
with:
# See auto-release for the bun pin rationale (SD-2784).
bun-version: 1.3.11
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install canvas system dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential libcairo2-dev libpango1.0-dev \
libjpeg-dev libgif-dev librsvg2-dev libpixman-1-dev
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Install Python build tools
run: pip install build
- name: Compute smoke version
id: smoke_version
run: |
SEMVER="0.0.0-next.${{ github.run_number }}"
echo "semver=$SEMVER" >> "$GITHUB_OUTPUT"
echo "### TestPyPI smoke" >> "$GITHUB_STEP_SUMMARY"
echo "Publishing all six Python projects at \`$SEMVER\` (PEP 440: \`0.0.0.dev${{ github.run_number }}\`)." >> "$GITHUB_STEP_SUMMARY"
- name: Set smoke version across SDK packages
run: node packages/sdk/scripts/sync-sdk-version.mjs --set "${{ steps.smoke_version.outputs.semver }}"
- name: Generate all artifacts
run: pnpm run generate:all
- name: Build superdoc package for CLI native bundling
run: pnpm --prefix packages/superdoc run build:es
- name: Verify superdoc build output exists
run: |
test -f packages/superdoc/dist/super-editor.es.js \
|| (echo "FATAL: packages/superdoc/dist/super-editor.es.js missing — build:es likely failed silently" && exit 1)
# Stage CLI native binaries into Python companion packages.
# build-python-sdk.mjs has a fail-fast prerequisite check that requires
# these binaries; the auto-release path stages them via
# sdk-release-publish.mjs --npm-only, which the smoke job bypasses.
# These three steps map to steps 2, 3, 5 of sdk-release-publish.mjs.
- name: Build CLI native binaries (all platforms)
run: pnpm --prefix apps/cli run build:native:all
- name: Stage CLI artifacts
run: pnpm --prefix apps/cli run build:stage
- name: Stage Python companion binaries
run: node packages/sdk/scripts/stage-python-companion-cli.mjs
- name: Build and verify Python SDK
run: node packages/sdk/scripts/build-python-sdk.mjs
- name: Publish companion Python packages to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.TEST_PYPI_TOKEN }}
repository-url: https://test.pypi.org/legacy/
packages-dir: packages/sdk/langs/python/companion-dist/
skip-existing: true
- name: Publish main Python SDK to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.TEST_PYPI_TOKEN }}
repository-url: https://test.pypi.org/legacy/
packages-dir: packages/sdk/langs/python/dist/
skip-existing: true