Skip to content

Merge pull request #248 from TransactionProcessing/codacy/high_fixes #2

Merge pull request #248 from TransactionProcessing/codacy/high_fixes

Merge pull request #248 from TransactionProcessing/codacy/high_fixes #2

Workflow file for this run

name: Sync Codacy Issues
on:
workflow_dispatch:
inputs:
dry_run:
description: "Run without creating GitHub issues"
required: false
default: "false"
push:
# branches to consider in the event; optional, defaults to all
branches:
- master
jobs:
sync-codacy-issues:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Fetch Codacy Issues
run: |
curl --request POST \
--url "https://app.codacy.com/api/v3/analysis/organizations/gh/${{ github.repository_owner }}/repositories/${{ github.event.repository.name }}/issues/search" \
--header "api-token: ${{ secrets.CODACY_API_TOKEN }}" \
--header "content-type: application/json" \
--data '{"levels":["Error","Warning","High"]}' \
--silent \
--fail \
-o issues.json
- name: Extract issues
run: jq '.data' issues.json > filtered_issues.json
- name: Create GitHub Issues
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const dryRun = "${{ github.event.inputs.dry_run }}" === "true";
const rawIssues = JSON.parse(fs.readFileSync('filtered_issues.json', 'utf8'));
const grouped = {};
for (const issue of rawIssues) {
grouped[issue.issueId] = grouped[issue.issueId] || [];
grouped[issue.issueId].push(issue);
}
const openIssues = await github.paginate(
github.rest.issues.listForRepo,
{
owner: context.repo.owner,
repo: context.repo.repo,
state: "open"
}
);
console.log(`Fetched ${openIssues.length} open issues from GitHub`);
const existingIds = new Set();
for (const ghIssue of openIssues) {
const matches = ghIssue.body?.match(/codacy-issue-([a-f0-9]+)/g);
if (matches) {
matches.forEach(m => existingIds.add(m.replace("codacy-issue-", "")));
}
}
console.log(`Found ${existingIds.size} existing Codacy issues in GitHub`);
for (const [issueId, issues] of Object.entries(grouped)) {
if (existingIds.has(issueId)) {
console.log(`Skipping duplicate Codacy issueId ${issueId}`);
continue;
}
const key = `codacy-issue-${issueId}`;
const first = issues[0];
const title = `[Codacy] ${first.patternInfo.severityLevel} issue(s) in ${first.filePath}`;
let body = `Codacy detected **${issues.length}** occurrence(s) of rule \`${first.patternInfo.id}\`:\n\n`;
for (const issue of issues) {
body += `- **${issue.patternInfo.severityLevel}** at \`${issue.filePath}:${issue.lineNumber}\` → ${issue.message}\n`;
}
body += `\nSee full details in [Codacy Report](https://app.codacy.com/gh/${context.repo.owner}/${context.repo.repo}/issues)\n\n`;
body += `Unique ID: \`${key}\``;
if (dryRun) {
console.log(`[DRY RUN] Would create issue: ${title}`);
} else {
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title,
body,
labels: ["codacy"]
});
console.log(`✅ Created GitHub issue for Codacy issueId ${issueId}`);
await new Promise(resolve => setTimeout(resolve, 2000));
}
}
- name: Close Resolved GitHub Issues
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const dryRun = "${{ github.event.inputs.dry_run }}" === "true";
const rawIssues = JSON.parse(fs.readFileSync('filtered_issues.json', 'utf8'));
// Build current Codacy set (only *active* issues, not ignored)
const currentCodacyIds = new Set(
rawIssues.filter(i => !i.ignored).map(i => i.issueId)
);
// Build ignored Codacy set
const ignoredCodacyIds = new Set(
rawIssues.filter(i => i.ignored).map(i => i.issueId)
);
// Fetch ALL GitHub issues with codacy label
const allIssues = await github.paginate(
github.rest.issues.listForRepo,
{
owner: context.repo.owner,
repo: context.repo.repo,
state: "all",
labels: ["codacy"]
}
);
for (const ghIssue of allIssues) {
const matches = ghIssue.body?.match(/codacy-issue-([a-f0-9]+)/g);
if (!matches) continue;
for (const match of matches) {
const issueId = match.replace("codacy-issue-", "");
// Close if not active OR explicitly ignored
if ((!currentCodacyIds.has(issueId) || ignoredCodacyIds.has(issueId))
&& ghIssue.state === "open") {
if (dryRun) {
console.log(`[DRY RUN] Would close issue #${ghIssue.number} (Codacy issueId ${issueId})`);
} else {
// Add comment before closing
const reason = ignoredCodacyIds.has(issueId)
? "Auto closed because Codacy issue is marked as *ignored*"
: "Auto closed as not found in last analysis";
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: ghIssue.number,
body: reason
});
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: ghIssue.number,
state: "closed"
});
console.log(`❌ Closed GitHub issue #${ghIssue.number} for Codacy issueId ${issueId}`);
}
}
}
}