agit: init agit helper
This initializes a very simple AGit helper script. Usage: ``` agit create ``` or ``` agit c ``` To create a new AGit Pr. Integrate Pulling from an AGit remote. Gitea doesn't expose an AGit endpoint in the api, or the `tea` cli. This makes pulling not feasible, since there is no robust way to query the AGit topic, which is the ref that need to be pulled. One possible solution currently could be scraping the gitea instructions for forking a pull request on an AGit PR.
This commit is contained in:
@@ -33,6 +33,7 @@
|
|||||||
self'.packages.tea-create-pr
|
self'.packages.tea-create-pr
|
||||||
self'.packages.merge-after-ci
|
self'.packages.merge-after-ci
|
||||||
self'.packages.pending-reviews
|
self'.packages.pending-reviews
|
||||||
|
self'.packages.agit
|
||||||
# treefmt with config defined in ./flake-parts/formatting.nix
|
# treefmt with config defined in ./flake-parts/formatting.nix
|
||||||
config.treefmt.build.wrapper
|
config.treefmt.build.wrapper
|
||||||
];
|
];
|
||||||
|
|||||||
37
pkgs/agit/README.md
Normal file
37
pkgs/agit/README.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# agit
|
||||||
|
|
||||||
|
A helper script for the AGit workflow with a gitea instance.
|
||||||
|
|
||||||
|
<!-- `$ agit --help` -->
|
||||||
|
|
||||||
|
```
|
||||||
|
usage: agit [-h] {create,c} ...
|
||||||
|
|
||||||
|
AGit utility for creating and pulling PRs
|
||||||
|
|
||||||
|
positional arguments:
|
||||||
|
{create,c} Commands
|
||||||
|
create (c) Create an AGit PR
|
||||||
|
|
||||||
|
options:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
|
||||||
|
The defaults that are assumed are:
|
||||||
|
TARGET_REMOTE_REPOSITORY = origin
|
||||||
|
DEFAULT_TARGET_BRANCH = main
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$ agit create
|
||||||
|
Will create an AGit Pr with the latest commit message title as it's topic.
|
||||||
|
|
||||||
|
$ agit create --topic "my-feature"
|
||||||
|
Set a custom topic.
|
||||||
|
|
||||||
|
$ agit create --force
|
||||||
|
Force push to a certain topic
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
References:
|
||||||
|
- https://docs.gitea.com/usage/agit
|
||||||
|
- https://git-repo.info/en/2020/03/agit-flow-and-git-repo/
|
||||||
205
pkgs/agit/agit.py
Normal file
205
pkgs/agit/agit.py
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
import argparse
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# push origin HEAD:refs/for/main
|
||||||
|
# HEAD: The target branch
|
||||||
|
# origin: The target repository (not a fork!)
|
||||||
|
# HEAD: The local branch containing the changes you are proposing
|
||||||
|
TARGET_REMOTE_REPOSITORY = "origin"
|
||||||
|
DEFAULT_TARGET_BRANCH = "main"
|
||||||
|
|
||||||
|
|
||||||
|
def run_git_command(command: list) -> tuple[int, str, str]:
|
||||||
|
"""Run a git command and return exit code, stdout, and stderr."""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(command, capture_output=True, text=True, check=False)
|
||||||
|
return result.returncode, result.stdout.strip(), result.stderr.strip()
|
||||||
|
except Exception as e:
|
||||||
|
return 1, "", str(e)
|
||||||
|
|
||||||
|
|
||||||
|
def get_latest_commit_info() -> tuple[str, str]:
|
||||||
|
"""Get the title and body of the latest commit."""
|
||||||
|
exit_code, commit_msg, error = run_git_command(
|
||||||
|
["git", "log", "-1", "--pretty=format:%B"]
|
||||||
|
)
|
||||||
|
|
||||||
|
if exit_code != 0:
|
||||||
|
print(f"Error getting commit info: {error}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
lines = commit_msg.strip().split("\n")
|
||||||
|
title = lines[0].strip() if lines else ""
|
||||||
|
|
||||||
|
body_lines = []
|
||||||
|
for line in lines[1:]:
|
||||||
|
if body_lines or line.strip():
|
||||||
|
body_lines.append(line)
|
||||||
|
|
||||||
|
body = "\n".join(body_lines).strip()
|
||||||
|
|
||||||
|
return title, body
|
||||||
|
|
||||||
|
|
||||||
|
def create_agit_push(
|
||||||
|
remote: str = "origin",
|
||||||
|
branch: str = "main",
|
||||||
|
topic: str | None = None,
|
||||||
|
title: str | None = None,
|
||||||
|
description: str | None = None,
|
||||||
|
force_push: bool = False,
|
||||||
|
local_branch: str = "HEAD",
|
||||||
|
) -> None:
|
||||||
|
if topic is None or title is None:
|
||||||
|
commit_title, _ = get_latest_commit_info()
|
||||||
|
|
||||||
|
if topic is None:
|
||||||
|
topic = commit_title
|
||||||
|
if title is None:
|
||||||
|
title = commit_title
|
||||||
|
|
||||||
|
refspec = f"{local_branch}:refs/for/{branch}"
|
||||||
|
push_cmd = ["git", "push", remote, refspec]
|
||||||
|
|
||||||
|
push_cmd.extend(["-o", f"topic={topic}"])
|
||||||
|
|
||||||
|
if title:
|
||||||
|
push_cmd.extend(["-o", f"title={title}"])
|
||||||
|
|
||||||
|
if description:
|
||||||
|
escaped_desc = description.replace('"', '\\"')
|
||||||
|
push_cmd.extend(["-o", f"description={escaped_desc}"])
|
||||||
|
|
||||||
|
if force_push:
|
||||||
|
push_cmd.extend(["-o", "force-push"])
|
||||||
|
|
||||||
|
if description:
|
||||||
|
print(
|
||||||
|
f" Description: {description[:50]}..."
|
||||||
|
if len(description) > 50
|
||||||
|
else f" Description: {description}"
|
||||||
|
)
|
||||||
|
print()
|
||||||
|
|
||||||
|
exit_code, stdout, stderr = run_git_command(push_cmd)
|
||||||
|
|
||||||
|
if stdout:
|
||||||
|
print(stdout)
|
||||||
|
if stderr:
|
||||||
|
print(stderr, file=sys.stderr)
|
||||||
|
|
||||||
|
if exit_code != 0:
|
||||||
|
print("\nPush failed!")
|
||||||
|
sys.exit(exit_code)
|
||||||
|
else:
|
||||||
|
print("\nPush successful!")
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_create(args: argparse.Namespace) -> None:
|
||||||
|
"""Handle the create subcommand."""
|
||||||
|
create_agit_push(
|
||||||
|
remote=args.remote,
|
||||||
|
branch=args.branch,
|
||||||
|
topic=args.topic,
|
||||||
|
title=args.title,
|
||||||
|
description=args.description,
|
||||||
|
force_push=args.force,
|
||||||
|
local_branch=args.local_branch,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def create_parser() -> argparse.ArgumentParser:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog="agit",
|
||||||
|
description="AGit utility for creating and pulling PRs",
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
epilog=f"""
|
||||||
|
The defaults that are assumed are:
|
||||||
|
TARGET_REMOTE_REPOSITORY = {TARGET_REMOTE_REPOSITORY}
|
||||||
|
DEFAULT_TARGET_BRANCH = {DEFAULT_TARGET_BRANCH}
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$ agit create
|
||||||
|
Will create an AGit Pr with the latest commit message title as it's topic.
|
||||||
|
|
||||||
|
$ agit create --topic "my-feature"
|
||||||
|
Set a custom topic.
|
||||||
|
|
||||||
|
$ agit create --force
|
||||||
|
Force push to a certain topic
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
|
||||||
|
subparsers = parser.add_subparsers(dest="subcommand", help="Commands")
|
||||||
|
|
||||||
|
create_parser = subparsers.add_parser(
|
||||||
|
"create",
|
||||||
|
aliases=["c"],
|
||||||
|
help="Create an AGit PR",
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
epilog="""
|
||||||
|
Examples:
|
||||||
|
$ agit create
|
||||||
|
Will create an AGit Pr with the latest commit message title as it's topic.
|
||||||
|
|
||||||
|
$ agit create --topic "my-feature"
|
||||||
|
Set a custom topic.
|
||||||
|
|
||||||
|
$ agit create --force
|
||||||
|
Force push to a certain topic
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
|
||||||
|
create_parser.add_argument(
|
||||||
|
"-r",
|
||||||
|
"--remote",
|
||||||
|
default=TARGET_REMOTE_REPOSITORY,
|
||||||
|
help=f"Git remote to push to (default: {TARGET_REMOTE_REPOSITORY})",
|
||||||
|
)
|
||||||
|
|
||||||
|
create_parser.add_argument(
|
||||||
|
"-b",
|
||||||
|
"--branch",
|
||||||
|
default=DEFAULT_TARGET_BRANCH,
|
||||||
|
help=f"Target branch for the PR (default: {DEFAULT_TARGET_BRANCH})",
|
||||||
|
)
|
||||||
|
|
||||||
|
create_parser.add_argument(
|
||||||
|
"-l",
|
||||||
|
"--local-branch",
|
||||||
|
default="HEAD",
|
||||||
|
help="Local branch to push (default: HEAD)",
|
||||||
|
)
|
||||||
|
|
||||||
|
create_parser.add_argument(
|
||||||
|
"-t", "--topic", help="Set PR topic (default: last commit title)"
|
||||||
|
)
|
||||||
|
|
||||||
|
create_parser.add_argument(
|
||||||
|
"--title", help="Set the PR title (default: last commit title)"
|
||||||
|
)
|
||||||
|
|
||||||
|
create_parser.add_argument(
|
||||||
|
"--description", help="Override the PR description (default: commit body)"
|
||||||
|
)
|
||||||
|
|
||||||
|
create_parser.add_argument(
|
||||||
|
"-f", "--force", action="store_true", help="Force push the changes"
|
||||||
|
)
|
||||||
|
|
||||||
|
create_parser.set_defaults(func=cmd_create)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = create_parser()
|
||||||
|
args = parser.parse_args()
|
||||||
|
if args.subcommand is None:
|
||||||
|
parser.print_help()
|
||||||
|
sys.exit(0)
|
||||||
|
args.func(args)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
26
pkgs/agit/default.nix
Normal file
26
pkgs/agit/default.nix
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
bash,
|
||||||
|
callPackage,
|
||||||
|
git,
|
||||||
|
lib,
|
||||||
|
openssh,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
writers = callPackage ../builders/script-writers.nix { };
|
||||||
|
in
|
||||||
|
writers.writePython3Bin "agit" {
|
||||||
|
flakeIgnore = [
|
||||||
|
"E501"
|
||||||
|
];
|
||||||
|
makeWrapperArgs = [
|
||||||
|
"--prefix"
|
||||||
|
"PATH"
|
||||||
|
":"
|
||||||
|
(lib.makeBinPath [
|
||||||
|
bash
|
||||||
|
git
|
||||||
|
openssh
|
||||||
|
])
|
||||||
|
];
|
||||||
|
} ./agit.py
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
{ config, pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
{
|
{
|
||||||
packages = {
|
packages = {
|
||||||
|
agit = pkgs.callPackage ./agit { };
|
||||||
tea-create-pr = pkgs.callPackage ./tea-create-pr { };
|
tea-create-pr = pkgs.callPackage ./tea-create-pr { };
|
||||||
zerotier-members = pkgs.callPackage ./zerotier-members { };
|
zerotier-members = pkgs.callPackage ./zerotier-members { };
|
||||||
zt-tcp-relay = pkgs.callPackage ./zt-tcp-relay { };
|
zt-tcp-relay = pkgs.callPackage ./zt-tcp-relay { };
|
||||||
|
|||||||
Reference in New Issue
Block a user