…tes its GitHub Releases itself — changesets/action@v1 finds no `New tag:` line under CLI v3 (objectui#11596) (objectstack-ai#11597)
Closes objectstack-ai#11596.
## What was wrong
- `changeset-release.yml` publishes through `changesets/action@v1`, and
v1's `runPublish` (read at v1.9.0, `src/run.ts`) pushes a tag and
creates a GitHub Release **only** for the packages whose `New tag:
PKG@VERSION` line it parses out of the publish script's stdout.
- `@changesets/cli` v3 (objectui#5296) prints no such line. Its
`changeset publish` still creates the annotated `PKG@VERSION` tags on
the runner (`createGitTags` in its `publish`), and those tags were never
pushed.
- Result: 17.6.0 and 17.7.0 are on npm, `git ls-remote --tags origin`
lists no `@17.6.0` or `@17.7.0` tag, and `GET
/repos/objectstack-ai/objectui/releases/tags/%40object-ui%2Fcore%4017.7.0`
answers 404 (17.5.0 answers 200).
- Older gap found while porting: `@object-ui/app-shell@17.5.0` has a tag
but no Release (38 of the 39 17.5.0 Releases exist). Its 17.5.0
CHANGELOG entry is 158,203 characters, over the Releases API's
125,000-character limit. objectstack hit the same limit
(objectstack#4900), and the ported script was written for it.
## What this PR changes
1. **`scripts/release-github-releases.mjs`**, ported from objectstack's
script of the same name at `7d0781482`. It creates one Release per
public package from that package's CHANGELOG entry. It looks the Release
up by tag first, so a re-run updates instead of re-creating. It
converges when a concurrent writer's POST answers 422 `already_exists`,
and its duplicate-Release audit never deletes anything. Bodies are
truncated at a line boundary to the API limit, with fences balanced and
a link to the full entry. Adapted to this repo:
- Packages come from `pnpm-workspace.yaml` through the existing
`workspacePackageDirs`, and private packages are skipped. The self-test
checks the release set against `.changeset/config.json`'s `fixed` group
minus its private members, comparing both ways.
- `--print-tags` prints the `PKG@VERSION` tags owed, one per line. The
push step reads this list.
- An empty release set is an error. The upstream script treats it as a
quiet no-op.
- `--dry-run` prints the planned tag → Release list with body sizes and
makes no API call.
- The self-test's oversized case is the real `@object-ui/types` 17.7.0
entry, over 1,000,000 characters. It has 15 batteries with per-battery
floors.
2. **`.github/workflows/changeset-release.yml`** gets two new steps
after `Verify the release reached npm` (which now has `id: verify-npm`).
Both steps have the same condition:
`${{ !cancelled() && github.event_name == 'push' &&
steps.changesets-publish.outcome == 'success' &&
steps.verify-npm.outcome == 'success' }}`.
On `schedule` / `workflow_dispatch` both outcomes are `skipped`, so
neither step can run there. A version already on npm never reaches the
job (the existing job guard). Both steps take `RELEASE_VERSION: ${{
needs.lane.outputs.manifest_version }}`.
- **Push the release tags**: pushes exactly the tags `--print-tags`
names that exist locally, in **one** `git push origin refs/tags/...`
(objectstack#2191). If any owed tag is missing locally, the step fails
and names it.
- **Create GitHub Releases (bodies truncated to the API limit)**: `node
scripts/release-github-releases.mjs` with `GITHUB_TOKEN`. It runs after
the tag push. A Release POSTed for a tag missing from the remote makes
the API create that tag as a lightweight tag at `target_commitish`.
- Permissions: the `release` job declares no `permissions:` of its own,
so it inherits the workflow-level `contents: write`. That covers both
the tag push and the Release create.
- `changesets/action` stays at `@v1`, and `createGithubReleases` stays
at its default. Under CLI v3 the action's own Release path never runs.
If it ever does again, the script updates the Releases it finds instead
of failing.
- Comments that said v1 creates the tags and Releases now say what
actually happens. That covers the dispatch-table annotation, THE LANES,
the every-`git` sweep (now listing `git rev-parse` and `git push`), the
`id:` note, property (2) of the `@v1` pin, the objectstack-ai#678 item and the
clear-step paragraph. Line addresses were not rewritten.
3. **`content/docs/guide/ci-cd-pipeline.md`**: new "Tags and GitHub
Releases" subsection under Changeset Release. The command-parity pin
needs the section to name the script, and it does.
4. **`scripts/__tests__/release-github-releases.test.ts`** (10 tests):
- Runs `--self-test`.
- Runs `--print-tags` and `--dry-run` for the anchor's current version,
with no token and an unreachable `GITHUB_API_URL`. The plan must equal
the tag list.
- Pins the wiring: the order `Publish to npm` → npm check → tag push →
Releases; every condition clause, with no `||`, `schedule`,
`workflow_dispatch` or `always()`; `RELEASE_VERSION` from the lane
output; the Release step running the script bare (not `--dry-run`); one
tag-ref push with no `--tags`, `--force`, `--delete` or `HEAD`;
effective `contents: write`; and the job's npm guard.
- 7 mutation tests show the pin can fail. Each one asserts its mutation
actually applied.
## Deviations from the suggested route, with reasons
- **No `git config user.*` in the push step.** The tags are created
inside the action step, after v1's default `setupGitUser` has configured
the identity. A push creates no object, so an identity set in the push
step would never be used. objectstack configures one because it runs
`changeset publish` itself.
- **Exact tag set instead of `git push origin --tags`.** The step pushes
only this version's tags, and it fails loudly when `changeset publish`
left one out. A bare `--tags` push answers `Everything up-to-date`
either way.
- **An empty release set fails.** In the upstream script a lost
`RELEASE_VERSION` would let the step report success having done nothing.
## `--dry-run` for 17.7.0
`GITHUB_REPOSITORY=objectstack-ai/objectui GITHUB_SHA=b493919c7
RELEASE_VERSION=17.7.0 node scripts/release-github-releases.mjs
--dry-run`, with no token in the environment:
```text
dry run: no API call. objectstack-ai/objectui, CHANGELOG links at b493919. tag -> release body:
@object-ui/app-shell@17.7.0 123919 chars (truncated from 1026237)
@object-ui/auth@17.7.0 35226 chars
@object-ui/cli@17.7.0 123970 chars (truncated from 131830)
@object-ui/collaboration@17.7.0 22506 chars
@object-ui/components@17.7.0 123919 chars (truncated from 605474)
@object-ui/core@17.7.0 123926 chars (truncated from 498537)
@object-ui/create-plugin@17.7.0 8714 chars
@object-ui/data-objectstack@17.7.0 123945 chars (truncated from 141243)
@object-ui/fields@17.7.0 123943 chars (truncated from 375807)
@object-ui/i18n@17.7.0 123947 chars (truncated from 310203)
@object-ui/layout@17.7.0 95867 chars
@object-ui/mobile@17.7.0 37713 chars
@object-ui/permissions@17.7.0 30565 chars
@object-ui/plugin-ai@17.7.0 49834 chars
@object-ui/plugin-calendar@17.7.0 123970 chars (truncated from 170068)
@object-ui/plugin-charts@17.7.0 123972 chars (truncated from 171267)
@object-ui/plugin-chatbot@17.7.0 123961 chars (truncated from 128322)
@object-ui/plugin-dashboard@17.7.0 123934 chars (truncated from 297362)
@object-ui/plugin-designer@17.7.0 123969 chars (truncated from 132685)
@object-ui/plugin-detail@17.7.0 123924 chars (truncated from 433424)
@object-ui/plugin-editor@17.7.0 35597 chars
@object-ui/plugin-form@17.7.0 123935 chars (truncated from 284678)
@object-ui/plugin-gantt@17.7.0 123915 chars (truncated from 183886)
@object-ui/plugin-grid@17.7.0 123645 chars (truncated from 335493)
@object-ui/plugin-kanban@17.7.0 123966 chars (truncated from 191209)
@object-ui/plugin-list@17.7.0 123934 chars (truncated from 260095)
@object-ui/plugin-map@17.7.0 107170 chars
@object-ui/plugin-markdown@17.7.0 52855 chars
@object-ui/plugin-report@17.7.0 84122 chars
@object-ui/plugin-timeline@17.7.0 123961 chars (truncated from 128776)
@object-ui/plugin-tree@17.7.0 119074 chars
@object-ui/plugin-view@17.7.0 123952 chars (truncated from 223540)
@object-ui/providers@17.7.0 23579 chars
@object-ui/react@17.7.0 123972 chars (truncated from 243678)
@object-ui/react-runtime@17.7.0 2764 chars
@object-ui/runner@17.7.0 62768 chars
@object-ui/sdui-parser@17.7.0 64848 chars
@object-ui/types@17.7.0 123646 chars (truncated from 1534879)
@object-ui/console@17.7.0 123941 chars (truncated from 211608)
39 release(s) planned (23 truncated to fit the 125000-character limit), 0 failed to plan.
```
`RELEASE_VERSION=17.7.0 node scripts/release-github-releases.mjs
--print-tags` prints 39 tags, one for each line above, and exits 0. For
comparison, the same dry-run plans 39 Releases with 1 truncated for
17.6.0, and 39 with 1 truncated for 17.5.0 (the truncated one is
`@object-ui/app-shell@17.5.0`, 158,203 → 123,973 characters).
## Verification (at `ae09c39a3`)
| Command | Result |
|---|---|
| `node scripts/release-github-releases.mjs --self-test` | `✓ ... 93
assertions` (15 batteries, each at its floor) |
| `pnpm exec vitest run
scripts/__tests__/release-github-releases.test.ts` | 10 passed |
| `bash ../objectstack/scripts/pm/os-verify-lock.sh -c 'pnpm exec vitest
run scripts/__tests__ --maxWorkers=2'` | `Test Files 179 passed, 2
skipped (181)`, `Tests 5452 passed, 2 skipped (5454)`, `VERDICT
command-exit 0` |
| `node scripts/check-changeset-presence.mjs` | exit 0, no changeset
owed (no released package's source or contract moved) |
| `node scripts/check-governed-queue-guard.mjs --test` with the 4
changed paths | `NOT GOVERNED` |
| `pnpm check:entry-guard` / `check:new-line-citations` /
`check:control-bytes` / `check:pre-install-import-graph` /
`check:bash32-floor` / `check:action-ref-convention` /
`check:shell-escape-residue` / `check:test-path-roots` | all exit 0; new
line citations: 0 |
| `pnpm type-check:scripts` | exit 0, and `--listFilesOnly` includes the
new test file |
| `eslint --no-inline-config` on the 2 new files | 0 problems. The test
file gets 118 rules. The `.mjs` gets none, because the repo's eslint
config gives `scripts/*.mjs` no rules. |
**Mutation check (one-off, not committed):** I changed `if
(targets.length === 0)` to `if (false && targets.length === 0)` with
`objectstack/scripts/ablation-replace.mjs`. The self-test went red, with
1 of 93 failing: "with no RELEASE_VERSION the tag list refuses instead
of printing nothing (exit 0)". The file was restored byte-for-byte to
the HEAD blob `083b5ea38`, and `git diff HEAD` was empty afterwards.
**Not verified:**
- The two new steps have not run on a real runner. The next release is
the first real run.
- No tag was created, pushed or deleted, and no Release was created,
during this work.
- `actionlint` is not available in this container. The workflow was
checked by YAML parse and by the pin tests only.
## Notes for the backfill (not done here)
- The issue names the commits each version was **published from**:
17.6.0 `b65fe911de22c7e3a573b669c4cd5efbe34758f2` (run 32739909630),
17.7.0 `b493919c71dc915afeb26c96338263f368e95c79` (objectstack-ai#5400 merge).
- Put the annotated tags on those commits **before** running the script.
Then run it with `RELEASE_VERSION=` that version and `GITHUB_SHA=` that
commit, so `target_commitish` and the CHANGELOG links point at it. A
Release for a tag that is not on the remote yet makes the API create a
lightweight tag at `GITHUB_SHA`.
- A run with `RELEASE_VERSION=17.5.0` updates the 38 existing 17.5.0
Releases and creates the missing `@object-ui/app-shell@17.5.0` one.
## Branch housekeeping
This branch was reused from objectstack-ai#11587, which was squash-merged. Merging
`main` into it brought back
`.changeset/11586-tailwind-source-none-console-runner.md`, which the
17.7.0 release commit had already consumed. A branch commit deletes it
again, so it does not appear in this PR's diff.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_014VGCS11YUtYAiinRcdqQwL
---
_Generated by [Claude
Code](https://claude.ai/code/session_014VGCS11YUtYAiinRcdqQwL)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
Closes #11586.
Why
The 17.7.0 release PR #5400 is red in four
Testshards on head1a91327c(CI run 37162552317), whilemainat0a53c67fwas green. Auto-merge is enabled on #5400, so these four failures are the only thing keeping 17.7.0 off npm.All four come from the release commit itself.
pnpm changeset:versionwrites 2,693 changeset bodies into 40CHANGELOG.mdfiles, and four checks read those files as if they were source.What changes
apps/console/src/index.css@import 'tailwindcss' source(none). Automatic detection is off, so only the declared@sourcelines feed the sheet, as inpackages/componentssince objectui#9569.packages/runner/src/index.css./**a no-op now says it is load-bearing.packages/types/.../handler-keys-string-any-mirrors-7344.test.tsactionscensus excludes:!*CHANGELOG.md, the spellingai-insights-retired-8800.test.tsand its siblings use.packages/types/.../base-bind-declared.test.tsbindcensus excludes:!*CHANGELOG.md.apps/console/.../index-css-scan-excludes-tests.test.tspackages/runner/.../published-stylesheet-sources.test.tsflex-shrink-0out..changeset/11586-…md@object-ui/consoleand@object-ui/runner, whose published CSS changes.The stylesheet change drops only prose-sourced rules
Each entry was compiled as written and with
source(none). Both compiles used the package root as base, which is wherepnpm buildruns.main7a7660c6.w-100, fromapps/console/docs/UI_IMPROVEMENT_PROPOSAL.mdprose-lg/prose-slatewith their descendants.flex-shrink-0and.isolate, frompackages/runner/CHANGELOG.mdflex-nowrap,flex-shrink-0,invert,isolateandpausedEvery rule that detection alone supplied comes from a
.mdfile. None comes from.ts/.tsxsource. Withsource(none)each entry compiles to the same rule set onmainand on the release tree.Verification (local, this worktree)
da0a6477) were overlaid onmain7a7660c6. Four files and seven tests failed, exactly the CI set.index.cssfiles were restored tomainwith the fix commit in place. The three new pins failed (3 failed, 6 passed), then passed again once the files were put back.packages/typestype-checkexits 0. The new test files add no TypeScript errors under the runner'stsconfig.test.jsonor the console'stsc --noEmit; those configs' other errors come from unbuilt workspace dependencies.eslinton the three changed test files reports 0 errors.node scripts/check-changeset-presence.mjspasses.node scripts/check-governed-queue-guard.mjs --testreports NOT GOVERNED.Changeset re-read (changeset-claim-re-read)
Three pending changesets name a file this PR edits. I re-read each one against the diff, and none is falsified:
6178-detail-section-header-color: a byte-identity measurement of an expression inplugin-detail. Switching detection off does not change it.8454-runner-stylesheet-source-set: says in the past tense what kept the runner sheet populated and what its two@source notlines removed. Both statements stay true, and both lines stay.6379-console-css-source-excludes-tests: explains why its@source notanchor sits at the repository root. That root covers the package globs and the automatic-detection root as they were when the anchor was chosen. The anchor still covers the package globs. This PR's changeset publishes in the same release and records that detection is now off.Not in this PR
The refresh lane's "Validate the post-version tree" step still runs only
scripts/__tests__. That scope was priced and ruled on objectui#5397. A package test that readsCHANGELOG.mdtherefore still surfaces only when CI runs on the release PR. Widening that scope is a separate decision.🤖 Generated with Claude Code
https://claude.ai/code/session_014VGCS11YUtYAiinRcdqQwL
Generated by Claude Code
Generated by Claude Code