Skip to content

Commit 450a10e

Browse files
authored
Merge pull request #4 from link-foundation/issue-3-91c520cb7611
fix(ci): Remove CI/CD check differences between PR and push events
2 parents 2a10999 + 65cdbbd commit 450a10e

File tree

4 files changed

+264
-7
lines changed

4 files changed

+264
-7
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'my-package': patch
3+
---
4+
5+
Fix CI/CD check differences between pull request and push events
6+
7+
Changes:
8+
9+
- Add `detect-changes` job with cross-platform `detect-code-changes.mjs` script
10+
- Make lint job independent of changeset-check (runs based on file changes only)
11+
- Allow docs-only PRs without changeset requirement
12+
- Handle changeset-check 'skipped' state in dependent jobs
13+
- Exclude `.changeset/`, `docs/`, `experiments/`, `examples/` folders and markdown files from code changes detection

.github/workflows/release.yml

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,60 @@ concurrency:
3535
cancel-in-progress: true
3636

3737
env:
38-
JAVA_VERSION: '17'
38+
JAVA_VERSION: '21'
3939
JAVA_DISTRIBUTION: 'temurin'
4040

4141
jobs:
42+
# =============================================================================
43+
# Detect Changes - determines which jobs should run
44+
# =============================================================================
45+
detect-changes:
46+
name: Detect Changes
47+
runs-on: ubuntu-latest
48+
if: github.event_name != 'workflow_dispatch'
49+
outputs:
50+
java-changed: ${{ steps.changes.outputs.java-changed }}
51+
pom-changed: ${{ steps.changes.outputs.pom-changed }}
52+
mjs-changed: ${{ steps.changes.outputs.mjs-changed }}
53+
docs-changed: ${{ steps.changes.outputs.docs-changed }}
54+
workflow-changed: ${{ steps.changes.outputs.workflow-changed }}
55+
any-code-changed: ${{ steps.changes.outputs.any-code-changed }}
56+
steps:
57+
- name: Checkout code
58+
uses: actions/checkout@v4
59+
with:
60+
fetch-depth: 0
61+
62+
- name: Set up Bun
63+
uses: oven-sh/setup-bun@v2
64+
with:
65+
bun-version: latest
66+
67+
- name: Detect changes
68+
id: changes
69+
env:
70+
GITHUB_EVENT_NAME: ${{ github.event_name }}
71+
GITHUB_BASE_SHA: ${{ github.event.pull_request.base.sha }}
72+
GITHUB_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
73+
run: bun scripts/detect-code-changes.mjs
74+
4275
# =============================================================================
4376
# Lint and Format Check
77+
# Runs independently of changeset-check - it's a fast check that should always run
78+
# See: https://github.com/link-foundation/js-ai-driven-development-pipeline-template/pull/18
4479
# =============================================================================
4580
lint:
4681
name: Lint & Format
4782
runs-on: ubuntu-latest
83+
needs: [detect-changes]
84+
if: |
85+
github.event_name == 'workflow_dispatch' ||
86+
github.event_name == 'push' ||
87+
needs.detect-changes.outputs.java-changed == 'true' ||
88+
needs.detect-changes.outputs.pom-changed == 'true' ||
89+
needs.detect-changes.outputs.mjs-changed == 'true' ||
90+
needs.detect-changes.outputs.docs-changed == 'true' ||
91+
needs.detect-changes.outputs.workflow-changed == 'true'
4892
steps:
4993
- name: Checkout code
5094
uses: actions/checkout@v4
@@ -76,11 +120,14 @@ jobs:
76120
test:
77121
name: Test (Java ${{ matrix.java }}, ${{ matrix.os }})
78122
runs-on: ${{ matrix.os }}
123+
needs: [detect-changes, changeset-check]
124+
# Run if: push event, workflow_dispatch, OR changeset-check succeeded, OR changeset-check was skipped (docs-only PR)
125+
if: always() && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || needs.changeset-check.result == 'success' || needs.changeset-check.result == 'skipped')
79126
strategy:
80127
fail-fast: false
81128
matrix:
82129
os: [ubuntu-latest, macos-latest, windows-latest]
83-
java: ['17', '21']
130+
java: ['21']
84131
steps:
85132
- name: Checkout code
86133
uses: actions/checkout@v4
@@ -96,7 +143,7 @@ jobs:
96143
run: mvn test jacoco:report -B
97144

