Skip to content

feat(freeze): add scheduled freeze CLI commands#993

Open
jd wants to merge 1 commit intomainfrom
devs/jd/worktree-scheduled-freeze/I414d168066f7b5070e75520bdcbb8189d694ad72
Open

feat(freeze): add scheduled freeze CLI commands#993
jd wants to merge 1 commit intomainfrom
devs/jd/worktree-scheduled-freeze/I414d168066f7b5070e75520bdcbb8189d694ad72

Conversation

@jd
Copy link
Member

@jd jd commented Feb 26, 2026

Add mergify freeze command group with list, create, update, and delete
subcommands for managing scheduled merge freezes. Supports both Mergify
application keys and GitHub tokens for authentication, auto-detects
repository from git remote, and provides both table and JSON output.

Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com

@mergify mergify bot had a problem deploying to Mergify Merge Protections February 26, 2026 19:11 Failure
@jd jd marked this pull request as draft February 26, 2026 19:11
@mergify
Copy link
Contributor

mergify bot commented Feb 26, 2026

Merge Protections

Your pull request matches the following merge protections and will not be merged until they are valid.

🔴 👀 Review Requirements

This rule is failing.
  • any of:
    • #approved-reviews-by>=2
    • author = dependabot[bot]
    • author = mergify-ci-bot
    • author = renovate[bot]

🔴 🔎 Reviews

This rule is failing.
  • #review-requested = 0
  • #review-threads-unresolved = 0
  • #changes-requested-reviews-by = 0

🟢 🤖 Continuous Integration

Wonderful, this rule succeeded.
  • all of:
    • check-success=ci-gate

🟢 Enforce conventional commit

Wonderful, this rule succeeded.

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?:

🟢 📕 PR description

Wonderful, this rule succeeded.
  • body ~= (?ms:.{48,})

@mergify
Copy link
Contributor

mergify bot commented Feb 26, 2026

🧪 CI Insights

Here's what we observed from your CI run for 389e2f9.

🟢 All jobs passed!

But CI Insights is watching 👀

@jd jd force-pushed the devs/jd/worktree-scheduled-freeze/I414d168066f7b5070e75520bdcbb8189d694ad72 branch from 572ba4a to e5d5b20 Compare February 26, 2026 19:48
@mergify mergify bot had a problem deploying to Mergify Merge Protections February 26, 2026 19:48 Failure
@mergify mergify bot requested a review from a team February 26, 2026 19:51
@jd jd marked this pull request as ready for review March 4, 2026 16:12
Copilot AI review requested due to automatic review settings March 4, 2026 16:12
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new mergify freeze command group to manage scheduled merge freezes via the Mergify API, along with a dedicated API wrapper and CLI tests.

Changes:

  • Introduces freeze CLI group with list, create, update, and delete subcommands, including table/JSON output.
  • Adds mergify_cli.freeze.api client functions for the scheduled freeze endpoints.
  • Adds a comprehensive respx-based CLI test suite for the new command group.

Reviewed changes

Copilot reviewed 4 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
mergify_cli/freeze/cli.py Implements the freeze click command group, formatting/output, and default token/repo detection.
mergify_cli/freeze/api.py Adds async API calls for list/create/update/delete scheduled freezes.
mergify_cli/cli.py Registers the new freeze command group in the top-level CLI.
mergify_cli/tests/freeze/test_cli.py Adds CLI tests covering list/create/update/delete flows and error paths.
mergify_cli/tests/freeze/__init__.py Adds package marker for freeze tests.
mergify_cli/freeze/__init__.py Adds package marker for freeze module.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jd jd force-pushed the devs/jd/worktree-scheduled-freeze/I414d168066f7b5070e75520bdcbb8189d694ad72 branch from e5d5b20 to 097f802 Compare March 5, 2026 07:10
@mergify mergify bot had a problem deploying to Mergify Merge Protections March 5, 2026 07:10 Failure
@jd jd force-pushed the devs/jd/worktree-scheduled-freeze/I414d168066f7b5070e75520bdcbb8189d694ad72 branch from 097f802 to 9ee9a71 Compare March 5, 2026 07:14
@mergify mergify bot had a problem deploying to Mergify Merge Protections March 5, 2026 07:14 Failure
Add `mergify freeze` command group with list, create, update, and delete
subcommands for managing scheduled merge freezes. Supports both Mergify
application keys and GitHub tokens for authentication, auto-detects
repository from git remote, and provides both table and JSON output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change-Id: I414d168066f7b5070e75520bdcbb8189d694ad72
Claude-Session-Id: 77d8be2d-854a-4d80-ac2d-4489fa50e31d
@jd jd force-pushed the devs/jd/worktree-scheduled-freeze/I414d168066f7b5070e75520bdcbb8189d694ad72 branch from 9ee9a71 to 389e2f9 Compare March 5, 2026 08:01
@mergify mergify bot had a problem deploying to Mergify Merge Protections March 5, 2026 08:01 Failure
@jd jd requested a review from Copilot March 5, 2026 08:13
Copy link
Contributor

