Search code examples
javascriptnode.jsgithubgithub-actions

Node/javascript script for setting GitHub actions secrets in bulk through GitHub APIs


I’m building a simple automated deploy pipeline for learning purposes. One of the steps I’d like to implement would be setting the GitHub Actions secrets in bulk.

One of the possibilities I thought about would be to create a node script (javascript) and run before deploy. Something like: set-secrets.js

Example: I’d like to start the script with something like:

// Replace these values with your own.
const REPO_OWNER = 'your-username';
const REPO_NAME = 'your-repo';
const GITHUB_TOKEN = 'your-personal-access-token';

And the data structure would be like:

// Data structure for setting the secrets.
const arrSecrets = [];
arrSecrets.push(['KEY_NAME1', 'secretValue1']);
arrSecrets.push(['KEY_NAME2', 'secretValue2']);
// I will have more values here, but I just want an example.

Then loop through the arrSecrets array and set/update the GitHub Actions secrets through GitHub API.

Would that be possible? If so, anyone has an example of how I could accomplish it? I tried already with a bash .sh but got stuck.

Constraints:

  • Node version 12 and up;
    Note: If not possible, any node version.
  • Has to work on Windows and Linux/Unix/Mac (OS agnostic);
  • I would like the script to overwrite a potential existing github actions secret key, if there is already one in place;

Solution

  • I managed to land on a satisfying solution. It sets the github actions repo secrets key/value in bulk. Very useful for project with tons of environment variables.

    const { Octokit } = require("@octokit/rest");
    const sodium = require('libsodium-wrappers');
    
    Replace these values with your own.
    const GITHUB_USER = 'your-username';
    const GITHUB_REPO_NAME = 'your-repo';
    const GITHUB_TOKEN = 'your-personal-access-token';
    
    // Secrets.
    const arrSecrets = [];
    arrSecrets.push(['KEY_NAME1', 'secretValue1']);
    arrSecrets.push(['KEY_NAME2', 'secretValue2']);
    // Add more.
    
    // Oktakit.
    const octokit = new Octokit({
      auth: GITHUB_TOKEN
    });
    
    (async () => {
      const { data: { key, key_id } } = await octokit.actions.getRepoPublicKey({
        owner: GITHUB_USER,
        repo: GITHUB_REPO_NAME
      });
    
      // Loop through the key/value arrays.
      arrSecrets.forEach(async ([secretKey, secretValue]) => {
        // Encrypt secret value.
        await sodium.ready;
    
        // Convert the secret and key to a Uint8Array.
        const binkey = sodium.from_base64(key, sodium.base64_variants.ORIGINAL);
        const binsec = sodium.from_string(secretValue);
    
        // Encrypt the secret using libsodium.
        const encBytes = sodium.crypto_box_seal(binsec, binkey);
    
        // Convert the encrypted Uint8Array to Base64.
        secretValueEncrypted = sodium.to_base64(encBytes, sodium.base64_variants.ORIGINAL);
    
        // Set secret.
        await octokit.request(`PUT /repos/${ GITHUB_USER}/${ GITHUB_REPO_NAME}/actions/secrets/${secretKey}`, {
            owner: GITHUB_USER,
            repo: GITHUB_REPO_NAME,
            secret_name: secretKey,
            encrypted_value: secretValueEncrypted,
            key_id: key_id,
            headers: {
              'X-GitHub-Api-Version': '2022-11-28'
          }
        });    
      });
    })();
    

    References:

    There’s even room to load your local file .env variables and set them all in bulk at once.