使用 GitHub REST API 修改 GitHub Actions Secrets

参考文档:https://docs.github.com/en/rest/actions/secrets

没代码没真相,直接上代码,后面再解释怎么回事。

import { Octokit } from '@octokit/core'
import sodium from 'libsodium-wrappers'

// Octokit.js
// https://github.com/octokit/core.js#readme
const octokit = new Octokit({
    auth: 'ghp_xxxxxxxxxxxxxxxxxx', // 替换为你的 GitHub token,参考 https://github.com/settings/tokens ,其中至少要有 repo 或 public_repo 权限才能修改 Actions Secrets
    request: {
        timeout: 10 * 1000, // 记得设置超时,不然会无限等待
    },
})

type GetARepositoryPublicKeyRequest = {
    owner: string
    repo: string
}

/**
 * 获取 存储库 公共密钥
 文档:https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28#get-a-repository-public-key
 */
async function getARepositoryPublicKey(data: GetARepositoryPublicKeyRequest) {
    return (await octokit.request('GET /repos/{owner}/{repo}/actions/secrets/public-key', data)).data
}

type CreateOrUpdateARepositorySecretRequest = {
    owner: string
    repo: string
    /**
     * 要更改的 secret
     */
    secret_name: string
    /**
     * 更改后的值
     */
    secret_value: string
}
/**
 * 创建或更新 Repository Secret
 文档:https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28#create-or-update-a-repository-secret
 */
async function createOrUpdateARepositorySecret(data: CreateOrUpdateARepositorySecretRequest) {
    const { secret_value, owner, repo, ...other } = data
    // Convert Secret & Base64 key to Uint8Array.
    const { key, key_id } = await getARepositoryPublicKey({ owner, repo }) // 获取公钥

    const binkey = sodium.from_base64(key, sodium.base64_variants.ORIGINAL)
    const binsec = sodium.from_string(secret_value)

    // Encrypt the secret using LibSodium
    const encBytes = sodium.crypto_box_seal(binsec, binkey)

    // Convert encrypted Uint8Array to Base64
    const encrypted_value = sodium.to_base64(encBytes, sodium.base64_variants.ORIGINAL) // 根据公钥计算加密后的值

    const newData = {
        ...other,
        owner,
        repo,
        encrypted_value,
        key_id,
    }

    return (await octokit.request('PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}', newData)).data
}

async function start() {
    await createOrUpdateARepositorySecret({
        owner: 'OWNER', // 你的 GitHub 用户名
        repo: 'REPO', // 仓库的名称
        secret_name: 'SECRET_NAME', // 要更改的 secret
        secret_value: 'xxxxxxxxxxxxxx', // 这里是 secret 的原始值
    })
}

start()

在更新 secret 前,需要先获取公钥,然后用公钥加密 secret,最后再 PUT

我之前搜索了一下这方面的内容,发现基本上没什么人提过这事,故特此记录一下。