Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve min as the earliest compatible Julia version (compatible with the user's project) #202

Merged
merged 34 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
f6c0c51
Support the special version "MIN"
omus Jan 5, 2024
4467bdb
fixup! Support the special version "MIN"
omus Jan 5, 2024
a01d66b
fixup! Support the special version "MIN"
omus Jan 5, 2024
ba08cda
Build
omus Jan 5, 2024
c551d5c
Support JULIA_PROJECT
omus Jan 5, 2024
319c0e6
WIP
omus Jan 8, 2024
bc76ff4
Add tests
omus Jan 8, 2024
0c43806
Rename special version to project-min
omus Jan 8, 2024
20471b9
Add forgotten test fixtures
omus Jan 8, 2024
ccc4e23
Run build
omus Jan 8, 2024
03c056a
Get latest prerelease/release
omus Jan 8, 2024
b69821d
No special pre-release behaviour
omus Jan 9, 2024
e371afc
Sticking with MIN for job matrix usage
omus Jan 9, 2024
304b120
build
omus Jan 9, 2024
5dfcf28
Add test for NPM semver difference
omus Jan 9, 2024
b8106f2
Cleanup
omus Jan 9, 2024
10a9b2f
build
omus Jan 9, 2024
02aa933
Addition tests
omus Jan 9, 2024
d897c08
Define validJuliaRange
omus Jan 9, 2024
ac7c4d5
Robust test suite
omus Jan 9, 2024
70ba35f
Disallow less-than-equal
omus Jan 9, 2024
266d809
npm run build
omus Jan 9, 2024
0718bbf
Refactor validJuliaCompatRange to return a validRange
omus Jan 10, 2024
f13a394
Build
omus Jan 10, 2024
8a8dfd5
Merge branch 'master' into cv/min-version
omus Aug 30, 2024
df5b495
Rename MIN to min
omus Aug 30, 2024
e5a91a7
fixup! Merge branch 'master' into cv/min-version
omus Aug 30, 2024
1170aba
Rename getProjectFile to getProjectFilePath
omus Aug 30, 2024
1259c4f
Comment on "project" input
omus Aug 30, 2024
ed73f04
Additional tests for getProjectFilePath
omus Aug 30, 2024
ecaeb80
Add comment on `juliaCompatRange`
omus Aug 30, 2024
b38942a
Build package
omus Aug 30, 2024
1665281
Update dependencies
omus Aug 30, 2024
34392a0
Merge branch 'master' into cv/min-version
omus Aug 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ This action sets up a Julia environment for use in actions by downloading a spec
#
# Default: false
show-versioninfo: ''

# Set the path to the project directory or file to use when resolving some versions (e.g. `min`).
#
# Defaults to using JULIA_PROJECT if defined, otherwise '.'
project: ''
```

### Outputs
Expand Down Expand Up @@ -121,6 +126,7 @@ You can either specify specific Julia versions or version ranges. If you specify
- `'pre'` will install the latest prerelease build (RCs, betas, and alphas).
- `'nightly'` will install the latest nightly build.
- `'1.7-nightly'` will install the latest nightly build for the upcoming 1.7 release. This version will only be available during certain phases of the Julia release cycle.
- `'min'` will install the earliest supported version of Julia compatible with the project. Especially useful in monorepos.

Internally the action uses node's semver package to resolve version ranges. Its [documentation](https://github.com/npm/node-semver#advanced-range-syntax) contains more details on the version range syntax. You can test what version will be selected for a given input in this JavaScript [REPL](https://repl.it/@SaschaMann/setup-julia-version-logic).

Expand Down
Empty file.
Empty file.
Empty file.
Empty file.
157 changes: 157 additions & 0 deletions __tests__/installer.test.ts
DilumAluthge marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,135 @@ process.env['RUNNER_TOOL_CACHE'] = toolDir
process.env['RUNNER_TEMP'] = tempDir

import * as installer from '../src/installer'
import exp from 'constants'

describe("getProjectFilePath tests", () => {
let orgJuliaProject
let orgWorkingDir

beforeEach(() => {
orgJuliaProject = process.env["JULIA_PROJECT"]
orgWorkingDir = process.cwd()
delete process.env["JULIA_PROJECT"]
})

afterEach(() => {
process.env["JULIA_PROJECT"] = orgJuliaProject
process.chdir(orgWorkingDir)
})

it("Can determine project file is missing", () => {
expect(() => installer.getProjectFilePath("DNE.toml")).toThrow("Unable to locate project file")
expect(() => installer.getProjectFilePath(fixtureDir)).toThrow("Unable to locate project file")
expect(() => installer.getProjectFilePath()).toThrow("Unable to locate project file")
})

it('Can determine project file from a directory', () => {
expect(installer.getProjectFilePath(path.join(fixtureDir, "PkgA"))).toEqual(path.join(fixtureDir, "PkgA", "Project.toml"))
expect(installer.getProjectFilePath(path.join(fixtureDir, "PkgB"))).toEqual(path.join(fixtureDir, "PkgB", "JuliaProject.toml"))
})

it("Prefers using JuliaProject.toml over Project.toml", () => {
expect(installer.getProjectFilePath(path.join(fixtureDir, "PkgC"))).toEqual(path.join(fixtureDir, "PkgC", "JuliaProject.toml"))
})

it("Can determine project from JULIA_PROJECT", () => {
process.env["JULIA_PROJECT"] = path.join(fixtureDir, "PkgA")
expect(installer.getProjectFilePath()).toEqual(path.join(fixtureDir, "PkgA", "Project.toml"))
})

it("Can determine project from the current working directory", () => {
process.chdir(path.join(fixtureDir, "PkgA"));
expect(installer.getProjectFilePath()).toEqual("Project.toml")
})

it("Ignores JULIA_PROJECT when argument is used", () => {
process.env["JULIA_PROJECT"] = path.join(fixtureDir, "PkgB")
expect(installer.getProjectFilePath(path.join(fixtureDir, "PkgA"))).toEqual(path.join(fixtureDir, "PkgA", "Project.toml"))
})
})

describe("validJuliaCompatRange tests", () => {
it('Handles default caret specifier', () => {
expect(installer.validJuliaCompatRange("1")).toEqual(semver.validRange("^1"))
expect(installer.validJuliaCompatRange("1.2")).toEqual(semver.validRange("^1.2"))
expect(installer.validJuliaCompatRange("1.2.3")).toEqual(semver.validRange("^1.2.3"))

// TODO: Pkg.jl currently does not support pre-release entries in compat so ideally this would fail
expect(installer.validJuliaCompatRange("1.2.3-rc1")).toEqual(semver.validRange("^1.2.3-rc1"))
})

it('Handle surrounding whitespace', () => {
expect(installer.validJuliaCompatRange(" 1")).toEqual(semver.validRange("^1"))
expect(installer.validJuliaCompatRange("1 ")).toEqual(semver.validRange("^1"))
expect(installer.validJuliaCompatRange(" 1 ")).toEqual(semver.validRange("^1"))
})

it('Handles version ranges with specifiers', () => {
expect(installer.validJuliaCompatRange("^1.2.3")).toEqual(semver.validRange("^1.2.3"))
expect(installer.validJuliaCompatRange("~1.2.3")).toEqual(semver.validRange("~1.2.3"))
expect(installer.validJuliaCompatRange("=1.2.3")).toEqual(semver.validRange("=1.2.3"))
expect(installer.validJuliaCompatRange(">=1.2.3")).toEqual(">=1.2.3")
expect(installer.validJuliaCompatRange("≥1.2.3")).toEqual(">=1.2.3")
expect(installer.validJuliaCompatRange("<1.2.3")).toEqual("<1.2.3")
})

it('Handles whitespace after specifiers', () => {
expect(installer.validJuliaCompatRange("^ 1.2.3")).toBeNull()
expect(installer.validJuliaCompatRange("~ 1.2.3")).toBeNull()
expect(installer.validJuliaCompatRange("= 1.2.3")).toBeNull()
expect(installer.validJuliaCompatRange(">= 1.2.3")).toEqual(">=1.2.3")
expect(installer.validJuliaCompatRange("≥ 1.2.3")).toEqual(">=1.2.3")
expect(installer.validJuliaCompatRange("< 1.2.3")).toEqual("<1.2.3")
})

it('Handles hypen ranges', () => {
expect(installer.validJuliaCompatRange("1.2.3 - 4.5.6")).toEqual(semver.validRange("1.2.3 - 4.5.6"))
expect(installer.validJuliaCompatRange("1.2.3-rc1 - 4.5.6")).toEqual(semver.validRange("1.2.3-rc1 - 4.5.6"))
expect(installer.validJuliaCompatRange("1.2.3-rc1-4.5.6")).toEqual(semver.validRange("^1.2.3-rc1-4.5.6")) // A version number and not a hypen range
expect(installer.validJuliaCompatRange("1.2.3-rc1 -4.5.6")).toBeNull()
expect(installer.validJuliaCompatRange("1.2.3-rc1- 4.5.6")).toBeNull() // Whitespace separate version ranges
})

it("Returns null AND operator on version ranges", () => {
expect(installer.validJuliaCompatRange("")).toBeNull()
expect(installer.validJuliaCompatRange("1 2 3")).toBeNull()
expect(installer.validJuliaCompatRange("1- 2")).toBeNull()
expect(installer.validJuliaCompatRange("<1 <1")).toBeNull()
expect(installer.validJuliaCompatRange("< 1 < 1")).toBeNull()
expect(installer.validJuliaCompatRange("< 1 < 1")).toBeNull()
})

it('Returns null with invalid specifiers', () => {
expect(installer.validJuliaCompatRange("<=1.2.3")).toBeNull()
expect(installer.validJuliaCompatRange("≤1.2.3")).toBeNull()
expect(installer.validJuliaCompatRange("*")).toBeNull()
})

it("Handles OR operator on version ranges", () => {
expect(installer.validJuliaCompatRange("1, 2, 3")).toEqual(semver.validRange("^1 || ^2 || ^3"))
expect(installer.validJuliaCompatRange("1, 2 - 3, ≥ 4")).toEqual(semver.validRange("^1 || >=2 <=3 || >=4"))
expect(installer.validJuliaCompatRange(",")).toBeNull()
})
})

describe("readJuliaCompatRange tests", () => {
it('Can determine Julia compat entries', () => {
const toml = '[compat]\njulia = "1, ^1.1, ~1.2, >=1.3, 1.4 - 1.5"'
expect(installer.readJuliaCompatRange(toml)).toEqual(semver.validRange("^1 || ^1.1 || ~1.2 || >=1.3 || 1.4 - 1.5"))
})

it('Throws with invalid version ranges', () => {
expect(() => installer.readJuliaCompatRange('[compat]\njulia = ""')).toThrow("Invalid version range")
expect(() => installer.readJuliaCompatRange('[compat]\njulia = "1 2 3"')).toThrow("Invalid version range")
})

it('Handle missing compat entries', () => {
expect(installer.readJuliaCompatRange("")).toEqual("*")
expect(installer.readJuliaCompatRange("[compat]")).toEqual("*")

})
})

describe('version matching tests', () => {
describe('specific versions', () => {
Expand Down Expand Up @@ -95,6 +224,34 @@ describe('version matching tests', () => {
})
})
})

describe('julia compat versions', () => {
it('Understands "min"', () => {
let versions = ["1.6.7", "1.7.1-rc1", "1.7.1-rc2", "1.7.1", "1.7.2", "1.8.0"]
expect(installer.getJuliaVersion(versions, "min", false, "^1.7")).toEqual("1.7.1")
expect(installer.getJuliaVersion(versions, "min", true, "^1.7")).toEqual("1.7.1-rc1")

versions = ["1.6.7", "1.7.3-rc1", "1.7.3-rc2", "1.8.0"]
expect(installer.getJuliaVersion(versions, "min", false, "^1.7")).toEqual("1.8.0")
expect(installer.getJuliaVersion(versions, "min", true, "^1.7")).toEqual("1.7.3-rc1")

expect(installer.getJuliaVersion(versions, "min", false, "~1.7 || ~1.8 || ~1.9")).toEqual("1.8.0")
expect(installer.getJuliaVersion(versions, "min", true, "~1.7 || ~1.8 || ~1.9")).toEqual("1.7.3-rc1")
expect(installer.getJuliaVersion(versions, "min", false, "~1.8 || ~1.7 || ~1.9")).toEqual("1.8.0")
expect(installer.getJuliaVersion(versions, "min", true, "~1.8 || ~1.7 || ~1.9")).toEqual("1.7.3-rc1")

expect(installer.getJuliaVersion(versions, "min", false, "1.7 - 1.9")).toEqual("1.8.0")
expect(installer.getJuliaVersion(versions, "min", true, "1.7 - 1.9")).toEqual("1.7.3-rc1")

expect(installer.getJuliaVersion(versions, "min", true, "< 1.9.0")).toEqual("1.6.7")
expect(installer.getJuliaVersion(versions, "min", true, ">= 1.6.0")).toEqual("1.6.7")

// NPM's semver package treats "1.7" as "~1.7" instead of "^1.7" like Julia
expect(() => installer.getJuliaVersion(versions, "min", false, "1.7")).toThrow("Could not find a Julia version that matches")

expect(() => installer.getJuliaVersion(versions, "min", true, "")).toThrow("Julia project file does not specify a compat for Julia")
})
})
})

describe('installer tests', () => {
Expand Down
4 changes: 4 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ inputs:
description: 'Display InteractiveUtils.versioninfo() after installing'
required: false
default: 'false'
project:
omus marked this conversation as resolved.
Show resolved Hide resolved
description: 'The path to the project directory or file to use when resolving some versions (e.g. min)'
required: false
default: '' # Special value which fallsback to using JULIA_PROJECT if defined, otherwise "."
outputs:
julia-version:
description: 'The installed Julia version. May vary from the version input if a version range was given as input.'
Expand Down
108 changes: 99 additions & 9 deletions lib/installer.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion lib/setup-julia.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading