Compare commits

..

41 commits

Author SHA1 Message Date
Edmund Miller
0aeab8d284 style: Use double-quotes
Co-authored-by: ewels <ewels@users.noreply.github.com>
2022-11-17 19:32:25 +00:00
Edmund Miller
05aef69bc1 ci: Don't double run jobs 2022-11-17 19:32:25 +00:00
Edmund Miller
57e49670a9 ci: Create seperate dev workflow checks 2022-11-17 19:32:25 +00:00
Edmund Miller
46187cd528 test: Add main test from template
Doesn't work yet though
2022-11-17 19:32:25 +00:00
Edmund Miller
95a4337530 style: Add simple-import-sort and run 2022-11-17 19:32:25 +00:00
Edmund Miller
981cd4fc2e style: Set bracketSpacing to true 2022-11-17 19:32:25 +00:00
Edmund Miller
7c79f790e0 chore: Clean up typing in tests 2022-11-17 19:32:25 +00:00
Edmund Miller
c3424c52c4 chore: Clean up release sorting types 2022-11-17 19:32:25 +00:00
Edmund Miller
93ff0f7ef2 test: Write tests for release_data function 2022-11-17 19:32:25 +00:00
Edmund Miller
c19a8b88ef chore: handle octokit typing 2022-11-17 19:32:25 +00:00
Edmund Miller
7a0ec2a9b4 chore: Clean up a bunch of object references
https://medium.com/@kgdilipchandana/how-to-solve-error-ts2339-property-label-does-not-exist-on-type-in-angular-5-db01843ef6a
2022-11-17 19:32:25 +00:00
Edmund Miller
955a936296 chore: Clean up some catch errors 2022-11-17 19:32:25 +00:00
Edmund Miller
a14c1ba3a6 ci: Add build step 2022-11-17 19:32:25 +00:00
Edmund Miller
edde65cf1e build: lib => lib/src 2022-11-17 19:32:25 +00:00
Edmund Miller
ab89b450a6 chore: Remove eslint failure for ava import 2022-11-17 19:32:25 +00:00
Edmund Miller
3f231d1a10 ci: Update hoops to jump through 2022-11-17 19:32:25 +00:00
Edmund Miller
03b7b3e3d3 test: Remove main test for now 2022-11-17 19:32:25 +00:00
Edmund Miller
0541993f3c build: Update scripts to be more selective on files
And run on test files
2022-11-17 19:32:25 +00:00
Edmund Miller
205818f4d8 style: format-check => format:check 2022-11-17 19:32:25 +00:00
Edmund Miller
4ad727836d ci: Split up steps 2022-11-17 19:32:25 +00:00
Edmund Miller
aebdb951b0 style: Fix all eslint errors 2022-11-17 19:32:25 +00:00
Edmund Miller
ea5ce95ba5 test: Add basic call for release_data
I just want these for the typechecks
2022-11-17 19:32:25 +00:00
Edmund Miller
ed6b338f42 test: Add test for latest_stable_release_data 2022-11-17 19:32:25 +00:00
Edmund Miller
6cafaa8e2c test: Add initial check for all_nf_releases 2022-11-17 19:32:25 +00:00
Edmund Miller
40343dfa61 build: Add eslint-plugin-ava 2022-11-17 19:32:25 +00:00
Edmund Miller
3538f89b6f build: Add lint:fix script 2022-11-17 19:32:25 +00:00
Edmund Miller
024f69ce91 style: Run prettier on everything 2022-11-17 19:32:25 +00:00
Edmund Miller
8c63dff97b style: Set sort-imports to warn 2022-11-17 19:32:25 +00:00
Edmund Miller
b9a7fb9499 refactor: Move functions functions to their own file 2022-11-17 19:32:25 +00:00
Edmund Miller
db82c135bd build: Setup typescript tests
I don't know why I do this to myself. But it's done now.
https://github.com/OneSignal/OneSignal-Website-SDK
Yoinked the configs from here
2022-11-17 19:32:25 +00:00
Edmund Miller
78e7fadd20 build: npm init ava 2022-11-17 19:32:25 +00:00
Edmund Miller
c976e6b883 refactor: Move everything into a src/ dir 2022-11-17 19:32:25 +00:00
Edmund Miller
0401be4ed7 chore: Update package.json 2022-11-17 19:32:25 +00:00
Edmund Miller
147da60f8f style: Run prettier 2022-11-17 19:32:25 +00:00
Edmund Miller
9ec3bc304f build: Add eslint and prettier 2022-11-17 19:32:25 +00:00
Edmund Miller
48ae930859 build: Add types 2022-11-17 19:32:25 +00:00
Edmund Miller
1b07a3f172 build: Add ncc
npm run build works now without global install
2022-11-17 19:32:25 +00:00
Edmund Miller
16725d6eeb chore: Update author 2022-11-17 19:32:25 +00:00
Edmund Miller
80fb69cc4f chore: Update license to MIT and nf-core 2022-11-17 19:32:25 +00:00
Edmund Miller
39c8738968 docs: Add note about why this is a complicated action 2022-11-17 19:32:25 +00:00
Edmund Miller
70311f90c6 docs: Update path 2022-11-17 19:32:25 +00:00
23 changed files with 8366 additions and 218 deletions