Choose a reason for hiding this comment

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

No error handling on API responses

The API functions (list_freezes, create_freeze, etc.) call .json() on responses without checking status codes first. The check_for_status event hook on the HTTP client will raise on 4xx/5xx, but only after
aread() — if the hook fails to trigger or the client is used differently, you'd get silent failures. This is consistent with the existing codebase pattern though, so it's acceptable.

required=True,
help="Matching condition (repeatable, e.g. -c 'base=main')",
)
@click.option(
Copy link
Contributor

Choose a reason for hiding this comment

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

Users shouldn't need to re-specify every field just to change the end time ?

@mergify mergify bot requested a review from a team March 5, 2026 08:19
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 8 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +337 to +344
except CommandError:
return None

try:
user, repo_name = get_slug(remote_url)
except (ValueError, IndexError):
return None

Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

New auto-detection logic in get_default_repository() is only tested for the GITHUB_REPOSITORY env-var path. Consider adding tests for the git-remote path (e.g., using the existing git_mock fixture that mocks remote.origin.url) and for the failure case to ensure the CLI surfaces a clear error when repository detection isn't possible.

Suggested change
except CommandError:
return None
try:
user, repo_name = get_slug(remote_url)
except (ValueError, IndexError):
return None
except CommandError:
console.print(
"error: unable to detect the default repository. "
"Please set the 'GITHUB_REPOSITORY' environment variable or "
"run this command from a Git repository with a configured "
"'origin' remote.",
style="red",
)
return None
try:
user, repo_name = get_slug(remote_url)
except (ValueError, IndexError):
console.print(
"error: unable to parse the repository from the git remote URL. "
"Please set the 'GITHUB_REPOSITORY' environment variable.",
style="red",
)
return None
if not user or not repo_name:
console.print(
"error: detected an invalid repository slug. "
"Please set the 'GITHUB_REPOSITORY' environment variable.",
style="red",
)
return None

Copilot uses AI. Check for mistakes.
Comment on lines +311 to +323
async def get_default_token() -> str | None:
token = os.environ.get("MERGIFY_TOKEN") or os.environ.get("GITHUB_TOKEN")
if not token:
try:
token = await run_command("gh", "auth", "token")
except CommandError:
console.print(
"error: please set the 'MERGIFY_TOKEN' or 'GITHUB_TOKEN' environment variable, "
"or make sure that gh client is installed and you are authenticated",
style="red",
)
return None
return token
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

get_default_token() returns None on failure but callers (e.g., get_mergify_http_client(api_url, token)) treat token as a required string and will happily send Authorization: Bearer None or trigger Click's MissingParameter after already printing an error. Consider making this helper fail fast by raising a click.ClickException/SystemExit (or returning an empty string consistently and letting the Click option validation handle it), rather than returning None.

Copilot uses AI. Check for mistakes.
Comment on lines +326 to +345
async def get_default_repository() -> str | None:
repo = os.environ.get("GITHUB_REPOSITORY")
if repo:
return repo

try:
remote_url = await git(
"config",
"--get",
"remote.origin.url",
)
except CommandError:
return None

try:
user, repo_name = get_slug(remote_url)
except (ValueError, IndexError):
return None

return f"{user}/{repo_name}"
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

get_default_repository() silently returns None when it can't detect the repo (no GITHUB_REPOSITORY, no remote.origin.url, or unparsable URL). Since the Click option is marked required=True, this turns into a generic "Missing option" error with no actionable guidance. Consider printing/raising a clear error explaining the supported detection methods (env var, --repository, or git remote) when detection fails.

Copilot uses AI. Check for mistakes.
"-t",
help="Mergify or GitHub token",
envvar=["MERGIFY_TOKEN", "GITHUB_TOKEN"],
required=True,
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

At the command-group level, --token is marked required=True but its default can evaluate to None (see utils.get_default_token()), which causes Click to raise MissingParameter after already printing a custom error message. This also makes mergify freeze (with no subcommand) prone to failing even though the handler tries to print help. Consider removing required=True on the group option and instead validating in freeze() only when a subcommand is invoked (or make the default raise a Click exception directly).

Suggested change
required=True,

Copilot uses AI. Check for mistakes.
Comment on lines +141 to +147
@click.option(
"--repository",
"-r",
help="Repository full name (owner/repo)",
required=True,
default=lambda: asyncio.run(utils.get_default_repository()),
)
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

Same issue as --token: --repository is required=True but the default detection can return None, which will prevent mergify freeze from showing help and yields a generic missing-parameter error without guidance. Consider making the group option non-required and performing explicit validation only when a subcommand runs, with a clear error message when auto-detection fails.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

3 participants