Skip to content

Commit d373fe5

Browse files
authored
Support inclusive bound (#3)
1 parent 5a960c7 commit d373fe5

File tree

4 files changed

+75
-34
lines changed

4 files changed

+75
-34
lines changed

.github/workflows/test.yaml

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,23 @@ jobs:
1414
- uses: actions/[email protected]
1515
- run: npm install
1616
- run: npm run build
17-
- name: Run action
17+
- name: Run action with inclusive upper bound
1818
uses: ./
1919
with:
20-
package-yaml-path: examples/package.yaml
21-
id: ghc-version
20+
package-yaml-path: examples/package-equal-or-less-than.yaml
21+
id: ghc-version-inclusive
2222
- name: Assert output
2323
run: |-
24-
if [ "${{ steps.ghc-version.outputs.ghc-version }}" != "8.10.7" ]; then
24+
if [ "${{ steps.ghc-version-inclusive.outputs.ghc-version }}" != "9.0.1" ]; then
25+
exit 1
26+
fi
27+
- name: Run action with exclusive upper bound
28+
uses: ./
29+
with:
30+
package-yaml-path: examples/package-less-than.yaml
31+
id: ghc-version-exclusive
32+
- name: Assert output
33+
run: |-
34+
if [ "${{ steps.ghc-version-exclusive.outputs.ghc-version }}" != "8.10.7" ]; then
2535
exit 1
2636
fi
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name: my-haskell-package
2+
version: 0.1.0.0
3+
dependencies: [base >= 4.10 && <= 4.15, containers, text]
4+
5+
library:
6+
exposed-modules: MyLib
7+
source-dirs: src
8+
ghc-options: -Wall
File renamed without changes.

src/index.js

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,43 @@ async function runCommand(cmd) {
1313
});
1414
}
1515

16-
function getBaseUpperBound(packageYamlPath) {
16+
function getBaseUpperBound(baseBound) {
17+
const baseBoundMatch = baseBound.match(/(<=|<)\s*((\d+)(\.(\d+))?(\.(\d+))?)/);
18+
if (!baseBoundMatch) return null;
19+
20+
const operator = baseBoundMatch[1];
21+
const version = baseBoundMatch[2];
22+
const inclusive = operator === "<=";
23+
24+
return { inclusive, version };
25+
}
26+
27+
function normalizeVersion(version, segments = 3) {
28+
const parts = version.split('.').map(Number);
29+
while (parts.length < segments) parts.push(0);
30+
return parts.slice(0, segments).join('.');
31+
}
32+
33+
function compareVersions(a, b) {
34+
const pa = normalizeVersion(a).split('.').map(Number);
35+
const pb = normalizeVersion(b).split('.').map(Number);
36+
const len = Math.max(pa.length, pb.length);
37+
38+
for (let i = 0; i < len; i++) {
39+
const na = pa[i] || 0;
40+
const nb = pb[i] || 0;
41+
if (na > nb) return 1;
42+
if (na < nb) return -1;
43+
}
44+
return 0;
45+
}
46+
47+
function versionLess(baseVersion, bound) {
48+
const cmp = compareVersions(baseVersion, bound.version);
49+
return cmp < 0 || (cmp === 0 && bound.inclusive);
50+
}
51+
52+
function parseBaseUpperBound(packageYamlPath) {
1753
const fileContent = fs.readFileSync(packageYamlPath, "utf-8");
1854
const parsed = yaml.load(fileContent);
1955

@@ -27,54 +63,40 @@ function getBaseUpperBound(packageYamlPath) {
2763
throw new Error("No base dependency found in package.yaml");
2864
}
2965

30-
const versionConstraint = baseDep.match(/<\s*([\d.]+)/);
66+
const versionConstraint = getBaseUpperBound(baseDep);
3167
if (!versionConstraint) {
3268
throw new Error("No upper bound for base found in package.yaml");
3369
}
3470

35-
return versionConstraint[1];
36-
}
37-
38-
function versionLess(v1, v2) {
39-
const a = v1.split('.').map(Number);
40-
const b = v2.split('.').map(Number);
41-
for (let i = 0; i < Math.max(a.length, b.length); i++) {
42-
const n1 = a[i] || 0;
43-
const n2 = b[i] || 0;
44-
if (n1 < n2) return true;
45-
if (n1 > n2) return false;
46-
}
47-
return false;
71+
return versionConstraint;
4872
}
4973

5074
async function main() {
5175
try {
52-
const files = fs.readdirSync(process.cwd());
5376
const packageYamlPath = githubCore.getInput("package-yaml-path") || path.join(process.cwd(), "package.yaml");
54-
55-
const baseUpperBound = getBaseUpperBound(packageYamlPath);
77+
const baseUpperBound = parseBaseUpperBound(packageYamlPath);
5678

5779
const ghcupListStr = await runCommand("ghcup list -t ghc -r");
5880
const lines = ghcupListStr.split("\n").filter(Boolean);
5981

60-
const ghcupList = lines.map(line => {
61-
const match = line.match(/^ghc\s([^\s]+)\s.*?base-([0-9.]+)/);
62-
if (!match) return null;
63-
return { version: match[1], base: match[2] };
64-
}).filter(Boolean);
82+
const ghcupList = lines.map(line => {
83+
const match = line.match(/^ghc\s([^\s]+)\s.*?base-([0-9.]+)/);
84+
if (!match) return null;
85+
return { version: match[1], base: match[2] };
86+
}).filter(Boolean);
6587

66-
if(ghcupList.length > 0) {
67-
console.log(`Found ${ghcupList.length} GHC versions`);
68-
} else {
69-
throw new Error('Failed to get GHC versions from GHCup');
70-
}
88+
if (ghcupList.length > 0) {
89+
console.log(`Found ${ghcupList.length} GHC versions`);
90+
} else {
91+
throw new Error('Failed to get GHC versions from GHCup');
92+
}
7193

7294
const validVersions = ghcupList.filter(ghcEntry => {
7395
return versionLess(ghcEntry.base, baseUpperBound);
7496
});
7597

7698
if (validVersions.length === 0) {
77-
throw new Error(`No GHC version found with base < ${baseUpperBound}`);
99+
throw new Error(`No GHC version found with base <${baseUpperBound.inclusive ? "=" : ""} ${baseUpperBound.version}`);
78100
}
79101

80102
validVersions.sort((a, b) => {
@@ -91,7 +113,8 @@ async function main() {
91113

92114
const latestGhc = validVersions[0].version;
93115

94-
console.log(`Latest GHC under base < ${baseUpperBound}: ${latestGhc}`);
116+
console.log(`Latest GHC under base < ${baseUpperBound.version}: ${latestGhc}`);
117+
95118
const outputPath = process.env.GITHUB_OUTPUT;
96119
fs.appendFileSync(outputPath, `ghc-version=${latestGhc}\n`);
97120
} catch (err) {

0 commit comments

Comments
 (0)