Skip to content

Commit

Permalink
feat: add git node staging command
Browse files Browse the repository at this point in the history
Add a new `git node staging` command that automates cherry-picking
commits into staging branches.

It works by cherry-picking all commits that have no conflicts and
stopping when finding commits that have conflicts so that the
releaser can either manually fix that conflict to continue or skip.

Usage:

Fetches a commit list using `branch-diff` and automatically
cherry-picks / skips commits based on whether or not they land
cleanly:

  git node staging

By default the script will add `backport-request` labels to any
skip commit, in order to run in a "dry-run" mode, avoiding the GH
cli interactions, you can use the `skipGH` flag, e.g:

  git node staging --skipGH

After fixing up manual conflicts using `git cherry-pick --continue`
the releaser can resume the cherry-pick queue:

  git node staging --continue

Similarly, after manually skipping a commit using
`git cherry-pick --skip` the releaser can resume the cherry-pick
queue using:

  git node staging --skip

If you want to run the script and automatically skip any commit
that is unable to land cleanly you can use the `--autoSkip` flag:

  git node staging --autoSkip

Sets a custom reporter at the end of the automated operation:

  git node staging --reporter=json

Limits to 10 the number of commits to be cherry-picked:

  git node staging --pagination=10

Automates the backport request message, this won't run any of the
`branch-diff` or cherry-pick routines. Useful for when you removed
a faulty commit from the branch and want to signal to PR author and
collaborators that commit now needs backporting, just use its PR#:

  git node staging --backport=12345

More:

The automate cherry-pick logic also includes local persistency of
the ongoing commit list, in case a fatal error happens during the
command execution, it's possible to resume after cleaning up the
git repo state by running `git node staging` again.
  • Loading branch information
ruyadorno committed Dec 21, 2024
1 parent 3afe24f commit 639d5fa
Show file tree
Hide file tree
Showing 3 changed files with 821 additions and 6 deletions.
105 changes: 105 additions & 0 deletions components/git/staging.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import CLI from '../../lib/cli.js';
import { runPromise } from '../../lib/run.js';
import { Staging } from '../../lib/staging.js';

export const command = 'staging';
export const describe = 'Automatic port commits to a release line branch';

const stagingOptions = {
autoSkip: {
describe: 'Automatically skip commits with conflicts that have to be manually resolved',
type: 'boolean'
},
backport: {
describe: 'The PR ID / number to backport, skip staging commits',
type: 'number'
},
continue: {
describe: 'Continue the staging process after a conflict',
type: 'boolean'
},
paginate: {
describe: 'Sets a maximum number of commits to port',
type: 'number'
},
releaseLine: {
describe: 'The major version of the target release',
type: 'number'
},
reportDestination: {
describe: 'The destination to write the report to. Possible values are: ' +
'stdout, github, or a file path, defaults to an interactive prompt.',
type: 'string',
default: undefined
},
reporter: {
describe: 'The reporter to use for the output',
type: 'string',
default: 'markdown'
},
reset: {
describe: 'Reset the staging process',
type: 'boolean'
},
skip: {
describe: 'Continue the staging process marking the current commit as skipped',
type: 'boolean'
},
skipGH: {
describe: 'Skip all `gh` cli actions. Will not read / add label to GitHub PRs',
type: 'boolean'
}
};

export function builder(yargs) {
return yargs
.options(stagingOptions)
.example('git node staging --releaseLine=23',
'Port commits to the v1.x-staging branch');
}

export function handler(argv) {
const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
const cli = new CLI(logStream);
const dir = process.cwd();

return runPromise(main(argv, cli, dir)).catch((err) => {
if (cli.spinner.enabled) {
cli.spinner.fail();
}
throw err;
});
}

async function main(argv, cli, dir) {
const {
autoSkip,
backport,
paginate,
releaseLine,
reportDestination,
reporter,
reset,
skip,
skipGH
} = argv;
const staging = new Staging({
cli,
dir,
cont: argv.continue,
autoSkip,
paginate,
releaseLine,
reportDestination,
reporter,
skip,
skipGH
});
if (backport) {
await staging.requestBackport(backport);
} else if (reset) {
await staging.reset();
} else {
await staging.run();
}
}
24 changes: 18 additions & 6 deletions lib/prepare_release.js
Original file line number Diff line number Diff line change
Expand Up @@ -523,12 +523,24 @@ export default class ReleasePreparation extends Session {
const { newVersion } = this;
const proposalBranch = `v${newVersion}-proposal`;

await runAsync('git', [
'checkout',
'-b',
proposalBranch,
base
]);
try {
await forceRunAsync('git', [
'checkout',
'-b',
proposalBranch,
base
], { captureStdout: true, captureStderr: true, ignoreFailures: false });
} catch (err) {
const branchExistsRE = /fatal: a branch named '.*' already exists/i;
if (branchExistsRE.test(err.stderr)) {
await runAsync('git', [
'checkout',
proposalBranch
]);
} else {
throw err;
}
}
return proposalBranch;
}

Expand Down
Loading

0 comments on commit 639d5fa

Please sign in to comment.