3
.eslintignore Normal file
View file

@ -0,0 +1,3 @@
dist/
lib/
node_modules/

63
.eslintrc.json Normal file
View file

@ -0,0 +1,63 @@
{
"plugins": ["ava", "@typescript-eslint", "simple-import-sort"],
"extends": ["plugin:github/recommended", "plugin:ava/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 9,
"sourceType": "module",
"project": "./tsconfig.json"
},
"rules": {
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error",
"i18n-text/no-en": "off",
"eslint-comments/no-use": "off",
"import/no-namespace": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/explicit-member-accessibility": [
"error",
{ "accessibility": "no-public" }
],
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/array-type": "error",
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/ban-ts-comment": "error",
"camelcase": "off",
"@typescript-eslint/consistent-type-assertions": "error",
"@typescript-eslint/explicit-function-return-type": [
"error",
{ "allowExpressions": true }
],
"@typescript-eslint/func-call-spacing": ["error", "never"],
"@typescript-eslint/no-array-constructor": "error",
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-extraneous-class": "error",
"@typescript-eslint/no-for-in-array": "error",
"@typescript-eslint/no-inferrable-types": "error",
"@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/no-unnecessary-qualifier": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-useless-constructor": "error",
"@typescript-eslint/no-var-requires": "error",
"@typescript-eslint/prefer-for-of": "warn",
"@typescript-eslint/prefer-function-type": "warn",
"@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/promise-function-async": "error",
"@typescript-eslint/require-array-sort-compare": "error",
"@typescript-eslint/restrict-plus-operands": "error",
"semi": "off",
"sort-imports": "off",
"@typescript-eslint/semi": ["error", "never"],
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/unbound-method": "error"
},
"env": {
"node": true,
"es6": true
}
}

1
.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
dist/** -diff linguist-generated=true

View file

@ -2,8 +2,12 @@ name: Example builds
on: on:
push: push:
branches:
- master
pull_request: pull_request:
workflow_dispatch: workflow_dispatch:
release:
types: [published]
jobs: jobs:
test: test:
@ -30,8 +34,9 @@ jobs:
with: with:
node-version: 16 node-version: 16
cache: "npm" cache: "npm"
- run: npm i -g @vercel/ncc - run: npm ci
- run: npm ci && npm run build - run: npm run build
- run: npm run package
- uses: ./ - uses: ./
with: with:
version: ${{ matrix.nextflow_version }} version: ${{ matrix.nextflow_version }}

View file

@ -13,8 +13,9 @@ jobs:
with: with:
node-version: 16 node-version: 16
cache: "npm" cache: "npm"
- run: npm i -g @vercel/ncc - run: npm ci
- run: npm ci && npm run build - run: npm run build
- run: npm run package
- uses: JasonEtco/build-and-tag-action@v1 - uses: JasonEtco/build-and-tag-action@v1
env: env:
GITHUB_TOKEN: ${{ github.token }} GITHUB_TOKEN: ${{ github.token }}

27
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,27 @@
name: Run tests
on:
push:
branches:
- master
pull_request:
workflow_dispatch:
release:
types: [published]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
cache: "npm"
- run: npm ci
- run: npm run build
- run: npm run format:check
- run: npm run lint
- run: npm run package
# FIXME Token doesn't get passed correctly
# - run: npm run test

4
.gitignore vendored
View file

@ -128,3 +128,7 @@ dist
.yarn/build-state.yml .yarn/build-state.yml
.yarn/install-state.gz .yarn/install-state.gz
.pnp.* .pnp.*
# Ignore built ts files
__tests__/runner/*
lib/**/*

