Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 5 additions & 88 deletions .github/workflows/trunk-upgrade.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,93 +19,10 @@ jobs:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Create Token for MasterpointBot App
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a #v2.1.0
id: generate-token
- name: Run Trunk Upgrade
uses: masterpointio/github-action-trunk-upgrade@v0.1.0
with:
app_id: ${{ secrets.MP_BOT_APP_ID }}
private_key: ${{ secrets.MP_BOT_APP_PRIVATE_KEY }}

- name: Upgrade
id: trunk-upgrade
uses: trunk-io/trunk-action/upgrade@4d5ecc89b2691705fd08c747c78652d2fc806a94 # v1.1.19
with:
github-token: ${{ steps.generate-token.outputs.token }}
app-id: ${{ secrets.MP_BOT_APP_ID }}
app-private-key: ${{ secrets.MP_BOT_APP_PRIVATE_KEY }}
github-token: ${{ secrets.MASTERPOINT_TEAM_PAT }}
reviewers: "@masterpointio/masterpoint-internal"
prefix: "chore: "

- name: Wait for checks to pass + Merge PR
if: steps.trunk-upgrade.outputs.pull-request-number != ''
env:
GH_TOKEN: ${{ secrets.MASTERPOINT_TEAM_PAT }}
PR_NUMBER: ${{ steps.trunk-upgrade.outputs.pull-request-number }}
run: |
echo "Waiting for status checks to pass on PR #$PR_NUMBER..."

# Wait a bit for checks to start
echo "Waiting 30 seconds for checks to initialize..."
sleep 30

# Try to get all checks first to see if any exist
ALL_CHECKS_JSON=$(gh pr checks "$PR_NUMBER" --json state,bucket || echo "[]")
echo "All checks: $ALL_CHECKS_JSON"

# Get required checks
REQUIRED_CHECKS_JSON=$(gh pr checks "$PR_NUMBER" --required --json state,bucket || echo "[]")
echo "Required checks: $REQUIRED_CHECKS_JSON"

# Check if we have any required checks
REQUIRED_CHECKS_COUNT=$(echo "$REQUIRED_CHECKS_JSON" | jq '. | length')
ALL_CHECKS_COUNT=$(echo "$ALL_CHECKS_JSON" | jq '. | length')

if [ "$REQUIRED_CHECKS_COUNT" -eq 0 ] && [ "$ALL_CHECKS_COUNT" -eq 0 ]; then
echo "No status checks found. This might be expected if no checks are configured."
echo "Proceeding with auto-approval and merge..."

# Auto-approve the PR
gh pr review "$PR_NUMBER" --approve --body "Auto-approved by trunk upgrade workflow (no status checks configured)"

# Merge the PR
gh pr merge "$PR_NUMBER" --squash --delete-branch --admin
exit 0
fi

# If we have required checks, wait for them. Otherwise, wait for all checks.
if [ "$REQUIRED_CHECKS_COUNT" -gt 0 ]; then
echo "Waiting for $REQUIRED_CHECKS_COUNT required status checks..."
CHECKS_TO_MONITOR="required"
else
echo "No required checks configured. Waiting for all $ALL_CHECKS_COUNT status checks..."
CHECKS_TO_MONITOR="all"
fi

# Wait for checks to complete
while true; do
if [ "$CHECKS_TO_MONITOR" = "required" ]; then
CHECKS_JSON=$(gh pr checks "$PR_NUMBER" --required --json state,bucket)
else
CHECKS_JSON=$(gh pr checks "$PR_NUMBER" --json state,bucket)
fi

echo "Current checks status: $CHECKS_JSON"

if echo "$CHECKS_JSON" | jq -e '.[] | select(.bucket=="fail")' > /dev/null; then
echo "One or more checks have failed. Exiting..."
exit 1
fi

FAILED_OR_PENDING_CHECKS=$(echo "$CHECKS_JSON" | jq '[.[] | select(.state!="SUCCESS" or .bucket!="pass")] | length')
if [ "$FAILED_OR_PENDING_CHECKS" -eq 0 ]; then
echo "All checks passed. Auto-approving and merging PR https://github.com/${{ github.repository }}/pull/$PR_NUMBER..."

# Auto-approve the PR
gh pr review "$PR_NUMBER" --approve --body "Auto-approved by trunk upgrade workflow"

