@leverege/build-tools 2.103.0 → 2.105.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "npm"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ open-pull-requests-limit: 10
package/README.md CHANGED
@@ -17,6 +17,7 @@ Most tools assume the following are installed and configured:
17
17
  - `helm` (v3+)
18
18
  - `fzf` (for interactive pod selection in `k8x` and `klog`)
19
19
  - Node.js 22+ - prefer 24
20
+ - npm 11.10+
20
21
 
21
22
  ---
22
23
 
@@ -121,12 +122,14 @@ Secrets are defined as JSON files in `src/service-man/config/secrets/`. See the
121
122
  ### Docker Image Management
122
123
 
123
124
  **`docker-to-registry`** — Builds and pushes Docker images (Node.js/general) to GCP
124
- Artifact Registry with git tagging.
125
+ Artifact Registry with git tagging. Any non-semver version string (e.g. `latest`) is
126
+ treated as a beta build and skips git tagging.
125
127
  ```bash
126
- docker-to-registry <version> # e.g. docker-to-registry v1.2.3
127
- docker-to-registry latest # beta build — any non-semver version skips git tagging
128
+ docker-to-registry v1.2.3 # tagged release build
129
+ docker-to-registry latest # beta build — skips git tagging
128
130
  docker-to-registry v1.2.3 --dry-run # skip confirmation prompt
129
131
  docker-to-registry guide-me # print helm/registry migration guidance
132
+ docker-to-registry v1.2.3 --debug # enable debug logging
130
133
  ```
131
134
 
132
135
  **`build-cnpg-image`** — Builds a custom CloudNative PostgreSQL image with jsquery and
@@ -221,11 +224,20 @@ package releases. Runs automatically as the npm `prepare` lifecycle hook.
221
224
  prepack [--compile] [--manual-tag] [--force-tag] [--push-tag]
222
225
  ```
223
226
 
224
- **`refresh-npm-token`** — Refreshes the Leverege npm registry token from GCP Secret
225
- Manager, updating `.npmrc` / `.yarnrc.yml`. Skips refresh if the token is less than
226
- 24 hours old.
227
- ```bash
228
- refresh-npm-token [--project <id>] [--force] [--dry-run]
227
+ **`refresh-npm-token`** — Fetches the current npm token and rc file templates from GCP
228
+ Secret Manager and overwrites `~/.npmrc` and `~/.yarnrc.yml`. DevOps controls the full
229
+ content of both files via three secrets: `REFRESH_NPM_TOKEN` (raw token),
230
+ `REFRESH_NPM_NPMRC`, and `REFRESH_NPM_YARNRC` (templates with `${REFRESH_NPM_TOKEN}`
231
+ placeholder). Skips refresh if `~/.npmrc` is less than 24 hours old. On first run,
232
+ existing rc files are preserved as `.LEGACY` before being overwritten.
233
+ ```bash
234
+ refresh-npm-token # refresh if older than 24h
235
+ refresh-npm-token --force # always refresh
236
+ refresh-npm-token --beta # use _BETA template secrets (migration testing)
237
+ refresh-npm-token --dry-run # fetch and validate without writing files
238
+ refresh-npm-token --no-version-check # warn on version requirements but continue
239
+ refresh-npm-token --no-update-yarnrc # skip ~/.yarnrc.yml update
240
+ refresh-npm-token --npmrc-age 1h # custom age threshold
229
241
  ```
230
242
 
231
243
  **`repo-panopticon`** — Multi-repo health dashboard. Checks each repository against a suite
