Skip to content

bcgov/action-test-and-analyse

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

BREAKING CHANGES in v1.0:

  • node_version is now required (previously defaulted to 16)
  • sonar_comment_token has been removed (ignored by SonarCloud)
  • sonar_project_token has been renamed sonar_token

Issues Pull Requests MIT License Lifecycle

Universal Test and Analyze with Triggers, SonarCloud, and Multi-Language Support

This action runs tests and analysis across the BC Gov ecosystem, optionally sending results and coverage to SonarCloud. It supports Node.js, Java, and Python projects with unified reporting, supply chain scanning, and dependency analysis.

Important

Deprecation Notice: This action now natively supports Java. If you are using bcgov/action-test-and-analyse-java, you should migrate to this universal action. The Java-specific repository is now considered redundant.

Conditional triggers are used to determine whether tests need to be run. If triggers are matched, the appropriate code is tested. Tests always run if no triggers are provided.

Supported Languages:

  • Node.js: Full support for Vitest/Jest/Mocha, Knip analysis, and Safe-Chain scanning.
  • Java: Native support for Maven/Gradle with integrated JUnit reporting and SonarCloud plugin support.
  • Python: Support for Pytest/Unittest with JUnit XML results and SonarCloud analysis.

Usage

- uses: bcgov/action-test-and-analyse@v2
  with:
    ### Language Selection
    # Primary language of the project. Options: node (default), java, python
    language: node

    ### Required
    # Commands to run tests (e.g., 'npm test' or 'mvn verify')
    commands: |
      npm ci
      npm run test:cov

    # Project/app directory (relative to workspace root)
    dir: frontend

    ### Versioning
    # Node.js version (for Node projects). Default: "24"
    node_version: "24"
    
    # Java version (for Java projects). Default: "21"
    java_version: "21"

    # Python version (for Python projects). Default: "3.12"
    python_version: "3.12"

    ### Typical / recommended
    # Sonar token available from sonarcloud.io
    sonar_token: ${{ secrets.SONAR_TOKEN }}

    # Sonar arguments (https://docs.sonarcloud.io/advanced-setup/analysis-parameters/)
    sonar_args: |
        -Dsonar.organization=bcgov-sonarcloud
        -Dsonar.projectKey=bcgov_${{ github.repository }}

    # Bash array to diff for build triggering (e.g., ('frontend/'))
    # Optional; if omitted, tests always run.
    triggers: ""

    # Enable supply chain attack detection (Node only). Default: true
    supply_scan: true

    # Knip dependency analysis (Node only). Options: off, warn (default), error
    dep_scan: warn

    ### Usually a bad idea / not recommended

    # Overrides the default branch to diff against
    # Defaults to the default branch, usually `main`
    diff_branch: ${{ github.event.repository.default_branch }}

    # Repository to clone and process
    # Useful for consuming other repos, like in testing
    # Defaults to the current one
    repository: ${{ github.repository }}

    # Branch to clone and process
    # Useful for consuming non-default branches, like in testing
    # Defants to empty, cloning the default branch
    branch: ""

Example, Single Directory with SonarCloud Analysis, Supply Chain Scanning, and Dependency/Export Analysis

Run tests and provide results to SonarCloud. This is a full workflow that runs on pull requests, merge to main and workflow_dispatch. Use a GitHub Action secret to provide ${{ secrets.SONAR_TOKEN }}.

The specified triggers will be used to decide whether this job runs tests and analysis or just exits successfully.

This example demonstrates the default behavior with supply chain scanning enabled (scans packages before installation) and Knip analysis set to error mode (detects unused dependencies and exports).

Create or modify a GitHub workflow, like below. E.g. ./github/workflows/tests.yml

Note: Provide an unpopulated SONAR_TOKEN until one is provisioned. SonarCloud will only run once populated, allowing for pre-setup.


# Example: Java (Maven) with Native SonarCloud Reporting

For Java projects, it is recommended to use the native Maven Sonar plugin. The action automatically exposes `SONAR_TOKEN` to your commands.

