monk / set_github_secrets.py
hf-actions
chore: add set_github_secrets.py helper and PyNaCl requirement
475fc0c
"""set_github_secrets.py
Reads values from .env and publishes them as GitHub Actions secrets for a given repo.
Usage:
python set_github_secrets.py --owner <owner> --repo <repo> --keys KEY1 KEY2 ...
Requires a GitHub token in the environment as `GITHUB_TOKEN` or `GH_TOKEN` with `repo` scope.
"""
import os
import sys
import json
import argparse
from pathlib import Path
try:
from dotenv import load_dotenv
load_dotenv()
except Exception:
pass
import requests
import base64
try:
from nacl import public, encoding
except Exception:
public = None
def load_token():
for k in ("GITHUB_TOKEN", "GH_TOKEN"):
v = os.getenv(k)
if v:
return v
return None
def get_public_key(token: str, owner: str, repo: str):
url = f"https://api.github.com/repos/{owner}/{repo}/actions/secrets/public-key"
headers = {"Authorization": f"token {token}", "Accept": "application/vnd.github+json"}
r = requests.get(url, headers=headers, timeout=15)
if r.status_code != 200:
raise RuntimeError(f"Failed to fetch public key: {r.status_code} {r.text}")
return r.json()
def encrypt_secret(public_key: str, value: str) -> str:
if public is None:
raise RuntimeError("PyNaCl is required to encrypt secrets. Install with 'pip install pynacl'.")
pk = base64.b64decode(public_key)
pk_obj = public.PublicKey(pk)
sealed_box = public.SealedBox(pk_obj)
encrypted = sealed_box.encrypt(value.encode('utf-8'))
return base64.b64encode(encrypted).decode('utf-8')
def put_secret(token: str, owner: str, repo: str, name: str, encrypted_value: str, key_id: str):
url = f"https://api.github.com/repos/{owner}/{repo}/actions/secrets/{name}"
headers = {"Authorization": f"token {token}", "Accept": "application/vnd.github+json"}
payload = {"encrypted_value": encrypted_value, "key_id": key_id}
r = requests.put(url, headers=headers, data=json.dumps(payload), timeout=15)
if r.status_code not in (201, 204):
raise RuntimeError(f"Failed setting secret {name}: {r.status_code} {r.text}")
return True
DEFAULT_KEYS = [
'FB_PAGE_ACCESS_TOKEN',
'FB_PAGE_ID',
'OPENAI_API_KEY',
'REPLICATE_API_TOKEN',
'DAILY_PROMPT',
'RUN_DAILY_REPLICATE',
'SHOW_POST_LOG',
]
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--owner', required=True)
parser.add_argument('--repo', required=True)
parser.add_argument('--keys', nargs='*')
args = parser.parse_args()
token = load_token()
if not token:
print('No GitHub token found in env (GITHUB_TOKEN/GH_TOKEN). Aborting.')
sys.exit(2)
keys = args.keys or DEFAULT_KEYS
to_publish = {}
for k in keys:
v = os.getenv(k)
if v is not None and v != '':
to_publish[k] = v
if not to_publish:
print('No configured keys found in environment to publish. Exiting.')
return
print(f'Publishing {len(to_publish)} secrets to {args.owner}/{args.repo}...')
pk_info = get_public_key(token, args.owner, args.repo)
key_id = pk_info.get('key_id')
public_key = pk_info.get('key')
if not key_id or not public_key:
print('Failed to retrieve public key info from GitHub API.')
sys.exit(3)
for k, v in to_publish.items():
try:
enc = encrypt_secret(public_key, v)
put_secret(token, args.owner, args.repo, k, enc, key_id)
print('OK', k)
except Exception as e:
print('ERROR', k, str(e))
if __name__ == '__main__':
main()