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
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ jobs:
- name: Test content-lint
run: pnpm run content:lint:test

# Same reason: the release notes only run on a publish, too late to find out.
- name: Test release-notes
run: pnpm run release-notes:test

# A ratchet, not a floor: every file this pull request touched has to score
# at least as well as the same file on the base branch, and introduce no
# finding id it did not already carry. A fixed bar would fail a new page
Expand Down
69 changes: 33 additions & 36 deletions .github/workflows/label-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,70 +4,67 @@ on:
pull_request_target:
types:
- opened
- reopened
- edited
branches:
- main

# One run per PR at a time. A queued run replaces an older queued one, which is fine: each run reads the PR's current state.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: false

jobs:
add-pr-labels:
name: Add PR labels
# One GitHub API call, no checkout and no build: a 1 vCPU runner is plenty.
# The type label sorts the PR in the release notes, so it follows the title, not the first draft of it.
if: github.event.action != 'edited' || github.event.changes.title.from != null
# A few GitHub API calls, no checkout and no build: a 1 vCPU runner is plenty.
runs-on: ubuntu-slim
timeout-minutes: 5
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
PULL_REQUEST_TITLE: ${{ github.event.pull_request.title }}
with:
script: |
const labelsToAdd = []

const pullRequest = {
number: ${{ github.event.pull_request.number }},
title: process.env.PULL_REQUEST_TITLE,
labelsNames: ${{ toJson(github.event.pull_request.labels.*.name) }}
}
// The payload is a snapshot from when the event fired; a later title edit may already be applied.
const { data: pr } = await github.rest.pulls.get({ ...context.repo, pull_number: context.payload.pull_request.number })
const { number, title } = pr
const current = pr.labels.map(l => l.name)

// Select label based on the type in PR title
const pullRequestTypeToLabelName = {
// Conventional Commits type → label from .github/release.yml
const typeLabels = {
breaking: 'breaking',
feat: 'feature',
fix: 'bug',
build: 'build',
ci: 'ci',
docs: 'documentation',
enhancement: 'enhancement',
chore: 'dependencies',
perf: 'performance',
style: 'style',
test: 'test',
refactor: 'refactor',
revert: 'revert'
revert: 'revert',
}

for (const [pullRequestType, labelName] of Object.entries(
pullRequestTypeToLabelName
)) {
if (
pullRequest.title.startsWith(pullRequestType) &&
!pullRequest.labelsNames.includes(
pullRequestTypeToLabelName[pullRequestType]
)
) {
labelsToAdd.push(labelName)

break
}
const match = title.match(/^(\w+)(?:\(([^)]*)\))?(!)?:/)
const wanted = new Set()
if (match) {
const [, type, scope, bang] = match
if (typeLabels[type]) wanted.add(typeLabels[type])
// Only dependency bumps are release-worthy chores; the others go to Other Changes.
if (type === 'chore' && scope === 'deps') wanted.add('dependencies')
if (bang) wanted.add('breaking')
}

// Add selected labels
if (labelsToAdd.length > 0) {
await github.rest.issues.addLabels({
issue_number: pullRequest.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: labelsToAdd
})
}
// `breaking` can be set by hand on a PR whose title has no `!`, so it is never removed.
const managed = [...Object.values(typeLabels), 'dependencies'].filter(l => l !== 'breaking')
const stale = current.filter(l => managed.includes(l) && !wanted.has(l))
const missing = [...wanted].filter(l => !current.includes(l))

const issue = { owner: context.repo.owner, repo: context.repo.repo, issue_number: number }
for (const name of stale) await github.rest.issues.removeLabel({ ...issue, name })
if (missing.length > 0) await github.rest.issues.addLabels({ ...issue, labels: missing })
Comment thread
coderabbitai[bot] marked this conversation as resolved.
13 changes: 4 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,14 @@ jobs:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true

# createGithubReleases gives us one release per package, tagged pkg@version — that part
# we keep. Its *body* is the CHANGELOG section, which for a dependent package is a wall of
# "Updated dependencies [<hash>, <hash>, …]" and is empty for packages with no changeset
# of their own. `--generate-notes` is not the answer either: GitHub builds those from a
# commit range, and monorepo tags interleave, so each package would list everybody's PRs.
# So we rewrite each body from the PRs that actually touched that package's directory,
# grouped by the same categories as .github/release.yml.
- name: Push tags
if: steps.changesets.outputs.published == 'true'
run: git push --tags

- name: Rewrite release notes
# Rebuilds each release body from that package's PRs and changesets, then moves Latest
# back to evlog. The header of scripts/release-notes/index.mjs says why.
- name: Rewrite release notes and mark evlog Latest
if: steps.changesets.outputs.published == 'true'
run: node scripts/release-notes.mjs '${{ steps.changesets.outputs.publishedPackages }}'
run: node scripts/release-notes/index.mjs '${{ steps.changesets.outputs.publishedPackages }}'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pnpm telemetry:cli <command> # run this repo's CLI into that local dashboa
pnpm cli:sandbox # disposable per-framework apps under .sandbox/ to test the CLI by hand (--reset restores them, --smoke runs the feature matrix)
pnpm content:lint [path] # rank the written corpus by content findings (see scripts/content-lint/README.md)
pnpm content:lint:test # the scanner's own tests, including its two calibration fixtures
pnpm release-notes:test # tests for scripts/release-notes, which rewrites GitHub release bodies and pins Latest to evlog
```

Publishing is automated: changesets + `.github/workflows/release.yml`. Never run `pnpm release` or `changeset publish` manually.
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"cli": "node packages/cli/bin/evlog.mjs",
"content:lint": "node scripts/content-lint/index.mjs",
"content:lint:test": "vitest run --root scripts/content-lint",
"release-notes:test": "vitest run --root scripts/release-notes",
"cli:sandbox": "node scripts/cli-sandbox.mjs",
"test:coverage": "pnpm --filter evlog run test:coverage",
"test:coverage:open": "pnpm --filter evlog run test:coverage && open packages/evlog/coverage/index.html",
Expand Down
Loading
Loading