Skip to content

🔧 DevTools Automation #5

🔧 DevTools Automation

🔧 DevTools Automation #5

# DevTools Script Automation
#
# Runs DevTools maintenance scripts automatically and on-demand.
# Scheduled: gh-prefix-labels + gh-dependabot-labels (weekly, Sunday 06:00 UTC)
# Manual: Any gh-* script via workflow_dispatch
#
# ┌─────────────────────────────────────────────────────────────────────┐
# │ Setup: DEVTOOLS_PAT │
# │ │
# │ Create a Fine-grained PAT (recommended) or Classic PAT and store │
# │ it as Org-Level secret "DEVTOOLS_PAT": │
# │ Org Settings > Secrets and variables > Actions > New secret │
# │ │
# │ ── Fine-grained PAT (Settings > Developer Settings > PATs) ── │
# │ │
# │ Resource owner: bauer-group │
# │ Repository access: All repositories │
# │ │
# │ Repository permissions: │
# │ Administration: Read and write (repo settings, topics, │
# │ branch protection) │
# │ Contents: Read and write (CODEOWNERS, templates, │
# │ branches, workflow files) │
# │ Issues: Read and write (labels for dependabot/sync) │
# │ Pull requests: Read and write (PR cleanup) │
# │ Actions: Read (usage, workflows, billing) │
# │ Environments: Read (environments audit) │
# │ Secrets: Read (secrets audit) │
# │ Webhooks: Read and write (webhook manager) │
# │ Workflows: Read and write (add workflow files) │
# │ Metadata: Read (always granted) │
# │ │
# │ Organization permissions: │
# │ Members: Read (org membership) │
# │ Administration: Read (billing info) │
# │ Self-hosted runners: Read (runner status) │
# │ │
# │ ── Classic PAT (alternative) ── │
# │ │
# │ Scopes: repo, admin:org, read:org, workflow, │
# │ read:packages, delete:packages, write:packages, │
# │ admin:repo_hook │
# └─────────────────────────────────────────────────────────────────────┘
name: 🔧 DevTools Automation
on:
schedule:
- cron: '0 6 * * 0' # Sunday 06:00 UTC
workflow_dispatch:
inputs:
script:
description: 'Script to run'
type: choice
options:
- gh-prefix-labels
- gh-dependabot-labels
- gh-labels-sync
- gh-stale-branches
- gh-codeowners-sync
- gh-branch-protection
- gh-repo-settings
- gh-template-sync
- gh-actions-usage
- gh-environments-audit
- gh-license-audit
- gh-secrets-audit
- gh-runners-selfhosted-status
- gh-topic-manager
extra-args:
description: 'Additional arguments (e.g. --repo CS-MyProject, --verbose)'
required: false
default: ''
dry-run:
description: 'Dry run (show changes without applying)'
type: boolean
default: true
permissions:
contents: read
jobs:
# ── Scheduled: runs weekly for recurring maintenance ──
scheduled:
if: github.event_name == 'schedule'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- script: gh-prefix-labels
args: '--execute'
- script: gh-dependabot-labels
args: '--execute'
name: 'scheduled: ${{ matrix.script }}'
env:
GH_TOKEN: ${{ secrets.DEVTOOLS_PAT }}
steps:
- name: Check DEVTOOLS_PAT
id: auth
env:
TOKEN: ${{ secrets.DEVTOOLS_PAT }}
run: |
if [ -z "$TOKEN" ]; then
echo "::warning::DEVTOOLS_PAT secret is not configured. Skipping script execution."
echo "::warning::See workflow file header for setup instructions (Fine-grained or Classic PAT)."
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- uses: actions/checkout@v4
if: steps.auth.outputs.skip != 'true'
- name: Install Python dependencies
if: steps.auth.outputs.skip != 'true'
run: pip install pyyaml
- name: Resolve script path
if: steps.auth.outputs.skip != 'true'
id: resolve
run: |
SCRIPT_PY="services/devtools/scripts/${{ matrix.script }}.py"
SCRIPT_SH="services/devtools/scripts/${{ matrix.script }}.sh"
if [ -f "$SCRIPT_PY" ]; then
echo "path=$SCRIPT_PY" >> "$GITHUB_OUTPUT"
echo "runner=python" >> "$GITHUB_OUTPUT"
elif [ -f "$SCRIPT_SH" ]; then
echo "path=$SCRIPT_SH" >> "$GITHUB_OUTPUT"
echo "runner=bash" >> "$GITHUB_OUTPUT"
else
echo "::error::Script not found: ${{ matrix.script }}"
exit 1
fi
- name: Run ${{ matrix.script }}
if: steps.auth.outputs.skip != 'true'
run: |
${{ steps.resolve.outputs.runner }} ${{ steps.resolve.outputs.path }} ${{ matrix.args }} 2>&1 | tee /tmp/script-output.log
- name: Job summary
if: always()
run: |
if [ "${{ steps.auth.outputs.skip }}" = "true" ]; then
echo "## ⚠️ ${{ matrix.script }} — Skipped" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "DEVTOOLS_PAT secret is not configured." >> "$GITHUB_STEP_SUMMARY"
echo "See workflow file header for setup instructions." >> "$GITHUB_STEP_SUMMARY"
else
echo "## 🔧 ${{ matrix.script }}" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "- **Trigger**: Schedule (weekly)" >> "$GITHUB_STEP_SUMMARY"
echo "- **Args**: \`${{ matrix.args }}\`" >> "$GITHUB_STEP_SUMMARY"
echo "- **Status**: ${{ job.status }}" >> "$GITHUB_STEP_SUMMARY"
if [ -f /tmp/script-output.log ]; then
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "<details><summary>Script output</summary>" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
sed 's/\x1b\[[0-9;]*m//g' /tmp/script-output.log >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
echo "</details>" >> "$GITHUB_STEP_SUMMARY"
fi
fi
# ── Manual: any script via workflow_dispatch ──
manual:
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
name: 'manual: ${{ inputs.script }}'
env:
GH_TOKEN: ${{ secrets.DEVTOOLS_PAT }}
steps:
- name: Check DEVTOOLS_PAT
id: auth
env:
TOKEN: ${{ secrets.DEVTOOLS_PAT }}
run: |
if [ -z "$TOKEN" ]; then
echo "::warning::DEVTOOLS_PAT secret is not configured. Skipping script execution."
echo "::warning::See workflow file header for setup instructions (Fine-grained or Classic PAT)."
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- uses: actions/checkout@v4
if: steps.auth.outputs.skip != 'true'
- name: Install Python dependencies
if: steps.auth.outputs.skip != 'true'
run: pip install pyyaml
- name: Resolve script path
if: steps.auth.outputs.skip != 'true'
id: resolve
run: |
SCRIPT_PY="services/devtools/scripts/${{ inputs.script }}.py"
SCRIPT_SH="services/devtools/scripts/${{ inputs.script }}.sh"
if [ -f "$SCRIPT_PY" ]; then
echo "path=$SCRIPT_PY" >> "$GITHUB_OUTPUT"
echo "runner=python" >> "$GITHUB_OUTPUT"
elif [ -f "$SCRIPT_SH" ]; then
echo "path=$SCRIPT_SH" >> "$GITHUB_OUTPUT"
echo "runner=bash" >> "$GITHUB_OUTPUT"
else
echo "::error::Script not found: ${{ inputs.script }}"
exit 1
fi
- name: Build arguments
if: steps.auth.outputs.skip != 'true'
id: args
run: |
SCRIPT="${{ inputs.script }}"
ARGS=""
# Scripts use different dry-run patterns:
# --execute pattern (default=dry-run): add --execute when dry-run is off
# --dry-run pattern (default=execute): add --dry-run when dry-run is on
# no toggle (read-only/direct): ignore dry-run setting
EXECUTE_SCRIPTS="gh-prefix-labels gh-dependabot-labels gh-stale-branches gh-codeowners-sync gh-repo-settings gh-template-sync"
DRYRUN_SCRIPTS="gh-labels-sync"
if echo "$EXECUTE_SCRIPTS" | grep -qw "$SCRIPT"; then
if [ "${{ inputs.dry-run }}" = "false" ]; then
ARGS="--execute"
fi
elif echo "$DRYRUN_SCRIPTS" | grep -qw "$SCRIPT"; then
if [ "${{ inputs.dry-run }}" = "true" ]; then
ARGS="--dry-run"
fi
fi
if [ -n "${{ inputs.extra-args }}" ]; then
ARGS="$ARGS ${{ inputs.extra-args }}"
fi
echo "value=$ARGS" >> "$GITHUB_OUTPUT"
- name: Run ${{ inputs.script }}
if: steps.auth.outputs.skip != 'true'
run: |
${{ steps.resolve.outputs.runner }} ${{ steps.resolve.outputs.path }} ${{ steps.args.outputs.value }} 2>&1 | tee /tmp/script-output.log
- name: Job summary
if: always()
run: |
if [ "${{ steps.auth.outputs.skip }}" = "true" ]; then
echo "## ⚠️ ${{ inputs.script }} — Skipped" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "DEVTOOLS_PAT secret is not configured." >> "$GITHUB_STEP_SUMMARY"
echo "See workflow file header for setup instructions." >> "$GITHUB_STEP_SUMMARY"
else
echo "## 🔧 ${{ inputs.script }}" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "- **Trigger**: Manual" >> "$GITHUB_STEP_SUMMARY"
echo "- **Dry run**: ${{ inputs.dry-run }}" >> "$GITHUB_STEP_SUMMARY"
echo "- **Args**: \`${{ steps.args.outputs.value }}\`" >> "$GITHUB_STEP_SUMMARY"
echo "- **Extra args**: \`${{ inputs.extra-args }}\`" >> "$GITHUB_STEP_SUMMARY"
echo "- **Status**: ${{ job.status }}" >> "$GITHUB_STEP_SUMMARY"
if [ -f /tmp/script-output.log ]; then
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "<details><summary>Script output</summary>" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
sed 's/\x1b\[[0-9;]*m//g' /tmp/script-output.log >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
echo "</details>" >> "$GITHUB_STEP_SUMMARY"
fi
fi