From 04031d192e62b0586d77e1e7721b6e334cc18860 Mon Sep 17 00:00:00 2001 From: Brian LeRoux Date: Tue, 25 Nov 2025 11:40:00 -0800 Subject: [PATCH 1/4] fix: upgrade to node22 and remove glob dep --- .gitignore | 2 +- fingerprint/index.js | 3 +- glob/index.js | 1 - index.js | 2 - package.json | 5 +- test/fingerprint/index-test.js | 74 ++++++++++++--------------- test/mock/fingerprint/foo/static.json | 4 ++ test/package-json-test.js | 9 ++++ 8 files changed, 49 insertions(+), 51 deletions(-) delete mode 100644 glob/index.js create mode 100644 test/mock/fingerprint/foo/static.json create mode 100644 test/package-json-test.js diff --git a/.gitignore b/.gitignore index 94dafe6..dec4ead 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ coverage/ node_modules/ package-lock.json -.kiro +.kiro \ No newline at end of file diff --git a/fingerprint/index.js b/fingerprint/index.js index 1dbf11e..a1f79f0 100644 --- a/fingerprint/index.js +++ b/fingerprint/index.js @@ -1,4 +1,4 @@ -let { existsSync } = require('fs') +let { existsSync, globSync } = require('fs') let fs = require('fs') // Broken out for testing writeFile calls let { basename, dirname, extname, join, sep } = require('path') @@ -7,7 +7,6 @@ let waterfall = require('../run-waterfall') let pathToUnix = require('../path-to-unix') let updater = require('../updater') let sort = require('../path-sort') -let { globSync } = require('../glob') let sha = require('../sha') /** diff --git a/glob/index.js b/glob/index.js deleted file mode 100644 index 491d3d2..0000000 --- a/glob/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('glob') diff --git a/index.js b/index.js index 40613f2..d01b387 100644 --- a/index.js +++ b/index.js @@ -14,7 +14,6 @@ let pathToUnix = require('./path-to-unix') let toLogicalID = require('./to-logical-id') let updater = require('./updater') let pathSort = require('./path-sort') -let glob = require('./glob') let hashid = require('./hashid') module.exports = { @@ -34,6 +33,5 @@ module.exports = { toLogicalID, // Converts dash casing into Pascal casing for CloudFormation updater, // Standard Arc status updater and progress indicator pathSort, // Sort paths - glob, // Glob paths hashid, // Generate unique IDs from numbers } diff --git a/package.json b/package.json index 2bbb8d7..8db634b 100644 --- a/package.json +++ b/package.json @@ -17,18 +17,17 @@ "rc": "npm version prerelease --preid RC" }, "engines": { - "node": ">=20" + "node": ">=22" }, "author": "Brian LeRoux ", "license": "Apache-2.0", "dependencies": { "@aws-lite/client": "^0.21.1", - "glob": "~10.3.12", "lambda-runtimes": "2.0.5" }, "devDependencies": { "@architect/eslint-config": "~3.0.0", - "@architect/inventory": "~4.0.4", + "@architect/inventory": "latest", "cross-env": "~7.0.3", "eslint": "~9.1.1", "proxyquire": "~2.1.3", diff --git a/test/fingerprint/index-test.js b/test/fingerprint/index-test.js index 683e806..337d289 100644 --- a/test/fingerprint/index-test.js +++ b/test/fingerprint/index-test.js @@ -1,17 +1,17 @@ let fs = require('fs') let { join } = require('path') let proxyquire = require('proxyquire') -let sha = require('../../sha') let sinon = require('sinon') let test = require('tape') let pathToUnix = require('../../path-to-unix') let _inventory = require('@architect/inventory') let inventory let globStub = sinon.stub().callsFake(() => []) -let glob = { globSync: globStub } -let shaStub = sinon.stub(sha, 'get').callsFake((file, callback) => callback(null, 'df330f3f12')) // Fake hash +let writeFileStub = sinon.stub().callsFake((dest, data, callback) => callback()) +let shaGetStub = sinon.stub().callsFake((file, callback) => callback(null, 'df330f3f12')) // Fake hash let fingerprint = proxyquire('../../fingerprint', { - '../glob': glob, + 'fs': { ...fs, globSync: globStub, writeFile: writeFileStub, '@global': true }, + '../sha': { get: shaGetStub }, }) let params = () => ({ @@ -34,9 +34,6 @@ function updateInventory (t, settings = '', callback) { } function reset (t) { - // Reset env for next test - fs.writeFile.restore() - shaStub.resetHistory() t.pass('Reset') } @@ -52,6 +49,8 @@ test('Set up env', t => { test('fingerprint respects folder setting', t => { t.plan(7) + // Reset stubs + shaGetStub.resetHistory() // Globbing globStub.resetBehavior() globStub.callsFake(() => [ @@ -61,7 +60,8 @@ test('fingerprint respects folder setting', t => { ]) // Static manifest let manifest - let fsStub = sinon.stub(fs, 'writeFile').callsFake((dest, data, callback) => { + writeFileStub.resetBehavior() + writeFileStub.callsFake((dest, data, callback) => { manifest = data callback() }) @@ -72,16 +72,13 @@ test('fingerprint respects folder setting', t => { console.log('Generated manifest:') console.log(manifest) - t.ok(shaStub.calledTwice, 'Correct number of files hashed') + t.ok(shaGetStub.calledTwice, 'Correct number of files hashed') t.equal(manifest['index.html'], 'index-df330f3f12.html', 'Manifest data parsed correctly for index.html') t.equal(result['index.html'], 'index-df330f3f12.html', 'Manifest data returned correctly for index.html') t.equal(manifest['css/styles.css'], 'css/styles-df330f3f12.css', 'Manifest data parsed correctly for css/styles.css') t.equal(result['css/styles.css'], 'css/styles-df330f3f12.css', 'Manifest data returned correctly for css/styles.css') - t.ok(fsStub.called, 'static.json manifest written') + t.ok(writeFileStub.called, 'static.json manifest written') - // Reset env for next test - fs.writeFile.restore() - shaStub.resetHistory() }) }) }) @@ -89,6 +86,8 @@ test('fingerprint respects folder setting', t => { test('fingerprint respects prefix setting (by doing nothing)', t => { // This test effectively does nothing, but it's here to ensure the presence of prefix does not influence how the static manifest is generated t.plan(7) + // Reset stubs + shaGetStub.resetHistory() // Globbing globStub.resetBehavior() globStub.callsFake(() => [ @@ -98,7 +97,8 @@ test('fingerprint respects prefix setting (by doing nothing)', t => { ]) // Static manifest let manifest - let fsStub = sinon.stub(fs, 'writeFile').callsFake((dest, data, callback) => { + writeFileStub.resetBehavior() + writeFileStub.callsFake((dest, data, callback) => { manifest = data callback() }) @@ -109,21 +109,19 @@ test('fingerprint respects prefix setting (by doing nothing)', t => { console.log('Generated manifest:') console.log(manifest) - t.ok(shaStub.calledTwice, 'Correct number of files hashed') + t.ok(shaGetStub.calledTwice, 'Correct number of files hashed') t.equal(manifest['index.html'], 'index-df330f3f12.html', 'Manifest data parsed correctly for index.html') t.equal(result['index.html'], 'index-df330f3f12.html', 'Manifest data returned correctly for index.html') t.equal(manifest['css/styles.css'], 'css/styles-df330f3f12.css', 'Manifest data parsed correctly for css/styles.css') t.equal(result['css/styles.css'], 'css/styles-df330f3f12.css', 'Manifest data returned correctly for css/styles.css') - t.ok(fsStub.called, 'static.json manifest written') + t.ok(writeFileStub.called, 'static.json manifest written') - // Reset env for next test - fs.writeFile.restore() - shaStub.resetHistory() }) }) }) test('fingerprint generates static.json manifest', t => { + shaGetStub.resetHistory() t.plan(7) // Globbing globStub.resetBehavior() @@ -134,7 +132,8 @@ test('fingerprint generates static.json manifest', t => { ]) // Static manifest let manifest - let fsStub = sinon.stub(fs, 'writeFile').callsFake((dest, data, callback) => { + writeFileStub.resetBehavior() + writeFileStub.callsFake((dest, data, callback) => { manifest = data callback() }) @@ -145,48 +144,40 @@ test('fingerprint generates static.json manifest', t => { console.log('Generated manifest:') console.log(manifest) - t.ok(shaStub.calledTwice, 'Correct number of files hashed') + t.ok(shaGetStub.calledTwice, 'Correct number of files hashed') t.equal(manifest['index.html'], 'index-df330f3f12.html', 'Manifest data parsed correctly for index.html') t.equal(result['index.html'], 'index-df330f3f12.html', 'Manifest data returned correctly for index.html') t.equal(manifest['css/styles.css'], 'css/styles-df330f3f12.css', 'Manifest data parsed correctly for css/styles.css') t.equal(result['css/styles.css'], 'css/styles-df330f3f12.css', 'Manifest data returned correctly for css/styles.css') - t.ok(fsStub.called, 'static.json manifest written') + t.ok(writeFileStub.called, 'static.json manifest written') - // Reset env for next test - fs.writeFile.restore() - shaStub.resetHistory() }) }) }) test('fingerprint does does not generate static.json when set to external', t => { + shaGetStub.resetHistory() + writeFileStub.resetHistory() t.plan(4) // Globbing globStub.resetBehavior() globStub.callsFake(() => [ pathToUnix(join(process.cwd(), 'public', 'index.html')), ]) - // Static manifest - let fsStub = sinon.stub(fs, 'writeFile').callsFake((dest, data, callback) => { - callback() - }) updateInventory(t, 'fingerprint external', () => { let p = params() delete p.fingerprint fingerprint(p, (err, result) => { if (err) t.fail(err) t.notOk(result, 'Did not pass back manifest') - t.ok(shaStub.notCalled, 'No files hashed') - t.ok(fsStub.notCalled, 'static.json manifest not written') - - // Reset env for next test - fs.writeFile.restore() - shaStub.resetHistory() + t.ok(shaGetStub.notCalled, 'No files hashed') + t.ok(writeFileStub.notCalled, 'static.json manifest not written') }) }) }) test('fingerprint ignores specified static assets', t => { + shaGetStub.resetHistory() t.plan(6) // Globbing globStub.resetBehavior() @@ -197,7 +188,8 @@ test('fingerprint ignores specified static assets', t => { ]) // Static manifest let manifest - let fsStub = sinon.stub(fs, 'writeFile').callsFake((dest, data, callback) => { + writeFileStub.resetBehavior() + writeFileStub.callsFake((dest, data, callback) => { manifest = data callback() }) @@ -208,16 +200,17 @@ test('fingerprint ignores specified static assets', t => { console.log('Generated manifest:') console.log(manifest) - t.ok(shaStub.calledOnce, 'Correct number of files hashed') + t.ok(shaGetStub.calledOnce, 'Correct number of files hashed') t.equal(manifest['index.html'], 'index-df330f3f12.html', 'Manifest data parsed correctly') t.equal(result['index.html'], 'index-df330f3f12.html', 'Manifest data returned correctly') - t.ok(fsStub.called, 'static.json manifest written') + t.ok(writeFileStub.called, 'static.json manifest written') reset(t) }) }) }) test('fingerprint cancels early if disabled', t => { + shaGetStub.resetHistory() t.plan(3) updateInventory(t, 'fingerprint false', () => { let p = params() @@ -225,11 +218,8 @@ test('fingerprint cancels early if disabled', t => { fingerprint(p, (err, result) => { if (err) t.fail(err) - t.ok(shaStub.notCalled, 'Correct number of files hashed (none)') + t.ok(shaGetStub.notCalled, 'Correct number of files hashed (none)') t.notOk(result, 'Returned no result') - - // Reset env for next test - shaStub.resetHistory() }) }) }) diff --git a/test/mock/fingerprint/foo/static.json b/test/mock/fingerprint/foo/static.json new file mode 100644 index 0000000..5fe0c55 --- /dev/null +++ b/test/mock/fingerprint/foo/static.json @@ -0,0 +1,4 @@ +{ + "index.html": "index-df330f3f12.html", + "css/styles.css": "css/styles-df330f3f12.css" +} \ No newline at end of file diff --git a/test/package-json-test.js b/test/package-json-test.js new file mode 100644 index 0000000..10e9c72 --- /dev/null +++ b/test/package-json-test.js @@ -0,0 +1,9 @@ +let test = require('tape') +let pkg = require('../package.json') + +test('Package.json validation for Node.js 22 upgrade', t => { + t.plan(2) + + t.equal(pkg.engines.node, '>=22', 'engines.node is set to ">=22"') + t.notOk(pkg.dependencies.glob, 'glob is not in dependencies') +}) From cb4cc180d68703f2578eb31f0139e7d3b10f827a Mon Sep 17 00:00:00 2001 From: Brian LeRoux Date: Tue, 25 Nov 2025 11:40:13 -0800 Subject: [PATCH 2/4] 6.0.0-RC.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8db634b..12ef6db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@architect/utils", - "version": "5.0.0", + "version": "6.0.0-RC.0", "description": "Common utility functions", "main": "index.js", "repository": { From e65ced62b8a74be5d7cc796fa27577b50e5ed757 Mon Sep 17 00:00:00 2001 From: Brian LeRoux Date: Tue, 25 Nov 2025 11:44:20 -0800 Subject: [PATCH 3/4] fix: ensure node22 in action workflow --- .github/workflows/build.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1c742c6..33c308c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - node-version: [ 20.x, 22.x, 24.x ] + node-version: [ 22.x, 24.x ] os: [ windows-latest, ubuntu-latest, macOS-latest ] # Go @@ -40,14 +40,7 @@ jobs: - name: Install run: npm install - - name: Test (Node.js <= 16.x) - if: matrix.node-version <= '16.x' - run: npm run test:nolint - env: - CI: true - - name: Test - if: matrix.node-version > '16.x' run: npm test env: CI: true From 4b9e0fe74a502cfd7b2bd8267d5e004d26ab7504 Mon Sep 17 00:00:00 2001 From: Brian LeRoux Date: Tue, 25 Nov 2025 12:20:39 -0800 Subject: [PATCH 4/4] fix: use isFile to ensure glob behaviour matches old behaviour --- fingerprint/index.js | 15 ++++++++++++++- test/fingerprint/index-test.js | 3 ++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/fingerprint/index.js b/fingerprint/index.js index a1f79f0..24f9618 100644 --- a/fingerprint/index.js +++ b/fingerprint/index.js @@ -60,7 +60,20 @@ module.exports = function fingerprint (params, callback) { function globFiles (callback) { let staticAssets = pathToUnix(folder + '/**/*') try { - let filesFound = globSync(staticAssets, { dot: true, nodir: true, follow: true }) + // Node 22 native fs.globSync returns an array of file paths (strings) + // Unlike the glob package, it includes directories, so we need to filter them out + let filesFound = globSync(staticAssets, { + exclude: (name) => name.includes('node_modules'), + }) + // Filter out directories (native glob includes them unlike the glob package with nodir: true) + filesFound = filesFound.filter(file => { + try { + return fs.statSync(file).isFile() + } + catch { + return false + } + }) // Renormalize to Unix, otherwise deployments from Windows will fail in Lambda filesFound = filesFound.map(file => pathToUnix(file)) callback(null, filesFound) diff --git a/test/fingerprint/index-test.js b/test/fingerprint/index-test.js index 337d289..7d9a0d1 100644 --- a/test/fingerprint/index-test.js +++ b/test/fingerprint/index-test.js @@ -8,9 +8,10 @@ let _inventory = require('@architect/inventory') let inventory let globStub = sinon.stub().callsFake(() => []) let writeFileStub = sinon.stub().callsFake((dest, data, callback) => callback()) +let statSyncStub = sinon.stub().callsFake(() => ({ isFile: () => true })) // All paths are files let shaGetStub = sinon.stub().callsFake((file, callback) => callback(null, 'df330f3f12')) // Fake hash let fingerprint = proxyquire('../../fingerprint', { - 'fs': { ...fs, globSync: globStub, writeFile: writeFileStub, '@global': true }, + 'fs': { ...fs, globSync: globStub, writeFile: writeFileStub, statSync: statSyncStub, '@global': true }, '../sha': { get: shaGetStub }, })