chore: more init sophistication #1
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Pipelines | ||
run-name: Unlock Gruntwork Pipelines | ||
on: | ||
workflow_call: | ||
inputs: | ||
lock_id: | ||
description: "The ID of the lock, usually a GUID. This is generally found in the console output when Terraform/OpenTofu command fails due to a timeout waiting to acquire a lock. (required if not running unlock_all)" | ||
required: false | ||
type: string | ||
working_directory: | ||
description: "Path to the terragrunt.hcl file where the lock is held (required if not running unlock_all)" | ||
required: false | ||
type: string | ||
unlock_all: | ||
description: "Forcibly reset all locks by deleting the dynamodb table" | ||
required: false | ||
type: boolean | ||
# This field can be overriden to customize the runner used for pipelines | ||
# workflows. | ||
# | ||
# IMPORTANT: To use self-hosted runners this workflow must be hosted in | ||
# the same GitHub organization as your infra-live repository. | ||
# See https://docs.github.com/en/actions/using-workflows/reusing-workflows#using-self-hosted-runners | ||
# | ||
# The value must be an escaped JSON string that will be decoded to the | ||
# jobs.runs-on field | ||
# See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on | ||
# | ||
# For example: | ||
# - A simple github runner: "\"ubuntu-22.04\"" | ||
# - A list of labels: "[\"self-hosted\", \"linux\"]" | ||
# - A map: "{group: \"ubuntu-runners\", labels: \"ubuntu-20.04-16core\"}" | ||
runner: | ||
type: string | ||
default: '"ubuntu-latest"' | ||
secrets: | ||
PIPELINES_READ_TOKEN: | ||
required: true | ||
env: | ||
PIPELINES_CLI_VERSION: v0.23.1 | ||
PIPELINES_ACTIONS_VERSION: main | ||
jobs: | ||
unlock_one: | ||
name: Unlock a single TF State | ||
if: ${{ !inputs.unlock_all }} | ||
runs-on: ${{ fromJSON(inputs.runner) }} | ||
steps: | ||
- name: Checkout Pipelines Actions | ||
uses: actions/checkout@v4 | ||
with: | ||
path: pipelines-actions | ||
repository: gruntwork-io/pipelines-actions | ||
ref: ${{ env.PIPELINES_ACTIONS_VERSION }} | ||
token: ${{ secrets.PIPELINES_READ_TOKEN }} | ||
- name: Check out repo code | ||
uses: actions/checkout@v4 | ||
with: | ||
path: infra-live-repo | ||
fetch-depth: 0 | ||
- name: Bootstrap Workflow | ||
id: gruntwork_context | ||
uses: ./pipelines-actions/.github/actions/pipelines-bootstrap | ||
with: | ||
token: ${{ secrets.PIPELINES_READ_TOKEN }} | ||
working_directory: ${{ inputs.working_directory }} | ||
terragrunt_command: "force-unlock -force ${{ inputs.lock_id }}" | ||
branch: 'main' | ||
- name: "Run terragrunt force-unlock in ${{ inputs.working_directory }}" | ||
id: terragrunt | ||
uses: ./pipelines-actions/.github/actions/pipelines-execute | ||
env: | ||
TERRAGRUNT_AUTH_PROVIDER_CMD: "pipelines auth terragrunt-credentials --ci github-actions --cloud aws --wd ." | ||
with: | ||
token: ${{ secrets.PIPELINES_READ_TOKEN }} | ||
tf_binary: ${{ steps.gruntwork_context.outputs.tf_binary }} | ||
working_directory: ${{ steps.gruntwork_context.outputs.working_directory }} | ||
terragrunt_command: ${{ steps.gruntwork_context.outputs.terragrunt_command }} | ||
infra_live_repo_branch: ${{ steps.gruntwork_context.outputs.branch }} | ||
gruntwork_config_file: ${{ steps.gruntwork_context.outputs.gruntwork_config_file }} | ||
infra_live_repo: "." | ||
infra_live_directory: "." | ||
deploy_branch_name: ${{ steps.gruntwork_context.outputs.deploy_branch_name }} | ||
unlock_all: | ||
name: Unlock all TF State files | ||
if: ${{ inputs.unlock_all }} | ||
runs-on: ${{ fromJSON(inputs.runner) }} | ||
steps: | ||
- name: Checkout Pipelines Actions | ||
uses: actions/checkout@v4 | ||
with: | ||
path: pipelines-actions | ||
repository: gruntwork-io/pipelines-actions | ||
ref: ${{ env.PIPELINES_ACTIONS_VERSION }} | ||
token: ${{ secrets.PIPELINES_READ_TOKEN }} | ||
- name: Check out repo code | ||
uses: actions/checkout@v4 | ||
with: | ||
path: infra-live-repo | ||
fetch-depth: 0 | ||
- name: Bootstrap Workflow | ||
id: gruntwork_context | ||
uses: ./pipelines-actions/.github/actions/pipelines-bootstrap | ||
with: | ||
token: ${{ secrets.PIPELINES_READ_TOKEN }} | ||
working_directory: ${{ inputs.working_directory }} | ||
terragrunt_command: "init" | ||
branch: 'main' | ||
- name: Setup Mise Toml | ||
id: mise-toml | ||
working-directory: ./infra-live-repo | ||
shell: bash | ||
env: | ||
GENERATE_MISE_TOML: false | ||
INFRA_LIVE_DIRECTORY: "." | ||
run: | | ||
if [[ "$GENERATE_MISE_TOML" == "true" ]] || [[ ! -f "$INFRA_LIVE_DIRECTORY/.mise.toml" ]]; then | ||
if [[ ! -f "$INFRA_LIVE_DIRECTORY/.mise.toml" ]]; then | ||
echo 'User does not have a `.mise.toml` file, generating one to avoid failure' | ||
fi | ||
echo 'TOML<<EOF' >> "$GITHUB_OUTPUT" | ||
echo '[tools]' >> "$GITHUB_OUTPUT" | ||
echo "$TF_BINARY = \"$TF_VERSION\"" >> "$GITHUB_OUTPUT" | ||
echo "terragrunt = \"$TG_VERSION\"" >> "$GITHUB_OUTPUT" | ||
echo 'EOF' >> "$GITHUB_OUTPUT" | ||
else | ||
echo 'TOML<<EOF' >> "$GITHUB_OUTPUT" | ||
cat $INFRA_LIVE_DIRECTORY/.mise.toml >> "$GITHUB_OUTPUT" | ||
echo '' >> "$GITHUB_OUTPUT" | ||
echo 'EOF' >> "$GITHUB_OUTPUT" | ||
fi | ||
- uses: jdx/mise-action@v2 | ||
with: | ||
install: true | ||
cache: true | ||
mise_toml: "${{ steps.mise-toml.outputs.TOML }}" | ||
- name: Wipe all dynamodb terraform lock tables | ||
shell: bash | ||
id: unlock_tables | ||
env: | ||
GITHUB_TOKEN: ${{ github.token }} | ||
GH_TOKEN: ${{ secrets.PIPELINES_READ_TOKEN }} | ||
run: | | ||
containsElement () { | ||
local e match="$1" | ||
shift | ||
for e; do [[ "$e" == "$match" ]] && return 0; done | ||
return 1 | ||
} | ||
if [[ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]]; then | ||
export GITHUB_EVENT_NAME=push | ||
fi | ||
echo "Wiping all dynamodb terraform lock tables" | ||
# Recursively find every terragrunt hcl | ||
COMPLETED_ACCOUNTS=() | ||
NEEDS_INIT_FOLDERS=() | ||
find . -name "terragrunt.hcl" -print0 | while read -d $'\0' unit | ||
do | ||
dir=$(dirname "$unit") | ||
cd $dir | ||
pipelinesConfigAuth=$(GH_TOKEN=$GITHUB_TOKEN pipelines config auth) | ||
ACCOUNTID=$(echo $pipelinesConfigAuth | jq -r ".Authentication.AWSOIDC.AccountID") | ||
echo ACCOUNTID="$ACCOUNTID" | ||
if containsElement $ACCOUNTID "${COMPLETED_ACCOUNTS[@]}"; then | ||
echo "Skipping $ACCOUNTID as it has already been processed" | ||
cd - | ||
continue | ||
else | ||
echo "Unlocking state in $ACCOUNTID in `pwd`" | ||
fi | ||
REGION=$(echo $pipelinesConfigAuth | jq -r ".Authentication.AWSOIDC.Region") | ||
echo REGION="$REGION" | ||
auth=$(GH_TOKEN=$GITHUB_TOKEN pipelines auth terragrunt-credentials --ci github-actions --cloud aws --wd .) | ||
export AWS_DEFAULT_REGION=$REGION | ||
export AWS_ACCESS_KEY_ID=$(echo $auth | jq -r '.awsCredentials.ACCESS_KEY_ID') | ||
export AWS_SECRET_ACCESS_KEY=$(echo $auth | jq -r '.awsCredentials.SECRET_ACCESS_KEY') | ||
export AWS_SESSION_TOKEN=$(echo $auth | jq -r '.awsCredentials.SESSION_TOKEN') | ||
set +x | ||
set +e | ||
echo "Fetching table status..." | ||
TABLE_STATUS=$(aws dynamodb describe-table --table-name "terraform-locks" --query "Table.TableStatus" --output text 2>&1 || true) | ||
echo $TABLE_STATUS | ||
if [[ "$TABLE_STATUS" == "ACTIVE" ]]; then | ||
aws dynamodb delete-table --table-name terraform-locks || true # Ignore failures if no table already exists | ||
fi | ||
echo "Lock removed, re-running tg init" | ||
NEEDS_INIT_FOLDERS+=($dir) | ||
COMPLETED_ACCOUNTS+=($ACCOUNTID) | ||
cd - | ||
done | ||
# Convert bash array of strings to a JSOn array | ||
JSON=$(printf "%s\n" ${NEEDS_INIT_FOLDERS[@]} | jq -R . | jq -s .) | ||
echo "unlock_folders=$JSON" >> "$GITHUB_OUTPUT" | ||
outputs: | ||
unlock_folders: ${{ steps.unlock_tables.outputs.jobs }} | ||
pipelines_reinit: | ||
name: "Rerun init in ${{ matrix.jobs.WorkingDirectory }}" | ||
needs: [unlock_all] | ||
runs-on: ${{ fromJSON(inputs.runner) }} | ||
if: ${{ inputs.unlock_all }} | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
working_directory: ${{ fromJson(needs.unlock_all.outputs.unlock_folders) }} | ||
steps: | ||
- name: Checkout Pipelines Actions | ||
uses: actions/checkout@v4 | ||
with: | ||
path: pipelines-actions | ||
repository: gruntwork-io/pipelines-actions | ||
ref: ${{ env.PIPELINES_ACTIONS_VERSION }} | ||
token: ${{ secrets.PIPELINES_READ_TOKEN }} | ||
- name: Check out repo code | ||
uses: actions/checkout@v4 | ||
with: | ||
path: infra-live-repo | ||
fetch-depth: 0 | ||
- name: Bootstrap Workflow | ||
id: gruntwork_context | ||
uses: ./pipelines-actions/.github/actions/pipelines-bootstrap | ||
with: | ||
token: ${{ secrets.PIPELINES_READ_TOKEN }} | ||
working_directory: ${{ matrix.working_directory }} | ||
terragrunt_command: "init" | ||
branch: 'main' | ||
- name: "Run Terragrunt Init" | ||
id: terragrunt | ||
uses: ./pipelines-actions/.github/actions/pipelines-execute | ||
env: | ||
TERRAGRUNT_AUTH_PROVIDER_CMD: "pipelines auth terragrunt-credentials --ci github-actions --cloud aws --wd ." | ||
with: | ||
token: ${{ secrets.PIPELINES_READ_TOKEN }} | ||
tf_binary: ${{ steps.gruntwork_context.outputs.tf_binary }} | ||
working_directory: ${{ steps.gruntwork_context.outputs.working_directory }} | ||
terragrunt_command: ${{ steps.gruntwork_context.outputs.terragrunt_command }} | ||
infra_live_repo_branch: ${{ steps.gruntwork_context.outputs.branch }} | ||
gruntwork_config_file: ${{ steps.gruntwork_context.outputs.gruntwork_config_file }} | ||
infra_live_repo: "." | ||
infra_live_directory: "." | ||
deploy_branch_name: ${{ steps.gruntwork_context.outputs.deploy_branch_name }} |