Skip to content
Open
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
11 changes: 11 additions & 0 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Changesets

Changesets in this directory describe user-facing changes to published packages. Add one by running:

```shell
pnpm changeset
```

Commit the generated Markdown file with the package change. The publishing workflow consumes these files to update package versions and changelogs before publishing to npm.

See the [package publishing guide](../docs/package-publishing.md) for the complete release process.
14 changes: 14 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.4/schema.json",
"changelog": [
"@changesets/changelog-github",
{ "repo": "nodejs/nodejs.org" }
],
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
8 changes: 8 additions & 0 deletions .changeset/modern-publishing-workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@node-core/website-i18n': patch
'@node-core/rehype-shiki': patch
'@node-core/remark-lint': patch
'@node-core/ui-components': patch
---

Migrate package versioning and publishing to Changesets.
118 changes: 39 additions & 79 deletions .github/workflows/publish-packages.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
name: Publish Packages

# This workflow publishes packages to npm when changes are merged to main branch or when manually triggered.
# Changesets opens or updates a version PR when release notes are present. Once that PR is merged,
# this workflow publishes the versioned packages to npm.

on:
push:
paths:
- 'packages/**'
# For security reasons, this should never be set to anything but `main`
branches: [main]
workflow_dispatch:
inputs:
package:
description: 'Specific package to publish (leave empty for all packages)'
required: false
type: string

permissions:
contents: read
Expand All @@ -26,12 +19,15 @@ env:
COMMIT_SHA: ${{ github.sha }}

jobs:
prepare-packages:
name: Prepare Packages
release:
name: Create Release PR or Publish
runs-on: ubuntu-latest
outputs:
# Output the matrix of packages to publish for use in the publish job
matrix: ${{ steps.generate-matrix.outputs.matrix }}
# Never attempt to publish from forks (no trusted-publisher match / secrets).
if: github.repository == 'nodejs/nodejs.org'
permissions:
contents: write
id-token: write
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
Expand Down Expand Up @@ -62,83 +58,47 @@ jobs:

echo "✅ Commit is verified and trusted."

- name: Checkout repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 2 # Need at least 2 commits to detect changes between commits
persist-credentials: false

