Skip to content
Open
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
4 changes: 3 additions & 1 deletion .github/workflows/dependabot-auto-merge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ name: Dependabot auto-merge
types: [completed]

permissions:
actions: write
contents: write
pull-requests: write
repository-projects: write # Required if the token needs to modify repository settings via API
Comment on lines 15 to +19

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if there are any existing workflows or docs that clarify the permission requirements
rg -n -C3 'allow_auto_merge|repository-projects' --type yaml

Repository: Framework-R-D/phlex

Length of output: 45


🏁 Script executed:

#!/bin/bash
# Check if the workflow file exists and read its actual content
if [ -f ".github/workflows/dependabot-auto-merge.yaml" ]; then
  echo "=== File exists, reading content ==="
  cat -n ".github/workflows/dependabot-auto-merge.yaml"
else
  echo "=== File not found, listing .github/workflows ==="
  ls -la .github/workflows/ 2>/dev/null || echo "No .github/workflows directory"
fi

Repository: Framework-R-D/phlex

Length of output: 6769


🌐 Web query:

GitHub API PATCH repos allow_auto_merge permission required

💡 Result:

To update the allow_auto_merge setting for a repository using the GitHub REST API PATCH /repos/{owner}/{repo} endpoint, the authenticated user must have repository write permissions [1][2][3]. Specifically, the following requirements apply: 1. Permission Level: You must have admin or maintainer access to the repository to successfully perform this update [3]. 2. Authentication: If you are using a fine-grained personal access token or a GitHub App, ensure the token has the necessary repository-level permissions. For fine-grained tokens, the write access to the repository is generally required to modify repository settings [1][2]. 3. API Usage: The PATCH request must include the allow_auto_merge parameter in the body, set to true or false [4][3]. Example request: curl -X PATCH -H "Accept: application/vnd.github+json" -H "Authorization: Bearer " https://api.github.com/repos/OWNER/REPO -d '{"allow_auto_merge":true}' [5][3] Note: This setting enables the ability to use auto-merge within the repository [6]. If you are trying to enable auto-merge on an individual pull request rather than enabling the feature for the entire repository, you must use the GraphQL API mutation enablePullRequestAutoMerge instead [5].

Citations:


🌐 Web query:

GitHub permission scope repository-projects what permissions does it grant

💡 Result:

The repository-projects permission scope grants access to interact with GitHub Projects associated with a repository [1]. Specifically, it provides read and write access to repository-level projects [1]. In contexts such as GitHub Codespaces configuration, this permission allows authorized entities to perform operations like viewing, creating, or editing projects linked to that specific repository [1]. While the exact level of access (read vs. write) depends on the specific implementation or configuration, it is generally treated as a distinct permission required to manage project boards and their items within the repository's scope [1]. It is important to note that this scope is distinct from broader project management scopes (such as the general project scope used for organization-wide or user-owned projects) [2][3]. Because repository projects are often treated as distinct resources, repository-projects ensures that automation or applications can manage these specific boards without requiring broader, higher-privilege access to all organization-level or personal projects [2][1].

Citations:


Fix the misleading comment about the permission requirement on line 19.

The comment states repository-projects: write is "Required if the token needs to modify repository settings via API," but repository-projects permission specifically governs GitHub Projects (kanban boards), not repository administration. According to GitHub's API documentation, the PATCH endpoint for allow_auto_merge requires admin/maintainer access or repository write permissions—not the Projects scope.

This permission likely works in practice because secrets.GITHUB_TOKEN in workflows has elevated permissions, but the comment's explanation is technically inaccurate and will confuse future maintainers. Either correct the comment to explain that GITHUB_TOKEN grants the necessary access despite the unusual permission name, or explicitly document why repository-projects: write is present if there's a non-obvious reason.

🧰 Tools
🪛 zizmor (1.25.2)

[error] 16-16: overly broad permissions (excessive-permissions): actions: write is overly broad at the workflow level

(excessive-permissions)


[error] 17-17: overly broad permissions (excessive-permissions): contents: write is overly broad at the workflow level

(excessive-permissions)