98145
- name: Upload coverage to Codecov
99-
if: matrix.os == 'ubuntu-latest' && matrix.java == '17'
146+
if: matrix.os == 'ubuntu-latest' && matrix.java == '21'
100147
uses: codecov/codecov-action@v4
101148
with:
102149
file: target/site/jacoco/jacoco.xml
@@ -112,6 +159,8 @@ jobs:
112159
name: Build Package
113160
runs-on: ubuntu-latest
114161
needs: [lint, test]
162+
# Run only if lint and test succeeded (handles skipped lint gracefully)
163+
if: always() && (needs.lint.result == 'success' || needs.lint.result == 'skipped') && needs.test.result == 'success'
115164
steps:
116165
- name: Checkout code
117166
uses: actions/checkout@v4
@@ -142,12 +191,14 @@ jobs:
142191
retention-days: 7
143192

144193
# =============================================================================
145-
# Changeset Validation (PRs only)
194+
# Changeset Validation (PRs only, skipped for docs-only changes)
195+
# Docs-only PRs (./docs folder, markdown files) don't require changesets
146196
# =============================================================================
147197
changeset-check:
148198
name: Changeset Check
149199
runs-on: ubuntu-latest
150-
if: github.event_name == 'pull_request'
200+
needs: [detect-changes]
201+
if: github.event_name == 'pull_request' && needs.detect-changes.outputs.any-code-changed == 'true'
151202
steps:
152203
- name: Checkout code
153204
uses: actions/checkout@v4

CHANGELOG.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ This implementation:
1919
- Supports three release modes: changeset, instant, and changeset-pr
2020
- Eliminates merge conflicts on CHANGELOG.md
2121

22-
23-
2422
## [0.1.0] - 2024-12-27
2523

2624
### Added