- name: Generate package matrix
id: generate-matrix
env:
PACKAGE: ${{ github.event.inputs.package }}
EVENT_NAME: ${{ github.event_name }}
run: |
if [ -n "$PACKAGE" ]; then
# If a specific package is requested via workflow_dispatch, just publish that one
echo "matrix={\"package\":[\"$PACKAGE\"]}" >> $GITHUB_OUTPUT
else
CHANGED_PACKAGES=()
for pkg in $(ls -d packages/*); do
PKG_NAME=$(basename "$pkg")
PKG_JSON="$pkg/package.json"

# Determine if the package has changed (or include all on manual trigger)
if [ "$EVENT_NAME" == "workflow_dispatch" ] || ! git diff --quiet $COMMIT_SHA~1 $COMMIT_SHA -- "$pkg/"; then
OLD_VERSION=$(git show $COMMIT_SHA~1:$PKG_JSON | jq -r '.version')
NEW_VERSION=$(jq -r '.version' "$PKG_JSON")
if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then
CHANGED_PACKAGES+=("$PKG_NAME")
fi
fi
done

# Format the output for GitHub Actions matrix using jq
PACKAGES_JSON=$(jq -n '$ARGS.positional' --args "${CHANGED_PACKAGES[@]}" -c)
echo "matrix={\"package\":$PACKAGES_JSON}" >> $GITHUB_OUTPUT
fi

publish:
name: Publish
needs: prepare-packages
runs-on: ubuntu-latest
permissions:
# Required for npm OIDC publishing (https://docs.npmjs.com/trusted-publishers)
id-token: write
# Skip if no packages need to be published
if: fromJson(needs.prepare-packages.outputs.matrix).package[0] != null
# Use the dynamic matrix from prepare-packages job to create parallel jobs for each package
strategy:
matrix: ${{ fromJson(needs.prepare-packages.outputs.matrix) }}
fail-fast: false # Continue publishing other packages even if one fails
steps:
- uses: nodejs/web-team/actions/setup-environment@9f3c83af227d721768d9dbb63009a47ed4f4282f
with:
pnpm: true
use-version-file: true
registry-url: 'https://registry.npmjs.org'
fetch-depth: 0

- name: Publish
working-directory: packages/${{ matrix.package }}
run: |
# Check if a custom publish script exists in package.json
if jq -e '.scripts.release' package.json > /dev/null; then
pnpm run release
fi

# Then publish the package to npm
pnpm publish --access public --no-git-checks
- name: Create release pull request or publish packages
id: changesets
uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1.9.0
with:
commit: 'chore: version packages'
title: 'chore: version packages'
version: node --run changeset:version
publish: node --run release
commitMode: github-api
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Notify on Manual Release
if: ${{ github.event_name == 'workflow_dispatch' }}
- name: Format published packages
if: steps.changesets.outputs.published == 'true'
id: notification
env:
PUBLISHED_PACKAGES: ${{ steps.changesets.outputs.publishedPackages }}
run: |
{
echo 'packages<<EOF'
jq -r '.[] | ":package: *Package*: `\(.name)@\(.version)` (<https://www.npmjs.com/package/\(.name)/v/\(.version)|View on npm>)"' <<< "$PUBLISHED_PACKAGES"
echo 'EOF'
} >> "$GITHUB_OUTPUT"

- name: Notify
if: steps.changesets.outputs.published == 'true'
uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0
env:
SLACK_COLOR: '#43853D'
SLACK_ICON: https://github.com/nodejs.png?size=48
SLACK_TITLE: ':rocket: Package Published: ${{ matrix.package }}'
SLACK_TITLE: ':rocket: Packages Published'
SLACK_MESSAGE: |
:package: *Package*: `${{ matrix.package }}` (<https://www.npmjs.com/package/@node-core/${{ matrix.package }}|View on npm>)
${{ steps.notification.outputs.packages }}
:bust_in_silhouette: *Published by*: ${{ github.triggering_actor }}
:octocat: *Commit*: <https://github.com/${{ github.repository }}/commit/${{ env.COMMIT_SHA }}|${{ env.COMMIT_SHA }}>
:octocat: *Commit*: <https://github.com/${{ github.repository }}/commit/${{ github.sha }}|${{ github.sha }}>
SLACK_USERNAME: nodejs-bot
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
52 changes: 52 additions & 0 deletions .github/workflows/pull-request-policy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Security Notes
# Only selected Actions are allowed within this repository. Please refer to (https://github.com/nodejs/nodejs.org/settings/actions)
# for the full list of available actions. If you want to add a new one, please reach out a maintainer with Admin permissions.
# REVIEWERS, please always double-check security practices before merging a PR that contains workflow changes!!
# AUTHORS, please only use actions with explicit SHA references, and avoid using `@master` or `@main` references or `@version` tags.

name: Pull Request Policy

on:
pull_request:
branches:
- main
types: [opened, edited, synchronize, reopened, ready_for_review]

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
changesets:
name: Changesets
runs-on: ubuntu-latest
if: |
github.event_name == 'pull_request' &&
!(github.event.pull_request.head.repo.full_name == github.repository &&
github.event.pull_request.head.ref == 'changeset-release/main')
steps:
- uses: nodejs/web-team/actions/setup-environment@9f3c83af227d721768d9dbb63009a47ed4f4282f
with:
pnpm: true
use-version-file: true
fetch-depth: 0

- run: pnpm changeset status --since origin/main

conventional-commits:
name: Conventional commits
runs-on: ubuntu-latest
steps:
- name: Check PR Title Format
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
REGEX="^[a-z][a-z0-9-]*(\([a-z0-9_.\/-]+\))?!?: .+"

if [[ ! "$PR_TITLE" =~ $REGEX || "$PR_TITLE" == *. ]]; then
echo 'PR titles must use <type>[optional scope][!]: <description> and must not end with a period.'
exit 1
fi
17 changes: 11 additions & 6 deletions .github/workflows/translations-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ jobs:
download_translations: true
localization_branch_name: ${{ env.BRANCH_NAME }}
create_pull_request: true
pull_request_title: '[automated]: crowdin sync'
pull_request_title: 'chore(i18n): sync translations from crowdin'
pull_request_body: 'New Crowdin translations from the [Node.js Crowdin project](https://crowdin.com/project/nodejs-web)'
commit_message: 'chore: synced translations from crowdin'
commit_message: 'chore(i18n): sync translations from crowdin'
env:
GITHUB_TOKEN: ${{ secrets.CROWDIN_GITHUB_BOT_TOKEN }}
# A numeric ID, found at https://crowdin.com/project/nodejs-web/tools/api
Expand Down Expand Up @@ -92,12 +92,17 @@ jobs:
cache-lint-${{ hashFiles('pnpm-lock.yaml') }}-
cache-lint-

- name: Patch version if the files changed
working-directory: packages/i18n
- name: Add changeset if the files changed
run: |
CHANGED_FILES=$(git diff --name-only HEAD^1 HEAD)
if [ -n "$CHANGED_FILES" ]; then
pnpm version patch --no-git-tag-version
cat > .changeset/crowdin-translations.md <<'EOF'
---
'@node-core/website-i18n': patch
---

Update translations from Crowdin.
EOF
fi

- name: Run ESLint
Expand All @@ -111,7 +116,7 @@ jobs:
uses: stefanzweifel/git-auto-commit-action@04702edda442b2e678b25b537cec683a1493fcb9 # v7.1.0
with:
commit_options: '--no-verify --signoff'
commit_message: 'chore: automated format of translated files'
commit_message: 'chore(i18n): format translated files'
branch: ${{ env.BRANCH_NAME }}

- name: Save Lint Cache
Expand Down
1 change: 1 addition & 0 deletions docs/code-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ This project follows the [Conventional Commits](https://www.conventionalcommits.
- Commit messages must include a "type" as described in Conventional Commits
- Commit messages **must** be written in lowercase, except proper nouns
- Commit messages **must not** end with a period `.`
- Pull request titles must use the same format and pass the `Conventional commits` check in the `Pull Request Policy` workflow

### Commit Signing

Expand Down
47 changes: 34 additions & 13 deletions docs/package-publishing.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This guide covers the package publishing process for the Node.js website's multi

- [Overview](#overview)
- [Repository Structure](#repository-structure)
- [Adding a Changeset](#adding-a-changeset)
- [Publishing Process](#publishing-process)

## Overview
Expand All @@ -29,24 +30,44 @@ nodejs.org/
└── site/ # Main website application
```

## Adding a Changeset

Package versions and release notes are managed with [Changesets](https://github.com/changesets/changesets). Changes to a published package should include a changeset in the same pull request.

Run the Changesets CLI from the repository root:

```shell
pnpm changeset
```

Select every package affected by the change, choose the appropriate semantic version bump, and write a concise summary for the changelog. Commit the generated Markdown file in `.changeset/` with the rest of the change.

The `Pull Request Policy` workflow rejects pull requests that change files under `packages/` without a changeset.

If a package change only affects internal tooling and does not require a release, run `pnpm changeset add --empty` instead.

Use `pnpm changeset:version` only to test the versioning step locally. The release workflow normally runs it and manages the resulting version changes.

## Publishing Process

Publishing is handled automatically through GitHub Actions, when changes are merged to the main branch (or via `workflow_dispatch`):
Publishing is handled automatically by the `Publish Packages` GitHub Actions workflow:

1. **Merge Queue**: Changes must come through GitHub's merge queue
2. **CI Pipeline**: "Linting and Tests" workflow completes successfully
3. **Auto-trigger**: "Publish Packages" workflow runs automatically
4. **Verification**: Committer must be verified from `noreply@github.com`
5. **Notification**: Slack notification sent to `#nodejs-website` if `workflow_dispatch` was the trigger.
2. **Collect changesets**: Merged changesets are collected into a `chore: version packages` pull request
3. **Version packages**: The version pull request updates package versions and changelogs
4. **Review and merge**: The version pull request goes through the normal review and merge queue
5. **Publish**: After that pull request lands, the workflow builds the workspace packages and publishes every newly versioned package to npm using trusted publishing
6. **Notify**: A release notification listing the published packages is sent to `#nodejs-web-infra-alerts`

Package versions should not be edited or published manually. A failed publishing run can be retried from GitHub Actions after its cause is resolved.

```mermaid
graph TD
A[Code Changes] --> B[Pull Request]
B --> C[CI Tests Pass]
C --> D[Merge to Main]
D --> E[Merge Queue]
E --> F[Auto Publish Trigger]
F --> G[Package Analysis]
G --> H[Version Bump]
H --> I[npm publish]
A[Package change and changeset] --> B[Pull request]
B --> C[Merge queue]
C --> D[Merge to main]
D --> E[Create or update Version Packages PR]
E --> F[Review and merge version PR]
F --> G[Build release artifacts]
G --> H[Publish to npm]
```
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
},
"scripts": {
"build": "turbo build",
"changeset": "changeset",
"changeset:version": "changeset version",
"cloudflare:deploy": "turbo cloudflare:deploy",
"cloudflare:preview": "turbo cloudflare:preview",
"deploy": "turbo deploy",
Expand All @@ -26,6 +28,7 @@
"prepare": "husky",
"prettier": "prettier \"**/*.{js,mjs,ts,tsx,md,mdx,json,yml,css}\" --check --cache --cache-strategy=content --cache-location=.prettiercache",
"prettier:fix": "node --run prettier -- --write",
"release": "pnpm --filter './packages/*' --if-present run build && changeset publish",
"scripts:release-post": "turbo scripts:release-post",
"start": "turbo start",
"storybook": "turbo storybook",
Expand All @@ -39,6 +42,8 @@
"turbo": "2.9.16"
},
"devDependencies": {
"@changesets/changelog-github": "^0.7.0",
"@changesets/cli": "^2.31.1",
"@eslint/js": "~10.0.1",
"@reporters/github": "^2.0.0",
"@testing-library/react": "~16.3.2",
Expand Down
1 change: 0 additions & 1 deletion packages/rehype-shiki/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"scripts": {
"build:ts": "tsc",
"build": "node --run build:ts",
"release": "node --run build",
"lint": "node --run lint:js",
"lint:fix": "node --run lint:js:fix",
"lint:js": "eslint \"**/*.mjs\"",
Expand Down
Loading
Loading