"""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()