From 6384b69f80a79038b5213c55f4cc64d65834ef9a Mon Sep 17 00:00:00 2001 From: ddjain Date: Thu, 30 Oct 2025 10:44:23 +0530 Subject: [PATCH 1/2] feat(rule): add ci-change detection (closes #13) --- __tests__/infrastructure/ci-change.test.js | 23 +++++++++++ src/rules/ci/ci-change.js | 46 ++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 __tests__/infrastructure/ci-change.test.js create mode 100644 src/rules/ci/ci-change.js diff --git a/__tests__/infrastructure/ci-change.test.js b/__tests__/infrastructure/ci-change.test.js new file mode 100644 index 0000000..3b84ba2 --- /dev/null +++ b/__tests__/infrastructure/ci-change.test.js @@ -0,0 +1,23 @@ +const rule = require('../../src/rules/ci/ci-change'); + +describe('CI Change Rule', () => { + it('labels for GitHub Actions workflow', () => { + const files = [{ filename: '.github/workflows/build.yml' }]; + const labels = rule({ files, pr: {}, enableDebug: false }); + expect(labels).toContain('ci-change'); + }); + + it('labels for Jenkinsfile', () => { + const files = [{ filename: 'ci/Jenkinsfile' }]; + const labels = rule({ files, pr: {}, enableDebug: false }); + expect(labels).toContain('ci-change'); + }); + + it('does not label for non-ci files', () => { + const files = [{ filename: 'src/index.ts' }]; + const labels = rule({ files, pr: {}, enableDebug: false }); + expect(labels).toEqual([]); + }); +}); + + diff --git a/src/rules/ci/ci-change.js b/src/rules/ci/ci-change.js new file mode 100644 index 0000000..95d4e13 --- /dev/null +++ b/src/rules/ci/ci-change.js @@ -0,0 +1,46 @@ +/** + * CI Workflow Change Rule + * + * Adds `ci-change` if CI pipeline/workflow files are modified. + */ + +module.exports = function ciChangeRule({ files, pr, enableDebug }) { + const labels = []; + + const patterns = [ + /^\.github\/workflows\//i, + /^\.circleci\//i, + /(^|\/)Jenkinsfile$/i, + /(^|\/)azure-pipelines\.ya?ml$/i, + /(^|\/)gitlab-ci\.ya?ml$/i, + /(^|\/)bitrise\.ya?ml$/i + ]; + + const detected = (files || []).some(f => { + const name = (f && f.filename ? String(f.filename) : '').toLowerCase(); + return name && patterns.some(rx => rx.test(name)); + }); + + if (detected) { + labels.push('ci-change'); + } + + if (enableDebug) { + console.log(`[CI Change Rule] → ${labels.join(', ') || 'none'}`); + } + + return labels; +}; + +module.exports.metadata = { + name: 'CI Workflow Changes', + description: 'Detects modifications to CI workflows/pipelines', + labels: [ + { name: 'ci-change', color: '0075CA', description: 'CI workflow changed' } + ], + author: 'pr-auto-labeler', + version: '1.0.0', + category: 'infrastructure' +}; + + From d1ef2f86e04f58112cc8e109286398498f8b41b0 Mon Sep 17 00:00:00 2001 From: ddjain Date: Thu, 30 Oct 2025 11:58:10 +0530 Subject: [PATCH 2/2] test: improve ci-change coverage to 85%+ --- __tests__/infrastructure/ci-change.test.js | 40 +++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/__tests__/infrastructure/ci-change.test.js b/__tests__/infrastructure/ci-change.test.js index 3b84ba2..1ad4457 100644 --- a/__tests__/infrastructure/ci-change.test.js +++ b/__tests__/infrastructure/ci-change.test.js @@ -7,17 +7,55 @@ describe('CI Change Rule', () => { expect(labels).toContain('ci-change'); }); + it('labels for CircleCI config', () => { + const files = [{ filename: '.circleci/config.yml' }]; + const labels = rule({ files, pr: {}, enableDebug: false }); + expect(labels).toContain('ci-change'); + }); + it('labels for Jenkinsfile', () => { const files = [{ filename: 'ci/Jenkinsfile' }]; const labels = rule({ files, pr: {}, enableDebug: false }); expect(labels).toContain('ci-change'); }); + it('labels for Azure Pipelines', () => { + const files = [{ filename: 'azure-pipelines.yml' }]; + const labels = rule({ files, pr: {}, enableDebug: false }); + expect(labels).toContain('ci-change'); + }); + + it('labels for GitLab CI', () => { + const files = [{ filename: 'root/gitlab-ci.yml' }]; + const labels = rule({ files, pr: {}, enableDebug: false }); + expect(labels).toContain('ci-change'); + }); + + it('labels for Bitrise', () => { + const files = [{ filename: 'config/bitrise.yml' }]; + const labels = rule({ files, pr: {}, enableDebug: false }); + expect(labels).toContain('ci-change'); + }); + it('does not label for non-ci files', () => { const files = [{ filename: 'src/index.ts' }]; const labels = rule({ files, pr: {}, enableDebug: false }); expect(labels).toEqual([]); }); -}); + it('handles empty files array', () => { + const labels = rule({ files: [], pr: {}, enableDebug: false }); + expect(labels).toEqual([]); + }); + + it('handles null filename', () => { + const files = [{ filename: null }]; + const labels = rule({ files, pr: {}, enableDebug: false }); + expect(labels).toEqual([]); + }); + it('handles debug mode', () => { + const files = [{ filename: '.github/workflows/test.yml' }]; + expect(() => rule({ files, pr: {}, enableDebug: true })).not.toThrow(); + }); +});