@@ -0,0 +1,13 @@
1
+ import eslintConfig from '@leverege/eslint-config-leverege'
2
+
3
+ export default [
4
+ ...eslintConfig,
5
+ {
6
+ rules: {
7
+ '@stylistic/curly-newline': 'off',
8
+ '@stylistic/key-spacing': 'off',
9
+ '@stylistic/object-curly-newline': 'off',
10
+ '@stylistic/nonblock-statement-body-position': 'off'
11
+ }
12
+ }
13
+ ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leverege/build-tools",
3
- "version": "2.103.0",
3
+ "version": "2.105.0",
4
4
  "description": "A collection of build / support tools for Leverege developers",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "scripts": {
11
11
  "clean": "rm -rf lib",
12
- "lint": "find ./src -name \\*.\\*js | xargs npx eslint --report-unused-disable-directives",
12
+ "lint": "eslint",
13
13
  "prepare": "[ $SKIP_PREPARE ] && exit 0 || ./src/hook-and-release.mjs",
14
14
  "test": "mocha -t 60000 --exit 'test/**/!(*e2e*).mjs'",
15
15
  "test-all": "mocha -t 60000 --exit 'test/**/**.mjs'",
@@ -66,14 +66,13 @@
66
66
  "send-to-slack": "src/send-to-slack.mjs",
67
67
  "service-man": "src/service-man/service-man.mjs",
68
68
  "snapshot-cnpg": "src/snapshot-cnpg.mjs",
69
- "unleash": "src/unleash.mjs",
70
69
  "upload-model": "src/upload-model.sh",
71
70
  "yarn-build": "src/yarn-build.mjs"
72
71
  },
73
72
  "author": "Leverege Devs",
74
73
  "engines": {
75
- "node": "^22.11 || >=24",
76
- "npm": ">=10.2 || >=11.6"
74
+ "node": "^22.22 || >=24.15",
75
+ "npm": ">=11.10"
77
76
  },
78
77
  "license": "SEE LICENSE IN LICENSE.md",
79
78
  "dependencies": {
@@ -88,32 +87,33 @@
88
87
  "execa": "^9.6.1",
89
88
  "find-yarn-workspace-root": "^2.0.0",
90
89
  "glob": "^13.0.6",
91
- "googleapis": "^171.4.0",
90
+ "googleapis": "^172.0.0",
92
91
  "handlebars": "^4.7.9",
93
92
  "ignore": "^7.0.5",
94
- "inquirer": "^13.4.3",
93
+ "inquirer": "^14.0.0",
95
94
  "js-yaml": "^4.1.1",
96
95
  "jsdoc": "^4.0.5",
97
96
  "ms": "^2.1.3",
98
- "npm-registry-fetch": "^19.1.1",
97
+ "npm-registry-fetch": "^20.0.0",
99
98
  "ora": "^9.4.0",
100
99
  "p-limit": "^7.3.0",
101
100
  "package-up": "^5.0.0",
102
101
  "readline-sync": "^1.4.10",
103
- "semver": "^7.8.0",
104
- "shell-quote": "^1.8.3",
102
+ "semver": "^7.8.1",
103
+ "shell-quote": "^1.8.4",
105
104
  "simple-git": "^3.36.0",
106
105
  "sloc": "^0.3.2",
107
106
  "superstruct": "^2.0.2",
108
- "tmp": "^0.2.5",
107
+ "tmp": "^0.2.6",
109
108
  "toml": "^4.1.1",
110
109
  "yaml": "^2.9.0",
111
110
  "zx": "^8.8.5"
112
111
  },
113
112
  "devDependencies": {
114
- "@leverege/eslint-config-leverege": "^5.1.2",
113
+ "@leverege/eslint-config-leverege": "^9.0.1",
115
114
  "chai": "^6.2.2",
116
- "mocha": "^11.7.5",
117
- "npm": "^11.14.1"
115
+ "eslint": "^9",
116
+ "mocha": "^11.7.6",
117
+ "npm": "^11.15.0"
118
118
  }
119
119
  }
package/src/Docker.mjs CHANGED
@@ -98,7 +98,7 @@ const generateDockerfile = async ( options ) => {
98
98
  // could probably be tightened up a little.
99
99
  try {
100
100
  fs.readdirSync( './docker' )
101
- } catch ( err ) {
101
+ } catch ( err ) { // eslint-disable-line no-unused-vars
102
102
  warning( 'creating the missing ./docker directory' )
103
103
  fs.mkdirSync( './docker' ) // TODO: blindly assumes the mkdir succeeds
104
104
  }
@@ -120,7 +120,7 @@ const generateDockerfile = async ( options ) => {
120
120
  try {
121
121
  fs.writeFileSync( bashrcFile, bashrcTemplate )
122
122
  fs.appendFileSync( bashrcFile, bashrcPlugin )
123
- } catch ( err ) {
123
+ } catch ( err ) { // eslint-disable-line no-unused-vars
124
124
  errorExit( `***ERROR: failed creation of the ${bashrcFile} file` )
125
125
  }
126
126
 
@@ -136,7 +136,7 @@ const generateDockerfile = async ( options ) => {
136
136
  if ( build.length === 0 ) {
137
137
  errorExit( `***ERROR: the build dir is empty - ${chalk.yellow( 'npm run build' )}` )
138
138
  }
139
- } catch ( err ) {
139
+ } catch ( err ) { // eslint-disable-line no-unused-vars
140
140
  errorExit( `***ERROR: expected the ${buildDir} directory to exist - ${chalk.yellow( 'npm run build' )}` )
141
141
  }
142
142
 
package/src/Utils.mjs CHANGED
@@ -132,7 +132,7 @@ export const shellCmd = async ( cmd, opts = {} ) => {
132
132
  } else {
133
133
  const parsed = parse( cmd ).filter( x => typeof x === 'string' )
134
134
  // the leading ; forces a break from previous line otherwise trailing ) and leading [ collide
135
- ;[ command, ...args ] = parsed // eslint-disable-line semi-style
135
+ ;[ command, ...args ] = parsed // eslint-disable-line @stylistic/semi-style
136
136
  }
137
137
 
138
138
  debug( { command, args }, '<== execa $ invoke' )
@@ -146,6 +146,35 @@ export const shellCmd = async ( cmd, opts = {} ) => {
146
146
  }
147
147
  }
148
148
 
149
+ export const versionChecks = async ( { node, npm, strict = true } = {} ) => {
150
+ const check = ( tool, raw, { min, preferred, hint } = {} ) => {
151
+ const current = semver.coerce( raw )
152
+ if ( !current ) {
153
+ warning( `could not parse ${tool} version: "${raw}"` )
154
+ return
155
+ }
156
+ debug( { tool, current : current.version, min, preferred, strict }, '<==versionChecks' )
157
+ const hintSuffix = hint ? ` — ${hint}` : ''
158
+ if ( min && semver.lt( current, min ) ) {
159
+ const msg = `${tool} ${min} or later is required (found ${current.version})${hintSuffix}`
160
+ if ( strict ) {
161
+ errorExit( `***ERROR: ${msg}` )
162
+ } else {
163
+ warning( `${msg} — continuing due to --no-version-check` )
164
+ }
165
+ }
166
+ if ( preferred && semver.lt( current, preferred ) ) {
167
+ warning( `${tool} ${preferred} or later is preferred (found ${current.version})` )
168
+ }
169
+ }
170
+
171
+ if ( node ) { check( 'node', process.version, node ) }
172
+ if ( npm ) {
173
+ const raw = ( await shellCmd( 'npm --version' ) ).trim()
174
+ check( 'npm', raw, npm )
175
+ }
176
+ }
177
+
149
178
  export const establishOauthAccess = async ( registryLocation = 'us-docker.pkg.dev' ) => {
150
179
  // gcloud auth print-access-token | helm registry login -u oauth2accesstoken \
151
180
  // -p ${password} https://us-docker.pkg.dev
@@ -271,7 +300,7 @@ export const getGitBranchAndUpstream = async () => {
271
300
  let upstream = 'no-upstream'
272
301
  try {
273
302
  upstream = await tryCommand( `git rev-parse --abbrev-ref ${branch}@{u}`, 'getGitUpstream' ) ?? 'no-upstream'
274
- } catch ( error ) {}
303
+ } catch ( error ) {} // eslint-disable-line no-unused-vars
275
304
  return { branch, upstream }
276
305
  }
277
306
 
@@ -710,12 +739,12 @@ export const analyzeRepository = async ( giveGuidance ) => {
710
739
  let wsName
711
740
  try {
712
741
  wsName = await shellCmd( 'npm exec -c pwd -ws' )
713
- } catch ( err ) { }
742
+ } catch ( err ) { } // eslint-disable-line no-unused-vars
714
743
 
715
744
  let gitRemote
716
745
  try {
717
746
  gitRemote = await shellCmd( 'git config --get remote.origin.url' )
718
- } catch ( err ) { }
747
+ } catch ( err ) { } // eslint-disable-line no-unused-vars
719
748
 
720
749
  // TODO: Add in Define VERBOSE_DOCKREATE_NPM="yes" for verbose build logging
721
750
  // TODO: Validity check for version and package.json