3
.prettierignore Normal file
View file

@ -0,0 +1,3 @@
dist/
lib/
node_modules/

10
.prettierrc.json Normal file
View file

@ -0,0 +1,10 @@
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": false,
"singleQuote": false,
"trailingComma": "none",
"bracketSpacing": true,
"arrowParens": "avoid"
}

View file

@ -37,8 +37,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- GitHub Actions workflow to test Nextflow installation and downstream usage - GitHub Actions workflow to test Nextflow installation and downstream usage
- Documentation and license files - Documentation and license files
[unreleased]: https://github.com/MillironX/setup-nextflow/compare/v1.2.0...HEAD [unreleased]: https://github.com/nf-core/setup-nextflow/compare/v1.2.0...HEAD
[1.2.0]: https://github.com/MillironX/setup-nextflow/compare/v1.1.1...v1.2.0 [1.2.0]: https://github.com/nf-core/setup-nextflow/compare/v1.1.1...v1.2.0
[1.1.0]: https://github.com/MillironX/setup-nextflow/compare/v1.0.1...v1.1.0 [1.1.0]: https://github.com/nf-core/setup-nextflow/compare/v1.0.1...v1.1.0
[1.0.1]: https://github.com/MillironX/setup-nextflow/compare/v1.0.0...v1.0.1 [1.0.1]: https://github.com/nf-core/setup-nextflow/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/MillironX/setup-nextflow/releases/tag/v1.0.0 [1.0.0]: https://github.com/nf-core/setup-nextflow/releases/tag/v1.0.0

22
LICENSE
View file

