diff --git a/.github/workflows/example.yml b/.github/workflows/example.yml index 15e69a4..fdb5d0e 100644 --- a/.github/workflows/example.yml +++ b/.github/workflows/example.yml @@ -26,11 +26,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Issue & PR Automation - uses: SillyLittleTech/AutomationSuite@v1 + uses: SillyLittleTech/AutomationSuite@v2.1.5 with: github-token: ${{ secrets.GITHUB_TOKEN }} project-name: ${{ env.PROJECT_NAME }} enable-project-automation: "true" enable-label-sync: "true" - enable-zap-labeling: "true" - zap-labels: "Meta,Stylistic,javascript,meta:seq,ZAP!" + enable-auto-labeling: "true" + auto-label-rules: '[{"pattern": "ZAP Scan Baseline Report", "labels": ["Meta", "Stylistic", "javascript", "meta:seq", "ZAP!"]}]' diff --git a/CHANGELOG.md b/CHANGELOG.md index 88aaf03..6472cd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.1.5] - 2025-05-XX + +### Changed +- Improved issue matching regex to accurately detect closes/fixes and bare GitHub links. +- Generalized the ZAP auto-labeling feature into a more robust `Auto-labeling Issues and PRs` feature using JSON configurations. +- Action inputs changed: `enable-zap-labeling` -> `enable-auto-labeling`, `zap-labels` -> `auto-label-rules`. + ## [1.0.0] - 2025-01-XX ### Added @@ -27,5 +34,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Automatic issue linking detection (multiple patterns supported) - Modular feature enablement (can enable/disable individual features) -[Unreleased]: https://github.com/SillyLittleTech/AutomationSuite/compare/v1.0.0...HEAD +[Unreleased]: https://github.com/SillyLittleTech/AutomationSuite/compare/v2.1.5...HEAD +[2.1.5]: https://github.com/SillyLittleTech/AutomationSuite/compare/v1.0.0...v2.1.5 [1.0.0]: https://github.com/SillyLittleTech/AutomationSuite/releases/tag/v1.0.0 diff --git a/README.md b/README.md index c12461d..db6291b 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ A comprehensive GitHub Action that automates issue and pull request workflows in ## Quick Start ```yaml -- uses: SillyLittleTech/AutomationSuite@v1 +- uses: SillyLittleTech/AutomationSuite@v2.1.5 with: github-token: ${{ secrets.GITHUB_TOKEN }} ``` @@ -21,8 +21,8 @@ A comprehensive GitHub Action that automates issue and pull request workflows in We recommend pinning to a specific version for stability: -- `@v1` - Latest v1.x.x release (recommended for most users) -- `@v1.0.0` - Specific release version (maximum stability) +- `@v2` - Latest v2.x.x release (recommended for most users) +- `@v2.1.5` - Specific release version (maximum stability) - `@main` - Latest development version (not recommended for production) ## Features @@ -79,7 +79,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Run Automation Suite - uses: SillyLittleTech/AutomationSuite@v1 + uses: SillyLittleTech/AutomationSuite@v2.1.5 with: github-token: ${{ secrets.GITHUB_TOKEN }} project-name: "My Project" @@ -91,14 +91,14 @@ Customize the behavior with input parameters: ```yaml - name: Run Automation Suite - uses: SillyLittleTech/AutomationSuite@v1 + uses: SillyLittleTech/AutomationSuite@v2.1.5 with: github-token: ${{ secrets.GITHUB_TOKEN }} project-name: "Portfolio Devmt" enable-project-automation: "true" enable-label-sync: "true" - enable-zap-labeling: "true" - zap-labels: "security,automated,zap-scan,needs-review" + enable-auto-labeling: "true" + auto-label-rules: '[{"pattern": "ZAP Scan Baseline Report", "labels": ["security", "automated", "zap-scan", "needs-review"]}]' ``` ### Selective Features @@ -109,38 +109,38 @@ Enable only specific features: ```yaml - name: Project Board Automation - uses: SillyLittleTech/AutomationSuite@v1 + uses: SillyLittleTech/AutomationSuite@v2.1.5 with: github-token: ${{ secrets.GITHUB_TOKEN }} project-name: "Sprint Board" enable-project-automation: "true" enable-label-sync: "false" - enable-zap-labeling: "false" + enable-auto-labeling: "false" ``` #### Label & Milestone Sync Only ```yaml - name: Label & Milestone Sync - uses: SillyLittleTech/AutomationSuite@v1 + uses: SillyLittleTech/AutomationSuite@v2.1.5 with: github-token: ${{ secrets.GITHUB_TOKEN }} enable-project-automation: "false" enable-label-sync: "true" - enable-zap-labeling: "false" + enable-auto-labeling: "false" ``` -#### ZAP Auto-labeling Only +#### Auto-labeling Only ```yaml -- name: ZAP Security Issue Labeling - uses: SillyLittleTech/AutomationSuite@v1 +- name: Auto-labeling Issues and PRs + uses: SillyLittleTech/AutomationSuite@v2.1.5 with: github-token: ${{ secrets.GITHUB_TOKEN }} enable-project-automation: "false" enable-label-sync: "false" - enable-zap-labeling: "true" - zap-labels: "security,zap,needs-triage" + enable-auto-labeling: "true" + auto-label-rules: '[{"pattern": "bug", "labels": ["bug", "triage"]}]' ``` ## Input Parameters @@ -151,8 +151,8 @@ Enable only specific features: | `project-name` | Name of GitHub Projects V2 project | No | `Portfolio Devmt` | | `enable-project-automation` | Enable project board automation | No | `true` | | `enable-label-sync` | Enable label/milestone sync | No | `true` | -| `enable-zap-labeling` | Enable ZAP issue auto-labeling | No | `true` | -| `zap-labels` | Comma-separated labels for ZAP issues | No | `Meta,Stylistic,javascript,meta:seq,ZAP!` | +| `enable-auto-labeling` | Enable auto-labeling based on text matching | No | `true` | +| `auto-label-rules` | JSON array configuring pattern matching and corresponding labels to apply | No | `[{"pattern": "ZAP Scan Baseline Report", "labels": ["Meta", "Stylistic", "javascript", "meta:seq", "ZAP!"]}]` | ## How It Works @@ -206,11 +206,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Run Full Automation - uses: SillyLittleTech/AutomationSuite@v1 + uses: SillyLittleTech/AutomationSuite@v2.1.5 with: github-token: ${{ secrets.GITHUB_TOKEN }} project-name: "Development Board" - zap-labels: "security,scan,automated,needs-review" + auto-label-rules: '[{"pattern": "ZAP Scan Baseline Report", "labels": ["security", "scan", "automated", "needs-review"]}]' ``` ### Example 2: Multiple Jobs for Different Features @@ -236,34 +236,34 @@ jobs: runs-on: ubuntu-latest if: github.event_name == 'issues' || github.event_name == 'pull_request' steps: - - uses: SillyLittleTech/AutomationSuite@v1 + - uses: SillyLittleTech/AutomationSuite@v2.1.5 with: github-token: ${{ secrets.GITHUB_TOKEN }} project-name: "Sprint Planning" enable-label-sync: "false" - enable-zap-labeling: "false" + enable-auto-labeling: "false" label-sync: name: Label & Milestone Sync runs-on: ubuntu-latest steps: - - uses: SillyLittleTech/AutomationSuite@v1 + - uses: SillyLittleTech/AutomationSuite@v2.1.5 with: github-token: ${{ secrets.GITHUB_TOKEN }} enable-project-automation: "false" - enable-zap-labeling: "false" + enable-auto-labeling: "false" security-labeling: name: Security Scan Labeling runs-on: ubuntu-latest if: github.event_name == 'issues' steps: - - uses: SillyLittleTech/AutomationSuite@v1 + - uses: SillyLittleTech/AutomationSuite@v2.1.5 with: github-token: ${{ secrets.GITHUB_TOKEN }} enable-project-automation: "false" enable-label-sync: "false" - zap-labels: "security,vulnerability,zap-baseline" + auto-label-rules: '[{"pattern": "ZAP Scan Baseline Report", "labels": ["security", "vulnerability", "zap-baseline"]}]' ``` ### Example 3: Security-Focused Setup @@ -283,13 +283,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Auto-label Security Scans - uses: SillyLittleTech/AutomationSuite@v1 + uses: SillyLittleTech/AutomationSuite@v2.1.5 with: github-token: ${{ secrets.GITHUB_TOKEN }} enable-project-automation: "false" enable-label-sync: "false" - enable-zap-labeling: "true" - zap-labels: "security,automated-scan,needs-triage,zap-report" + enable-auto-labeling: "true" + auto-label-rules: '[{"pattern": "ZAP Scan Baseline Report", "labels": ["security", "automated-scan", "needs-triage", "zap-report"]}]' ``` ## Prerequisites @@ -332,11 +332,12 @@ If you see "Project not found" errors: - Check that the PR body or title contains valid issue references - Verify labels exist in the repository -### ZAP issues not being labeled +### Issues or PRs not being auto-labeled -- Confirm the issue title contains "ZAP Scan Baseline Report" (case-insensitive) -- Verify labels specified in `zap-labels` exist in your repository -- Check workflow permissions include `issues: write` +- Confirm the issue/PR title or body contains the exact text pattern specified in your JSON config (case-insensitive). +- Verify labels specified in your `auto-label-rules` JSON exist in your repository. +- Ensure `auto-label-rules` is valid JSON and passed correctly. +- Check workflow permissions include `issues: write` and `pull-requests: write`. ## Contributing diff --git a/action.yml b/action.yml index 45d0696..d0f92e0 100644 --- a/action.yml +++ b/action.yml @@ -23,14 +23,14 @@ inputs: description: 'Enable label and milestone synchronization between issues and PRs' required: false default: 'true' - enable-zap-labeling: - description: 'Enable automatic labeling of ZAP security scan issues' + enable-auto-labeling: + description: 'Enable automatic labeling of issues and PRs based on text matching' required: false default: 'true' - zap-labels: - description: 'Comma-separated list of labels to apply to ZAP scan issues' + auto-label-rules: + description: 'JSON array of objects with "pattern" and "labels" keys. Example: [{"pattern": "ZAP Scan Baseline Report", "labels": ["Meta","Stylistic","javascript","meta:seq","ZAP!"]}]' required: false - default: 'Meta,Stylistic,javascript,meta:seq,ZAP!' + default: '[{"pattern": "ZAP Scan Baseline Report", "labels": ["Meta", "Stylistic", "javascript", "meta:seq", "ZAP!"]}]' runs: using: 'composite' @@ -172,12 +172,13 @@ runs: // Extract issue numbers from PR body and title const prText = `${pr.title} ${pr.body || ''}`; - const issuePattern = /#(\d+)|(?:close[ds]?|fix(?:e[ds]?)?|resolve[ds]?)\s+#(\d+)|(?:https?:\/\/)?(?:www\.)?github\.com\/[^\/]+\/[^\/]+\/issues\/(\d+)/gi; + // Improved issue pattern matching + const issuePattern = /(?:(?:close[sd]?|fix(?:e[sd]?)?|resolve[sd]?)\s+(?:#(\d+)|https?:\/\/github\.com\/[^\/]+\/[^\/]+\/issues\/(\d+)))|(?:#(\d+))|(?:https?:\/\/github\.com\/[^\/]+\/[^\/]+\/issues\/(\d+))/gi; const issueNumbers = new Set(); let match; while ((match = issuePattern.exec(prText)) !== null) { - const issueNum = match[1] || match[2] || match[3]; + const issueNum = match[1] || match[2] || match[3] || match[4]; if (issueNum) { issueNumbers.add(parseInt(issueNum)); } @@ -343,12 +344,13 @@ runs: // Helper function to extract linked issue numbers from text function extractIssueNumbers(text) { - const issuePattern = /#(\d+)|(?:close[ds]?|fix(?:e[ds]?)?|resolve[ds]?)\s+#(\d+)|(?:https?:\/\/)?(?:www\.)?github\.com\/[^\/]+\/[^\/]+\/issues\/(\d+)/gi; + // Improved issue pattern matching + const issuePattern = /(?:(?:close[sd]?|fix(?:e[sd]?)?|resolve[sd]?)\s+(?:#(\d+)|https?:\/\/github\.com\/[^\/]+\/[^\/]+\/issues\/(\d+)))|(?:#(\d+))|(?:https?:\/\/github\.com\/[^\/]+\/[^\/]+\/issues\/(\d+))/gi; const issueNumbers = new Set(); let match; while ((match = issuePattern.exec(text)) !== null) { - const issueNum = match[1] || match[2] || match[3]; + const issueNum = match[1] || match[2] || match[3] || match[4]; if (issueNum) { issueNumbers.add(parseInt(issueNum)); } @@ -535,53 +537,72 @@ runs: console.log('🎉 Label & milestone sync completed') - - name: Auto-label ZAP Security Scan Issues - if: ${{ inputs.enable-zap-labeling == 'true' && github.event_name == 'issues' && (github.event.action == 'opened' || github.event.action == 'edited') }} + - name: Auto-label Issues and PRs + if: ${{ inputs.enable-auto-labeling == 'true' && (github.event_name == 'issues' || github.event_name == 'pull_request') && (github.event.action == 'opened' || github.event.action == 'edited') }} uses: actions/github-script@v7 with: github-token: ${{ inputs.github-token }} script: | const { owner, repo } = context.repo; - const issue = context.payload.issue; - const issueNumber = issue.number; - const issueTitle = issue.title || ''; + const isIssue = context.eventName === 'issues'; + const item = isIssue ? context.payload.issue : context.payload.pull_request; + const itemNumber = item.number; + const itemTitle = item.title || ''; + const itemBody = item.body || ''; + const textToMatch = `${itemTitle} ${itemBody}`.toLowerCase(); - console.log(`Checking issue #${issueNumber}: "${issueTitle}"`); + console.log(`Checking ${isIssue ? 'issue' : 'PR'} #${itemNumber}: "${itemTitle}"`); - // Configurable pattern for ZAP Scan Baseline Report (case-insensitive) - const zapPattern = 'ZAP Scan Baseline Report'; - if (issueTitle.toLowerCase().includes(zapPattern.toLowerCase())) { - console.log('✅ Issue title matches ZAP Scan Baseline Report pattern'); - - // Parse labels from input - const labelsToApply = '${{ inputs.zap-labels }}'.split(',').map(l => l.trim()).filter(Boolean); - - // Get current labels on the issue - const currentLabels = issue.labels.map(label => - typeof label === 'object' ? label.name : label - ).filter(Boolean); - - console.log('Current labels:', currentLabels); - - // Combine current labels with new labels (avoiding duplicates) - const allLabels = [...new Set([...currentLabels, ...labelsToApply])]; + let rules = []; + try { + rules = JSON.parse('${{ inputs.auto-label-rules }}'); + if (!Array.isArray(rules)) { + console.error('❌ auto-label-rules must be a JSON array'); + return; + } + } catch (e) { + console.error('❌ Failed to parse auto-label-rules JSON:', e.message); + return; + } + + // Get current labels on the item + const currentLabels = (item.labels || []).map(label => + typeof label === 'object' ? label.name : label + ).filter(Boolean); + + let labelsToApply = new Set(currentLabels); + let matchedAny = false; + + for (const rule of rules) { + if (!rule.pattern || !rule.labels || !Array.isArray(rule.labels)) { + console.log('âš ī¸ Skipping invalid rule:', JSON.stringify(rule)); + continue; + } - console.log('Applying labels:', allLabels); + if (textToMatch.includes(rule.pattern.toLowerCase())) { + console.log(`✅ Matched pattern: "${rule.pattern}"`); + matchedAny = true; + rule.labels.forEach(label => labelsToApply.add(label.trim())); + } + } + + if (matchedAny) { + const allLabels = Array.from(labelsToApply); + console.log('Applying combined labels:', allLabels); try { await github.rest.issues.setLabels({ owner, repo, - issue_number: issueNumber, + issue_number: itemNumber, labels: allLabels, }); - - console.log(`✅ Successfully applied labels to issue #${issueNumber}`); + console.log(`✅ Successfully applied labels to ${isIssue ? 'issue' : 'PR'} #${itemNumber}`); } catch (error) { - console.error(`❌ Failed to apply labels to issue #${issueNumber}:`, error.message); + console.error(`❌ Failed to apply labels:`, error.message); } } else { - console.log('â„šī¸ Issue title does not match ZAP Scan Baseline Report pattern, skipping'); + console.log('â„šī¸ No matching patterns found, skipping auto-labeling'); } - console.log('✅ ZAP issue labeling completed') + console.log('✅ Auto-labeling completed')