GitHub Actions の Repository Secrets を登録する Python スクリプトを書いてみた

GitHub Actionsを利用する上で、1つの Organization に複数のチームで利用していて、 Organization Secret を登録しにくい場合に、自分たちで管理する各 Repository Secret に登録・更新するスクリプトを書きました。
2022.07.07

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

サーモン大好き横山です。

GitHub Actions を利用する上で、1つの Organization に複数のチームで利用していて、 Organization Secret を登録しにくい場合に、自分たちで管理する各 Repository Secret に登録・更新する作業が面倒になることがありますよね。……はい、うちは面倒になりました。

ということで Python コードを書きました。

事前準備

Python バージョン

$ python3 -V
Python 3.10.4

パッケージインストール

$ pip install requests pynacl

コード

GitHub の REST API の Doc に書いてある方法を一部そのまま使いました。 ORG_NAMEREPO_NAMESGITHUB_TOKENACTIONS_SECRETS の値は適宜書き換えて実行してください。

main.py

from base64 import b64encode
from typing import Dict, List

import requests
from nacl import encoding, public

ORG_NAME = "OWNER"
REPO_NAMES = ["repo-name1", "repo-name2", "repo-name3"]
GITHUB_TOKEN = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

ACTIONS_SECRETS: List[Dict[str, str]] = [
    {
        "name": "SECRET_VALUE1",
        "value": "hoge",
    },
    {
        "name": "SECRET_VALUE2",
        "value": "fuga",
    },
    {
        "name": "SECRET_VALUE3",
        "value": "foo",
    },
]


def encrypt(public_key: str, secret_value: str) -> str:
    """Encrypt a Unicode string using the public key."""
    public_key = public.PublicKey(public_key.encode("utf-8"), encoding.Base64Encoder())
    sealed_box = public.SealedBox(public_key)
    encrypted = sealed_box.encrypt(secret_value.encode("utf-8"))
    return b64encode(encrypted).decode("utf-8")


def get_repository_public_key(
    session: requests.Session, org_name: str, repo_name: str
) -> Dict[str, str]:
    resp = session.get(
        f"https://api.github.com/repos/{org_name}/{repo_name}/actions/secrets/public-key"
    )
    return resp.json()


def main() -> None:
    session = requests.Session()

    session.headers.update(
        {
            "Accept": "application/vnd.github+json",
            "Authorization": f"token {GITHUB_TOKEN}",
        }
    )

    for repo_name in REPO_NAMES:
        public_key_dict = get_repository_public_key(session, ORG_NAME, repo_name)
        key_id = public_key_dict["key_id"]
        public_key = public_key_dict["key"]

        for secret in ACTIONS_SECRETS:
            url = f"https://api.github.com/repos/{ORG_NAME}/{repo_name}/actions/secrets/{secret['name']}"
            params = {
                "key_id": key_id,
                "encrypted_value": encrypt(public_key, secret["value"]),
            }
            resp = session.put(url, json=params)
            print(f"{repo_name}, {secret['name']}: {resp.status_code}")

        print("-" * 40)


if __name__ == "__main__":
    main()

実行結果例

$ python3 main.py
repo-name1, SECRET_VALUE1: 201
repo-name1, SECRET_VALUE2: 201
repo-name1, SECRET_VALUE3: 201
----------------------------------------
repo-name2, SECRET_VALUE1: 201
repo-name2, SECRET_VALUE2: 201
repo-name2, SECRET_VALUE3: 201
----------------------------------------
repo-name3, SECRET_VALUE1: 201
repo-name3, SECRET_VALUE2: 201
repo-name3, SECRET_VALUE3: 201
----------------------------------------

まとめ

GitHub Actions の設定に関して、同じ境遇の人の助力になれば幸いです。