@ -1,7 +1,21 @@
Copyright 2022 Thomas A. Christensen II MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Copyright (c) 2022 nf-core
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,8 +1,8 @@
# Setup Nextflow for GitHub Actions # Setup Nextflow for GitHub Actions
[![Testing](https://github.com/MillironX/setup-nextflow/actions/workflows/example.yml/badge.svg)](https://github.com/MillironX/setup-nextflow/actions/workflows/example.yml) [![Testing](https://github.com/nf-core/setup-nextflow/actions/workflows/example.yml/badge.svg)](https://github.com/nf-core/setup-nextflow/actions/workflows/example.yml)
[![MIT License](https://img.shields.io/github/license/MillironX/setup-nextflow?logo=opensourceinitiative)](https://github.com/MillironX/setup-nextflow/blob/master/LICENSE) [![MIT License](https://img.shields.io/github/license/nf-core/setup-nextflow?logo=opensourceinitiative)](https://github.com/nf-core/setup-nextflow/blob/master/LICENSE)
[![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/MillironX/setup-nextflow?logo=github)](https://github.com/MillironX/setup-nextflow/releases/latest) [![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/nf-core/setup-nextflow?logo=github)](https://github.com/nf-core/setup-nextflow/releases/latest)
[![Get from GitHub Actions](https://img.shields.io/static/v1?label=actions&message=marketplace&color=green&logo=githubactions)](https://github.com/marketplace/actions/setup-nextflow) [![Get from GitHub Actions](https://img.shields.io/static/v1?label=actions&message=marketplace&color=green&logo=githubactions)](https://github.com/marketplace/actions/setup-nextflow)
An action to install [Nextflow](https://nextflow.io) into a GitHub Actions workflow and make it available for subsequent steps. An action to install [Nextflow](https://nextflow.io) into a GitHub Actions workflow and make it available for subsequent steps.
@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: MillironX/setup-nextflow@v1 - uses: nf-core/setup-nextflow@v1.2.0
- run: nextflow run ${GITHUB_WORKSPACE} - run: nextflow run ${GITHUB_WORKSPACE}
``` ```
@ -73,3 +73,20 @@ This action locates the releases based upon the GitHub API, and requires an acce
## Outputs ## Outputs
There are no outputs from this action. There are no outputs from this action.
## Why was this action made?
[Slack link](https://nfcore.slack.com/archives/CE56GDKN0/p1655210460795839)
You may be asking, why not just a few yaml lines?
```yaml
- name: Install Nextflow
env:
NXF_VER: ${{ matrix.NXF_VER }}
run: |
wget -qO- get.nextflow.io | bash
sudo mv nextflow /usr/local/bin/
```
The versioning. From the Nextflow install script you can't get `latest-edge` or `latest-everything` for example.

View file

@ -1,6 +1,6 @@
name: "Setup Nextflow" name: "Setup Nextflow"
description: "Install Nextflow and add it to the PATH" description: "Install Nextflow and add it to the PATH"
author: "Thomas A. Christensen II" author: "nf-core"
inputs: inputs:
version: version:
description: "The Nextflow version to download (if necessary) and use. Example: 21.10.3" description: "The Nextflow version to download (if necessary) and use. Example: 21.10.3"

180
index.ts
View file

@ -1,180 +0,0 @@
import * as core from "@actions/core";
import * as exec from "@actions/exec";
import * as fs from "fs";
import * as github from "@actions/github";
import * as tc from "@actions/tool-cache";
import retry = require("async-retry");
import semver = require("semver");
const NEXTFLOW_REPO = { owner: "nextflow-io", repo: "nextflow" };
async function all_nf_releases(ok) {
return await ok.paginate(
ok.rest.repos.listReleases,
NEXTFLOW_REPO,
(response) => response.data
);
}
async function latest_stable_release_data(ok) {
const { data: stable_release } = await ok.rest.repos.getLatestRelease(
NEXTFLOW_REPO
);
return stable_release;
}
async function release_data(version, ok) {
// Setup tag-based filtering
let filter = (r) => {
return semver.satisfies(r.tag_name, version, true);
};
// Check if the user passed a 'latest*' tag, and override filtering
// accordingly
if (version.includes("latest")) {
if (version.includes("-everything")) {
// No filtering
filter = (r) => {
return true;
};
} else if (version.includes("-edge")) {
filter = (r) => {
return r.tag_name.endsWith("-edge");
};
} else {
// This is special: passing 'latest' or 'latest-stable' allows us to use
// the latest stable GitHub release direct from the API
const stable_release = await latest_stable_release_data(ok);
return stable_release;
}
}
// Get all the releases
const all_releases = await all_nf_releases(ok);
let matching_releases = all_releases.filter(filter);
matching_releases.sort(function (x, y) {
semver.compare(x.tag_name, y.tag_name, true);
});
return matching_releases[0];
}
function nextflow_bin_url(release, get_all) {
const release_assets = release.assets;
const all_asset = release_assets.filter((a) => {
return a.browser_download_url.endsWith("-all");
})[0];
const regular_asset = release_assets.filter((a) => {
return a.name == "nextflow";
})[0];
const dl_asset = get_all ? all_asset : regular_asset;
return dl_asset.browser_download_url;
}
async function install_nextflow(url, version) {
core.debug(`Downloading Nextflow from ${url}`);
const nf_dl_path = await retry(
async (bail) => {
return await tc.downloadTool(url);
},
{
onRetry: (err) => {
core.debug(`Download of ${url} failed, trying again. Error ${err}`);
},
}
);
const temp_install_dir = fs.mkdtempSync(`nxf-${version}`);
const nf_path = `${temp_install_dir}/nextflow`;
fs.renameSync(nf_dl_path, nf_path);
fs.chmodSync(nf_path, "0711");
return temp_install_dir;
}
async function run() {
// Set environment variables
core.exportVariable("CAPSULE_LOG", "none");
// Read in the arguments
const token = core.getInput("token");
const version = core.getInput("version");
const get_all = core.getBooleanInput("all");
let resolved_version = "";
// Setup the API
let octokit = {};
try {
octokit = github.getOctokit(token);
} catch (e: any) {
core.setFailed(
`Could not authenticate to GitHub Releases API with provided token\n${e.message}`
);
}
// Get the release info for the desired release
let release: any = {};
try {
release = await release_data(version, octokit);
resolved_version = release.tag_name;
core.info(
`Input version '${version}' resolved to Nextflow ${release.name}`
);
} catch (e: any) {
core.setFailed(
`Could not retrieve Nextflow release matching ${version}.\n${e.message}`
);
}
// Get the download url for the desired release
let url = "";
try {
url = nextflow_bin_url(release, get_all);
core.info(`Preparing to download from ${url}`);
} catch (e: any) {
core.setFailed(`Could not parse the download URL\n${e.message}`);
}
try {
// Download Nextflow and add it to path
let nf_path = "";
nf_path = tc.find("nextflow", resolved_version);
if (!nf_path) {
core.debug(`Could not find Nextflow ${resolved_version} in cache`);
const nf_install_path = await install_nextflow(url, resolved_version);
nf_path = await tc.cacheDir(
nf_install_path,
"nextflow",
resolved_version
);
core.debug(`Added Nextflow to cache: ${nf_path}`);
fs.rmdirSync(nf_install_path, { recursive: true });
} else {
core.debug(`Using cached version of Nextflow: ${nf_path}`);
}
core.addPath(nf_path);
core.info(`Downloaded \`nextflow\` to ${nf_path} and added to PATH`);
} catch (e: any) {
core.setFailed(e.message);
}
// Run Nextflow so it downloads its dependencies
try {
const nf_exit_code = await exec.exec("nextflow", ["help"])
} catch (e: any) {
core.warning("Nextflow appears to have installed correctly, but an error was thrown while running it.")
}
}
run();

7832
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,49 @@
{ {
"name": "install-nextflow-action", "name": "install-nextflow-action",
"version": "1.2.0", "version": "1.2.0",
"description": "", "description": "An action to install Nextflow into a GitHub Actions workflow and make it available for subsequent steps.",
"main": "dist/index.js", "main": "lib/src/main.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "build": "tsc",
"build": "ncc build index.ts --license LICENSE" "format": "prettier --write '**/*.{ts,js,yml,md,json}'",
"format:check": "prettier --check '**/*.{ts,js,yml,md,json}'",
"lint": "eslint {src,test}/**/*.ts",
"lint:fix": "eslint --fix {src,test}/**/*.ts",
"package": "ncc build --source-map --license LICENSE",
"test": "ava",
"all": "npm run build && npm run format && npm run lint && npm run package && npm test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/nf-core/setup-nextflow.git"
},
"keywords": [
"actions",
"node",
"setup"
],
"author": "nf-core",
"ava": {
"extensions": [
"ts"
],
"rewritePaths": {
"src/": "lib/"
},
"require": [
"ts-node/register/transpile-only"
],
"files": [
"test/**/*.ts",
"!test/utils.ts"
],
"source": [
"src/**/*.ts"
],
"concurrency": 1,
"serial": true,
"powerAssert": true
}, },
"keywords": [],
"author": "",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^1.8.2", "@actions/core": "^1.8.2",
@ -20,7 +55,22 @@
"semver": "^7.3.7" "semver": "^7.3.7"
}, },
"devDependencies": { "devDependencies": {
"@ava/typescript": "^3.0.1",
"@tsconfig/node16": "^1.0.3", "@tsconfig/node16": "^1.0.3",
"@types/async-retry": "^1.4.5",
"@types/node": "^18.11.0",
"@types/semver": "^7.3.13",
"@typescript-eslint/parser": "^5.42.1",
"@vercel/ncc": "^0.34.0",
"ava": "^5.0.1",
"eslint": "^8.27.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-ava": "^13.2.0",
"eslint-plugin-github": "^4.4.1",
"eslint-plugin-jest": "^27.1.5",
"eslint-plugin-simple-import-sort": "^8.0.0",
"prettier": "^2.7.1",
"ts-node": "^10.9.1",
"typescript": "^4.7.3" "typescript": "^4.7.3"
} }
} }

113
src/functions.ts Normal file
View file

@ -0,0 +1,113 @@
import * as core from "@actions/core"
import { GitHub } from "@actions/github/lib/utils"
import * as tc from "@actions/tool-cache"
import retry from "async-retry"
import * as fs from "fs"
import semver from "semver"
const NEXTFLOW_REPO = { owner: "nextflow-io", repo: "nextflow" }
// HACK Private but I want to test this
export async function all_nf_releases(
ok: InstanceType<typeof GitHub>
): Promise<object[]> {
return await ok.paginate(
ok.rest.repos.listReleases,
NEXTFLOW_REPO,
response => response.data
)
}
// HACK Private but I want to test this
export async function latest_stable_release_data(
ok: InstanceType<typeof GitHub>
): Promise<object> {
const { data: stable_release } = await ok.rest.repos.getLatestRelease(
NEXTFLOW_REPO
)
return stable_release
}
export async function release_data(
version: string,
ok: InstanceType<typeof GitHub>
): Promise<object> {
// Setup tag-based filtering
let filter = (r: object): boolean => {
return semver.satisfies(r["tag_name"], version, true)
}
// Check if the user passed a 'latest*' tag, and override filtering
// accordingly
if (version.includes("latest")) {
if (version.includes("-everything")) {
// No filtering
// eslint-disable-next-line @typescript-eslint/no-unused-vars
filter = (r: object) => {
return true
}
} else if (version.includes("-edge")) {
filter = r => {
return r["tag_name"].endsWith("-edge")
}
} else {
// This is special: passing 'latest' or 'latest-stable' allows us to use
// the latest stable GitHub release direct from the API
const stable_release = await latest_stable_release_data(ok)
return stable_release
}
}
// Get all the releases
const all_releases: object[] = await all_nf_releases(ok)
const matching_releases = all_releases.filter(filter)
matching_releases.sort((x, y) => {
// HACK IDK why the value flip is necessary with the return
return semver.compare(x["tag_name"], y["tag_name"], true) * -1
})
return matching_releases[0]
}
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
return dl_asset.browser_download_url
}
export async function install_nextflow(
url: string,
version: string
): Promise<string> {
core.debug(`Downloading Nextflow from ${url}`)
const nf_dl_path = await retry(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async bail => {
return await tc.downloadTool(url)
},
{
onRetry: err => {
core.debug(`Download of ${url} failed, trying again. Error ${err}`)
}
}
)
const temp_install_dir = fs.mkdtempSync(`nxf-${version}`)
const nf_path = `${temp_install_dir}/nextflow`
fs.renameSync(nf_dl_path, nf_path)
fs.chmodSync(nf_path, "0711")
return temp_install_dir
}

99
src/main.ts Normal file
View file

@ -0,0 +1,99 @@
import * as core from "@actions/core"
import * as exec from "@actions/exec"
import * as github from "@actions/github"
import { GitHub } from "@actions/github/lib/utils"
import * as tc from "@actions/tool-cache"
import * as fs from "fs"
import { install_nextflow, nextflow_bin_url, release_data } from "./functions"
async function run(): Promise<void> {
// Set environment variables
core.exportVariable("CAPSULE_LOG", "none")
// Read in the arguments
const token = core.getInput("token")
const version = core.getInput("version")
const get_all = core.getBooleanInput("all")
let resolved_version = ""
// Setup the API
let octokit: InstanceType<typeof GitHub> | undefined
try {
octokit = github.getOctokit(token)
} catch (e: unknown) {
if (e instanceof Error) {
core.setFailed(
`Could not authenticate to GitHub Releases API with provided token\n${e.message}`
)
}
}
// Get the release info for the desired release
let release = {}
try {
if (octokit !== undefined) {
release = await release_data(version, octokit)
}
resolved_version = release["tag_name"]
core.info(
`Input version '${version}' resolved to Nextflow ${release["name"]}`
)
} catch (e: unknown) {
if (e instanceof Error) {
core.setFailed(
`Could not retrieve Nextflow release matching ${version}.\n${e.message}`
)
}
}
// Get the download url for the desired release
let url = ""
try {
url = nextflow_bin_url(release, get_all)
core.info(`Preparing to download from ${url}`)
} catch (e: unknown) {
if (e instanceof Error) {
core.setFailed(`Could not parse the download URL\n${e.message}`)
}
}
try {
// Download Nextflow and add it to path
let nf_path = ""
nf_path = tc.find("nextflow", resolved_version)
if (!nf_path) {
core.debug(`Could not find Nextflow ${resolved_version} in cache`)
const nf_install_path = await install_nextflow(url, resolved_version)
nf_path = await tc.cacheDir(nf_install_path, "nextflow", resolved_version)
core.debug(`Added Nextflow to cache: ${nf_path}`)
fs.rmdirSync(nf_install_path, { recursive: true })
} else {
core.debug(`Using cached version of Nextflow: ${nf_path}`)
}
core.addPath(nf_path)
core.info(`Downloaded \`nextflow\` to ${nf_path} and added to PATH`)
} catch (e: unknown) {
if (e instanceof Error) {
core.setFailed(e.message)
}
}
// Run Nextflow so it downloads its dependencies
try {
await exec.exec("nextflow", ["help"])
} catch (e: unknown) {
if (e instanceof Error) {
core.warning(
"Nextflow appears to have installed correctly, but an error was thrown while running it."
)
}
}
}
run()

36
test/functions.ts Normal file
View file

@ -0,0 +1,36 @@
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 * as functions from "../src/functions"
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)
}
})
test("all_nf_releases", async t => {
const result = await functions.all_nf_releases(t.context["octokit"])
t.is(typeof result, "object")
})
test("lastest_stable_release_data", async t => {
const result = await functions.latest_stable_release_data(
t.context["octokit"]
)
t.is(typeof result, "object")
t.is(result["tag_name"], "v22.10.2")
})
test.todo("nextflow_bin_url")
test.todo("install_nextflow")