[error] 18-18: overly broad permissions (excessive-permissions): pull-requests: write is overly broad at the workflow level

(excessive-permissions)


[warning] 19-19: overly broad permissions (excessive-permissions): repository-projects: write is overly broad at the workflow level

(excessive-permissions)


[warning] 16-16: permissions without explanatory comments (undocumented-permissions): needs an explanatory comment

(undocumented-permissions)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/dependabot-auto-merge.yaml around lines 15 - 19, The
comment on the `repository-projects: write` permission line is technically
inaccurate. The comment claims this permission is required to modify repository
settings via API, but `repository-projects` actually governs GitHub Projects
(kanban boards) functionality, not repository administration. The PATCH endpoint
for auto_merge requires write permissions which come from GITHUB_TOKEN's
elevated privileges in workflows. Replace the misleading comment with one that
either accurately explains why `repository-projects: write` is necessary for the
dependabot auto-merge workflow (documenting any non-obvious reasons), or clarify
that GITHUB_TOKEN provides the necessary access despite the unexpected
permission scope name.


jobs:
dependabot:
Expand Down Expand Up @@ -63,7 +65,7 @@ jobs:
shell: bash
env:
PR_NUMBER: ${{ steps.pr.outputs.number }}
GH_TOKEN: ${{ secrets.WORKFLOW_PAT }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Replaced WORKFLOW_PAT with GITHUB_TOKEN
# yamllint disable rule:line-length
run: |
set -o pipefail
Expand Down
32 changes: 32 additions & 0 deletions .github/workflows/guardrail-audit-alert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Guardrail Push Alerts
on:
push:
branches: [main]

jobs:
slack-alert:
runs-on: ubuntu-latest
permissions: {} # Explicitly drops all token permissions
if: github.actor != 'dependabot[bot]'
steps:
- name: Process and Format Slack Alert
env:
SLACK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
# Safely map variables as structured environment inputs
COMMIT_MSG: ${{ github.event.head_commit.message }}
COMMITTER: ${{ github.event.head_commit.committer.name }}
REPO_NAME: ${{ github.repository }}
ACTOR: ${{ github.actor }}
run: |
# If the committer isn't 'GitHub', an admin pushed directly via local CLI.
if [ "$COMMITTER" != "GitHub" ]; then

# Use jq to format a completely safe, escaped JSON object for Slack
PAYLOAD=$(jq -n \
--arg msg "$COMMIT_MSG" \
--arg repo "$REPO_NAME" \
--arg actor "$ACTOR" \
'{"text": "⚠️ *Direct Push to Main Detected!*\n*Repo:* `\($repo)`\n*Actor:* `@\($actor)`\n*Commit:* `\($msg)`"}')
Comment on lines +25 to +29

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Line 29 exceeds yamllint's 120-char limit (127 chars).

Split the jq heredoc for readability:

-            PAYLOAD=$(jq -n \
-              --arg msg "$COMMIT_MSG" \
-              --arg repo "$REPO_NAME" \
-              --arg actor "$ACTOR" \
-              '{"text": "⚠️ *Direct Push to Main Detected!*\n*Repo:* `\($repo)`\n*Actor:* `@\($actor)`\n*Commit:* `\($msg)`"}')
+            PAYLOAD=$(jq -n \
+              --arg msg "$COMMIT_MSG" \
+              --arg repo "$REPO_NAME" \
+              --arg actor "$ACTOR" \
+              '{"text": "⚠️ *Direct Push to Main Detected!*\n*Repo:* `\($repo)`\n*Actor:* `@\($actor)`\n*Commit:* `\($msg | .[0:100])`"}')

Actually, the cleaner fix is just to break the string literal—but since jq filter strings don't easily split, you could also just disable the lint for this line with a # yamllint disable-line rule:line-length comment if you prefer keeping it as-is.

🧰 Tools
🪛 GitHub Check: yaml-check

[warning] 29-29:
29:121 [line-length] line too long (127 > 120 characters)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/guardrail-audit-alert.yaml around lines 25 - 29, The jq
command constructing the PAYLOAD variable on line 29 exceeds yamllint's
120-character limit at 127 characters. Fix this by either adding a yamllint
disable comment at the end of line 29 with `# yamllint disable-line
rule:line-length`, or alternatively, restructure the PAYLOAD assignment to break
the jq filter string across multiple lines for better readability while staying
within the character limit.

Source: Linters/SAST tools


curl -X POST -H 'Content-type: application/json' --data "$PAYLOAD" "$SLACK_URL"
fi
Comment on lines +20 to +32

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Consider adding a guard for missing SLACK_WEBHOOK_URL.

If the secret isn't configured in a new repo, this will fail silently (or curl will try to POST to an empty string). Adding a quick check would surface configuration issues faster:

 run: |
+  if [ -z "$SLACK_URL" ]; then
+    echo "::warning::SLACK_WEBHOOK_URL secret is not configured - skipping alert"
+    exit 0
+  fi
   # If the committer isn't 'GitHub', an admin pushed directly via local CLI.

Also, kudos on using jq for the payload construction—that's the right call to avoid injection issues from funky commit messages.

📝 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
run: |
# If the committer isn't 'GitHub', an admin pushed directly via local CLI.
if [ "$COMMITTER" != "GitHub" ]; then
# Use jq to format a completely safe, escaped JSON object for Slack
PAYLOAD=$(jq -n \
--arg msg "$COMMIT_MSG" \
--arg repo "$REPO_NAME" \
--arg actor "$ACTOR" \
'{"text": "⚠️ *Direct Push to Main Detected!*\n*Repo:* `\($repo)`\n*Actor:* `@\($actor)`\n*Commit:* `\($msg)`"}')
curl -X POST -H 'Content-type: application/json' --data "$PAYLOAD" "$SLACK_URL"
fi
run: |
if [ -z "$SLACK_URL" ]; then
echo "::warning::SLACK_WEBHOOK_URL secret is not configured - skipping alert"
exit 0
fi
# If the committer isn't 'GitHub', an admin pushed directly via local CLI.
if [ "$COMMITTER" != "GitHub" ]; then
# Use jq to format a completely safe, escaped JSON object for Slack
PAYLOAD=$(jq -n \
--arg msg "$COMMIT_MSG" \
--arg repo "$REPO_NAME" \
--arg actor "$ACTOR" \
'{"text": "⚠️ *Direct Push to Main Detected!*\n*Repo:* `\($repo)`\n*Actor:* `@\($actor)`\n*Commit:* `\($msg)`"}')
curl -X POST -H 'Content-type: application/json' --data "$PAYLOAD" "$SLACK_URL"
fi
🧰 Tools
🪛 GitHub Check: yaml-check

[warning] 29-29:
29:121 [line-length] line too long (127 > 120 characters)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/guardrail-audit-alert.yaml around lines 20 - 32, Add a
guard check to validate that the SLACK_URL secret is configured before
attempting to use it in the curl command. Within the existing if block that
checks if the committer is not 'GitHub', add an additional condition to verify
that SLACK_URL is not empty. If SLACK_URL is missing or empty, either exit with
an error message to surface the configuration issue or add a separate guard that
returns early, preventing the curl command from executing with an empty webhook
URL and ensuring configuration problems are caught immediately rather than
failing silently.

159 changes: 159 additions & 0 deletions scripts/release/detect-action-tiers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#!/usr/bin/env bash
set -euo pipefail

echo "=================================================="
echo "ACTION TIER CHANGE DETECTION"
echo "=================================================="

# 1. Find all action repositories
ACTION_REPOS=()
for dir in actions/*; do
[ -d "$dir" ] || continue
[ -f "$dir/action.yaml" ] || continue
ACTION_REPOS+=("${dir#actions/}")
done

# 2. Determine dependencies for each repo
declare -A DEPENDENCIES
declare -A TIERS

for repo in "${ACTION_REPOS[@]}"; do
# Extract dependencies on Framework-R-D/action-*
# Format: Framework-R-D/action-NAME@SHA # vVERSION
# We want the NAME part.
DEPS=$(grep "uses: Framework-R-D/action-" "actions/$repo/action.yaml" | sed -E 's/.*Framework-R-D\/action-([^@ ]+).*/\1/' || true)
DEPENDENCIES["$repo"]="$DEPS"
done

# 3. Iteratively assign tiers
UNASSIGNED=("${ACTION_REPOS[@]}")
CURRENT_TIER=1

while [ ${#UNASSIGNED[@]} -gt 0 ]; do
TIER_REPOS=()
STILL_UNASSIGNED=()

for repo in "${UNASSIGNED[@]}"; do
CAN_ASSIGN=true
for dep in ${DEPENDENCIES["$repo"]}; do
# If dep is an action repo and not yet assigned a tier, we can't assign this repo yet
if [[ " ${ACTION_REPOS[*]} " == *" $dep "* ]] && [ -z "${TIERS["$dep"]:-}" ]; then
CAN_ASSIGN=false
break
fi
done

if [ "$CAN_ASSIGN" = true ]; then
TIER_REPOS+=("$repo")
else
STILL_UNASSIGNED+=("$repo")
fi
done

if [ ${#TIER_REPOS[@]} -eq 0 ]; then
echo "❌ Error: Circular dependency detected among: ${STILL_UNASSIGNED[*]}"
exit 1
fi

for repo in "${TIER_REPOS[@]}"; do
TIERS["$repo"]=$CURRENT_TIER
done

UNASSIGNED=("${STILL_UNASSIGNED[@]}")
((CURRENT_TIER++))
done

# Pre-calculate latest tags and change status for all action repos to avoid redundant git calls
declare -A LATEST_TAGS
declare -A HAS_CHANGES
declare -A UNCOMMITTED

for repo in "${ACTION_REPOS[@]}"; do
(
cd "actions/$repo" || exit 1
git fetch --tags -q
TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "NONE")
echo "TAG=$TAG"
if [ "$TAG" != "NONE" ]; then
CHANGES=$(git log "${TAG}..HEAD" --oneline -- action.yaml 2>/dev/null || echo "")
if [ -n "$CHANGES" ]; then
echo "CHANGES=YES"
else
echo "CHANGES=NO"
fi
else
echo "CHANGES=NONE"
fi

UNCOMMITTED_STATUS=$(git status --porcelain 2>/dev/null || echo "")
if [ -n "$UNCOMMITTED_STATUS" ]; then
echo "UNCOMMITTED=YES"
else
echo "UNCOMMITTED=NO"
fi
) > "actions/.tmp_${repo}_status"
done

# Load the pre-calculated data
for repo in "${ACTION_REPOS[@]}"; do
source "actions/.tmp_${repo}_status"
LATEST_TAGS["$repo"]=$TAG
HAS_CHANGES["$repo"]=$CHANGES
UNCOMMITTED["$repo"]=$UNCOMMITTED
rm "actions/.tmp_${repo}_status"
done

# 4. Analyze each tier
for ((tier=1; tier < CURRENT_TIER; tier++)); do
echo "--- Tier $tier ---"

for repo in "${ACTION_REPOS[@]}"; do
if [ "${TIERS["$repo"]:-}" -eq "$tier" ]; then
TAG=${LATEST_TAGS["$repo"]}
CHANGE_STATUS=${HAS_CHANGES["$repo"]}

if [ "$TAG" = "NONE" ]; then
echo "📌 REPO: $repo (No tags found)"
elif [ "$CHANGE_STATUS" = "YES" ]; then
echo "🔥 REPO: $repo has unreleased changes since $TAG:"
( cd "actions/$repo" && git log "${TAG}..HEAD" --oneline -- action.yaml ) | sed 's/^/ - /'
else
echo "✅ REPO: $repo is clean ($TAG matches HEAD)"
fi

if [ "${UNCOMMITTED["$repo"]:-}" = "YES" ]; then
echo " ⚠️ REPO: $repo has uncommitted changes!"
fi

# Check dependencies for annotations
for dep in ${DEPENDENCIES["$repo"]}; do
if [[ " ${ACTION_REPOS[*]} " == *" $dep "* ]]; then
# 1. Check if dep has changes
DEP_CHANGE_STATUS=${HAS_CHANGES["$dep"]}
if [ "$DEP_CHANGE_STATUS" = "YES" ]; then
echo " ⚠️ Dependency $dep has unreleased changes!"
fi

# 2. Check for version mismatch
DEP_TAG=${LATEST_TAGS["$dep"]}
REF_VERSION=$(grep "uses: Framework-R-D/action-$dep" "actions/$repo/action.yaml" | sed -E 's/.*# ([^ ]+).*/\1/' | head -n 1 || true)
if [ -n "$REF_VERSION" ] && [ "$DEP_TAG" != "NONE" ]; then
if [ "$(printf '%s\n%s' "$REF_VERSION" "$DEP_TAG" | sort -V | tail -n 1)" != "$REF_VERSION" ]; then
echo " ⚠️ Dependency $dep is at version $DEP_TAG, but $repo refers to $REF_VERSION"
fi
fi
fi
done
fi
done
done

echo ""
echo "=================================================="
echo "CHANGELOGS TO UPDATE"
echo "=================================================="
for repo in "${ACTION_REPOS[@]}"; do
if [ "${HAS_CHANGES["$repo"]:-}" = "YES" ]; then
echo "actions/$repo/CHANGELOG.md"
fi
done
99 changes: 99 additions & 0 deletions scripts/release/execute-action-release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/usr/bin/env bash
set -euo pipefail

REPO_DIR="${1:-}"
if [ -z "$REPO_DIR" ] || [ ! -d "$REPO_DIR" ]; then
echo "Error: Pass a valid local repository directory name."
exit 1
fi

cd "$REPO_DIR"
echo "=================================================="
echo "EXECUTING AUTOMATED RELEASE FOR: $REPO_DIR"
echo "=================================================="

# Ensure workspace tree is clean and synchronized
git fetch upstream main -q
git checkout main -q
git pull --ff-only upstream main -q

# Ensure CHANGELOG.md is committed before tagging
if ! git diff --quiet CHANGELOG.md; then
echo "❌ ERROR: Uncommitted changes detected in CHANGELOG.md."
echo "Please commit your changelog updates before running the release script."
exit 1
fi

# Step 1: Determine target version from CHANGELOG.md
echo "-> Determining target version from CHANGELOG.md..."
TARGET_VERSION=""

# Iterate through version headers in CHANGELOG.md in order of appearance
while read -r line; do
VERSION=$(echo "$line" | sed 's/^## //; s/ --- .*//')

# A version is pending if:
# 1. The tag doesn't exist yet.
# 2. The tag exists, but the README hasn't been pinned to this version.
if ! git rev-parse "$VERSION" >/dev/null 2>&1 || ! grep -q " # ${VERSION}$" README.md; then
TARGET_VERSION="$VERSION"
break
fi
done < <(grep "^## v[0-9]*" CHANGELOG.md)

if [ -z "$TARGET_VERSION" ]; then
echo "❌ ERROR: No pending versions found in CHANGELOG.md (all versions tagged and pinned)."
exit 1
fi

NEXT_VERSION="$TARGET_VERSION"
echo "-> Target version identified: $NEXT_VERSION"

# Step 2: Build and push the Annotated Tag
if ! git rev-parse "$NEXT_VERSION" >/dev/null 2>&1; then
echo "-> Injecting annotated metadata tag..."
git tag "$NEXT_VERSION" -m "$NEXT_VERSION - automated dependency cycle release"
git push upstream "$NEXT_VERSION" -q
else
echo "-> Tag $NEXT_VERSION already exists. Skipping."
fi

# Step 3: Construct server-side Release notes wrapper
if ! gh release view "$NEXT_VERSION" >/dev/null 2>&1; then
echo "-> Publishing GitHub Release notes configuration..."
gh release create "$NEXT_VERSION" \
--title "$NEXT_VERSION - updates" \
--generate-notes
else
echo "-> GitHub Release $NEXT_VERSION already exists. Skipping."
fi

# Step 4: Update README.md usage example block automatically
echo "-> Calculating immutable SHA signatures..."
NEW_SHA=$(gh api "repos/{owner}/{repo}/git/ref/tags/${NEXT_VERSION}" --jq .object.sha)
TYPE=$(gh api "repos/{owner}/{repo}/git/ref/tags/${NEXT_VERSION}" --jq .object.type)

if [ "$TYPE" = "tag" ]; then
NEW_SHA=$(gh api "repos/{owner}/{repo}/git/tags/${NEW_SHA}" --jq .object.sha)
fi

REPO_FULL_NAME=$(gh repo view --json nameWithOwner --jq '.nameWithOwnership')

echo "-> Found Target Release SHA: $NEW_SHA"
echo "-> Modifying local README string tokens..."

OLD_PATTERN="${REPO_FULL_NAME}@[0-9a-f]\{40\} # v[0-9]*"
NEW_STRING="${REPO_FULL_NAME}@${NEW_SHA} # ${NEXT_VERSION}"

if grep -q "$NEW_STRING" README.md; then
echo "-> README already pinned to $NEXT_VERSION. Skipping commit."
else
sed -i "s|${OLD_PATTERN}|${NEW_STRING}|" README.md

# Commit the updated documentation pin back to main
git add README.md
git commit -m "docs: pin README usage example to ${NEXT_VERSION} SHA" -q
git push upstream main -q
fi

echo "🚀 SUCCESS: $NEXT_VERSION is complete and pinned!"
85 changes: 85 additions & 0 deletions scripts/release/trigger-dependabot.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/usr/bin/env bash
set -euo pipefail

echo "=================================================="
echo "Cascading Dependabot Check via Dynamic Header PR"
echo "=================================================="

if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "❌ Error: This folder is not a git repository."
exit 1
fi

if [ ! -f ".github/dependabot.yml" ]; then
echo "❌ Error: .github/dependabot.yml missing. Cannot scan."
exit 1
fi

# 1. Dynamically resolve the real remote tracking name (handles upstream vs origin)
echo "--> Resolving active tracking remote name..."
REMOTE_NAME=$(git config --get branch."$(git branch --show-current)".remote || true)
if [ -z "$REMOTE_NAME" ]; then
REMOTE_NAME=$(git config --get branch.main.remote || true)
fi
if [ -z "$REMOTE_NAME" ]; then
REMOTE_NAME=$(git remote | head -n 1)
fi
if [ -z "$REMOTE_NAME" ]; then
echo "❌ ERROR: No git remotes configured in this repository."
exit 1
fi
echo " Found Remote Tracking Target: $REMOTE_NAME"

# 2. Dynamically resolve the default branch name BEFORE checking out feature branch
# This guarantees $DEFAULT_BRANCH is bound and available for the final sync step.
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/"$REMOTE_NAME"/HEAD 2>/dev/null | sed "s|refs/remotes/$REMOTE_NAME/||" || echo "main")
echo " Found Default Branch Target: $DEFAULT_BRANCH"

# 3. Check out a temporary branch specifically for the trigger
echo "--> Creating local trigger branch..."
TRIGGER_BRANCH="automation/trigger-dependabot-$(date +%s)"
git checkout -b "$TRIGGER_BRANCH" -q
Comment on lines +38 to +41

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Local trigger branch is never cleaned up.

gh pr close --delete-branch only removes the remote branch. After the script completes, the local $TRIGGER_BRANCH remains orphaned in the user's repo. Repeated runs will accumulate stale local branches like automation/trigger-dependabot-1718649600.

Add explicit local branch cleanup after syncing to the default branch:

🧹 Proposed fix to delete the local trigger branch
 # 9. Sync your default branch cleanly to ensure local tracking matches remote tip
 echo "--> Synchronizing local default branch workspace..."
 git checkout "$DEFAULT_BRANCH" -q
 git pull --ff-only "$REMOTE_NAME" "$DEFAULT_BRANCH" -q
+git branch -D "$TRIGGER_BRANCH" -q 2>/dev/null || true

 echo "⚡ SUCCESS: Dependabot has initialized an immediate version check cycle cleanly!"

Also applies to: 78-78, 82-83

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/release/trigger-dependabot.sh` around lines 38 - 41, The local
trigger branch stored in $TRIGGER_BRANCH is created with git checkout but never
deleted after the script completes. After the script syncs back to the default
branch (following the PR operations), add an explicit git command to delete the
local trigger branch by name to prevent accumulation of stale local branches
like automation/trigger-dependabot-1718649600 on repeated script runs.


# 4. Inject a dynamic timestamp comment at the top of dependabot.yml
echo "--> Injecting fresh timestamp cache key to dependabot.yml header..."
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

# Write the new timestamp comment as line 1, then append the original file contents
{
echo "# Triggered via automation release cycle at: ${TIMESTAMP}"
cat .github/dependabot.yml
} > .github/dependabot.yml.tmp

mv .github/dependabot.yml.tmp .github/dependabot.yml

# 5. Commit bypassing pre-commit hooks and GPG code-signing requirements
git add .github/dependabot.yml
git commit \
--no-verify \
--no-gpg-sign \
-m "chore: force-trigger contemporaneous dependabot evaluation" -q

# 6. Push the feature branch to the dynamically identified remote
git push "$REMOTE_NAME" "$TRIGGER_BRANCH" -q

# 7. Open a temporary Pull Request to force GitHub's backend to process the file change
echo "--> Opening short-lived activation Pull Request..."
PR_URL=$(gh pr create \
--title "chore: dynamic version check sweep" \
--body "Automated system trigger. Safe to delete." \
--head "$TRIGGER_BRANCH")

PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')
echo " PR Opened: #$PR_NUMBER"
Comment on lines +67 to +73

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

PR number extraction could fail silently with an unhelpful error.

If gh pr create ever changes its output format or returns an error message instead of a URL, grep -oE '[0-9]+$' will produce an empty string (or wrong number), and gh pr close "" will fail cryptically. Consider validating the extracted value:

🛡️ Proposed defensive check
 PR_URL=$(gh pr create \
   --title "chore: dynamic version check sweep" \
   --body "Automated system trigger. Safe to delete." \
   --head "$TRIGGER_BRANCH")

 PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')
+if [ -z "$PR_NUMBER" ]; then
+  echo "❌ Error: Failed to extract PR number from: $PR_URL"
+  exit 1
+fi
 echo "    PR Opened: #$PR_NUMBER"
📝 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
PR_URL=$(gh pr create \
--title "chore: dynamic version check sweep" \
--body "Automated system trigger. Safe to delete." \
--head "$TRIGGER_BRANCH")
PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')
echo " PR Opened: #$PR_NUMBER"
PR_URL=$(gh pr create \
--title "chore: dynamic version check sweep" \
--body "Automated system trigger. Safe to delete." \
--head "$TRIGGER_BRANCH")
PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')
if [ -z "$PR_NUMBER" ]; then
echo "❌ Error: Failed to extract PR number from: $PR_URL"
exit 1
fi
echo " PR Opened: #$PR_NUMBER"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/release/trigger-dependabot.sh` around lines 67 - 73, The PR number
extraction using grep -oE '[0-9]+$' on the PR_URL output lacks validation and
could silently produce an empty string or incorrect value if the gh pr create
command output format changes. Add a defensive check immediately after the
PR_NUMBER assignment that validates the extracted value is not empty and
contains only digits, and exit with a clear error message if the validation
fails. This prevents downstream commands like gh pr close from failing with
cryptic errors due to an invalid or empty PR number.


# 8. Close the PR and delete the remote/local branches instantly
echo "--> Closing PR and cleaning up remote branch..."
# gh handles deleting the remote branch and checking back out into your local workspace tracking branch
gh pr close "$PR_NUMBER" --delete-branch

# 9. Sync your default branch cleanly to ensure local tracking matches remote tip
echo "--> Synchronizing local default branch workspace..."
git checkout "$DEFAULT_BRANCH" -q
git pull --ff-only "$REMOTE_NAME" "$DEFAULT_BRANCH" -q

echo "⚡ SUCCESS: Dependabot has initialized an immediate version check cycle cleanly!"
Loading
Loading