iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🔑

How to Share GitHub Secrets and Variables across Multiple Repositories

に公開

You often want to configure things like Slack Webhook URLs or S3 access keys to upload or notify CI/CD results.

However, when you own multiple repositories, managing these values becomes a hassle.

GitHub Organization paid plans feature "Organization secrets & variables," and you can solve this tedious problem by using this feature to set cross-repository secrets and variables.

While this is the standard approach, it costs money, so it's a solution individual developers might want to avoid to keep expenses down.

I have built a reasonably satisfactory workflow by creating a GitHub Action to sync Secrets and Variables, which I will introduce here.

GitHub Actions

Create a workflow like the following:

name: Sync Secrets and Variables

on:
  workflow_dispatch:

defaults:
  run:
    shell: bash

jobs:
  sync_secrets:
    runs-on: ubuntu-latest
    timeout-minutes: 1
    strategy:
      fail-fast: false
      matrix:
        include:
          - repo: repo-aaa
            secrets:
              - "R2_ACCOUNT_ID"
              - "R2_BUCKET_NAME"
              - "R2_ACCESS_KEY_ID"
              - "R2_SECRET_ACCESS_KEY"
              - "SLACK_WEBHOOK"
          - repo: repo-bbb
            secrets:
              - "R2_ACCOUNT_ID"
              - "R2_BUCKET_NAME"
              - "R2_ACCESS_KEY_ID"
              - "R2_SECRET_ACCESS_KEY"
              - "SLACK_WEBHOOK"
    steps:
      - name: Set secrets
        run: |
          for secret_name in ${{ join(matrix.secrets, ' ') }}; do
            secret_value="${!secret_name}"
            echo "Setting $secret_name for ${{ matrix.repo }}"
            echo "$secret_value" | gh secret set "$secret_name" \
              --repo="${{ github.repository_owner }}/${{ matrix.repo }}"
          done
        env:
          GH_TOKEN: ${{ secrets.TOKEN_RW_SECRETS_VARIABLES }} # needed to use gh cli to set secrets on other repos
          R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
          R2_BUCKET_NAME: ${{ secrets.R2_BUCKET_NAME }}
          R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
          R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}

  sync_vars:
    runs-on: ubuntu-latest
    timeout-minutes: 1
    strategy:
      fail-fast: false
      matrix:
        include:
          - repo: repo-aaa
            vars:
              - "R2_PUBLIC_DEV_URL"
          - repo: repo-bbb
            vars:
              - "R2_PUBLIC_DEV_URL"
    steps:
      - name: Set vars
        run: |
          for var_name in ${{ join(matrix.vars, ' ') }}; do
            var_value="${!var_name}"
            echo "Setting $var_name for ${{ matrix.repo }}"
            gh variable set "$var_name" \
              --repo="${{ github.repository_owner }}/${{ matrix.repo }}" \
              --body="$var_value"
          done
        env:
          GH_TOKEN: ${{ secrets.TOKEN_RW_SECRETS_VARIABLES }} # needed to use gh cli to set vars on other repos
          R2_PUBLIC_DEV_URL: ${{ vars.R2_PUBLIC_DEV_URL }}

Notes and Explanation

  • It is a very simple workflow.
  • It sends the Secrets & Variables from the repository running this workflow to other repositories.
  • It achieves setting Secrets & Variables in other repositories by using gh secret set and gh variable set.
  • To do this, a Personal Access Token (PAT) with appropriate permissions is required. That is TOKEN_RW_SECRETS_VARIABLES. Please create a PAT and grant it Read and Write access to actions variables and secrets permissions.
  • The env and matrix.include sections are designed to allow for detailed mapping. Please customize them according to your specific needs.
    • The description can become redundant as the number of repositories and variables increases. While there is room for further optimization, I like the current method because it is explicit and easy to understand.
    • (*Added 2025-11-03) It seems YAML anchor syntax was supported a few months ago, so you can write it more concisely using that. I tried it out and it worked well. However, it does cause a syntax error false positive in the VS Code extension...

Operation Method

Initial Setup

  • Please create and configure a PAT as described above.

Regular Operation

  1. When you have repositories or Secrets & Variables you want to share, edit this workflow.
  2. Commit and push.
  3. Run this workflow on the pushed branch.
  4. Verify the results in the target repository. There is a Last updated column in the Repository secrets list, which is useful to check.

  • Sharing secrets carries security risks, so please carefully consider which values to share and which repositories to send them to.

Discussion