64 lines
3.0 KiB
Python
64 lines
3.0 KiB
Python
"""Create/update repository secrets without putting their values in command arguments."""
|
|
import argparse
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import re
|
|
import urllib.error
|
|
import urllib.request
|
|
from urllib.parse import urlsplit
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--server", required=True)
|
|
parser.add_argument("--repo", default="trevor/new_lottery_bot")
|
|
parser.add_argument("--token-file", type=Path)
|
|
parser.add_argument("--ssh-key", type=Path, required=True)
|
|
parser.add_argument("--known-hosts", type=Path, required=True)
|
|
parser.add_argument("--env-file", type=Path, default=Path(".env.prod"))
|
|
parser.add_argument("--deploy-user", required=True)
|
|
parser.add_argument("--deploy-path", required=True)
|
|
args = parser.parse_args()
|
|
if urlsplit(args.server).scheme != "https":
|
|
parser.error("Drone must use HTTPS")
|
|
if not re.fullmatch(r"[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+", args.repo):
|
|
parser.error("Invalid repository name")
|
|
token = args.token_file.read_text(encoding="utf-8").strip() if args.token_file else os.getenv("DRONE_TOKEN", "")
|
|
if not token:
|
|
parser.error("Provide --token-file or DRONE_TOKEN")
|
|
values = {
|
|
"lottery_deploy_user": args.deploy_user,
|
|
"lottery_deploy_path": args.deploy_path,
|
|
"lottery_deploy_ssh_key": args.ssh_key.read_text(encoding="utf-8"),
|
|
"lottery_deploy_known_hosts": args.known_hosts.read_text(encoding="utf-8"),
|
|
"lottery_env": args.env_file.read_text(encoding="utf-8-sig"),
|
|
}
|
|
if any(not value.strip() for value in values.values()):
|
|
parser.error("All secret values must be nonempty")
|
|
endpoint = args.server.rstrip("/") + f"/api/repos/{args.repo}/secrets"
|
|
def request(url, method="GET", body=None):
|
|
payload = None if body is None else json.dumps(body).encode("utf-8")
|
|
req = urllib.request.Request(url, data=payload, method=method,
|
|
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"})
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=30) as response:
|
|
data = response.read()
|
|
return json.loads(data) if data else None
|
|
except urllib.error.HTTPError as error:
|
|
raise SystemExit(f"Drone API rejected {method}: HTTP {error.code}. Secret values were not logged.") from None
|
|
existing = {item["name"] for item in request(endpoint)}
|
|
for name, value in values.items():
|
|
body = {"name": name, "data": value, "pull_request": False}
|
|
request(endpoint + "/" + name if name in existing else endpoint,
|
|
"PATCH" if name in existing else "POST", body)
|
|
print(f"Configured {name} (pull requests disabled)")
|
|
configured = {item["name"] for item in request(endpoint)}
|
|
if not values.keys() <= configured:
|
|
raise SystemExit("Drone secret verification failed")
|
|
print("All required repository secrets are present")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|