diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f387b3c6..741c4bb3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,7 +7,6 @@ updates: open-pull-requests-limit: 10 labels: - dependencies - - security - package-ecosystem: github-actions directory: "/" diff --git a/.github/labels.yml b/.github/labels.yml index fc276b5e..da01463d 100644 --- a/.github/labels.yml +++ b/.github/labels.yml @@ -410,6 +410,10 @@ aliases: - dependencies +- name: meta:dependabot-security + color: B60205 + description: "Dependabot update appears security-related and eligible for guarded automation" + - name: area:integration color: D93F0B description: "3rd-party integrations / ecosystem" diff --git a/.github/mergify.yml b/.github/mergify.yml new file mode 100644 index 00000000..05d42662 --- /dev/null +++ b/.github/mergify.yml @@ -0,0 +1,15 @@ +pull_request_rules: + - name: Auto-merge Dependabot security updates on develop + conditions: + - author=dependabot[bot] + - base=develop + - label=dependencies + - label=meta:dependabot-security + - check-success=CI / check + - check-success=lint / lint + - check-success=reviewer / coderabbit-gate + - -draft + - -conflict + actions: + merge: + method: squash diff --git a/.github/workflows/dependabot-security-label.yml b/.github/workflows/dependabot-security-label.yml new file mode 100644 index 00000000..64dec184 --- /dev/null +++ b/.github/workflows/dependabot-security-label.yml @@ -0,0 +1,75 @@ +name: dependabot-security-label + +on: + pull_request: + types: [opened, reopened, edited, synchronize] + branches: [develop] + +jobs: + label-security-updates: + if: github.event.pull_request.user.login == 'dependabot[bot]' + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + issues: write + steps: + - name: Detect security-related Dependabot updates + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const issue_number = context.payload.pull_request.number; + const labelName = 'meta:dependabot-security'; + + const title = context.payload.pull_request.title || ''; + const body = context.payload.pull_request.body || ''; + const text = `${title}\n${body}`; + + // Keep this strict to avoid matching boilerplate text present in most Dependabot PRs. + const securityPatterns = [ + /\bto fix\b/i, + /\bvulnerabilit(?:y|ies)\b/i, + /\bcve-\d{4}-\d+\b/i, + /\bghsa-[a-z0-9-]+\b/i, + /\bsecurity\s+fix\b/i, + ]; + + const isSecurityRelated = securityPatterns.some((pattern) => pattern.test(text)); + + try { + await github.rest.issues.getLabel({ owner, repo, name: labelName }); + } catch (error) { + if (error.status === 404) { + await github.rest.issues.createLabel({ + owner, + repo, + name: labelName, + color: 'B60205', + description: 'Dependabot update appears security-related and eligible for guarded automation', + }); + } else { + throw error; + } + } + + const existing = await github.rest.issues.listLabelsOnIssue({ owner, repo, issue_number }); + const hasLabel = existing.data.some((label) => label.name === labelName); + + if (isSecurityRelated && !hasLabel) { + await github.rest.issues.addLabels({ owner, repo, issue_number, labels: [labelName] }); + core.notice(`Added '${labelName}' to PR #${issue_number}.`); + return; + } + + if (!isSecurityRelated && hasLabel) { + await github.rest.issues.removeLabel({ owner, repo, issue_number, name: labelName }); + core.notice(`Removed '${labelName}' from PR #${issue_number}.`); + return; + } + + core.notice( + `No label change needed for PR #${issue_number}; security-related=${isSecurityRelated}, hasLabel=${hasLabel}.`, + ); diff --git a/CHANGELOG.md b/CHANGELOG.md index e69223d3..c554e35e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,8 @@ title: "Changelog" description: "All notable changes to this project, formatted per Keep a Changelog 1.1.0 and Semantic Versioning" file_type: "documentation" category: "Governance" -version: "1.0.2" -last_updated: "2026-05-29T05:30:00Z" +version: "1.0.3" +last_updated: "2026-05-29" owners: ["Engineering Team"] tags: ["changelog", "versioning", "releases", "governance"] status: "active" @@ -35,7 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Merged header-footer.js, badges.js, footerUtils.js, and badgeUtils.js into single ES Module - Maintains all public API functions for footer selection, insertion, removal, and badge generation - Supports configuration-driven footer phrases and badge schema mapping - - Provides unified import path for all branding utilities in meta agent workflows ([#47](https://github.com/lightspeedwp/.github/pull/PENDING)) + - Provides unified import path for all branding utilities in meta agent workflows ([#47](https://github.com/lightspeedwp/.github/issues/47)) - **Wave 3C: README and Mermaid Maintenance Workflow** — New `.github/workflows/readme-update.yml` workflow automates README and Mermaid diagram maintenance with: - Mermaid accessibility updates (adds `accTitle` and `accDescr` attributes per WCAG 2.2 AA) @@ -80,6 +80,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Added guarded Dependabot security auto-merge automation for `develop` by introducing Mergify conditions tied to Dependabot author, dependency/security labels, conflict/draft guards, and successful required checks. Added and wired a Dependabot security labelling workflow and aligned labels to canonical naming (`meta:dependabot-security`) to satisfy label governance and enable controlled auto-merge behaviour. ([#563](https://github.com/lightspeedwp/.github/pull/563)) + - **Release Agent Integration** — Updated `agents/release.agent.md` (v2.2 → v2.3) with post-release `readme-update.yml` invocation: - Documented workflow contract with inputs, outputs, and failure handling - Added to orchestration algorithm as non-blocking post-release action