Skip to content

Instantly share code, notes, and snippets.

@comdotlinux
Last active February 28, 2021 22:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save comdotlinux/9a53bb00767a16d6646464c4b8249094 to your computer and use it in GitHub Desktop.
Save comdotlinux/9a53bb00767a16d6646464c4b8249094 to your computer and use it in GitHub Desktop.
Create Github Secrets when you are not the owner and dont have access from the UI

Create Github Secrets

  • Needs python >= 3.6 (uses f strings)
  1. Create a python virtual environment
  2. python3 -m venv .venv
  3. source .venv/bin/activate
  4. Create the two files, requirements.txt and github_create_secret.py in this directory (being in this directory is not necessary but eases documentation)
  5. Install required dependencies
  6. pip install -U pip
  7. pip install -r requirements.txt
  8. Export required variables
  9. export GITHUB_AUTHORIZATION_TOKEN=CreatePatFromGithubUi
  10. export SECRET_KEY_NAME=NameYouWouldLikeToUseInYourActionsItWillBeCapitalizedInTheScript
  11. export SECRET_VALUE=SecretThatYouWouldLikeToStoreAndReferenceUsingTheSecretNameInActions
  12. Run The Script
  13. python github_create_secret.py -o repo_owner -r repo_name
  14. List The Secrets (Only needs the export GITHUB_AUTHORIZATION_TOKEN=CreatePatFromGithubUi)
  15. python github_create_secret.py -o repo_owner -r repo_name -l
References / Inspirations
  1. Github Secrets Api Documentation
  • Replace the orgs/{org} with repos/{repo_owner}/{repo}
  • e.g. https://api.github.com/orgs/ORG/actions/secrets changes to https://api.github.com/repos/owner/repo/actions/secrets
  1. Writing GitHub Secrets to a Repository You Don't Own
  2. Was not all copied from above 😅 but I already did this using postman before.

N.B: Did not use KotlinNative / GraalVM + (Quarkus / Kotlin Script) because the libraries require libsodium native at least for Java

import json
from argparse import ArgumentError, ArgumentParser
from base64 import b64encode
from os import environ
from typing import TypedDict
import requests
from nacl import public, encoding
def encrypt(public_key_for_repo: str, secret_value_input: str) -> str:
"""Encrypt a Unicode string using the public key."""
sealed_box = public.SealedBox(public.PublicKey(public_key_for_repo.encode("utf-8"), encoding.Base64Encoder()))
encrypted = sealed_box.encrypt(secret_value_input.encode("utf-8"))
return b64encode(encrypted).decode("utf-8")
def get_public_key(gh_base_url: str, gh_owner: str, gh_repo: str, gh_auth_token: str) -> (str, str):
public_key_endpoint: str = f"{gh_base_url}/{gh_owner}/{gh_repo}/actions/secrets/public-key"
headers: TypedDict[str, str] = {"Authorization": f"Bearer {gh_auth_token}"}
response = requests.get(url=public_key_endpoint, headers=headers)
if response.status_code != 200:
raise IOError(f"Could not get public key for repository {gh_owner}/{gh_repo}. The Response code was {response.status_code}")
public_key_json = response.json()
return public_key_json['key_id'], public_key_json['key']
def show_all_secrets(gh_base_url: str, gh_owner: str, gh_repo: str, gh_auth_token: str):
get_secrets_url = f"{gh_base_url}/{gh_owner}/{gh_repo}/actions/secrets"
headers: TypedDict[str, str] = {"Authorization": f"Bearer {gh_auth_token}"}
get_all_secrets_response = requests.get(url=get_secrets_url, headers=headers)
if get_all_secrets_response.status_code != 200:
raise IOError(f"Could not get all existing secrets for repository {gh_owner}/{gh_repo}. The Response code was {get_all_secrets_response.status_code}")
print(json.dumps(get_all_secrets_response.json(), indent=4, sort_keys=True))
def set_secret(gh_base_url: str, gh_owner: str, gh_repo: str, gh_auth_token: str, public_key_id: str, secret_key: str, encrypted_secret_value: str):
secret_creation_url = f"{gh_base_url}/{gh_owner}/{gh_repo}/actions/secrets/{secret_key}"
secret_creation_body = {"key_id": public_key_id, "encrypted_value": encrypted_secret_value}
headers: TypedDict[str, str] = {"Authorization": f"Bearer {gh_auth_token}", "Content-Type": "application/json"}
secret_creation_response = requests.put(url=secret_creation_url, json=secret_creation_body, headers=headers)
if secret_creation_response.status_code == 201 or secret_creation_response.status_code == 204:
print("--Secret Created / Updated!--")
else:
print(f"-- Error creating / updating github secret, the reason was : {secret_creation_response.reason}")
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
parser = ArgumentParser(description="A Python 3 script to create Github Secrets. Expects GITHUB_AUTHORIZATION_TOKEN env variable with value authorization token with repo permissions.")
# parser.add_argument("-v", "--verbosity", dest="verbose", help="increase output verbosity", action="store_true") # TODO: Add this for logging later
parser.add_argument("-o", "--owner", dest="owner", help="The Owner of the repository.", required=True)
parser.add_argument("-r", "--repo", dest="repo", help="The name of the repository for which we would like to create the secret", required=True)
parser.add_argument("--github-api-base-url", dest="github_api_url", help="Base endpoint for github api", default="https://api.github.com/repos")
parser.add_argument("-l", "--only_list", dest="only_list", help="Only List Secrets, do not update anything", action="store_true")
args = parser.parse_args()
github_authorization_token = environ.get('GITHUB_AUTHORIZATION_TOKEN')
if github_authorization_token is None or len(github_authorization_token) == 0:
raise ArgumentError(argument=None, message="Missing Environment variable GITHUB_AUTHORIZATION_TOKEN")
if not args.only_list:
secret_key_environment_variable_name = 'SECRET_KEY_NAME'
secret_key_input = str(environ.get(secret_key_environment_variable_name)).upper()
if secret_key_input is None or len(secret_key_input) == 0:
raise ArgumentError(argument=None, message=f"Missing environment variable with key {secret_key_environment_variable_name}. It will be the Key to create the secret.")
secret_value = environ.get('SECRET_VALUE')
if secret_value is None or len(secret_value) == 0:
raise ArgumentError(argument=None, message=f"Missing environment variable with key SECRET_VALUE. It will be the Key to create the secret.")
key_id, public_key = get_public_key(args.github_api_url, args.owner, args.repo, github_authorization_token)
encrypted_secret: str = encrypt(public_key_for_repo=public_key, secret_value_input=secret_value)
set_secret(gh_base_url=args.github_api_url, gh_owner=args.owner, gh_repo=args.repo, gh_auth_token=github_authorization_token, public_key_id=key_id, secret_key=secret_key_input, encrypted_secret_value=encrypted_secret)
show_all_secrets(gh_base_url=args.github_api_url, gh_owner=args.owner, gh_repo=args.repo, gh_auth_token=github_authorization_token)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment