Last active
May 3, 2026 19:55
-
-
Save pgmac/e6f1ddf1eb4519f5967423d52dcea30f to your computer and use it in GitHub Desktop.
Adding the applications secrets to it's repository [install]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """Script to add secrets to a GitHub repository.""" | |
| import argparse | |
| import subprocess | |
| import sys | |
| from pathlib import Path | |
| def run_cmd(cmd: list[str], capture: bool = True) -> subprocess.CompletedProcess: | |
| return subprocess.run(cmd, capture_output=capture, text=True) | |
| def get_current_repo() -> str | None: | |
| result = run_cmd(["gh", "repo", "view", "--json", "nameWithOwner", "-q", ".nameWithOwner"]) | |
| if result.returncode == 0: | |
| return result.stdout.strip() | |
| return None | |
| def list_org_secrets(org: str) -> list[str]: | |
| result = run_cmd(["gh", "secret", "list", "--org", org]) | |
| if result.returncode != 0: | |
| print(f"Error listing org secrets: {result.stderr}", file=sys.stderr) | |
| return [] | |
| lines = result.stdout.strip().split("\n") | |
| return [line.split()[0] for line in lines if line.strip()] | |
| def list_repo_secrets(repo: str) -> list[str]: | |
| result = run_cmd(["gh", "secret", "list", "-R", repo]) | |
| if result.returncode != 0: | |
| print(f"Error listing repo secrets: {result.stderr}", file=sys.stderr) | |
| return [] | |
| lines = result.stdout.strip().split("\n") | |
| return [line.split()[0] for line in lines if line.strip()] | |
| def add_secret(repo: str, name: str, value: str) -> bool: | |
| result = run_cmd(["gh", "secret", "set", name, "-b", value, "-R", repo]) | |
| if result.returncode != 0: | |
| print(f"Error setting secret {name}: {result.stderr}", file=sys.stderr) | |
| return False | |
| print(f"Added secret: {name}") | |
| return True | |
| def parse_tfvars(path: Path) -> dict[str, str]: | |
| secrets = {} | |
| content = path.read_text() | |
| for line in content.split("\n"): | |
| line = line.strip() | |
| if not line or line.startswith("#"): | |
| continue | |
| if "=" in line: | |
| key, value = line.split("=", 1) | |
| key = key.strip() | |
| value = value.strip().strip('"').strip("'") | |
| if key and value: | |
| secrets[key] = value | |
| return secrets | |
| def parse_env(path: Path) -> dict[str, str]: | |
| secrets = {} | |
| content = path.read_text() | |
| for line in content.split("\n"): | |
| line = line.strip() | |
| if not line or line.startswith("#"): | |
| continue | |
| if "=" in line: | |
| key, value = line.split("=", 1) | |
| key = key.strip() | |
| value = value.strip().strip('"').strip("'") | |
| if key and value: | |
| secrets[key] = value | |
| return secrets | |
| def select_secrets(secrets: dict[str, str], prompt: str) -> list[tuple[str, str]]: | |
| print(f"\n{prompt}") | |
| for i, key in enumerate(secrets.keys(), 1): | |
| print(f" {i}. {key}") | |
| print(" 0. None") | |
| print(" a. All") | |
| choice = input("Select (comma-separated for multiple): ").strip() | |
| if choice == "0": | |
| return [] | |
| if choice.lower() == "a": | |
| return list(secrets.items()) | |
| selected = [] | |
| for part in choice.split(","): | |
| idx = int(part.strip()) - 1 | |
| if 0 <= idx < len(secrets): | |
| key = list(secrets.keys())[idx] | |
| selected.append((key, secrets[key])) | |
| return selected | |
| def main(): | |
| parser = argparse.ArgumentParser(description="Add secrets to a GitHub repository") | |
| parser.add_argument("-R", "--repo", help="Repository (owner/repo), defaults to current repo") | |
| parser.add_argument("--org", default="pgmac-net", help="Organization to copy secrets from") | |
| parser.add_argument("--copy-org", action="store_true", help="Copy secrets from org (disabled by default)") | |
| parser.add_argument("--skip-tfvars", action="store_true", help="Skip terraform.tfvars") | |
| parser.add_argument("--skip-env", action="store_true", help="Skip .env files") | |
| args = parser.parse_args() | |
| repo = args.repo or get_current_repo() | |
| if not repo: | |
| print("Error: Not in a git repo or --repo not specified", file=sys.stderr) | |
| sys.exit(1) | |
| print(f"Target repository: {repo}") | |
| existing_secrets = list_repo_secrets(repo) | |
| if args.copy_org: | |
| print(f"\nFetching org secrets from {args.org}...") | |
| org_secrets = list_org_secrets(args.org) | |
| if org_secrets: | |
| print(f"Org secrets: {', '.join(org_secrets)}") | |
| choice = input("Select secrets to copy (comma-separated), or Enter for all: ").strip() | |
| if choice: | |
| selected = [] | |
| for part in choice.split(","): | |
| idx = int(part.strip()) - 1 | |
| if 0 <= idx < len(org_secrets): | |
| selected.append(org_secrets[idx]) | |
| else: | |
| selected = org_secrets | |
| for name in selected: | |
| if name in existing_secrets: | |
| print(f"Skipping {name} (already exists)") | |
| continue | |
| result = run_cmd(["gh", "secret", "list", "--org", args.org, "-q", f".[] | .[] | select(.key == \"{name}\") | .value"]) | |
| value = result.stdout.strip() | |
| if value: | |
| add_secret(repo, name, value) | |
| else: | |
| print("No org secrets found") | |
| if not args.skip_tfvars: | |
| tfvars_path = Path("terraform.tfvars") | |
| if tfvars_path.exists(): | |
| print(f"\nProcessing {tfvars_path}...") | |
| secrets = parse_tfvars(tfvars_path) | |
| for name, value in secrets.items(): | |
| secret_name = f"TF_VAR_{name}" | |
| if secret_name in existing_secrets: | |
| print(f"Skipping {secret_name} (already exists)") | |
| continue | |
| add_secret(repo, secret_name, value) | |
| if not args.skip_env: | |
| env_files = list(Path(".").glob(".env*")) + list(Path(".").glob("*.env")) | |
| for env_path in env_files: | |
| if env_path.is_file(): | |
| print(f"\nProcessing {env_path}...") | |
| secrets = parse_env(env_path) | |
| selected = select_secrets(secrets, f"Select secrets from {env_path}:") | |
| for name, value in selected: | |
| if name in existing_secrets: | |
| print(f"Skipping {name} (already exists)") | |
| continue | |
| add_secret(repo, name, value) | |
| print("\nDone!") | |
| if __name__ == "__main__": | |
| main() |
Author
Author
The [install] marker is something I'm trying with a script to identify and install gist scripts in my home directory.
Not sure how that'll go, or if I'll keep it.
Github doesn't expose which gist's I've starred via the API, so I'm using this as a workaround.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is a script I quickly vibed up to add the secrets used in
.envand in1erraform.tfvarsas Actions secrets in the associated GitHub repo.It's really just a wrapper around
gh secretto help simplify/automate the process.