Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ been tested to work successfully.

### `cuda`

**Optional** The CUDA version to install. View `src/link/windows-links.ts` and
`src/link/linux-links.ts` for available versions.
**Optional** The CUDA version to install. A full version like `'13.2.0'`
installs exactly that version; a major.minor pin like `'13.2'` resolves to the
newest available patch of that series, and a major-only pin like `'13'` resolves
to the newest available release of that major series. View
`src/link/windows-links.ts` and `src/link/linux-links.ts` for available
versions.

Default: `'13.2.0'`.

Expand Down Expand Up @@ -96,7 +100,8 @@ Default: `'log.txt'`

### `cuda`

The cuda version installed (same as `cuda` from input).
The cuda version installed (the input with a version pin resolved to the
installed release).

### `CUDA_PATH`

Expand Down
45 changes: 45 additions & 0 deletions __tests__/version.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,48 @@ test.concurrent.each<Method>(['local', 'network'])(
}
}
)

test.concurrent.each<Method>(['local', 'network'])(
'Resolve major.minor version pin to the newest listed patch for method %s',
async (method) => {
const version = await getVersion('11.2', method)
expect(version).toBeInstanceOf(SemVer)
expect(version.compare(new SemVer('11.2.2'))).toBe(0)
}
)

test.concurrent.each<Method>(['local', 'network'])(
'Resolve major-only version pin to the newest listed release of the series for method %s',
async (method) => {
const version = await getVersion('11', method)
expect(version).toBeInstanceOf(SemVer)
expect(version.compare(new SemVer('11.8.0'))).toBe(0)
}
)

test.concurrent.each<Method>(['local', 'network'])(
'Expect error to be thrown on unavailable major.minor pin for method %s',
async (method) => {
await expect(getVersion('0.1', method)).rejects.toThrow(
'Version not available: 0.1.0'
)
}
)

test.concurrent.each<Method>(['local', 'network'])(
'Expect error to be thrown on unavailable major-only pin for method %s',
async (method) => {
await expect(getVersion('0', method)).rejects.toThrow(
'Version not available: 0.0.0'
)
}
)

test.concurrent.each<Method>(['local', 'network'])(
'Expect error to be thrown on full version outside the listed patches for method %s',
async (method) => {
await expect(getVersion('11.2.3', method)).rejects.toThrow(
'Version not available: 11.2.3'
)
}
)
41 changes: 34 additions & 7 deletions dist/index.js

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

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ async function run(): Promise<void> {
const cudaPath: string = await updatePath(version)

// Set output variables
core.setOutput('cuda', cuda)
core.setOutput('cuda', version.version)
core.setOutput('CUDA_PATH', cudaPath)
} catch (error) {
if (error instanceof Error) {
Expand Down
50 changes: 45 additions & 5 deletions src/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,37 @@ import { SemVer } from 'semver'
import { WindowsLinks } from './links/windows-links.js'
import { getLinks } from './links/get-links.js'

// How loosely the input pins the version: a full semver string installs
// exactly that version, a major.minor pin floats across patches of that
// series, and a major-only pin floats across the whole major series.
type VersionPin = 'full' | 'major.minor' | 'major'

// Convert a version string to SemVer plus its pin level. Anything that is
// neither full semver nor a bare major(.minor) number is rejected.
function parseVersion(versionString: string): {
version: SemVer
pin: VersionPin
} {
try {
return { version: new SemVer(versionString), pin: 'full' }
} catch {
const match = versionString.trim().match(/^v?(\d+)(?:\.(\d+))?/)
if (match === null) {
throw new TypeError(`Invalid Version: ${versionString}`)
}
return {
version: new SemVer(`${match[1]}.${match[2] ?? '0'}.0`),
pin: match[2] === undefined ? 'major' : 'major.minor'
}
}
}

// Helper for converting string to SemVer and verifying it exists in the links
export async function getVersion(
versionString: string,
method: Method
): Promise<SemVer> {
const version = new SemVer(versionString)
const { version, pin } = parseVersion(versionString)
const links: AbstractLinks = await getLinks()
let versions
switch (method) {
Expand All @@ -32,11 +57,26 @@ export async function getVersion(
}
}
core.debug(`Available versions: ${versions}`)
if (versions.find((v) => v.compare(version) === 0) !== undefined) {
if (pin !== 'full') {
// A pin floats to the newest listed release it covers, matching how apt
// resolves cuda-nvcc-<major>-<minor> on network installs.
const newest = versions
.filter(
(v) =>
v.major === version.major &&
(pin === 'major' || v.minor === version.minor)
)
.reduce<
SemVer | undefined
>((newest, v) => (newest === undefined || v.compare(newest) > 0 ? v : newest), undefined)
if (newest !== undefined) {
core.debug(`Version pin ${version} resolves to: ${newest}`)
return newest
Comment thread
myl7 marked this conversation as resolved.
Resolved
}
} else if (versions.find((v) => v.compare(version) === 0) !== undefined) {
core.debug(`Version available: ${version}`)
return version
} else {
core.debug(`Version not available error!`)
throw new Error(`Version not available: ${version}`)
}
core.debug(`Version not available error!`)
throw new Error(`Version not available: ${version}`)
}