LogoSkills

ci-secrets

CI/CD secret encoding, GitHub Secrets registration, and CI workflow decoding management

CI/CD Secrets Management#

Triggers#

  • When keystores, Firebase service keys, or Fastlane config files are changed
  • When adding secrets for a new environment (dev/staging/production)
  • When writing steps that use secrets in GitHub Actions workflows
  • When errors occur running make encode_keystore or make github_secrets

Overall Flow#

Local sensitive files → make encode_keystore → .envrc (base64 encoded)
                                           ↓ direnv allow
                                    Loaded as env vars
                                           ↓
                               make github_secrets
                                           ↓ gh secret set
                               GitHub Actions Secrets
                                           ↓
                               Decoded in CI workflow
                                           ↓ base64 -d / tar -xzf
                               Files restored in build environment

Actions#

1. Encoding (Local → .envrc)#

Encode sensitive files to base64 and store them as environment variables in .envrc. The make encode_keystore target performs this task.

Single File Encoding

# Keystores, JSON files, etc.
base64 -i  < file_path > 
 # Example: base64 -i android/keystore/release.keystore

Store the result in .envrc in export VAR_NAME="<base64>" format.

Directory Encoding (tar.gz → base64)

# fastlane, deploy scripts, etc. directories
tar -czf archive.tar.gz -C  < parent_dir >   --exclude= " < unnecessary_files > "   < target_dir > 
 base64 -i archive.tar.gz | tr -d  ' \n ' 
 rm -f archive.tar.gz

.envrc Update Pattern

If the value exists, replace with sed; otherwise, append with echo >>:

@if grep -q  " VAR_NAME "   .envrc; then \
    sed -i.bak  " s|^export VAR_NAME=.*|export VAR_NAME=\ " $$ENCODED\ " | "   .envrc; \
else \
    echo  " export VAR_NAME=\ " $$ENCODED\ " "   > >   .envrc; \
fi

After encoding, run direnv allow to load environment variables.

2. GitHub Secrets Registration (Env Vars → GitHub)#

make github_secrets registers environment variables loaded from .envrc to GitHub via gh secret set.

# Register a single secret
gh secret set SECRET_NAME --body  " $VALUE "   --repo  < org > / < repo > 

 # Register file contents directly (env files, etc.)
gh secret set ENV_STAGING --body  " $(cat shared/config/.env.staging) "   --repo  < org > / < repo >

Helper function pattern:

set_secret() {
  name= " $1 " ; val= " $2 " 
   if [ -n  " $val "   ]; then
    gh secret set  " $name "   --body  " $val "   --repo $REPO_PATH
    echo  " set $name " 
   else
    echo  " skip $name (empty) " 
   fi
}

3. CI Decoding (GitHub Secrets → Build Files)#

Single File: timheuer/base64-to-file Action

- name: Decode Android keystore
  uses: timheuer/base64-to-file@v1.2
  with:
    fileName: release.keystore
    fileDir: app/kobic/android/keystore/
    encodedString: $ { {   secrets.ANDROID_RELEASE_KEY_BASE64  } }

Directory: base64 Decode + tar Extract

- name: Decode fastlane directory
  env:
    FASTLANE_ANDROID_BASE64: $ { {   secrets.FASTLANE_ANDROID_BASE64  } } 
   run: |
    echo  " $FASTLANE_ANDROID_BASE64 "   | base64 -d  >   android/fastlane.tar.gz
    mkdir -p android/fastlane
    tar -xzf android/fastlane.tar.gz -C android/fastlane --strip-components=1
    rm -f android/fastlane.tar.gz

Secret Categories#

Android Keystore (2)#

Secret NameEncoding MethodSource File
ANDROID_RELEASE_KEY_BASE64 base64 (file) android/keystore/release.keystore
ANDROID_KEY_PROPERTIES_BASE64 base64 (file) android/key.properties

iOS / App Store (2)#

Secret NameEncoding MethodSource File
APPSTORE_CONNECT_API_KEY_BASE64 base64 (file) ios/fastlane/appstore_connect_api_key.json
FASTLANE_IOS_BASE64 tar.gz -> base64 ios/fastlane/ (excluding metadata, screenshots, scripts)

Firebase App Distribution (3)#

Secret NameEncoding MethodSource File
FIREBASE_DEV_APP_DISTRIBUTION_CREDENTIALS_BASE64 base64 android/keystore/development.json
FIREBASE_STG_APP_DISTRIBUTION_CREDENTIALS_BASE64 base64 android/keystore/staging.json
FIREBASE_PROD_APP_DISTRIBUTION_CREDENTIALS_BASE64 base64 android/keystore/production.json

Firebase Admin SDK (3)#

Secret NameEncoding MethodSource File
FIREBASE_DEV_CREDENTIALS_BASE64 base64 backend/.../firebase_admin/development.json
FIREBASE_STG_CREDENTIALS_BASE64 base64 backend/.../firebase_admin/staging.json
FIREBASE_PROD_CREDENTIALS_BASE64 base64 backend/.../firebase_admin/production.json

Firebase App IDs (~33)#

Combinations of environment (dev/stg/prod) x platform (android, android_console, ios, ios_console, web, macos, macos_console, windows, windows_console, console, widgetbook):

PatternExample
FIREBASE_{ENV}_{PLATFORM}_ID FIREBASE_DEV_ANDROID_ID, FIREBASE_STG_WEB_ID

Values are set directly in .envrc (not base64 encoded).

Fastlane (2)#

Secret NameEncoding MethodSource
FASTLANE_ANDROID_BASE64 tar.gz -> base64 android/fastlane/ (excluding metadata, scripts)
FASTLANE_IOS_BASE64 tar.gz -> base64 ios/fastlane/ (excluding metadata, screenshots, scripts)

AWS Deploy (1)#

Secret NameEncoding MethodSource
AWS_DEPLOY_SCRIPTS_BASE64 tar.gz -> base64 backend/.../deploy/aws/scripts/

Serverpod (1)#

Secret NameEncoding MethodSource File
SERVERPOD_PASSWORDS base64 backend/.../config/passwords.yaml

Environment Files (2)#

Secret NameEncoding MethodSource File
ENV_STAGING direct value (cat) shared/config/.env.staging
ENV_PRODUCTION direct value (cat) shared/config/.env.production

Match (iOS Signing) (3)#

Secret NameEncoding Method
MATCH_KEYCHAIN_NAMEdirect value
MATCH_KEYCHAIN_PASSWORDdirect value
MATCH_GIT_BASIC_AUTHORIZATION_BASE64direct value

Other (1)#

Secret NameEncoding Method
RELEASE_STORE_PASSWORDdirect value

Output#

  • Base64-encoded secret environment variables stored in .envrc
  • Registered in GitHub Actions Secrets
  • Decoded in CI workflows to restore build files

Prerequisites#

  • direnv installed and configured: auto-loads .envrc environment variables
  • gh (GitHub CLI) installed and authenticated: gh auth login
  • Original sensitive files must exist locally (included in .gitignore)

Adding New Secrets#

  1. Add encoding logic to the encode_keystore target in Makefile
  2. Add set_secret call to the github_secrets target in Makefile
  3. Add decoding step to CI workflow
  4. Verify original file path is included in .gitignore

Notes#

  • .envrc must be included in .gitignore and never committed
  • After running direnv allow, environment variables are loaded into the shell
  • gh secret set overwrites existing values (upsert behavior)
  • Firebase App IDs are public values but managed as secrets for consistency
  • When encoding with tar.gz, exclude unnecessary large files like metadata/screenshots with --exclude