forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdesktop-release-source-identity.py
More file actions
192 lines (169 loc) · 8.03 KB
/
Copy pathdesktop-release-source-identity.py
File metadata and controls
192 lines (169 loc) · 8.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#!/usr/bin/env python3
"""Build fail-closed evidence for a macOS candidate tag's source identity."""
from __future__ import annotations
import argparse
import json
import os
import re
import subprocess
from pathlib import Path
SHA_RE = re.compile(r"^[0-9a-f]{40}$")
SCHEMA = "desktop-release-planner-source-identity/v3"
RELEASE_TAG_RE = re.compile(r"^v[0-9]+\.[0-9]+\.[0-9]+\+[0-9]+-macos$")
def require_sha(name: str, value: str) -> str:
if not SHA_RE.fullmatch(value):
raise ValueError(f"{name} must be a 40-character lowercase SHA")
return value
def require_release_tag(value: str) -> str:
if not RELEASE_TAG_RE.fullmatch(value):
raise ValueError("release_tag must be an exact v<version>+<build>-macos tag")
return value
def git(repository_root: Path, args: list[str], *, check: bool = True) -> str:
# The caller can be a Git hook or another repository-scoped tool. Do not
# inherit its GIT_DIR/index/worktree state when proving a different,
# explicit repository root.
environment = {name: value for name, value in os.environ.items() if not name.startswith("GIT_")}
result = subprocess.run(
["git", "-C", str(repository_root), *args],
check=check,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=environment,
)
return result.stdout.strip()
def ensure_candidate_history_is_safe(
*,
repository_root: Path,
planned_source_sha: str,
candidate_source_sha: str,
origin_main_sha: str,
changelog_commit: str = "",
changelog_parent_sha: str = "",
) -> None:
"""Bind a candidate to the checked source without requiring a quiet main.
A direct candidate is exactly the green planner source. A changelog
candidate is exactly one child of that source and may change only desktop
changelog files. Either candidate must already be reachable from the fresh
main tip, but later main commits are intentionally outside the candidate.
"""
try:
git(
repository_root,
["merge-base", "--is-ancestor", candidate_source_sha, origin_main_sha],
)
except subprocess.CalledProcessError as error:
raise ValueError("candidate source must be reachable from fresh origin/main") from error
if changelog_commit:
if candidate_source_sha != changelog_commit:
raise ValueError("changelog candidate must exactly equal the consolidated changelog commit")
actual_parent = git(repository_root, ["rev-parse", f"{changelog_commit}^1"])
if actual_parent != changelog_parent_sha:
raise ValueError("changelog commit parent does not match its recorded source SHA")
if changelog_parent_sha != planned_source_sha:
raise ValueError("changelog commit must be based directly on the green planner source")
changed_paths = git(
repository_root,
["diff-tree", "--no-commit-id", "--name-only", "--diff-filter=ACDMR", "-r", changelog_commit],
).splitlines()
invalid_paths = [
path
for path in changed_paths
if path != "desktop/macos/CHANGELOG.json" and not path.startswith("desktop/macos/changelog/")
]
if not changed_paths:
raise ValueError("changelog candidate must contain a changelog-only change")
if invalid_paths:
raise ValueError("changelog candidate contains non-changelog paths: " + ", ".join(invalid_paths))
elif candidate_source_sha != planned_source_sha:
raise ValueError("direct candidate must exactly equal the green planner source")
def build_evidence(
*,
release_tag: str,
planned_source_sha: str,
candidate_source_sha: str,
origin_main_sha: str,
changelog_parent_sha: str = "",
changelog_commit: str = "",
changelog_pr: str = "",
) -> dict[str, object]:
"""Record the exact checked source and its optional changelog-only child."""
release_tag = require_release_tag(release_tag)
planned_source_sha = require_sha("planned_source_sha", planned_source_sha)
candidate_source_sha = require_sha("candidate_source_sha", candidate_source_sha)
origin_main_sha = require_sha("origin_main_sha", origin_main_sha)
if changelog_commit or changelog_pr or changelog_parent_sha:
if not (changelog_commit and changelog_pr and changelog_parent_sha):
raise ValueError("merged changelog evidence requires commit, PR URL, and first parent")
changelog_commit = require_sha("changelog_commit", changelog_commit)
changelog_parent_sha = require_sha("changelog_parent_sha", changelog_parent_sha)
if candidate_source_sha != changelog_commit:
raise ValueError("changelog candidate must exactly equal the consolidated changelog commit")
if changelog_parent_sha != planned_source_sha:
raise ValueError("changelog commit must be based directly on the green planner source")
if not re.fullmatch(r"https://github\.com/[^/]+/[^/]+/pull/[1-9][0-9]*", changelog_pr):
raise ValueError("changelog_pr must be a canonical GitHub pull request URL")
evidence = {
"schema": SCHEMA,
"release_tag": release_tag,
"mode": "changelog-only",
"planned_source_sha": planned_source_sha,
"candidate_source_sha": candidate_source_sha,
"origin_main_sha": origin_main_sha,
"changelog_commit": changelog_commit,
"changelog_pr": changelog_pr,
"changelog_parent_sha": changelog_parent_sha,
}
return evidence
if candidate_source_sha != planned_source_sha:
raise ValueError("direct candidate must exactly equal the green planner source")
return {
"schema": SCHEMA,
"release_tag": release_tag,
"mode": "direct",
"planned_source_sha": planned_source_sha,
"candidate_source_sha": candidate_source_sha,
"origin_main_sha": origin_main_sha,
}
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--release-tag", required=True)
parser.add_argument("--planned-source-sha", required=True)
parser.add_argument("--candidate-source-sha", required=True)
parser.add_argument("--origin-main-sha", required=True)
parser.add_argument("--changelog-parent-sha", default="")
parser.add_argument("--changelog-commit", default="")
parser.add_argument("--changelog-pr", default="")
parser.add_argument("--repository-root", default=".")
parser.add_argument("--output", required=True)
args = parser.parse_args()
try:
planned_source_sha = require_sha("planned_source_sha", args.planned_source_sha)
candidate_source_sha = require_sha("candidate_source_sha", args.candidate_source_sha)
changelog_parent_sha = args.changelog_parent_sha
if args.changelog_commit or args.changelog_pr or changelog_parent_sha:
changelog_parent_sha = require_sha("changelog_parent_sha", changelog_parent_sha)
ensure_candidate_history_is_safe(
repository_root=Path(args.repository_root),
planned_source_sha=planned_source_sha,
candidate_source_sha=candidate_source_sha,
origin_main_sha=require_sha("origin_main_sha", args.origin_main_sha),
changelog_commit=args.changelog_commit,
changelog_parent_sha=changelog_parent_sha,
)
evidence = build_evidence(
release_tag=args.release_tag,
planned_source_sha=planned_source_sha,
candidate_source_sha=candidate_source_sha,
origin_main_sha=args.origin_main_sha,
changelog_parent_sha=changelog_parent_sha,
changelog_commit=args.changelog_commit,
changelog_pr=args.changelog_pr,
)
except ValueError as error:
parser.error(str(error))
Path(args.output).write_text(json.dumps(evidence, indent=2, sort_keys=True) + "\n", encoding="utf-8")
print(f"desktop release source identity verified for {evidence['candidate_source_sha']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())