Stabilize staff concurrency and premium emoji; enable verified Drone deployment
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
63
scripts/configure_drone_secrets.py
Normal file
63
scripts/configure_drone_secrets.py
Normal file
@@ -0,0 +1,63 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user