Skip to content

Instantly share code, notes, and snippets.

@gustavovalverde
Last active December 26, 2024 17:56
Show Gist options
  • Save gustavovalverde/b0e566c2da331ae51116db9f74d30583 to your computer and use it in GitHub Desktop.
Save gustavovalverde/b0e566c2da331ae51116db9f74d30583 to your computer and use it in GitHub Desktop.
A bash script to handle GitHub environment variables, mainly to create new environments, and copy variables values from one environment to another
#!/bin/bash
# MIT License
# Copyright (c) 2024 Gustavo A. Valverde
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
set -e
REPO=""
log() {
echo "[INFO] $1"
}
error() {
echo "[ERROR] $1" >&2
}
# Parse .env file
parse_env_file() {
ENV_FILE=$1
VARIABLES=()
while IFS= read -r line || [ -n "$line" ]; do
[[ "$line" =~ ^#.*$ ]] && continue
[[ ! "$line" =~ ^[A-Za-z_][A-Za-z0-9_]*=.*$ ]] && continue
VARIABLES+=("$line")
done < "$ENV_FILE"
}
# Create environment if it doesn't exist
create_environment() {
ENV=$1
if gh api "repos/$REPO/environments/$ENV" --method PUT > /dev/null 2>&1; then
log "Created environment: $ENV"
else
log "Environment $ENV already exists"
fi
}
# Set variable in environment
set_variable() {
ENV=$1
KEY=$2
VALUE=$3
if gh api -X POST "repos/$REPO/environments/$ENV/variables" -f "name=$KEY" -f "value=$(printf '%q' "$VALUE")" > /dev/null 2>&1; then
log "Set variable $KEY in environment $ENV"
else
log "Failed to set variable $KEY in environment $ENV"
fi
}
# Add variables to environment
add_variables_to_environment() {
ENV=$1
for VAR in "${VARIABLES[@]}"; do
IFS='=' read -r KEY VALUE <<< "$VAR"
set_variable "$ENV" "$KEY" "$VALUE"
done
}
# Create environments with variables
create_environments() {
ENVIRONMENTS=("${@:2}")
for ENV in "${ENVIRONMENTS[@]}"; do
create_environment "$ENV"
add_variables_to_environment "$ENV"
done
}
# Fetch variables from the source environment using GitHub API
fetch_variables_from_source() {
SOURCE_ENV=$1
VARIABLES=$(gh api "repos/$REPO/environments/$SOURCE_ENV/variables" --jq '.variables | map({key: .name, value: .value})')
}
# Fetch variables from a target environment using GitHub API
fetch_variables_from_target() {
TARGET_ENV=$1
EXISTING_VARIABLES=$(gh api "repos/$REPO/environments/$TARGET_ENV/variables" --jq '[.variables[] | {(.name): true}] | add')
}
# Check if a variable exists in the target environment
variable_exists_in_target() {
KEY=$1
echo "$EXISTING_VARIABLES" | jq -e "has(\"$KEY\")" > /dev/null 2>&1
}
# Transfer variables from one environment to others
transfer_variables() {
SOURCE_ENV=$1
TARGET_ENVS=("${@:2}")
fetch_variables_from_source "$SOURCE_ENV"
for TARGET_ENV in "${TARGET_ENVS[@]}"; do
create_environment "$TARGET_ENV"
fetch_variables_from_target "$TARGET_ENV"
echo "$VARIABLES" | jq -c '.[]' | while IFS= read -r var; do
KEY=$(echo "$var" | jq -r '.key')
VALUE=$(echo "$var" | jq -r '.value')
if variable_exists_in_target "$KEY"; then
if [[ "$OVERRIDE" = true ]]; then
log "Overriding variable $KEY in environment $TARGET_ENV"
set_variable "$TARGET_ENV" "$KEY" "$VALUE"
else
log "Variable $KEY already exists in environment $TARGET_ENV and will not be overwritten"
fi
else
log "Setting new variable $KEY in environment $TARGET_ENV"
set_variable "$TARGET_ENV" "$KEY" "$VALUE"
fi
done
done
}
# Override variables in environments
override_variables() {
ENVIRONMENTS=("${@:2}")
for ENV in "${ENVIRONMENTS[@]}"; do
create_environment "$ENV"
for VAR in "${VARIABLES[@]}"; do
IFS='=' read -r KEY VALUE <<< "$VAR"
set_variable "$ENV" "$KEY" "$VALUE"
done
done
}
# Main script
ENV_FILE=""
CREATE_ENVIRONMENTS=()
COPY_ENV=""
TARGET_ENVIRONMENTS=()
OVERRIDE=false
while [[ "$#" -gt 0 ]]; do
case $1 in
--repo)
REPO="$2"
shift 2
;;
--env)
ENV_FILE="$2"
shift 2
;;
--create)
IFS=',' read -r -a CREATE_ENVIRONMENTS <<< "$2"
shift 2
;;
--copy)
COPY_ENV="$2"
IFS=',' read -r -a TARGET_ENVIRONMENTS <<< "$3"
shift 3
;;
--override)
OVERRIDE=true
shift
;;
*)
error "Unknown parameter passed: $1"
exit 1
;;
esac
done
if [[ -z "$REPO" ]]; then
error "--repo is required"
exit 1
fi
if [[ -z "$ENV_FILE" && ${#CREATE_ENVIRONMENTS[@]} -ne 0 ]]; then
error "--env file is required for creating environments"
exit 1
fi
if [[ -n "$ENV_FILE" && ! -f "$ENV_FILE" ]]; then
error "The specified env file does not exist"
exit 1
fi
if [[ -n "$ENV_FILE" ]]; then
parse_env_file "$ENV_FILE"
fi
if [[ ${#CREATE_ENVIRONMENTS[@]} -ne 0 ]]; then
log "Creating environments: ${CREATE_ENVIRONMENTS[*]}"
create_environments "${CREATE_ENVIRONMENTS[@]}"
fi
if [[ -n "$COPY_ENV" && ${#TARGET_ENVIRONMENTS[@]} -ne 0 ]]; then
log "Copying variables from $COPY_ENV to ${TARGET_ENVIRONMENTS[*]}"
transfer_variables "$COPY_ENV" "${TARGET_ENVIRONMENTS[@]}"
fi
if [[ "$OVERRIDE" = true && ${#TARGET_ENVIRONMENTS[@]} -ne 0 ]]; then
log "Overriding variables in environments: ${TARGET_ENVIRONMENTS[*]}"
override_variables "${TARGET_ENVIRONMENTS[@]}"
fi
@gustavovalverde
Copy link
Author

gustavovalverde commented Jul 15, 2024

GitHub Environments Management Script

Introduction

This script allows you to manage GitHub repository environments programmatically using the GitHub CLI (gh). It provides functionality to create environments, set environment variables, and copy variables between environments. The script is designed to handle special characters in variable values and provides options to override existing variables.

Features

  • Create Environments: Create multiple environments and set variables from a .env file.
  • Copy Variables: Copy variables from one environment to others.
  • Override Variables: Override existing variables in target environments.

Prerequisites

  • Install the GitHub CLI (gh): GitHub CLI Installation
  • Ensure you have access to the GitHub repository and have the necessary permissions to manage environments.

Usage

Command-Line Options

  • --repo <owner/repo>: Specifies the GitHub repository in the format owner/repo.
  • --env <file>: Specifies the path to a .env file containing variables to be set.
  • --create <environments>: Comma-separated list of environments to create and set variables in.
  • --copy <source_env> <target_envs>: Copies variables from the source environment to the target environments (comma-separated).
  • --override: Overrides existing variables in the target environments.

Examples

1. Create Environments and Set Variables

To create environments (dev, test, prod) and set variables from a .env file:

./gh-environment-manager.sh --repo owner/repo --env .env --create "dev,test,prod"

2. Copy Variables Between Environments

To copy variables from the dev environment to the test and prod environments:

./gh-environment-manager.sh --repo owner/repo --copy dev test,prod

3. Override Existing Variables

To override existing variables in the test and prod environments using variables from a .env file:

./gh-environment-manager.sh --repo owner/repo --env .env --override --create "test,prod"

@excalq
Copy link

excalq commented Sep 5, 2024

The gh CLI now pretty-prints JSON when using --jq, breaking your script. To fix it, add this near the top:

export NO_COLOR=true
export GH_FORCE_TTY=false

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment