|
| 1 | +# (C) Datadog, Inc. 2026-present |
| 2 | +# All rights reserved |
| 3 | +# Licensed under a 3-clause BSD style license (see LICENSE) |
| 4 | +from __future__ import annotations |
| 5 | + |
| 6 | +from typing import TYPE_CHECKING |
| 7 | + |
| 8 | +import click |
| 9 | + |
| 10 | +if TYPE_CHECKING: |
| 11 | + from ddev.cli.application import Application |
| 12 | + |
| 13 | + |
| 14 | +@click.command(name='port-commit', short_help='Backport a commit onto a target branch') |
| 15 | +@click.pass_obj |
| 16 | +@click.argument('commit_hash', required=False) |
| 17 | +@click.option('-t', '--target-branch', default='master', show_default=True, help='Target branch to port to.') |
| 18 | +@click.option('-p', '--branch-prefix', default='port', show_default=True, help='Branch name prefix.') |
| 19 | +@click.option('-s', '--branch-suffix', default=None, help='Branch name suffix. Defaults to `to-<target-branch>`.') |
| 20 | +@click.option( |
| 21 | + '-l', |
| 22 | + '--pr-labels', |
| 23 | + default='qa/skip-qa', |
| 24 | + show_default=True, |
| 25 | + help='Comma-separated PR labels.', |
| 26 | +) |
| 27 | +@click.option('--no-pr', is_flag=True, default=False, help="Don't create a pull request.") |
| 28 | +@click.option('--draft', is_flag=True, default=False, help='Open the PR as a draft.') |
| 29 | +@click.option('--verify', is_flag=True, default=False, help='Run commit hooks (skipped by default).') |
| 30 | +@click.option('--dry-run', is_flag=True, default=False, help='Print every step instead of executing it.') |
| 31 | +def port_commit( |
| 32 | + app: Application, |
| 33 | + commit_hash: str | None, |
| 34 | + target_branch: str, |
| 35 | + branch_prefix: str, |
| 36 | + branch_suffix: str | None, |
| 37 | + pr_labels: str, |
| 38 | + no_pr: bool, |
| 39 | + draft: bool, |
| 40 | + verify: bool, |
| 41 | + dry_run: bool, |
| 42 | +) -> None: |
| 43 | + """ |
| 44 | + Backport a commit onto a target branch. |
| 45 | +
|
| 46 | + Cherry-picks COMMIT_HASH onto `--target-branch` (default `master`) on a new branch named |
| 47 | + `<github-user>/<prefix>-<sha[:10]>-<suffix>`, preserving `.in-toto` files from the target |
| 48 | + branch so package signatures stay intact. Pushes the branch and, unless `--no-pr` is set, |
| 49 | + opens a pull request titled `[Backport] <subject>` and labeled with `--pr-labels`. |
| 50 | +
|
| 51 | + If COMMIT_HASH is omitted, the current HEAD commit is used after confirmation. |
| 52 | +
|
| 53 | + The GitHub user for the branch prefix is taken from `ddev config` (`github.user`) or the |
| 54 | + `DD_GITHUB_USER` / `GITHUB_USER` / `GITHUB_ACTOR` environment variables. |
| 55 | + """ |
| 56 | + from ddev.cli.release.port_commit_workflow import PortStepError, build_port_steps, resolve_port_plan |
| 57 | + |
| 58 | + plan = resolve_port_plan( |
| 59 | + app, |
| 60 | + commit_hash=commit_hash, |
| 61 | + target_branch=target_branch, |
| 62 | + branch_prefix=branch_prefix, |
| 63 | + branch_suffix=branch_suffix, |
| 64 | + pr_labels=pr_labels, |
| 65 | + no_pr=no_pr, |
| 66 | + draft=draft, |
| 67 | + verify=verify, |
| 68 | + dry_run=dry_run, |
| 69 | + ) |
| 70 | + steps, pr_step = build_port_steps(app, plan) |
| 71 | + |
| 72 | + try: |
| 73 | + for step in steps: |
| 74 | + step.run() |
| 75 | + except PortStepError as e: |
| 76 | + app.abort(str(e)) |
| 77 | + |
| 78 | + if pr_step is not None and not plan.dry_run: |
| 79 | + app.display_success(f'Pull request created: {pr_step.pr_url}') |
| 80 | + app.display_success('All done.') |
0 commit comments