Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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!"]}]'
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
67 changes: 34 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
```
Expand All @@ -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
Expand Down Expand Up @@ -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"
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down
99 changes: 60 additions & 39 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -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')
Loading