11name : Codacy Bootstrap Issues
22
33on :
4- workflow_dispatch : # allows manual trigger
4+ workflow_dispatch :
5+ inputs :
6+ dry_run :
7+ description : " Run without creating GitHub issues"
8+ required : false
9+ default : " false"
510
611jobs :
7- bootstrap :
12+ sync-codacy-issues :
813 runs-on : ubuntu-latest
914 steps :
10- - name : Fetch all outstanding Codacy issues
15+ - name : Checkout
16+ uses : actions/checkout@v4
17+
18+ - name : Fetch Codacy Issues
1119 run : |
1220 curl --request POST \
13- --url https://app.codacy.com/api/v3/analysis/organizations/gh/TransactionProcessing /repositories/Shared /issues/search \
21+ --url " https://app.codacy.com/api/v3/analysis/organizations/gh/${{ github.repository_owner }} /repositories/${{ github.event.repository.name }} /issues/search" \
1422 --header "api-token: ${{ secrets.CODACY_API_TOKEN }}" \
1523 --header "content-type: application/json" \
16- --data '{
17- "levels": ["Error", "Warning"]
18- }' \
19- -o codacy_issues.json
20-
21- # Debug: see response
22- cat codacy_issues.json
24+ --data '{"levels":["Error","Warning"]}' \
25+ --silent \
26+ --fail \
27+ -o issues.json
2328
24- # Filter only Medium+/Warning+ issues
25- jq '[ .data[] | select(.patternInfo.severityLevel=="Warning" or .patternInfo.severityLevel=="Error")]' codacy_issues .json > filtered_issues.json
29+ - name : Extract issues
30+ run : jq '.data' issues .json > filtered_issues.json
2631
2732 - name : Create GitHub Issues
2833 uses : actions/github-script@v7
2934 with :
3035 script : |
3136 const fs = require('fs');
37+ const dryRun = "${{ github.event.inputs.dry_run }}" === "true";
38+
3239 const rawIssues = JSON.parse(fs.readFileSync('filtered_issues.json', 'utf8'));
3340
34- // ✅ Group Codacy issues by issueId
3541 const grouped = {};
3642 for (const issue of rawIssues) {
3743 grouped[issue.issueId] = grouped[issue.issueId] || [];
3844 grouped[issue.issueId].push(issue);
3945 }
4046
41- // ✅ Fetch ALL open GitHub issues
4247 const openIssues = await github.paginate(
4348 github.rest.issues.listForRepo,
4449 {
5055
5156 console.log(`Fetched ${openIssues.length} open issues from GitHub`);
5257
53- // ✅ Extract Codacy IDs from issue bodies
5458 const existingIds = new Set();
5559 for (const ghIssue of openIssues) {
5660 const matches = ghIssue.body?.match(/codacy-issue-([a-f0-9]+)/g);
6165
6266 console.log(`Found ${existingIds.size} existing Codacy issues in GitHub`);
6367
64- // ✅ Create issues for any Codacy issueId not already present
6568 for (const [issueId, issues] of Object.entries(grouped)) {
6669 if (existingIds.has(issueId)) {
6770 console.log(`Skipping duplicate Codacy issueId ${issueId}`);
@@ -76,20 +79,20 @@ jobs:
7679 for (const issue of issues) {
7780 body += `- **${issue.patternInfo.severityLevel}** at \`${issue.filePath}:${issue.lineNumber}\` → ${issue.message}\n`;
7881 }
79-
8082 body += `\nSee full details in [Codacy Report](https://app.codacy.com/gh/${context.repo.owner}/${context.repo.repo}/issues)\n\n`;
8183 body += `Unique ID: \`${key}\``;
8284
83- await github.rest.issues.create({
84- owner: context.repo.owner,
85- repo: context.repo.repo,
86- title,
87- body,
88- labels: ["codacy"]
89- });
90-
91- console.log(`✅ Created GitHub issue for Codacy issueId ${issueId}`);
92-
93- // ✅ Sleep 2s to avoid secondary rate limits
94- await new Promise(resolve => setTimeout(resolve, 2000));
95- }
85+ if (dryRun) {
86+ console.log(`[DRY RUN] Would create issue: ${title}`);
87+ } else {
88+ await github.rest.issues.create({
89+ owner: context.repo.owner,
90+ repo: context.repo.repo,
91+ title,
92+ body,
93+ labels: ["codacy"]
94+ });
95+ console.log(`✅ Created GitHub issue for Codacy issueId ${issueId}`);
96+ await new Promise(resolve => setTimeout(resolve, 2000));
97+ }
98+ }
0 commit comments