mirror of
https://github.com/MillironX/setup-nextflow.git
synced 2024-11-23 18:09:55 +00:00
Compare commits
33 commits
bb811ccf98
...
6d07f25101
Author | SHA1 | Date | |
---|---|---|---|
6d07f25101 | |||
ad89639887 | |||
65a3486a82 | |||
a0546498bf | |||
3b23b7abfe | |||
d3023422c1 | |||
eaeab23c1a | |||
5d1d40a4ea | |||
20ed9d58ea | |||
e8d313e9d2 | |||
34cd96c6c5 | |||
29edf6bed3 | |||
1927f1a07e | |||
21ad9d9cb6 | |||
525dd341cb | |||
31898fbe2b | |||
2145dbec41 | |||
243e2d8f57 | |||
76b5d90248 | |||
34b2731a55 | |||
e259f2a032 | |||
d4db5065bf | |||
f65f3889f1 | |||
92d5fbf3ef | |||
3fdf4e64ae | |||
f4e0bf4b97 | |||
34c114ad83 | |||
78ff7697d2 | |||
6174369e79 | |||
199dd22747 | |||
a4976909b6 | |||
74ebaacc41 | |||
222380fece |
23 changed files with 1109 additions and 487 deletions
3
.actrc
Normal file
3
.actrc
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
-P ubuntu-latest=catthehacker/ubuntu:act-latest
|
||||||
|
-s GITHUB_TOKEN
|
||||||
|
--container-architecture linux/amd64
|
14
.github/workflows/example.yml
vendored
14
.github/workflows/example.yml
vendored
|
@ -10,7 +10,7 @@ on:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
example-usage:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
|
@ -37,13 +37,18 @@ jobs:
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
- run: npm run package
|
- run: npm run package
|
||||||
|
- uses: actions/setup-java@v2
|
||||||
|
if: ${{ env.ACT }}
|
||||||
|
with:
|
||||||
|
java-version: 11
|
||||||
|
distribution: zulu
|
||||||
- uses: ./
|
- uses: ./
|
||||||
with:
|
with:
|
||||||
version: ${{ matrix.nextflow_version }}
|
version: ${{ matrix.nextflow_version }}
|
||||||
all: ${{ matrix.all_distribution }}
|
all: ${{ matrix.all_distribution }}
|
||||||
- run: nextflow -v
|
- run: nextflow -v
|
||||||
|
|
||||||
test-14:
|
example-maximized-build-space:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: easimon/maximize-build-space@v7
|
- uses: easimon/maximize-build-space@v7
|
||||||
|
@ -55,5 +60,10 @@ jobs:
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
- run: npm run package
|
- run: npm run package
|
||||||
|
- uses: actions/setup-java@v2
|
||||||
|
if: ${{ env.ACT}}
|
||||||
|
with:
|
||||||
|
java-version: 11
|
||||||
|
distribution: zulu
|
||||||
- uses: ./
|
- uses: ./
|
||||||
- run: nextflow -v
|
- run: nextflow -v
|
||||||
|
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -89,7 +89,9 @@ out
|
||||||
|
|
||||||
# Nuxt.js build / generate output
|
# Nuxt.js build / generate output
|
||||||
.nuxt
|
.nuxt
|
||||||
dist
|
# TODO: Remove the dist folder again after merging
|
||||||
|
# This folder is only committed right now to allow for remote testing
|
||||||
|
#dist
|
||||||
|
|
||||||
# Gatsby files
|
# Gatsby files
|
||||||
.cache/
|
.cache/
|
||||||
|
@ -132,3 +134,7 @@ dist
|
||||||
# Ignore built ts files
|
# Ignore built ts files
|
||||||
__tests__/runner/*
|
__tests__/runner/*
|
||||||
lib/**/*
|
lib/**/*
|
||||||
|
|
||||||
|
# Testing garbage
|
||||||
|
.tmp
|
||||||
|
nxf-v*
|
||||||
|
|
1
.husky/.gitignore
vendored
Normal file
1
.husky/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
_
|
2
.husky/pre-commit
Normal file
2
.husky/pre-commit
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
npx lint-staged
|
24
.vscode/launch.json
vendored
Normal file
24
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch Action",
|
||||||
|
"program": "${workspaceFolder}/src/main.ts",
|
||||||
|
"preLaunchTask": "tsc: build - tsconfig.json",
|
||||||
|
"outFiles": ["${workspaceFolder}/lib/**/*.js"],
|
||||||
|
"env": {
|
||||||
|
"INPUT_ALL": "false",
|
||||||
|
"INPUT_VERSION": "latest",
|
||||||
|
"INPUT_TOKEN": "${env:GITHUB_TOKEN}",
|
||||||
|
"INPUT_COOLDOWN": "60",
|
||||||
|
"INPUT_MAX_RETRIES": "3",
|
||||||
|
"RUNNER_TEMP": "${workspaceFolder}/.tmp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- CI workflow steps for better compatibility with <https://github.com/nektos/act>
|
||||||
|
- Visual Studio Code debugging configuration
|
||||||
|
- Husky pre-commit hooks for linting and formatting
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- GitHub Octokit API switched to nf-core custom API
|
||||||
|
|
||||||
## [1.5.1] - 2024-01-30
|
## [1.5.1] - 2024-01-30
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
12
action.yml
12
action.yml
|
@ -10,18 +10,6 @@ inputs:
|
||||||
description: "Whether to install every Nextflow release via the '-all' distribution."
|
description: "Whether to install every Nextflow release via the '-all' distribution."
|
||||||
required: false
|
required: false
|
||||||
default: false
|
default: false
|
||||||
token:
|
|
||||||
description: "GitHub token to access the GitHub Releases API. The default token should be sufficient for all use cases."
|
|
||||||
required: false
|
|
||||||
default: ${{ github.token }}
|
|
||||||
cooldown:
|
|
||||||
description: "Time (in seconds) to wait before querying the GitHub Releases API in the case of hitting a rate limit."
|
|
||||||
required: false
|
|
||||||
default: "60"
|
|
||||||
max-retries:
|
|
||||||
description: "The number of times that to try querying the GitHub Releases API in case of a rate-limited failure."
|
|
||||||
required: false
|
|
||||||
default: "3"
|
|
||||||
runs:
|
runs:
|
||||||
using: "node16"
|
using: "node16"
|
||||||
main: "dist/index.js"
|
main: "dist/index.js"
|
||||||
|
|
BIN
dist/LICENSE
generated
vendored
Normal file
BIN
dist/LICENSE
generated
vendored
Normal file
Binary file not shown.
BIN
dist/index.js
generated
vendored
Normal file
BIN
dist/index.js
generated
vendored
Normal file
Binary file not shown.
BIN
dist/index.js.map
generated
vendored
Normal file
BIN
dist/index.js.map
generated
vendored
Normal file
Binary file not shown.
BIN
dist/sourcemap-register.js
generated
vendored
Normal file
BIN
dist/sourcemap-register.js
generated
vendored
Normal file
Binary file not shown.
943
package-lock.json
generated
943
package-lock.json
generated
File diff suppressed because it is too large
Load diff
13
package.json
13
package.json
|
@ -11,7 +11,8 @@
|
||||||
"lint:fix": "eslint --fix {src,test}/**/*.ts",
|
"lint:fix": "eslint --fix {src,test}/**/*.ts",
|
||||||
"package": "ncc build --source-map --license LICENSE",
|
"package": "ncc build --source-map --license LICENSE",
|
||||||
"test": "c8 --reporter=lcov ava",
|
"test": "c8 --reporter=lcov ava",
|
||||||
"all": "npm run build && npm run format && npm run lint && npm run package && npm test"
|
"all": "npm run build && npm run format && npm run lint && npm run package && npm test",
|
||||||
|
"prepare": "husky && husky install"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -27,6 +28,9 @@
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"ts"
|
"ts"
|
||||||
],
|
],
|
||||||
|
"environmentVariables": {
|
||||||
|
"RUNNER_TEMP": "./.tmp"
|
||||||
|
},
|
||||||
"rewritePaths": {
|
"rewritePaths": {
|
||||||
"src/": "lib/"
|
"src/": "lib/"
|
||||||
},
|
},
|
||||||
|
@ -50,7 +54,6 @@
|
||||||
"@actions/exec": "^1.1.1",
|
"@actions/exec": "^1.1.1",
|
||||||
"@actions/github": "^6.0.0",
|
"@actions/github": "^6.0.0",
|
||||||
"@actions/tool-cache": "^2.0.1",
|
"@actions/tool-cache": "^2.0.1",
|
||||||
"@octokit/plugin-throttling": "^8.1.3",
|
|
||||||
"@types/node": "^17.0.44",
|
"@types/node": "^17.0.44",
|
||||||
"async-retry": "^1.3.3",
|
"async-retry": "^1.3.3",
|
||||||
"semver": "^7.3.7"
|
"semver": "^7.3.7"
|
||||||
|
@ -71,8 +74,14 @@
|
||||||
"eslint-plugin-github": "^4.4.1",
|
"eslint-plugin-github": "^4.4.1",
|
||||||
"eslint-plugin-jest": "^27.1.5",
|
"eslint-plugin-jest": "^27.1.5",
|
||||||
"eslint-plugin-simple-import-sort": "^8.0.0",
|
"eslint-plugin-simple-import-sort": "^8.0.0",
|
||||||
|
"husky": "^9.0.10",
|
||||||
|
"lint-staged": "^15.2.1",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^4.7.3"
|
"typescript": "^4.7.3"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{js,ts}": "eslint --cache --fix",
|
||||||
|
"*.{ts,js,yml,md,json}": "prettier --write"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
108
src/functions.ts
108
src/functions.ts
|
@ -6,115 +6,15 @@ import semver from "semver"
|
||||||
|
|
||||||
import { NextflowRelease } from "./nextflow-release"
|
import { NextflowRelease } from "./nextflow-release"
|
||||||
|
|
||||||
async function get_latest_everything_nextflow_release(
|
|
||||||
releases: AsyncGenerator<NextflowRelease>
|
|
||||||
): Promise<NextflowRelease> {
|
|
||||||
// Need to make sure we aren't in the edge case where a patch release is
|
|
||||||
// more recent chronologically than the edge release
|
|
||||||
let latest_release = {} as NextflowRelease
|
|
||||||
|
|
||||||
for await (const release of releases) {
|
|
||||||
// First iteration:
|
|
||||||
if (Object.keys(latest_release).length === 0) {
|
|
||||||
// If the most recent release is an edge release, then we have nothing to
|
|
||||||
// worry about, return it.
|
|
||||||
if (release.isEdge) {
|
|
||||||
return release
|
|
||||||
}
|
|
||||||
// Ok, so the most recent release is a stable release. We need to keep
|
|
||||||
// tabs on it
|
|
||||||
latest_release = release
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// A larger version number that is older than the "latest" release
|
|
||||||
// indicates that we've hit an edge release that is more up-to-date
|
|
||||||
// than a patch release. Return it.
|
|
||||||
if (semver.gt(release.versionNumber, latest_release.versionNumber, true)) {
|
|
||||||
return release
|
|
||||||
}
|
|
||||||
|
|
||||||
// A smaller version number that is also an edge release indicates that the
|
|
||||||
// chronologically most recent release is also the most up-to-date
|
|
||||||
if (
|
|
||||||
release.isEdge &&
|
|
||||||
semver.lt(release.versionNumber, latest_release.versionNumber, true)
|
|
||||||
) {
|
|
||||||
return latest_release
|
|
||||||
}
|
|
||||||
|
|
||||||
// Once we've hit the major.minor.0 of the version that is the most recent
|
|
||||||
// patch, we know that we would have traversed any edge releases along the
|
|
||||||
// way, so check to see if we've hit that point yet.
|
|
||||||
const latest_release_major = semver.major(
|
|
||||||
latest_release.versionNumber,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
const latest_release_minor = semver.minor(
|
|
||||||
latest_release.versionNumber,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
const latest_release_minver = `${latest_release_major}.${latest_release_minor}.0`
|
|
||||||
if (semver.eq(release.versionNumber, latest_release_minver, true)) {
|
|
||||||
return latest_release
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We should never get here, but just in case
|
|
||||||
return {} as NextflowRelease
|
|
||||||
}
|
|
||||||
|
|
||||||
async function get_latest_edge_nextflow_release(
|
|
||||||
releases: AsyncGenerator<NextflowRelease>
|
|
||||||
): Promise<NextflowRelease> {
|
|
||||||
// Because we don't have to worry about crossing between edge and stable
|
|
||||||
// releases, we can just return the first edge release we come across
|
|
||||||
for await (const release of releases) {
|
|
||||||
if (release.isEdge) {
|
|
||||||
return release
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We should never get here, but just in case
|
|
||||||
return {} as NextflowRelease
|
|
||||||
}
|
|
||||||
|
|
||||||
async function get_latest_stable_nextflow_release(
|
|
||||||
releases: AsyncGenerator<NextflowRelease>
|
|
||||||
): Promise<NextflowRelease> {
|
|
||||||
// Because we don't have to worry about crossing between edge and stable
|
|
||||||
// releases, we can just return the first stable release we come across
|
|
||||||
for await (const release of releases) {
|
|
||||||
if (!release.isEdge) {
|
|
||||||
return release
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We should never get here, but just in case
|
|
||||||
return {} as NextflowRelease
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function get_nextflow_release(
|
export async function get_nextflow_release(
|
||||||
version: string,
|
version: string,
|
||||||
releases: AsyncGenerator<NextflowRelease>
|
releases: NextflowRelease[] | AsyncGenerator<NextflowRelease>
|
||||||
): Promise<NextflowRelease> {
|
): Promise<NextflowRelease> {
|
||||||
// First, check to see if we are using a "latest-*" version system, and return
|
|
||||||
// early
|
|
||||||
if (version === "latest-everything") {
|
|
||||||
return await get_latest_everything_nextflow_release(releases)
|
|
||||||
}
|
|
||||||
if (version === "latest-edge") {
|
|
||||||
return await get_latest_edge_nextflow_release(releases)
|
|
||||||
}
|
|
||||||
if (version === "latest" || version === "latest-stable") {
|
|
||||||
return await get_latest_stable_nextflow_release(releases)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The releases are sent in reverse chronological order
|
// The releases are sent in reverse chronological order
|
||||||
// If we are sent a numbered tag, then back through the list until we find
|
// If we are sent a numbered tag, then back through the list until we find
|
||||||
// a release that fulfils the requested version number
|
// a release that fulfils the requested version number
|
||||||
for await (const release of releases) {
|
for await (const release of releases) {
|
||||||
if (semver.satisfies(release.versionNumber, version, true)) {
|
if (semver.satisfies(release.version, version, true)) {
|
||||||
return release
|
return release
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,8 +27,8 @@ export async function install_nextflow(
|
||||||
release: NextflowRelease,
|
release: NextflowRelease,
|
||||||
get_all: boolean
|
get_all: boolean
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const url = get_all ? release.allBinaryURL : release.binaryURL
|
const url = get_all ? release.downloadUrlAll : release.downloadUrl
|
||||||
const version = release.versionNumber
|
const version = release.version
|
||||||
|
|
||||||
core.debug(`Downloading Nextflow from ${url}`)
|
core.debug(`Downloading Nextflow from ${url}`)
|
||||||
const nf_dl_path = await retry(
|
const nf_dl_path = await retry(
|
||||||
|
|
27
src/main.ts
27
src/main.ts
|
@ -11,10 +11,9 @@ import {
|
||||||
} from "./functions"
|
} from "./functions"
|
||||||
import { NextflowRelease } from "./nextflow-release"
|
import { NextflowRelease } from "./nextflow-release"
|
||||||
import {
|
import {
|
||||||
pull_latest_stable_release,
|
get_latest_nextflow_version,
|
||||||
pull_releases,
|
get_nextflow_versions
|
||||||
setup_octokit
|
} from "./nf-core-api-wrapper"
|
||||||
} from "./octokit-wrapper"
|
|
||||||
|
|
||||||
async function run(): Promise<void> {
|
async function run(): Promise<void> {
|
||||||
// CAPSULE_LOG leads to a bunch of boilerplate being output to the logs: turn
|
// CAPSULE_LOG leads to a bunch of boilerplate being output to the logs: turn
|
||||||
|
@ -22,33 +21,29 @@ async function run(): Promise<void> {
|
||||||
core.exportVariable("CAPSULE_LOG", "none")
|
core.exportVariable("CAPSULE_LOG", "none")
|
||||||
|
|
||||||
// Read in the arguments
|
// Read in the arguments
|
||||||
const token = core.getInput("token")
|
|
||||||
const version = core.getInput("version")
|
const version = core.getInput("version")
|
||||||
const get_all = core.getBooleanInput("all")
|
const get_all = core.getBooleanInput("all")
|
||||||
const cooldown = Number(core.getInput("cooldown"))
|
|
||||||
const max_retries = Number(core.getInput("max-retries"))
|
|
||||||
|
|
||||||
// Check the cache for the Nextflow version that matched last time
|
// Check the cache for the Nextflow version that matched last time
|
||||||
if (check_cache(version)) {
|
if (check_cache(version)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the API
|
|
||||||
const octokit = await setup_octokit(token, cooldown, max_retries)
|
|
||||||
|
|
||||||
// Get the release info for the desired release
|
// Get the release info for the desired release
|
||||||
let release = {} as NextflowRelease
|
let release = {} as NextflowRelease
|
||||||
let resolved_version = ""
|
let resolved_version = ""
|
||||||
try {
|
try {
|
||||||
if (version === "latest" || version === "latest-stable") {
|
if (version.includes("latest")) {
|
||||||
release = await pull_latest_stable_release(octokit)
|
let flavor = version.split("-")[1]
|
||||||
|
flavor = flavor ? flavor : "stable"
|
||||||
|
release = await get_latest_nextflow_version(flavor)
|
||||||
} else {
|
} else {
|
||||||
const release_iterator = pull_releases(octokit)
|
const nextflow_releases = await get_nextflow_versions()
|
||||||
release = await get_nextflow_release(version, release_iterator)
|
release = await get_nextflow_release(version, nextflow_releases)
|
||||||
}
|
}
|
||||||
resolved_version = release.versionNumber
|
resolved_version = release.version
|
||||||
core.info(
|
core.info(
|
||||||
`Input version '${version}' resolved to Nextflow ${release["name"]}`
|
`Input version '${version}' resolved to Nextflow ${release.version}`
|
||||||
)
|
)
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
|
|
|
@ -2,48 +2,9 @@
|
||||||
* Houses the pertinent data that GitHub exposes for each Nextflow release
|
* Houses the pertinent data that GitHub exposes for each Nextflow release
|
||||||
*/
|
*/
|
||||||
export type NextflowRelease = {
|
export type NextflowRelease = {
|
||||||
versionNumber: string
|
version: string
|
||||||
isEdge: boolean
|
isEdge: boolean
|
||||||
binaryURL: string
|
downloadUrl: string
|
||||||
allBinaryURL: string
|
downloadUrlAll: string
|
||||||
}
|
published_at?: string
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the raw OctoKit data into a structured NextflowRelease
|
|
||||||
* @param data A "release" data struct from OctoKit
|
|
||||||
* @returns `data` converted into a `NextflowRelease`
|
|
||||||
*/
|
|
||||||
export function nextflow_release(data: object): NextflowRelease {
|
|
||||||
const nf_release: NextflowRelease = {
|
|
||||||
versionNumber: data["tag_name"],
|
|
||||||
isEdge: data["prerelease"],
|
|
||||||
binaryURL: nextflow_bin_url(data, false),
|
|
||||||
allBinaryURL: nextflow_bin_url(data, true)
|
|
||||||
}
|
|
||||||
return nf_release
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the download URL of a Nextflow binary
|
|
||||||
* @param release A "release" data struct from OctoKit
|
|
||||||
* @param get_all Whether to return the url for the "all" variant of Nextflow
|
|
||||||
* @returns The URL of the Nextflow binary
|
|
||||||
*/
|
|
||||||
export function nextflow_bin_url(release: object, get_all: boolean): string {
|
|
||||||
const release_assets = release["assets"]
|
|
||||||
const all_asset = release_assets.filter((a: object) => {
|
|
||||||
return a["browser_download_url"].endsWith("-all")
|
|
||||||
})[0]
|
|
||||||
const regular_asset = release_assets.filter((a: object) => {
|
|
||||||
return a["name"] === "nextflow"
|
|
||||||
})[0]
|
|
||||||
|
|
||||||
const dl_asset = get_all ? all_asset : regular_asset
|
|
||||||
if (dl_asset) {
|
|
||||||
return dl_asset.browser_download_url
|
|
||||||
} else {
|
|
||||||
// Old pre-release versions of Nextflow didn't have an "all" variant. To
|
|
||||||
// avoid downstream errors, substitute the regular url here.
|
|
||||||
return regular_asset.browser_download_url
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
45
src/nf-core-api-wrapper.ts
Normal file
45
src/nf-core-api-wrapper.ts
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import { info } from "@actions/core"
|
||||||
|
import { downloadTool } from "@actions/tool-cache"
|
||||||
|
import retry from "async-retry"
|
||||||
|
import { readFileSync } from "fs"
|
||||||
|
|
||||||
|
import { NextflowRelease } from "./nextflow-release"
|
||||||
|
|
||||||
|
async function fetch_nextflow_versions_data(): Promise<object> {
|
||||||
|
// Occasionally the connection is reset for unknown reasons
|
||||||
|
// In those cases, retry the download
|
||||||
|
const versionsFile = await retry(
|
||||||
|
async () => {
|
||||||
|
return await downloadTool("https://nf-co.re/nextflow_version")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
retries: 5,
|
||||||
|
onRetry: (err: Error) => {
|
||||||
|
info(`Download of versions.json failed, trying again. Error: ${err}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return JSON.parse(readFileSync(versionsFile).toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get_nextflow_versions(): Promise<NextflowRelease[]> {
|
||||||
|
const version_dataset = await fetch_nextflow_versions_data()
|
||||||
|
const versions = version_dataset["versions"]
|
||||||
|
const nextflow_releases: NextflowRelease[] = []
|
||||||
|
// eslint-disable-next-line github/array-foreach
|
||||||
|
versions.array.forEach(element => {
|
||||||
|
const release = element as NextflowRelease
|
||||||
|
nextflow_releases.push(release)
|
||||||
|
})
|
||||||
|
return nextflow_releases
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get_latest_nextflow_version(
|
||||||
|
flavor: string
|
||||||
|
): Promise<NextflowRelease> {
|
||||||
|
const version_dataset = await fetch_nextflow_versions_data()
|
||||||
|
const latest_versions = version_dataset["latest"]
|
||||||
|
const latest_version = latest_versions[flavor] as NextflowRelease
|
||||||
|
return latest_version
|
||||||
|
}
|
|
@ -1,95 +0,0 @@
|
||||||
import * as core from "@actions/core"
|
|
||||||
import { getOctokitOptions, GitHub } from "@actions/github/lib/utils"
|
|
||||||
import { throttling } from "@octokit/plugin-throttling"
|
|
||||||
|
|
||||||
import { nextflow_release, NextflowRelease } from "./nextflow-release"
|
|
||||||
|
|
||||||
const NEXTFLOW_REPO = {
|
|
||||||
owner: "nextflow-io",
|
|
||||||
repo: "nextflow",
|
|
||||||
per_page: 100,
|
|
||||||
headers: {
|
|
||||||
"x-github-api-version": "2022-11-28"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function setup_octokit(
|
|
||||||
github_token: string,
|
|
||||||
cooldown = 60,
|
|
||||||
max_retries = 3
|
|
||||||
): Promise<InstanceType<typeof GitHub>> {
|
|
||||||
const throttledOctokit = GitHub.plugin(throttling)
|
|
||||||
let octokit = {} as InstanceType<typeof GitHub>
|
|
||||||
try {
|
|
||||||
octokit = new throttledOctokit(
|
|
||||||
getOctokitOptions(github_token, {
|
|
||||||
throttle: {
|
|
||||||
onRateLimit: (retryAfter, options, ok, retryCount) => {
|
|
||||||
ok.log.warn(
|
|
||||||
`Request quota exhausted for request ${options.method} ${options.url}`
|
|
||||||
)
|
|
||||||
|
|
||||||
if (retryCount < max_retries) {
|
|
||||||
ok.log.info(`Retrying after ${retryAfter} seconds!`)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSecondaryRateLimit: (retryAfter, options, ok, retryCount) => {
|
|
||||||
ok.log.warn(
|
|
||||||
`SecondaryRateLimit detected for request ${options.method} ${options.url}`
|
|
||||||
)
|
|
||||||
|
|
||||||
if (retryCount < max_retries) {
|
|
||||||
octokit.log.info(`Retrying after ${retryAfter} seconds!`)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fallbackSecondaryRateRetryAfter: cooldown
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
} catch (e: unknown) {
|
|
||||||
if (e instanceof Error) {
|
|
||||||
core.setFailed(
|
|
||||||
`Could not authenticate to GitHub Releases API with provided token\n${e.message}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return octokit
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function* pull_releases(
|
|
||||||
octokit: InstanceType<typeof GitHub>
|
|
||||||
): AsyncGenerator<NextflowRelease> {
|
|
||||||
const iterator = octokit.paginate.iterator(
|
|
||||||
octokit.rest.repos.listReleases,
|
|
||||||
NEXTFLOW_REPO
|
|
||||||
)
|
|
||||||
let item_index = 0
|
|
||||||
let release_items = []
|
|
||||||
|
|
||||||
/* eslint-disable-next-line @typescript-eslint/unbound-method */
|
|
||||||
const { next } = iterator[Symbol.asyncIterator]()
|
|
||||||
|
|
||||||
let request = await next()
|
|
||||||
release_items = request.value.data
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (item_index > release_items.length) {
|
|
||||||
request = await next()
|
|
||||||
release_items = request.value.data
|
|
||||||
item_index = 0
|
|
||||||
}
|
|
||||||
yield nextflow_release(release_items[item_index++])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function pull_latest_stable_release(
|
|
||||||
ok: InstanceType<typeof GitHub>
|
|
||||||
): Promise<NextflowRelease> {
|
|
||||||
const { data: stable_release } = await ok.rest.repos.getLatestRelease(
|
|
||||||
NEXTFLOW_REPO
|
|
||||||
)
|
|
||||||
|
|
||||||
return nextflow_release(stable_release)
|
|
||||||
}
|
|
|
@ -1,6 +1,7 @@
|
||||||
import test from "ava" // eslint-disable-line import/no-unresolved
|
import test from "ava" //eslint-disable-line import/no-unresolved
|
||||||
|
import { execSync } from "child_process"
|
||||||
|
|
||||||
import * as functions from "../src/functions"
|
import { get_nextflow_release, install_nextflow } from "../src/functions"
|
||||||
import { NextflowRelease } from "../src/nextflow-release"
|
import { NextflowRelease } from "../src/nextflow-release"
|
||||||
|
|
||||||
// The Nextflow releases we are going to use for testing follow a regular
|
// The Nextflow releases we are going to use for testing follow a regular
|
||||||
|
@ -9,10 +10,10 @@ import { NextflowRelease } from "../src/nextflow-release"
|
||||||
function nf_release_gen(version_number: string): NextflowRelease {
|
function nf_release_gen(version_number: string): NextflowRelease {
|
||||||
const is_edge = version_number.endsWith("-edge")
|
const is_edge = version_number.endsWith("-edge")
|
||||||
const release: NextflowRelease = {
|
const release: NextflowRelease = {
|
||||||
versionNumber: version_number,
|
version: version_number,
|
||||||
isEdge: is_edge,
|
isEdge: is_edge,
|
||||||
binaryURL: `https://github.com/nextflow-io/nextflow/releases/download/${version_number}/nextflow`,
|
downloadUrl: `https://github.com/nextflow-io/nextflow/releases/download/${version_number}/nextflow`,
|
||||||
allBinaryURL: `https://github.com/nextflow-io/nextflow/releases/download/${version_number}/nextflow-${version_number.replace(
|
downloadUrlAll: `https://github.com/nextflow-io/nextflow/releases/download/${version_number}/nextflow-${version_number.replace(
|
||||||
"v",
|
"v",
|
||||||
""
|
""
|
||||||
)}-all`
|
)}-all`
|
||||||
|
@ -20,95 +21,85 @@ function nf_release_gen(version_number: string): NextflowRelease {
|
||||||
return release
|
return release
|
||||||
}
|
}
|
||||||
|
|
||||||
// A mock set of Nextflow releases
|
const release_test_macro = test.macro(
|
||||||
async function* edge_is_newer(): AsyncGenerator<NextflowRelease> {
|
|
||||||
yield nf_release_gen("v23.09.1-edge")
|
|
||||||
yield nf_release_gen("v23.04.3")
|
|
||||||
yield nf_release_gen("v23.04.2")
|
|
||||||
}
|
|
||||||
|
|
||||||
async function* edge_is_older(): AsyncGenerator<NextflowRelease> {
|
|
||||||
yield nf_release_gen("v23.04.3")
|
|
||||||
yield nf_release_gen("v23.04.2")
|
|
||||||
yield nf_release_gen("v23.03.0-edge")
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The whole reason this action exists is to handle the cases where a final
|
|
||||||
release is the "bleeding edge" release, rather than the "edge" release, even
|
|
||||||
though that's what the name would imply. Therefore, we need to test that the
|
|
||||||
'latest-everything' parameter can find the correct one regardless of whether
|
|
||||||
an "edge" release or a stable release is the true latest
|
|
||||||
*/
|
|
||||||
const release_filter_macro = test.macro(
|
|
||||||
async (
|
async (
|
||||||
t,
|
t,
|
||||||
input_version: string,
|
input_version: string,
|
||||||
expected_version: string,
|
expected_version: string,
|
||||||
releases: AsyncGenerator<NextflowRelease>
|
is_edge_older: Boolean
|
||||||
) => {
|
) => {
|
||||||
const matched_release = await functions.get_nextflow_release(
|
const releases_set = [
|
||||||
|
nf_release_gen("v21.05.1-edge"),
|
||||||
|
nf_release_gen("v21.05.0-edge"),
|
||||||
|
nf_release_gen("v21.04.2"),
|
||||||
|
nf_release_gen("v21.04.1"),
|
||||||
|
nf_release_gen("v21.04.0"),
|
||||||
|
nf_release_gen("v21.03.0-edge")
|
||||||
|
]
|
||||||
|
const release_filtered = releases_set.splice(is_edge_older ? 2 : 0)
|
||||||
|
const matched_release = await get_nextflow_release(
|
||||||
input_version,
|
input_version,
|
||||||
releases
|
release_filtered
|
||||||
)
|
)
|
||||||
t.is(matched_release.versionNumber, expected_version)
|
t.is(matched_release.version, expected_version)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
"Latest-everything install with newer edge release",
|
"Major and minor version only (edge newer)",
|
||||||
release_filter_macro,
|
release_test_macro,
|
||||||
"latest-everything",
|
"v21.04",
|
||||||
"v23.09.1-edge",
|
"v21.04.2",
|
||||||
edge_is_newer()
|
false
|
||||||
)
|
)
|
||||||
test(
|
test(
|
||||||
"Latest-everything install with older edge release",
|
"Major and minor version only (edge older)",
|
||||||
release_filter_macro,
|
release_test_macro,
|
||||||
"latest-everything",
|
"v21.04",
|
||||||
"v23.04.3",
|
"v21.04.2",
|
||||||
edge_is_older()
|
true
|
||||||
)
|
)
|
||||||
test(
|
test(
|
||||||
"Latest-edge install with newer edge release",
|
"Major, minor, and patch version: latest patch (edge newer)",
|
||||||
release_filter_macro,
|
release_test_macro,
|
||||||
"latest-edge",
|
"v21.04.2",
|
||||||
"v23.09.1-edge",
|
"v21.04.2",
|
||||||
edge_is_newer()
|
false
|
||||||
)
|
)
|
||||||
test(
|
test(
|
||||||
"Latest-edge install with older edge release",
|
"Major, minor, and patch version: latest patch (edge older)",
|
||||||
release_filter_macro,
|
release_test_macro,
|
||||||
"latest-edge",
|
"v21.04.2",
|
||||||
"v23.03.0-edge",
|
"v21.04.2",
|
||||||
edge_is_older()
|
true
|
||||||
)
|
)
|
||||||
test(
|
test(
|
||||||
"Latest-stable install with newer edge release",
|
"Major, minor, and patch version: older patch (edge newer)",
|
||||||
release_filter_macro,
|
release_test_macro,
|
||||||
"latest",
|
"v21.04.1",
|
||||||
"v23.04.3",
|
"v21.04.1",
|
||||||
edge_is_newer()
|
false
|
||||||
)
|
)
|
||||||
test(
|
test(
|
||||||
"Latest-stable install with older edge release",
|
"Major, minor, and patch version: older patch (edge older)",
|
||||||
release_filter_macro,
|
release_test_macro,
|
||||||
"latest",
|
"v21.04.1",
|
||||||
"v23.04.3",
|
"v21.04.1",
|
||||||
edge_is_older()
|
true
|
||||||
)
|
)
|
||||||
test(
|
test(
|
||||||
"Fully versioned tag release",
|
"Edge release",
|
||||||
release_filter_macro,
|
release_test_macro,
|
||||||
"v23.04.2",
|
"v21.03.0-edge",
|
||||||
"v23.04.2",
|
"v21.03.0-edge",
|
||||||
edge_is_newer()
|
false
|
||||||
)
|
|
||||||
test(
|
|
||||||
"Partially versioned tag release",
|
|
||||||
release_filter_macro,
|
|
||||||
"v23.04",
|
|
||||||
"v23.04.3",
|
|
||||||
edge_is_newer()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
test.todo("install_nextflow")
|
test("Install Nextflow", async t => {
|
||||||
|
const release = nf_release_gen("v23.10.1")
|
||||||
|
const install_dir = await install_nextflow(release, false)
|
||||||
|
|
||||||
|
const version_output = execSync(`${install_dir}/nextflow -v`).toString()
|
||||||
|
const version_regex = /nextflow version 23\.10\.1.*/
|
||||||
|
t.regex(version_output, version_regex)
|
||||||
|
})
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
import * as github from "@actions/github"
|
|
||||||
import { GitHub } from "@actions/github/lib/utils"
|
|
||||||
import anyTest, { TestFn } from "ava" // eslint-disable-line import/no-unresolved
|
|
||||||
|
|
||||||
import { NextflowRelease } from "../src/nextflow-release"
|
|
||||||
import {
|
|
||||||
pull_latest_stable_release,
|
|
||||||
pull_releases
|
|
||||||
} from "../src/octokit-wrapper"
|
|
||||||
import { getToken } from "./utils"
|
|
||||||
|
|
||||||
const test = anyTest as TestFn<{
|
|
||||||
token: string
|
|
||||||
octokit: InstanceType<typeof GitHub>
|
|
||||||
}>
|
|
||||||
|
|
||||||
test.before(t => {
|
|
||||||
const first = true
|
|
||||||
const current_token = getToken(first)
|
|
||||||
t.context = {
|
|
||||||
token: current_token,
|
|
||||||
octokit: github.getOctokit(current_token)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
async function get_latest_release(
|
|
||||||
octokit: InstanceType<typeof GitHub>,
|
|
||||||
use_latest_api: boolean
|
|
||||||
): Promise<NextflowRelease> {
|
|
||||||
if (use_latest_api) {
|
|
||||||
return await pull_latest_stable_release(octokit)
|
|
||||||
} else {
|
|
||||||
const all_releases = pull_releases(octokit)
|
|
||||||
const first_response = await all_releases.next()
|
|
||||||
const first_release = first_response.value
|
|
||||||
? first_response.value
|
|
||||||
: ({} as NextflowRelease)
|
|
||||||
return first_release
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const version_macro = test.macro(
|
|
||||||
async (t, object_name: string, use_latest_api: boolean) => {
|
|
||||||
const latest_release = await get_latest_release(
|
|
||||||
t.context.octokit,
|
|
||||||
use_latest_api
|
|
||||||
)
|
|
||||||
t.assert(latest_release[object_name])
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
"OctoKit iterator returns semver-parsable version number",
|
|
||||||
version_macro,
|
|
||||||
"versionNumber",
|
|
||||||
false
|
|
||||||
)
|
|
||||||
test(
|
|
||||||
"OctoKit latest API returns semver-parable version number",
|
|
||||||
version_macro,
|
|
||||||
"versionNumber",
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
const binary_url_macro = test.macro(
|
|
||||||
async (t, get_all: boolean, use_latest_api: boolean) => {
|
|
||||||
const latest_release = await get_latest_release(
|
|
||||||
t.context.octokit,
|
|
||||||
use_latest_api
|
|
||||||
)
|
|
||||||
const url = get_all ? latest_release.allBinaryURL : latest_release.binaryURL
|
|
||||||
t.notThrows(() => new URL(url))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test("Nextflow binary URL from iterator valid", binary_url_macro, false, false)
|
|
||||||
test("Nextflow binary URL from latest API valid", binary_url_macro, false, true)
|
|
||||||
test(
|
|
||||||
"Nextflow all binary URL from iterator valid",
|
|
||||||
binary_url_macro,
|
|
||||||
true,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
test(
|
|
||||||
"Nextflow all binary URL from latest API valid",
|
|
||||||
binary_url_macro,
|
|
||||||
true,
|
|
||||||
true
|
|
||||||
)
|
|
|
@ -1,12 +0,0 @@
|
||||||
export function getToken(first: boolean): string {
|
|
||||||
const token = process.env["GITHUB_TOKEN"] || ""
|
|
||||||
if (!token && first) {
|
|
||||||
/* eslint-disable-next-line no-console */
|
|
||||||
console.warn(
|
|
||||||
"Skipping GitHub tests. Set $GITHUB_TOKEN to run REST client and GraphQL client tests"
|
|
||||||
)
|
|
||||||
first = false
|
|
||||||
}
|
|
||||||
|
|
||||||
return token
|
|
||||||
}
|
|
|
@ -6,7 +6,9 @@
|
||||||
"outDir": "./lib",
|
"outDir": "./lib",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"esModuleInterop": true
|
"esModuleInterop": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"lib": ["esnext", "dom"]
|
||||||
},
|
},
|
||||||
"include": ["src/**/*", "test/**/*"],
|
"include": ["src/**/*", "test/**/*"],
|
||||||
"exclude": ["node_modules", "**/*.test.ts"]
|
"exclude": ["node_modules", "**/*.test.ts"]
|
||||||
|
|
Loading…
Reference in a new issue