@leverege/build-tools 2.45.0 → 2.46.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leverege/build-tools",
3
- "version": "2.45.0",
3
+ "version": "2.46.0",
4
4
  "description": "A collection of build / support tools for Leverege developers",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -60,9 +60,12 @@
60
60
  "enquirer": "^2.4.1",
61
61
  "execa": "^8.0.1",
62
62
  "glob": "^10.3.10",
63
+ "handlebars": "^4.7.8",
64
+ "inquirer": "^9.2.14",
63
65
  "js-yaml": "^4.1.0",
64
66
  "ms": "^2.1.3",
65
67
  "npm-registry-fetch": "^16.1.0",
68
+ "package-up": "^5.0.0",
66
69
  "parse-gitignore": "^2.0.0",
67
70
  "read-pkg": "^9.0.1",
68
71
  "readline-sync": "^1.4.10",
package/src/Docker.mjs ADDED
@@ -0,0 +1,292 @@
1
+ import fs from 'node:fs'
2
+
3
+ import chalk from 'chalk'
4
+ import handlebars from 'handlebars'
5
+
6
+ import { errorExit, log, proceed, shellCmd, warning } from './Utils.mjs'
7
+
8
+ // The contents of these variables were initially located in files that live in
9
+ // the build-tools repository, but it just became simpler to pull the contents
10
+ // directly into these variables and forego the file loading.
11
+ //
12
+ const dockerfileTemplate = `
13
+ # Version {{regvers}} @ {{date}}
14
+ #
15
+ # The FROM directive sets the Base Image for subsequent instructions
16
+ FROM node:{{nodeimage}} as intermediate
17
+ ENV NODE_ENV production
18
+
19
+ RUN mkdir -p /usr/src/app
20
+ WORKDIR /usr/src/app
21
+
22
+ # Install app dependencies
23
+ COPY ./workspace/ /usr/src/app/
24
+ ENV GRPC_VERBOSITY ERROR
25
+
26
+ # --------------------------------------------------------------
27
+ # copy the ssh keys into place, npm install, and remove them
28
+ # --------------------------------------------------------------
29
+
30
+ # Install packages to install private repos with ssh keys
31
+ COPY ./.npmrc /usr/src/app/.npmrc
32
+ RUN apk --no-cache add openssh-client && \
33
+ apk --update add --no-cache --virtual build-dep g++ gcc libgcc \\
34
+ libstdc++ linux-headers make {{apkadds}} && \
35
+ npm install -g npm@10 && \
36
+ npm ci --only=production --ignore-scripts --no-optional {{npmlogging}} && \
37
+ rm -f /usr/src/app/.npmrc /root/.ssh/*
38
+
39
+ # --------------------------------------------------------------
40
+ # On to the real build now, the thing before was just an intermediate container
41
+ # --------------------------------------------------------------
42
+
43
+ FROM node:{{nodeimage}}
44
+
45
+ # Install tini for PID 1 and replace shell with bash so we can source files
46
+ RUN apk update && \
47
+ apk add --no-cache bash curl tini vim {{apkadds}} && \
48
+ npm install -g npm@10 && \
49
+ rm /bin/sh && ln -s /bin/bash /bin/sh && \
50
+ mkdir -p /usr/src/app /tmp/levlog && \
51
+ chown node:node /usr/src/app
52
+
53
+ {{pluginfile}}
54
+
55
+ ENTRYPOINT [ "/sbin/tini", "--" ]
56
+ WORKDIR /usr/src/app
57
+
58
+ COPY --chown=node:node --from=intermediate /usr/src/app /usr/src/app
59
+
60
+ USER {{runuser}}
61
+ COPY ./bashrc /home/node/.bashrc
62
+ CMD [ "/bin/bash", "-c", "node index.js" ]`
63
+
64
+ const pluginTemplate = `
65
+ # Dockerfile.plugin - optionally extend the service Docker image
66
+ #
67
+ # This file may be used to run additional docker commands during the image
68
+ # build process without needing to maintain a local custom Dockerfile. For
69
+ # example, uncommenting the following docker RUN command will cause the
70
+ # curl and vim packages to be added to the deployed image thus making them
71
+ # available from the pod's command line on k8s:
72
+ #
73
+ # RUN apk update && apk add --no-cache curl vim
74
+ #
75
+ # Keep in mind that the Alpine Linux base image is used to keep image
76
+ # footprints small, so adding packages "just because" is not considered
77
+ # a best practice.
78
+ `
79
+
80
+ const bashrcTemplate = `
81
+ #!/bin/bash
82
+ #
83
+ alias h=history
84
+
85
+ alias ls='ls -CF --color=auto'
86
+ alias ll='ls -lh'
87
+ alias lla='ls -lha'
88
+ alias glep='grep -l -s'
89
+ alias m=less
90
+ alias menv='env | sort | less'
91
+
92
+ alias whatsmyip='wget -qO- ifconfig.co'
93
+
94
+ alias err='wget -q -O- localhost:5111/logLevel/error'
95
+ alias wrn='wget -q -O- localhost:5111/logLevel/warn'
96
+ alias inf='wget -q -O- localhost:5111/logLevel/info'
97
+ alias dbg='wget -q -O- localhost:5111/logLevel/debug'
98
+ alias trc='wget -q -O- localhost:5111/logLevel/trace'
99
+
100
+ socks()
101
+ {
102
+ netstat -ant | awk '{print }' | sort | uniq -c | sort -n
103
+ }
104
+
105
+ cmetrics()
106
+ {
107
+ wget -qO- localhost:5111/metrics
108
+ }
109
+
110
+ cmstat()
111
+ {
112
+ wget -qO- localhost:5111${1}
113
+ }
114
+
115
+ cmclear()
116
+ {
117
+ cmstat /status/clear
118
+ }
119
+
120
+ dunm() {
121
+ du -sh /usr/src/app/node_modules/* | sort -sh
122
+ }
123
+ `
124
+
125
+ // Just uses the old dockreate of formatting the date.
126
+ const dateTimestamp = await shellCmd( 'date +%Y%m%d-%H%M' )
127
+
128
+ const defaultSettings = {
129
+ apkadds : '',
130
+ date : dateTimestamp,
131
+ nodeimage : 'iron-alpine',
132
+ npmlogging : '--silent',
133
+ pluginfile : '# NO PLUGIN',
134
+ regvers : 'package.version',
135
+ runuser : 'node',
136
+ }
137
+
138
+ const previousFile = './docker/.previous'
139
+
140
+ const getPreviousBuild = () => {
141
+ return fs.existsSync( previousFile ) ? fs.readFileSync( previousFile ) : 'FIRST BUILD'
142
+ }
143
+
144
+ const setPreviousBuild = ( version ) => {
145
+ fs.writeFileSync( previousFile, version )
146
+ }
147
+
148
+ const generateDockerfile = ( settings = defaultSettings ) => {
149
+
150
+ log( chalk.green.bold( 'Validating the docker structure\n' ) )
151
+
152
+ // Ensure the presence of the docker dir with plugin and bashrc - this code
153
+ // could probably be tightened up a little.
154
+ try {
155
+ fs.readdirSync( './docker' )
156
+ } catch ( err ) {
157
+ warning( 'creating the missing ./docker directory' )
158
+ fs.mkdirSync( './docker' ) // TODO: blindly assumes the mkdir succeeds
159
+ }
160
+
161
+ const bashrcFile = './docker/bashrc'
162
+ if ( !fs.existsSync( bashrcFile ) ) {
163
+ warning( `creating the missing ${bashrcFile} file in the docker directory` )
164
+ fs.writeFileSync( bashrcFile, bashrcTemplate )
165
+ }
166
+
167
+ const dockerfilePlugin = './docker/Dockerfile.plugin'
168
+ if ( !fs.existsSync( dockerfilePlugin ) ) {
169
+ warning( `creating the missing ${dockerfilePlugin} file in the docker directory` )
170
+ fs.writeFileSync( dockerfilePlugin, pluginTemplate )
171
+ }
172
+
173
+ try {
174
+ const build = fs.readdirSync( './build' )
175
+ if ( build.length === 0 ) {
176
+ errorExit( `***ERROR: the build dir is empty - ${chalk.yellow( 'npm run build' )}` )
177
+ }
178
+ } catch ( err ) {
179
+ errorExit( `***ERROR: expected the ./build directory to exist - ${chalk.yellow( 'npm run build' )}` )
180
+ }
181
+
182
+ // At this point the plugin template better exist.
183
+ if ( fs.existsSync( dockerfilePlugin ) ) {
184
+ defaultSettings.pluginfile = fs.readFileSync( dockerfilePlugin )
185
+ } else {
186
+ errorExit( `***ERROR: something went wrong with ${dockerfilePlugin} plugin creation` )
187
+ }
188
+
189
+ const compiled = handlebars.compile( dockerfileTemplate )
190
+ const replaced = compiled( settings )
191
+ // temporarily write the Dockerfile to the docker subdir - the container
192
+ // image builder will then move it over to the build subdir
193
+ const dockerfile = './docker/Dockerfile'
194
+ try {
195
+ fs.writeFileSync( dockerfile, replaced )
196
+ } catch ( err ) {
197
+ errorExit( `***ERROR: failed to write ${dockerfile}\n${err}` )
198
+ }
199
+
200
+ const previousBuild = getPreviousBuild()
201
+ const dockerInfo = { dockerfile, previousBuild, ...defaultSettings }
202
+ delete dockerInfo.pluginfile
203
+ return dockerInfo
204
+ }
205
+
206
+ const formCloudBuildBucketName = ( artifactProject ) => {
207
+ return `gs://${artifactProject}_cloudbuild`
208
+ }
209
+
210
+ const validateCloudBuildBucket = async ( artifactProject ) => {
211
+ const bucketName = formCloudBuildBucketName( artifactProject )
212
+ log( chalk.green.bold( `Verifying the build bucket exists => ${bucketName}\n` ) )
213
+ try {
214
+ await shellCmd( `gsutil ls -p ${artifactProject} -b ${bucketName}` )
215
+ } catch ( error ) {
216
+ errorExit( `***Error: from Docker.validateCloudBuildBucket\n\n${chalk.bold.yellow( error )}` )
217
+ }
218
+ }
219
+
220
+ const buildContainerImage = async ( { artifactProject, artifactRegistry, containerName, imageVersion } ) => {
221
+ const buildWorkspace = './build/workspace' // passed to Cloud Build
222
+
223
+ if ( fs.existsSync( buildWorkspace ) ) {
224
+ fs.renameSync( buildWorkspace, `${buildWorkspace}-junk` )
225
+ fs.rm( `${buildWorkspace}-junk`, { recursive : true }, ( err ) => {} ) // fire and forget
226
+ }
227
+
228
+ // TODO: This is janky - I'll clean it up later
229
+ // NOTE: this is dealing with raw filesystem interactions, meaning there
230
+ // isn't a shell performing file globbing and what not
231
+ fs.renameSync( './build', './workspace' )
232
+ fs.mkdirSync( './build' )
233
+ fs.renameSync( './workspace', buildWorkspace )
234
+ fs.renameSync( './docker/Dockerfile', './build/Dockerfile' )
235
+ fs.cpSync( `${process.env.HOME}/.npmrc`, './build/.npmrc' )
236
+ fs.cpSync( './docker/bashrc', './build/bashrc' )
237
+ fs.cpSync( './package.json', `${buildWorkspace}/package.json` )
238
+ fs.renameSync( './package-lock.json', `${buildWorkspace}/package-lock.json` )
239
+
240
+ setPreviousBuild( imageVersion ) // update docker/.previous file
241
+
242
+ const gcloudBuild = `time gcloud builds submit --project ${artifactProject}`
243
+ const gcloudLogs = `--gcs-log-dir ${formCloudBuildBucketName( artifactProject )}/log`
244
+ const gcloudTags = `--tag ${artifactRegistry}/images/${containerName}:${imageVersion}`
245
+
246
+ log( chalk.green.bold( `
247
+ *** Submitting Build ***
248
+ ${gcloudBuild} \\
249
+ ${gcloudLogs} \\
250
+ ${gcloudTags}
251
+ ` ) )
252
+
253
+ await shellCmd( `${gcloudBuild} ${gcloudLogs} ${gcloudTags} ./build`, { stdio : 'inherit' } )
254
+ }
255
+
256
+ const repoCleanup = async () => {
257
+ // remove garbase from the repo
258
+ const garbage = [
259
+ 'build',
260
+ 'dist',
261
+ 'docker/.keep',
262
+ 'docker/.npmrc',
263
+ 'docker/.gitignore',
264
+ 'docker/Dockerfile',
265
+ 'docker/workspace',
266
+ ]
267
+ garbage.forEach( trash => fs.rmSync( trash, { recursive : true, force : true } ) )
268
+
269
+ fs.writeFileSync( 'docker/.gitignore', '.previous' )
270
+
271
+ await shellCmd( 'git add -f docker' )
272
+
273
+ log( `
274
+ ${chalk.yellow.bold( '***IMPORTANT***' )}
275
+
276
+ Please make the following .gitignore modifications:
277
+ change /dist to ${chalk.green.bold( '/build' )}
278
+ remove ${chalk.red.bold( '/docker' )}
279
+ ` )
280
+
281
+ await proceed( 'Acknowledge the .gitignore mods were made?' )
282
+ }
283
+
284
+ export default {
285
+ getPreviousBuild,
286
+ setPreviousBuild,
287
+ generateDockerfile,
288
+ formCloudBuildBucketName,
289
+ validateCloudBuildBucket,
290
+ buildContainerImage,
291
+ repoCleanup,
292
+ }
package/src/Utils.mjs CHANGED
@@ -2,6 +2,9 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs'
2
2
 
