From 78714bc05b5f7ce3d5b8e6f0b079384d9af8b3cf Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Sat, 26 Sep 2026 16:28:18 -0500 Subject: [PATCH] chore: update Node.js version requirements and refactor environment loading --- .github/workflows/build.yml | 4 +-- .github/workflows/test.yml | 4 +-- README.md | 7 +++- lib/cli.js | 4 +-- lib/load-env.js | 11 +++++++ package.json | 3 +- test/cli-env.js | 66 +++++++++++++++++++++++++++++++++++++ test/index.js | 2 +- 8 files changed, 91 insertions(+), 10 deletions(-) create mode 100644 lib/load-env.js create mode 100644 test/cli-env.js diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a237191..1d1f8de 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,7 @@ jobs: uses: actions/configure-pages@v5 - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - uses: actions/cache@v4 id: cache with: @@ -50,4 +50,4 @@ jobs: path: './gh-pages' - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 \ No newline at end of file + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 193261f..e9e68a9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [18, 19, 20, 21, 22, 23] + node-version: [22, 24, 26] steps: - uses: actions/checkout@v4 @@ -48,4 +48,4 @@ jobs: - name: Run tests env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: npm test \ No newline at end of file + run: npm test diff --git a/README.md b/README.md index 24eef3b..45ff628 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,11 @@ Example statusboards using `@pkgjs/statusboard`: ## Setup +Requires Node.js 22 or newer. The CLI loads `.env` from the current directory +using Node.js's native environment file support. Use `--env ` to load a +different file. Existing environment variables take precedence, and the file +is optional when variables are provided by the shell or CI. + WARNING: work in process, the following doesn't work yet, but soon! The easiest way to create a status board for your project is using Github Pages. To get started, create a new repo for your project and clone it @@ -63,4 +68,4 @@ $ cd build && git add . && git commit -m "our new statuspage" && git push - Typescript support (load typings or if authored in TS) - People/Teams (specify and display teams, for example the express TC) - GH CI status -- Pinned projects \ No newline at end of file +- Pinned projects diff --git a/lib/cli.js b/lib/cli.js index 71d2710..1b53b26 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -1,6 +1,6 @@ 'use strict' const yargs = require('yargs/yargs') -const dotenv = require('dotenv') +const loadEnv = require('./load-env') const path = require('path') const SHARED_OPTIONS = { @@ -34,7 +34,7 @@ const SHARED_OPTIONS = { function createBoard (create, argv) { if (argv.env) { - dotenv.config(argv.env || '.env') + loadEnv(argv.env) } const opts = {} diff --git a/lib/load-env.js b/lib/load-env.js new file mode 100644 index 0000000..d063bc0 --- /dev/null +++ b/lib/load-env.js @@ -0,0 +1,11 @@ +'use strict' +const { loadEnvFile } = require('node:process') + +module.exports = function loadEnv (file = '.env') { + try { + loadEnvFile(file) + } catch (error) { + // An env file is optional when variables are supplied by the environment. + if (error.code !== 'ENOENT') throw error + } +} diff --git a/package.json b/package.json index 381973f..8e4c9d6 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "@wesleytodd/buildjs": "0.0.8", "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", "cptmpl": "0.0.4", - "dotenv": "^17.2.0", "es5-lit-element": "^2.2.1", "es5-lit-html": "^1.1.1", "express": "^5.2.1", @@ -64,6 +63,6 @@ "yargs": "^17.7.2" }, "engines": { - "node": ">=18" + "node": ">=22" } } diff --git a/test/cli-env.js b/test/cli-env.js new file mode 100644 index 0000000..f1cf75b --- /dev/null +++ b/test/cli-env.js @@ -0,0 +1,66 @@ +'use strict' +const { suite, test, beforeEach, afterEach } = require('mocha') +const assert = require('node:assert/strict') +const fs = require('node:fs/promises') +const os = require('node:os') +const path = require('node:path') +const { execFileSync } = require('node:child_process') + +suite('CLI environment files', () => { + let directory + + beforeEach(async () => { + directory = await fs.mkdtemp(path.join(os.tmpdir(), 'statusboard-env-')) + await fs.writeFile(path.join(directory, 'config.cjs'), + 'module.exports = { tokenFromConfig: process.env.GITHUB_TOKEN }') + }) + + afterEach(async () => { + await fs.rm(directory, { recursive: true, force: true }) + }) + + function runCli (args = [], token) { + const env = { ...process.env } + for (const key of ['GITHUB_TOKEN', 'GITHUB_USER', 'GITHUB_PASS', 'GITHUB_2FA']) { + delete env[key] + } + if (token) env.GITHUB_TOKEN = token + const script = ` + const cli = require(${JSON.stringify(require.resolve('../lib/cli'))}) + cli(async opts => { + process.stdout.write(JSON.stringify(opts)) + return { buildSite: async () => {} } + }).parseAsync(['site', '--config', 'config.cjs', ...process.argv.slice(1)]) + .catch(() => { process.exitCode = 1 }) + ` + return JSON.parse(execFileSync(process.execPath, ['-e', script, '--', ...args], { + cwd: directory, + env, + encoding: 'utf8' + })) + } + + test('loads the default .env before reading configuration', async () => { + await fs.writeFile(path.join(directory, '.env'), 'GITHUB_TOKEN="from-file"\n') + const opts = runCli() + assert.equal(opts.github.token, 'from-file') + assert.equal(opts.tokenFromConfig, 'from-file') + }) + + test('honors --env instead of loading the default file', async () => { + await fs.writeFile(path.join(directory, '.env'), 'GITHUB_TOKEN=default\n') + await fs.writeFile(path.join(directory, 'custom.env'), 'GITHUB_TOKEN=custom\n') + const opts = runCli(['--env', 'custom.env']) + assert.equal(opts.github.token, 'custom') + assert.equal(opts.tokenFromConfig, 'custom') + }) + + test('preserves variables supplied by the environment', async () => { + await fs.writeFile(path.join(directory, '.env'), 'GITHUB_TOKEN=from-file\n') + assert.equal(runCli([], 'from-shell').github.token, 'from-shell') + }) + + test('works without an .env file when the token is supplied externally', () => { + assert.equal(runCli([], 'from-ci').github.token, 'from-ci') + }) +}) diff --git a/test/index.js b/test/index.js index 0e1631b..4c58252 100644 --- a/test/index.js +++ b/test/index.js @@ -1,5 +1,5 @@ 'use strict' -require('dotenv').config() +require('../lib/load-env')() const { suite, test, before } = require('mocha') const assert = require('assert') const fs = require('fs-extra')