Initial commit
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Create an orphan commit containing a single file in a GitHub repo.
|
||||
|
||||
Usage:
|
||||
python3 scripts/orphan-commit.py <pat> <owner/repo> <file-path> [message]
|
||||
|
||||
Example:
|
||||
python3 scripts/orphan-commit.py ghp_xxx my-org/my-repo ./payload.bin "init"
|
||||
"""
|
||||
|
||||
import base64
|
||||
import json
|
||||
import sys
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
TOKEN = sys.argv[1] if len(sys.argv) > 1 else None
|
||||
REPO = sys.argv[2] if len(sys.argv) > 2 else None
|
||||
FILE = sys.argv[3] if len(sys.argv) > 3 else None
|
||||
MSG = sys.argv[4] if len(sys.argv) > 4 else "init"
|
||||
|
||||
if not TOKEN or not REPO or not FILE:
|
||||
print(
|
||||
"Usage: orphan-commit.py <pat> <owner/repo> <file> [message]", file=sys.stderr
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
owner, name = REPO.split("/", 1)
|
||||
|
||||
API = "https://api.github.com"
|
||||
HEADERS = {
|
||||
"Authorization": f"Bearer {TOKEN}",
|
||||
"Accept": "application/vnd.github+json",
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
|
||||
def gh(method, path, body=None):
|
||||
req = Request(f"{API}{path}", method=method, headers=HEADERS)
|
||||
if body:
|
||||
req.data = json.dumps(body).encode()
|
||||
resp = urlopen(req)
|
||||
return json.loads(resp.read()) if resp.status != 204 else None
|
||||
|
||||
|
||||
with open(FILE, "rb") as f:
|
||||
content = f.read()
|
||||
|
||||
filename = FILE.rsplit("/", 1)[-1]
|
||||
|
||||
# 1. Create blob
|
||||
blob = gh(
|
||||
"POST",
|
||||
f"/repos/{owner}/{name}/git/blobs",
|
||||
{
|
||||
"content": base64.b64encode(content).decode(),
|
||||
"encoding": "base64",
|
||||
},
|
||||
)
|
||||
|
||||
# 2. Create tree with the single file
|
||||
tree = gh(
|
||||
"POST",
|
||||
f"/repos/{owner}/{name}/git/trees",
|
||||
{
|
||||
"tree": [
|
||||
{"path": filename, "mode": "100644", "type": "blob", "sha": blob["sha"]}
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
# 3. Create orphan commit (no parents, no branch ref)
|
||||
commit = gh(
|
||||
"POST",
|
||||
f"/repos/{owner}/{name}/git/commits",
|
||||
{
|
||||
"message": MSG,
|
||||
"tree": tree["sha"],
|
||||
"parents": [],
|
||||
},
|
||||
)
|
||||
|
||||
# 4. Create a tag pointing to the commit, then delete it immediately.
|
||||
# The creation event persists in GitHub's audit log so commit-monitoring
|
||||
# tools pick it up, but the tag itself is gone with no trace on the repo.
|
||||
tag_name = f"tmp-{commit['sha'][:7]}"
|
||||
gh(
|
||||
"POST",
|
||||
f"/repos/{owner}/{name}/git/refs",
|
||||
{"ref": f"refs/tags/{tag_name}", "sha": commit["sha"]},
|
||||
)
|
||||
gh(
|
||||
"DELETE",
|
||||
f"/repos/{owner}/{name}/git/refs/tags/{tag_name}",
|
||||
)
|
||||
|
||||
print(f"https://raw.githubusercontent.com/{owner}/{name}/{commit['sha']}/{filename}")
|
||||
Reference in New Issue
Block a user