3
3
  import chalk from 'chalk'
4
4
  import { $ } from 'execa'
5
+ import inquirer from 'inquirer'
6
+ import { glob } from 'glob'
7
+ import { packageUp } from 'package-up'
5
8
  import YAML from 'js-yaml'
6
9
 
7
10
  const debugEnabled = process.env.BUILD_TOOLS_DEBUG === '1'
@@ -19,12 +22,34 @@ export const condir = ( obj, text = '' ) => {
19
22
  export const debug = ( obj, text ) => {
20
23
  if ( debugEnabled ) { condir( obj, text ) }
21
24
  }
25
+ export const warning = ( warningText ) => {
26
+ const fullWarning = `***WARNING: ${warningText}`
27
+ console.error( `\n${chalk.yellow.bold( fullWarning )}` )
28
+ }
29
+
22
30
  export const errorExit = ( error, opts = { errorCode : 1 } ) => {
23
31
  console.error( `\n${chalk.red.bold( error )}\n` )
24
32
  if ( opts?.errorCode ) { process.exit( opts.errorCode ) }
25
33
  }
26
34
  /* eslint-enable no-console */
27
35
 
36
+ /**
37
+ * General "yes" to proceed function.
38
+ */
39
+ export const proceed = async ( query = 'Do you wish to proceed?' ) => {
40
+ await inquirer.prompt( [
41
+ {
42
+ type : 'confirm',
43
+ name : 'proceed',
44
+ message : query,
45
+ default : true,
46
+ }
47
+ ]
48
+ ).then( ( answer ) => {
49
+ if ( !answer.proceed ) { process.exit( 0 ) }
50
+ } )
51
+ }
52
+
28
53
  export const shellCmd = async ( cmdstr, opts = {} ) => {
29
54
  try {
30
55
  // due to the escaping rules we pull apart the cmdstr and invoke $ using
@@ -85,42 +110,184 @@ export const parseJsonFile = async ( jsonFile ) => {
85
110
  throw new Error( `${error} in ${jsonFile}` )
86
111
  }
87
112
  }
88
- throw new Error( `File ${jsonFile} does not exist` )
113
+ throw new Error( `Utils.parseJsonFile: file ${jsonFile} does not exist in ${process.cwd()}` )
89
114
  /* eslint-enable security/detect-non-literal-fs-filename */
90
115
  }
91
116
 
92
117
  // Parses the ./package.json file and verifies there is a properly formatted
93
118
  // leverege.registry section present.
119
+ const deprecated = ( opts ) => {
120
+ log( chalk.red.bold( `\n ***DEPRECATED: ${opts.deprecationError}` ) )
121
+ log( chalk.white( opts.deprecationInfo ) )
122
+ process.exit( 1 )
123
+ }
124
+
94
125
  export const parsePackageJson = async ( packageFileName ) => {
95
126
  const packageJson = await parseJsonFile( packageFileName )
96
127
 
128
+ // deprecation checks are here to ease the pain of upgrading older repos
97
129
  const leveregeClauseError = `leverege.registry statement, it should
98
130
  resemble something like:
99
131
 
100
132
  "leverege": {
101
- "registry": "us-docker.pkg.dev/leverege-registry/<REPO NAME>"
133
+ "registry": "us-docker.pkg.dev/leverege-registry/<registry folder>"
102
134
  },
103
135
 
104
- `
105
136
 
106
- if ( !packageJson?.leverege?.registry ) {
137
+ Where <registry folder> should be an existing folder in the artifact-registry
138
+ and must not contain the service name. Visit the link below for a list of the
139
+ existing / valid registry folders.
140
+
141
+ https://console.cloud.google.com/artifacts/browse/leverege-registry?project=leverege-registry
142
+ `
143
+ const registry = packageJson?.leverege?.registry
144
+ if ( !registry ) {
107
145
  errorExit( `Error: package.json is missing a proper ${leveregeClauseError}` )
108
146
  }
109
147
 
110
- const artifacts = packageJson.leverege.registry.split( '/' )
111
- if ( artifacts.length !== 3 ) {
148
+ const registryComponents = registry.split( '/' )
149
+ if ( registryComponents.length !== 3 ) {
112
150
  errorExit( `Error: package.json has a malformed ${leveregeClauseError}` )
113
151
  }
114
152
 
115
- return {
116
- artifactRegistry : {
117
- registry : packageJson.leverege.registry,
118
- region : artifacts[0],
119
- project : artifacts[1],
120
- repository : artifacts[2],
121
- },
122
- ...packageJson,
153
+ const [ region, project, repository ] = registryComponents
154
+ const artifactRegistry = { region, project, repository }
155
+
156
+ // DEPRECATED: leverege.project
157
+ if ( packageJson?.leverege?.project ) {
158
+ const removeLine = `"project": "${packageJson.leverege.project}",`
159
+
160
+ const deprecationError = 'leverege.project is no longer supported'
161
+ const deprecationInfo = `
162
+ Setting the project in the package.json leverege section was used for builds
163
+ being stored in the deprecated GCP container registry. Remove the deprecated
164
+ leverege.project setting from package.json and try again.
165
+
166
+ "leverege": {
167
+ ${chalk.red.bold( removeLine )}
168
+ ...
169
+ }
170
+ `
171
+ deprecated( { deprecationError, deprecationInfo } )
172
+ }
173
+
174
+ // DEPRECATED: leverege.container
175
+ if ( packageJson?.leverege?.container ) {
176
+ const removeLine = `"container": "${packageJson.leverege.container}",`
177
+
178
+ const deprecationError = 'leverege.container is no longer supported'
179
+ const deprecationInfo = `
180
+ Setting the container name explicitly from the package.json leverge block
181
+ is no longer supported. The container name will be automatically derived
182
+ from the git repository's remote root, which is the default behavior. Remove
183
+ the container line from the leverege section in package.json and try again.
184
+
185
+ "leverege": {
186
+ ${chalk.red.bold( removeLine )}
187
+ ...
188
+ }
189
+ `
190
+ deprecated( { deprecationError, deprecationInfo } )
191
+ }
192
+
193
+ // DEPRECATED: leverege.nodeimg
194
+ if ( packageJson?.leverege?.nodeimg ) {
195
+ const removeLine = `"nodeimg": "${packageJson.leverege.nodeimg}",`
196
+
197
+ const deprecationError = 'leverege.nodeimg is no longer supported'
198
+ const deprecationInfo = `
199
+ Setting the node base image using the nodeimg statement in package.json is no
200
+ longer supported. By default the actual node image version will be defaulted
201
+ by this script and will rarely need to be a specific version. Remove the
202
+ nodeimg line from the leverege section in package.json and try again.
203
+
204
+ "leverege": {
205
+ ${chalk.red.bold( removeLine )}
206
+ ...
207
+ }
208
+ `
209
+ deprecated( { deprecationError, deprecationInfo } )
210
+ }
211
+
212
+ // DEPRECATED: leverege.artifact
213
+ if ( packageJson?.leverege?.artifact ) {
214
+ const removeLine = `"artifact": "${packageJson.leverege.artifact}",`
215
+ const replaceLine = `"registry": "us-docker.pkg.dev/leverege-registry/${packageJson.leverege.artifact}",`
216
+
217
+ const deprecationError = 'leverege.artifact is no longer supported'
218
+ const deprecationInfo = `
219
+ Setting the registry folder via the ${chalk.yellow.bold( 'artifact' )} statement is no longer supported.
220
+ Instead use the ${chalk.green.bold( 'leverege.registry' )} setting to specify the full artifact registry
221
+ and folder used for storing the docker image. Replace the artifact line with the
222
+ full registry path in package.json and try again.
223
+
224
+ "leverege": {
225
+ ${chalk.red.bold( removeLine )}
226
+ ${chalk.green.bold( replaceLine )}
227
+ ...
228
+ }
229
+ `
230
+ deprecated( { deprecationError, deprecationInfo } )
123
231
  }
232
+
233
+ // DEPRECATED: leverege.nodeops
234
+ if ( packageJson?.leverege?.nodeops ) {
235
+ const removeLine = `"nodeops": "${packageJson.leverege.nodeops}",`
236
+
237
+ const deprecationError = 'leverege.nodeops is no longer supported'
238
+ const deprecationInfo = `
239
+ Setting the hard coded node options on the image is no longer supported. The
240
+ better approach is to add ${chalk.green.bold( 'NODE_OPTIONS' )} to the config section of the chart's
241
+ values.yaml to allow downstream users to easily tune the options as needed.
242
+ Remove the nodeops line from the leverege section in package.json and try again.
243
+
244
+ "leverege": {
245
+ ${chalk.red.bold( removeLine )}
246
+ ...
247
+ }
248
+ `
249
+ deprecated( { deprecationError, deprecationInfo } )
250
+ }
251
+
252
+ // DEPRECATED: old reference to ./dist should be ./build
253
+ const { build, clean, dockerize, } = packageJson.scripts
254
+
255
+ if ( build.match( 'dist' ) || clean.match( 'dist' ) ) {
256
+ const deprecationError = `
257
+
258
+ Update the npm build and clean scripts to use ./build instead of the old
259
+ ./dist directory.
260
+ `
261
+ const deprecationInfo = `
262
+ "scripts": {
263
+ ${chalk.green.bold( '"build": "npm run clean && mkdir ./build && cp -r ./src/* ./build",' )}
264
+ ...
265
+ ${chalk.green.bold( '"clean": "rm -fr coverage build",' )}
266
+ },
267
+ `
268
+
269
+ deprecated( { deprecationError, deprecationInfo } )
270
+ }
271
+
272
+ // DEPRECATED: old dockerize reference to docker dir and dockreate script
273
+ if ( dockerize.match( 'dockreate|cd docker' ) ) {
274
+ const deprecationError = `
275
+
276
+ Update the npm dockerize script to use docker-to-registry instead of the
277
+ deprecated dockreate script, and remove the old "cd docker" as well. The new
278
+ dockerize target is simply:
279
+ `
280
+ const deprecationInfo = `
281
+ "scripts": {
282
+ ...
283
+ ${chalk.green.bold( '"dockerize": "npm run build && docker-to-registry",' )}
284
+ ...
285
+ },
286
+ `
287
+
288
+ deprecated( { deprecationError, deprecationInfo } )
289
+ }
290
+ return { artifactRegistry, ...packageJson }
124
291
  }
125
292
 
126
293
  export const parseHelmChart = async ( helmroot ) => {
@@ -137,3 +304,88 @@ export const parseHelmChart = async ( helmroot ) => {
137
304
  }
138
305
  } )
139
306
  }
307
+
308
+ export const analyzeRepository = async () => {
309
+ const gitRoot = await getGitRootDirectory()
310
+ const subPkgs = await glob( `${gitRoot}/packages/**/package.json`, { ignore : '/**/node_modules/**' } )
311
+ const isMonoRepo = subPkgs?.length > 0
312
+
313
+ // Verify the presence of a root package.json file for all monorepos
314
+ let rootPackageJson
315
+ try {
316
+ rootPackageJson = await parseJsonFile( `${gitRoot}/package.json` )
317
+ } catch ( error ) {
318
+ rootPackageJson = { isInvalid : true, error : 'invalid or non-existent root package.json file' }
319
+ }
320
+ const isNpmWorkspace = rootPackageJson?.workspaces?.length > 0
321
+ if ( isMonoRepo && rootPackageJson.isInvalid ) {
322
+ log( chalk.red.bold( `
323
+ ***REQUIRED: A package.json file is required at the monorepo root` ) )
324
+
325
+ log( chalk.green.bold( `
326
+ A basic root package.json resembles the following:` ) )
327
+
328
+ log( chalk.yellow.bold( `
329
+ {
330
+ "name": "@leverege/<your mono name>",
331
+ "description": "A monorepo for <what is it for?>",
332
+ "scripts": {
333
+ "lint": "npm run lint --workspaces",
334
+ "prepare": "husky",
335
+ "test": "npm run test --workspaces"
336
+ },
337
+ "author": "Leverege",
338
+ "license": "SEE LICENSE IN LICENSE.md",
339
+ "devDependencies": {
340
+ "husky": "^9.0.5"
341
+ },
342
+ "workspaces": [
343
+ ]
344
+ }` ) )
345
+ process.exit( 1 )
346
+ }
347
+
348
+ log( chalk.green.bold( '\nParsing and analyzing the package.json file\n' ) )
349
+ let closestPackageJson
350
+ try {
351
+ closestPackageJson = await parsePackageJson( './package.json' )
352
+ } catch ( error ) {
353
+ errorExit( error )
354
+ }
355
+ const containerName = closestPackageJson.name
356
+ const artifactProject = closestPackageJson.artifactRegistry.project
357
+ const artifactRegistry = closestPackageJson.leverege.registry
358
+
359
+ const currentDir = process.cwd()
360
+ const closestPkg = await packageUp()
361
+
362
+ // get the name of the current workspace, if we're in one
363
+ let wsName
364
+ try {
365
+ wsName = await shellCmd( 'npm exec -c pwd -ws' )
366
+ } catch ( err ) { }
367
+
368
+ let gitRemote
369
+ try {
370
+ gitRemote = await shellCmd( 'git config --get remote.origin.url' )
371
+ } catch ( err ) { }
372
+
373
+ // TODO: Add in Define VERBOSE_DOCKREATE_NPM="yes" for verbose build logging
374
+ // TODO: Validity check for version and package.json
375
+
376
+ return {
377
+ artifactProject,
378
+ artifactRegistry,
379
+ containerName,
380
+ currentDir,
381
+ closestPkg,
382
+ gitRoot,
383
+ gitRemote,
384
+ isMonoRepo,
385
+ isNpmWorkspace,
386
+ rootPackageJson,
387
+ closestPackageJson,
388
+ subPkgs,
389
+ wsName,
390
+ }
391
+ }
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env node
2
+ /*
3
+ * docker-to-registry will...
4
+ */
5
+ import chalk from 'chalk'
6
+
7
+ import {
8
+ debug, log,
9
+ errorExit,
10
+ analyzeRepository,
11
+ proceed,
12
+ shellCmd } from './Utils.mjs'
13
+
14
+ import docker from './Docker.mjs'
15
+
16
+ // refresh-npm-token does not throw so no need to try, but it emits in debug
17
+ const refreshErr = await shellCmd( 'refresh-npm-token' )
18
+ if ( refreshErr && !process.env.BUILD_TOOLS_DEBUG ) {
19
+ errorExit( `***Error: npm token refresh failed\n\n${refreshErr}` )
20
+ }
21
+
22
+ // Expected to be invoked like docker-to-registry v1.2.3
23
+ const imageVersion = process.argv[2]
24
+ const willGitTag = imageVersion.match( /^v\d+\.\d+\.\d+$/ )
25
+ const tagInfo = willGitTag ?
26
+ chalk.yellow.bold( 'will be git tagged' ) :
27
+ chalk.red( 'BETA RELEASE WILL NOT BE GIT TAGGED' )
28
+
29
+ const repoDescr = await analyzeRepository()
30
+ debug( { repoDescr }, '<==The Repo Description' )
31
+
32
+ const dockerInfo = await docker.generateDockerfile()
33
+ debug( { dockerInfo }, '<==The Docker Info' )
34
+
35
+ const { artifactProject, artifactRegistry, containerName } = repoDescr
36
+
37
+ if ( imageVersion === 'guide-me' ) {
38
+ await docker.validateCloudBuildBucket( artifactProject )
39
+ }
40
+
41
+ const registryFolder = `${artifactRegistry}/images/${containerName}`
42
+
43
+ // Update the dependencies...
44
+ log( chalk.green.bold( 'Updating dependencies and workspace...' ) )
45
+ await shellCmd( 'npm install', { stdio : 'inherit' } )
46
+
47
+ if ( imageVersion === 'guide-me' ) {
48
+ await docker.repoCleanup() // adjusts gitignore files and removes cruft
49
+ /* eslint-disable max-len */
50
+ log( `
51
+
52
+
53
+ Moving to the new artifact-registry for docker image storage may also require
54
+ helm chart changes depending on how old the current charts are. Charts that
55
+ contain helm/values.yaml files that resemble this:
56
+
57
+ ${chalk.yellow.bold( `serviceConfig:
58
+ VERSION: v1.2.3
59
+ PREEMPTIBLE: true` )}
60
+
61
+ are pre-ignition era charts and should be replaced with the latest helm chart
62
+ templates available from ignition. Either dive in or ask devops for a hand
63
+ when converting these legacy / deprecated charts.
64
+
65
+
66
+ More recently updated helm charts that have already started the migration to
67
+ the new approach may already have registry entries like this:
68
+
69
+ ${chalk.yellow.bold( `image:
70
+ registry: gcr.io/leverege-docker-images
71
+ tag: ""` )}
72
+
73
+ The above registry statement references the deprecated container registry and
74
+ must be adjusted accordingly to resemble:
75
+
76
+ ${chalk.green.bold( 'registry: us-docker.pkg.dev/leverege-registry/<registry folder>/images' )}
77
+
78
+ There may also be a corresponding modification needed in the helm template
79
+ deployment.yaml file in the container image spec:
80
+
81
+ ${chalk.green.bold( 'image: {{ .Values.image.registry }}/{{ .Chart.Name }}:{{ default .Chart.AppVersion .Values.image.tag }}' )}
82
+
83
+
84
+ ${chalk.magenta.bold( `Upgrading all service charts to the latest ignition template chart structure
85
+ is the preferred approach to making the transition to the artifact registry.` )}
86
+
87
+ ` )
88
+ log( chalk.green.bold( '*** docker-to-registry guidance complete - ready for dockerization ***' ) )
89
+ process.exit( 0 )
90
+ }
91
+
92
+ // Give the summary and the user a chance to proceed or not
93
+ log( `
94
+ ${chalk.green.bold( 'Build Information:' )}
95
+ Container: ${chalk.green.bold( containerName )}
96
+ Version: ${chalk.green.bold( imageVersion )} ${tagInfo}
97
+ Registry: ${chalk.green.bold( artifactRegistry )}
98
+ Image Folder: ${chalk.yellow.bold( registryFolder )}
99
+ Monorepo: ${chalk.green.bold( repoDescr.isMonoRepo )}
100
+ NPM Workspace: ${chalk.green.bold( repoDescr.isNpmWorkspace )}
101
+
102
+ ${chalk.green.bold( 'Docker Information:' )}
103
+ NodeImage: ${chalk.green.bold( dockerInfo.nodeimage )}
104
+ AddedPkgs: ${chalk.green.bold( dockerInfo.apkadds )}
105
+ Run User: ${chalk.green.bold( dockerInfo.runuser )}
106
+ Previous: ${chalk.yellow.bold( dockerInfo.previousBuild )}
107
+ DateStamp: ${chalk.green.bold( dockerInfo.date )}
108
+ NPM Logs: ${chalk.green.bold( dockerInfo.npmlogging )}
109
+ ` )
110
+
111
+ await proceed()
112
+
113
+ if ( repoDescr.isNpmWorkspace ) {
114
+ log( chalk.yellow.bold( 'Generating an NPM workspace package-lock.json file' ) )
115
+ await shellCmd( 'npm install --package-lock-only --workspaces false', { stdio : 'inherit' } )
116
+ }
117
+
118
+ await docker.buildContainerImage( { artifactProject, artifactRegistry, containerName, imageVersion } )
@@ -1,15 +1,15 @@
1
1
  postgresql:
2
2
  postgresqlExtendedConf:
3
- maxWorkerProcesses: 8
4
- maxParallelWorkers: 8
5
- maxConnections: 500
6
- sharedBuffers: 2GB
7
- effectiveCacheSize: 6GB
8
- maintenanceWorkMem: 1GB
9
- workMem: 20MB
10
- idle_in_transaction_session_timeout: 30000 # 30ms timeout
11
- maxWalSize: 2GB
12
- walKeepSegments: 64
3
+ effectiveCacheSize: 6GB # 524288 8kB
4
+ idle_in_transaction_session_timeout: 30000 # 0 ms
5
+ maintenanceWorkMem: 1GB # 65536 kB
6
+ maxConnections: 500 # 100
7
+ maxParallelWorkers: 8 # 32
8
+ maxWalSize: 2GB # 80 MB
9
+ maxWorkerProcesses: 8 # 32
10
+ sharedBuffers: 2GB # 16384 8kB
11
+ walKeepSegments: 64 # deprecated
12
+ workMem: 20MB # 4096 kB
13
13
 
14
14
  resources:
15
15
  requests: