Browse Source

chore: add pull request maintenance auto labelling and sizes (#5200)

* chore: add pull request maintenance auto labelling and sizes

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>

* ref: add dynamic scope handling and adjust labels for test and perf

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>

---------

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
Gergely Brautigam 7 months ago
parent
commit
11288bbaf4
3 changed files with 192 additions and 0 deletions
  1. 15 0
      .github/config/labeler.yml
  2. 22 0
      .github/pull_request_template.md
  3. 155 0
      .github/workflows/pull-request-label.yml

+ 15 - 0
.github/config/labeler.yml

@@ -0,0 +1,15 @@
+# see https://github.com/actions/labeler?tab=readme-ov-file#match-object to configure correctly
+kind/dependency:
+- any:
+  - head-branch: 'dependencies/*'
+  - head-branch: 'dependabot/*'
+  - changed-files:
+    - any-glob-to-any-file: ['go.mod', 'go.sum']
+component/github-actions:
+- any:
+  - changed-files:
+    - any-glob-to-any-file: ['.github/**']
+kind/documentation:
+- any:
+  - changed-files:
+    - any-glob-to-any-file: ['docs/**', 'examples/**']

+ 22 - 0
.github/pull_request_template.md

@@ -10,6 +10,28 @@ Fixes #...
 
 How do you like to solve the issue and why?
 
+## Format
+
+Please ensure that your PR follows the following format for the title:
+```
+feat(scope): add new feature
+fix(scope): fix bug
+docs(scope): update documentation
+chore(scope): update build tool or dependencies
+ref(scope): refactor code
+clean(scope): provider cleanup
+test(scope): add tests
+perf(scope): improve performance
+desig(scope): improve design
+```
+
+Where `scope` is _optionally_ one of:
+- charts
+- release
+- testing
+- security
+- templating
+
 ## Checklist
 
 - [ ] I have read the [contribution guidelines](https://external-secrets.io/latest/contributing/process/#submitting-a-pull-request)

+ 155 - 0
.github/workflows/pull-request-label.yml

@@ -0,0 +1,155 @@
+name: Pull Request Maintenance
+on:
+  pull_request_target:
+    # make sure that when the PR changes, we also update
+    types:
+      - opened
+      - edited
+      - synchronize
+      - reopened
+
+permissions:
+  pull-requests: write
+  issues: write
+
+jobs:
+  conventional-commit-labeler:
+    name: Label PR based on Conventional Commit Specification
+    permissions:
+      contents: read
+      pull-requests: write
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
+        env:
+          TYPE_TO_LABEL: |
+            {
+              "feat":"kind/feature",
+              "fix":"kind/bug",
+              "chore":"kind/chore",
+              "ref":"kind/refactor",
+              "clean":"kind/cleanup",
+              "design":"kind/design",
+              "docs":"kind/documentation",
+              "test":"kind/testing",
+              "perf":"kind/performance"
+            }
+        with:
+          script: |
+            console.log("Verify that the PR title follows the Conventional Commit format");
+
+            // Parse mappings from environment variables
+            const typeToLabel = JSON.parse(process.env.TYPE_TO_LABEL);
+            console.log("Type-to-Label Mapping:", typeToLabel);
+
+            // Dynamically generate allowed types
+            const allowedTypes = Object.keys(typeToLabel).join('|');
+            console.log(`Allowed Types: ${allowedTypes}`);
+
+            const prTitle = context.payload.pull_request.title;
+            console.log(`PR Title: ${prTitle}`);
+
+            // We know this regex looks scary, but it's just to match the Conventional Commit format
+            // It parses out a Title into several named regex groups, which we can use to extract various semantic patterns:
+            // - type: The type of change (feat, fix, etc.)
+            // - scope: The scope of the change (optional and set in brackets)
+            // - breaking: A flag to indicate a breaking change (!)
+            // - subject: The subject of the change
+            // Example: feat(scope)!: add new feature
+            //          ^^^^ ^^^^^ ^  ^^^^^^^^^^^^^^^
+            //          type scope    subject
+            const regex = new RegExp(
+            `^(((Initial commit)|(Merge [^\\r\\n]+(\\s)[^\\r\\n]+((\\s)((\\s)[^\\r\\n]+)+)*(\\s)?)|^((?<type>${allowedTypes})(\\((?<scope>[\\w\\-]+)\\))?(?<breaking>!?): (?<subject>[^\\r\\n]+((\\s)((\\s)[^\\r\\n]+)+)*))(\\s)?)$)`
+            );
+            console.log(`Regex: ${regex}`);
+
+            const match = prTitle.match(regex);
+            console.log(`Match: ${match != null}`);
+
+            if (match && match.groups) {
+              const { type, scope, breaking } = match.groups;
+
+              // Initialize labels array
+              const labels = [];
+
+              if (breaking) {
+                console.log("Adding breaking change label");
+                labels.push(process.env.BREAKING_CHANGE_LABEL);
+              }
+
+              // Add type-based label
+              if (type && typeToLabel[type]) {
+                labels.push(typeToLabel[type]);
+              } else {
+                console.log(`No label found for type: ${type}`);
+              }
+
+              // Add scope-based label. If no scope is provided, we don't add a label.
+              // This action will just fail if the label doesn't exist.
+              if (scope) {
+                labels.push(`area/${scope}`);
+              }
+
+              if (labels.length > 0) {
+                console.log(`Adding labels: ${labels}`);
+                await github.rest.issues.addLabels({
+                  owner: context.repo.owner,
+                  repo: context.repo.repo,
+                  issue_number: context.payload.pull_request.number,
+                  labels: labels,
+                });
+              } else {
+                console.log("No labels to add.");
+              }
+            } else {
+              console.log("Invalid PR title format. Make sure you named the PR after the specification at https://www.conventionalcommits.org/en/v1.0.0/#specification. Exiting...");
+              process.exit(1);
+            }
+  labeler:
+    name: Label PR based on Config
+    permissions:
+      contents: read
+      pull-requests: write
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+        with:
+          sparse-checkout: |
+            .github/config/labeler.yml
+      - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5
+        with:
+          configuration-path: .github/config/labeler.yml
+  size-labeler:
+    runs-on: ubuntu-latest
+    name: Label PR based on size
+    permissions:
+      issues: write
+      pull-requests: write
+    steps:
+      - uses: codelytv/pr-size-labeler@4ec67706cd878fbc1c8db0a5dcd28b6bb412e85a # v1
+        with:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          xs_label: 'size/xs'
+          xs_max_size: '10'
+          s_label: 'size/s'
+          s_max_size: '100'
+          m_label: 'size/m'
+          m_max_size: '500'
+          l_label: 'size/l'
+          l_max_size: '10000'
+          xl_label: 'size/xl'
+          fail_if_xl: 'false'
+          message_if_xl: >
+            This PR exceeds the recommended size of 10000 lines.
+            Please make sure you are NOT addressing multiple issues with one PR.
+            Note this PR might be rejected due to its size.
+  verify-labels:
+    needs: [labeler, size-labeler, conventional-commit-labeler]
+    name: verify labels
+    runs-on: ubuntu-latest
+    steps:
+      - name: PRs should have at least one qualifying label
+        uses: docker://agilepathway/pull-request-label-checker:latest@sha256:14f5f3dfda922496d07d53494e2d2b42885165f90677a1c03d600059b7706a61
+        with:
+          any_of: kind/chore,kind/bug,kind/feature,kind/dependency,kind/refactor,kind/design
+          repo_token: ${{ secrets.GITHUB_TOKEN }}