package/src/bash-funcs CHANGED
@@ -270,7 +270,7 @@ function sendToSlack() {
270
270
  read -rd '' slackmsg<<EOSLACK
271
271
  --- $running by \`$account\` in \`$GCP_PROJECT_ID\` @ $dts
272
272
  \`$toPost\`
273
- node `node --version` / `build-tools --version` / helm `helm version --short`
273
+ node `node --version` / npm `npm --version` / `build-tools --version` / helm `helm version --short`
274
274
  EOSLACK
275
275
  send-to-slack --project ${REFRESH_NPM_PROJECT:-leverege-registry} \
276
276
  --config REFRESH_NPM_SLACK_CONFIG \
@@ -139,7 +139,7 @@ const buildAndPushImage = async () => {
139
139
  const dockerfilePath = path.join( buildDir, 'Dockerfile' )
140
140
 
141
141
  try {
142
- fs.writeFileSync( dockerfilePath, renderedDockerfile )
142
+ fs.writeFileSync( dockerfilePath, renderedDockerfile ) // eslint-disable-line security/detect-non-literal-fs-filename
143
143
 
144
144
  log( chalk.yellow( '\nBuilding Docker image...' ) )
145
145
  const cloudBuildSubmit = 'gcloud builds submit --project leverege-registry'
@@ -96,7 +96,7 @@ if ( args.init ) {
96
96
  errorExit( '\n***ERROR: expected to run in the root of a platform-k8s branch - no overwhelm.yaml present' )
97
97
  }
98
98
 
99
- if ( fs.existsSync( args.compass ) ) {
99
+ if ( fs.existsSync( args.compass ) ) { // eslint-disable-line security/detect-non-literal-fs-filename
100
100
  errorExit( `\n***ERROR: ${args.compass} compass file already exists - not overwriting` )
101
101
  }
102
102
 
@@ -107,12 +107,12 @@ if ( args.init ) {
107
107
 
108
108
  // simply wraps the YAML read/sync
109
109
  const buildCompassMap = ( yamlFile ) => {
110
- if ( !fs.existsSync( yamlFile ) ) {
110
+ if ( !fs.existsSync( yamlFile ) ) { // eslint-disable-line security/detect-non-literal-fs-filename
111
111
  debug( `chart-compass file ${yamlFile} does not exist` )
112
112
  process.exit( 1 )
113
113
  }
114
114
 
115
- const chartCompass = YAML.load( fs.readFileSync( yamlFile, 'utf8' ) )
115
+ const chartCompass = YAML.load( fs.readFileSync( yamlFile, 'utf8' ) ) // eslint-disable-line security/detect-non-literal-fs-filename
116
116
  return chartCompass
117
117
  }
118
118
 
@@ -151,7 +151,7 @@ try {
151
151
  } else {
152
152
  errorExit( `\n***ERROR: chart ${chartName} version ${chartVersion} already exists` )
153
153
  }
154
- } catch ( error ) {
154
+ } catch ( error ) { // eslint-disable-line no-unused-vars
155
155
  debug( `Chart ${chartName} version ${chartVersion} does not exist - proceeding` )
156
156
  }
157
157
 
@@ -123,7 +123,7 @@ const runner = async ( projects ) => {
123
123
  log( `Project [${project}]` )
124
124
  try {
125
125
  await prepareContext( project ) // eslint-disable-line no-await-in-loop
126
- } catch ( error ) {
126
+ } catch ( error ) { // eslint-disable-line no-unused-vars
127
127
  log( `Project or context is invalid, skipping [${chalk.redBright( project )}]` )
128
128
  continue
129
129
  }
@@ -59,6 +59,7 @@ export default class DashboardManager {
59
59
  // leaf directory — apply JSON files using basename as the Grafana folder name
60
60
  const folderName = path.basename( dir )
61
61
  for ( const file of jsonFiles ) {
62
+ // eslint-disable-next-line no-await-in-loop
62
63
  const name = await this.#applyDashboard( folderName, path.join( dir, file.name ) )
63
64
  if ( name ) applied.add( name )
64
65
  }
@@ -79,6 +80,7 @@ export default class DashboardManager {
79
80
  .filter( f => f.endsWith( '.json' ) )
80
81
 
81
82
  for ( const file of files ) {
83
+ // eslint-disable-next-line no-await-in-loop
82
84
  const name = await this.#applyDashboard( folder.name, path.join( dir, folder.name, file ) )
83
85
  if ( name ) applied.add( name )
84
86
  }
@@ -159,6 +161,7 @@ export default class DashboardManager {
159
161
  if ( subDirs.length === 0 ) {
160
162
  const folderName = path.basename( dir )
161
163
  for ( const file of jsonFiles ) {
164
+ // eslint-disable-next-line no-await-in-loop
162
165
  const name = await this.#applyDashboard( folderName, path.join( dir, file.name ), local )
163
166
  if ( name ) applied.add( name )
164
167
  }
@@ -168,6 +171,7 @@ export default class DashboardManager {
168
171
  .filter( f => f.endsWith( '.json' ) )
169
172
 
170
173
  for ( const file of files ) {
174
+ // eslint-disable-next-line no-await-in-loop
171
175
  const name = await this.#applyDashboard( folder.name, path.join( dir, folder.name, file ), local )
172
176
  if ( name ) applied.add( name )
173
177
  }
@@ -259,6 +263,7 @@ export default class DashboardManager {
259
263
  continue
260
264
  }
261
265
  try {
266
+ // eslint-disable-next-line no-await-in-loop
262
267
  await api.deleteNamespacedConfigMap( { name, namespace : this.#namespace } )
263
268
  log( chalk.red( ` ✗ pruned ${name}` ) )
264
269
  } catch ( e ) {
@@ -290,6 +295,7 @@ export default class DashboardManager {
290
295
  continue
291
296
  }
292
297
  try {
298
+ // eslint-disable-next-line no-await-in-loop
293
299
  await api.deleteNamespacedConfigMap( { name, namespace : this.#namespace } )
294
300
  log( chalk.red( ` ✗ pruned (local) ${name}` ) )
295
301
  } catch ( e ) {