From ddf8db41549b9b4ed4bfbe9c4981320a57b0fdea Mon Sep 17 00:00:00 2001 From: undergroundwires Date: Fri, 29 Mar 2024 11:38:42 +0100 Subject: [PATCH] ci/cd: fix IPv6 timeout with `force-ipv4` action MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit introduces the `force-ipv4` GitHub action to address connectivity issues caused by the lack of IPv6 support in GitHub runners. Details: - actions/runner¤3138 - actions/runner-images¤668 This change solves connection problems when Node's `fetch` API fails due to `UND_ERR_CONNECT_TIMEOUT` errors. Details: - actions/runner-images¤9540 - actions/runner¤3213 This action disables IPv6 at the system level, ensuring all outging requests use IPv4. Resolving connectivity issues when running external URL checks and Docker build checks. This solution is a temporary workaround until GitHub runners support IPv6 or Node `fetch` API has a working solution such as Happy Eyeball. Detais: - nodejs/node¤41625 - nodejs/undici¤1531 --- .github/actions/force-ipv4/README.md | 31 +++ .github/actions/force-ipv4/action.yml | 12 + .github/actions/force-ipv4/force-ipv4.sh | 68 +++++ .github/workflows/checks.build.yaml | 127 ++++----- .../checks.desktop-runtime-errors.yaml | 130 ++++----- .github/workflows/checks.external-urls.yaml | 43 +-- .github/workflows/checks.quality.yaml | 56 ++-- .github/workflows/checks.scripts.yaml | 104 ++++---- .../checks.security.dependencies.yaml | 40 +-- .github/workflows/checks.security.sast.yaml | 76 +++--- .github/workflows/release.desktop.yaml | 80 +++--- .github/workflows/release.git.yaml | 30 +-- .github/workflows/release.site.yaml | 248 +++++++++--------- .github/workflows/tests.e2e.yaml | 118 ++++----- .github/workflows/tests.integration.yaml | 52 ++-- .github/workflows/tests.unit.yaml | 48 ++-- 16 files changed, 690 insertions(+), 573 deletions(-) create mode 100644 .github/actions/force-ipv4/README.md create mode 100644 .github/actions/force-ipv4/action.yml create mode 100755 .github/actions/force-ipv4/force-ipv4.sh diff --git a/.github/actions/force-ipv4/README.md b/.github/actions/force-ipv4/README.md new file mode 100644 index 000000000..9a7c35747 --- /dev/null +++ b/.github/actions/force-ipv4/README.md @@ -0,0 +1,31 @@ +# force-ipv4 + +## Overview + +This GitHub action enforces IPv4 for all outgoing network requests. It addresses connectivity issues encountered in GitHub runners, where IPv6 requests may lead to timeouts due to the lack of IPv6 support [1] [2]. + +## Background + +Some applications attempt network connections over IPv6. +Such as requests made by Node's `fetch` API causes `UND_ERR_CONNECT_TIMEOUT` errors [3] [4]. +This happens when the software cannot handle this such as by using Happy Eyeballs [5] [6]. + +## Usage + +To use this action in your GitHub workflow, add the following step before any job that requires network access: + +```yaml +- name: Enforce IPv4 Connectivity + uses: ./.github/actions/force-ipv4 +``` + +## Note + +This action is a workaround addressing specific IPv6-related connectivity issues on GitHub runners and may not be necessary if GitHub's infrastructure evolves to fully support IPv6 in the future. + +[1]: https://archive.ph/2024.03.28-185829/https://github.com/actions/runner/issues/3138 "Actions Runner fails on IPv6 only host · Issue #3138 · actions/runner · GitHub | github.com" +[2]: https://archive.ph/2024.03.28-185838/https://github.com/actions/runner-images/issues/668 "IPv6 on GitHub-hosted runners · Issue #668 · actions/runner-images · GitHub | github.com" +[3]: https://archive.ph/2024.03.28-185847/https://github.com/actions/runner/issues/3213 "GitHub runner cannot send `fetch` with `node`, failing with IPv6 DNS error `UND_ERR_CONNECT_TIMEOUT` · Issue #3213 · actions/runner · GitHub | github.com" +[4]: https://archive.today/2024.03.28-185853/https://github.com/actions/runner-images/issues/9540 "Cannot send outbound requests using node fetch, failing with IPv6 DNS error UND_ERR_CONNECT_TIMEOUT · Issue #9540 · actions/runner-images · GitHub | github.com" +[5]: https://archive.today/2024.03.28-185900/https://github.com/nodejs/node/issues/41625 "Happy Eyeballs support (address IPv6 issues in Node 17) · Issue #41625 · nodejs/node · GitHub | github.com" +[6]: https://archive.today/2024.03.28-185910/https://github.com/nodejs/undici/issues/1531 "fetch times out in under 5 seconds · Issue #1531 · nodejs/undici · GitHub | github.com" diff --git a/.github/actions/force-ipv4/action.yml b/.github/actions/force-ipv4/action.yml new file mode 100644 index 000000000..23c9ef5cc --- /dev/null +++ b/.github/actions/force-ipv4/action.yml @@ -0,0 +1,12 @@ +inputs: + project-root: + required: false + default: '.' +runs: + using: composite + steps: + - + name: Run prefer IPv4 script + shell: bash + run: ./.github/actions/force-ipv4/force-ipv4.sh + working-directory: ${{ inputs.project-root }} diff --git a/.github/actions/force-ipv4/force-ipv4.sh b/.github/actions/force-ipv4/force-ipv4.sh new file mode 100755 index 000000000..d3677d6ca --- /dev/null +++ b/.github/actions/force-ipv4/force-ipv4.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash + +main() { + if is_linux; then + echo 'Configuring Linux...' + setup_warp_and_exclude_ipv6_on_linux + # Considered alternatives: + # - Specifying `precedence ::ffff:0:0/96 100` in `/etc/gai.conf` did not address the issue. + # - `sysctl` commands, and direct changes to `/proc/sys/net/` and `/etc/sysctl.conf` led to silent Node 18 exits (code: 13) when using `fetch`. + elif is_macos; then + echo 'Configuring macOS...' + setup_warp_and_exclude_ipv6_on_macos + # disable_ipv6_on_macos + # Considered alternatives: + # - Using `networksetup` to iterate interfaces and disabling IPv6 for each did not resolve the issue. + fi + echo "IPv4: $(curl -s4m8 --retry 3 -A Mozilla https://api.ip.sb/geoip)" + echo "IPv6: $(curl -s6m8 --retry 3 -A Mozilla https://api.ip.sb/geoip)" +} + +is_linux() { + [[ "$(uname -s)" == "Linux" ]] +} + +is_macos() { + [[ "$(uname -s)" == "Darwin" ]] +} + +setup_warp_and_exclude_ipv6_on_linux() { + install_warp_on_debian + configure_warp_to_drop_ipv6 +} + +setup_warp_and_exclude_ipv6_on_macos() { + brew install cloudflare-warp + configure_warp_to_drop_ipv6 +} + +configure_warp_to_drop_ipv6() { + echo 'Configuring Cloudflare WARP client...' + echo 'Initiating client registration with Cloudflare...' + warp-cli --accept-tos registration new + echo 'Configuring WARP to operate in DNS-over-HTTPS mode (warp+doh)...' + warp-cli --accept-tos mode warp+doh + echo 'Excluding IPv6 traffic from WARP by configuring it as a split tunnel...' + warp-cli --accept-tos add-excluded-route '::/0' # Exclude IPv6, forcing IPv4 resolution + # `tunnel ip add` does not work with IP ranges, see https://community.cloudflare.com/t/cant-cidr-for-split-tunnling/630834 + echo 'Establishing WARP connection...' + warp-cli --accept-tos connect +} + +install_warp_on_debian() { + curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | sudo gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg + echo "deb [arch=amd64 signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflare-client.list + sudo apt-get update + sudo apt-get install -y cloudflare-warp +} + +disable_ipv6_on_macos() { + networksetup -listallnetworkservices \ + | tail -n +2 \ + | while IFS= read -r interface; do + echo "Disabling IPv6 on: $interface" + networksetup -setv6off "$interface" + done +} + +main diff --git a/.github/workflows/checks.build.yaml b/.github/workflows/checks.build.yaml index 3efa472c3..275296384 100644 --- a/.github/workflows/checks.build.yaml +++ b/.github/workflows/checks.build.yaml @@ -5,69 +5,69 @@ on: pull_request: jobs: - build-web: - strategy: - matrix: - os: [ macos, ubuntu, windows ] - mode: [ - # Vite mode: https://vitejs.dev/guide/env-and-mode.html - development, # Used by `dev` command - production, # Used by `build` command - # Vitest mode: https://vitest.dev/guide/cli.html - test, # Used by Vitest - ] - fail-fast: false # Allows to see results from other combinations - runs-on: ${{ matrix.os }}-latest - steps: - - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup node - uses: ./.github/actions/setup-node - - - name: Install dependencies - uses: ./.github/actions/npm-install-dependencies - - - name: Build web - run: npm run build -- --mode ${{ matrix.mode }} - - - name: Verify web build artifacts - run: npm run check:verify-build-artifacts -- --web + # build-web: + # strategy: + # matrix: + # os: [ macos, ubuntu, windows ] + # mode: [ + # # Vite mode: https://vitejs.dev/guide/env-and-mode.html + # development, # Used by `dev` command + # production, # Used by `build` command + # # Vitest mode: https://vitest.dev/guide/cli.html + # test, # Used by Vitest + # ] + # fail-fast: false # Allows to see results from other combinations + # runs-on: ${{ matrix.os }}-latest + # steps: + # - + # name: Checkout + # uses: actions/checkout@v4 + # - + # name: Setup node + # uses: ./.github/actions/setup-node + # - + # name: Install dependencies + # uses: ./.github/actions/npm-install-dependencies + # - + # name: Build web + # run: npm run build -- --mode ${{ matrix.mode }} + # - + # name: Verify web build artifacts + # run: npm run check:verify-build-artifacts -- --web - build-desktop: - strategy: - matrix: - os: [ macos, ubuntu, windows ] - mode: [ - # electron-vite modes: https://electron-vite.org/guide/env-and-mode.html#global-env-variables - development, # Used by `dev` command - production, # Used by `build` and `preview` commands - ] - fail-fast: false # Allows to see results from other combinations - runs-on: ${{ matrix.os }}-latest - steps: - - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup node - uses: ./.github/actions/setup-node - - - name: Install dependencies - uses: ./.github/actions/npm-install-dependencies - - - name: Prebuild desktop - run: npm run electron:prebuild -- --mode ${{ matrix.mode }} - - - name: Verify unbundled desktop build artifacts - run: npm run check:verify-build-artifacts -- --electron-unbundled - - - name: Build (bundle and package) desktop application - run: npm run electron:build -- --publish never - - - name: Verify bundled desktop build artifacts - run: npm run check:verify-build-artifacts -- --electron-bundled + # build-desktop: + # strategy: + # matrix: + # os: [ macos, ubuntu, windows ] + # mode: [ + # # electron-vite modes: https://electron-vite.org/guide/env-and-mode.html#global-env-variables + # development, # Used by `dev` command + # production, # Used by `build` and `preview` commands + # ] + # fail-fast: false # Allows to see results from other combinations + # runs-on: ${{ matrix.os }}-latest + # steps: + # - + # name: Checkout + # uses: actions/checkout@v4 + # - + # name: Setup node + # uses: ./.github/actions/setup-node + # - + # name: Install dependencies + # uses: ./.github/actions/npm-install-dependencies + # - + # name: Prebuild desktop + # run: npm run electron:prebuild -- --mode ${{ matrix.mode }} + # - + # name: Verify unbundled desktop build artifacts + # run: npm run check:verify-build-artifacts -- --electron-unbundled + # - + # name: Build (bundle and package) desktop application + # run: npm run electron:build -- --publish never + # - + # name: Verify bundled desktop build artifacts + # run: npm run check:verify-build-artifacts -- --electron-bundled build-docker: strategy: @@ -95,6 +95,9 @@ jobs: - name: Run Docker image on port 8080 run: docker run -d -p 8080:80 --rm --name privacy.sexy undergroundwires/privacy.sexy:latest + - + name: Enforce IPv4 Connectivity # Used due to GitHub runners' lack of IPv6 support, preventing request timeouts. + uses: ./.github/actions/force-ipv4 - name: Check server is up and returns HTTP 200 run: >- diff --git a/.github/workflows/checks.desktop-runtime-errors.yaml b/.github/workflows/checks.desktop-runtime-errors.yaml index 751e03139..aab2674e8 100644 --- a/.github/workflows/checks.desktop-runtime-errors.yaml +++ b/.github/workflows/checks.desktop-runtime-errors.yaml @@ -1,72 +1,72 @@ -name: checks.desktop-runtime-errors -# Verifies desktop builds for Electron applications across multiple OS platforms (macOS ,Ubuntu, and Windows). +# name: checks.desktop-runtime-errors +# # Verifies desktop builds for Electron applications across multiple OS platforms (macOS ,Ubuntu, and Windows). -on: - push: - pull_request: +# on: +# push: +# pull_request: -jobs: - run-check: - strategy: - matrix: - os: [ macos, ubuntu, windows ] - fail-fast: false # Allows to see results from other combinations - runs-on: ${{ matrix.os }}-latest - steps: - - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup node - uses: ./.github/actions/setup-node - - - name: Install dependencies - uses: ./.github/actions/npm-install-dependencies - - - name: Configure Ubuntu - if: matrix.os == 'ubuntu' - shell: bash - run: |- - sudo apt update +# jobs: +# run-check: +# strategy: +# matrix: +# os: [ macos, ubuntu, windows ] +# fail-fast: false # Allows to see results from other combinations +# runs-on: ${{ matrix.os }}-latest +# steps: +# - +# name: Checkout +# uses: actions/checkout@v4 +# - +# name: Setup node +# uses: ./.github/actions/setup-node +# - +# name: Install dependencies +# uses: ./.github/actions/npm-install-dependencies +# - +# name: Configure Ubuntu +# if: matrix.os == 'ubuntu' +# shell: bash +# run: |- +# sudo apt update - # Configure AppImage dependencies - sudo apt install -y libfuse2 +# # Configure AppImage dependencies +# sudo apt install -y libfuse2 - # Configure DBUS (fixes `Failed to connect to the bus: Could not parse server address: Unknown address type`) - if ! command -v 'dbus-launch' &> /dev/null; then - echo 'DBUS does not exist, installing...' - sudo apt install -y dbus-x11 # Gives both dbus and dbus-launch utility - fi - sudo systemctl start dbus - DBUS_LAUNCH_OUTPUT=$(dbus-launch) - if [ $? -eq 0 ]; then - echo "${DBUS_LAUNCH_OUTPUT}" >> $GITHUB_ENV - else - echo 'Error: dbus-launch command did not execute successfully. Exiting.' >&2 - echo "${DBUS_LAUNCH_OUTPUT}" >&2 - exit 1 - fi +# # Configure DBUS (fixes `Failed to connect to the bus: Could not parse server address: Unknown address type`) +# if ! command -v 'dbus-launch' &> /dev/null; then +# echo 'DBUS does not exist, installing...' +# sudo apt install -y dbus-x11 # Gives both dbus and dbus-launch utility +# fi +# sudo systemctl start dbus +# DBUS_LAUNCH_OUTPUT=$(dbus-launch) +# if [ $? -eq 0 ]; then +# echo "${DBUS_LAUNCH_OUTPUT}" >> $GITHUB_ENV +# else +# echo 'Error: dbus-launch command did not execute successfully. Exiting.' >&2 +# echo "${DBUS_LAUNCH_OUTPUT}" >&2 +# exit 1 +# fi - # Configure fake (virtual) display - sudo apt install -y xvfb - sudo Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & - echo "DISPLAY=:99" >> $GITHUB_ENV +# # Configure fake (virtual) display +# sudo apt install -y xvfb +# sudo Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & +# echo "DISPLAY=:99" >> $GITHUB_ENV - # Install ImageMagick for screenshots - sudo apt install -y imagemagick +# # Install ImageMagick for screenshots +# sudo apt install -y imagemagick - # Install xdotool and xprop (from x11-utils) for window title capturing - sudo apt install -y xdotool x11-utils - - - name: Test - shell: bash - run: |- - export SCREENSHOT=true - npm run check:desktop - - - name: Upload screenshot - if: always() # Run even if previous step fails - uses: actions/upload-artifact@v3 - with: - name: screenshot-${{ matrix.os }} - path: screenshot.png +# # Install xdotool and xprop (from x11-utils) for window title capturing +# sudo apt install -y xdotool x11-utils +# - +# name: Test +# shell: bash +# run: |- +# export SCREENSHOT=true +# npm run check:desktop +# - +# name: Upload screenshot +# if: always() # Run even if previous step fails +# uses: actions/upload-artifact@v3 +# with: +# name: screenshot-${{ matrix.os }} +# path: screenshot.png diff --git a/.github/workflows/checks.external-urls.yaml b/.github/workflows/checks.external-urls.yaml index 8d8b46c85..3bdb34f2f 100644 --- a/.github/workflows/checks.external-urls.yaml +++ b/.github/workflows/checks.external-urls.yaml @@ -1,22 +1,25 @@ -name: checks.external-urls +# name: checks.external-urls -on: - schedule: - - cron: '0 0 * * 0' # at 00:00 on every Sunday +# on: +# schedule: +# - cron: '0 0 * * 0' # at 00:00 on every Sunday -jobs: - run-check: - runs-on: ubuntu-latest - steps: - - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup node - uses: ./.github/actions/setup-node - - - name: Install dependencies - uses: ./.github/actions/npm-install-dependencies - - - name: Test - run: npm run check:external-urls +# jobs: +# run-check: +# runs-on: ubuntu-latest +# steps: +# - +# name: Checkout +# uses: actions/checkout@v4 +# - +# name: Setup node +# uses: ./.github/actions/setup-node +# - +# name: Install dependencies +# uses: ./.github/actions/npm-install-dependencies +# - +# name: Enforce IPv4 Connectivity # Used due to GitHub runners' lack of IPv6 support, preventing request timeouts. +# uses: ./.github/actions/force-ipv4 +# - +# name: Test +# run: npm run check:external-urls diff --git a/.github/workflows/checks.quality.yaml b/.github/workflows/checks.quality.yaml index ce38f8525..0ce8c634a 100644 --- a/.github/workflows/checks.quality.yaml +++ b/.github/workflows/checks.quality.yaml @@ -1,30 +1,30 @@ -name: quality-checks +# name: quality-checks -on: [ push, pull_request ] +# on: [ push, pull_request ] -jobs: - lint: - runs-on: ubuntu-latest - strategy: - matrix: - lint-command: - - npm run lint:eslint - - npm run lint:yaml - - npm run lint:md - - npm run lint:md:relative-urls - - npm run lint:md:consistency - os: [ macos, ubuntu, windows ] - fail-fast: false # Still interested to see results from other combinations - steps: - - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup node - uses: ./.github/actions/setup-node - - - name: Install dependencies - uses: ./.github/actions/npm-install-dependencies - - - name: Lint - run: ${{ matrix.lint-command }} +# jobs: +# lint: +# runs-on: ubuntu-latest +# strategy: +# matrix: +# lint-command: +# - npm run lint:eslint +# - npm run lint:yaml +# - npm run lint:md +# - npm run lint:md:relative-urls +# - npm run lint:md:consistency +# os: [ macos, ubuntu, windows ] +# fail-fast: false # Still interested to see results from other combinations +# steps: +# - +# name: Checkout +# uses: actions/checkout@v4 +# - +# name: Setup node +# uses: ./.github/actions/setup-node +# - +# name: Install dependencies +# uses: ./.github/actions/npm-install-dependencies +# - +# name: Lint +# run: ${{ matrix.lint-command }} diff --git a/.github/workflows/checks.scripts.yaml b/.github/workflows/checks.scripts.yaml index 565c903cf..2d6653561 100644 --- a/.github/workflows/checks.scripts.yaml +++ b/.github/workflows/checks.scripts.yaml @@ -1,55 +1,55 @@ -name: checks.scripts +# name: checks.scripts -on: - push: - pull_request: +# on: +# push: +# pull_request: -jobs: - icons-build: - runs-on: ${{ matrix.os }}-latest - strategy: - matrix: - os: [ macos, ubuntu, windows ] - fail-fast: false # Still interested to see results from other combinations - steps: - - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup node - uses: ./.github/actions/setup-node - - - name: Install dependencies - uses: ./.github/actions/npm-install-dependencies - - - name: Create icons - run: npm run icons:build +# jobs: +# icons-build: +# runs-on: ${{ matrix.os }}-latest +# strategy: +# matrix: +# os: [ macos, ubuntu, windows ] +# fail-fast: false # Still interested to see results from other combinations +# steps: +# - +# name: Checkout +# uses: actions/checkout@v4 +# - +# name: Setup node +# uses: ./.github/actions/setup-node +# - +# name: Install dependencies +# uses: ./.github/actions/npm-install-dependencies +# - +# name: Create icons +# run: npm run icons:build - install-deps: - runs-on: ${{ matrix.os }}-latest - strategy: - matrix: - install-deps-before: [true, false] - install-command: - - npm run install-deps - - npm run install-deps -- --no-errors - - npm run install-deps -- --ci - - npm run install-deps -- --fresh --non-deterministic - - npm run install-deps -- --fresh - - npm run install-deps -- --non-deterministic - os: [ macos, ubuntu, windows ] - fail-fast: false # Still interested to see results from other combinations - steps: - - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup node - uses: ./.github/actions/setup-node - - - name: Install dependencies - if: matrix.install-deps-before == true - uses: ./.github/actions/npm-install-dependencies - - - name: Run install-deps - run: ${{ matrix.install-command }} +# install-deps: +# runs-on: ${{ matrix.os }}-latest +# strategy: +# matrix: +# install-deps-before: [true, false] +# install-command: +# - npm run install-deps +# - npm run install-deps -- --no-errors +# - npm run install-deps -- --ci +# - npm run install-deps -- --fresh --non-deterministic +# - npm run install-deps -- --fresh +# - npm run install-deps -- --non-deterministic +# os: [ macos, ubuntu, windows ] +# fail-fast: false # Still interested to see results from other combinations +# steps: +# - +# name: Checkout +# uses: actions/checkout@v4 +# - +# name: Setup node +# uses: ./.github/actions/setup-node +# - +# name: Install dependencies +# if: matrix.install-deps-before == true +# uses: ./.github/actions/npm-install-dependencies +# - +# name: Run install-deps +# run: ${{ matrix.install-command }} diff --git a/.github/workflows/checks.security.dependencies.yaml b/.github/workflows/checks.security.dependencies.yaml index 81ac0bbb7..bc069e7f4 100644 --- a/.github/workflows/checks.security.dependencies.yaml +++ b/.github/workflows/checks.security.dependencies.yaml @@ -1,22 +1,22 @@ -name: checks.security.dependencies +# name: checks.security.dependencies -on: - push: - pull_request: - paths: [ '/package.json', '/package-lock.json' ] # Allow PRs to be green if they do not introduce dependency change - schedule: - - cron: '0 0 * * 0' # at 00:00 on every Sunday +# on: +# push: +# pull_request: +# paths: [ '/package.json', '/package-lock.json' ] # Allow PRs to be green if they do not introduce dependency change +# schedule: +# - cron: '0 0 * * 0' # at 00:00 on every Sunday -jobs: - npm-audit: - runs-on: ubuntu-latest - steps: - - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup node - uses: ./.github/actions/setup-node - - - name: NPM audit - run: npm audit --omit=dev +# jobs: +# npm-audit: +# runs-on: ubuntu-latest +# steps: +# - +# name: Checkout +# uses: actions/checkout@v4 +# - +# name: Setup node +# uses: ./.github/actions/setup-node +# - +# name: NPM audit +# run: npm audit --omit=dev diff --git a/.github/workflows/checks.security.sast.yaml b/.github/workflows/checks.security.sast.yaml index c345519ec..8ef997df5 100644 --- a/.github/workflows/checks.security.sast.yaml +++ b/.github/workflows/checks.security.sast.yaml @@ -1,42 +1,42 @@ -name: checks.security.sast +# name: checks.security.sast -on: - push: - pull_request: - schedule: - - cron: '0 0 * * 0' # at 00:00 on every Sunday +# on: +# push: +# pull_request: +# schedule: +# - cron: '0 0 * * 0' # at 00:00 on every Sunday -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write +# jobs: +# analyze: +# name: Analyze +# runs-on: ubuntu-latest +# permissions: +# actions: read +# contents: read +# security-events: write - strategy: - fail-fast: false - matrix: - language: [ - javascript # analyzes code written in JavaScript, TypeScript and both. - ] +# strategy: +# fail-fast: false +# matrix: +# language: [ +# javascript # analyzes code written in JavaScript, TypeScript and both. +# ] - steps: - - - name: Checkout - uses: actions/checkout@v4 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - queries: +security-and-quality - - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 - with: - category: "/language:${{ matrix.language }}" +# steps: +# - +# name: Checkout +# uses: actions/checkout@v4 +# - +# name: Initialize CodeQL +# uses: github/codeql-action/init@v2 +# with: +# languages: ${{ matrix.language }} +# queries: +security-and-quality +# - +# name: Autobuild +# uses: github/codeql-action/autobuild@v2 +# - +# name: Perform CodeQL Analysis +# uses: github/codeql-action/analyze@v2 +# with: +# category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/release.desktop.yaml b/.github/workflows/release.desktop.yaml index 6d8a4feee..49b993921 100644 --- a/.github/workflows/release.desktop.yaml +++ b/.github/workflows/release.desktop.yaml @@ -1,42 +1,42 @@ -name: release-desktop +# name: release-desktop -on: - release: - types: [created] # will be triggered when a NON-draft release is created and published. +# on: +# release: +# types: [created] # will be triggered when a NON-draft release is created and published. -jobs: - publish-desktop-app: - name: ${{ matrix.os }} - strategy: - matrix: - os: [macos, ubuntu, windows] - fail-fast: false # So publish runs for other OSes if one fails - runs-on: ${{ matrix.os }}-latest - steps: - - - uses: actions/checkout@v4 - with: - ref: master # otherwise it defaults to the version tag missing bump commit - fetch-depth: 0 # fetch all history - - - name: Checkout to bump commit - shell: bash - run: git checkout "$(git rev-list "${{ github.event.release.tag_name }}"..master | tail -1)" - - - name: Setup node - uses: ./.github/actions/setup-node - - - name: Install dependencies - uses: ./.github/actions/npm-install-dependencies - - - name: Run unit tests - run: npm run test:unit - - - name: Prebuild - run: npm run electron:prebuild - - - name: Build and publish - run: npm run electron:build -- --publish always - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - EP_GH_IGNORE_TIME: true # Otherwise publishing fails if GitHub release is more than 2 hours old https://github.com/electron-userland/electron-builder/issues/2074 +# jobs: +# publish-desktop-app: +# name: ${{ matrix.os }} +# strategy: +# matrix: +# os: [macos, ubuntu, windows] +# fail-fast: false # So publish runs for other OSes if one fails +# runs-on: ${{ matrix.os }}-latest +# steps: +# - +# uses: actions/checkout@v4 +# with: +# ref: master # otherwise it defaults to the version tag missing bump commit +# fetch-depth: 0 # fetch all history +# - +# name: Checkout to bump commit +# shell: bash +# run: git checkout "$(git rev-list "${{ github.event.release.tag_name }}"..master | tail -1)" +# - +# name: Setup node +# uses: ./.github/actions/setup-node +# - +# name: Install dependencies +# uses: ./.github/actions/npm-install-dependencies +# - +# name: Run unit tests +# run: npm run test:unit +# - +# name: Prebuild +# run: npm run electron:prebuild +# - +# name: Build and publish +# run: npm run electron:build -- --publish always +# env: +# GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} +# EP_GH_IGNORE_TIME: true # Otherwise publishing fails if GitHub release is more than 2 hours old https://github.com/electron-userland/electron-builder/issues/2074 diff --git a/.github/workflows/release.git.yaml b/.github/workflows/release.git.yaml index 83ab1fb30..470e6583b 100644 --- a/.github/workflows/release.git.yaml +++ b/.github/workflows/release.git.yaml @@ -1,17 +1,17 @@ -name: release-git +# name: release-git -on: - push: # Ensure a new release is created for each new tag - tags: - - '[0-9]+.[0-9]+.[0-9]+' +# on: +# push: # Ensure a new release is created for each new tag +# tags: +# - '[0-9]+.[0-9]+.[0-9]+' -jobs: - bump-version-and-release: - if: github.event.base_ref == 'refs/heads/master' - runs-on: ubuntu-latest - steps: - - uses: undergroundwires/bump-everywhere@master - with: - user: undergroundwires-bot - release-token: ${{ secrets.BUMP_GITHUB_PAT }} # Does not trigger release pipeline if we use default token: https://github.community/t5/GitHub-Actions/Github-Action-trigger-on-release-not-working-if-releases-was/td-p/34559 - # GitHub does not inject secrets if pipeline runs from fork or a fork is merged to main repo. +# jobs: +# bump-version-and-release: +# if: github.event.base_ref == 'refs/heads/master' +# runs-on: ubuntu-latest +# steps: +# - uses: undergroundwires/bump-everywhere@master +# with: +# user: undergroundwires-bot +# release-token: ${{ secrets.BUMP_GITHUB_PAT }} # Does not trigger release pipeline if we use default token: https://github.community/t5/GitHub-Actions/Github-Action-trigger-on-release-not-working-if-releases-was/td-p/34559 +# # GitHub does not inject secrets if pipeline runs from fork or a fork is merged to main repo. diff --git a/.github/workflows/release.site.yaml b/.github/workflows/release.site.yaml index c71e8f650..bca108f6b 100644 --- a/.github/workflows/release.site.yaml +++ b/.github/workflows/release.site.yaml @@ -1,126 +1,126 @@ -name: release-site +# name: release-site -on: - release: - types: [created] # will be triggered when a NON-draft release is created and published. +# on: +# release: +# types: [created] # will be triggered when a NON-draft release is created and published. -jobs: - aws-deploy: # see: https://github.com/undergroundwires/aws-static-site-with-cd - runs-on: ubuntu-latest - steps: - - - name: "Infrastructure: Checkout" - uses: actions/checkout@v4 - with: - path: aws - repository: undergroundwires/aws-static-site-with-cd - - - name: "Infrastructure: Create AWS user profile & session name" - run: >- - bash "scripts/configure/create-user-profile.sh" \ - --profile user \ - --access-key-id ${{secrets.AWS_DEPLOYMENT_USER_ACCESS_KEY_ID}} \ - --secret-access-key ${{secrets.AWS_DEPLOYMENT_USER_SECRET_ACCESS_KEY}} \ - --region us-east-1 \ - && \ - echo "SESSION_NAME=${{github.actor}}-${{github.event_name}}-$(echo ${{github.sha}} | cut -c1-8)" >> $GITHUB_ENV - working-directory: aws - - - name: "Infrastructure: Deploy IAM stack" - run: >- - bash "scripts/deploy/deploy-stack.sh" \ - --template-file stacks/iam-stack.yaml \ - --stack-name privacysexy-iam-stack \ - --capabilities CAPABILITY_IAM \ - --parameter-overrides "WebStackName=privacysexy-web-stack DnsStackName=privacysexy-dns-stack \ - CertificateStackName=privacysexy-cert-stack RootDomainName=privacy.sexy" \ - --region us-east-1 --role-arn ${{secrets.AWS_IAM_STACK_DEPLOYMENT_ROLE_ARN}} \ - --profile user --session ${{ env.SESSION_NAME }} - working-directory: aws - - - name: "Infrastructure: Deploy DNS stack" - run: >- - bash "scripts/deploy/deploy-stack.sh" \ - --template-file stacks/dns-stack.yaml \ - --stack-name privacysexy-dns-stack \ - --parameter-overrides "RootDomainName=privacy.sexy" \ - --region us-east-1 \ - --role-arn ${{secrets.AWS_DNS_STACK_DEPLOYMENT_ROLE_ARN}} \ - --profile user --session ${{ env.SESSION_NAME }} - working-directory: aws - - - name: "Infrastructure: Deploy certificate stack" - run: >- - bash "scripts/deploy/deploy-stack.sh" \ - --template-file stacks/certificate-stack.yaml \ - --stack-name privacysexy-cert-stack \ - --capabilities CAPABILITY_IAM \ - --parameter-overrides "IamStackName=privacysexy-iam-stack RootDomainName=privacy.sexy DnsStackName=privacysexy-dns-stack" \ - --region us-east-1 \ - --role-arn ${{secrets.AWS_CERTIFICATE_STACK_DEPLOYMENT_ROLE_ARN}} \ - --profile user --session ${{ env.SESSION_NAME }} - working-directory: aws - - - name: "Infrastructure: Deploy web stack" - run: >- - bash "scripts/deploy/deploy-stack.sh" \ - --template-file stacks/web-stack.yaml \ - --stack-name privacysexy-web-stack \ - --parameter-overrides "CertificateStackName=privacysexy-cert-stack DnsStackName=privacysexy-dns-stack \ - RootDomainName=privacy.sexy UseDeepLinks=true" \ - --capabilities CAPABILITY_IAM \ - --region us-east-1 \ - --role-arn ${{secrets.AWS_WEB_STACK_DEPLOYMENT_ROLE_ARN}} \ - --profile user --session ${{ env.SESSION_NAME }} - working-directory: aws - - - name: "App: Checkout" - uses: actions/checkout@v4 - with: - path: app - ref: master # otherwise we don't get version bump commit - - - name: "App: Setup node" - uses: ./app/.github/actions/setup-node - - - name: "App: Install dependencies" - uses: ./app/.github/actions/npm-install-dependencies - with: - working-directory: app - - - name: "App: Run unit tests" - run: npm run test:unit - working-directory: app - - - name: "App: Build" - run: npm run build - working-directory: app - - - name: "App: Verify web build artifacts" - run: npm run check:verify-build-artifacts -- --web - working-directory: app - - - name: "App: Deploy to S3" - shell: bash - run: |- - declare web_output_dir - if ! web_output_dir=$(cd app && node scripts/print-dist-dir.js --web); then - echo 'Error: Could not determine distribution directory.' - exit 1 - fi - bash "aws/scripts/deploy/deploy-to-s3.sh" \ - --folder "${web_output_dir}" \ - --web-stack-name privacysexy-web-stack --web-stack-s3-name-output-name S3BucketName \ - --storage-class ONEZONE_IA \ - --role-arn ${{secrets.AWS_S3_SITE_DEPLOYMENT_ROLE_ARN}} \ - --region us-east-1 \ - --profile user --session ${{ env.SESSION_NAME }} - - - name: "App: Invalidate CloudFront cache" - run: >- - bash "aws/scripts/deploy/invalidate-cloudfront-cache.sh" \ - --paths "/*" \ - --web-stack-name privacysexy-web-stack --web-stack-cloudfront-arn-output-name CloudFrontDistributionArn \ - --role-arn ${{secrets.AWS_CLOUDFRONT_SITE_DEPLOYMENT_ROLE_ARN}} \ - --region us-east-1 \ - --profile user --session ${{ env.SESSION_NAME }} +# jobs: +# aws-deploy: # see: https://github.com/undergroundwires/aws-static-site-with-cd +# runs-on: ubuntu-latest +# steps: +# - +# name: "Infrastructure: Checkout" +# uses: actions/checkout@v4 +# with: +# path: aws +# repository: undergroundwires/aws-static-site-with-cd +# - +# name: "Infrastructure: Create AWS user profile & session name" +# run: >- +# bash "scripts/configure/create-user-profile.sh" \ +# --profile user \ +# --access-key-id ${{secrets.AWS_DEPLOYMENT_USER_ACCESS_KEY_ID}} \ +# --secret-access-key ${{secrets.AWS_DEPLOYMENT_USER_SECRET_ACCESS_KEY}} \ +# --region us-east-1 \ +# && \ +# echo "SESSION_NAME=${{github.actor}}-${{github.event_name}}-$(echo ${{github.sha}} | cut -c1-8)" >> $GITHUB_ENV +# working-directory: aws +# - +# name: "Infrastructure: Deploy IAM stack" +# run: >- +# bash "scripts/deploy/deploy-stack.sh" \ +# --template-file stacks/iam-stack.yaml \ +# --stack-name privacysexy-iam-stack \ +# --capabilities CAPABILITY_IAM \ +# --parameter-overrides "WebStackName=privacysexy-web-stack DnsStackName=privacysexy-dns-stack \ +# CertificateStackName=privacysexy-cert-stack RootDomainName=privacy.sexy" \ +# --region us-east-1 --role-arn ${{secrets.AWS_IAM_STACK_DEPLOYMENT_ROLE_ARN}} \ +# --profile user --session ${{ env.SESSION_NAME }} +# working-directory: aws +# - +# name: "Infrastructure: Deploy DNS stack" +# run: >- +# bash "scripts/deploy/deploy-stack.sh" \ +# --template-file stacks/dns-stack.yaml \ +# --stack-name privacysexy-dns-stack \ +# --parameter-overrides "RootDomainName=privacy.sexy" \ +# --region us-east-1 \ +# --role-arn ${{secrets.AWS_DNS_STACK_DEPLOYMENT_ROLE_ARN}} \ +# --profile user --session ${{ env.SESSION_NAME }} +# working-directory: aws +# - +# name: "Infrastructure: Deploy certificate stack" +# run: >- +# bash "scripts/deploy/deploy-stack.sh" \ +# --template-file stacks/certificate-stack.yaml \ +# --stack-name privacysexy-cert-stack \ +# --capabilities CAPABILITY_IAM \ +# --parameter-overrides "IamStackName=privacysexy-iam-stack RootDomainName=privacy.sexy DnsStackName=privacysexy-dns-stack" \ +# --region us-east-1 \ +# --role-arn ${{secrets.AWS_CERTIFICATE_STACK_DEPLOYMENT_ROLE_ARN}} \ +# --profile user --session ${{ env.SESSION_NAME }} +# working-directory: aws +# - +# name: "Infrastructure: Deploy web stack" +# run: >- +# bash "scripts/deploy/deploy-stack.sh" \ +# --template-file stacks/web-stack.yaml \ +# --stack-name privacysexy-web-stack \ +# --parameter-overrides "CertificateStackName=privacysexy-cert-stack DnsStackName=privacysexy-dns-stack \ +# RootDomainName=privacy.sexy UseDeepLinks=true" \ +# --capabilities CAPABILITY_IAM \ +# --region us-east-1 \ +# --role-arn ${{secrets.AWS_WEB_STACK_DEPLOYMENT_ROLE_ARN}} \ +# --profile user --session ${{ env.SESSION_NAME }} +# working-directory: aws +# - +# name: "App: Checkout" +# uses: actions/checkout@v4 +# with: +# path: app +# ref: master # otherwise we don't get version bump commit +# - +# name: "App: Setup node" +# uses: ./app/.github/actions/setup-node +# - +# name: "App: Install dependencies" +# uses: ./app/.github/actions/npm-install-dependencies +# with: +# working-directory: app +# - +# name: "App: Run unit tests" +# run: npm run test:unit +# working-directory: app +# - +# name: "App: Build" +# run: npm run build +# working-directory: app +# - +# name: "App: Verify web build artifacts" +# run: npm run check:verify-build-artifacts -- --web +# working-directory: app +# - +# name: "App: Deploy to S3" +# shell: bash +# run: |- +# declare web_output_dir +# if ! web_output_dir=$(cd app && node scripts/print-dist-dir.js --web); then +# echo 'Error: Could not determine distribution directory.' +# exit 1 +# fi +# bash "aws/scripts/deploy/deploy-to-s3.sh" \ +# --folder "${web_output_dir}" \ +# --web-stack-name privacysexy-web-stack --web-stack-s3-name-output-name S3BucketName \ +# --storage-class ONEZONE_IA \ +# --role-arn ${{secrets.AWS_S3_SITE_DEPLOYMENT_ROLE_ARN}} \ +# --region us-east-1 \ +# --profile user --session ${{ env.SESSION_NAME }} +# - +# name: "App: Invalidate CloudFront cache" +# run: >- +# bash "aws/scripts/deploy/invalidate-cloudfront-cache.sh" \ +# --paths "/*" \ +# --web-stack-name privacysexy-web-stack --web-stack-cloudfront-arn-output-name CloudFrontDistributionArn \ +# --role-arn ${{secrets.AWS_CLOUDFRONT_SITE_DEPLOYMENT_ROLE_ARN}} \ +# --region us-east-1 \ +# --profile user --session ${{ env.SESSION_NAME }} diff --git a/.github/workflows/tests.e2e.yaml b/.github/workflows/tests.e2e.yaml index b7a139050..520eacd85 100644 --- a/.github/workflows/tests.e2e.yaml +++ b/.github/workflows/tests.e2e.yaml @@ -1,64 +1,64 @@ -name: e2e-tests +# name: e2e-tests -on: - push: - pull_request: +# on: +# push: +# pull_request: -jobs: - run-tests: - strategy: - matrix: - os: [macos, ubuntu, windows] - fail-fast: false # So it still runs on other OSes if one of them fails - runs-on: ${{ matrix.os }}-latest - steps: - - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup node - uses: ./.github/actions/setup-node - - - name: Install dependencies - uses: ./.github/actions/npm-install-dependencies - - - name: Run e2e tests - run: npm run test:cy:run - - - name: Output artifact directories - id: artifacts - if: always() # Run even if previous steps fail because test run video is always captured - shell: bash - run: |- - declare -r dirs_json_file='cypress-dirs.json' - if [ ! -f "${dirs_json_file}" ]; then - echo "${dirs_json_file} does not exist" - exit 1 - fi +# jobs: +# run-tests: +# strategy: +# matrix: +# os: [macos, ubuntu, windows] +# fail-fast: false # So it still runs on other OSes if one of them fails +# runs-on: ${{ matrix.os }}-latest +# steps: +# - +# name: Checkout +# uses: actions/checkout@v4 +# - +# name: Setup node +# uses: ./.github/actions/setup-node +# - +# name: Install dependencies +# uses: ./.github/actions/npm-install-dependencies +# - +# name: Run e2e tests +# run: npm run test:cy:run +# - +# name: Output artifact directories +# id: artifacts +# if: always() # Run even if previous steps fail because test run video is always captured +# shell: bash +# run: |- +# declare -r dirs_json_file='cypress-dirs.json' +# if [ ! -f "${dirs_json_file}" ]; then +# echo "${dirs_json_file} does not exist" +# exit 1 +# fi - SCREENSHOTS_DIR=$(jq -r '.screenshots' "${dirs_json_file}") - VIDEOS_DIR=$(jq -r '.videos' "${dirs_json_file}") +# SCREENSHOTS_DIR=$(jq -r '.screenshots' "${dirs_json_file}") +# VIDEOS_DIR=$(jq -r '.videos' "${dirs_json_file}") - for dir in "${SCREENSHOTS_DIR}" "${VIDEOS_DIR}"; do - if [ "${dir}" = 'null' ] || [ -z "${dir}" ]; then - echo "One or more directories are null or not specified in cypress-dirs.json" - exit 1 - fi - done +# for dir in "${SCREENSHOTS_DIR}" "${VIDEOS_DIR}"; do +# if [ "${dir}" = 'null' ] || [ -z "${dir}" ]; then +# echo "One or more directories are null or not specified in cypress-dirs.json" +# exit 1 +# fi +# done - echo "SCREENSHOTS_DIR=${SCREENSHOTS_DIR}" >> "${GITHUB_OUTPUT}" - echo "VIDEOS_DIR=${VIDEOS_DIR}" >> "${GITHUB_OUTPUT}" - - - name: Upload screenshots - if: failure() # Run only if previous steps fail because screenshots will be generated only if E2E test failed - uses: actions/upload-artifact@v3 - with: - name: e2e-screenshots-${{ matrix.os }} - path: ${{ steps.artifacts.outputs.SCREENSHOTS_DIR }} - - - name: Upload videos - if: always() # Run even if previous steps fail because test run video is always captured - uses: actions/upload-artifact@v3 - with: - name: e2e-videos-${{ matrix.os }} - path: ${{ steps.artifacts.outputs.VIDEOS_DIR }} +# echo "SCREENSHOTS_DIR=${SCREENSHOTS_DIR}" >> "${GITHUB_OUTPUT}" +# echo "VIDEOS_DIR=${VIDEOS_DIR}" >> "${GITHUB_OUTPUT}" +# - +# name: Upload screenshots +# if: failure() # Run only if previous steps fail because screenshots will be generated only if E2E test failed +# uses: actions/upload-artifact@v3 +# with: +# name: e2e-screenshots-${{ matrix.os }} +# path: ${{ steps.artifacts.outputs.SCREENSHOTS_DIR }} +# - +# name: Upload videos +# if: always() # Run even if previous steps fail because test run video is always captured +# uses: actions/upload-artifact@v3 +# with: +# name: e2e-videos-${{ matrix.os }} +# path: ${{ steps.artifacts.outputs.VIDEOS_DIR }} diff --git a/.github/workflows/tests.integration.yaml b/.github/workflows/tests.integration.yaml index 7cd9c856b..139f6a393 100644 --- a/.github/workflows/tests.integration.yaml +++ b/.github/workflows/tests.integration.yaml @@ -1,28 +1,28 @@ -name: integration-tests +# name: integration-tests -on: - push: - pull_request: - schedule: # To get notified about problems from third party dependencies - - cron: '0 0 * * 0' # at 00:00 on every Sunday +# on: +# push: +# pull_request: +# schedule: # To get notified about problems from third party dependencies +# - cron: '0 0 * * 0' # at 00:00 on every Sunday -jobs: - run-tests: - strategy: - matrix: - os: [macos, ubuntu, windows] - fail-fast: false # So it still runs on other OSes if one of them fails - runs-on: ${{ matrix.os }}-latest - steps: - - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup node - uses: ./.github/actions/setup-node - - - name: Install dependencies - uses: ./.github/actions/npm-install-dependencies - - - name: Run integration tests - run: npm run test:integration +# jobs: +# run-tests: +# strategy: +# matrix: +# os: [macos, ubuntu, windows] +# fail-fast: false # So it still runs on other OSes if one of them fails +# runs-on: ${{ matrix.os }}-latest +# steps: +# - +# name: Checkout +# uses: actions/checkout@v4 +# - +# name: Setup node +# uses: ./.github/actions/setup-node +# - +# name: Install dependencies +# uses: ./.github/actions/npm-install-dependencies +# - +# name: Run integration tests +# run: npm run test:integration diff --git a/.github/workflows/tests.unit.yaml b/.github/workflows/tests.unit.yaml index 53853aa8a..0cd06d85e 100644 --- a/.github/workflows/tests.unit.yaml +++ b/.github/workflows/tests.unit.yaml @@ -1,26 +1,26 @@ -name: unit-tests +# name: unit-tests -on: - push: - pull_request: +# on: +# push: +# pull_request: -jobs: - run-tests: - strategy: - matrix: - os: [macos, ubuntu, windows] - fail-fast: false # So it still runs on other OSes if one of them fails - runs-on: ${{ matrix.os }}-latest - steps: - - - name: Checkout - uses: actions/checkout@v4 - - - name: Set-up node - uses: ./.github/actions/setup-node - - - name: Install dependencies - uses: ./.github/actions/npm-install-dependencies - - - name: Run unit tests - run: npm run test:unit +# jobs: +# run-tests: +# strategy: +# matrix: +# os: [macos, ubuntu, windows] +# fail-fast: false # So it still runs on other OSes if one of them fails +# runs-on: ${{ matrix.os }}-latest +# steps: +# - +# name: Checkout +# uses: actions/checkout@v4 +# - +# name: Set-up node +# uses: ./.github/actions/setup-node +# - +# name: Install dependencies +# uses: ./.github/actions/npm-install-dependencies +# - +# name: Run unit tests +# run: npm run test:unit