# Merge the PR
gh pr merge "$PR_NUMBER" --squash --delete-branch --admin
break
else
echo "Some checks are still running or pending. Retrying in 30s..."
sleep 30
fi
done
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
# Therefore, configs specific to their coding practices are their responsibilty to judiciously manage.
.claude/*

.tmp-template-sync/
2 changes: 2 additions & 0 deletions aqua.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ packages:
- name: TomWright/dasel@v2.4.1
- name: stedolan/jq@jq-1.7.1
tags: [cursor]
- name: cli/cli@v2.74.2
tags: [gh]
206 changes: 184 additions & 22 deletions lib/os-modules/Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ vars:
terraform-aws-tailscale \
terraform-aws-identity-center-users \
terraform-datadog-users \
terraform-github-organization \
terraform-github-teams \
terraform-googleworkspace-users-groups-automation \
terraform-postgres-config-dbs-users-roles \
Expand All @@ -15,9 +14,33 @@ vars:
terraform-spacelift-aws-integrations \
terraform-spacelift-events-collector-audit-trail \
terraform-spacelift-policies

# Configuration constants
SYNC_BRANCH: chore/sync-with-template
SHARED_TMP_DIR: .tmp-template-sync
TEMPLATE_REPO: https://github.com/masterpointio/terraform-module-template.git
TEMPLATE_OWNER: masterpointio
SYNC_MESSAGE: "chore: sync with latest template state"

# Files to synchronize
SYNC_FILES: >-
.checkov.yaml
.coderabbit.yaml
.editorconfig
.gitignore
.github
.markdownlint.yaml
.terraform-docs.yaml
.tflint.hcl
.trunk
.yamllint.yaml
LICENSE
aqua.yaml
tasks:
# ============================================================================
# File Synchronization Tasks
# ============================================================================

sync:
desc: |
Sync files from `terraform-module-template` to specified Terraform open-source module repos.
Expand All @@ -32,33 +55,24 @@ tasks:

vars:
MODULES: "{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}{{.DEFAULT_MODULES}}{{end}}"
FILES: >-
.checkov.yaml
.coderabbit.yaml
.editorconfig
.gitignore
.github
.markdownlint.yaml
.terraform-docs.yaml
.tflint.hcl
.trunk
.yamllint.yaml
LICENSE
aqua.yaml
cmds:
- |
# Convert newlines to spaces and remove backslashes
modules=$(echo "{{.MODULES}}" | tr '\n' ' ' | sed 's/\\//g')
for module in $modules
do
echo "Syncing files to ../$module ..."
for file in {{.FILES}}
for file in {{.SYNC_FILES}}
do
echo " Syncing $file"
rsync -av --delete {{.SHARED_TMP_DIR}}/$file ../$module/
done
done

# ============================================================================
# Git Branch Management Tasks
# ============================================================================

pull-and-branch:
desc: |
Pull main branch and create a sync branch for specified Terraform open-source module repos.
Expand All @@ -82,7 +96,7 @@ tasks:
echo -e "\n\n🚀 Processing ---------------- $module \n"
if [ ! -d ../$module ]; then
echo "🧲 Cloning repository..."
git clone "git@github.com:masterpointio/$module.git" ../$module
git clone "git@github.com:{{.TEMPLATE_OWNER}}/$module.git" ../$module
fi
cd ../$module

Expand All @@ -94,7 +108,15 @@ tasks:

# If branch exists and delete option is turned off - skip creation
if git branch --list "{{.SYNC_BRANCH}}" | grep -q "{{.SYNC_BRANCH}}" && [ "{{.DELETE_EXISTING_SYNC_BRANCH}}" = "false" ]; then
echo "⏭️ Branch {{.SYNC_BRANCH}} already exists, skipping creation."
echo "⏭️ Branch {{.SYNC_BRANCH}} already exists, checking it out."
git checkout {{.SYNC_BRANCH}}

# Check if local and remote branches have diverged
if git status --porcelain -b | grep -q "ahead\|behind\|diverged"; then
echo "⚠️ Local and remote branches have diverged. Resetting to remote branch..."
git fetch origin {{.SYNC_BRANCH}}
git reset --hard origin/{{.SYNC_BRANCH}}
fi
Comment on lines 110 to +119
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Divergence check risks clobbering local commits

git status --porcelain -b flags any “ahead/behind” state and then hard-resets to the remote, which will silently drop local commits if the branch is only ahead of origin.

A safer pattern is to reset only when the branch is behind or has actually diverged:

- if git status --porcelain -b | grep -q "ahead\|behind\|diverged"; then
-   echo "⚠️  Local and remote branches have diverged. Resetting to remote branch..."
-   git fetch origin {{.SYNC_BRANCH}}
-   git reset --hard origin/{{.SYNC_BRANCH}}
+ # Count commits left (behind) and right (ahead) of remote
+ if [ "$(git rev-list --left-right --count {{.SYNC_BRANCH}}...origin/{{.SYNC_BRANCH}} | awk '{print $1}')" -gt 0 ]; then
+   echo "🔄 Local branch is behind remote. Resetting hard to remote state…"
+   git fetch origin {{.SYNC_BRANCH}}
+   git reset --hard origin/{{.SYNC_BRANCH}}
+ fi

This preserves un-pushed work while still self-healing in the “behind” scenario.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In lib/os-modules/Taskfile.yaml around lines 95 to 104, the current divergence
check resets the branch hard whenever it is ahead, behind, or diverged, which
risks losing local commits. Modify the condition to reset only if the branch is
behind or diverged, excluding the ahead-only case. Adjust the git status check
to detect only "behind" or "diverged" states before performing the hard reset to
preserve un-pushed local commits.


# If branch exists and delete option is turned on - delete and create new branch
elif git branch --list "{{.SYNC_BRANCH}}" | grep -q "{{.SYNC_BRANCH}}" && [ "{{.DELETE_EXISTING_SYNC_BRANCH}}" = "true" ]; then
Expand All @@ -110,6 +132,10 @@ tasks:
cd -
done

# ============================================================================
# Commit and Push Tasks
# ============================================================================

push:
desc: |
Commit and push changes with the specified commit message.
Expand All @@ -131,25 +157,138 @@ tasks:
do
echo "🚀 Processing ../$module..."
cd ../$module
echo "📝 Committing changes..."
echo "🔄 Checking out {{.SYNC_BRANCH}} branch..."
git checkout {{.SYNC_BRANCH}}
echo "📝 Staging changes..."
git add .
git commit -m "chore: update with the latest template state"
echo "⬆️ Pushing changes..."
git push origin {{.SYNC_BRANCH}}

# Check if there are any changes to commit
if git diff --staged --quiet; then
echo "⚠️ No changes to commit in $module, skipping..."
else
echo "📝 Committing changes..."
git commit -m "{{.SYNC_MESSAGE}}"
echo "⬆️ Pushing changes..."
git push origin {{.SYNC_BRANCH}}
fi
cd -
done

# ============================================================================
# Pull Request Management Tasks
# ============================================================================

pr:
desc: |
Create pull requests for the changes pushed to SYNC_BRANCH.
Example: `task os:pr -- terraform-spacelift-automation`
summary: |
This will create pull requests from the SYNC_BRANCH to main for each of the specified
Terraform module repositories using GitHub CLI.
To create PRs for specific repositories, pass their names as arguments:
`task os:pr -- terraform-custom-module`
or for multiple modules: `task os:pr -- "terraform-custom-module terraform-another-module"`

vars:
MODULES: "{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}{{.DEFAULT_MODULES}}{{end}}"
cmds:
- |
# Get the latest commit SHA from terraform-module-template
echo "🔍 Fetching latest commit SHA from terraform-module-template..."
template_sha=$(git ls-remote {{.TEMPLATE_REPO}} HEAD | cut -f1)
template_short_sha=$(echo $template_sha | cut -c1-7)

# Try to get the latest tag from terraform-module-template
echo "🏷️ Fetching latest tag from terraform-module-template..."
template_tag=$(git ls-remote --tags --sort=-version:refname {{.TEMPLATE_REPO}} | head -n1 | sed 's/.*refs\/tags\///' | sed 's/\^{}//')

# If no tag found, use "no tags"
if [ -z "$template_tag" ]; then
template_tag="no tags"
fi

echo "📝 Template info: Tag: $template_tag, SHA: $template_short_sha"

# Convert newlines to spaces and remove backslashes
modules=$(echo "{{.MODULES}}" | tr '\n' ' ' | sed 's/\\//g')
for module in $modules
do
echo "🚀 Creating PR for ../$module..."
cd ../$module

current_branch=$(git branch --show-current)
if [ "$current_branch" != "{{.SYNC_BRANCH}}" ]; then
echo "⚠️ Warning: Not on {{.SYNC_BRANCH}} branch. Current branch: $current_branch"
echo "🔄 Checking out {{.SYNC_BRANCH}}..."
git checkout {{.SYNC_BRANCH}}
fi

Comment on lines +219 to +225
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Branch-safety check: bail out early instead of continuing on wrong branch

The current logic merely warns when the user happens to be on a different branch, then checks out {{.SYNC_BRANCH}}. If the checkout fails (e.g., branch missing), the script still proceeds and gh pr create will mis-behave. Prefer failing fast:

- git checkout {{.SYNC_BRANCH}}
+ if ! git checkout {{.SYNC_BRANCH}}; then
+   echo "❌ Failed to switch to {{.SYNC_BRANCH}} – skipping PR for $module"
+   cd -; continue
+ fi

Improves robustness across all repos.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
current_branch=$(git branch --show-current)
if [ "$current_branch" != "{{.SYNC_BRANCH}}" ]; then
echo "⚠️ Warning: Not on {{.SYNC_BRANCH}} branch. Current branch: $current_branch"
echo "🔄 Checking out {{.SYNC_BRANCH}}..."
git checkout {{.SYNC_BRANCH}}
fi
current_branch=$(git branch --show-current)
if [ "$current_branch" != "{{.SYNC_BRANCH}}" ]; then
echo "⚠️ Warning: Not on {{.SYNC_BRANCH}} branch. Current branch: $current_branch"
echo "🔄 Checking out {{.SYNC_BRANCH}}..."
if ! git checkout {{.SYNC_BRANCH}}; then
echo "❌ Failed to switch to {{.SYNC_BRANCH}} – skipping PR for $module"
cd -; continue
fi
fi
🤖 Prompt for AI Agents
In lib/os-modules/Taskfile.yaml around lines 183 to 189, the script checks out
the sync branch if the current branch differs but does not handle checkout
failures. Modify the script to immediately exit with an error if the checkout
command fails, preventing further execution on the wrong branch. This can be
done by adding a check after the git checkout command and using an exit
statement to fail fast if the checkout is unsuccessful.

# Check if there are commits ahead of main
commits_ahead=$(git rev-list --count main..{{.SYNC_BRANCH}})
if [ "$commits_ahead" -eq 0 ]; then
echo "⏭️ No commits ahead of main, skipping PR creation for $module"
cd -
continue
fi

# Ensure remote branch exists
if ! git ls-remote --heads origin {{.SYNC_BRANCH}} | grep -q {{.SYNC_BRANCH}}; then
echo "📤 Remote branch doesn't exist, pushing {{.SYNC_BRANCH}} to origin..."
git push origin {{.SYNC_BRANCH}}
fi

# Check if PR already exists
existing_pr=$(gh pr list --head {{.SYNC_BRANCH}} --base main --json number --jq '.[0].number' 2>/dev/null || echo "")
if [ -n "$existing_pr" ]; then
echo "📋 PR already exists (#$existing_pr), skipping PR creation for $module"
cd -
continue
fi

# Create PR body with template version info
echo "📋 Creating pull request..."

# Create PR body using printf to properly handle multi-line formatting
pr_body=$(printf "%s\n\n%s\n%s\n%s\n\n%s\n%s\n%s\n%s\n%s" \
"This PR syncs the repository with the latest state from [terraform-module-template]({{.TEMPLATE_REPO}})." \
"**Template Version:**" \
"- **Tag:** [$template_tag]({{.TEMPLATE_REPO}}/releases/tag/$template_tag)" \
"- **Commit SHA:** $template_short_sha" \
"**Changes include:**" \
"- Updated configuration files (.checkov.yaml, .markdownlint.yaml, etc.)" \
"- Updated GitHub workflows and templates" \
"- Updated linting and formatting configurations" \
"- Updated documentation templates")

gh pr create \
--title "{{.SYNC_MESSAGE}}" \
--body "$pr_body" \
--base main \
--head {{.SYNC_BRANCH}} \
--repo "{{.TEMPLATE_OWNER}}/$module"

cd -
done

# ============================================================================
# Template Management Tasks
# ============================================================================

setup-template:
desc: Set up the template repository in a shared temporary directory
cmds:
- task: cleanup-template
- git clone --depth 1 https://github.com/masterpointio/terraform-module-template.git {{.SHARED_TMP_DIR}}
- git clone --depth 1 {{.TEMPLATE_REPO}} {{.SHARED_TMP_DIR}}

cleanup-template:
desc: Clean up the shared temporary directory
cmds:
- rm -rf {{.SHARED_TMP_DIR}}

# ============================================================================
# Composite Workflow Tasks
# ============================================================================

sync-all:
desc: |
Pull main branch, create a sync branch, and sync with template for specified Terraform open-source module repos.
Expand All @@ -171,3 +310,26 @@ tasks:
- task: pull-and-branch
- task: sync
- task: cleanup-template

sync-and-pr:
desc: |
Complete workflow: sync with template, push changes, and create pull requests.
Example: `task os:sync-and-pr -- terraform-spacelift-automation`
summary: |
This will:
1. Pull the main branch and create a new branch named 'chore/sync-with-template'
2. Sync files from the template repository
3. Commit and push changes
4. Create pull requests for the changes
for each of the default Terraform module repositories listed in DEFAULT_MODULES.
To sync to a specific repository (or a custom list of repositories), pass their names as arguments:
`task os:sync-and-pr -- terraform-custom-module`
or for multiple modules: `task os:sync-and-pr -- "terraform-custom-module terraform-another-module"`

vars:
MODULES: "{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}{{.DEFAULT_MODULES}}{{end}}"
DELETE_EXISTING_SYNC_BRANCH: "{{.DELETE_EXISTING_SYNC_BRANCH | default false}}"
cmds:
- task: sync-all
- task: push
- task: pr