Guard normal operations-only fast-forward publication
This commit is contained in:
1 parent
78d7d12b9f
commit
3a7f4283c3
1 file changed
+12
-2
+12
-2
@@ -50,10 +50,18 @@ def check_bundle(args, source):
|
|||||||
resolved = run(["git", "--git-dir", source, "rev-parse", args.tag + "^{}"]).stdout.decode().strip()
|
resolved = run(["git", "--git-dir", source, "rev-parse", args.tag + "^{}"]).stdout.decode().strip()
|
||||||
require(resolved == args.commit, "Release tag does not point to reviewed commit")
|
require(resolved == args.commit, "Release tag does not point to reviewed commit")
|
||||||
refs = remote_refs()
|
refs = remote_refs()
|
||||||
|
if args.ops_update_from:
|
||||||
|
require(args.ops_branch and re.fullmatch(r"[a-f0-9]{40}", args.ops_update_from), "Explicit operations update guard required")
|
||||||
|
require(refs.get("refs/heads/main") == args.commit, "Published release main changed")
|
||||||
|
require(refs.get("refs/tags/" + args.tag + "^{}") == args.commit, "Published signed tag changed")
|
||||||
|
require(refs.get("refs/heads/" + args.ops_branch) == args.ops_update_from, "Operations branch changed")
|
||||||
|
run(["git", "--git-dir", source, "merge-base", "--is-ancestor", args.ops_update_from, "refs/heads/" + args.ops_branch])
|
||||||
|
else:
|
||||||
require(refs.get("refs/heads/main") == args.old_commit, "Canonical main changed; review before publishing")
|
require(refs.get("refs/heads/main") == args.old_commit, "Canonical main changed; review before publishing")
|
||||||
require("refs/tags/" + args.tag not in refs, "Release tag already exists; never overwrite")
|
require("refs/tags/" + args.tag not in refs, "Release tag already exists; never overwrite")
|
||||||
if args.ops_branch:
|
if args.ops_branch:
|
||||||
require(re.fullmatch(r"ops/[a-z0-9][a-z0-9/-]*", args.ops_branch), "Invalid operations branch")
|
require(re.fullmatch(r"ops/[a-z0-9][a-z0-9/-]*", args.ops_branch), "Invalid operations branch")
|
||||||
|
if not args.ops_update_from:
|
||||||
require("refs/heads/" + args.ops_branch not in refs, "Operations branch already exists; do not overwrite")
|
require("refs/heads/" + args.ops_branch not in refs, "Operations branch already exists; do not overwrite")
|
||||||
run(["git", "--git-dir", source, "merge-base", "--is-ancestor", args.commit, "refs/heads/" + args.ops_branch])
|
run(["git", "--git-dir", source, "merge-base", "--is-ancestor", args.commit, "refs/heads/" + args.ops_branch])
|
||||||
return refs
|
return refs
|
||||||
@@ -132,7 +140,8 @@ def publish(args):
|
|||||||
before = check_bundle(args, source)
|
before = check_bundle(args, source)
|
||||||
state = {"schema": 1, "repository": "gerardo/framebridge", "old_commit": args.old_commit,
|
state = {"schema": 1, "repository": "gerardo/framebridge", "old_commit": args.old_commit,
|
||||||
"commit": args.commit, "tag": args.tag, "bundle_sha256": args.bundle_sha256,
|
"commit": args.commit, "tag": args.tag, "bundle_sha256": args.bundle_sha256,
|
||||||
"phase": "CHECKED", "push_performed": False, "token_revoked": False}
|
"phase": "CHECKED", "push_performed": False, "token_revoked": False,
|
||||||
|
"ops_update_from": args.ops_update_from}
|
||||||
if not args.apply:
|
if not args.apply:
|
||||||
print(json.dumps(state, sort_keys=True))
|
print(json.dumps(state, sort_keys=True))
|
||||||
return
|
return
|
||||||
@@ -169,7 +178,7 @@ def publish(args):
|
|||||||
server, stop, thread, client = askpass_server(directory, token)
|
server, stop, thread, client = askpass_server(directory, token)
|
||||||
env = dict(os.environ, GIT_ASKPASS=str(client), GIT_TERMINAL_PROMPT="0", LC_ALL="C")
|
env = dict(os.environ, GIT_ASKPASS=str(client), GIT_TERMINAL_PROMPT="0", LC_ALL="C")
|
||||||
# No force flag. The normal receive-pack checks and Gitea hooks run.
|
# No force flag. The normal receive-pack checks and Gitea hooks run.
|
||||||
refspecs = [args.commit + ":refs/heads/main", "refs/tags/" + args.tag]
|
refspecs = [] if args.ops_update_from else [args.commit + ":refs/heads/main", "refs/tags/" + args.tag]
|
||||||
if args.ops_branch:
|
if args.ops_branch:
|
||||||
refspecs.append("refs/heads/" + args.ops_branch + ":refs/heads/" + args.ops_branch)
|
refspecs.append("refs/heads/" + args.ops_branch + ":refs/heads/" + args.ops_branch)
|
||||||
run(["git", "--git-dir", source, "-c", "credential.helper=", "push", "--atomic",
|
run(["git", "--git-dir", source, "-c", "credential.helper=", "push", "--atomic",
|
||||||
@@ -232,6 +241,7 @@ def main():
|
|||||||
parser.add_argument("--old-commit", required=True)
|
parser.add_argument("--old-commit", required=True)
|
||||||
parser.add_argument("--tag", required=True)
|
parser.add_argument("--tag", required=True)
|
||||||
parser.add_argument("--ops-branch", help="Publish one new operations branch atomically with the release")
|
parser.add_argument("--ops-branch", help="Publish one new operations branch atomically with the release")
|
||||||
|
parser.add_argument("--ops-update-from", help="Fast-forward only operations from this exact old commit; preserve published main and tag")
|
||||||
parser.add_argument("--journal", type=Path)
|
parser.add_argument("--journal", type=Path)
|
||||||
parser.add_argument("--apply", action="store_true")
|
parser.add_argument("--apply", action="store_true")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|||||||
Reference in new issue
Block a user