From 4cf27ba9c6200d5a61262ca563440ed1ee49a245 Mon Sep 17 00:00:00 2001 From: Seth Van Niekerk Date: Thu, 26 Mar 2026 14:10:52 -0400 Subject: [PATCH 1/9] Add push trigger for CodeQL workflow on main branch --- .github/workflows/codeql.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3b82996..67f36a9 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -4,6 +4,8 @@ name: CodeQL # which runs CodeQL as part of the plugin validation flow. on: workflow_dispatch: + push: + branches: [main] schedule: - cron: '0 6 * * 1' From 699260b1a44dff0707806014fc273e6c32eb52e1 Mon Sep 17 00:00:00 2001 From: Seth Van Niekerk Date: Thu, 26 Mar 2026 14:25:11 -0400 Subject: [PATCH 2/9] Enhance detect-changes script and workflow to assign labels to PRs --- .github/scripts/validate/detect-changes.sh | 24 +++++++-- .github/workflows/validate-plugin.yml | 58 ++++++++++++++++++++-- 2 files changed, 74 insertions(+), 8 deletions(-) diff --git a/.github/scripts/validate/detect-changes.sh b/.github/scripts/validate/detect-changes.sh index bcf2298..2e0736c 100755 --- a/.github/scripts/validate/detect-changes.sh +++ b/.github/scripts/validate/detect-changes.sh @@ -60,6 +60,8 @@ if [[ -z "$PLUGIN_LIST" ]]; then echo "close_reason=" >> "$GITHUB_OUTPUT" echo "skip_validation=false" >> "$GITHUB_OUTPUT" echo "outside_violation=true" >> "$GITHUB_OUTPUT" + echo "has_new_plugin=false" >> "$GITHUB_OUTPUT" + echo "has_updated_plugin=false" >> "$GITHUB_OUTPUT" { echo "outside_files<> "$GITHUB_OUTPUT" echo "outside_violation=false" >> "$GITHUB_OUTPUT" echo "pub_key_changed=$PUB_KEY_CHANGED" >> "$GITHUB_OUTPUT" + echo "has_new_plugin=false" >> "$GITHUB_OUTPUT" + echo "has_updated_plugin=false" >> "$GITHUB_OUTPUT" + if [[ -n "$OUTSIDE_CHANGES" ]]; then + { + echo "outside_files<> "$GITHUB_OUTPUT" + fi echo "No plugin changes detected - skipping plugin validation (author has write access)." exit 0 fi @@ -105,18 +116,22 @@ if [[ -z "$PLUGIN_LIST" ]]; then echo "close_reason=no-valid-plugins" >> "$GITHUB_OUTPUT" echo "plugin_count=0" >> "$GITHUB_OUTPUT" echo "matrix=[]" >> "$GITHUB_OUTPUT" + echo "has_new_plugin=false" >> "$GITHUB_OUTPUT" + echo "has_updated_plugin=false" >> "$GITHUB_OUTPUT" exit 0 fi PLUGIN_COUNT=$(echo "$PLUGIN_LIST" | wc -l | tr -d ' ') -# --- Check if any modified plugin is new (does not exist on base branch) --- +# --- Check if any modified plugin is new / updated --- HAS_NEW_PLUGIN=0 +HAS_UPDATED_PLUGIN=0 while IFS= read -r plugin; do [[ -z "$plugin" ]] && continue if ! git show "origin/${BASE_REF}:plugins/${plugin}/plugin.json" > /dev/null 2>&1; then HAS_NEW_PLUGIN=1 - break + else + HAS_UPDATED_PLUGIN=1 fi done <<< "$PLUGIN_LIST" @@ -166,7 +181,7 @@ if [[ "$CLOSE_PR" == "true" ]]; then else echo "close_reason=" >> "$GITHUB_OUTPUT" fi -if [[ $HAS_OUTSIDE_VIOLATION -eq 1 ]]; then +if [[ -n "$OUTSIDE_CHANGES" ]]; then { echo "outside_files<> "$GITHUB_OUTPUT" +echo "has_new_plugin=$( [[ $HAS_NEW_PLUGIN -eq 1 ]] && echo true || echo false )" >> "$GITHUB_OUTPUT" +echo "has_updated_plugin=$( [[ $HAS_UPDATED_PLUGIN -eq 1 ]] && echo true || echo false )" >> "$GITHUB_OUTPUT" + echo "Detected $PLUGIN_COUNT plugin(s): $PLUGIN_LIST" echo "close_pr=$CLOSE_PR" diff --git a/.github/workflows/validate-plugin.yml b/.github/workflows/validate-plugin.yml index 719c02c..e7391d1 100644 --- a/.github/workflows/validate-plugin.yml +++ b/.github/workflows/validate-plugin.yml @@ -58,6 +58,8 @@ jobs: outside_violation: ${{ steps.detect.outputs.outside_violation }} skip_validation: ${{ steps.detect.outputs.skip_validation }} pub_key_changed: ${{ steps.detect.outputs.pub_key_changed }} + has_new_plugin: ${{ steps.detect.outputs.has_new_plugin }} + has_updated_plugin: ${{ steps.detect.outputs.has_updated_plugin }} steps: - name: Checkout base branch scripts (trusted) uses: actions/checkout@v4 @@ -102,7 +104,53 @@ jobs: "${{ github.event.pull_request.base.ref }}" # -------------------------------------------------------------------------- - # Job 3: CodeQL security and quality analysis (runs after validate-plugin) + # Job 3: Apply PR labels based on change classification + # -------------------------------------------------------------------------- + label-pr: + needs: [detect-changes] + if: always() && needs.detect-changes.result == 'success' + runs-on: ubuntu-latest + timeout-minutes: 2 + steps: + - name: Ensure labels exist + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + run: | + gh label create "New Plugin" --color "2ea44f" --description "PR adds a new plugin" --force 2>/dev/null || true + gh label create "Plugin Update" --color "a333c8" --description "PR updates an existing plugin" --force 2>/dev/null || true + gh label create "Repo Update" --color "0ea5e9" --description "PR modifies files outside plugins/" --force 2>/dev/null || true + + - name: Apply labels + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number }} + HAS_NEW_PLUGIN: ${{ needs.detect-changes.outputs.has_new_plugin }} + HAS_UPDATED_PLUGIN: ${{ needs.detect-changes.outputs.has_updated_plugin }} + OUTSIDE_FILES: ${{ needs.detect-changes.outputs.outside_files }} + OUTSIDE_VIOLATION: ${{ needs.detect-changes.outputs.outside_violation }} + run: | + apply_label() { + local label=$1 condition=$2 + if [[ "$condition" == "true" ]]; then + gh pr edit "$PR_NUMBER" --add-label "$label" || true + else + gh pr edit "$PR_NUMBER" --remove-label "$label" 2>/dev/null || true + fi + } + apply_label "New Plugin" "$HAS_NEW_PLUGIN" + apply_label "Plugin Update" "$HAS_UPDATED_PLUGIN" + # Repo Update only when there are outside files AND it's not a violation (authorized) + if [[ -n "$OUTSIDE_FILES" && "$OUTSIDE_VIOLATION" != "true" ]]; then + IS_REPO_UPDATE=true + else + IS_REPO_UPDATE=false + fi + apply_label "Repo Update" "$IS_REPO_UPDATE" + + # -------------------------------------------------------------------------- + # Job 4: CodeQL security and quality analysis (runs after validate-plugin) # Skipped automatically if any validation shard fails, saving ~15 min. # Analysis is explicitly scoped to only the changed plugin directories. # -------------------------------------------------------------------------- @@ -326,7 +374,7 @@ jobs: run: exit 1 # -------------------------------------------------------------------------- - # Job 4: Validate each plugin in parallel via matrix + # Job 5: Validate each plugin in parallel via matrix # -------------------------------------------------------------------------- validate-plugin: needs: [detect-changes] @@ -397,7 +445,7 @@ jobs: run: exit 1 # -------------------------------------------------------------------------- - # Job 5: Aggregate all fragments, post PR comment, set final status + # Job 6: Aggregate all fragments, post PR comment, set final status # -------------------------------------------------------------------------- report: needs: [detect-changes, validate-plugin, codeql-analyze] @@ -463,7 +511,7 @@ jobs: exit 1 # -------------------------------------------------------------------------- - # Job 6: Auto-close unauthorized PRs + # Job 7: Auto-close unauthorized PRs # -------------------------------------------------------------------------- close-unauthorized: needs: detect-changes @@ -493,7 +541,7 @@ jobs: "/dev/null" # -------------------------------------------------------------------------- - # Job 7: Gate - single fixed status check for branch protection rules + # Job 8: Gate - single fixed status check for branch protection rules # Reference this job by name "Plugin PR Check" in your branch protection. # Passes only when detect-changes + CodeQL + all matrix validations + report # all succeed. Fails for any error, including unauthorized PRs. From 8ae2bb85c4110cd68705908cd395515f421c3abe Mon Sep 17 00:00:00 2001 From: Seth Van Niekerk Date: Thu, 26 Mar 2026 14:33:35 -0400 Subject: [PATCH 3/9] Remove SARIF upload step from CodeQL workflow --- .github/workflows/validate-plugin.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/validate-plugin.yml b/.github/workflows/validate-plugin.yml index e7391d1..0147fe6 100644 --- a/.github/workflows/validate-plugin.yml +++ b/.github/workflows/validate-plugin.yml @@ -203,16 +203,6 @@ jobs: upload: false continue-on-error: true - - name: Upload SARIF to GitHub (always, so resolved alerts are closed) - if: always() && hashFiles('sarif-results/**') != '' - uses: github/codeql-action/upload-sarif@v4 - with: - sarif_file: sarif-results - ref: refs/pull/${{ github.event.pull_request.number }}/merge - sha: ${{ steps.merge.outputs.sha }} - category: /language:python/pr-${{ github.event.pull_request.number }} - continue-on-error: true - - name: Set CodeQL status output id: status if: always() From 1e4bce2ddf25916f8fe6c7f1a5be4bb97ff68240 Mon Sep 17 00:00:00 2001 From: sethwv Date: Thu, 26 Mar 2026 14:34:44 -0400 Subject: [PATCH 4/9] Bump version from 2.4.1 to 2.4.2 --- plugins/dispatcharr-exporter/plugin.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/dispatcharr-exporter/plugin.json b/plugins/dispatcharr-exporter/plugin.json index 395f670..374f22a 100644 --- a/plugins/dispatcharr-exporter/plugin.json +++ b/plugins/dispatcharr-exporter/plugin.json @@ -1,6 +1,6 @@ { "name": "Dispatcharr Exporter", - "version": "2.4.1", + "version": "2.4.2", "description": "Expose Dispatcharr metrics in Prometheus exporter-compatible format for monitoring", "license": "MIT", "discord_thread": "https://discord.com/channels/1340492560220684331/1451260201775923421", @@ -11,4 +11,4 @@ "default_port": 9192, "default_host": "0.0.0.0", "auto_start_default": false -} \ No newline at end of file +} From fb4ee4c69d6e655e191625d83336624b2a1244c1 Mon Sep 17 00:00:00 2001 From: sethwv Date: Thu, 26 Mar 2026 14:35:19 -0400 Subject: [PATCH 5/9] Revert version from 2.4.2 to 2.4.1 --- plugins/dispatcharr-exporter/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dispatcharr-exporter/plugin.json b/plugins/dispatcharr-exporter/plugin.json index 374f22a..b83634e 100644 --- a/plugins/dispatcharr-exporter/plugin.json +++ b/plugins/dispatcharr-exporter/plugin.json @@ -1,6 +1,6 @@ { "name": "Dispatcharr Exporter", - "version": "2.4.2", + "version": "2.4.1", "description": "Expose Dispatcharr metrics in Prometheus exporter-compatible format for monitoring", "license": "MIT", "discord_thread": "https://discord.com/channels/1340492560220684331/1451260201775923421", From e56c126d2933167ac511404fc66dac69281a2ccf Mon Sep 17 00:00:00 2001 From: Seth Van Niekerk Date: Thu, 26 Mar 2026 14:44:46 -0400 Subject: [PATCH 6/9] Add "Invalid" label for unauthorized changes in PRs --- .github/workflows/validate-plugin.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/validate-plugin.yml b/.github/workflows/validate-plugin.yml index 0147fe6..32b2dcc 100644 --- a/.github/workflows/validate-plugin.yml +++ b/.github/workflows/validate-plugin.yml @@ -120,6 +120,7 @@ jobs: gh label create "New Plugin" --color "2ea44f" --description "PR adds a new plugin" --force 2>/dev/null || true gh label create "Plugin Update" --color "a333c8" --description "PR updates an existing plugin" --force 2>/dev/null || true gh label create "Repo Update" --color "0ea5e9" --description "PR modifies files outside plugins/" --force 2>/dev/null || true + gh label create "Invalid" --color "e4e669" --description "PR contains unauthorized changes" --force 2>/dev/null || true - name: Apply labels env: @@ -130,6 +131,7 @@ jobs: HAS_UPDATED_PLUGIN: ${{ needs.detect-changes.outputs.has_updated_plugin }} OUTSIDE_FILES: ${{ needs.detect-changes.outputs.outside_files }} OUTSIDE_VIOLATION: ${{ needs.detect-changes.outputs.outside_violation }} + CLOSE_PR: ${{ needs.detect-changes.outputs.close_pr }} run: | apply_label() { local label=$1 condition=$2 @@ -148,6 +150,13 @@ jobs: IS_REPO_UPDATE=false fi apply_label "Repo Update" "$IS_REPO_UPDATE" + # Invalid when PR has unauthorized outside changes or unauthorized plugin modifications + if [[ "$OUTSIDE_VIOLATION" == "true" || "$CLOSE_PR" == "true" ]]; then + IS_INVALID=true + else + IS_INVALID=false + fi + apply_label "Invalid" "$IS_INVALID" # -------------------------------------------------------------------------- # Job 4: CodeQL security and quality analysis (runs after validate-plugin) From ec01269f4efb8ac0c1f4ca985ed11576b319f288 Mon Sep 17 00:00:00 2001 From: Seth Van Niekerk Date: Thu, 26 Mar 2026 14:54:36 -0400 Subject: [PATCH 7/9] Fix variable reference in security severity mapping for CodeQL results --- .github/workflows/validate-plugin.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/validate-plugin.yml b/.github/workflows/validate-plugin.yml index 32b2dcc..81bf5c0 100644 --- a/.github/workflows/validate-plugin.yml +++ b/.github/workflows/validate-plugin.yml @@ -263,9 +263,9 @@ jobs: ([ (($run.tool.driver.rules // [])[] | {key: (.id // ""), value: ((.properties["security-severity"] // "0") | tostring)}), ((($run.tool.extensions // [])[].rules // [])[] | {key: (.id // ""), value: ((.properties["security-severity"] // "0") | tostring)}) ] | from_entries) as $secmap | ($run.results // [])[] | - (($r.ruleId // $r.rule.id // "") | tostring) as $rid | + ((.ruleId // .rule.id // "") | tostring) as $rid | select((($secmap[$rid] // "0") | tonumber) >= 7.0) | - " [blocking] \($rid) sec-sev=\($secmap[$rid] // \"n/a\")" ] | .[]' || true + " [blocking] \($rid) sec-sev=\($secmap[$rid] // "n/a")" ] | .[]' || true RESULT_COUNT=$((RESULT_COUNT + ${COUNT:-0})) MEDIUM_COUNT=$((MEDIUM_COUNT + ${MED:-0})) TOTAL_COUNT=$((TOTAL_COUNT + ${TOT:-0})) From 4991bc89c697f55d01235df905f52f7db2c7748a Mon Sep 17 00:00:00 2001 From: Seth Van Niekerk Date: Thu, 26 Mar 2026 15:03:52 -0400 Subject: [PATCH 8/9] Remove label creation step from PR label application job --- .github/workflows/validate-plugin.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/validate-plugin.yml b/.github/workflows/validate-plugin.yml index 81bf5c0..2de718c 100644 --- a/.github/workflows/validate-plugin.yml +++ b/.github/workflows/validate-plugin.yml @@ -112,16 +112,6 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 2 steps: - - name: Ensure labels exist - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GH_REPO: ${{ github.repository }} - run: | - gh label create "New Plugin" --color "2ea44f" --description "PR adds a new plugin" --force 2>/dev/null || true - gh label create "Plugin Update" --color "a333c8" --description "PR updates an existing plugin" --force 2>/dev/null || true - gh label create "Repo Update" --color "0ea5e9" --description "PR modifies files outside plugins/" --force 2>/dev/null || true - gh label create "Invalid" --color "e4e669" --description "PR contains unauthorized changes" --force 2>/dev/null || true - - name: Apply labels env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From b2bcb4b8437c06bbd95e049ac61ea7d109566245 Mon Sep 17 00:00:00 2001 From: Seth Van Niekerk Date: Thu, 26 Mar 2026 15:13:36 -0400 Subject: [PATCH 9/9] Fix missing newline at end of file in plugin.json --- plugins/dispatcharr-exporter/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dispatcharr-exporter/plugin.json b/plugins/dispatcharr-exporter/plugin.json index b83634e..395f670 100644 --- a/plugins/dispatcharr-exporter/plugin.json +++ b/plugins/dispatcharr-exporter/plugin.json @@ -11,4 +11,4 @@ "default_port": 9192, "default_host": "0.0.0.0", "auto_start_default": false -} +} \ No newline at end of file