```yaml
jobs:
  tests:
    name: Java Tests
    runs-on: ubuntu-24.04
    steps:
      - uses: bcgov/action-test-and-analyse@v2
        with:
          language: java
          java_version: "21"
          commands: |
            mvn -B verify sonar:sonar \
              -Dsonar.organization=bcgov-sonarcloud \
              -Dsonar.projectKey=bcgov_your-project-key
          dir: backend
          sonar_token: ${{ secrets.SONAR_TOKEN }}
          triggers: ('backend/' 'pom.xml')

Example: Python with Pytest and JUnit Reporting

The action automatically parses JUnit XML files found in your project to generate rich step summaries.

jobs:
  tests:
    name: Python Tests
    runs-on: ubuntu-24.04
    steps:
      - uses: bcgov/action-test-and-analyse@v2
        with:
          language: python
          python_version: "3.12"
          commands: |
            pip install -r requirements.txt
            pytest --junitxml=junit.xml
          dir: app
          triggers: ('app/' 'requirements.txt')

Example, Only Running Tests (No SonarCloud, No Dependency/Export Analysis), No Triggers

No triggers are provided so tests will always run. SonarCloud is skipped, and dependency/export analysis is skipped. Supply chain scanning remains enabled by default (recommended).

jobs:
  tests:
    name: Test and Analyze
    runs-on: ubuntu-24.04
    steps:
      - uses: bcgov/action-test-and-analyse@v2
        with:
          commands: |
            npm ci
            npm run test:cov
          dir: frontend
          node_version: "20"
          # supply_scan defaults to true (enabled) - strongly discouraged to disable
          dep_scan: off  # Disable dependency analysis

Example, Matrix / Multiple Directories with Sonar Cloud and Triggers

Test and analyze projects in multiple directories in parallel. This time repository and branch are provided. Please note how secrets must be passed in to composite Actions using the secrets[matrix.variable] syntax.

jobs:
  tests:
    name: Test and Analyze
    runs-on: ubuntu-24.04
    strategy:
      matrix:
        dir: [backend, frontend]
        include:
          - dir: backend
            token: SONAR_TOKEN_BACKEND
            triggers: ('frontend/' 'charts/frontend')
          - dir: frontend
            token: SONAR_TOKEN_FRONTEND
            triggers: ('backend/' 'charts/backend')
    steps:
      - uses: actions/checkout@v5
      - uses: bcgov/action-test-and-analyse@v2
        with:
          commands: |
            npm ci
            npm run test:cov
          dir: ${{ matrix.dir }}
          node_version: "20"
          sonar_args: |
            -Dsonar.exclusions=**/coverage/**,**/node_modules/**
            -Dsonar.organization=bcgov-nr
            -Dsonar.projectKey=bcgov-nr_action-test-and-analyse_${{ matrix.dir }}
          sonar_token: ${{ secrets[matrix.token] }}
          triggers: ${{ matrix.triggers }}
          repository: bcgov/quickstart-openshift
          branch: main

Outputs

Output Description
triggered Whether the action was triggered based on path changes (true/false)

Has the action been triggered by path changes? [true|false]

- id: test
  uses: bcgov/action-test-and-analyse@v2
  with:
    commands: |
      npm ci
      npm run test:cov
    dir: frontend
    node_version: "20"
    triggers: ('frontend/')

- if: steps.test.outputs.triggered == 'true'
  run: echo "✅ Tests were triggered by path changes"

- if: steps.test.outputs.triggered == 'false'
  run: echo "ℹ️ Tests were not triggered (no matching path changes)"

Sonar Project Token

SonarCloud project tokens are free, available from SonarCloud or your organization's aministrators.

For BC Government projects, please create an issue for our platform team.

After sign up, a token should be available from your project on the SonarCloud site. Multirepo projects (e.g. backend, frontend) will have multiple projects. Click Administration > Analysis Method > GitHub Actions (tutorial) to find yours.

E.g. https://sonarcloud.io/project/configuration?id={}&analysisMode=GitHubActions

Supply Chain Scanning

This action supports supply chain attack detection using @aikidosec/safe-chain. Supply chain scanning is enabled by default (default: true) because catching supply chain problems is critical security. When enabled, safe-chain wraps npm commands to scan packages before installation, protecting against malicious code, typosquats, and suspicious scripts.

Default Behavior

Supply chain scanning is enabled by default. No configuration is required - it will automatically scan packages during npm ci and other package manager commands.

⚠️ Disabling Supply Chain Scanning (Not Recommended)

⚠️ WARNING: Disabling supply chain scanning is dangerous and strongly discouraged. It leaves your project vulnerable to malicious packages, typosquatting, and supply chain attacks. Only disable if absolutely necessary and you understand the security risks. If you must disable, set supply_scan: false in your workflow:

- uses: bcgov/action-test-and-analyse@v2
  with:
    commands: |
      npm ci
      npm run test:cov
    dir: frontend
    node_version: "20"
    supply_scan: false  # ⚠️ DANGEROUS: Disables security scanning - not recommended

When enabled, safe-chain will:

  • Scan packages against Aikido's threat intelligence database
  • Block known malicious packages and supply chain attacks (installation will fail if threats are detected)
  • Protect against typosquatting and suspicious install scripts

No additional configuration or API tokens are required. The scanning happens automatically during npm ci and other package manager commands.

Knip - Dependency and Export Analysis

This action supports dependency and export analysis using Knip. When enabled, Knip scans JavaScript/TypeScript projects to identify unused dependencies, devDependencies, and exports, helping keep your codebase clean and maintainable.

Default behavior: Runs in warn mode (shows issues without failing) to encourage adoption without blocking builds. You can disable with dep_scan: off or enforce with dep_scan: error.

How to Use

The dep_scan parameter supports three modes:

  • off - Skip Knip analysis entirely
  • warn - Run Knip and show issues, but don't fail the workflow (default)
  • error - Run Knip and fail the workflow if issues are found

Example: Warn Mode (Default)

- uses: bcgov/action-test-and-analyse@v2
  with:
    commands: |
      npm ci
      npm run test:cov
    dir: frontend
    node_version: "20"
    dep_scan: warn

Example: Error Mode (Enforce Cleanup)

- uses: bcgov/action-test-and-analyse@v2
  with:
    commands: |
      npm ci
      npm run test:cov
    dir: frontend
    node_version: "20"
    dep_scan: error

When enabled, Knip will:

  • Analyze your project for unused dependencies and devDependencies
  • Detect unused exports that can be removed
  • In error mode: Fail the workflow if unused dependencies or exports are found, encouraging cleanup
  • In warn mode: Show issues without failing, allowing teams to see problems without blocking builds

This helps maintain a lean dependency footprint and reduces security surface area by removing unnecessary packages.

Default Configuration

The action provides a default .knip.json configuration with common exceptions to reduce false positives. When no knip_config is provided, this default configuration is written to .knip.json in the project directory and will overwrite any existing .knip.json.

Why These Packages Are Excluded

The default configuration excludes the following packages that are commonly flagged as unused but are actually needed:

  • swagger-ui-express - Peer dependency for NestJS's SwaggerModule.setup(). NestJS dynamically requires this package at runtime, so Knip doesn't detect it as used. This is a common pattern with peer dependencies that are loaded dynamically.

  • rimraf - Build tool commonly used in npm scripts (e.g., "clean": "rimraf dist"). Knip may flag it as unused because it's referenced in package.json scripts rather than imported in code. It's also listed in ignoreBinaries since it's used as a command-line tool.

  • @types/node - TypeScript type definitions for Node.js. These are used by the TypeScript compiler for type checking but aren't directly imported in source code, so Knip may flag them as unused.

  • @types/react and @types/react-dom - TypeScript type definitions for React. Similar to @types/node, these are used by the TypeScript compiler but may not appear as direct imports in your codebase.

Custom Configuration

When knip_config is not provided, the action uses its default configuration. If you need a custom configuration, specify it using the knip_config parameter:

- uses: bcgov/action-test-and-analyse@v2
  with:
    dep_scan: error
    knip_config: "configs/custom.knip.json"  # Path is relative to the GitHub workspace root, not to the `dir` input

Note: The knip_config path is resolved relative to the GitHub workspace root (github.workspace), not relative to the dir input parameter. If you do not provide knip_config, the action will use its default configuration.

Even better, tell us when you encounter false positives! Your contributions are greatly appreciated, so please send suggestions by writing an issue or sending a PR.

Common Exclusion Options

Knip provides several ways to exclude packages and files from analysis:

  • ignoreDependencies - Exclude specific packages from dependency analysis (supports regular expressions)

    {
      "ignoreDependencies": ["hidden-package", "@org/.+"]
    }
  • ignoreBinaries - Exclude binaries that aren't provided by dependencies

    {
      "ignoreBinaries": ["zip", "docker-compose"]
    }
  • ignore - Suppress all issue types for matching files/patterns

    {
      "ignore": ["**/*.d.ts", "**/fixtures"]
    }
  • ignoreWorkspaces - Exclude workspaces in monorepos

    {
      "ignoreWorkspaces": ["packages/go-server"]
    }
  • ignoreExports - Ignore specific exports from analysis

For complete configuration options, see the Knip documentation.

Requirements

  • JavaScript or TypeScript projects only (for Knip analysis)
  • Project must have a package.json file
  • Works best with projects that have clear entry points defined in configuration

Knip supports many JavaScript/TypeScript tools and frameworks out of the box. For advanced configuration beyond exclusions, you can also use knip.json or knip.ts configuration files. See Knip documentation for all available options.

Feedback

Please contribute your ideas! Issues and pull requests are appreciated.

About

GitHub Action to change run node unit tests and analyse with SonarCloud.

Resources

License

Stars

Watchers

Forks

Contributors