Node CI #3281
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# docs: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions | |
name: Node CI | |
on: | |
push: | |
branches: [ main ] | |
tags: [ "v*" ] # have tools scan this stable version | |
pull_request: | |
workflow_dispatch: | |
schedule: | |
# schedule daily tests, since dependencies are not intended to be locked | |
# this means: at 23:42 every day | |
- cron: '42 23 * * *' | |
concurrency: | |
group: ${{ github.workflow }}-${{ github.ref }} | |
cancel-in-progress: true | |
env: | |
NODE_ACTIVE_LTS: "20" # see https://nodejs.org/en/about/releases/ | |
REPORTS_DIR: "CI_reports" | |
TESTS_REPORTS_ARTIFACT: tests-reports | |
STANDARD_REPORTS_ARTIFACT: cs-reports | |
CJL_TEST_UPDATE_SNAPSHOTS: '' # set to a string that nodeJS would not evaluate to TRUE - which can only be an empty string | |
jobs: | |
build: | |
name: build ${{ matrix.target }} | |
runs-on: "ubuntu-latest" | |
strategy: | |
fail-fast: false | |
matrix: | |
target: | |
- node | |
- web | |
- d | |
timeout-minutes: 10 | |
steps: | |
- name: Checkout | |
# see https://github.com/actions/checkout | |
uses: actions/checkout@v4 | |
- name: Setup Node.js ${{ env.NODE_ACTIVE_LTS }} | |
# see https://github.com/actions/setup-node | |
uses: actions/setup-node@v4 | |
with: | |
node-version: ${{ env.NODE_ACTIVE_LTS }} | |
# cache: "npm" | |
# cache-dependency-path: "**/package-lock.json" | |
- name: setup project | |
run: npm i --ignore-scripts --include=optional --loglevel=silly | |
- name: build ${{ matrix.target }} | |
run: npm run build:${{ matrix.target }} | |
- name: artifact build result | |
# see https://github.com/actions/upload-artifact | |
uses: actions/upload-artifact@v4 | |
with: | |
name: dist.${{ matrix.target }} | |
path: dist.${{ matrix.target }} | |
if-no-files-found: error | |
test-lint: | |
name: test lint | |
runs-on: ubuntu-latest | |
timeout-minutes: 10 | |
steps: | |
- name: Checkout | |
# see https://github.com/actions/checkout | |
uses: actions/checkout@v4 | |
- name: Setup Node.js ${{ env.NODE_ACTIVE_LTS }} | |
# see https://github.com/actions/setup-node | |
uses: actions/setup-node@v4 | |
with: | |
node-version: ${{ env.NODE_ACTIVE_LTS }} | |
# cache: "npm" | |
# cache-dependency-path: "**/package-lock.json" | |
- name: setup project | |
run: npm i --ignore-scripts --include=optional --loglevel=silly | |
- name: test | |
run: npm run test:lint | |
test-standard: | |
name: test standard | |
runs-on: ubuntu-latest | |
timeout-minutes: 10 | |
steps: | |
- name: Checkout | |
# see https://github.com/actions/checkout | |
uses: actions/checkout@v4 | |
- name: Setup Node.js ${{ env.NODE_ACTIVE_LTS }} | |
# see https://github.com/actions/setup-node | |
uses: actions/setup-node@v4 | |
with: | |
node-version: ${{ env.NODE_ACTIVE_LTS }} | |
# cache: "npm" | |
# cache-dependency-path: "**/package-lock.json" | |
- name: setup project | |
run: | | |
echo "::group::install deps" | |
npm i --ignore-scripts --loglevel=silly | |
echo "::endgroup::" | |
echo "::group::install docs-gen deps" | |
npm --prefix tools/docs-gen i --ignore-scripts --loglevel=silly | |
echo "::endgroup::" | |
echo "::group::install code-style deps" | |
npm --prefix tools/code-style i --ignore-scripts --loglevel=silly | |
echo "::endgroup::" | |
- name: make reports dir | |
run: mkdir -p "$REPORTS_DIR" | |
- name: test | |
run: > | |
npm run -- test:standard | |
--format checkstyle | |
--output-file "$PWD/$REPORTS_DIR/eslint.xml" | |
- name: Publish Checkstyle report | |
# see https://github.com/Juuxel/publish-checkstyle-report | |
uses: Juuxel/publish-checkstyle-report@v1 | |
if: ${{ failure() || success() }} | |
with: | |
reports: ${{ env.REPORTS_DIR }}/eslint.xml | |
- name: artifact build result | |
# see https://github.com/actions/upload-artifact | |
uses: actions/upload-artifact@v4 | |
if: ${{ failure() }} | |
with: | |
name: ${{ env.STANDARD_REPORTS_ARTIFACT }} | |
path: ${{ env.REPORTS_DIR }} | |
if-no-files-found: error | |
test-node: | |
needs: [ 'build' ] | |
name: test node (${{ matrix.node-version }}, ${{ matrix.os }}) | |
runs-on: ${{ matrix.os }} | |
strategy: | |
fail-fast: false | |
matrix: | |
node-version: | |
# action based on https://github.com/actions/node-versions/releases | |
# see also: https://nodejs.org/en/about/releases/ | |
- "22" # Current | |
- "20" # active LTS | |
- "18" | |
- "16" | |
- "14" | |
- "14.0.0" # lowest supported | |
os: | |
- ubuntu-latest | |
- macos-13 # macos-latest has issues with node14 | |
- windows-latest | |
timeout-minutes: 10 | |
steps: | |
- name: Checkout | |
# see https://github.com/actions/checkout | |
uses: actions/checkout@v4 | |
- name: Setup Node.js ${{ matrix.node-version }} | |
# see https://github.com/actions/setup-node | |
uses: actions/setup-node@v4 | |
with: | |
node-version: ${{ matrix.node-version }} | |
# do not use caching, use fresh version always, since some deps are not pinned -- since this is a library. | |
- name: bump npm | |
shell: bash | |
env: | |
NODE_VERSION: '${{ matrix.node-version }}' | |
run: | |
case "$NODE_VERSION" in | |
'20' | '18') | |
npm i -g npm@^10 | |
;; | |
'16') | |
npm i -g npm@^9 | |
;; | |
'14') | |
npm i -g npm@^8 | |
;; | |
'14.0.0') | |
npm i -g npm@^7 | |
;; | |
esac | |
- name: setup project | |
shell: bash | |
env: | |
NODE_VERSION: '${{ matrix.node-version }}' | |
run: | | |
set -ex | |
# for the purpose of testing strange setups, | |
# we need to craft compatible versions by hand, | |
# and might utilize `npm_config_engine_strict=false` | |
dev_constraints=' npm-run-all2 c8 mocha fast-glob rimraf ' | |
# as long as npm cannot auto-resolve engine-constraints, we need to help here | |
case "$NODE_VERSION" in | |
'16') | |
dev_constraints="${dev_constraints/ c8 / c8@^9 }" | |
dev_constraints="${dev_constraints/ rimraf / rimraf@^4 }" | |
export npm_config_engine_strict=false | |
;; | |
'14') | |
dev_constraints="${dev_constraints/ c8 / c8@^9 }" | |
dev_constraints="${dev_constraints/ npm-run-all2 / npm-run-all2@^5 }" | |
dev_constraints="${dev_constraints/ rimraf / rimraf@^4 }" | |
export npm_config_engine_strict=false | |
;; | |
'14.0.0') | |
dev_constraints="${dev_constraints/ c8 / c8@^8 }" | |
dev_constraints="${dev_constraints/ npm-run-all2 / npm-run-all2@^5 }" | |
dev_constraints="${dev_constraints/ rimraf / rimraf@^4 }" | |
export npm_config_engine_strict=false | |
;; | |
esac | |
echo "::group::install prod" | |
npm_config_engine_strict=false npm i --ignore-scripts --include=optional --omit=dev --only=prod --production --loglevel=silly | |
echo "::endgroup::" | |
echo "::group::install dev" | |
npm i --ignore-scripts --loglevel=silly --no-save $dev_constraints | |
echo "::endgroup::" | |
echo "::group::rebuild libxmljs2" | |
## rebuild deps for which scripts were ignored, or partially installed - since "ignore-scripts" was used | |
npm rebuild --loglevel=silly libxmljs2 || npm uninstall --no-save libxmljs2 || true | |
echo "::endgroup::" | |
- name: fetch build artifact | |
# see https://github.com/actions/download-artifact | |
uses: actions/download-artifact@v4 | |
with: | |
name: dist.node | |
path: dist.node | |
- name: test | |
run: > | |
npm run -- test:node | |
${{ startsWith(matrix.node-version, env.NODE_ACTIVE_LTS) && ' --forbid-pending' || '' }} | |
- name: collect coverage | |
if: ${{ failure() || success() }} | |
run: > | |
node -- node_modules/c8/bin/c8.js | |
report | |
--reporter clover | |
--reports-dir '${{ env.REPORTS_DIR }}/coverage/${{ matrix.os }}_node${{ matrix.node-version }}' | |
- name: artifact test reports | |
if: ${{ ! cancelled() }} | |
# see https://github.com/actions/upload-artifact | |
uses: actions/upload-artifact@v4 | |
with: | |
name: '${{ env.TESTS_REPORTS_ARTIFACT }}_regular_${{ matrix.os }}_node${{ matrix.node-version }}' | |
path: ${{ env.REPORTS_DIR }} | |
# test-web: | |
# TODO via https://github.com/CycloneDX/cyclonedx-javascript-library/issues/51 | |
test-omit-optional-deps: | |
needs: [ 'build' ] | |
name: test w/o optional dependencies | |
runs-on: ubuntu-latest | |
steps: | |
- name: Checkout | |
# see https://github.com/actions/checkout | |
uses: actions/checkout@v4 | |
- name: Setup Node.js ${{ env.NODE_ACTIVE_LTS }} | |
# see https://github.com/actions/setup-node | |
uses: actions/setup-node@v4 | |
with: | |
node-version: ${{ env.NODE_ACTIVE_LTS }} | |
- name: setup library | |
run: | | |
set -ex | |
echo "::group::install prod" | |
npm i --ignore-scripts --omit=optional --omit=dev --loglevel=silly | |
echo "::endgroup::" | |
echo "::endgroup::install dev" | |
## install the needed dev-deps | |
npm i --ignore-scripts --omit=optional --no-save --loglevel=silly mocha c8 npm-run-all2 fast-glob | |
echo "::endgroup::" | |
- name: fetch build artifact | |
# see https://github.com/actions/download-artifact | |
uses: actions/download-artifact@v4 | |
with: | |
name: dist.node | |
path: dist.node | |
- name: test | |
run: npm run test:node | |
- name: collect coverage | |
if: ${{ failure() || success() }} | |
run: > | |
npm exec -- c8 report | |
--reporter clover | |
--reports-dir '${{ env.REPORTS_DIR }}/coverage/no-optional-deps' | |
- name: artifact test reports | |
if: ${{ ! cancelled() }} | |
# see https://github.com/actions/upload-artifact | |
uses: actions/upload-artifact@v4 | |
with: | |
name: '${{ env.TESTS_REPORTS_ARTIFACT }}_no-opt_${{ matrix.os }}_node${{ matrix.node-version }}' | |
path: ${{ env.REPORTS_DIR }} | |
report-coverage: | |
name: Publish test coverage | |
needs: | |
- test-node | |
- test-omit-optional-deps | |
runs-on: ubuntu-latest | |
timeout-minutes: 5 | |
steps: | |
- name: fetch test artifacts | |
# see https://github.com/actions/download-artifact | |
uses: actions/download-artifact@v4 | |
with: | |
pattern: '${{ env.TESTS_REPORTS_ARTIFACT }}_*' | |
merge-multiple: true | |
path: ${{ env.REPORTS_DIR }} | |
- name: Run codacy-coverage-reporter | |
env: | |
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }} | |
## see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-using-secrets | |
if: ${{ env.CODACY_PROJECT_TOKEN != '' }} | |
# see https://github.com/codacy/codacy-coverage-reporter-action | |
uses: codacy/codacy-coverage-reporter-action@v1 | |
with: | |
project-token: ${{ env.CODACY_PROJECT_TOKEN }} | |
coverage-reports: ${{ env.REPORTS_DIR }}/coverage/*/* | |
examples-JS: | |
needs: [ 'build' ] | |
name: example JS ${{ matrix.js-type }} | |
runs-on: ubuntu-latest | |
strategy: | |
fail-fast: false | |
matrix: | |
js-type: [ 'cjs', 'mjs' ] | |
env: | |
EXAMPLE_DIR: examples/node/javascript | |
steps: | |
- name: Checkout | |
# see https://github.com/actions/checkout | |
uses: actions/checkout@v4 | |
- name: Setup Node.js ${{ env.NODE_ACTIVE_LTS }} | |
# see https://github.com/actions/setup-node | |
uses: actions/setup-node@v4 | |
with: | |
node-version: ${{ env.NODE_ACTIVE_LTS }} | |
- name: fetch build artifact 'node' | |
# see https://github.com/actions/download-artifact | |
uses: actions/download-artifact@v4 | |
with: | |
name: dist.node | |
path: dist.node | |
- name: setup library | |
run: | | |
set -ex | |
echo "::group::install" | |
npm i --ignore-scripts --omit=dev --include=optional --loglevel=silly | |
echo "::endgroup::" | |
echo "::group::setup libxml2" | |
## rebuild deps for which scripts were ignored, or partially installed - since "ignore-scripts" was used | |
npm rebuild --loglevel=silly libxmljs2 || npm uninstall --no-save libxmljs2 | |
echo "::endgroup::" | |
- name: setup example project | |
run: npm i --no-save --loglevel=silly | |
working-directory: ${{ env.EXAMPLE_DIR }} | |
- name: run example | |
run: node -- 'example.${{ matrix.js-type }}' | |
working-directory: ${{ env.EXAMPLE_DIR }} | |
example-TS: | |
needs: [ 'build' ] | |
name: example TS${{ matrix.typescript-version }} ${{ matrix.js-type }} | |
runs-on: ubuntu-latest | |
strategy: | |
fail-fast: false | |
matrix: | |
typescript-version: | |
- '^5' # latest 5.X | |
- '^4' # latest 4.X | |
js-type: [ 'cjs', 'mjs' ] | |
include: | |
- # lowest reasonable number that works | |
typescript-version: '^3.8' | |
nodeTypes-version: '^14' | |
js-type: 'cjs' | |
env: | |
EXAMPLE_DIR: examples/node/typescript/example.${{ matrix.js-type }} | |
timeout-minutes: 10 | |
steps: | |
- name: Checkout | |
# see https://github.com/actions/checkout | |
uses: actions/checkout@v4 | |
- name: Setup Node.js ${{ env.NODE_ACTIVE_LTS }} | |
# see https://github.com/actions/setup-node | |
uses: actions/setup-node@v4 | |
with: | |
node-version: ${{ env.NODE_ACTIVE_LTS }} | |
- name: fetch build artifact 'd' | |
# see https://github.com/actions/download-artifact | |
uses: actions/download-artifact@v4 | |
with: | |
name: dist.d | |
path: dist.d | |
- name: fetch build artifact 'node' | |
# see https://github.com/actions/download-artifact | |
uses: actions/download-artifact@v4 | |
with: | |
name: dist.node | |
path: dist.node | |
- name: setup library | |
run: | | |
set -ex | |
echo "::group::install" | |
npm i --ignore-scripts --omit=dev --include=optional --loglevel=silly | |
echo "::endgroup::" | |
echo "::group::setup libxml2" | |
## rebuild deps for which scripts were ignored, or partially installed - since "ignore-scripts" was used | |
npm rebuild --loglevel=silly libxmljs2 || npm uninstall --no-save libxmljs2 | |
echo "::endgroup::" | |
- name: setup example project | |
run: npm i --no-save --loglevel=silly 'typescript@${{ matrix.typescript-version }}' | |
working-directory: ${{ env.EXAMPLE_DIR }} | |
- name: get TS-version | |
id: ts_version | |
run: node -p '`v=ts${require("typescript").version.split(".",2).join(".")}`' >> "$GITHUB_OUTPUT" | |
working-directory: ${{ env.EXAMPLE_DIR }} | |
- name: adjust @types/node version | |
run: npm i --no-save --loglevel=silly '@types/node@${{ matrix.nodeTypes-version || steps.ts_version.outputs.v}}' | |
working-directory: ${{ env.EXAMPLE_DIR }} | |
- name: build example | |
run: npm run build | |
working-directory: ${{ env.EXAMPLE_DIR }} | |
- name: run example | |
run: npm run example | |
working-directory: ${{ env.EXAMPLE_DIR }} | |
examples-Web: | |
needs: [ 'build' ] | |
name: example Web ${{ matrix.packer }} | |
runs-on: ubuntu-latest | |
strategy: | |
fail-fast: false | |
matrix: | |
packer: | |
- parcel | |
- webpack | |
env: | |
EXAMPLE_DIR: examples/web/${{ matrix.packer }} | |
steps: | |
- name: Checkout | |
# see https://github.com/actions/checkout | |
uses: actions/checkout@v4 | |
- name: Setup Node.js ${{ env.NODE_ACTIVE_LTS }} | |
# see https://github.com/actions/setup-node | |
uses: actions/setup-node@v4 | |
with: | |
node-version: ${{ env.NODE_ACTIVE_LTS }} | |
- name: fetch build artifact 'node' | |
# see https://github.com/actions/download-artifact | |
uses: actions/download-artifact@v4 | |
with: | |
name: dist.web | |
path: dist.web | |
- name: setup library | |
run: npm i --ignore-scripts --omit=dev --include=optional --loglevel=silly | |
- name: setup example project | |
run: npm i --no-save --loglevel=silly | |
working-directory: ${{ env.EXAMPLE_DIR }} | |
- name: build example | |
run: npm run build | |
working-directory: ${{ env.EXAMPLE_DIR }} | |
api-doc: | |
name: api-doc ${{ matrix.target }} | |
runs-on: "ubuntu-latest" | |
strategy: | |
fail-fast: false | |
matrix: | |
target: | |
- node | |
- web | |
timeout-minutes: 10 | |
steps: | |
- name: Checkout | |
# see https://github.com/actions/checkout | |
uses: actions/checkout@v4 | |
- name: Setup Node.js ${{ env.NODE_ACTIVE_LTS }} | |
# see https://github.com/actions/setup-node | |
uses: actions/setup-node@v4 | |
with: | |
node-version: ${{ env.NODE_ACTIVE_LTS }} | |
# cache: "npm" | |
# cache-dependency-path: "**/package-lock.json" | |
- name: setup project | |
run: | | |
set -ex | |
echo "::group::install project" | |
npm install --ignore-scripts --loglevel=silly | |
echo "::endgroup::" | |
echo "::group::install docs-gen" | |
npm --prefix tools/docs-gen install --ignore-scripts --loglevel=silly | |
echo "::endgroup::" | |
- name: api-doc ${{ matrix.target }} | |
run: npm run api-doc:${{ matrix.target }} |