Skip to content

Coalesce <br><br> hard-break runs into a paragraph break #278

Coalesce <br><br> hard-break runs into a paragraph break

Coalesce <br><br> hard-break runs into a paragraph break #278

Workflow file for this run

name: JavaScript Checks and Release
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
# Manual release support - consolidated here to work with npm trusted publishing
# npm only allows ONE workflow file as trusted publisher, so all publishing
# must go through this workflow (js.yml)
workflow_dispatch:
inputs:
release_mode:
description: 'Manual release mode'
required: true
type: choice
default: 'instant'
options:
- instant
- changeset-pr
bump_type:
description: 'Manual release type'
required: true
type: choice
options:
- patch
- minor
- major
description:
description: 'Manual release description (optional)'
required: false
type: string
# Concurrency: Only one workflow run per branch at a time
# - For main branch (releases): cancel older runs to prevent blocking newer releases
# - For PR branches: queue runs to avoid cancelling checks on force-pushes
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref == 'refs/heads/main' }}
defaults:
run:
working-directory: js
jobs:
# === DETECT CHANGES - determines which jobs should run ===
detect-changes:
name: JS - Detect Changes
runs-on: ubuntu-latest
if: github.event_name != 'workflow_dispatch'
outputs:
js-code-changed: ${{ steps.changes.outputs.js-code-changed }}
js-changed: ${{ steps.changes.outputs.js-changed }}
scripts-changed: ${{ steps.changes.outputs.scripts-changed }}
js-workflow-changed: ${{ steps.changes.outputs.js-workflow-changed }}
any-js-code-changed: ${{ steps.changes.outputs.any-js-code-changed }}
any-code-changed: ${{ steps.changes.outputs.any-code-changed }}
docs-changed: ${{ steps.changes.outputs.docs-changed }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Detect changes
id: changes
working-directory: .
env:
GITHUB_EVENT_NAME: ${{ github.event_name }}
GITHUB_BASE_SHA: ${{ github.event.pull_request.base.sha }}
GITHUB_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: node scripts/detect-code-changes.mjs
# Changeset check - only runs on PRs with JS package source changes
changeset-check:
name: JS - Check for Changesets
runs-on: ubuntu-latest
needs: [detect-changes]
if: github.event_name == 'pull_request' && needs.detect-changes.outputs.js-code-changed == 'true'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24.x'
- name: Install dependencies
run: npm install
- name: Check for changesets
run: |
# Skip changeset check for automated version PRs
if [[ "${{ github.head_ref }}" == "changeset-release/"* ]]; then
echo "Skipping changeset check for automated release PR"
exit 0
fi
# Run changeset validation script
node ../scripts/validate-changeset.mjs
# === PUBLISHABLE CHANGES CHECK ===
# Warns when PR touches publishable paths without changeset
# This is informational — auto-release on main handles the gap
publishable-check:
name: JS - Publishable Changes Check
runs-on: ubuntu-latest
needs: [detect-changes]
if: github.event_name == 'pull_request' && needs.detect-changes.outputs.any-js-code-changed == 'true'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check for publishable changes without changeset
working-directory: .
env:
GITHUB_BASE_REF: ${{ github.base_ref }}
run: node scripts/check-publishable-changes.mjs
# Linting and formatting - runs when JS-relevant files change
lint:
name: JS - Lint and Format Check
runs-on: ubuntu-latest
needs: [detect-changes]
if: |
always() && !cancelled() && (
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch' ||
needs.detect-changes.outputs.any-js-code-changed == 'true' ||
needs.detect-changes.outputs.docs-changed == 'true'
)
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24.x'
- name: Install dependencies
run: npm install
- name: Run ESLint
run: npm run lint
- name: Check formatting
run: npm run format:check
- name: Check code duplication
run: npm run check:duplication
# Test matrix: Node.js on Ubuntu with unit tests and e2e tests
test:
name: JS - Test (Node.js on ${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 30
needs: [detect-changes, changeset-check]
# Run if: push/dispatch event, OR (changeset check passed/skipped AND JS code actually changed)
if: |
always() && !cancelled() && (
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch' ||
(
(needs.changeset-check.result == 'success' || needs.changeset-check.result == 'skipped') &&
needs.detect-changes.outputs.any-js-code-changed == 'true'
)
)
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24.x'
- name: Install dependencies
run: npm install
- name: Install Playwright browsers
run: npx playwright install --with-deps chromium
- name: Install Puppeteer browsers
run: npx puppeteer browsers install chrome
- name: Run unit, integration, and e2e process tests
run: npm test -- --testPathIgnorePatterns="docker.test.js"
- name: Run Habr article live integration tests
env:
HABR_INTEGRATION: 'true'
run: npm test -- --testPathPattern="habr-article" --testTimeout=120000
timeout-minutes: 15
- name: Run Google Docs public-document live integration tests (issue #90)
env:
GDOCS_INTEGRATION: 'true'
run: npm test -- --testPathPattern="gdocs-public-doc" --testTimeout=120000
timeout-minutes: 10
- name: Build Docker image for e2e tests
run: docker compose build
timeout-minutes: 10
- name: Run e2e Docker tests
run: npm test -- tests/e2e/docker.test.js
timeout-minutes: 5
# Release - only runs on main after tests pass (for push events)
# Self-healing: detects unreleased commits and auto-bumps patch version
release:
name: JS - Release
needs: [lint, test]
if: |
!cancelled() &&
github.ref == 'refs/heads/main' &&
github.event_name == 'push' &&
needs.lint.result == 'success' &&
needs.test.result == 'success'
runs-on: ubuntu-latest
# Permissions required for npm OIDC trusted publishing
permissions:
contents: write
pull-requests: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24.x'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm install
- name: Update npm for OIDC trusted publishing
run: node ../scripts/setup-npm.mjs
- name: Check if release is needed
id: release_check
run: node ../scripts/check-release-needed.mjs
- name: Auto-bump patch version for unreleased changes
if: steps.release_check.outputs.should_release == 'true' && steps.release_check.outputs.needs_auto_bump == 'true'
id: auto_bump
working-directory: .
# Uses scripts/safe-git-push.mjs for fetch+rebase+retry so concurrent
# pushes from the Rust release workflow don't cause non-fast-forward
# rejections. See docs/case-studies/issue-94.
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
cd js
node ../scripts/instant-version-bump.mjs --bump-type patch --description "Auto-release unreleased changes"
cd ..
git add -A
NEW_VERSION=$(node -p "require('./js/package.json').version")
git commit -m "chore(js): auto-bump to ${NEW_VERSION} for unreleased changes"
node scripts/safe-git-push.mjs --branch main
echo "version=${NEW_VERSION}" >> $GITHUB_OUTPUT
echo "Auto-bumped to ${NEW_VERSION}"
- name: Version packages and commit to main (changeset flow)
if: steps.release_check.outputs.has_changesets == 'true' && steps.release_check.outputs.needs_auto_bump != 'true'
id: version
run: node ../scripts/version-and-commit.mjs --mode changeset
- name: Publish to npm
if: |
steps.release_check.outputs.should_release == 'true' && (
steps.version.outputs.version_committed == 'true' ||
steps.version.outputs.already_released == 'true' ||
steps.auto_bump.outputs.version != '' ||
steps.release_check.outputs.needs_auto_bump == 'false'
)
id: publish
run: node ../scripts/publish-to-npm.mjs --should-pull
- name: Create GitHub Release
if: steps.publish.outputs.published == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: node ../scripts/create-github-release.mjs --release-version "${{ steps.publish.outputs.published_version }}" --repository "${{ github.repository }}"
- name: Format GitHub release notes
if: steps.publish.outputs.published == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: node ../scripts/format-github-release.mjs --release-version "${{ steps.publish.outputs.published_version }}" --repository "${{ github.repository }}" --commit-sha "${{ github.sha }}"
# Manual Instant Release - triggered via workflow_dispatch with instant mode
# This job is in js.yml because npm trusted publishing
# only allows one workflow file to be registered as a trusted publisher
instant-release:
name: JS - Instant Release
if: github.event_name == 'workflow_dispatch' && github.event.inputs.release_mode == 'instant'
runs-on: ubuntu-latest
# Permissions required for npm OIDC trusted publishing
permissions:
contents: write
pull-requests: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24.x'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm install
- name: Update npm for OIDC trusted publishing
run: node ../scripts/setup-npm.mjs
- name: Version packages and commit to main
id: version
run: node ../scripts/version-and-commit.mjs --mode instant --bump-type "${{ github.event.inputs.bump_type }}" --description "${{ github.event.inputs.description }}"
- name: Publish to npm
# Run if version was committed OR if a previous attempt already committed (for re-runs)
if: steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
id: publish
run: node ../scripts/publish-to-npm.mjs
- name: Create GitHub Release
if: steps.publish.outputs.published == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: node ../scripts/create-github-release.mjs --release-version "${{ steps.publish.outputs.published_version }}" --repository "${{ github.repository }}"
- name: Format GitHub release notes
if: steps.publish.outputs.published == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: node ../scripts/format-github-release.mjs --release-version "${{ steps.publish.outputs.published_version }}" --repository "${{ github.repository }}" --commit-sha "${{ github.sha }}"
# Manual Changeset PR - creates a pull request with the changeset for review
changeset-pr:
name: JS - Create Changeset PR
if: github.event_name == 'workflow_dispatch' && github.event.inputs.release_mode == 'changeset-pr'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24.x'
- name: Install dependencies
run: npm install
- name: Create changeset file
run: node ../scripts/create-manual-changeset.mjs --bump-type "${{ github.event.inputs.bump_type }}" --description "${{ github.event.inputs.description }}"
- name: Format changeset with Prettier
run: |
# Run Prettier on the changeset file to ensure it matches project style
npx prettier --write ".changeset/*.md" || true
echo "Formatted changeset files"
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: 'chore(js): add changeset for manual ${{ github.event.inputs.bump_type }} release'
branch: changeset-js-manual-release-${{ github.run_id }}
delete-branch: true
title: 'chore(js): manual ${{ github.event.inputs.bump_type }} release'
body: |
## Manual Release Request (JavaScript)
This PR was created by a manual workflow trigger to prepare a **${{ github.event.inputs.bump_type }}** release.
### Release Details
- **Type:** ${{ github.event.inputs.bump_type }}
- **Description:** ${{ github.event.inputs.description || 'Manual release' }}
- **Triggered by:** @${{ github.actor }}
### Next Steps
1. Review the changeset in this PR
2. Merge this PR to main
3. The automated release workflow will create a version PR
4. Merge the version PR to publish to npm and create a GitHub release