17
test/main.ts Normal file
View file

@ -0,0 +1,17 @@
import test from "ava" // eslint-disable-line import/no-unresolved
import * as cp from "child_process"
import * as path from "path"
import * as process from "process"
// eslint-disable-next-line ava/no-skip-test
test.skip("test runs", t => {
process.env["INPUT_VERSION"] = "v22.10.2"
const np = process.execPath
const ip = path.join(__dirname, "..", "lib", "src", "main.js")
const options: cp.ExecFileSyncOptions = {
env: process.env
}
// eslint-disable-next-line no-console
console.log(cp.execFileSync(np, [ip], options).toString())
t.pass()
})

30
test/releasedata.ts Normal file
View file

@ -0,0 +1,30 @@
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 { release_data } from "../src/functions"
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)
}
})
const macro = test.macro(async (t, version: string, expected: string) => {
const result = await release_data(version, t.context["octokit"])
t.is(result["tag_name"], expected)
})
test("hard version", macro, "v22.10.2", "v22.10.2")
test("latest-stable", macro, "latest-stable", "v22.10.2")
test("latest-edge", macro, "latest-edge", "v22.09.7-edge")
test("latest-everything", macro, "latest-everything", "v22.10.2")

12
test/utils.ts Normal file
View file

@ -0,0 +1,12 @@
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
}

View file

@ -3,8 +3,11 @@
"compilerOptions": { "compilerOptions": {
"target": "es6", "target": "es6",
"module": "commonjs", "module": "commonjs",
"outDir": "./lib",
"strict": true, "strict": true,
"noImplicitAny": false, "noImplicitAny": false,
"esModuleInterop": true, "esModuleInterop": true
} },
"include": ["src/**/*", "test/**/*"],
"exclude": ["node_modules", "**/*.test.ts"]
} }