Repository navigation
Add release-merge: the release branch into the base branch, then the base branch into develop - #10
Conversation
release-prep's three-or-four-parts pattern moves to .github/lib/versions.js, where the other version rules live, so the release-merge action applies the same rule from the same place. The wrapper around the real @actions/exec moves from the release-prep test into tests/support, for the tests that run git for real.
Merges the release branch into the base branch and the base back into develop, then publishes the GitHub release for the version. Every step is safe to rerun: a branch that already holds the other merges as a no-op and an existing release is skipped. A conflict abandons the merge, prints a MERGE_CONFLICT line the release dashboard reads, and fails with the branch named. The token reaches git through the environment of the fetch and the pushes only, never the checkout's config, so the caller can keep persist-credentials off and run third-party actions after it. The tests run the merges with real git against a bare repository on disk.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
A tag with a published release was read as a retry and the merges went on with the publish skipped. That is right when the release came from this branch, and wrong when the version went out earlier from another commit and the branch was never bumped past it: the new changes would land under a version that is already out. The tag is now fetched and the action goes on only when it contains the branch; otherwise it refuses before merging, naming the fix.
Two releases prepared with the same version and deployed at the same time would merge side by side, the second landing its changes under a version the first had just published. One concurrency group for the whole repository makes the second wait, and the action then refuses it on the published tag. The README says why the caller has to do this.
readRepository, isNotFound and tagExists move to .github/lib/github.js and runOrFail to .github/lib/exec.js, so release-prep and release-merge read GITHUB_REPOSITORY, a tag and a command's failure the same way. The docs name the base branch as main or master wherever it comes up, say which rule release-prep applies and when, and note the two step outputs the action does not expose.
The version becomes the tag and the title of the release, so it has to be the one release-prep wrote into the branch. The action now reads every file the branch's .puprc lists under paths.versions, with the regex pup uses for each, and refuses before merging when any holds a different version, when one holds none, or when the branch has no .puprc. The reader lives in .github/lib/puprc.js.
The release dashboard now sends the deploy step the branch, as it sends prepare, so the template declares branch and passes it to the action as it is.
…ex JavaScript rejects A publish value that is neither true nor false failed only in the publish step, after both merges were pushed; the merge step reads it now too, so the run stops before anything is merged. A .puprc regex pup accepts but JavaScript does not surfaced as a bare SyntaxError; the error now names the file and the regex. The README says where in the sequence the tag ancestry check runs.
…the branch's tip createRelease took the base branch's name as target_commitish, so the tag was made at whatever the base pointed to when the publish step ran: a pull request merged into the base between the push and the publish would have been tagged instead of the release. The merge step now reads the commit it left the base on and hands it to the publish step, which tags that commit. It is also an output of the action, base-sha, since it is the commit the caller builds from. SOFT-5188
The merge back into develop was unconditional: the fetch asked for refs/heads/develop and a repository without that branch (the TEC plugins keep main and release branches only) failed before anything was merged. An empty develop-branch now skips the second merge: the release is merged into the base and pushed, nothing is merged back, and the develop-branch output is empty. SOFT-5188
…ent is the gate SOFT-5188
… through a dispatch input A workflow_dispatch input left empty takes its default, which a test run confirmed, so a repository without develop writes develop-branch: '' in its caller. SOFT-5188
The publish step, the publish input and the published / release-url outputs are gone. The action merges, pushes and leaves the commit to publish at in base-sha; the caller builds from it and publishes the GitHub release after the build, so the release can carry the build and is published only once it exists. validateVersion moves to the shared lib, for the publish action that follows. SOFT-5188
The default queue keeps one pending run and cancels it when another arrives, so a third release requested while one waited for approval dropped the second. queue: max keeps them all, in order. SOFT-5188
…onger sends it SOFT-5188
…: max The plugin repositories lint their workflows with actionlint 1.7, which refuses the key, and the template exists to be copied into them. The README says what the default does and that a cancelled deploy is started again. SOFT-5188
…lishes No repository merges a release without publishing it, so a template around release-merge alone had no consumer. The complete deploy workflow, release-merge then the build then release-publish, comes with the publish action. SOFT-5188
The merges, the branch and version-file reads and the ancestry check go through Octokit: repos.merge, repos.getBranch, repos.getContent and repos.compareCommitsWithBasehead. GitHub makes the merge commits under the token's identity and signs them, so the bot identity, the token handed to git through its environment and the shallow-clone handling are gone, and the action needs no checkout. The caller checks out base-sha afterwards to build from it. A 204 from the merge endpoint is the retry case. A 409 that says "Merge conflict" prints the MERGE_CONFLICT marker as before; a 409 for a base that moved while the merge was being made fails without it, to be run again. readTag in lib/github.js reads the commit a tag points at, through the tag object when the tag is annotated, for the ancestry check against the release branch; tagExists keeps its contract on top of it. The tests run the action against a recorded client and assert the requests it sends and the ones it does not, with no git on disk. The recorded fetch answers a 204 without a body, as the merge endpoint does. Answers the review on #10: the raw git commands, the 41898282+ prefix (the constant is gone) and the dashboard named in the regex comment (the regex is gone; the marker line is the one place left that names it).
The merges, the branch and version-file reads and the ancestry check go through Octokit: repos.merge, repos.getBranch, repos.getContent and repos.compareCommitsWithBasehead. GitHub makes the merge commits under the token's identity and signs them, so the bot identity, the token handed to git through its environment and the shallow-clone handling are gone, and the action needs no checkout. The caller checks out base-sha afterwards to build from it. A 204 from the merge endpoint is the retry case. A 409 that says "Merge conflict" prints the MERGE_CONFLICT marker as before; a 409 for a base that moved while the merge was being made fails without it, to be run again. readTag in lib/github.js reads the commit a tag points at, through the tag object when the tag is annotated, for the ancestry check against the release branch; tagExists keeps its contract on top of it. The tests run the action against a recorded client and assert the requests it sends and the ones it does not, with no git on disk. The recorded fetch answers a 204 without a body, as the merge endpoint does. Answers the review on #10: the raw git commands, the 41898282+ prefix (the constant is gone) and the dashboard named in the regex comment (the regex is gone; the marker line is the one place left that names it).
The release branch's tip is read once, and that commit is what the version files are read at, what a published tag is compared against and what is merged into the base. Read by name at each step, a commit pushed to the branch between the version check and the merge reached the base unchecked, and a tag named like the branch could change what a read by name returned. The branch name stays in the merge message and in the messages.
A deploy that waits for an approval merges the branch's tip when the approval comes, which may not be the commit the run was started on, built as the RC and reviewed. The new optional head input takes that commit (github.sha in the caller) and the action refuses, before anything is merged, a branch whose tip is another commit. Empty keeps today's behaviour: the tip at that moment merges.
c7fa613 to
eeb3d77
Compare
Related
Resolves SOFT-5188.
Summary
Every plugin repository merges a release the same way: the release branch into the base branch (
main, ormasterwhere that is its name), then the base branch back intodevelop.release-mergeis that part as a composite action, so a repository's deploy workflow runs it, builds from the result, and goes on to its own steps. Publishing the GitHub release is not part of it: #16 addsrelease-publishfor that, and the caller runs it after the build at the commit this action leaves on the base branch. #16 also brings the deploy workflow to copy,templates/workflows/release.yml, which runs the two actions with the build between them; this PR ships the action alone.The action follows the shape #3 and #7 set: a CommonJS module run by
actions/github-script, inputs read fromenv, unit tests withnode:testagainst the shared harness. Everything goes through Octokit, the merges included: the action runs no git and needs no checkout.Changes
branch,version,base-branch(defaultmain),develop-branch(defaultdevelop; empty for a repository without one, which skips the second merge),token(defaultgithub.token)..puprclists underpaths.versionsholds it, and its tag either does not exist or belongs to a release published from this branch (a retry). A tag without a release, or made from another commit, is refused with nothing merged.repos.merge), which always makes a merge commit; GitHub makes it under the token's identity,github-actions[bot], and signs it. The second merge takes the merge commit itself as head, sodevelopreceives exactly what was released. A base that already holds the head answers 204 and nothing is written, so a failed run is rerun as it is.MERGE_CONFLICTon a line of its own (the marker the release dashboard reads from the log), names the branch in theconflictoutput and fails the step. The other 409, a base that moved while the merge was being made, fails without the marker and is run again.version,branch,base-sha(the merge commit on the base branch: the caller checks it out to build, and publishes the release there) andconflict.develop, since they are not pull requests; a rule that requires signed commits is satisfied. The caller template puts the job in an environment with required reviewers.concurrencygroup: two releases prepared with the same version would otherwise merge side by side..github/lib/github.js(readRepository,isNotFound,readTag, which reads the commit behind an annotated tag too, andtagExistson top of it),.github/lib/exec.js(runOrFail),.github/lib/puprc.js(readVersionFiles,versionIn),validateVersionandSTELLARWP_VERSION_PATTERNin.github/lib/versions.js;recordedRealExecmoves totests/support/actions.js, whose recorded fetch now answers a 204 without a body.How to test
mainanddevelop, write aworkflow_dispatchworkflow withbranchandversioninputs that runs this action and then checks out${{ steps.merge.outputs.base-sha }}(the README's first snippet), withuses:pointing at this branch's commit.release/x.y.zfrommain, bump the version files.puprclists on it (release-prepdoes this) and push.gh workflow run <that workflow> -f branch=release/x.y.z -f version=x.y.z.maingetsMerge release/x.y.z into main,developgetsMerge main back into develop (release/x.y.z), both bygithub-actions[bot]and verified, andbase-shais the merge commit onmain.developand on a new release branch, then run step 3 for that branch: the job fails on the develop merge with aMERGE_CONFLICT:line,mainhas the release,developis unchanged.node --test(301 tests).The steps above were run on the two private test plugins with
release-mergeandrelease-publishpinned to356cf3e, this stack's head, and the deploy callers of both checking outbase-shaafter the merge:github-actions[bot]and verified; the zip built from a checkout ofbase-shaattached to the release, tagged at the merge commitdevelop-branch: '':mainmerged and 2.1.46 published,developnever read nor touchedreadme.txtnot bumped), a published version whose tag does not contain the branch, a branch the repository does not havemain(a branch cut before 2.1.45 went out, bumped on the same lines):MERGE_CONFLICTon its own line,conflict=main, nothing written; after mergingmaininto the branch by hand, the rerun merges and publishes 2.1.46developconflicts with the release:mainmerged,MERGE_CONFLICTon its own line, no tag, no release; after resolvingdevelopby hand, the rerun finds both merges done and publishes 1.1.17branch+version): prepare, RC build, then this action merging the API way and release-publish atbase-sha; 2.1.47 with the zip, 1.1.18 withoutStack created with GitHub Stacks CLI • Give Feedback 💬
Summary by CodeRabbit