From 63a177701a545c72c896e6ea8145cffcd480a63d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:41:37 +0000 Subject: [PATCH 1/8] Create build, lint, and test composite actions and update workflow Co-authored-by: neilime <314088+neilime@users.noreply.github.com> --- .github/workflows/continuous-integration.yml | 141 ++------------- actions/build/README.md | 102 +++++++++++ actions/build/action.yml | 136 +++++++++++++++ actions/lint/README.md | 124 +++++++++++++ actions/lint/action.yml | 122 +++++++++++++ actions/test/README.md | 172 +++++++++++++++++++ actions/test/action.yml | 142 +++++++++++++++ 7 files changed, 814 insertions(+), 125 deletions(-) create mode 100644 actions/build/README.md create mode 100644 actions/build/action.yml create mode 100644 actions/lint/README.md create mode 100644 actions/lint/action.yml create mode 100644 actions/test/README.md create mode 100644 actions/test/action.yml diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index da778b6..09848b8 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -276,25 +276,12 @@ jobs: - run: | if [ -f .gitignore ]; then grep -q "self-workflow" .gitignore || echo "self-workflow" >> .gitignore; else echo "self-workflow" >> .gitignore; fi if [ -f .dockerignore ]; then grep -q "self-workflow" .dockerignore || echo "self-workflow" >> .dockerignore; else echo "self-workflow" >> .dockerignore; fi + # jscpd:ignore-end - - id: setup-node - if: inputs.container == '' - uses: ./self-workflow/actions/setup-node - with: - working-directory: ${{ inputs.working-directory }} - dependencies-cache: | - nx - prettier - - - id: get-package-manager - if: inputs.container - uses: ./self-workflow/actions/get-package-manager + - uses: ./self-workflow/actions/lint with: working-directory: ${{ inputs.working-directory }} - # jscpd:ignore-end - - - run: ${{ inputs.container && steps.get-package-manager.outputs.run-script-command || steps.setup-node.outputs.run-script-command }} lint - working-directory: ${{ inputs.working-directory }} + container: ${{ inputs.container != '' }} build: name: ๐Ÿ—๏ธ Build @@ -311,7 +298,7 @@ jobs: # FIXME: This is a workaround for having workflow ref. See https://github.com/orgs/community/discussions/38659 id-token: write outputs: - artifact-id: ${{ steps.build-artifact-id.outputs.artifact-id }} + artifact-id: ${{ steps.build.outputs.artifact-id }} steps: - uses: hoverkraft-tech/ci-github-common/actions/checkout@753288393de1f3d92f687a6761d236ca800f5306 # 0.28.1 if: needs.setup.outputs.build-commands && inputs.container == '' @@ -333,79 +320,17 @@ jobs: if [ -f .gitignore ]; then grep -q "self-workflow" .gitignore || echo "self-workflow" >> .gitignore; else echo "self-workflow" >> .gitignore; fi if [ -f .dockerignore ]; then grep -q "self-workflow" .dockerignore || echo "self-workflow" >> .dockerignore; else echo "self-workflow" >> .dockerignore; fi # jscpd:ignore-end - - if: needs.setup.outputs.build-commands - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - BUILD_ENV: ${{ needs.setup.outputs.build-env }} - BUILD_SECRETS: ${{ secrets.build-secrets }} - with: - script: | - const envInput = process.env.BUILD_ENV || '{}'; - - let buildEnv = {}; - - try { - buildEnv = JSON.parse(envInput); - } catch (e) { - core.setFailed(`Invalid build env JSON: ${e.message}`); - } - - for (const [key, value] of Object.entries(buildEnv)) { - core.exportVariable(key, value); - } - - const secretsInput = process.env.BUILD_SECRETS || ''; - for (const line of secretsInput.split('\n').map(line => line.trim()).filter(Boolean)) { - const [key, ...rest] = line.split('='); - if (!key || !rest.length) { - return core.setFailed(`Invalid build secrets format: ${line}`); - } - const value = rest.join('='); - core.exportVariable(key.trim(), value.trim()); - } - - id: setup-node - if: needs.setup.outputs.build-commands && inputs.container == '' - uses: ./self-workflow/actions/setup-node - with: - working-directory: ${{ inputs.working-directory }} - dependencies-cache: | - nx - gatsby - storybook - - - id: get-package-manager - if: needs.setup.outputs.build-commands && inputs.container - uses: ./self-workflow/actions/get-package-manager + - id: build + if: needs.setup.outputs.build-commands + uses: ./self-workflow/actions/build with: working-directory: ${{ inputs.working-directory }} - - - if: needs.setup.outputs.build-commands - working-directory: ${{ inputs.working-directory }} - env: - BUILD_COMMANDS: ${{ needs.setup.outputs.build-commands }} - RUN_SCRIPT_COMMAND: ${{ inputs.container && steps.get-package-manager.outputs.run-script-command || steps.setup-node.outputs.run-script-command }} - run: | - echo "$BUILD_COMMANDS" | while IFS= read -r COMMAND ; do - # Trim whitespace - COMMAND=$(echo "$COMMAND" | xargs) - - # Skip empty lines - if [ -z "$COMMAND" ]; then - continue - fi - - echo -e "\n - Running $COMMAND" - $RUN_SCRIPT_COMMAND "$COMMAND" - done - - - id: build-artifact-id - if: needs.setup.outputs.build-commands && needs.setup.outputs.build-artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 - with: - name: ${{ fromJSON(needs.setup.outputs.build-artifact).name }} - path: ${{ fromJSON(needs.setup.outputs.build-artifact).paths }} - if-no-files-found: error + build-commands: ${{ needs.setup.outputs.build-commands }} + build-env: ${{ needs.setup.outputs.build-env }} + build-secrets: ${{ secrets.build-secrets }} + build-artifact: ${{ needs.setup.outputs.build-artifact }} + container: ${{ inputs.container != '' }} test: name: ๐Ÿงช Test @@ -422,6 +347,7 @@ jobs: contents: read # FIXME: This is a workaround for having workflow ref. See https://github.com/orgs/community/discussions/38659 id-token: write + pull-requests: write steps: - uses: hoverkraft-tech/ci-github-common/actions/checkout@753288393de1f3d92f687a6761d236ca800f5306 # 0.28.1 if: inputs.container == '' @@ -446,43 +372,8 @@ jobs: if [ -f .gitignore ]; then grep -q "self-workflow" .gitignore || echo "self-workflow" >> .gitignore; else echo "self-workflow" >> .gitignore; fi if [ -f .dockerignore ]; then grep -q "self-workflow" .dockerignore || echo "self-workflow" >> .dockerignore; else echo "self-workflow" >> .dockerignore; fi - - id: setup-node - if: inputs.container == '' - uses: ./self-workflow/actions/setup-node + - uses: ./self-workflow/actions/test with: working-directory: ${{ inputs.working-directory }} - dependencies-cache: | - nx - jest - - - id: get-package-manager - if: needs.setup.outputs.build-commands && inputs.container - uses: ./self-workflow/actions/get-package-manager - with: - working-directory: ${{ inputs.working-directory }} - - - run: ${{ inputs.container && steps.get-package-manager.outputs.run-script-command || steps.setup-node.outputs.run-script-command }} test:ci - working-directory: ${{ inputs.working-directory }} - env: - CI: "true" - - - if: inputs.coverage == 'codecov' && inputs.container - env: - REQUIRED_DEPS: | - git - curl - gpg - run: | - apt-get update - for dep in $REQUIRED_DEPS; do - if ! dpkg -s "$dep" >/dev/null 2>&1; then - apt-get install -y "$dep" - fi - done - - - name: ๐Ÿ“Š Code coverage - if: inputs.coverage == 'codecov' - uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 - with: - use_oidc: true - disable_telem: true + container: ${{ inputs.container != '' }} + coverage: ${{ inputs.coverage }} diff --git a/actions/build/README.md b/actions/build/README.md new file mode 100644 index 0000000..2bec13f --- /dev/null +++ b/actions/build/README.md @@ -0,0 +1,102 @@ +# Build Action + +Composite action to build Node.js projects with support for custom commands, environment variables, and artifact handling. + +## Usage + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/build@main + with: + # Working directory where build commands are executed + # Default: "." + working-directory: "." + + # List of build commands to execute (npm/pnpm/yarn script names) + # Required + build-commands: | + build + compile + + # JSON object of environment variables for the build + # Default: "{}" + build-env: | + { + "NODE_ENV": "production", + "API_URL": "https://api.example.com" + } + + # Multi-line string of secrets in env format + # Default: "" + build-secrets: | + SECRET_KEY=${{ secrets.SECRET_KEY }} + API_TOKEN=${{ secrets.API_TOKEN }} + + # JSON object for artifact upload configuration + # Default: "" + build-artifact: | + { + "name": "build-artifacts", + "paths": "dist/\nbuild/" + } + + # Whether running in container mode (skips checkout and node setup) + # Default: "false" + container: "false" +``` + +## Inputs + +| Name | Description | Required | Default | +| --------------------- | -------------------------------------------------------------------------------- | -------- | ------- | +| `working-directory` | Working directory where build commands are executed | No | `.` | +| `build-commands` | List of build commands to execute (npm/pnpm/yarn script names), one per line | Yes | - | +| `build-env` | JSON object of environment variables to set during the build | No | `{}` | +| `build-secrets` | Multi-line string of secrets in env format (KEY=VALUE) | No | `""` | +| `build-artifact` | JSON object specifying artifact upload configuration | No | `""` | +| `container` | Whether running in container mode (skips checkout and node setup) | No | `false` | + +## Outputs + +| Name | Description | +| ------------- | ------------------------------------------------------ | +| `artifact-id` | ID of the uploaded artifact (if artifact was specified) | + +## Examples + +### Basic build + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/build@main + with: + build-commands: "build" +``` + +### Build with artifact upload + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/build@main + with: + build-commands: | + build + package + build-artifact: | + { + "name": "build-output", + "paths": "dist/" + } +``` + +### Build with environment variables and secrets + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/build@main + with: + build-commands: "build" + build-env: | + { + "NODE_ENV": "production", + "API_URL": "https://api.example.com" + } + build-secrets: | + SECRET_KEY=${{ secrets.SECRET_KEY }} +``` diff --git a/actions/build/action.yml b/actions/build/action.yml new file mode 100644 index 0000000..a76927a --- /dev/null +++ b/actions/build/action.yml @@ -0,0 +1,136 @@ +name: "Build" +description: "Action to build Node.js projects with support for custom commands, environment variables, and artifact handling" +author: Hoverkraft +branding: + icon: package + color: gray-dark + +inputs: + working-directory: + description: | + Working directory where the build commands are executed. + Can be absolute or relative to the repository root. + required: false + default: "." + build-commands: + description: | + List of build commands to execute, one per line. + These are npm/pnpm/yarn script names (e.g., "build", "compile"). + required: true + build-env: + description: | + JSON object of environment variables to set during the build. + Example: {"NODE_ENV": "production", "API_URL": "https://api.example.com"} + required: false + default: "{}" + build-secrets: + description: | + Multi-line string of secrets in env format (KEY=VALUE). + Example: + ``` + SECRET_KEY=${{ secrets.SECRET_KEY }} + API_TOKEN=${{ secrets.API_TOKEN }} + ``` + required: false + default: "" + build-artifact: + description: | + JSON object specifying artifact upload configuration. + Format: {"name": "artifact-name", "paths": "path1\npath2"} + required: false + default: "" + container: + description: "Whether running in container mode (skips checkout and node setup)" + required: false + default: "false" + +outputs: + artifact-id: + description: "ID of the uploaded artifact (if artifact was specified)" + value: ${{ steps.build-artifact-id.outputs.artifact-id }} + +runs: + using: "composite" + steps: + - shell: bash + # FIXME: workaround until will be merged: https://github.com/actions/runner/pull/1684 + run: mkdir -p ./self-build-action/ && cp -r $GITHUB_ACTION_PATH/../* ./self-build-action/ + + - id: setup-node + if: inputs.container != 'true' + uses: ./self-build-action/setup-node + with: + working-directory: ${{ inputs.working-directory }} + dependencies-cache: | + nx + gatsby + storybook + + - id: get-package-manager + if: inputs.container == 'true' + uses: ./self-build-action/get-package-manager + with: + working-directory: ${{ inputs.working-directory }} + + - shell: bash + env: + BUILD_ENV: ${{ inputs.build-env }} + BUILD_SECRETS: ${{ inputs.build-secrets }} + run: | + set -e + + # Export build environment variables + BUILD_ENV_JSON="${BUILD_ENV:-"{}"}" + + # Parse and export build env variables + if [ "$BUILD_ENV_JSON" != "{}" ]; then + echo "$BUILD_ENV_JSON" | jq -r 'to_entries[] | "\(.key)=\(.value)"' | while IFS= read -r line; do + if [ -n "$line" ]; then + echo "$line" >> $GITHUB_ENV + fi + done + fi + + # Export build secrets + if [ -n "$BUILD_SECRETS" ]; then + echo "$BUILD_SECRETS" | while IFS= read -r line; do + line=$(echo "$line" | xargs) + if [ -n "$line" ]; then + echo "$line" >> $GITHUB_ENV + fi + done + fi + + - shell: bash + working-directory: ${{ inputs.working-directory }} + env: + BUILD_COMMANDS: ${{ inputs.build-commands }} + RUN_SCRIPT_COMMAND: ${{ inputs.container == 'true' && steps.get-package-manager.outputs.run-script-command || steps.setup-node.outputs.run-script-command }} + run: | + set -e + + echo "$BUILD_COMMANDS" | while IFS= read -r COMMAND ; do + # Trim whitespace + COMMAND=$(echo "$COMMAND" | xargs) + + # Skip empty lines + if [ -z "$COMMAND" ]; then + continue + fi + + echo -e "\n๐Ÿ—๏ธ Running build command: $COMMAND" + $RUN_SCRIPT_COMMAND "$COMMAND" + done + + - id: build-artifact-id + if: inputs.build-artifact != '' + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + with: + name: ${{ fromJSON(inputs.build-artifact).name }} + path: ${{ fromJSON(inputs.build-artifact).paths }} + if-no-files-found: error + + # FIXME: workaround until will be merged: https://github.com/actions/runner/pull/1684 + - shell: bash + if: always() + run: rm -fr ./self-build-action diff --git a/actions/lint/README.md b/actions/lint/README.md new file mode 100644 index 0000000..1151f4b --- /dev/null +++ b/actions/lint/README.md @@ -0,0 +1,124 @@ +# Lint Action + +Composite action to lint Node.js projects with support for pull request reporting and GitHub annotations. + +## Features + +- Runs project lint scripts +- Processes lint report files and converts them to GitHub annotations +- Supports multiple report formats (JSON, XML/Checkstyle) +- Provides PR feedback with inline comments on violations +- Can be configured to fail or warn on errors + +## Usage + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/lint@main + with: + # Working directory where lint commands are executed + # Default: "." + working-directory: "." + + # Whether running in container mode (skips checkout and node setup) + # Default: "false" + container: "false" + + # Path to lint report file for annotation processing + # Default: "" + report-file: "lint-report.json" + + # Whether to fail the action if linting errors are found + # Default: "true" + fail-on-error: "true" +``` + +## Inputs + +| Name | Description | Required | Default | +| --------------------- | -------------------------------------------------------------------------------- | -------- | ------- | +| `working-directory` | Working directory where lint commands are executed | No | `.` | +| `container` | Whether running in container mode (skips checkout and node setup) | No | `false` | +| `report-file` | Path to lint report file to process as GitHub annotations | No | `""` | +| `fail-on-error` | Whether to fail the action if linting errors are found | No | `true` | + +## Supported Report Formats + +### ESLint JSON + +Configure your ESLint to output JSON: + +```json +{ + "scripts": { + "lint": "eslint . --format json --output-file lint-report.json || true" + } +} +``` + +Then use the action: + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/lint@main + with: + report-file: "lint-report.json" +``` + +### Checkstyle XML + +Configure your linter to output Checkstyle XML format: + +```json +{ + "scripts": { + "lint": "eslint . --format checkstyle --output-file lint-report.xml || true" + } +} +``` + +Then use the action: + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/lint@main + with: + report-file: "lint-report.xml" +``` + +## Examples + +### Basic lint + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/lint@main +``` + +### Lint with report processing + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/lint@main + with: + report-file: "eslint-report.json" +``` + +### Lint without failing on errors (warnings only) + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/lint@main + with: + fail-on-error: "false" +``` + +### Lint in a specific directory + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/lint@main + with: + working-directory: "packages/frontend" + report-file: "packages/frontend/lint-report.json" +``` + +## Notes + +- The action expects a `lint` script to be defined in your `package.json` +- Report files are processed after linting completes, regardless of success/failure +- For pull requests, annotations will appear as inline comments on the Files Changed tab +- Supports both ESLint JSON and Checkstyle XML formats out of the box diff --git a/actions/lint/action.yml b/actions/lint/action.yml new file mode 100644 index 0000000..a53b5cb --- /dev/null +++ b/actions/lint/action.yml @@ -0,0 +1,122 @@ +name: "Lint" +description: "Action to lint Node.js projects with support for pull request reporting and annotations" +author: Hoverkraft +branding: + icon: check-circle + color: gray-dark + +inputs: + working-directory: + description: | + Working directory where lint commands are executed. + Can be absolute or relative to the repository root. + required: false + default: "." + container: + description: "Whether running in container mode (skips checkout and node setup)" + required: false + default: "false" + report-file: + description: | + Path to lint report file to process as GitHub annotations. + Supports common formats like checkstyle XML, eslint JSON, etc. + If not specified, no report processing is done. + required: false + default: "" + fail-on-error: + description: "Whether to fail the action if linting errors are found" + required: false + default: "true" + +runs: + using: "composite" + steps: + - shell: bash + # FIXME: workaround until will be merged: https://github.com/actions/runner/pull/1684 + run: mkdir -p ./self-lint-action/ && cp -r $GITHUB_ACTION_PATH/../* ./self-lint-action/ + + - id: setup-node + if: inputs.container != 'true' + uses: ./self-lint-action/setup-node + with: + working-directory: ${{ inputs.working-directory }} + dependencies-cache: | + nx + prettier + + - id: get-package-manager + if: inputs.container == 'true' + uses: ./self-lint-action/get-package-manager + with: + working-directory: ${{ inputs.working-directory }} + + - shell: bash + id: run-lint + working-directory: ${{ inputs.working-directory }} + env: + RUN_SCRIPT_COMMAND: ${{ inputs.container == 'true' && steps.get-package-manager.outputs.run-script-command || steps.setup-node.outputs.run-script-command }} + FAIL_ON_ERROR: ${{ inputs.fail-on-error }} + run: | + set +e # Don't exit on error, we want to process the report + + echo "๐Ÿ‘• Running lint..." + $RUN_SCRIPT_COMMAND lint + LINT_EXIT_CODE=$? + + echo "lint-exit-code=$LINT_EXIT_CODE" >> $GITHUB_OUTPUT + + if [ "$FAIL_ON_ERROR" = "true" ] && [ $LINT_EXIT_CODE -ne 0 ]; then + echo "::error::Linting failed with exit code $LINT_EXIT_CODE" + fi + + # Always exit successfully here; we'll handle the failure later if needed + exit 0 + + - shell: bash + if: inputs.report-file != '' && hashFiles(inputs.report-file) != '' + working-directory: ${{ inputs.working-directory }} + env: + REPORT_FILE: ${{ inputs.report-file }} + run: | + set -e + + if [ ! -f "$REPORT_FILE" ]; then + echo "::warning::Lint report file not found: $REPORT_FILE" + exit 0 + fi + + echo "๐Ÿ“Š Processing lint report: $REPORT_FILE" + + # Detect report format and process accordingly + if echo "$REPORT_FILE" | grep -q "\.json$"; then + echo "Detected JSON format" + # Process ESLint JSON format + if jq -e 'type == "array"' "$REPORT_FILE" > /dev/null 2>&1; then + jq -r '.[] | select(.errorCount > 0 or .warningCount > 0) | .messages[] | + "::warning file=\(.filePath // "unknown"),line=\(.line // 1),col=\(.column // 1)::\(.message)"' \ + "$REPORT_FILE" || true + fi + elif echo "$REPORT_FILE" | grep -q "\.xml$"; then + echo "Detected XML format (checkstyle)" + # Process checkstyle XML format + # This is a simplified processor; for production, consider using a dedicated tool + if command -v xmllint > /dev/null 2>&1; then + xmllint --xpath '//error' "$REPORT_FILE" 2>/dev/null | \ + sed -E 's/]*>/::warning line=\1,col=\2::\4/g' || true + else + echo "::warning::xmllint not available, skipping XML report processing" + fi + else + echo "::warning::Unknown report format for $REPORT_FILE" + fi + + - shell: bash + if: steps.run-lint.outputs.lint-exit-code != '0' && inputs.fail-on-error == 'true' + run: | + echo "::error::Linting failed. Please fix the errors above." + exit 1 + + # FIXME: workaround until will be merged: https://github.com/actions/runner/pull/1684 + - shell: bash + if: always() + run: rm -fr ./self-lint-action diff --git a/actions/test/README.md b/actions/test/README.md new file mode 100644 index 0000000..f9c65dc --- /dev/null +++ b/actions/test/README.md @@ -0,0 +1,172 @@ +# Test Action + +Composite action to test Node.js projects with support for coverage reporting, pull request annotations, and multiple coverage reporters. + +## Features + +- Runs project test scripts +- Supports Codecov and LCOV coverage reporters +- Provides PR feedback with coverage reports +- Can be configured to fail or warn on test failures +- Handles both container and non-container modes + +## Usage + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/test@main + with: + # Working directory where test commands are executed + # Default: "." + working-directory: "." + + # Whether running in container mode (skips checkout and node setup) + # Default: "false" + container: "false" + + # Code coverage reporter: "codecov", "lcov", or "" + # Default: "" + coverage: "codecov" + + # Path to LCOV file (only for "lcov" coverage) + # Default: "coverage/lcov.info" + lcov-file: "coverage/lcov.info" + + # Codecov token (only for private repositories) + # Default: "" + codecov-token: ${{ secrets.CODECOV_TOKEN }} + + # Whether to fail the action if tests fail + # Default: "true" + fail-on-error: "true" +``` + +## Inputs + +| Name | Description | Required | Default | +| --------------------- | -------------------------------------------------------------------------------- | -------- | -------------------- | +| `working-directory` | Working directory where test commands are executed | No | `.` | +| `container` | Whether running in container mode (skips checkout and node setup) | No | `false` | +| `coverage` | Code coverage reporter: "codecov", "lcov", or "" | No | `""` | +| `lcov-file` | Path to LCOV file for coverage reporting (used with "lcov" coverage) | No | `coverage/lcov.info` | +| `codecov-token` | Codecov token for private repositories | No | `""` | +| `fail-on-error` | Whether to fail the action if tests fail | No | `true` | + +## Outputs + +| Name | Description | +| ----------------- | ---------------------------- | +| `test-exit-code` | Exit code from the test run | + +## Coverage Reporters + +### Codecov + +Upload coverage to [Codecov](https://codecov.io/): + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/test@main + with: + coverage: "codecov" + # For private repositories: + codecov-token: ${{ secrets.CODECOV_TOKEN }} +``` + +### LCOV Reporter + +Use [LCOV Reporter Action](https://github.com/zgosalvez/github-actions-report-lcov) for PR comments: + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/test@main + with: + coverage: "lcov" + lcov-file: "coverage/lcov.info" +``` + +This will post coverage reports as PR comments. + +## Examples + +### Basic test without coverage + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/test@main +``` + +### Test with Codecov + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/test@main + with: + coverage: "codecov" +``` + +### Test with LCOV reporter for PR comments + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/test@main + with: + coverage: "lcov" + lcov-file: "coverage/lcov.info" +``` + +### Test in a specific directory + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/test@main + with: + working-directory: "packages/api" + coverage: "codecov" +``` + +### Test without failing on errors (warnings only) + +```yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/test@main + with: + fail-on-error: "false" +``` + +## Test Script Requirements + +The action expects a `test:ci` script to be defined in your `package.json`: + +```json +{ + "scripts": { + "test:ci": "jest --coverage --ci" + } +} +``` + +## Coverage File Generation + +For LCOV coverage, ensure your test framework generates an LCOV file: + +### Jest + +```json +{ + "jest": { + "coverageReporters": ["lcov", "text"] + } +} +``` + +### Vitest + +```typescript +export default defineConfig({ + test: { + coverage: { + reporter: ['lcov', 'text'] + } + } +}) +``` + +## Notes + +- The action runs in CI mode (`CI=true`) for consistent behavior +- Coverage reports are generated after tests complete, regardless of success/failure +- For pull requests with LCOV reporter, coverage reports appear as PR comments +- Codecov uses OIDC by default for public repositories (no token needed) diff --git a/actions/test/action.yml b/actions/test/action.yml new file mode 100644 index 0000000..1764354 --- /dev/null +++ b/actions/test/action.yml @@ -0,0 +1,142 @@ +name: "Test" +description: "Action to test Node.js projects with support for coverage reporting and pull request annotations" +author: Hoverkraft +branding: + icon: check-square + color: gray-dark + +inputs: + working-directory: + description: | + Working directory where test commands are executed. + Can be absolute or relative to the repository root. + required: false + default: "." + container: + description: "Whether running in container mode (skips checkout and node setup)" + required: false + default: "false" + coverage: + description: | + Code coverage reporter to use. Supported values: + - "codecov": Upload coverage to Codecov + - "lcov": Use lcov-reporter-action for PR comments + - "": No coverage reporting + required: false + default: "" + lcov-file: + description: | + Path to LCOV file for coverage reporting. + Only used when coverage is set to "lcov". + required: false + default: "coverage/lcov.info" + codecov-token: + description: | + Codecov token for private repositories. + Not required for public repositories when using OIDC. + required: false + default: "" + fail-on-error: + description: "Whether to fail the action if tests fail" + required: false + default: "true" + +outputs: + test-exit-code: + description: "Exit code from the test run" + value: ${{ steps.run-test.outputs.test-exit-code }} + +runs: + using: "composite" + steps: + - shell: bash + # FIXME: workaround until will be merged: https://github.com/actions/runner/pull/1684 + run: mkdir -p ./self-test-action/ && cp -r $GITHUB_ACTION_PATH/../* ./self-test-action/ + + - id: setup-node + if: inputs.container != 'true' + uses: ./self-test-action/setup-node + with: + working-directory: ${{ inputs.working-directory }} + dependencies-cache: | + nx + jest + + - id: get-package-manager + if: inputs.container == 'true' + uses: ./self-test-action/get-package-manager + with: + working-directory: ${{ inputs.working-directory }} + + - shell: bash + id: run-test + working-directory: ${{ inputs.working-directory }} + env: + RUN_SCRIPT_COMMAND: ${{ inputs.container == 'true' && steps.get-package-manager.outputs.run-script-command || steps.setup-node.outputs.run-script-command }} + CI: "true" + FAIL_ON_ERROR: ${{ inputs.fail-on-error }} + run: | + set +e # Don't exit on error, we want to process coverage + + echo "๐Ÿงช Running tests..." + $RUN_SCRIPT_COMMAND test:ci + TEST_EXIT_CODE=$? + + echo "test-exit-code=$TEST_EXIT_CODE" >> $GITHUB_OUTPUT + + if [ "$FAIL_ON_ERROR" = "true" ] && [ $TEST_EXIT_CODE -ne 0 ]; then + echo "::error::Tests failed with exit code $TEST_EXIT_CODE" + fi + + # Always exit successfully here; we'll handle the failure later if needed + exit 0 + + # Install dependencies for codecov in container mode + - shell: bash + if: inputs.coverage == 'codecov' && inputs.container == 'true' + env: + REQUIRED_DEPS: | + git + curl + gpg + run: | + apt-get update + for dep in $REQUIRED_DEPS; do + if ! dpkg -s "$dep" >/dev/null 2>&1; then + apt-get install -y "$dep" + fi + done + + # Upload to Codecov + - name: ๐Ÿ“Š Upload coverage to Codecov + if: inputs.coverage == 'codecov' + uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 + with: + working-directory: ${{ inputs.working-directory }} + token: ${{ inputs.codecov-token }} + use_oidc: ${{ inputs.codecov-token == '' }} + disable_telem: true + fail_ci_if_error: false + + # LCOV Reporter for PR comments + - name: ๐Ÿ“Š Generate coverage report + if: inputs.coverage == 'lcov' && github.event_name == 'pull_request' + uses: zgosalvez/github-actions-report-lcov@f9eec0e06a1b39a77f558c7cd7fb91e0e7dbbb7b # v4.1.17 + with: + coverage-files: ${{ inputs.working-directory }}/${{ inputs.lcov-file }} + minimum-coverage: 0 + artifact-name: coverage-report + github-token: ${{ github.token }} + update-comment: true + working-directory: ${{ inputs.working-directory }} + + - shell: bash + if: steps.run-test.outputs.test-exit-code != '0' && inputs.fail-on-error == 'true' + run: | + echo "::error::Tests failed. Please fix the failing tests." + exit 1 + + # FIXME: workaround until will be merged: https://github.com/actions/runner/pull/1684 + - shell: bash + if: always() + run: rm -fr ./self-test-action From a4fdc4135894b58c64dd3021fcd4b8e73bdb38ea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:47:10 +0000 Subject: [PATCH 2/8] Fix linting issues in new actions Co-authored-by: neilime <314088+neilime@users.noreply.github.com> --- .github/workflows/continuous-integration.yml | 2 -- actions/build/README.md | 20 ++++++------- actions/build/action.yml | 8 +++--- actions/lint/README.md | 12 ++++---- actions/lint/action.yml | 14 ++++----- actions/test/README.md | 30 ++++++++++---------- actions/test/action.yml | 8 +++--- 7 files changed, 46 insertions(+), 48 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 09848b8..496943a 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -277,7 +277,6 @@ jobs: if [ -f .gitignore ]; then grep -q "self-workflow" .gitignore || echo "self-workflow" >> .gitignore; else echo "self-workflow" >> .gitignore; fi if [ -f .dockerignore ]; then grep -q "self-workflow" .dockerignore || echo "self-workflow" >> .dockerignore; else echo "self-workflow" >> .dockerignore; fi # jscpd:ignore-end - - uses: ./self-workflow/actions/lint with: working-directory: ${{ inputs.working-directory }} @@ -320,7 +319,6 @@ jobs: if [ -f .gitignore ]; then grep -q "self-workflow" .gitignore || echo "self-workflow" >> .gitignore; else echo "self-workflow" >> .gitignore; fi if [ -f .dockerignore ]; then grep -q "self-workflow" .dockerignore || echo "self-workflow" >> .dockerignore; else echo "self-workflow" >> .dockerignore; fi # jscpd:ignore-end - - id: build if: needs.setup.outputs.build-commands uses: ./self-workflow/actions/build diff --git a/actions/build/README.md b/actions/build/README.md index 2bec13f..0b3cb2a 100644 --- a/actions/build/README.md +++ b/actions/build/README.md @@ -46,19 +46,19 @@ Composite action to build Node.js projects with support for custom commands, env ## Inputs -| Name | Description | Required | Default | -| --------------------- | -------------------------------------------------------------------------------- | -------- | ------- | -| `working-directory` | Working directory where build commands are executed | No | `.` | -| `build-commands` | List of build commands to execute (npm/pnpm/yarn script names), one per line | Yes | - | -| `build-env` | JSON object of environment variables to set during the build | No | `{}` | -| `build-secrets` | Multi-line string of secrets in env format (KEY=VALUE) | No | `""` | -| `build-artifact` | JSON object specifying artifact upload configuration | No | `""` | -| `container` | Whether running in container mode (skips checkout and node setup) | No | `false` | +| Name | Description | Required | Default | +| ------------------- | ---------------------------------------------------------------------------- | -------- | ------- | +| `working-directory` | Working directory where build commands are executed | No | `.` | +| `build-commands` | List of build commands to execute (npm/pnpm/Yarn script names), one per line | Yes | - | +| `build-env` | JSON object of environment variables to set during the build | No | `{}` | +| `build-secrets` | Multi-line string of secrets in env format (KEY=VALUE) | No | `""` | +| `build-artifact` | JSON object specifying artifact upload configuration | No | `""` | +| `container` | Whether running in container mode (skips checkout and node setup) | No | `false` | ## Outputs -| Name | Description | -| ------------- | ------------------------------------------------------ | +| Name | Description | +| ------------- | ------------------------------------------------------- | | `artifact-id` | ID of the uploaded artifact (if artifact was specified) | ## Examples diff --git a/actions/build/action.yml b/actions/build/action.yml index a76927a..73b4d04 100644 --- a/actions/build/action.yml +++ b/actions/build/action.yml @@ -78,10 +78,10 @@ runs: BUILD_SECRETS: ${{ inputs.build-secrets }} run: | set -e - + # Export build environment variables BUILD_ENV_JSON="${BUILD_ENV:-"{}"}" - + # Parse and export build env variables if [ "$BUILD_ENV_JSON" != "{}" ]; then echo "$BUILD_ENV_JSON" | jq -r 'to_entries[] | "\(.key)=\(.value)"' | while IFS= read -r line; do @@ -90,7 +90,7 @@ runs: fi done fi - + # Export build secrets if [ -n "$BUILD_SECRETS" ]; then echo "$BUILD_SECRETS" | while IFS= read -r line; do @@ -108,7 +108,7 @@ runs: RUN_SCRIPT_COMMAND: ${{ inputs.container == 'true' && steps.get-package-manager.outputs.run-script-command || steps.setup-node.outputs.run-script-command }} run: | set -e - + echo "$BUILD_COMMANDS" | while IFS= read -r COMMAND ; do # Trim whitespace COMMAND=$(echo "$COMMAND" | xargs) diff --git a/actions/lint/README.md b/actions/lint/README.md index 1151f4b..bac2d69 100644 --- a/actions/lint/README.md +++ b/actions/lint/README.md @@ -34,12 +34,12 @@ Composite action to lint Node.js projects with support for pull request reportin ## Inputs -| Name | Description | Required | Default | -| --------------------- | -------------------------------------------------------------------------------- | -------- | ------- | -| `working-directory` | Working directory where lint commands are executed | No | `.` | -| `container` | Whether running in container mode (skips checkout and node setup) | No | `false` | -| `report-file` | Path to lint report file to process as GitHub annotations | No | `""` | -| `fail-on-error` | Whether to fail the action if linting errors are found | No | `true` | +| Name | Description | Required | Default | +| ------------------- | ----------------------------------------------------------------- | -------- | ------- | +| `working-directory` | Working directory where lint commands are executed | No | `.` | +| `container` | Whether running in container mode (skips checkout and node setup) | No | `false` | +| `report-file` | Path to lint report file to process as GitHub annotations | No | `""` | +| `fail-on-error` | Whether to fail the action if linting errors are found | No | `true` | ## Supported Report Formats diff --git a/actions/lint/action.yml b/actions/lint/action.yml index a53b5cb..7c03f4d 100644 --- a/actions/lint/action.yml +++ b/actions/lint/action.yml @@ -58,17 +58,17 @@ runs: FAIL_ON_ERROR: ${{ inputs.fail-on-error }} run: | set +e # Don't exit on error, we want to process the report - + echo "๐Ÿ‘• Running lint..." $RUN_SCRIPT_COMMAND lint LINT_EXIT_CODE=$? - + echo "lint-exit-code=$LINT_EXIT_CODE" >> $GITHUB_OUTPUT - + if [ "$FAIL_ON_ERROR" = "true" ] && [ $LINT_EXIT_CODE -ne 0 ]; then echo "::error::Linting failed with exit code $LINT_EXIT_CODE" fi - + # Always exit successfully here; we'll handle the failure later if needed exit 0 @@ -79,14 +79,14 @@ runs: REPORT_FILE: ${{ inputs.report-file }} run: | set -e - + if [ ! -f "$REPORT_FILE" ]; then echo "::warning::Lint report file not found: $REPORT_FILE" exit 0 fi - + echo "๐Ÿ“Š Processing lint report: $REPORT_FILE" - + # Detect report format and process accordingly if echo "$REPORT_FILE" | grep -q "\.json$"; then echo "Detected JSON format" diff --git a/actions/test/README.md b/actions/test/README.md index f9c65dc..f8bc6aa 100644 --- a/actions/test/README.md +++ b/actions/test/README.md @@ -42,20 +42,20 @@ Composite action to test Node.js projects with support for coverage reporting, p ## Inputs -| Name | Description | Required | Default | -| --------------------- | -------------------------------------------------------------------------------- | -------- | -------------------- | -| `working-directory` | Working directory where test commands are executed | No | `.` | -| `container` | Whether running in container mode (skips checkout and node setup) | No | `false` | -| `coverage` | Code coverage reporter: "codecov", "lcov", or "" | No | `""` | -| `lcov-file` | Path to LCOV file for coverage reporting (used with "lcov" coverage) | No | `coverage/lcov.info` | -| `codecov-token` | Codecov token for private repositories | No | `""` | -| `fail-on-error` | Whether to fail the action if tests fail | No | `true` | +| Name | Description | Required | Default | +| ------------------- | -------------------------------------------------------------------- | -------- | -------------------- | +| `working-directory` | Working directory where test commands are executed | No | `.` | +| `container` | Whether running in container mode (skips checkout and node setup) | No | `false` | +| `coverage` | Code coverage reporter: "Codecov", "lcov", or "" | No | `""` | +| `lcov-file` | Path to LCOV file for coverage reporting (used with "lcov" coverage) | No | `coverage/lcov.info` | +| `codecov-token` | Codecov token for private repositories | No | `""` | +| `fail-on-error` | Whether to fail the action if tests fail | No | `true` | ## Outputs -| Name | Description | -| ----------------- | ---------------------------- | -| `test-exit-code` | Exit code from the test run | +| Name | Description | +| ---------------- | --------------------------- | +| `test-exit-code` | Exit code from the test run | ## Coverage Reporters @@ -158,10 +158,10 @@ For LCOV coverage, ensure your test framework generates an LCOV file: export default defineConfig({ test: { coverage: { - reporter: ['lcov', 'text'] - } - } -}) + reporter: ["lcov", "text"], + }, + }, +}); ``` ## Notes diff --git a/actions/test/action.yml b/actions/test/action.yml index 1764354..249d1eb 100644 --- a/actions/test/action.yml +++ b/actions/test/action.yml @@ -77,17 +77,17 @@ runs: FAIL_ON_ERROR: ${{ inputs.fail-on-error }} run: | set +e # Don't exit on error, we want to process coverage - + echo "๐Ÿงช Running tests..." $RUN_SCRIPT_COMMAND test:ci TEST_EXIT_CODE=$? - + echo "test-exit-code=$TEST_EXIT_CODE" >> $GITHUB_OUTPUT - + if [ "$FAIL_ON_ERROR" = "true" ] && [ $TEST_EXIT_CODE -ne 0 ]; then echo "::error::Tests failed with exit code $TEST_EXIT_CODE" fi - + # Always exit successfully here; we'll handle the failure later if needed exit 0 From 2b5243a44f0a22006d2389b9032cb47ec9f0567e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:51:04 +0000 Subject: [PATCH 3/8] Fix github-token handling in test action and update documentation Co-authored-by: neilime <314088+neilime@users.noreply.github.com> --- .github/workflows/continuous-integration.yml | 1 + actions/lint/action.yml | 2 +- actions/test/README.md | 6 ++++++ actions/test/action.yml | 8 +++++++- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 496943a..c6b9372 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -375,3 +375,4 @@ jobs: working-directory: ${{ inputs.working-directory }} container: ${{ inputs.container != '' }} coverage: ${{ inputs.coverage }} + github-token: ${{ github.token }} diff --git a/actions/lint/action.yml b/actions/lint/action.yml index 7c03f4d..85a826d 100644 --- a/actions/lint/action.yml +++ b/actions/lint/action.yml @@ -73,7 +73,7 @@ runs: exit 0 - shell: bash - if: inputs.report-file != '' && hashFiles(inputs.report-file) != '' + if: inputs.report-file != '' working-directory: ${{ inputs.working-directory }} env: REPORT_FILE: ${{ inputs.report-file }} diff --git a/actions/test/README.md b/actions/test/README.md index f8bc6aa..cca8161 100644 --- a/actions/test/README.md +++ b/actions/test/README.md @@ -35,6 +35,10 @@ Composite action to test Node.js projects with support for coverage reporting, p # Default: "" codecov-token: ${{ secrets.CODECOV_TOKEN }} + # GitHub token (required for "lcov" coverage PR comments) + # Default: "" + github-token: ${{ github.token }} + # Whether to fail the action if tests fail # Default: "true" fail-on-error: "true" @@ -49,6 +53,7 @@ Composite action to test Node.js projects with support for coverage reporting, p | `coverage` | Code coverage reporter: "Codecov", "lcov", or "" | No | `""` | | `lcov-file` | Path to LCOV file for coverage reporting (used with "lcov" coverage) | No | `coverage/lcov.info` | | `codecov-token` | Codecov token for private repositories | No | `""` | +| `github-token` | GitHub token for LCOV reporter PR comments | No | `""` | | `fail-on-error` | Whether to fail the action if tests fail | No | `true` | ## Outputs @@ -107,6 +112,7 @@ This will post coverage reports as PR comments. with: coverage: "lcov" lcov-file: "coverage/lcov.info" + github-token: ${{ github.token }} ``` ### Test in a specific directory diff --git a/actions/test/action.yml b/actions/test/action.yml index 249d1eb..f6d8718 100644 --- a/actions/test/action.yml +++ b/actions/test/action.yml @@ -36,6 +36,12 @@ inputs: Not required for public repositories when using OIDC. required: false default: "" + github-token: + description: | + GitHub token for LCOV reporter PR comments. + Required when coverage is set to "lcov". + required: false + default: "" fail-on-error: description: "Whether to fail the action if tests fail" required: false @@ -126,7 +132,7 @@ runs: coverage-files: ${{ inputs.working-directory }}/${{ inputs.lcov-file }} minimum-coverage: 0 artifact-name: coverage-report - github-token: ${{ github.token }} + github-token: ${{ inputs.github-token }} update-comment: true working-directory: ${{ inputs.working-directory }} From 0bd11ae7d9fae2ee4aa108ab1c4f4d85a13ba79a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:53:23 +0000 Subject: [PATCH 4/8] Fix trailing spaces in build action Co-authored-by: neilime <314088+neilime@users.noreply.github.com> --- actions/build/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actions/build/action.yml b/actions/build/action.yml index 73b4d04..25ea682 100644 --- a/actions/build/action.yml +++ b/actions/build/action.yml @@ -112,12 +112,12 @@ runs: echo "$BUILD_COMMANDS" | while IFS= read -r COMMAND ; do # Trim whitespace COMMAND=$(echo "$COMMAND" | xargs) - + # Skip empty lines if [ -z "$COMMAND" ]; then continue fi - + echo -e "\n๐Ÿ—๏ธ Running build command: $COMMAND" $RUN_SCRIPT_COMMAND "$COMMAND" done From 893bff4c84750fcf7f0668e5f0066a7a92b55e58 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:56:28 +0000 Subject: [PATCH 5/8] Update README with new CI actions section Co-authored-by: neilime <314088+neilime@users.noreply.github.com> Signed-off-by: Emilien Escalle --- README.md | 10 + actions/build/README.md | 196 +++++++++------ actions/build/action.yml | 4 +- actions/dependencies-cache/action.yml | 4 +- actions/get-package-manager/action.yml | 4 +- actions/has-installed-dependencies/action.yml | 4 +- actions/lint/README.md | 165 ++++++------ actions/lint/action.yml | 4 +- actions/setup-node/action.yml | 4 +- actions/test/README.md | 236 +++++++----------- actions/test/action.yml | 4 +- 11 files changed, 306 insertions(+), 329 deletions(-) diff --git a/README.md b/README.md index 0a917f0..f071b42 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,16 @@ This repository centralizes the Hoverkraft toolkit for building, testing, and sh ## Actions +### CI Actions + +_Actions for continuous integration steps: build, lint, and test._ + +#### - [Build](actions/build/README.md) + +#### - [Lint](actions/lint/README.md) + +#### - [Test](actions/test/README.md) + ### Dependencies _Actions dedicated to caching and validating Node.js dependencies._ diff --git a/actions/build/README.md b/actions/build/README.md index 0b3cb2a..09f7589 100644 --- a/actions/build/README.md +++ b/actions/build/README.md @@ -1,102 +1,132 @@ -# Build Action + -Composite action to build Node.js projects with support for custom commands, environment variables, and artifact handling. +# ![Icon](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJmZWF0aGVyIGZlYXRoZXItcGFja2FnZSIgY29sb3I9ImdyYXktZGFyayI+PGxpbmUgeDE9IjE2LjUiIHkxPSI5LjQiIHgyPSI3LjUiIHkyPSI0LjIxIj48L2xpbmU+PHBhdGggZD0iTTIxIDE2VjhhMiAyIDAgMCAwLTEtMS43M2wtNy00YTIgMiAwIDAgMC0yIDBsLTcgNEEyIDIgMCAwIDAgMyA4djhhMiAyIDAgMCAwIDEgMS43M2w3IDRhMiAyIDAgMCAwIDIgMGw3LTRBMiAyIDAgMCAwIDIxIDE2eiI+PC9wYXRoPjxwb2x5bGluZSBwb2ludHM9IjMuMjcgNi45NiAxMiAxMi4wMSAyMC43MyA2Ljk2Ij48L3BvbHlsaW5lPjxsaW5lIHgxPSIxMiIgeTE9IjIyLjA4IiB4Mj0iMTIiIHkyPSIxMiI+PC9saW5lPjwvc3ZnPg==) GitHub Action: Build + +
+ Build +
+ +--- + + + + +[![Marketplace](https://img.shields.io/badge/Marketplace-build-blue?logo=github-actions)](https://github.com/marketplace/actions/build) +[![Release](https://img.shields.io/github/v/release/hoverkraft-tech/ci-github-nodejs)](https://github.com/hoverkraft-tech/ci-github-nodejs/releases) +[![License](https://img.shields.io/github/license/hoverkraft-tech/ci-github-nodejs)](http://choosealicense.com/licenses/mit/) +[![Stars](https://img.shields.io/github/stars/hoverkraft-tech/ci-github-nodejs?style=social)](https://img.shields.io/github/stars/hoverkraft-tech/ci-github-nodejs?style=social) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/hoverkraft-tech/ci-github-nodejs/blob/main/CONTRIBUTING.md) + + + + +## Overview + +Action to build Node.js projects with support for custom commands, environment variables, and artifact handling + + + ## Usage -```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/build@main +````yaml +- uses: hoverkraft-tech/ci-github-nodejs/actions/build@db1c1d36ff3e87c4513eded15d85acb78dcbd9b9 # copilot/refactor-ci-actions-lint-test with: - # Working directory where build commands are executed - # Default: "." - working-directory: "." - - # List of build commands to execute (npm/pnpm/yarn script names) - # Required - build-commands: | - build - compile - - # JSON object of environment variables for the build - # Default: "{}" - build-env: | - { - "NODE_ENV": "production", - "API_URL": "https://api.example.com" - } - - # Multi-line string of secrets in env format - # Default: "" - build-secrets: | - SECRET_KEY=${{ secrets.SECRET_KEY }} - API_TOKEN=${{ secrets.API_TOKEN }} - - # JSON object for artifact upload configuration - # Default: "" - build-artifact: | - { - "name": "build-artifacts", - "paths": "dist/\nbuild/" - } + # Working directory where the build commands are executed. + # Can be absolute or relative to the repository root. + # + # Default: `.` + working-directory: . + + # List of build commands to execute, one per line. + # These are npm/pnpm/yarn script names (e.g., "build", "compile"). + # + # This input is required. + build-commands: "" + + # JSON object of environment variables to set during the build. + # Example: {"NODE_ENV": "production", "API_URL": "https://api.example.com"} + # + # Default: `{}` + build-env: "{}" + + # Multi-line string of secrets in env format (KEY=VALUE). + # Example: + # ``` + # SECRET_KEY=${{ secrets.SECRET_KEY }} + # API_TOKEN=${{ secrets.API_TOKEN }} + # ``` + build-secrets: "" + + # JSON object specifying artifact upload configuration. + # Format: {"name": "artifact-name", "paths": "path1\npath2"} + build-artifact: "" # Whether running in container mode (skips checkout and node setup) - # Default: "false" + # Default: `false` container: "false" -``` +```` + + + ## Inputs -| Name | Description | Required | Default | -| ------------------- | ---------------------------------------------------------------------------- | -------- | ------- | -| `working-directory` | Working directory where build commands are executed | No | `.` | -| `build-commands` | List of build commands to execute (npm/pnpm/Yarn script names), one per line | Yes | - | -| `build-env` | JSON object of environment variables to set during the build | No | `{}` | -| `build-secrets` | Multi-line string of secrets in env format (KEY=VALUE) | No | `""` | -| `build-artifact` | JSON object specifying artifact upload configuration | No | `""` | -| `container` | Whether running in container mode (skips checkout and node setup) | No | `false` | +| **Input** | **Description** | **Required** | **Default** | +| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ----------- | +| **`working-directory`** | Working directory where the build commands are executed. | **false** | `.` | +| | Can be absolute or relative to the repository root. | | | +| **`build-commands`** | List of build commands to execute, one per line. | **true** | - | +| | These are npm/pnpm/Yarn script names (e.g., "build", "compile"). | | | +| **`build-env`** | JSON object of environment variables to set during the build. | **false** | `\{}` | +| | Example: {"NODE_ENV": "production", "API_URL": " | | | +| **`build-secrets`** | Multi-line string of secrets in env format (KEY=VALUE). | **false** | - | +| | Example: | | | +| |
SECRET_KEY=${{ secrets.SECRET_KEY }}
API_TOKEN=${{ secrets.API_TOKEN }}
| | | +| **`build-artifact`** | JSON object specifying artifact upload configuration. | **false** | - | +| | Format: {"name": "artifact-name", "paths": "path1\npath2"} | | | +| **`container`** | Whether running in container mode (skips checkout and node setup) | **false** | `false` | + + + + + ## Outputs -| Name | Description | -| ------------- | ------------------------------------------------------- | -| `artifact-id` | ID of the uploaded artifact (if artifact was specified) | +| **Output** | **Description** | +| ----------------- | ------------------------------------------------------- | +| **`artifact-id`** | ID of the uploaded artifact (if artifact was specified) | -## Examples + + + + -### Basic build +## Contributing -```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/build@main - with: - build-commands: "build" -``` +Contributions are welcome! Please see the [contributing guidelines](https://github.com/hoverkraft-tech/ci-github-nodejs/blob/main/CONTRIBUTING.md) for more details. -### Build with artifact upload + + + + -```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/build@main - with: - build-commands: | - build - package - build-artifact: | - { - "name": "build-output", - "paths": "dist/" - } -``` - -### Build with environment variables and secrets - -```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/build@main - with: - build-commands: "build" - build-env: | - { - "NODE_ENV": "production", - "API_URL": "https://api.example.com" - } - build-secrets: | - SECRET_KEY=${{ secrets.SECRET_KEY }} -``` +## License + +This project is licensed under the MIT License. + +SPDX-License-Identifier: MIT + +Copyright ยฉ 2025 Hoverkraft + +For more details, see the [license](http://choosealicense.com/licenses/mit/). + + + + +--- + +This documentation was automatically generated by [CI Dokumentor](https://github.com/hoverkraft-tech/ci-dokumentor). + + diff --git a/actions/build/action.yml b/actions/build/action.yml index 25ea682..87463d7 100644 --- a/actions/build/action.yml +++ b/actions/build/action.yml @@ -1,9 +1,9 @@ name: "Build" description: "Action to build Node.js projects with support for custom commands, environment variables, and artifact handling" -author: Hoverkraft +author: hoverkraft branding: icon: package - color: gray-dark + color: blue inputs: working-directory: diff --git a/actions/dependencies-cache/action.yml b/actions/dependencies-cache/action.yml index 56d1bff..245c3da 100644 --- a/actions/dependencies-cache/action.yml +++ b/actions/dependencies-cache/action.yml @@ -1,9 +1,9 @@ name: "Dependencies cache" description: "Action to setup dependencies cache managment." -author: Hoverkraft +author: hoverkraft branding: icon: archive - color: gray-dark + color: blue inputs: dependencies: diff --git a/actions/get-package-manager/action.yml b/actions/get-package-manager/action.yml index 944f9a3..35d09e0 100644 --- a/actions/get-package-manager/action.yml +++ b/actions/get-package-manager/action.yml @@ -1,9 +1,9 @@ name: "Get package manager" description: "Action to detect the package manager used. Supports Yarn, pnpm, and npm" -author: Hoverkraft +author: hoverkraft branding: icon: package - color: gray-dark + color: blue inputs: working-directory: diff --git a/actions/has-installed-dependencies/action.yml b/actions/has-installed-dependencies/action.yml index f8e82b9..078f4e0 100644 --- a/actions/has-installed-dependencies/action.yml +++ b/actions/has-installed-dependencies/action.yml @@ -1,9 +1,9 @@ name: "Has installed dependencies" description: "Action to check if dependencies have been installed according to the package manager used." -author: Hoverkraft +author: hoverkraft branding: icon: settings - color: gray-dark + color: blue inputs: dependencies: diff --git a/actions/lint/README.md b/actions/lint/README.md index bac2d69..5f5dc4e 100644 --- a/actions/lint/README.md +++ b/actions/lint/README.md @@ -1,124 +1,105 @@ -# Lint Action + -Composite action to lint Node.js projects with support for pull request reporting and GitHub annotations. +# ![Icon](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJmZWF0aGVyIGZlYXRoZXItY2hlY2stY2lyY2xlIiBjb2xvcj0iZ3JheS1kYXJrIj48cGF0aCBkPSJNMjIgMTEuMDhWMTJhMTAgMTAgMCAxIDEtNS45My05LjE0Ij48L3BhdGg+PHBvbHlsaW5lIHBvaW50cz0iMjIgNCAxMiAxNC4wMSA5IDExLjAxIj48L3BvbHlsaW5lPjwvc3ZnPg==) GitHub Action: Lint -## Features +
+ Lint +
-- Runs project lint scripts -- Processes lint report files and converts them to GitHub annotations -- Supports multiple report formats (JSON, XML/Checkstyle) -- Provides PR feedback with inline comments on violations -- Can be configured to fail or warn on errors +--- + + + + +[![Marketplace](https://img.shields.io/badge/Marketplace-lint-blue?logo=github-actions)](https://github.com/marketplace/actions/lint) +[![Release](https://img.shields.io/github/v/release/hoverkraft-tech/ci-github-nodejs)](https://github.com/hoverkraft-tech/ci-github-nodejs/releases) +[![License](https://img.shields.io/github/license/hoverkraft-tech/ci-github-nodejs)](http://choosealicense.com/licenses/mit/) +[![Stars](https://img.shields.io/github/stars/hoverkraft-tech/ci-github-nodejs?style=social)](https://img.shields.io/github/stars/hoverkraft-tech/ci-github-nodejs?style=social) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/hoverkraft-tech/ci-github-nodejs/blob/main/CONTRIBUTING.md) + + + + +## Overview + +Action to lint Node.js projects with support for pull request reporting and annotations + + + ## Usage ```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/lint@main +- uses: hoverkraft-tech/ci-github-nodejs/actions/lint@db1c1d36ff3e87c4513eded15d85acb78dcbd9b9 # copilot/refactor-ci-actions-lint-test with: - # Working directory where lint commands are executed - # Default: "." - working-directory: "." + # Working directory where lint commands are executed. + # Can be absolute or relative to the repository root. + # + # Default: `.` + working-directory: . # Whether running in container mode (skips checkout and node setup) - # Default: "false" + # Default: `false` container: "false" - # Path to lint report file for annotation processing - # Default: "" - report-file: "lint-report.json" + # Path to lint report file to process as GitHub annotations. + # Supports common formats like checkstyle XML, eslint JSON, etc. + # If not specified, no report processing is done. + report-file: "" # Whether to fail the action if linting errors are found - # Default: "true" + # Default: `true` fail-on-error: "true" ``` -## Inputs - -| Name | Description | Required | Default | -| ------------------- | ----------------------------------------------------------------- | -------- | ------- | -| `working-directory` | Working directory where lint commands are executed | No | `.` | -| `container` | Whether running in container mode (skips checkout and node setup) | No | `false` | -| `report-file` | Path to lint report file to process as GitHub annotations | No | `""` | -| `fail-on-error` | Whether to fail the action if linting errors are found | No | `true` | - -## Supported Report Formats - -### ESLint JSON - -Configure your ESLint to output JSON: - -```json -{ - "scripts": { - "lint": "eslint . --format json --output-file lint-report.json || true" - } -} -``` - -Then use the action: - -```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/lint@main - with: - report-file: "lint-report.json" -``` + + -### Checkstyle XML +## Inputs -Configure your linter to output Checkstyle XML format: +| **Input** | **Description** | **Required** | **Default** | +| ----------------------- | ----------------------------------------------------------------- | ------------ | ----------- | +| **`working-directory`** | Working directory where lint commands are executed. | **false** | `.` | +| | Can be absolute or relative to the repository root. | | | +| **`container`** | Whether running in container mode (skips checkout and node setup) | **false** | `false` | +| **`report-file`** | Path to lint report file to process as GitHub annotations. | **false** | - | +| | Supports common formats like checkstyle XML, ESLint JSON, etc. | | | +| | If not specified, no report processing is done. | | | +| **`fail-on-error`** | Whether to fail the action if linting errors are found | **false** | `true` | -```json -{ - "scripts": { - "lint": "eslint . --format checkstyle --output-file lint-report.xml || true" - } -} -``` + + + + + + + + -Then use the action: +## Contributing -```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/lint@main - with: - report-file: "lint-report.xml" -``` +Contributions are welcome! Please see the [contributing guidelines](https://github.com/hoverkraft-tech/ci-github-nodejs/blob/main/CONTRIBUTING.md) for more details. -## Examples + + + + -### Basic lint +## License -```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/lint@main -``` +This project is licensed under the MIT License. -### Lint with report processing +SPDX-License-Identifier: MIT -```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/lint@main - with: - report-file: "eslint-report.json" -``` - -### Lint without failing on errors (warnings only) +Copyright ยฉ 2025 Hoverkraft -```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/lint@main - with: - fail-on-error: "false" -``` +For more details, see the [license](http://choosealicense.com/licenses/mit/). -### Lint in a specific directory + + -```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/lint@main - with: - working-directory: "packages/frontend" - report-file: "packages/frontend/lint-report.json" -``` +--- -## Notes +This documentation was automatically generated by [CI Dokumentor](https://github.com/hoverkraft-tech/ci-dokumentor). -- The action expects a `lint` script to be defined in your `package.json` -- Report files are processed after linting completes, regardless of success/failure -- For pull requests, annotations will appear as inline comments on the Files Changed tab -- Supports both ESLint JSON and Checkstyle XML formats out of the box + diff --git a/actions/lint/action.yml b/actions/lint/action.yml index 85a826d..d28112b 100644 --- a/actions/lint/action.yml +++ b/actions/lint/action.yml @@ -1,9 +1,9 @@ name: "Lint" description: "Action to lint Node.js projects with support for pull request reporting and annotations" -author: Hoverkraft +author: hoverkraft branding: icon: check-circle - color: gray-dark + color: blue inputs: working-directory: diff --git a/actions/setup-node/action.yml b/actions/setup-node/action.yml index ce6de12..61caef8 100644 --- a/actions/setup-node/action.yml +++ b/actions/setup-node/action.yml @@ -1,9 +1,9 @@ name: "Setup Node.js" description: "Action to setup Node.js and install dependencies according to the package manager used." -author: Hoverkraft +author: hoverkraft branding: icon: settings - color: gray-dark + color: blue inputs: dependencies-cache: diff --git a/actions/test/README.md b/actions/test/README.md index cca8161..9f33872 100644 --- a/actions/test/README.md +++ b/actions/test/README.md @@ -1,178 +1,134 @@ -# Test Action + -Composite action to test Node.js projects with support for coverage reporting, pull request annotations, and multiple coverage reporters. +# ![Icon](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJmZWF0aGVyIGZlYXRoZXItY2hlY2stc3F1YXJlIiBjb2xvcj0iZ3JheS1kYXJrIj48cG9seWxpbmUgcG9pbnRzPSI5IDExIDEyIDE0IDIyIDQiPjwvcG9seWxpbmU+PHBhdGggZD0iTTIxIDEydjdhMiAyIDAgMCAxLTIgMkg1YTIgMiAwIDAgMS0yLTJWNWEyIDIgMCAwIDEgMi0yaDExIj48L3BhdGg+PC9zdmc+) GitHub Action: Test -## Features +
+ Test +
-- Runs project test scripts -- Supports Codecov and LCOV coverage reporters -- Provides PR feedback with coverage reports -- Can be configured to fail or warn on test failures -- Handles both container and non-container modes +--- -## Usage - -```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/test@main - with: - # Working directory where test commands are executed - # Default: "." - working-directory: "." - - # Whether running in container mode (skips checkout and node setup) - # Default: "false" - container: "false" - - # Code coverage reporter: "codecov", "lcov", or "" - # Default: "" - coverage: "codecov" - - # Path to LCOV file (only for "lcov" coverage) - # Default: "coverage/lcov.info" - lcov-file: "coverage/lcov.info" - - # Codecov token (only for private repositories) - # Default: "" - codecov-token: ${{ secrets.CODECOV_TOKEN }} - - # GitHub token (required for "lcov" coverage PR comments) - # Default: "" - github-token: ${{ github.token }} - - # Whether to fail the action if tests fail - # Default: "true" - fail-on-error: "true" -``` + + -## Inputs - -| Name | Description | Required | Default | -| ------------------- | -------------------------------------------------------------------- | -------- | -------------------- | -| `working-directory` | Working directory where test commands are executed | No | `.` | -| `container` | Whether running in container mode (skips checkout and node setup) | No | `false` | -| `coverage` | Code coverage reporter: "Codecov", "lcov", or "" | No | `""` | -| `lcov-file` | Path to LCOV file for coverage reporting (used with "lcov" coverage) | No | `coverage/lcov.info` | -| `codecov-token` | Codecov token for private repositories | No | `""` | -| `github-token` | GitHub token for LCOV reporter PR comments | No | `""` | -| `fail-on-error` | Whether to fail the action if tests fail | No | `true` | +[![Marketplace](https://img.shields.io/badge/Marketplace-test-blue?logo=github-actions)](https://github.com/marketplace/actions/test) +[![Release](https://img.shields.io/github/v/release/hoverkraft-tech/ci-github-nodejs)](https://github.com/hoverkraft-tech/ci-github-nodejs/releases) +[![License](https://img.shields.io/github/license/hoverkraft-tech/ci-github-nodejs)](http://choosealicense.com/licenses/mit/) +[![Stars](https://img.shields.io/github/stars/hoverkraft-tech/ci-github-nodejs?style=social)](https://img.shields.io/github/stars/hoverkraft-tech/ci-github-nodejs?style=social) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/hoverkraft-tech/ci-github-nodejs/blob/main/CONTRIBUTING.md) -## Outputs + + -| Name | Description | -| ---------------- | --------------------------- | -| `test-exit-code` | Exit code from the test run | +## Overview -## Coverage Reporters +Action to test Node.js projects with support for coverage reporting and pull request annotations -### Codecov + + -Upload coverage to [Codecov](https://codecov.io/): +## Usage ```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/test@main +- uses: hoverkraft-tech/ci-github-nodejs/actions/test@db1c1d36ff3e87c4513eded15d85acb78dcbd9b9 # copilot/refactor-ci-actions-lint-test with: - coverage: "codecov" - # For private repositories: - codecov-token: ${{ secrets.CODECOV_TOKEN }} -``` + # Working directory where test commands are executed. + # Can be absolute or relative to the repository root. + # + # Default: `.` + working-directory: . -### LCOV Reporter + # Whether running in container mode (skips checkout and node setup) + # Default: `false` + container: "false" -Use [LCOV Reporter Action](https://github.com/zgosalvez/github-actions-report-lcov) for PR comments: + # Code coverage reporter to use. Supported values: + # - "codecov": Upload coverage to Codecov + # - "lcov": Use lcov-reporter-action for PR comments + # - "": No coverage reporting + coverage: "" -```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/test@main - with: - coverage: "lcov" - lcov-file: "coverage/lcov.info" -``` - -This will post coverage reports as PR comments. + # Path to LCOV file for coverage reporting. + # Only used when coverage is set to "lcov". + # + # Default: `coverage/lcov.info` + lcov-file: coverage/lcov.info -## Examples + # Codecov token for private repositories. + # Not required for public repositories when using OIDC. + codecov-token: "" -### Basic test without coverage + # GitHub token for LCOV reporter PR comments. + # Required when coverage is set to "lcov". + github-token: "" -```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/test@main + # Whether to fail the action if tests fail + # Default: `true` + fail-on-error: "true" ``` -### Test with Codecov + + -```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/test@main - with: - coverage: "codecov" -``` +## Inputs -### Test with LCOV reporter for PR comments +| **Input** | **Description** | **Required** | **Default** | +| ----------------------- | ----------------------------------------------------------------- | ------------ | -------------------- | +| **`working-directory`** | Working directory where test commands are executed. | **false** | `.` | +| | Can be absolute or relative to the repository root. | | | +| **`container`** | Whether running in container mode (skips checkout and node setup) | **false** | `false` | +| **`coverage`** | Code coverage reporter to use. Supported values: | **false** | - | +| | - "Codecov": Upload coverage to Codecov | | | +| | - "lcov": Use lcov-reporter-action for PR comments | | | +| | - "": No coverage reporting | | | +| **`lcov-file`** | Path to LCOV file for coverage reporting. | **false** | `coverage/lcov.info` | +| | Only used when coverage is set to "lcov". | | | +| **`codecov-token`** | Codecov token for private repositories. | **false** | - | +| | Not required for public repositories when using OIDC. | | | +| **`github-token`** | GitHub token for LCOV reporter PR comments. | **false** | - | +| | Required when coverage is set to "lcov". | | | +| **`fail-on-error`** | Whether to fail the action if tests fail | **false** | `true` | + + + + + -```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/test@main - with: - coverage: "lcov" - lcov-file: "coverage/lcov.info" - github-token: ${{ github.token }} -``` +## Outputs -### Test in a specific directory +| **Output** | **Description** | +| -------------------- | --------------------------- | +| **`test-exit-code`** | Exit code from the test run | -```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/test@main - with: - working-directory: "packages/api" - coverage: "codecov" -``` + + + + -### Test without failing on errors (warnings only) +## Contributing -```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/test@main - with: - fail-on-error: "false" -``` +Contributions are welcome! Please see the [contributing guidelines](https://github.com/hoverkraft-tech/ci-github-nodejs/blob/main/CONTRIBUTING.md) for more details. -## Test Script Requirements + + + + -The action expects a `test:ci` script to be defined in your `package.json`: +## License -```json -{ - "scripts": { - "test:ci": "jest --coverage --ci" - } -} -``` +This project is licensed under the MIT License. -## Coverage File Generation +SPDX-License-Identifier: MIT -For LCOV coverage, ensure your test framework generates an LCOV file: +Copyright ยฉ 2025 Hoverkraft -### Jest +For more details, see the [license](http://choosealicense.com/licenses/mit/). -```json -{ - "jest": { - "coverageReporters": ["lcov", "text"] - } -} -``` + + -### Vitest - -```typescript -export default defineConfig({ - test: { - coverage: { - reporter: ["lcov", "text"], - }, - }, -}); -``` +--- -## Notes +This documentation was automatically generated by [CI Dokumentor](https://github.com/hoverkraft-tech/ci-dokumentor). -- The action runs in CI mode (`CI=true`) for consistent behavior -- Coverage reports are generated after tests complete, regardless of success/failure -- For pull requests with LCOV reporter, coverage reports appear as PR comments -- Codecov uses OIDC by default for public repositories (no token needed) + diff --git a/actions/test/action.yml b/actions/test/action.yml index f6d8718..32a2a4a 100644 --- a/actions/test/action.yml +++ b/actions/test/action.yml @@ -1,9 +1,9 @@ name: "Test" description: "Action to test Node.js projects with support for coverage reporting and pull request annotations" -author: Hoverkraft +author: hoverkraft branding: icon: check-square - color: gray-dark + color: blue inputs: working-directory: From ef0509da468b94adeb96fef3f02f7761f6a85fe1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 10:46:07 +0000 Subject: [PATCH 6/8] Refactor actions to use github-script and improve coverage/lint reporting - Replace bash scripts with actions/github-script for better error handling - Change coverage type from "lcov" to "github" with ReportGenerator support - Use eslint-annotate-action for ESLint report processing - Use checkstyle-github-action for Checkstyle report processing - Add always() condition for post-processing steps - Update documentation to reflect new inputs and supported formats - Add jscpd:ignore markers to handle expected README duplication Co-authored-by: neilime <314088+neilime@users.noreply.github.com> --- actions/build/README.md | 2 + actions/build/action.yml | 93 +++++++++++++++++++------------- actions/lint/README.md | 16 +++++- actions/lint/action.yml | 114 ++++++++++++++++++++------------------- actions/test/README.md | 37 +++++++------ actions/test/action.yml | 93 ++++++++++++++++++++------------ 6 files changed, 212 insertions(+), 143 deletions(-) diff --git a/actions/build/README.md b/actions/build/README.md index 09f7589..7670f22 100644 --- a/actions/build/README.md +++ b/actions/build/README.md @@ -87,6 +87,7 @@ Action to build Node.js projects with support for custom commands, environment v | | Format: {"name": "artifact-name", "paths": "path1\npath2"} | | | | **`container`** | Whether running in container mode (skips checkout and node setup) | **false** | `false` | + @@ -130,3 +131,4 @@ For more details, see the [license](http://choosealicense.com/licenses/mit/). This documentation was automatically generated by [CI Dokumentor](https://github.com/hoverkraft-tech/ci-dokumentor). + diff --git a/actions/build/action.yml b/actions/build/action.yml index 87463d7..8533dbe 100644 --- a/actions/build/action.yml +++ b/actions/build/action.yml @@ -72,56 +72,75 @@ runs: with: working-directory: ${{ inputs.working-directory }} - - shell: bash + - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: BUILD_ENV: ${{ inputs.build-env }} BUILD_SECRETS: ${{ inputs.build-secrets }} - run: | - set -e + with: + script: | + const envInput = process.env.BUILD_ENV || '{}'; - # Export build environment variables - BUILD_ENV_JSON="${BUILD_ENV:-"{}"}" + let buildEnv = {}; - # Parse and export build env variables - if [ "$BUILD_ENV_JSON" != "{}" ]; then - echo "$BUILD_ENV_JSON" | jq -r 'to_entries[] | "\(.key)=\(.value)"' | while IFS= read -r line; do - if [ -n "$line" ]; then - echo "$line" >> $GITHUB_ENV - fi - done - fi + try { + buildEnv = JSON.parse(envInput); + } catch (e) { + core.setFailed(`Invalid build env JSON: ${e.message}`); + } - # Export build secrets - if [ -n "$BUILD_SECRETS" ]; then - echo "$BUILD_SECRETS" | while IFS= read -r line; do - line=$(echo "$line" | xargs) - if [ -n "$line" ]; then - echo "$line" >> $GITHUB_ENV - fi - done - fi + for (const [key, value] of Object.entries(buildEnv)) { + core.exportVariable(key, value); + } - - shell: bash - working-directory: ${{ inputs.working-directory }} + const secretsInput = process.env.BUILD_SECRETS || ''; + for (const line of secretsInput.split('\n').map(line => line.trim()).filter(Boolean)) { + const [key, ...rest] = line.split('='); + if (!key || !rest.length) { + return core.setFailed(`Invalid build secrets format: ${line}`); + } + const value = rest.join('='); + core.exportVariable(key.trim(), value.trim()); + } + + # jscpd:ignore-start + - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: BUILD_COMMANDS: ${{ inputs.build-commands }} RUN_SCRIPT_COMMAND: ${{ inputs.container == 'true' && steps.get-package-manager.outputs.run-script-command || steps.setup-node.outputs.run-script-command }} - run: | - set -e - - echo "$BUILD_COMMANDS" | while IFS= read -r COMMAND ; do - # Trim whitespace - COMMAND=$(echo "$COMMAND" | xargs) + WORKING_DIRECTORY: ${{ inputs.working-directory }} + with: + script: | + const { exec } = require('child_process'); + const { promisify } = require('util'); + const execAsync = promisify(exec); + const path = require('path'); - # Skip empty lines - if [ -z "$COMMAND" ]; then - continue - fi + const buildCommands = process.env.BUILD_COMMANDS || ''; + const runScriptCommand = process.env.RUN_SCRIPT_COMMAND; + const workingDirectory = process.env.WORKING_DIRECTORY || '.'; - echo -e "\n๐Ÿ—๏ธ Running build command: $COMMAND" - $RUN_SCRIPT_COMMAND "$COMMAND" - done + const commands = buildCommands.split('\n') + .map(cmd => cmd.trim()) + .filter(Boolean); + for (const command of commands) { + core.info(`\n๐Ÿ—๏ธ Running build command: ${command}`); + + try { + const { stdout, stderr } = await execAsync(`${runScriptCommand} ${command}`, { + cwd: path.resolve(process.env.GITHUB_WORKSPACE, workingDirectory) + }); + + if (stdout) core.info(stdout); + if (stderr) core.warning(stderr); + } catch (error) { + if (error.stdout) core.info(error.stdout); + if (error.stderr) core.error(error.stderr); + core.setFailed(`Build command "${command}" failed with exit code ${error.code || 1}`); + throw error; + } + } + # jscpd:ignore-end - id: build-artifact-id if: inputs.build-artifact != '' uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 diff --git a/actions/lint/README.md b/actions/lint/README.md index 5f5dc4e..be845a6 100644 --- a/actions/lint/README.md +++ b/actions/lint/README.md @@ -43,10 +43,16 @@ Action to lint Node.js projects with support for pull request reporting and anno container: "false" # Path to lint report file to process as GitHub annotations. - # Supports common formats like checkstyle XML, eslint JSON, etc. + # Supports ESLint JSON and Checkstyle XML formats. # If not specified, no report processing is done. report-file: "" + # Format of the lint report file. Supported values: + # - "eslint": ESLint JSON format + # - "checkstyle": Checkstyle XML format + # - "": Auto-detect from file extension + report-format: "" + # Whether to fail the action if linting errors are found # Default: `true` fail-on-error: "true" @@ -63,10 +69,15 @@ Action to lint Node.js projects with support for pull request reporting and anno | | Can be absolute or relative to the repository root. | | | | **`container`** | Whether running in container mode (skips checkout and node setup) | **false** | `false` | | **`report-file`** | Path to lint report file to process as GitHub annotations. | **false** | - | -| | Supports common formats like checkstyle XML, ESLint JSON, etc. | | | +| | Supports ESLint JSON and Checkstyle XML formats. | | | | | If not specified, no report processing is done. | | | +| **`report-format`** | Format of the lint report file. Supported values: | **false** | - | +| | - "ESLint": ESLint JSON format | | | +| | - "checkstyle": Checkstyle XML format | | | +| | - "": Auto-detect from file extension | | | | **`fail-on-error`** | Whether to fail the action if linting errors are found | **false** | `true` | + @@ -103,3 +114,4 @@ For more details, see the [license](http://choosealicense.com/licenses/mit/). This documentation was automatically generated by [CI Dokumentor](https://github.com/hoverkraft-tech/ci-dokumentor). + diff --git a/actions/lint/action.yml b/actions/lint/action.yml index d28112b..e33a495 100644 --- a/actions/lint/action.yml +++ b/actions/lint/action.yml @@ -19,10 +19,18 @@ inputs: report-file: description: | Path to lint report file to process as GitHub annotations. - Supports common formats like checkstyle XML, eslint JSON, etc. + Supports ESLint JSON and Checkstyle XML formats. If not specified, no report processing is done. required: false default: "" + report-format: + description: | + Format of the lint report file. Supported values: + - "eslint": ESLint JSON format + - "checkstyle": Checkstyle XML format + - "": Auto-detect from file extension + required: false + default: "" fail-on-error: description: "Whether to fail the action if linting errors are found" required: false @@ -50,68 +58,66 @@ runs: with: working-directory: ${{ inputs.working-directory }} - - shell: bash - id: run-lint - working-directory: ${{ inputs.working-directory }} + # jscpd:ignore-start + - id: run-lint + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: RUN_SCRIPT_COMMAND: ${{ inputs.container == 'true' && steps.get-package-manager.outputs.run-script-command || steps.setup-node.outputs.run-script-command }} + WORKING_DIRECTORY: ${{ inputs.working-directory }} FAIL_ON_ERROR: ${{ inputs.fail-on-error }} - run: | - set +e # Don't exit on error, we want to process the report - - echo "๐Ÿ‘• Running lint..." - $RUN_SCRIPT_COMMAND lint - LINT_EXIT_CODE=$? - - echo "lint-exit-code=$LINT_EXIT_CODE" >> $GITHUB_OUTPUT - - if [ "$FAIL_ON_ERROR" = "true" ] && [ $LINT_EXIT_CODE -ne 0 ]; then - echo "::error::Linting failed with exit code $LINT_EXIT_CODE" - fi + with: + script: | + const { exec } = require('child_process'); + const { promisify } = require('util'); + const execAsync = promisify(exec); + const path = require('path'); - # Always exit successfully here; we'll handle the failure later if needed - exit 0 + const workingDirectory = process.env.WORKING_DIRECTORY || '.'; + const runScriptCommand = process.env.RUN_SCRIPT_COMMAND; + const failOnError = process.env.FAIL_ON_ERROR === 'true'; - - shell: bash - if: inputs.report-file != '' - working-directory: ${{ inputs.working-directory }} - env: - REPORT_FILE: ${{ inputs.report-file }} - run: | - set -e + core.info('๐Ÿ‘• Running lint...'); - if [ ! -f "$REPORT_FILE" ]; then - echo "::warning::Lint report file not found: $REPORT_FILE" - exit 0 - fi - - echo "๐Ÿ“Š Processing lint report: $REPORT_FILE" + try { + const { stdout, stderr } = await execAsync(`${runScriptCommand} lint`, { + cwd: path.resolve(process.env.GITHUB_WORKSPACE, workingDirectory) + }); + + if (stdout) core.info(stdout); + if (stderr) core.warning(stderr); + + core.setOutput('lint-exit-code', 0); + } catch (error) { + core.setOutput('lint-exit-code', error.code || 1); + + if (error.stdout) core.info(error.stdout); + if (error.stderr) core.error(error.stderr); + + if (failOnError) { + core.setFailed(`Linting failed with exit code ${error.code || 1}`); + } else { + core.warning(`Linting failed with exit code ${error.code || 1}`); + } + } + # jscpd:ignore-end + # Process ESLint report + - name: ๐Ÿ“Š Annotate ESLint results + if: always() && inputs.report-file != '' && (inputs.report-format == 'eslint' || inputs.report-format == '') + uses: ataylorme/eslint-annotate-action@d57a1193d4c59cbfbf2f1529e82e2f8e0da1498d # v3.0.0 + with: + report-json: ${{ inputs.working-directory }}/${{ inputs.report-file }} + fail-on-error: false + fail-on-warning: false - # Detect report format and process accordingly - if echo "$REPORT_FILE" | grep -q "\.json$"; then - echo "Detected JSON format" - # Process ESLint JSON format - if jq -e 'type == "array"' "$REPORT_FILE" > /dev/null 2>&1; then - jq -r '.[] | select(.errorCount > 0 or .warningCount > 0) | .messages[] | - "::warning file=\(.filePath // "unknown"),line=\(.line // 1),col=\(.column // 1)::\(.message)"' \ - "$REPORT_FILE" || true - fi - elif echo "$REPORT_FILE" | grep -q "\.xml$"; then - echo "Detected XML format (checkstyle)" - # Process checkstyle XML format - # This is a simplified processor; for production, consider using a dedicated tool - if command -v xmllint > /dev/null 2>&1; then - xmllint --xpath '//error' "$REPORT_FILE" 2>/dev/null | \ - sed -E 's/]*>/::warning line=\1,col=\2::\4/g' || true - else - echo "::warning::xmllint not available, skipping XML report processing" - fi - else - echo "::warning::Unknown report format for $REPORT_FILE" - fi + # Process Checkstyle report + - name: ๐Ÿ“Š Annotate Checkstyle results + if: always() && inputs.report-file != '' && inputs.report-format == 'checkstyle' + uses: lcollins/checkstyle-github-action@8e0abb97e71a72c2cf9d6f0619e38002cb6e36c9 # v2.0.0 + with: + path: ${{ inputs.working-directory }}/${{ inputs.report-file }} - shell: bash - if: steps.run-lint.outputs.lint-exit-code != '0' && inputs.fail-on-error == 'true' + if: always() && steps.run-lint.outputs.lint-exit-code != '0' && inputs.fail-on-error == 'true' run: | echo "::error::Linting failed. Please fix the errors above." exit 1 diff --git a/actions/test/README.md b/actions/test/README.md index 9f33872..9776d17 100644 --- a/actions/test/README.md +++ b/actions/test/README.md @@ -72,23 +72,25 @@ Action to test Node.js projects with support for coverage reporting and pull req ## Inputs -| **Input** | **Description** | **Required** | **Default** | -| ----------------------- | ----------------------------------------------------------------- | ------------ | -------------------- | -| **`working-directory`** | Working directory where test commands are executed. | **false** | `.` | -| | Can be absolute or relative to the repository root. | | | -| **`container`** | Whether running in container mode (skips checkout and node setup) | **false** | `false` | -| **`coverage`** | Code coverage reporter to use. Supported values: | **false** | - | -| | - "Codecov": Upload coverage to Codecov | | | -| | - "lcov": Use lcov-reporter-action for PR comments | | | -| | - "": No coverage reporting | | | -| **`lcov-file`** | Path to LCOV file for coverage reporting. | **false** | `coverage/lcov.info` | -| | Only used when coverage is set to "lcov". | | | -| **`codecov-token`** | Codecov token for private repositories. | **false** | - | -| | Not required for public repositories when using OIDC. | | | -| **`github-token`** | GitHub token for LCOV reporter PR comments. | **false** | - | -| | Required when coverage is set to "lcov". | | | -| **`fail-on-error`** | Whether to fail the action if tests fail | **false** | `true` | - +| **Input** | **Description** | **Required** | **Default** | +| ----------------------- | --------------------------------------------------------------------- | ------------ | --------------------------------- | +| **`working-directory`** | Working directory where test commands are executed. | **false** | `.` | +| | Can be absolute or relative to the repository root. | | | +| **`container`** | Whether running in container mode (skips checkout and node setup) | **false** | `false` | +| **`coverage`** | Code coverage reporter to use. Supported values: | **false** | - | +| | - "Codecov": Upload coverage to Codecov | | | +| | - "GitHub": Use ReportGenerator for PR comments with coverage reports | | | +| | - "": No coverage reporting | | | +| **`coverage-files`** | Path to coverage files for reporting. | **false** | `coverage/cobertura-coverage.xml` | +| | Supports multiple formats (Cobertura, OpenCover, lcov, etc.). | | | +| | Can be a single file or multiple files separated by semicolons. | | | +| **`codecov-token`** | Codecov token for private repositories. | **false** | - | +| | Not required for public repositories when using OIDC. | | | +| **`github-token`** | GitHub token for coverage PR comments. | **false** | - | +| | Required when coverage is set to "GitHub". | | | +| **`fail-on-error`** | Whether to fail the action if tests fail | **false** | `true` | + + @@ -132,3 +134,4 @@ For more details, see the [license](http://choosealicense.com/licenses/mit/). This documentation was automatically generated by [CI Dokumentor](https://github.com/hoverkraft-tech/ci-dokumentor). + diff --git a/actions/test/action.yml b/actions/test/action.yml index 32a2a4a..d1c4669 100644 --- a/actions/test/action.yml +++ b/actions/test/action.yml @@ -20,16 +20,17 @@ inputs: description: | Code coverage reporter to use. Supported values: - "codecov": Upload coverage to Codecov - - "lcov": Use lcov-reporter-action for PR comments + - "github": Use ReportGenerator for PR comments with coverage reports - "": No coverage reporting required: false default: "" - lcov-file: + coverage-files: description: | - Path to LCOV file for coverage reporting. - Only used when coverage is set to "lcov". + Path to coverage files for reporting. + Supports multiple formats (Cobertura, OpenCover, lcov, etc.). + Can be a single file or multiple files separated by semicolons. required: false - default: "coverage/lcov.info" + default: "coverage/cobertura-coverage.xml" codecov-token: description: | Codecov token for private repositories. @@ -74,29 +75,49 @@ runs: with: working-directory: ${{ inputs.working-directory }} - - shell: bash - id: run-test - working-directory: ${{ inputs.working-directory }} + # jscpd:ignore-start + - id: run-test + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: RUN_SCRIPT_COMMAND: ${{ inputs.container == 'true' && steps.get-package-manager.outputs.run-script-command || steps.setup-node.outputs.run-script-command }} - CI: "true" + WORKING_DIRECTORY: ${{ inputs.working-directory }} FAIL_ON_ERROR: ${{ inputs.fail-on-error }} - run: | - set +e # Don't exit on error, we want to process coverage - - echo "๐Ÿงช Running tests..." - $RUN_SCRIPT_COMMAND test:ci - TEST_EXIT_CODE=$? - - echo "test-exit-code=$TEST_EXIT_CODE" >> $GITHUB_OUTPUT + with: + script: | + const { exec } = require('child_process'); + const { promisify } = require('util'); + const execAsync = promisify(exec); + const path = require('path'); - if [ "$FAIL_ON_ERROR" = "true" ] && [ $TEST_EXIT_CODE -ne 0 ]; then - echo "::error::Tests failed with exit code $TEST_EXIT_CODE" - fi + const workingDirectory = process.env.WORKING_DIRECTORY || '.'; + const runScriptCommand = process.env.RUN_SCRIPT_COMMAND; + const failOnError = process.env.FAIL_ON_ERROR === 'true'; - # Always exit successfully here; we'll handle the failure later if needed - exit 0 + core.info('๐Ÿงช Running tests...'); + try { + const { stdout, stderr } = await execAsync(`${runScriptCommand} test:ci`, { + cwd: path.resolve(process.env.GITHUB_WORKSPACE, workingDirectory), + env: { ...process.env, CI: 'true' } + }); + + if (stdout) core.info(stdout); + if (stderr) core.warning(stderr); + + core.setOutput('test-exit-code', 0); + } catch (error) { + core.setOutput('test-exit-code', error.code || 1); + + if (error.stdout) core.info(error.stdout); + if (error.stderr) core.error(error.stderr); + + if (failOnError) { + core.setFailed(`Tests failed with exit code ${error.code || 1}`); + } else { + core.warning(`Tests failed with exit code ${error.code || 1}`); + } + } + # jscpd:ignore-end # Install dependencies for codecov in container mode - shell: bash if: inputs.coverage == 'codecov' && inputs.container == 'true' @@ -115,7 +136,7 @@ runs: # Upload to Codecov - name: ๐Ÿ“Š Upload coverage to Codecov - if: inputs.coverage == 'codecov' + if: always() && inputs.coverage == 'codecov' uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 with: working-directory: ${{ inputs.working-directory }} @@ -124,20 +145,26 @@ runs: disable_telem: true fail_ci_if_error: false - # LCOV Reporter for PR comments + # ReportGenerator for PR comments with coverage reports - name: ๐Ÿ“Š Generate coverage report - if: inputs.coverage == 'lcov' && github.event_name == 'pull_request' - uses: zgosalvez/github-actions-report-lcov@f9eec0e06a1b39a77f558c7cd7fb91e0e7dbbb7b # v4.1.17 + if: always() && inputs.coverage == 'github' && github.event_name == 'pull_request' + uses: danielpalme/ReportGenerator-GitHub-Action@d78d7a99ab2e53f4fa9e37c558feb9cd6a7a1c70 # v5.4.3 with: - coverage-files: ${{ inputs.working-directory }}/${{ inputs.lcov-file }} - minimum-coverage: 0 - artifact-name: coverage-report - github-token: ${{ inputs.github-token }} - update-comment: true - working-directory: ${{ inputs.working-directory }} + reports: ${{ inputs.coverage-files }} + targetdir: coveragereport + reporttypes: MarkdownSummaryGithub + sourcedirs: ${{ inputs.working-directory }} + + - name: ๐Ÿ“Š Add coverage PR comment + if: always() && inputs.coverage == 'github' && github.event_name == 'pull_request' + uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 # v2.9.0 + with: + recreate: true + path: coveragereport/SummaryGithub.md + GITHUB_TOKEN: ${{ inputs.github-token }} - shell: bash - if: steps.run-test.outputs.test-exit-code != '0' && inputs.fail-on-error == 'true' + if: always() && steps.run-test.outputs.test-exit-code != '0' && inputs.fail-on-error == 'true' run: | echo "::error::Tests failed. Please fix the failing tests." exit 1 From 90dd79cf357d0dc3bda7f9ec4d3ccd7238d5a8b6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 12:27:35 +0000 Subject: [PATCH 7/8] Use @actions/exec instead of child_process and add auto-detection - Replace child_process with @actions/exec package - Use exec.getExecOutput for better error handling - Use node:path instead of path - Add auto-detection for lint report files (eslint-report.json, checkstyle.xml, etc.) - Add auto-detection for coverage files (cobertura-coverage.xml, lcov.info, etc.) - Update documentation to reflect auto-detection capabilities Co-authored-by: neilime <314088+neilime@users.noreply.github.com> --- actions/build/action.yml | 16 +++---- actions/lint/action.yml | 91 ++++++++++++++++++++++++++++++---------- actions/test/action.yml | 78 +++++++++++++++++++++++++--------- 3 files changed, 132 insertions(+), 53 deletions(-) diff --git a/actions/build/action.yml b/actions/build/action.yml index 8533dbe..37ad7c8 100644 --- a/actions/build/action.yml +++ b/actions/build/action.yml @@ -110,10 +110,8 @@ runs: WORKING_DIRECTORY: ${{ inputs.working-directory }} with: script: | - const { exec } = require('child_process'); - const { promisify } = require('util'); - const execAsync = promisify(exec); - const path = require('path'); + const path = require('node:path'); + const exec = require('@actions/exec'); const buildCommands = process.env.BUILD_COMMANDS || ''; const runScriptCommand = process.env.RUN_SCRIPT_COMMAND; @@ -127,16 +125,14 @@ runs: core.info(`\n๐Ÿ—๏ธ Running build command: ${command}`); try { - const { stdout, stderr } = await execAsync(`${runScriptCommand} ${command}`, { + const result = await exec.getExecOutput(runScriptCommand, [command], { cwd: path.resolve(process.env.GITHUB_WORKSPACE, workingDirectory) }); - if (stdout) core.info(stdout); - if (stderr) core.warning(stderr); + if (result.stdout) core.info(result.stdout); + if (result.stderr) core.warning(result.stderr); } catch (error) { - if (error.stdout) core.info(error.stdout); - if (error.stderr) core.error(error.stderr); - core.setFailed(`Build command "${command}" failed with exit code ${error.code || 1}`); + core.setFailed(`Build command "${command}" failed: ${error.message}`); throw error; } } diff --git a/actions/lint/action.yml b/actions/lint/action.yml index e33a495..76997a0 100644 --- a/actions/lint/action.yml +++ b/actions/lint/action.yml @@ -20,7 +20,9 @@ inputs: description: | Path to lint report file to process as GitHub annotations. Supports ESLint JSON and Checkstyle XML formats. - If not specified, no report processing is done. + If not specified, auto-detection will be attempted for common paths: + - eslint-report.json, eslint.json + - checkstyle-result.xml, checkstyle.xml required: false default: "" report-format: @@ -67,10 +69,8 @@ runs: FAIL_ON_ERROR: ${{ inputs.fail-on-error }} with: script: | - const { exec } = require('child_process'); - const { promisify } = require('util'); - const execAsync = promisify(exec); - const path = require('path'); + const path = require('node:path'); + const exec = require('@actions/exec'); const workingDirectory = process.env.WORKING_DIRECTORY || '.'; const runScriptCommand = process.env.RUN_SCRIPT_COMMAND; @@ -79,42 +79,87 @@ runs: core.info('๐Ÿ‘• Running lint...'); try { - const { stdout, stderr } = await execAsync(`${runScriptCommand} lint`, { - cwd: path.resolve(process.env.GITHUB_WORKSPACE, workingDirectory) + const result = await exec.getExecOutput(runScriptCommand, ['lint'], { + cwd: path.resolve(process.env.GITHUB_WORKSPACE, workingDirectory), + ignoreReturnCode: true }); - if (stdout) core.info(stdout); - if (stderr) core.warning(stderr); + if (result.stdout) core.info(result.stdout); + if (result.stderr) core.warning(result.stderr); - core.setOutput('lint-exit-code', 0); - } catch (error) { - core.setOutput('lint-exit-code', error.code || 1); - - if (error.stdout) core.info(error.stdout); - if (error.stderr) core.error(error.stderr); + core.setOutput('lint-exit-code', result.exitCode); - if (failOnError) { - core.setFailed(`Linting failed with exit code ${error.code || 1}`); - } else { - core.warning(`Linting failed with exit code ${error.code || 1}`); + if (result.exitCode !== 0 && failOnError) { + core.setFailed(`Linting failed with exit code ${result.exitCode}`); + } else if (result.exitCode !== 0) { + core.warning(`Linting failed with exit code ${result.exitCode}`); } + } catch (error) { + core.setOutput('lint-exit-code', 1); + core.setFailed(`Linting error: ${error.message}`); } # jscpd:ignore-end + # Auto-detect report file if not specified + - id: detect-report-file + if: always() && inputs.report-file == '' + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + WORKING_DIRECTORY: ${{ inputs.working-directory }} + with: + script: | + const fs = require('node:fs'); + const path = require('node:path'); + + const workingDirectory = process.env.WORKING_DIRECTORY || '.'; + const workDir = path.resolve(process.env.GITHUB_WORKSPACE, workingDirectory); + + // Common lint report file paths + const commonPaths = [ + 'eslint-report.json', + 'eslint.json', + '.eslint-report.json', + 'checkstyle-result.xml', + 'checkstyle.xml', + 'lint-results.xml', + 'reports/eslint-report.json', + 'reports/checkstyle.xml' + ]; + + for (const filePath of commonPaths) { + const fullPath = path.join(workDir, filePath); + if (fs.existsSync(fullPath)) { + core.info(`Auto-detected lint report file: ${filePath}`); + core.setOutput('report-file', filePath); + + // Auto-detect format from extension + if (filePath.endsWith('.json')) { + core.setOutput('report-format', 'eslint'); + } else if (filePath.endsWith('.xml')) { + core.setOutput('report-format', 'checkstyle'); + } + return; + } + } + + core.info('No lint report file auto-detected'); + core.setOutput('report-file', ''); + core.setOutput('report-format', ''); + # Process ESLint report - name: ๐Ÿ“Š Annotate ESLint results - if: always() && inputs.report-file != '' && (inputs.report-format == 'eslint' || inputs.report-format == '') + if: always() && (inputs.report-file != '' || steps.detect-report-file.outputs.report-file != '') && (inputs.report-format == 'eslint' || steps.detect-report-file.outputs.report-format == 'eslint' || (inputs.report-format == '' && steps.detect-report-file.outputs.report-format == '')) uses: ataylorme/eslint-annotate-action@d57a1193d4c59cbfbf2f1529e82e2f8e0da1498d # v3.0.0 with: - report-json: ${{ inputs.working-directory }}/${{ inputs.report-file }} + report-json: ${{ inputs.working-directory }}/${{ inputs.report-file != '' && inputs.report-file || steps.detect-report-file.outputs.report-file }} fail-on-error: false fail-on-warning: false # Process Checkstyle report - name: ๐Ÿ“Š Annotate Checkstyle results - if: always() && inputs.report-file != '' && inputs.report-format == 'checkstyle' + if: always() && (inputs.report-file != '' || steps.detect-report-file.outputs.report-file != '') && (inputs.report-format == 'checkstyle' || steps.detect-report-file.outputs.report-format == 'checkstyle') uses: lcollins/checkstyle-github-action@8e0abb97e71a72c2cf9d6f0619e38002cb6e36c9 # v2.0.0 with: - path: ${{ inputs.working-directory }}/${{ inputs.report-file }} + path: ${{ inputs.working-directory }}/${{ inputs.report-file != '' && inputs.report-file || steps.detect-report-file.outputs.report-file }} - shell: bash if: always() && steps.run-lint.outputs.lint-exit-code != '0' && inputs.fail-on-error == 'true' diff --git a/actions/test/action.yml b/actions/test/action.yml index d1c4669..a6bf944 100644 --- a/actions/test/action.yml +++ b/actions/test/action.yml @@ -29,8 +29,12 @@ inputs: Path to coverage files for reporting. Supports multiple formats (Cobertura, OpenCover, lcov, etc.). Can be a single file or multiple files separated by semicolons. + If not specified, auto-detection will be attempted for common paths: + - coverage/cobertura-coverage.xml, coverage/coverage.xml + - coverage/lcov.info + - coverage/clover.xml required: false - default: "coverage/cobertura-coverage.xml" + default: "" codecov-token: description: | Codecov token for private repositories. @@ -84,10 +88,8 @@ runs: FAIL_ON_ERROR: ${{ inputs.fail-on-error }} with: script: | - const { exec } = require('child_process'); - const { promisify } = require('util'); - const execAsync = promisify(exec); - const path = require('path'); + const path = require('node:path'); + const exec = require('@actions/exec'); const workingDirectory = process.env.WORKING_DIRECTORY || '.'; const runScriptCommand = process.env.RUN_SCRIPT_COMMAND; @@ -96,26 +98,25 @@ runs: core.info('๐Ÿงช Running tests...'); try { - const { stdout, stderr } = await execAsync(`${runScriptCommand} test:ci`, { + const result = await exec.getExecOutput(runScriptCommand, ['test:ci'], { cwd: path.resolve(process.env.GITHUB_WORKSPACE, workingDirectory), - env: { ...process.env, CI: 'true' } + env: { ...process.env, CI: 'true' }, + ignoreReturnCode: true }); - if (stdout) core.info(stdout); - if (stderr) core.warning(stderr); - - core.setOutput('test-exit-code', 0); - } catch (error) { - core.setOutput('test-exit-code', error.code || 1); + if (result.stdout) core.info(result.stdout); + if (result.stderr) core.warning(result.stderr); - if (error.stdout) core.info(error.stdout); - if (error.stderr) core.error(error.stderr); + core.setOutput('test-exit-code', result.exitCode); - if (failOnError) { - core.setFailed(`Tests failed with exit code ${error.code || 1}`); - } else { - core.warning(`Tests failed with exit code ${error.code || 1}`); + if (result.exitCode !== 0 && failOnError) { + core.setFailed(`Tests failed with exit code ${result.exitCode}`); + } else if (result.exitCode !== 0) { + core.warning(`Tests failed with exit code ${result.exitCode}`); } + } catch (error) { + core.setOutput('test-exit-code', 1); + core.setFailed(`Test execution error: ${error.message}`); } # jscpd:ignore-end # Install dependencies for codecov in container mode @@ -134,6 +135,43 @@ runs: fi done + # Auto-detect coverage files if not specified + - id: detect-coverage-files + if: always() && inputs.coverage == 'github' && inputs.coverage-files == '' + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + WORKING_DIRECTORY: ${{ inputs.working-directory }} + with: + script: | + const fs = require('node:fs'); + const path = require('node:path'); + + const workingDirectory = process.env.WORKING_DIRECTORY || '.'; + const workDir = path.resolve(process.env.GITHUB_WORKSPACE, workingDirectory); + + // Common coverage file paths + const commonPaths = [ + 'coverage/cobertura-coverage.xml', + 'coverage/coverage.xml', + 'coverage/lcov.info', + 'coverage/clover.xml', + 'coverage/coverage-final.json', + 'test-results/coverage.xml', + 'test-results/cobertura-coverage.xml' + ]; + + for (const filePath of commonPaths) { + const fullPath = path.join(workDir, filePath); + if (fs.existsSync(fullPath)) { + core.info(`Auto-detected coverage file: ${filePath}`); + core.setOutput('coverage-files', filePath); + return; + } + } + + core.warning('No coverage file auto-detected'); + core.setOutput('coverage-files', 'coverage/cobertura-coverage.xml'); + # Upload to Codecov - name: ๐Ÿ“Š Upload coverage to Codecov if: always() && inputs.coverage == 'codecov' @@ -150,7 +188,7 @@ runs: if: always() && inputs.coverage == 'github' && github.event_name == 'pull_request' uses: danielpalme/ReportGenerator-GitHub-Action@d78d7a99ab2e53f4fa9e37c558feb9cd6a7a1c70 # v5.4.3 with: - reports: ${{ inputs.coverage-files }} + reports: ${{ inputs.coverage-files != '' && inputs.coverage-files || steps.detect-coverage-files.outputs.coverage-files }} targetdir: coveragereport reporttypes: MarkdownSummaryGithub sourcedirs: ${{ inputs.working-directory }} From c23133c146659da98254a0d0d30c9682b33ae0d4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 14:16:39 +0000 Subject: [PATCH 8/8] Simplify actions: remove unnecessary inputs and always use best defaults - Remove require statements for path/exec (available by default in github-script) - Remove report-format input from lint action (always auto-detect) - Remove fail-on-error inputs (always fail on error for consistency) - Change test coverage default to "github" for better DX - Remove codecov-token input (always use OIDC) - Update codecov action to always use OIDC - Replace sticky-pull-request-comment with hoverkraft create-or-update-comment - Use jaxxstorm/action-install-gh-release for codecov dependencies - Remove redundant final failure checks (handled in main script) Co-authored-by: neilime <314088+neilime@users.noreply.github.com> Signed-off-by: Emilien Escalle --- .github/linters/.jscpd.json | 4 + .github/workflows/__main-ci.yml | 2 +- .github/workflows/__pull-request-ci.yml | 2 +- .github/workflows/__shared-ci.yml | 2 +- ...__test-workflow-continuous-integration.yml | 22 +++ .github/workflows/continuous-integration.yml | 75 +++++++-- .gitignore | 1 + actions/build/README.md | 17 ++- actions/build/action.yml | 22 +-- actions/lint/README.md | 56 ++++--- actions/lint/action.yml | 88 +++++------ actions/test/README.md | 94 ++++++------ actions/test/action.yml | 142 ++++++++---------- tests/npm/package-lock.json | 61 ++++---- tests/npm/package.json | 3 +- tests/npm/src/index.js | 5 + tests/npm/src/index.spec.js | 8 + 17 files changed, 329 insertions(+), 275 deletions(-) create mode 100644 .github/linters/.jscpd.json create mode 100644 tests/npm/src/index.js create mode 100644 tests/npm/src/index.spec.js diff --git a/.github/linters/.jscpd.json b/.github/linters/.jscpd.json new file mode 100644 index 0000000..86eddd9 --- /dev/null +++ b/.github/linters/.jscpd.json @@ -0,0 +1,4 @@ +{ + "threshold": 5, + "ignore": ["**/tests/npm/coverage/*", "**/node_modules/*"] +} diff --git a/.github/workflows/__main-ci.yml b/.github/workflows/__main-ci.yml index 3eafe25..a47fa3b 100644 --- a/.github/workflows/__main-ci.yml +++ b/.github/workflows/__main-ci.yml @@ -24,7 +24,7 @@ jobs: actions: read contents: read packages: write - pull-requests: read + pull-requests: write id-token: write issues: read security-events: write diff --git a/.github/workflows/__pull-request-ci.yml b/.github/workflows/__pull-request-ci.yml index dc88b4d..d405dee 100644 --- a/.github/workflows/__pull-request-ci.yml +++ b/.github/workflows/__pull-request-ci.yml @@ -18,7 +18,7 @@ jobs: actions: read contents: read packages: write - pull-requests: read + pull-requests: write id-token: write issues: read security-events: write diff --git a/.github/workflows/__shared-ci.yml b/.github/workflows/__shared-ci.yml index f2e1988..f7b709a 100644 --- a/.github/workflows/__shared-ci.yml +++ b/.github/workflows/__shared-ci.yml @@ -49,7 +49,7 @@ jobs: permissions: contents: read packages: write - pull-requests: read + pull-requests: write id-token: write issues: read security-events: write diff --git a/.github/workflows/__test-workflow-continuous-integration.yml b/.github/workflows/__test-workflow-continuous-integration.yml index 94899e2..9c5e6ec 100644 --- a/.github/workflows/__test-workflow-continuous-integration.yml +++ b/.github/workflows/__test-workflow-continuous-integration.yml @@ -11,6 +11,7 @@ jobs: uses: ./.github/workflows/continuous-integration.yml permissions: contents: read + pull-requests: write security-events: write # FIXME: This is a workaround for having workflow ref. See https://github.com/orgs/community/discussions/38659 id-token: write @@ -35,6 +36,24 @@ jobs: - name: Check the build artifacts run: test -f tests/npm/dist/test.txt + act-without-container-and-github-reports: + name: Act - Run the continuous integration workflow (without container and GitHub reports) + uses: ./.github/workflows/continuous-integration.yml + permissions: + contents: read + pull-requests: write + security-events: write + # FIXME: This is a workaround for having workflow ref. See https://github.com/orgs/community/discussions/38659 + id-token: write + with: + working-directory: tests/npm + build: | + { + "artifact": "dist" + } + test: | + {"coverage": "github"} + arrange-with-container: permissions: id-token: write @@ -63,6 +82,7 @@ jobs: needs: arrange-with-container permissions: contents: read + pull-requests: write security-events: write # FIXME: This is a workaround for having workflow ref. See https://github.com/orgs/community/discussions/38659 id-token: write @@ -73,6 +93,8 @@ jobs: { "artifact": "dist" } + test: | + {"coverage": "codecov"} assert-with-container: name: Assert - Ensure build artifact has been uploaded (with container) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index c6b9372..08dfdae 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -52,10 +52,13 @@ on: required: false default: true lint: - description: "Optional flag to enable linting." - type: boolean + description: | + Whether to enable linting. + Set to `null` or empty to disable. + Accepts a JSON object for lint options. See [lint action](../actions/lint/README.md). + type: string required: false - default: true + default: "true" code-ql: description: "Code QL analysis language. See ." type: string @@ -67,15 +70,13 @@ on: required: false default: true test: - description: "Optional flag to enable test." - type: boolean - required: false - default: true - coverage: - description: "Specify code coverage reporter. Supported values: `codecov`." + description: | + Whether to enable testing. + Set to `null` or empty to disable. + Accepts a JSON object for test options. See [test action](../actions/test/README.md). type: string required: false - default: "codecov" + default: "true" working-directory: description: "Working directory where the dependencies are installed." type: string @@ -247,7 +248,7 @@ jobs: lint: name: ๐Ÿ‘• Lint - if: inputs.checks == true && inputs.lint != false + if: inputs.checks == true && inputs.lint runs-on: ${{ inputs.runs-on && fromJson(inputs.runs-on) || 'ubuntu-latest' }} container: image: ${{ inputs.container != '' && inputs.container || null }} @@ -277,6 +278,25 @@ jobs: if [ -f .gitignore ]; then grep -q "self-workflow" .gitignore || echo "self-workflow" >> .gitignore; else echo "self-workflow" >> .gitignore; fi if [ -f .dockerignore ]; then grep -q "self-workflow" .dockerignore || echo "self-workflow" >> .dockerignore; else echo "self-workflow" >> .dockerignore; fi # jscpd:ignore-end + - id: preparel-lint-options + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + LINT_INPUT: ${{ inputs.lint }} + with: + script: | + const lintInput = process.env.LINT_INPUT.trim(); + + let lintOptions = {}; + if (lintInput && lintInput !== 'true') { + try { + const parsed = JSON.parse(lintInput); + lintOptions = { ...lintOptions, ...parsed }; + } catch (error) { + core.setFailed(`Failed to parse lint input as JSON: ${error.message}`); + return; + } + } + - uses: ./self-workflow/actions/lint with: working-directory: ${{ inputs.working-directory }} @@ -332,7 +352,7 @@ jobs: test: name: ๐Ÿงช Test - if: inputs.checks == true && inputs.test == true + if: inputs.checks == true && inputs.test runs-on: ${{ inputs.runs-on && fromJson(inputs.runs-on) || 'ubuntu-latest' }} container: image: ${{ inputs.container != '' && inputs.container || null }} @@ -343,9 +363,9 @@ jobs: - build permissions: contents: read + pull-requests: write # FIXME: This is a workaround for having workflow ref. See https://github.com/orgs/community/discussions/38659 id-token: write - pull-requests: write steps: - uses: hoverkraft-tech/ci-github-common/actions/checkout@753288393de1f3d92f687a6761d236ca800f5306 # 0.28.1 if: inputs.container == '' @@ -370,9 +390,36 @@ jobs: if [ -f .gitignore ]; then grep -q "self-workflow" .gitignore || echo "self-workflow" >> .gitignore; else echo "self-workflow" >> .gitignore; fi if [ -f .dockerignore ]; then grep -q "self-workflow" .dockerignore || echo "self-workflow" >> .dockerignore; else echo "self-workflow" >> .dockerignore; fi + - id: prepare-test-options + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + TEST_INPUT: ${{ inputs.test }} + with: + script: | + const testInput = process.env.TEST_INPUT.trim(); + + let testOptions = {}; + if (testInput && testInput !== 'true') { + try { + const parsed = JSON.parse(testInput); + testOptions = { ...testOptions, ...parsed }; + } catch (error) { + core.setFailed(`Failed to parse test input as JSON: ${error.message}`); + return; + } + } + + if (testOptions.coverage === undefined) { + testOptions.coverage = 'github'; + } + core.setOutput('coverage', testOptions.coverage ); + + core.setOutput('coverage-files', testOptions['coverage-files'] || ''); + - uses: ./self-workflow/actions/test with: working-directory: ${{ inputs.working-directory }} container: ${{ inputs.container != '' }} - coverage: ${{ inputs.coverage }} + coverage: ${{ steps.prepare-test-options.outputs.coverage }} + coverage-files: ${{ steps.prepare-test-options.outputs['coverage-files'] }} github-token: ${{ github.token }} diff --git a/.gitignore b/.gitignore index 4e3864b..58e75bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /node_modules /tests/*/node_modules +/tests/*/coverage /dist \ No newline at end of file diff --git a/actions/build/README.md b/actions/build/README.md index 7670f22..93907cd 100644 --- a/actions/build/README.md +++ b/actions/build/README.md @@ -1,6 +1,6 @@ -# ![Icon](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJmZWF0aGVyIGZlYXRoZXItcGFja2FnZSIgY29sb3I9ImdyYXktZGFyayI+PGxpbmUgeDE9IjE2LjUiIHkxPSI5LjQiIHgyPSI3LjUiIHkyPSI0LjIxIj48L2xpbmU+PHBhdGggZD0iTTIxIDE2VjhhMiAyIDAgMCAwLTEtMS43M2wtNy00YTIgMiAwIDAgMC0yIDBsLTcgNEEyIDIgMCAwIDAgMyA4djhhMiAyIDAgMCAwIDEgMS43M2w3IDRhMiAyIDAgMCAwIDIgMGw3LTRBMiAyIDAgMCAwIDIxIDE2eiI+PC9wYXRoPjxwb2x5bGluZSBwb2ludHM9IjMuMjcgNi45NiAxMiAxMi4wMSAyMC43MyA2Ljk2Ij48L3BvbHlsaW5lPjxsaW5lIHgxPSIxMiIgeTE9IjIyLjA4IiB4Mj0iMTIiIHkyPSIxMiI+PC9saW5lPjwvc3ZnPg==) GitHub Action: Build +# ![Icon](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJmZWF0aGVyIGZlYXRoZXItcGFja2FnZSIgY29sb3I9ImJsdWUiPjxsaW5lIHgxPSIxNi41IiB5MT0iOS40IiB4Mj0iNy41IiB5Mj0iNC4yMSI+PC9saW5lPjxwYXRoIGQ9Ik0yMSAxNlY4YTIgMiAwIDAgMC0xLTEuNzNsLTctNGEyIDIgMCAwIDAtMiAwbC03IDRBMiAyIDAgMCAwIDMgOHY4YTIgMiAwIDAgMCAxIDEuNzNsNyA0YTIgMiAwIDAgMCAyIDBsNy00QTIgMiAwIDAgMCAyMSAxNnoiPjwvcGF0aD48cG9seWxpbmUgcG9pbnRzPSIzLjI3IDYuOTYgMTIgMTIuMDEgMjAuNzMgNi45NiI+PC9wb2x5bGluZT48bGluZSB4MT0iMTIiIHkxPSIyMi4wOCIgeDI9IjEyIiB5Mj0iMTIiPjwvbGluZT48L3N2Zz4=) GitHub Action: Build
Build @@ -30,7 +30,7 @@ Action to build Node.js projects with support for custom commands, environment v ## Usage ````yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/build@db1c1d36ff3e87c4513eded15d85acb78dcbd9b9 # copilot/refactor-ci-actions-lint-test +- uses: hoverkraft-tech/ci-github-nodejs/actions/build@dde8f0c67661ed66da8871a9fb104d36e146d644 # copilot/refactor-ci-actions-lint-test with: # Working directory where the build commands are executed. # Can be absolute or relative to the repository root. @@ -87,7 +87,6 @@ Action to build Node.js projects with support for custom commands, environment v | | Format: {"name": "artifact-name", "paths": "path1\npath2"} | | | | **`container`** | Whether running in container mode (skips checkout and node setup) | **false** | `false` | - @@ -102,6 +101,11 @@ Action to build Node.js projects with support for custom commands, environment v + + + ## Contributing @@ -119,7 +123,7 @@ This project is licensed under the MIT License. SPDX-License-Identifier: MIT -Copyright ยฉ 2025 Hoverkraft +Copyright ยฉ 2025 hoverkraft For more details, see the [license](http://choosealicense.com/licenses/mit/). @@ -131,4 +135,7 @@ For more details, see the [license](http://choosealicense.com/licenses/mit/). This documentation was automatically generated by [CI Dokumentor](https://github.com/hoverkraft-tech/ci-dokumentor). - + + diff --git a/actions/build/action.yml b/actions/build/action.yml index 37ad7c8..cd1649e 100644 --- a/actions/build/action.yml +++ b/actions/build/action.yml @@ -28,8 +28,8 @@ inputs: Multi-line string of secrets in env format (KEY=VALUE). Example: ``` - SECRET_KEY=${{ secrets.SECRET_KEY }} - API_TOKEN=${{ secrets.API_TOKEN }} + SECRET_KEY=$\{{ secrets.SECRET_KEY }} + API_TOKEN=$\{{ secrets.API_TOKEN }} ``` required: false default: "" @@ -102,7 +102,6 @@ runs: core.exportVariable(key.trim(), value.trim()); } - # jscpd:ignore-start - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: BUILD_COMMANDS: ${{ inputs.build-commands }} @@ -110,9 +109,6 @@ runs: WORKING_DIRECTORY: ${{ inputs.working-directory }} with: script: | - const path = require('node:path'); - const exec = require('@actions/exec'); - const buildCommands = process.env.BUILD_COMMANDS || ''; const runScriptCommand = process.env.RUN_SCRIPT_COMMAND; const workingDirectory = process.env.WORKING_DIRECTORY || '.'; @@ -122,21 +118,25 @@ runs: .filter(Boolean); for (const command of commands) { - core.info(`\n๐Ÿ—๏ธ Running build command: ${command}`); + core.info(`Running build command: ${command}`); try { const result = await exec.getExecOutput(runScriptCommand, [command], { - cwd: path.resolve(process.env.GITHUB_WORKSPACE, workingDirectory) + cwd: require('path').resolve(process.env.GITHUB_WORKSPACE, workingDirectory) }); - if (result.stdout) core.info(result.stdout); - if (result.stderr) core.warning(result.stderr); + if (result.stdout) { + core.info(result.stdout); + } + if (result.stderr) { + core.warning(result.stderr); + } } catch (error) { core.setFailed(`Build command "${command}" failed: ${error.message}`); throw error; } } - # jscpd:ignore-end + - id: build-artifact-id if: inputs.build-artifact != '' uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 diff --git a/actions/lint/README.md b/actions/lint/README.md index be845a6..a24544e 100644 --- a/actions/lint/README.md +++ b/actions/lint/README.md @@ -1,6 +1,6 @@ -# ![Icon](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJmZWF0aGVyIGZlYXRoZXItY2hlY2stY2lyY2xlIiBjb2xvcj0iZ3JheS1kYXJrIj48cGF0aCBkPSJNMjIgMTEuMDhWMTJhMTAgMTAgMCAxIDEtNS45My05LjE0Ij48L3BhdGg+PHBvbHlsaW5lIHBvaW50cz0iMjIgNCAxMiAxNC4wMSA5IDExLjAxIj48L3BvbHlsaW5lPjwvc3ZnPg==) GitHub Action: Lint +# ![Icon](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJmZWF0aGVyIGZlYXRoZXItY2hlY2stY2lyY2xlIiBjb2xvcj0iYmx1ZSI+PHBhdGggZD0iTTIyIDExLjA4VjEyYTEwIDEwIDAgMSAxLTUuOTMtOS4xNCI+PC9wYXRoPjxwb2x5bGluZSBwb2ludHM9IjIyIDQgMTIgMTQuMDEgOSAxMS4wMSI+PC9wb2x5bGluZT48L3N2Zz4=) GitHub Action: Lint
Lint @@ -30,7 +30,7 @@ Action to lint Node.js projects with support for pull request reporting and anno ## Usage ```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/lint@db1c1d36ff3e87c4513eded15d85acb78dcbd9b9 # copilot/refactor-ci-actions-lint-test +- uses: hoverkraft-tech/ci-github-nodejs/actions/lint@dde8f0c67661ed66da8871a9fb104d36e146d644 # copilot/refactor-ci-actions-lint-test with: # Working directory where lint commands are executed. # Can be absolute or relative to the repository root. @@ -44,18 +44,10 @@ Action to lint Node.js projects with support for pull request reporting and anno # Path to lint report file to process as GitHub annotations. # Supports ESLint JSON and Checkstyle XML formats. - # If not specified, no report processing is done. + # If not specified, auto-detection will be attempted for common paths: + # - eslint-report.json, eslint.json + # - checkstyle-result.xml, checkstyle.xml report-file: "" - - # Format of the lint report file. Supported values: - # - "eslint": ESLint JSON format - # - "checkstyle": Checkstyle XML format - # - "": Auto-detect from file extension - report-format: "" - - # Whether to fail the action if linting errors are found - # Default: `true` - fail-on-error: "true" ``` @@ -63,21 +55,17 @@ Action to lint Node.js projects with support for pull request reporting and anno ## Inputs -| **Input** | **Description** | **Required** | **Default** | -| ----------------------- | ----------------------------------------------------------------- | ------------ | ----------- | -| **`working-directory`** | Working directory where lint commands are executed. | **false** | `.` | -| | Can be absolute or relative to the repository root. | | | -| **`container`** | Whether running in container mode (skips checkout and node setup) | **false** | `false` | -| **`report-file`** | Path to lint report file to process as GitHub annotations. | **false** | - | -| | Supports ESLint JSON and Checkstyle XML formats. | | | -| | If not specified, no report processing is done. | | | -| **`report-format`** | Format of the lint report file. Supported values: | **false** | - | -| | - "ESLint": ESLint JSON format | | | -| | - "checkstyle": Checkstyle XML format | | | -| | - "": Auto-detect from file extension | | | -| **`fail-on-error`** | Whether to fail the action if linting errors are found | **false** | `true` | - - +| **Input** | **Description** | **Required** | **Default** | +| ----------------------- | -------------------------------------------------------------------- | ------------ | ----------- | +| **`working-directory`** | Working directory where lint commands are executed. | **false** | `.` | +| | Can be absolute or relative to the repository root. | | | +| **`container`** | Whether running in container mode (skips checkout and node setup) | **false** | `false` | +| **`report-file`** | Path to lint report file to process as GitHub annotations. | **false** | - | +| | Supports ESLint JSON and Checkstyle XML formats. | | | +| | If not specified, auto-detection will be attempted for common paths: | | | +| | - eslint-report.json, eslint.json | | | +| | - checkstyle-result.xml, checkstyle.xml | | | + @@ -85,6 +73,11 @@ Action to lint Node.js projects with support for pull request reporting and anno + + + ## Contributing @@ -102,7 +95,7 @@ This project is licensed under the MIT License. SPDX-License-Identifier: MIT -Copyright ยฉ 2025 Hoverkraft +Copyright ยฉ 2025 hoverkraft For more details, see the [license](http://choosealicense.com/licenses/mit/). @@ -114,4 +107,7 @@ For more details, see the [license](http://choosealicense.com/licenses/mit/). This documentation was automatically generated by [CI Dokumentor](https://github.com/hoverkraft-tech/ci-dokumentor). - + + diff --git a/actions/lint/action.yml b/actions/lint/action.yml index 76997a0..1e8f692 100644 --- a/actions/lint/action.yml +++ b/actions/lint/action.yml @@ -25,18 +25,6 @@ inputs: - checkstyle-result.xml, checkstyle.xml required: false default: "" - report-format: - description: | - Format of the lint report file. Supported values: - - "eslint": ESLint JSON format - - "checkstyle": Checkstyle XML format - - "": Auto-detect from file extension - required: false - default: "" - fail-on-error: - description: "Whether to fail the action if linting errors are found" - required: false - default: "true" runs: using: "composite" @@ -60,27 +48,21 @@ runs: with: working-directory: ${{ inputs.working-directory }} - # jscpd:ignore-start - id: run-lint uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: - RUN_SCRIPT_COMMAND: ${{ inputs.container == 'true' && steps.get-package-manager.outputs.run-script-command || steps.setup-node.outputs.run-script-command }} + RUN_LINT_COMMAND: ${{ inputs.container == 'true' && steps.get-package-manager.outputs.run-script-command || steps.setup-node.outputs.run-script-command }} WORKING_DIRECTORY: ${{ inputs.working-directory }} - FAIL_ON_ERROR: ${{ inputs.fail-on-error }} with: script: | - const path = require('node:path'); - const exec = require('@actions/exec'); - const workingDirectory = process.env.WORKING_DIRECTORY || '.'; - const runScriptCommand = process.env.RUN_SCRIPT_COMMAND; - const failOnError = process.env.FAIL_ON_ERROR === 'true'; + const runScriptCommand = process.env.RUN_LINT_COMMAND; core.info('๐Ÿ‘• Running lint...'); try { const result = await exec.getExecOutput(runScriptCommand, ['lint'], { - cwd: path.resolve(process.env.GITHUB_WORKSPACE, workingDirectory), + cwd: require('path').resolve(process.env.GITHUB_WORKSPACE, workingDirectory), ignoreReturnCode: true }); @@ -89,30 +71,52 @@ runs: core.setOutput('lint-exit-code', result.exitCode); - if (result.exitCode !== 0 && failOnError) { + if (result.exitCode !== 0) { core.setFailed(`Linting failed with exit code ${result.exitCode}`); - } else if (result.exitCode !== 0) { - core.warning(`Linting failed with exit code ${result.exitCode}`); } } catch (error) { core.setOutput('lint-exit-code', 1); core.setFailed(`Linting error: ${error.message}`); } - # jscpd:ignore-end + # Auto-detect report file if not specified - id: detect-report-file - if: always() && inputs.report-file == '' + if: always() uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: + REPORT_FILE: ${{ inputs.report-file }} WORKING_DIRECTORY: ${{ inputs.working-directory }} with: script: | - const fs = require('node:fs'); const path = require('node:path'); + const fs = require('node:fs'); const workingDirectory = process.env.WORKING_DIRECTORY || '.'; const workDir = path.resolve(process.env.GITHUB_WORKSPACE, workingDirectory); + const autoDetectReportFormat = (filePath) => { + if (filePath.endsWith('.json')) { + return 'eslint'; + } else if (filePath.endsWith('.xml')) { + return 'checkstyle'; + } + return core.setFailed(`Unable to auto-detect report format from file extension: ${filePath}`); + }; + + if (process.env.REPORT_FILE && process.env.REPORT_FILE.trim() !== '') { + const reportFilePath = path.join(workDir, process.env.REPORT_FILE.trim()); + + if (!fs.existsSync(reportFilePath)) { + core.setFailed(`Specified report file does not exist: ${process.env.REPORT_FILE.trim()}`); + return; + } + core.info(`Report file specified: ${reportFilePath}`); + core.setOutput('report-file', reportFilePath); + core.setOutput('report-format', autoDetectReportFormat(reportFilePath)); + + return; + } + // Common lint report file paths const commonPaths = [ 'eslint-report.json', @@ -128,44 +132,32 @@ runs: for (const filePath of commonPaths) { const fullPath = path.join(workDir, filePath); if (fs.existsSync(fullPath)) { - core.info(`Auto-detected lint report file: ${filePath}`); - core.setOutput('report-file', filePath); + core.info(`Auto-detected lint report file: ${fullPath}`); + core.setOutput('report-file', fullPath); // Auto-detect format from extension - if (filePath.endsWith('.json')) { - core.setOutput('report-format', 'eslint'); - } else if (filePath.endsWith('.xml')) { - core.setOutput('report-format', 'checkstyle'); - } + core.setOutput('report-format', autoDetectReportFormat(fullPath)); return; } } core.info('No lint report file auto-detected'); - core.setOutput('report-file', ''); - core.setOutput('report-format', ''); # Process ESLint report - name: ๐Ÿ“Š Annotate ESLint results - if: always() && (inputs.report-file != '' || steps.detect-report-file.outputs.report-file != '') && (inputs.report-format == 'eslint' || steps.detect-report-file.outputs.report-format == 'eslint' || (inputs.report-format == '' && steps.detect-report-file.outputs.report-format == '')) - uses: ataylorme/eslint-annotate-action@d57a1193d4c59cbfbf2f1529e82e2f8e0da1498d # v3.0.0 + if: always() && steps.detect-report-file.outputs.report-format == 'eslint' + uses: ataylorme/eslint-annotate-action@d57a1193d4c59cbfbf3f86c271f42612f9dbd9e9 # v3.0.0 with: - report-json: ${{ inputs.working-directory }}/${{ inputs.report-file != '' && inputs.report-file || steps.detect-report-file.outputs.report-file }} + report-json: ${{ steps.detect-report-file.outputs.report-file }} fail-on-error: false fail-on-warning: false # Process Checkstyle report - name: ๐Ÿ“Š Annotate Checkstyle results - if: always() && (inputs.report-file != '' || steps.detect-report-file.outputs.report-file != '') && (inputs.report-format == 'checkstyle' || steps.detect-report-file.outputs.report-format == 'checkstyle') - uses: lcollins/checkstyle-github-action@8e0abb97e71a72c2cf9d6f0619e38002cb6e36c9 # v2.0.0 + if: always() && steps.detect-report-file.outputs.report-format == 'checkstyle' + uses: lcollins/checkstyle-github-action@658deddef713a1132a9c59b64d754221b66a6f27 # v3.1.0 with: - path: ${{ inputs.working-directory }}/${{ inputs.report-file != '' && inputs.report-file || steps.detect-report-file.outputs.report-file }} - - - shell: bash - if: always() && steps.run-lint.outputs.lint-exit-code != '0' && inputs.fail-on-error == 'true' - run: | - echo "::error::Linting failed. Please fix the errors above." - exit 1 + path: ${{ steps.detect-report-file.outputs.report-file }} # FIXME: workaround until will be merged: https://github.com/actions/runner/pull/1684 - shell: bash diff --git a/actions/test/README.md b/actions/test/README.md index 9776d17..ffce263 100644 --- a/actions/test/README.md +++ b/actions/test/README.md @@ -1,6 +1,6 @@ -# ![Icon](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJmZWF0aGVyIGZlYXRoZXItY2hlY2stc3F1YXJlIiBjb2xvcj0iZ3JheS1kYXJrIj48cG9seWxpbmUgcG9pbnRzPSI5IDExIDEyIDE0IDIyIDQiPjwvcG9seWxpbmU+PHBhdGggZD0iTTIxIDEydjdhMiAyIDAgMCAxLTIgMkg1YTIgMiAwIDAgMS0yLTJWNWEyIDIgMCAwIDEgMi0yaDExIj48L3BhdGg+PC9zdmc+) GitHub Action: Test +# ![Icon](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJmZWF0aGVyIGZlYXRoZXItY2hlY2stc3F1YXJlIiBjb2xvcj0iYmx1ZSI+PHBvbHlsaW5lIHBvaW50cz0iOSAxMSAxMiAxNCAyMiA0Ij48L3BvbHlsaW5lPjxwYXRoIGQ9Ik0yMSAxMnY3YTIgMiAwIDAgMS0yIDJINWEyIDIgMCAwIDEtMi0yVjVhMiAyIDAgMCAxIDItMmgxMSI+PC9wYXRoPjwvc3ZnPg==) GitHub Action: Test
Test @@ -30,7 +30,7 @@ Action to test Node.js projects with support for coverage reporting and pull req ## Usage ```yaml -- uses: hoverkraft-tech/ci-github-nodejs/actions/test@db1c1d36ff3e87c4513eded15d85acb78dcbd9b9 # copilot/refactor-ci-actions-lint-test +- uses: hoverkraft-tech/ci-github-nodejs/actions/test@dde8f0c67661ed66da8871a9fb104d36e146d644 # copilot/refactor-ci-actions-lint-test with: # Working directory where test commands are executed. # Can be absolute or relative to the repository root. @@ -43,28 +43,25 @@ Action to test Node.js projects with support for coverage reporting and pull req container: "false" # Code coverage reporter to use. Supported values: + # - "github": Use ReportGenerator for PR comments with coverage reports # - "codecov": Upload coverage to Codecov - # - "lcov": Use lcov-reporter-action for PR comments # - "": No coverage reporting - coverage: "" - - # Path to LCOV file for coverage reporting. - # Only used when coverage is set to "lcov". # - # Default: `coverage/lcov.info` - lcov-file: coverage/lcov.info - - # Codecov token for private repositories. - # Not required for public repositories when using OIDC. - codecov-token: "" - - # GitHub token for LCOV reporter PR comments. - # Required when coverage is set to "lcov". + # Default: `github` + coverage: github + + # Path to coverage files for reporting. + # Supports multiple formats (Cobertura, OpenCover, lcov, etc.). + # Can be a single file or multiple files separated by semicolons. + # If not specified, auto-detection will be attempted for common paths: + # - coverage/cobertura-coverage.xml, coverage/coverage.xml + # - coverage/lcov.info + # - coverage/clover.xml + coverage-files: "" + + # GitHub token for coverage PR comments. + # Required when coverage is set to "github". github-token: "" - - # Whether to fail the action if tests fail - # Default: `true` - fail-on-error: "true" ``` @@ -72,39 +69,37 @@ Action to test Node.js projects with support for coverage reporting and pull req ## Inputs -| **Input** | **Description** | **Required** | **Default** | -| ----------------------- | --------------------------------------------------------------------- | ------------ | --------------------------------- | -| **`working-directory`** | Working directory where test commands are executed. | **false** | `.` | -| | Can be absolute or relative to the repository root. | | | -| **`container`** | Whether running in container mode (skips checkout and node setup) | **false** | `false` | -| **`coverage`** | Code coverage reporter to use. Supported values: | **false** | - | -| | - "Codecov": Upload coverage to Codecov | | | -| | - "GitHub": Use ReportGenerator for PR comments with coverage reports | | | -| | - "": No coverage reporting | | | -| **`coverage-files`** | Path to coverage files for reporting. | **false** | `coverage/cobertura-coverage.xml` | -| | Supports multiple formats (Cobertura, OpenCover, lcov, etc.). | | | -| | Can be a single file or multiple files separated by semicolons. | | | -| **`codecov-token`** | Codecov token for private repositories. | **false** | - | -| | Not required for public repositories when using OIDC. | | | -| **`github-token`** | GitHub token for coverage PR comments. | **false** | - | -| | Required when coverage is set to "GitHub". | | | -| **`fail-on-error`** | Whether to fail the action if tests fail | **false** | `true` | - - +| **Input** | **Description** | **Required** | **Default** | +| ----------------------- | --------------------------------------------------------------------- | ------------ | ----------- | +| **`working-directory`** | Working directory where test commands are executed. | **false** | `.` | +| | Can be absolute or relative to the repository root. | | | +| **`container`** | Whether running in container mode (skips checkout and node setup) | **false** | `false` | +| **`coverage`** | Code coverage reporter to use. Supported values: | **false** | `github` | +| | - "GitHub": Use ReportGenerator for PR comments with coverage reports | | | +| | - "Codecov": Upload coverage to Codecov | | | +| | - "": No coverage reporting | | | +| **`coverage-files`** | Path to coverage files for reporting. | **false** | - | +| | Supports multiple formats (Cobertura, OpenCover, lcov, etc.). | | | +| | Can be a single file or multiple files separated by semicolons. | | | +| | If not specified, auto-detection will be attempted for common paths: | | | +| | - coverage/cobertura-coverage.xml, coverage/coverage.xml | | | +| | - coverage/lcov.info | | | +| | - coverage/clover.xml | | | +| **`github-token`** | GitHub token for coverage PR comments. | **false** | - | +| | Required when coverage is set to "GitHub". | | | + - -## Outputs - -| **Output** | **Description** | -| -------------------- | --------------------------- | -| **`test-exit-code`** | Exit code from the test run | - + + + ## Contributing @@ -122,7 +117,7 @@ This project is licensed under the MIT License. SPDX-License-Identifier: MIT -Copyright ยฉ 2025 Hoverkraft +Copyright ยฉ 2025 hoverkraft For more details, see the [license](http://choosealicense.com/licenses/mit/). @@ -134,4 +129,7 @@ For more details, see the [license](http://choosealicense.com/licenses/mit/). This documentation was automatically generated by [CI Dokumentor](https://github.com/hoverkraft-tech/ci-dokumentor). - + + diff --git a/actions/test/action.yml b/actions/test/action.yml index a6bf944..6f0fd23 100644 --- a/actions/test/action.yml +++ b/actions/test/action.yml @@ -19,11 +19,11 @@ inputs: coverage: description: | Code coverage reporter to use. Supported values: - - "codecov": Upload coverage to Codecov - "github": Use ReportGenerator for PR comments with coverage reports + - "codecov": Upload coverage to Codecov - "": No coverage reporting required: false - default: "" + default: "github" coverage-files: description: | Path to coverage files for reporting. @@ -35,27 +35,12 @@ inputs: - coverage/clover.xml required: false default: "" - codecov-token: - description: | - Codecov token for private repositories. - Not required for public repositories when using OIDC. - required: false - default: "" github-token: description: | - GitHub token for LCOV reporter PR comments. - Required when coverage is set to "lcov". + GitHub token for coverage PR comments. + Required when coverage is set to "github". required: false default: "" - fail-on-error: - description: "Whether to fail the action if tests fail" - required: false - default: "true" - -outputs: - test-exit-code: - description: "Exit code from the test run" - value: ${{ steps.run-test.outputs.test-exit-code }} runs: using: "composite" @@ -79,27 +64,19 @@ runs: with: working-directory: ${{ inputs.working-directory }} - # jscpd:ignore-start - id: run-test + name: ๐Ÿงช Run tests uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: - RUN_SCRIPT_COMMAND: ${{ inputs.container == 'true' && steps.get-package-manager.outputs.run-script-command || steps.setup-node.outputs.run-script-command }} + RUN_TEST_COMMAND: ${{ inputs.container == 'true' && steps.get-package-manager.outputs.run-script-command || steps.setup-node.outputs.run-script-command }} WORKING_DIRECTORY: ${{ inputs.working-directory }} - FAIL_ON_ERROR: ${{ inputs.fail-on-error }} with: script: | - const path = require('node:path'); - const exec = require('@actions/exec'); - const workingDirectory = process.env.WORKING_DIRECTORY || '.'; - const runScriptCommand = process.env.RUN_SCRIPT_COMMAND; - const failOnError = process.env.FAIL_ON_ERROR === 'true'; - - core.info('๐Ÿงช Running tests...'); - + const runScriptCommand = process.env.RUN_TEST_COMMAND; try { const result = await exec.getExecOutput(runScriptCommand, ['test:ci'], { - cwd: path.resolve(process.env.GITHUB_WORKSPACE, workingDirectory), + cwd: require('path').resolve(process.env.GITHUB_WORKSPACE, workingDirectory), env: { ...process.env, CI: 'true' }, ignoreReturnCode: true }); @@ -109,46 +86,35 @@ runs: core.setOutput('test-exit-code', result.exitCode); - if (result.exitCode !== 0 && failOnError) { + if (result.exitCode !== 0) { core.setFailed(`Tests failed with exit code ${result.exitCode}`); - } else if (result.exitCode !== 0) { - core.warning(`Tests failed with exit code ${result.exitCode}`); } } catch (error) { core.setOutput('test-exit-code', 1); core.setFailed(`Test execution error: ${error.message}`); } - # jscpd:ignore-end - # Install dependencies for codecov in container mode - - shell: bash - if: inputs.coverage == 'codecov' && inputs.container == 'true' - env: - REQUIRED_DEPS: | - git - curl - gpg - run: | - apt-get update - for dep in $REQUIRED_DEPS; do - if ! dpkg -s "$dep" >/dev/null 2>&1; then - apt-get install -y "$dep" - fi - done # Auto-detect coverage files if not specified - id: detect-coverage-files - if: always() && inputs.coverage == 'github' && inputs.coverage-files == '' + if: always() && inputs.coverage == 'github' uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: WORKING_DIRECTORY: ${{ inputs.working-directory }} + COVERAGE_FILES: ${{ inputs.coverage-files }} with: script: | - const fs = require('node:fs'); const path = require('node:path'); + const fs = require('node:fs'); const workingDirectory = process.env.WORKING_DIRECTORY || '.'; const workDir = path.resolve(process.env.GITHUB_WORKSPACE, workingDirectory); + if (process.env.COVERAGE_FILES && process.env.COVERAGE_FILES.trim() !== '') { + core.info(`Using specified coverage files: ${process.env.COVERAGE_FILES}`); + core.setOutput('coverage-files', process.env.COVERAGE_FILES); + return; + } + // Common coverage file paths const commonPaths = [ 'coverage/cobertura-coverage.xml', @@ -163,49 +129,65 @@ runs: for (const filePath of commonPaths) { const fullPath = path.join(workDir, filePath); if (fs.existsSync(fullPath)) { - core.info(`Auto-detected coverage file: ${filePath}`); - core.setOutput('coverage-files', filePath); + core.info(`Auto-detected coverage file: ${fullPath}`); + core.setOutput('coverage-files', fullPath); return; } } core.warning('No coverage file auto-detected'); - core.setOutput('coverage-files', 'coverage/cobertura-coverage.xml'); - - # Upload to Codecov - - name: ๐Ÿ“Š Upload coverage to Codecov - if: always() && inputs.coverage == 'codecov' - uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 - with: - working-directory: ${{ inputs.working-directory }} - token: ${{ inputs.codecov-token }} - use_oidc: ${{ inputs.codecov-token == '' }} - disable_telem: true - fail_ci_if_error: false # ReportGenerator for PR comments with coverage reports - name: ๐Ÿ“Š Generate coverage report - if: always() && inputs.coverage == 'github' && github.event_name == 'pull_request' - uses: danielpalme/ReportGenerator-GitHub-Action@d78d7a99ab2e53f4fa9e37c558feb9cd6a7a1c70 # v5.4.3 + if: always() && inputs.coverage == 'github' && github.event_name == 'pull_request' && steps.detect-coverage-files.outputs.coverage-files + uses: danielpalme/ReportGenerator-GitHub-Action@dcdfb6e704e87df6b2ed0cf123a6c9f69e364869 # v5.5.0 with: - reports: ${{ inputs.coverage-files != '' && inputs.coverage-files || steps.detect-coverage-files.outputs.coverage-files }} - targetdir: coveragereport + reports: ${{ steps.detect-coverage-files.outputs.coverage-files }} + targetdir: ${{ runner.temp }}/coveragereport-${{ github.run_id }} reporttypes: MarkdownSummaryGithub sourcedirs: ${{ inputs.working-directory }} + - if: always() && inputs.coverage == 'github' + id: get-coverage-report-summary + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + SUMMARY_FILE: ${{ runner.temp }}/coveragereport-${{ github.run_id }}/SummaryGithub.md + with: + script: | + const fs = require('fs'); + const summaryFilePath = process.env.SUMMARY_FILE; + + if (!fs.existsSync(summaryFilePath)) { + return core.setFailed(`Coverage summary file not found: ${summaryFilePath}`); + } + + const summaryContent = fs.readFileSync(summaryFilePath, 'utf8'); + core.summary.addRaw(summaryContent).write(); + + core.setOutput('summary-content', summaryContent); + - name: ๐Ÿ“Š Add coverage PR comment - if: always() && inputs.coverage == 'github' && github.event_name == 'pull_request' - uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 # v2.9.0 + if: always() && steps.get-coverage-report-summary.outputs.summary-content + uses: hoverkraft-tech/ci-github-common/actions/create-or-update-comment@5f11437c716059f30c635f90055060e4ef8b31a0 # 0.28.0 with: - recreate: true - path: coveragereport/SummaryGithub.md - GITHUB_TOKEN: ${{ inputs.github-token }} + title: "Code Coverage Report" + body: ${{ steps.get-coverage-report-summary.outputs.summary-content }} - - shell: bash - if: always() && steps.run-test.outputs.test-exit-code != '0' && inputs.fail-on-error == 'true' - run: | - echo "::error::Tests failed. Please fix the failing tests." - exit 1 + # Install dependencies for codecov in container mode + - name: Install Codecov dependencies + if: inputs.coverage == 'codecov' && inputs.container == 'true' + uses: pkgxdev/setup@f211ee4db3110b42e5a156282372527e7c1ed723 # v4.0.0 + with: + +: git curl gnupg.org + + - name: ๐Ÿ“Š Upload coverage to Codecov + if: always() && inputs.coverage == 'codecov' + uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 + with: + working-directory: ${{ inputs.working-directory }} + use_oidc: true + disable_telem: true + fail_ci_if_error: false # FIXME: workaround until will be merged: https://github.com/actions/runner/pull/1684 - shell: bash diff --git a/tests/npm/package-lock.json b/tests/npm/package-lock.json index ed2590a..f7342f5 100644 --- a/tests/npm/package-lock.json +++ b/tests/npm/package-lock.json @@ -91,6 +91,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz", "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -2739,6 +2740,7 @@ "resolved": "https://registry.npmjs.org/@gatsbyjs/reach-router/-/reach-router-2.0.1.tgz", "integrity": "sha512-gmSZniS9/phwgEgpFARMpNg21PkYDZEpfgEzvkgpE/iku4uvXqCrxr86fXbTpI9mkrhKS1SCTYmLGe60VdHcdQ==", "license": "MIT", + "peer": true, "dependencies": { "invariant": "^2.2.4", "prop-types": "^15.8.1" @@ -4515,6 +4517,7 @@ "resolved": "https://registry.npmjs.org/@parcel/core/-/core-2.8.3.tgz", "integrity": "sha512-Euf/un4ZAiClnlUXqPB9phQlKbveU+2CotZv7m7i+qkgvFn5nAGnrV4h1OzQU42j9dpgOxWi7AttUDMrvkbhCQ==", "license": "MIT", + "peer": true, "dependencies": { "@mischnic/json-sourcemap": "^0.1.0", "@parcel/cache": "2.8.3", @@ -5637,7 +5640,6 @@ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -5657,7 +5659,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -5670,7 +5671,6 @@ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "license": "Apache-2.0", - "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -5680,7 +5680,6 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -5694,8 +5693,7 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@testing-library/jest-dom": { "version": "6.6.3", @@ -5777,8 +5775,7 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -6060,7 +6057,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz", "integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==", "license": "MIT", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.7", "@typescript-eslint/scope-manager": "4.33.0", @@ -6085,7 +6081,6 @@ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "license": "MIT", - "peer": true, "dependencies": { "eslint-visitor-keys": "^2.0.0" }, @@ -6132,7 +6127,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/types": "4.33.0", "@typescript-eslint/visitor-keys": "4.33.0" @@ -6150,7 +6144,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", "license": "MIT", - "peer": true, "engines": { "node": "^8.10.0 || ^10.13.0 || >=11.10.1" }, @@ -6164,7 +6157,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/types": "4.33.0", "@typescript-eslint/visitor-keys": "4.33.0", @@ -6192,7 +6184,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/types": "4.33.0", "eslint-visitor-keys": "^2.0.0" @@ -6782,6 +6773,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6848,6 +6840,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -7393,7 +7386,6 @@ "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", "deprecated": "babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.0.0", "@babel/parser": "^7.7.0", @@ -7414,7 +7406,6 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=4" } @@ -8006,6 +7997,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001735", "electron-to-chromium": "^1.5.204", @@ -8940,6 +8932,7 @@ "integrity": "sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==", "hasInstallScript": true, "license": "MIT", + "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" @@ -9621,7 +9614,6 @@ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -9734,8 +9726,7 @@ "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/dom-converter": { "version": "0.2.0", @@ -10307,6 +10298,7 @@ "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -10341,20 +10333,6 @@ "@esbuild/win32-x64": "0.25.5" } }, - "node_modules/esbuild-register": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", - "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "debug": "^4.3.4" - }, - "peerDependencies": { - "esbuild": ">=0.12 <1" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -10388,6 +10366,7 @@ "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.3", @@ -10524,6 +10503,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.10.0.tgz", "integrity": "sha512-vcz32f+7TP+kvTUyMXZmCnNujBQZDNmcqPImw8b9PZ+16w1Qdm6ryRuYZYVaG9xRqqmAPr2Cs9FAX5gN+x/bjw==", "license": "BSD-3-Clause", + "peer": true, "dependencies": { "lodash": "^4.17.15", "string-natural-compare": "^3.0.1" @@ -10540,6 +10520,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", @@ -10603,6 +10584,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", "license": "MIT", + "peer": true, "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", @@ -10632,6 +10614,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "license": "MIT", + "peer": true, "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -10664,6 +10647,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -12440,6 +12424,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", @@ -12829,6 +12814,7 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz", "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==", "license": "MIT", + "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -15395,7 +15381,6 @@ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "license": "MIT", - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -17146,6 +17131,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", @@ -17755,6 +17741,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -18246,6 +18233,7 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -18429,6 +18417,7 @@ "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.9.2" } @@ -18936,7 +18925,6 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" } @@ -18965,6 +18953,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -20684,6 +20673,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "license": "(MIT OR CC0-1.0)", + "peer": true, "engines": { "node": ">=10" }, @@ -21227,6 +21217,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", diff --git a/tests/npm/package.json b/tests/npm/package.json index 9641c7e..3cdc0e2 100644 --- a/tests/npm/package.json +++ b/tests/npm/package.json @@ -1,9 +1,10 @@ { "name": "test-for-ci-with-npm", + "main": "src/index.js", "scripts": { "lint": "echo \"lint test\"", "build": "mkdir -p dist && echo \"build test\" > dist/test.txt", - "test:ci": "echo \"test CI\"" + "test:ci": "jest --coverage" }, "dependencies": { "gatsby": "^5.15.0", diff --git a/tests/npm/src/index.js b/tests/npm/src/index.js new file mode 100644 index 0000000..a7c7a71 --- /dev/null +++ b/tests/npm/src/index.js @@ -0,0 +1,5 @@ +function sample() { + return "sample"; +} + +module.exports = { sample }; diff --git a/tests/npm/src/index.spec.js b/tests/npm/src/index.spec.js new file mode 100644 index 0000000..038296d --- /dev/null +++ b/tests/npm/src/index.spec.js @@ -0,0 +1,8 @@ +const index = require("./index"); + +describe("sample function", () => { + it('should return "sample"', () => { + const result = index.sample(); + expect(result).toBe("sample"); + }); +});