scripts/detect-code-changes.mjs

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Detect code changes for CI/CD pipeline
5+
*
6+
* This script detects what types of files have changed between two commits
7+
* and outputs the results for use in GitHub Actions workflow conditions.
8+
*
9+
* Key behavior:
10+
* - For PRs: compares PR head against base branch
11+
* - For pushes: compares HEAD against HEAD^
12+
* - Excludes certain folders and file types from "code changes" detection
13+
*
14+
* Excluded from code changes (don't require changesets):
15+
* - Markdown files (*.md) in any folder
16+
* - .changeset/ folder (changeset metadata)
17+
* - docs/ folder (documentation)
18+
* - experiments/ folder (experimental scripts)
19+
* - examples/ folder (example scripts)
20+
*
21+
* Usage:
22+
* node scripts/detect-code-changes.mjs
23+
* bun scripts/detect-code-changes.mjs
24+
*
25+
* Environment variables (set by GitHub Actions):
26+
* - GITHUB_EVENT_NAME: 'pull_request' or 'push'
27+
* - GITHUB_BASE_SHA: Base commit SHA for PR
28+
* - GITHUB_HEAD_SHA: Head commit SHA for PR
29+
*
30+
* Outputs (written to GITHUB_OUTPUT):
31+
* - java-changed: 'true' if any .java files changed
32+
* - pom-changed: 'true' if pom.xml changed
33+
* - mjs-changed: 'true' if any .mjs files changed
34+
* - docs-changed: 'true' if any .md files changed
35+
* - workflow-changed: 'true' if any .github/workflows/ files changed
36+
* - any-code-changed: 'true' if any code files changed (excludes docs, changesets, experiments, examples)
37+
*/
38+
39+
import { execSync } from 'child_process';
40+
import { appendFileSync } from 'fs';
41+
42+
/**
43+
* Execute a shell command and return trimmed output
44+
* @param {string} command - The command to execute
45+
* @returns {string} - The trimmed command output
46+
*/
47+
function exec(command) {
48+
try {
49+
return execSync(command, { encoding: 'utf-8' }).trim();
50+
} catch (error) {
51+
console.error(`Error executing command: ${command}`);
52+
console.error(error.message);
53+
return '';
54+
}
55+
}
56+
57+
/**
58+
* Write output to GitHub Actions output file
59+
* @param {string} name - Output name
60+
* @param {string} value - Output value
61+
*/
62+
function setOutput(name, value) {
63+
const outputFile = process.env.GITHUB_OUTPUT;
64+
if (outputFile) {
65+
appendFileSync(outputFile, `${name}=${value}\n`);
66+
}
67+
console.log(`${name}=${value}`);
68+
}
69+
70+
/**
71+
* Get the list of changed files between two commits
72+
* @returns {string[]} Array of changed file paths
73+
*/
74+
function getChangedFiles() {
75+
const eventName = process.env.GITHUB_EVENT_NAME || 'local';
76+
77+
if (eventName === 'pull_request') {
78+
const baseSha = process.env.GITHUB_BASE_SHA;
79+
const headSha = process.env.GITHUB_HEAD_SHA;
80+
81+
if (baseSha && headSha) {
82+
console.log(`Comparing PR: ${baseSha}...${headSha}`);
83+
try {
84+
// Ensure we have the base commit
85+
try {
86+
execSync(`git cat-file -e ${baseSha}`, { stdio: 'ignore' });
87+
} catch {
88+
console.log('Base commit not available locally, attempting fetch...');
89+
execSync(`git fetch origin ${baseSha}`, { stdio: 'inherit' });
90+
}
91+
const output = exec(`git diff --name-only ${baseSha} ${headSha}`);
92+
return output ? output.split('\n').filter(Boolean) : [];
93+
} catch (error) {
94+
console.error(`Git diff failed: ${error.message}`);
95+
}
96+
}
97+
}
98+
99+
// For push events or fallback
100+
console.log('Comparing HEAD^ to HEAD');
101+
try {
102+
const output = exec('git diff --name-only HEAD^ HEAD');
103+
return output ? output.split('\n').filter(Boolean) : [];
104+
} catch {
105+
// If HEAD^ doesn't exist (first commit), list all files in HEAD
106+
console.log('HEAD^ not available, listing all files in HEAD');
107+
const output = exec('git ls-tree --name-only -r HEAD');
108+
return output ? output.split('\n').filter(Boolean) : [];
109+
}
110+
}
111+
112+
/**
113+
* Check if a file should be excluded from code changes detection
114+
* @param {string} filePath - The file path to check
115+
* @returns {boolean} True if the file should be excluded
116+
*/
117+
function isExcludedFromCodeChanges(filePath) {
118+
// Exclude markdown files in any folder
119+
if (filePath.endsWith('.md')) {
120+
return true;
121+
}
122+
123+
// Exclude specific folders from code changes
124+
const excludedFolders = ['.changeset/', 'docs/', 'experiments/', 'examples/'];
125+
126+
for (const folder of excludedFolders) {
127+
if (filePath.startsWith(folder)) {
128+
return true;
129+
}
130+
}
131+
132+
return false;
133+
}
134+
135+
/**
136+
* Main function to detect changes
137+
*/
138+
function detectChanges() {
139+
console.log('Detecting file changes for CI/CD...\n');
140+
141+
const changedFiles = getChangedFiles();
142+
143+
console.log('Changed files:');
144+
if (changedFiles.length === 0) {
145+
console.log(' (none)');
146+
} else {
147+
changedFiles.forEach((file) => console.log(` ${file}`));
148+
}
149+
console.log('');
150+
151+
// Detect .java file changes
152+
const javaChanged = changedFiles.some((file) => file.endsWith('.java'));
153+
setOutput('java-changed', javaChanged ? 'true' : 'false');
154+
155+
// Detect pom.xml changes
156+
const pomChanged = changedFiles.some((file) => file === 'pom.xml');
157+
setOutput('pom-changed', pomChanged ? 'true' : 'false');
158+
159+
// Detect .mjs file changes (scripts)
160+
const mjsChanged = changedFiles.some((file) => file.endsWith('.mjs'));
161+
setOutput('mjs-changed', mjsChanged ? 'true' : 'false');
162+
163+
// Detect documentation changes (any .md file)
164+
const docsChanged = changedFiles.some((file) => file.endsWith('.md'));
165+
setOutput('docs-changed', docsChanged ? 'true' : 'false');
166+
167+
// Detect workflow changes
168+
const workflowChanged = changedFiles.some((file) =>
169+
file.startsWith('.github/workflows/')
170+
);
171+
setOutput('workflow-changed', workflowChanged ? 'true' : 'false');
172+
173+
// Detect code changes (excluding docs, changesets, experiments, examples folders, and markdown files)
174+
const codeChangedFiles = changedFiles.filter(
175+
(file) => !isExcludedFromCodeChanges(file)
176+
);
177+
178+
console.log('\nFiles considered as code changes:');
179+
if (codeChangedFiles.length === 0) {
180+
console.log(' (none)');
181+
} else {
182+
codeChangedFiles.forEach((file) => console.log(` ${file}`));
183+
}
184+
console.log('');
185+
186+
// Check if any code files changed (.java, .mjs, .xml, .yml, .yaml, or workflow files)
187+
const codePattern = /\.(java|mjs|xml|yml|yaml|properties)$|\.github\/workflows\//;
188+
const codeChanged = codeChangedFiles.some((file) => codePattern.test(file));
189+
setOutput('any-code-changed', codeChanged ? 'true' : 'false');
190+
191+
console.log('\nChange detection completed.');
192+
}
193+
194+
// Run the detection
195+
detectChanges();

0 commit comments

Comments
 (0)