Skip to content

ci: harden and document the release procedure#268

Open
Kyleasmth wants to merge 18 commits into
mainfrom
ks/YPE-2486-release-hardening
Open

ci: harden and document the release procedure#268
Kyleasmth wants to merge 18 commits into
mainfrom
ks/YPE-2486-release-hardening

Conversation

@Kyleasmth

@Kyleasmth Kyleasmth commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Ports the release-hardening work from YPE-2486 to the React SDK.

What's here

  • RELEASE-RUNBOOK.md — 10 npm/Changesets failure modes with state-check + recovery commands.
  • docs/release-hardening-plan.md — engineering plan; records the keep-Changesets decision and per-AC status.
  • Commit-lint (AC6) — CI workflow anchored at the origin/main tip, husky commit-msg hook, and config (allows the YPE-#### prefix, ignores the release commit). Ported from the Kotlin SDK.
  • PUBLISHING.md — cross-links the runbook.

Greptile Summary

This PR hardens the React SDK release procedure by porting CI guardrails from the Kotlin SDK and documenting 10 failure modes for npm/Changesets workflows. It adds four new GitHub Actions workflows (commitlint, PR title lint, changeset gate, and workflow comments), corepack-aware Husky hooks implementing Decision 2, and companion documentation (RELEASE-RUNBOOK.md, docs/release-hardening-plan.md).

  • New CI gates: conventional PR title (amannn/action-semantic-pull-request@v6), per-commit commitlint anchored to git merge-base origin/main, and a hard-fail changeset-per-PR check that exempts changeset-release/main.
  • Corepack-aware Husky hooks: both commit-msg and pre-commit prefer corepack pnpm and fall back to bare pnpm where corepack isn't available, enforcing the repo-pinned pnpm version.
  • Documentation: RELEASE-RUNBOOK.md covers 10 failure modes using correct per-package tags (@youversion/platform-core@$VERSION); docs/release-hardening-plan.md records the keep-Changesets decision and AC status — though its AC6 section still describes a headerPattern for YPE-#### ticket prefixes that was removed per Decision 1 and is absent from commitlint.config.js.

Confidence Score: 4/5

Safe to merge once the stale ticket-prefix documentation in docs/release-hardening-plan.md is corrected or removed.

The code changes are correct and well-reasoned throughout — workflows, hooks, and the commitlint config all implement the documented decisions faithfully. The one concrete defect is in docs/release-hardening-plan.md: the 'Ticket prefix allowed' paragraph in the AC6 section describes a headerPattern for YPE-#### prefixes that was explicitly removed per Decision 1 and does not exist in commitlint.config.js. A contributor following that documentation would try the described format and hit an unexpected commitlint failure on every commit.

docs/release-hardening-plan.md — the AC6 'Ticket prefix allowed' section contradicts both Decision 1 and the actual commitlint.config.js.

Important Files Changed

Filename Overview
docs/release-hardening-plan.md New engineering plan document; 'Ticket prefix allowed' section in the AC6 block describes a headerPattern that was explicitly removed per Decision 1 and is absent from commitlint.config.js — stale docs that will mislead contributors.
.github/workflows/changeset.yml New CI gate enforcing a changeset per PR; uses --diff-filter=d (excludes deletions) where --diff-filter=A (added only) better matches the stated intent of detecting newly added changeset files.
.github/workflows/commitlint.yml New commit-lint CI workflow; correctly uses Node 24, full-depth fetch, and merge-base range calculation to lint only new commits in the PR.
.github/workflows/pr-title.yml New PR title lint workflow using amannn/action-semantic-pull-request@v6; implements Decision 1 correctly as the primary gate for the squash-merge commit format.
commitlint.config.js New ESM commitlint config extending config-conventional; correctly ignores the Changesets release commit (anchored regex) and merge commits, with body/footer line-length rules disabled.
.husky/commit-msg New husky hook implementing Decision 2 correctly: prefers corepack pnpm, falls back to bare pnpm for Node 25+ environments without corepack.
RELEASE-RUNBOOK.md New runbook covering 10 npm/Changesets failure modes; correctly uses per-package tags (e.g., @youversion/platform-core@$VERSION) throughout sections 1 and 9.
package.json Adds @commitlint/cli@19.8.1 and @commitlint/config-conventional@19.8.1; version 19.x supports Node >=22.13 (satisfies the engine floor) and was deliberately pinned to avoid the Node-incompatible @21 version.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    PR([Pull Request opened / updated]) --> PT[pr-title.yml\namannn/action-semantic-pull-request\nLints PR title as Conventional Commit]
    PR --> CL[commitlint.yml\npnpm exec commitlint\nLints individual commits in range]
    PR --> CS[changeset.yml\nDetects added .changeset/*.md\nExempts changeset-release/main]
    PR --> CI[ci.yml\nBuild · Test · Lint · Typecheck\nNode 24 LTS]
    PT -->|Pass| MERGE{Merge to main\nsquash-merge}
    CL -->|Pass| MERGE
    CS -->|Pass| MERGE
    CI -->|Pass| MERGE
    MERGE --> RA[release.yml\nchangesets/action\nOpens Version Packages PR]
    RA --> VPR([Version Packages PR\nmanual approval gate\nfor major bumps])
    VPR -->|Merge| PUB[Trusted Publishing\n+ NPM_TOKEN fallback\npnpm publish per package]
    PUB --> TAG[Per-package git tags\n@youversion/platform-core@V\n@youversion/platform-react-hooks@V\n@youversion/platform-react-ui@V]
    CS -->|No changeset| FAIL1([PR blocked\npnpm changeset or\npnpm changeset --empty])
    PT -->|Not conventional| FAIL2([PR blocked\nfix PR title])
    CL -->|Bad commit msg| FAIL3([PR blocked\nfix commit messages])
Loading

Reviews (12): Last reviewed commit: "Merge branch 'main' into ks/YPE-2486-rel..." | Re-trigger Greptile

@changeset-bot

changeset-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 97d82b7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 0 packages

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Comment thread .github/workflows/commitlint.yml
Comment thread package.json Outdated
Comment thread RELEASE-RUNBOOK.md Outdated
Comment thread commitlint.config.js Outdated
Comment thread .husky/commit-msg Outdated
Comment thread commitlint.config.js Outdated
@jhampton

Copy link
Copy Markdown
Collaborator

⚠️ No Changeset found

Latest commit: 9e3c408

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets
When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

I understand that there are no changesets, but there should be one, or at least there should be a recommendation about the version impact. In a perfect world, we would at least see the next version calculated based on conventional commits and semantic release conventions. So in this case, there are two commits that contain the prefix fix. And for those, that should trigger a patch increase.

Now obviously when the PR lands, it would be a single patch level increase, not multiple patch level increases. I also wonder what happens if we don't have a change set and instead we decide to roll together n number of changes in a specific build. In that case, I suppose the next time we went to go do a change set, it would calculate and find the highest version bump that was available.

The problem with that is that now we have a batching problem. In lean software engineering, you'd want to be releasing more often. So the fact that there is a PR without a changeset to me feels like a smell or as possibly an anti pattern.

I'm willing to have the conversation and there are no right answers here. There are just a bunch of potentially wrong ones. So please take this as more of a, let's think about this – this is feedback v "sosmething here is wrong." I think this is suboptimal right now, and I think that we can probably improve it.

Those of us who are handling releases have the confidence that we're doing the right thing for each individual PR and that we're shipping improvements to the community as fast as humanly possible and as responsibly as our mission demands. For things like patch releases that may address small issues (and don't necessarily rise to the level of a point increase), most consumers will allow patch level increases for things like security fixes, or small bugs. But even we shouldn't wholesale be adopting point level increases, especially when we consider things like supply chain attacks.

Small PRs that are patch fixes and bug fixes tend to get approved as long as the change set is relatively small. The size of a change and its "reviewability" are a signal about a given change's impact, and smaller changes should receive careful-enough scrutiny to avoid loosening security or introducing other subtle bugs, especially since so many of us allow these patch-level increases automatically (like Dependabot, e.g.)

Things like point increases should trigger much more diligence. Our downstream dependencies and their transitive dependencies may have wider wider version ranges that they accept. But generally speaking, in this day and age, we should be pinning specific versions and maybe allowing for patch increases. And we should probably be telling our consumers and partners to do the same.

@jhampton jhampton left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please take the feedback and make sure it's captured in this repo as learning (agents, skills, etc). Some of these need @davidfedor consulted if not deciding. We can (and should) defer landing this until we have clear decisions, then pin those behaviors with both deterministic (code/config) guardrails and semi-deterministic (Greptile, AGENTS.md) rules.

Comment thread .github/workflows/commitlint.yml
Comment thread .husky/commit-msg Outdated
Comment thread commitlint.config.js Outdated
Comment thread docs/release-hardening-decisions.md

@jhampton jhampton left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved the two questions. Lookin' good.

Comment thread docs/release-hardening-decisions.md Outdated
Comment thread docs/release-hardening-decisions.md Outdated
Comment thread docs/release-hardening-decisions.md Outdated
Comment thread docs/release-hardening-decisions.md Outdated
Comment thread docs/release-hardening-decisions.md Outdated
@Kyleasmth Kyleasmth changed the title YPE-2486: harden and document the release procedure ci: harden and document the release procedure (YPE-2486) Jul 20, 2026
@Kyleasmth Kyleasmth changed the title ci: harden and document the release procedure (YPE-2486) ci: harden and document the release procedure Jul 20, 2026
@Kyleasmth
Kyleasmth force-pushed the ks/YPE-2486-release-hardening branch from 578c2b4 to 7f7ece4 Compare July 20, 2026 21:34
@Kyleasmth
Kyleasmth requested a review from jhampton July 21, 2026 15:55
@Kyleasmth
Kyleasmth requested a review from davidfedor July 21, 2026 15:55
@jhampton
jhampton dismissed their stale review July 21, 2026 18:36

All feedback has been addressed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants