diff --git a/.github/actions/TEMPLATE/README_TEMPLATE.md b/.github/actions/TEMPLATE/README_TEMPLATE.md index 06f62a9..6232250 100644 --- a/.github/actions/TEMPLATE/README_TEMPLATE.md +++ b/.github/actions/TEMPLATE/README_TEMPLATE.md @@ -52,7 +52,7 @@ Basic usage example: ```yaml - name: Name for step id: - uses: ./.github/actions/ + uses: OpenSesame/core-github-actions/.github/actions/@actions//vX.Y.Z with: : ``` diff --git a/.github/actions/pr-open-check/README.md b/.github/actions/pr-open-check/README.md index 7e783c6..c56a070 100644 --- a/.github/actions/pr-open-check/README.md +++ b/.github/actions/pr-open-check/README.md @@ -53,7 +53,7 @@ permissions: ```yaml - name: Check for open PR id: pr_check - uses: ./.github/actions/pr-check-open + uses: OpenSesame/core-github-actions/.github/actions/pr-open-check@actions/pr-open-check/2.0.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} commit-identifier: ${{ github.sha }} diff --git a/.github/actions/run-semgrep/CHANGELOG.md b/.github/actions/run-semgrep/CHANGELOG.md new file mode 100644 index 0000000..0f8bc01 --- /dev/null +++ b/.github/actions/run-semgrep/CHANGELOG.md @@ -0,0 +1,14 @@ +# Changelog for run-semgrep Composite Action + +All notable changes to the run-semgrep composite GitHub Action will be documented in this file. + +## 1.0.0 - Initial Release + +### Added + +- Initial release of the reusable composite action for running Semgrep scans +- Inputs are passed via environment variables +- Support running on both push and pull_request events +- Standardizes baseline resolution for diff scans +- Outputs include scan summary, config summary, scan status, and finding counts +- Designed to integrate with reviewdog for annotations diff --git a/.github/actions/run-semgrep/README.md b/.github/actions/run-semgrep/README.md new file mode 100644 index 0000000..859f857 --- /dev/null +++ b/.github/actions/run-semgrep/README.md @@ -0,0 +1,113 @@ +# Run Semgrep Action + +## 🧭 Summary + +Runs a Semgrep scan normalizing the baseline for diff scans depending on push vs PR context. Outputs scan results and summaries for downstream steps. + +## Scope/Limitations + +- Supports both push and pull request events. +- Requires Semgrep to be installed and available in the runner environment. +- Expects environment variables for configuration (see below). + +## 🔒 Permissions + +The following GHA permissions are required to use this step: + +```yaml +permissions: + contents: read +``` + +## Dependencies + +- `semgrep` — must be installed in the runner environment. +- `https` — standard Node.js module for API requests included in default action runners +- `reviewdog` — for annotation output (optional, for downstream steps). + +## ⚙️ Inputs + +This action is environment-driven. The following environment variables are required: + +| Name | Required | Description | +| ------------------- | -------- | ------------------------------------------------------------------------------------------- | +| `HAS_PR` | ✅ | Whether the current context has an associated PR (true/false) | +| `PR_NUMBER` | ❌ | PR number if applicable | +| `PR_URL` | ❌ | PR URL if applicable | +| `INPUT_BASELINE` | ✅ | Baseline ref to use for diffing (e.g., origin/main) | +| `GITHUB_EVENT_NAME` | ✅ | GitHub provided environment variable for event name (e.g., push, pull_request) | +| `GITHUB_REF_NAME` | ✅ | GitHub provided environment variable for the branch or tag name that triggered the workflow | +| `GITHUB_BASE_REF` | ❌ | GitHub provided environment variable for the base ref of a PR (if applicable) | +| `GITHUB_REPOSITORY` | ✅ | GitHub provided environment variable for the repository (e.g., owner/repo) | +| `GITHUB_TOKEN` | ✅ | GitHub token for API access | +| `SCAN_MODE` | ✅ | 'diff' or 'full' scan mode | +| `SEMGREP_CONFIG` | ✅ | Semgrep ruleset(s) to use | +| `SEMGREP_TARGETS` | ✅ | Targets to scan (default: current directory) | +| `FAIL_LEVEL` | ✅ | Severity level to fail on (e.g., ERROR, WARNING) | +| `EXTRA_ARGS` | ❌ | Additional arguments to pass to Semgrep | + +## 📤 Outputs + +Along with writing files for reviewdog annotations and inputs, this action provides the following outputs: + +| Name | Description | +| -------------------- | --------------------------------------------------- | +| `normalizedBaseline` | The resolved baseline ref | +| `scanSummary` | Summary of findings in markdown format | +| `configSummary` | Summary of scan config in markdown format | +| `scanStatus` | 'success' or 'failure' based on findings/fail level | +| `totalFindings` | Total number of findings | +| `numErrors` | Number of ERROR severity findings | +| `numWarnings` | Number of WARNING severity findings | +| `numInfo` | Number of INFO severity findings | + +## 🚀 Usage + +Basic usage example: + +```yaml +- name: Run Semgrep + id: semgrep + uses: OpenSesame/core-github-actions/.github/actions/run-semgrep@actions/run-semgrep/1.0.0 + env: + HAS_PR: ${{ env.HAS_PR }} + INPUT_BASELINE: ${{ env.INPUT_BASELINE }} + GITHUB_EVENT_NAME: ${{ github.event_name }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} + SEMGREP_CONFIG: 'p/default' + SEMGREP_TARGETS: '.' + SCAN_MODE: 'full' + FAIL_LEVEL: 'error' + EXTRA_ARGS: '' +``` + +Example outputs: + +```yaml +steps.semgrep.outputs.scanStatus +steps.semgrep.outputs.totalFindings +``` + +Example usage of outputs in later steps: + +```yaml +if: steps.semgrep.outputs.scanStatus == 'failure' + run: echo "Semgrep scan failed at or above threshold." +``` + +## 🧠 Notes + +- This action writes a file for reviewdog annotations (`reviewdog_input.txt`). +- Unit tests for the script are included in `run-semgrep.unit.test.js` (not used by the action, but kept for maintainability). + +## Versioning + +This action uses namespaced tags for versioning and is tracked in the CHANGELOG. + +```text +actions/run-semgrep/vX.Y.Z +``` + +See the repository's versioning documentation for details on how tags are validated and created. diff --git a/.github/actions/run-semgrep/action.yml b/.github/actions/run-semgrep/action.yml new file mode 100644 index 0000000..01de3e7 --- /dev/null +++ b/.github/actions/run-semgrep/action.yml @@ -0,0 +1,35 @@ +name: 'Run Semgrep' +description: 'Run a Semgrep scan and output results for reviewdog and future steps' +outputs: + normalizedBaseline: + description: 'The resolved baseline ref used for reviewdog annotations' + value: ${{ steps.run-semgrep.outputs.normalizedBaseline }} + scanSummary: + description: 'Markdown summary of the Semgrep scan results' + value: ${{ steps.run-semgrep.outputs.scanSummary }} + configSummary: + description: 'Markdown summary of the Semgrep configuration used' + value: ${{ steps.run-semgrep.outputs.configSummary }} + scanStatus: + description: 'success/failure based on findings and input fail level' + value: ${{ steps.run-semgrep.outputs.scanStatus }} + totalFindings: + description: 'Total number of findings from the Semgrep scan' + value: ${{ steps.run-semgrep.outputs.totalFindings }} + numErrors: + description: 'Number of findings at error severity level' + value: ${{ steps.run-semgrep.outputs.numErrors }} + numWarnings: + description: 'Number of findings at warning severity level' + value: ${{ steps.run-semgrep.outputs.numWarnings }} + numInfos: + description: 'Number of findings at info severity level' + value: ${{ steps.run-semgrep.outputs.numInfos }} + +runs: + using: composite + steps: + - name: Run Semgrep Scan + id: run-semgrep + shell: bash + run: node ${{ github.action_path }}/run-semgrep.js diff --git a/scripts/gha-lib/run-semgrep.js b/.github/actions/run-semgrep/run-semgrep.js similarity index 80% rename from scripts/gha-lib/run-semgrep.js rename to .github/actions/run-semgrep/run-semgrep.js index e1131d6..584274e 100644 --- a/scripts/gha-lib/run-semgrep.js +++ b/.github/actions/run-semgrep/run-semgrep.js @@ -1,61 +1,51 @@ -/* - * Run Semgrep scan - * Normalizes baseline for diff scans depending on push vs PR context - * - * Expects the following environment variables: - * HAS_PR - whether the current context has an associated PR (true/false) - * PR_NUMBER - PR number if applicable - * PR_URL - PR URL if applicable - * INPUT_BASELINE - baseline ref to use for diffing (e.g., origin/main) - * GITHUB_EVENT_NAME - GitHub provided environment variable for event name (e.g., push, pull_request) - * GITHUB_REF - Github provided environment variable for the git ref that triggered the workflow - * GITHUB_REF_NAME - GitHub provided environment variable for the branch or tag name that triggered the workflow - * GITHUB_BASE_REF - GitHub provided environment variable for the base ref of a PR (if applicable) - * GITHUB_REPOSITORY - GitHub provided environment variable for the repository (e.g., owner/repo) - * GITHUB_TOKEN - GitHub token for API access - * SCAN_MODE - 'diff' or 'full' scan mode - * SEMGREP_CONFIG - Semgrep ruleset(s) to use - * SEMGREP_TARGETS - Targets to scan (default: current directory) - * FAIL_LEVEL - Severity level to fail on (e.g., ERROR, WARNING) - * EXTRA_ARGS - Additional arguments to pass to Semgrep - * - * Outputs: - * - Writes file for reviewdog annotations, reviewdog_input.txt - * - Sets GitHub Action outputs - * - normalizedBaseline - the resolved baseline ref - * - totalFindings - total number of findings - * - numErrors - number of ERROR severity findings - * - numWarnings - number of WARNING severity findings - * - numInfo - number of INFO severity findings - * - scanSummary - summary of findings in md format - * - configSummary - summary of scan config in md format - * - scanStatus - 'success' or 'failure' based on findings and fail level - */ - const { spawnSync } = require('child_process'); const fs = require('fs'); -const fetch = require('node-fetch'); -const { validateEnvVar } = require('../utils/env-helpers'); +const https = require('https'); const SEMGREP_RESULTS_FILE_NAME = 'semgrep_results.json'; const REVIEWDOG_INPUT_FILE_NAME = 'reviewdog_input.txt'; -async function getPrBaseBranch(owner, repo, branch, token) { +function getPrBaseBranch(owner, repo, branch, token) { // Use GitHub API to find open PR for the branch and get its base branch - const url = `https://api.github.com/repos/${owner}/${repo}/pulls?state=open&head=${owner}:${branch}`; - const res = await fetch(url, { + const url = `/repos/${owner}/${repo}/pulls?state=open&head=${owner}:${branch}`; + const options = { + hostname: 'api.github.com', + path: url, + method: 'GET', headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github.v3+json', 'User-Agent': 'normalize-push-baseline-script', }, + }; + + return new Promise(resolve => { + const req = https.request(options, res => { + let data = ''; + res.on('data', chunk => { + data += chunk; + }); + res.on('end', () => { + if (res.statusCode < 200 || res.statusCode >= 300) { + return resolve(null); + } + try { + const prs = JSON.parse(data); + if (Array.isArray(prs) && prs.length > 0 && prs[0].base && prs[0].base.ref) { + resolve(prs[0].base.ref); + } else { + resolve(null); + } + } catch (_e) { + resolve(null); + } + }); + }); + req.on('error', _err => { + resolve(null); + }); + req.end(); }); - if (!res.ok) return null; - const prs = await res.json(); - if (prs.length > 0 && prs[0].base && prs[0].base.ref) { - return prs[0].base.ref; - } - return null; } /* Normalize the baseline ref for push events in GitHub Actions. @@ -327,6 +317,13 @@ if (require.main === module) { }); } +function validateEnvVar(name) { + if (!process.env[name]) { + console.error(`::error::Environment variable ${name} is required`); + process.exit(1); + } +} + module.exports = { main, getPrBaseBranch, @@ -338,6 +335,7 @@ module.exports = { writeFindingsMarkdown, writeConfigMarkdown, evaluateScanStatus, + validateEnvVar, SEMGREP_RESULTS_FILE_NAME, REVIEWDOG_INPUT_FILE_NAME, }; diff --git a/scripts/gha-lib/run-semgrep.unit.test.js b/.github/actions/run-semgrep/run-semgrep.unit.test.js similarity index 80% rename from scripts/gha-lib/run-semgrep.unit.test.js rename to .github/actions/run-semgrep/run-semgrep.unit.test.js index 6871f03..d5d71b0 100644 --- a/scripts/gha-lib/run-semgrep.unit.test.js +++ b/.github/actions/run-semgrep/run-semgrep.unit.test.js @@ -1,4 +1,3 @@ -const fetch = require('node-fetch'); const fs = require('fs'); const { getPrBaseBranch, @@ -9,6 +8,7 @@ const { writeFindingsMarkdown, writeConfigMarkdown, evaluateScanStatus, + validateEnvVar, REVIEWDOG_INPUT_FILE_NAME, } = require('./run-semgrep'); @@ -50,48 +50,64 @@ const exampleSemgrepOutput = { }; const emptySemgrepOutput = '{"results":[]}'; -jest.mock('node-fetch'); +const https = require('https'); + +function mockHttpsRequest(responseData, statusCode = 200) { + https.request = jest.fn((options, callback) => { + const res = require('stream').Readable({ read() {} }); + res.statusCode = statusCode; + setImmediate(() => { + callback(res); + res.emit('data', Buffer.from(responseData)); + res.emit('end'); + }); + return { on: jest.fn(), end: jest.fn() }; + }); +} describe('getPrBaseBranch', () => { const OWNER = 'test-owner'; const REPO = 'test-repo'; const BRANCH = 'feature-branch'; const TOKEN = 'ghp_testtoken'; + let originalRequest; + + beforeAll(() => { + originalRequest = https.request; + }); afterEach(() => { - fetch.mockClear(); + https.request = originalRequest; }); it('returns base branch when PR exists', async () => { - const mockResponse = [ + const responseData = JSON.stringify([ { base: { ref: 'main' }, }, - ]; - fetch.mockResolvedValue({ - ok: true, - json: async () => mockResponse, - }); - + ]); + mockHttpsRequest(responseData, 200); const baseBranch = await getPrBaseBranch(OWNER, REPO, BRANCH, TOKEN); expect(baseBranch).toBe('main'); }); it('returns null when no PR exists', async () => { - fetch.mockResolvedValue({ - ok: true, - json: async () => [], - }); - + const responseData = JSON.stringify([]); + mockHttpsRequest(responseData, 200); const baseBranch = await getPrBaseBranch(OWNER, REPO, BRANCH, TOKEN); expect(baseBranch).toBeNull(); }); - it('returns null on fetch error', async () => { - fetch.mockResolvedValue({ - ok: false, - }); + it('returns null on HTTP error', async () => { + const responseData = ''; + mockHttpsRequest(responseData, 404); + const baseBranch = await getPrBaseBranch(OWNER, REPO, BRANCH, TOKEN); + expect(baseBranch).toBeNull(); + }); + it('returns null on invalid JSON', async () => { + const responseData = 'not-json'; + mockHttpsRequest(responseData, 200); const baseBranch = await getPrBaseBranch(OWNER, REPO, BRANCH, TOKEN); expect(baseBranch).toBeNull(); }); @@ -101,9 +117,14 @@ describe('normalizeBaseline', () => { const FULL_REPO_NAME = 'repo-owner/test-repo'; const GITHUB_TOKEN = 'ghp_testtoken'; const inputBaseline = 'origin/main'; + let originalRequest; + + beforeAll(() => { + originalRequest = https.request; + }); afterEach(() => { - fetch.mockClear(); + https.request = originalRequest; }); it('returns input baseline for non-PR event', async () => { @@ -136,15 +157,12 @@ describe('normalizeBaseline', () => { }); it('fetches base branch when baseRef is not provided', async () => { - const mockResponse = [ + const mockResponse = JSON.stringify([ { base: { ref: 'staging' }, }, - ]; - fetch.mockResolvedValue({ - ok: true, - json: async () => mockResponse, - }); + ]); + mockHttpsRequest(mockResponse, 200); const hasPr = 'true'; const githubDetails = { @@ -161,9 +179,7 @@ describe('normalizeBaseline', () => { }); it('falls back to input baseline when base branch cannot be fetched', async () => { - fetch.mockResolvedValue({ - ok: false, - }); + mockHttpsRequest('', 404); const hasPr = 'true'; const githubDetails = { @@ -247,7 +263,7 @@ describe('getSemgrepMetrics', () => { it('correctly parses semgrep JSON output', () => { const fakeInputFileName = 'fake-results.json'; - jest.spyOn(fs, 'readFileSync').mockImplementation((fileName, encoding) => { + jest.spyOn(fs, 'readFileSync').mockImplementation((fileName, _encoding) => { if (fileName === fakeInputFileName) { return JSON.stringify(exampleSemgrepOutput); } @@ -266,7 +282,7 @@ describe('getSemgrepMetrics', () => { it('handles empty results', () => { const fakeInputFileName = 'fake-results.json'; - jest.spyOn(fs, 'readFileSync').mockImplementation((fileName, encoding) => { + jest.spyOn(fs, 'readFileSync').mockImplementation((fileName, _encoding) => { if (fileName === fakeInputFileName) { return JSON.stringify(emptySemgrepOutput); } @@ -345,3 +361,35 @@ describe('evaluateScanStatus', () => { expect(status).toBe('success'); }); }); + +describe('validateEnvVar', () => { + const ORIGINAL_EXIT = process.exit; + const ORIGINAL_CONSOLE_ERROR = console.error; + + beforeEach(() => { + process.exit = jest.fn(); + console.error = jest.fn(); + }); + + afterEach(() => { + process.exit = ORIGINAL_EXIT; + console.error = ORIGINAL_CONSOLE_ERROR; + }); + + it('does not exit when env var is set', () => { + process.env.TEST_VAR = 'value'; + validateEnvVar('TEST_VAR'); + expect(process.exit).not.toHaveBeenCalled(); + expect(console.error).not.toHaveBeenCalled(); + delete process.env.TEST_VAR; + }); + + it('exits with error when env var is not set', () => { + delete process.env.TEST_VAR; + validateEnvVar('TEST_VAR'); + expect(console.error).toHaveBeenCalledWith( + '::error::Environment variable TEST_VAR is required' + ); + expect(process.exit).toHaveBeenCalledWith(1); + }); +}); diff --git a/.github/actions/upsert-pr-comment/README.md b/.github/actions/upsert-pr-comment/README.md index 125507d..35f438c 100644 --- a/.github/actions/upsert-pr-comment/README.md +++ b/.github/actions/upsert-pr-comment/README.md @@ -46,7 +46,7 @@ Basic usage example: ```yaml - name: Upsert PR summary comment - uses: ./.github/actions/upsert-pr-comment + uses: OpenSesame/core-github-actions/.github/actions/upsert-pr-comment@actions/upsert-pr-comment/1.0.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} pr-number: ${{ github.event.pull_request.number }} diff --git a/.github/workflows/CHANGELOGS/run_semgrep_scan.md b/.github/workflows/CHANGELOGS/run_semgrep_scan.md index 7e432a7..6a0e6bc 100644 --- a/.github/workflows/CHANGELOGS/run_semgrep_scan.md +++ b/.github/workflows/CHANGELOGS/run_semgrep_scan.md @@ -2,6 +2,12 @@ All notable changes to the **run_semgrep_scan** callable workflow are documented in this file. +## 1.0.1 + +### Changed + +- Updated workflow to support cross-repository usage by repo qualifying the internal composite action calls. + ## 1.0.0 ### Added diff --git a/.github/workflows/internal_on_push_ci.yml b/.github/workflows/internal_on_push_ci.yml index 3870b67..978b080 100644 --- a/.github/workflows/internal_on_push_ci.yml +++ b/.github/workflows/internal_on_push_ci.yml @@ -4,6 +4,7 @@ on: push: branches-ignore: - main + workflow_dispatch: permissions: contents: read diff --git a/.github/workflows/run_semgrep_scan.yml b/.github/workflows/run_semgrep_scan.yml index 0d54de8..0fc97c5 100644 --- a/.github/workflows/run_semgrep_scan.yml +++ b/.github/workflows/run_semgrep_scan.yml @@ -66,6 +66,51 @@ on: value: ${{ jobs.semgrep.outputs.info_count }} workflow_dispatch: + inputs: + commit_identifier: + description: 'Commit SHA or ref to scan (default: current ref)' + type: string + required: false + cancel_in_progress: + description: 'Cancel in-progress run for the same ref' + type: boolean + default: true + semgrep_config: + description: Semgrep rulesets to use. Accepts a YAML array, newline or space-separated list. Default is "p/default" + type: string + default: p/default + semgrep_targets: + description: Files/directories to scan. Accepts a YAML array, newline or space-separated list. Default is current directory (`.`) + type: string + default: . + extra_args: + description: 'Additional arguments to pass to Semgrep (e.g., --exclude, --timeout). Optional.' + type: string + required: false + semgrep_version: + description: 'Semgrep version to install. Default: latest.' + type: string + required: false + fail_severity: + description: 'error | warning | info' + type: string + default: 'error' + semgrep_scan_mode: + description: What should Semgrep scan? "full | diff | baseline" + type: string + default: 'full' + baseline_ref: + description: 'Ref for diff/baseline (e.g., origin/main)' + type: string + default: 'origin/main' + reviewdog_filter_mode: + description: What should reviewdog display (does not change what Semgrep scans)? "added | diff_context | nofilter" + type: string + default: 'nofilter' + reviewdog_reporter: + description: 'review output: github-pr-review | github-pr-check (only applies if PR context exists, on push github-check is used)' + type: string + default: 'github-pr-review' permissions: contents: read @@ -101,7 +146,7 @@ jobs: - name: Check for open PR (by commit) id: pr_check - uses: ./.github/actions/pr-open-check + uses: OpenSesame/core-github-actions/.github/actions/pr-open-check@actions/pr-open-check/2.0.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} commit-identifier: ${{ inputs.commit_identifier }} @@ -125,6 +170,7 @@ jobs: fi - name: Run Semgrep + uses: OpenSesame/core-github-actions/.github/actions/run-semgrep@actions/run-semgrep/1.0.0 id: semgrep env: INPUT_BASELINE: ${{ inputs.baseline_ref }} @@ -135,7 +181,6 @@ jobs: SEMGREP_TARGETS: ${{ inputs.semgrep_targets }} FAIL_LEVEL: ${{ inputs.fail_severity }} EXTRA_ARGS: ${{ inputs.extra_args }} - run: node scripts/gha-lib/run-semgrep.js - name: Upload Artifact if: ${{ steps.semgrep.outputs.totalFindings > 0 }} @@ -265,7 +310,7 @@ jobs: - name: Upsert PR comment if: ${{ github.event_name == 'pull_request' || steps.pr_check.outputs.pr_exists == 'true' }} - uses: ./.github/actions/upsert-pr-comment + uses: OpenSesame/core-github-actions/.github/actions/upsert-pr-comment@actions/upsert-pr-comment/1.0.0 with: pr-number: ${{ steps.pr_check.outputs.pr_number }} github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.vscode/settings.json b/.vscode/settings.json index ef7602b..ec5f418 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -43,5 +43,5 @@ // Optional: Spell checker "cSpell.enabled": true, - "cSpell.words": ["opensesame", "reviewdog", "semgrep", "upserting"] + "cSpell.words": ["nosemgrep", "nvmrc", "opensesame", "reviewdog", "semgrep", "upserting"] } diff --git a/package-lock.json b/package-lock.json index 5f5049e..15a9728 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,6 @@ "name": "core-github-actions", "version": "1.0.0", "license": "UNLICENSED", - "dependencies": { - "node-fetch": "2.7.0" - }, "devDependencies": { "@eslint/js": "9.39.2", "@eslint/json": "0.14.0", @@ -5178,26 +5175,6 @@ "dev": true, "license": "MIT" }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -5979,12 +5956,6 @@ "node": ">=8.0" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -6196,22 +6167,6 @@ "makeerror": "1.0.12" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 2868b5b..fb4e2ed 100644 --- a/package.json +++ b/package.json @@ -31,8 +31,5 @@ "format:fix": "prettier --write './*.js' './*.mjs' './*.json' './*.md' 'scripts/**/*.js' '.github/actions/**/*.*'", "scan": "semgrep --config=p/ci --config=p/security-audit --config=p/javascript ./*.js ./*.mjs ./*.json scripts/ .github/actions/", "check": "npm run audit && npm run test && npm run lint:check && npm run format:check && npm run scan" - }, - "dependencies": { - "node-fetch": "2.7.0" } } diff --git a/scripts/internal-ci/get-version-tags/index.integration.test.js b/scripts/internal-ci/get-version-tags/index.integration.test.js index 4bd55f6..7c208b8 100644 --- a/scripts/internal-ci/get-version-tags/index.integration.test.js +++ b/scripts/internal-ci/get-version-tags/index.integration.test.js @@ -1,5 +1,5 @@ const { versionLabelPrefix, untrackedLabel } = require('../validate-version-labels/.'); -const { parseGithubOutput } = require('../../utils/test-helpers'); +const { parseGithubOutput } = require('../../internal-utils/test-helpers'); describe('get-version-tags main module integration', () => { const fs = require('fs'); diff --git a/scripts/internal-ci/validate-version-labels/index.integration.test.js b/scripts/internal-ci/validate-version-labels/index.integration.test.js index 6cb8acf..6d5f1ff 100644 --- a/scripts/internal-ci/validate-version-labels/index.integration.test.js +++ b/scripts/internal-ci/validate-version-labels/index.integration.test.js @@ -1,5 +1,5 @@ const { versionLabelPrefix, untrackedLabel } = require('.'); -const { parseGithubOutput } = require('../../utils/test-helpers'); +const { parseGithubOutput } = require('../../internal-utils/test-helpers'); describe('validate-version-labels main module integration', () => { const fs = require('fs'); diff --git a/scripts/utils/test-helpers.js b/scripts/internal-utils/test-helpers.js similarity index 100% rename from scripts/utils/test-helpers.js rename to scripts/internal-utils/test-helpers.js diff --git a/scripts/utils/env-helpers.js b/scripts/utils/env-helpers.js deleted file mode 100644 index 1851876..0000000 --- a/scripts/utils/env-helpers.js +++ /dev/null @@ -1,10 +0,0 @@ -function validateEnvVar(name) { - if (!process.env[name]) { - console.error(`::error::Environment variable ${name} is required`); - process.exit(1); - } -} - -module.exports = { - validateEnvVar, -}; diff --git a/scripts/utils/env-helpers.unit.test.js b/scripts/utils/env-helpers.unit.test.js deleted file mode 100644 index 73c0020..0000000 --- a/scripts/utils/env-helpers.unit.test.js +++ /dev/null @@ -1,33 +0,0 @@ -const { validateEnvVar } = require('./env-helpers'); - -describe('validateEnvVar', () => { - const ORIGINAL_EXIT = process.exit; - const ORIGINAL_CONSOLE_ERROR = console.error; - - beforeEach(() => { - process.exit = jest.fn(); - console.error = jest.fn(); - }); - - afterEach(() => { - process.exit = ORIGINAL_EXIT; - console.error = ORIGINAL_CONSOLE_ERROR; - }); - - it('does not exit when env var is set', () => { - process.env.TEST_VAR = 'value'; - validateEnvVar('TEST_VAR'); - expect(process.exit).not.toHaveBeenCalled(); - expect(console.error).not.toHaveBeenCalled(); - delete process.env.TEST_VAR; - }); - - it('exits with error when env var is not set', () => { - delete process.env.TEST_VAR; - validateEnvVar('TEST_VAR'); - expect(console.error).toHaveBeenCalledWith( - '::error::Environment variable TEST_VAR is required' - ); - expect(process.exit).toHaveBeenCalledWith(1); - }); -});