@leverege/build-tools 2.93.5 → 2.93.7

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.93.5",
3
+ "version": "2.93.7",
4
4
  "description": "A collection of build / support tools for Leverege developers",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -82,7 +82,7 @@
82
82
  "glob": "^13.0.0",
83
83
  "handlebars": "^4.7.8",
84
84
  "ignore": "^7.0.5",
85
- "inquirer": "^12.11.1",
85
+ "inquirer": "^13.1.0",
86
86
  "js-yaml": "^4.1.1",
87
87
  "jsdoc": "^4.0.5",
88
88
  "ms": "^2.1.3",
@@ -105,4 +105,4 @@
105
105
  "mocha": "^11.7.5",
106
106
  "npm": "^11.7.0"
107
107
  }
108
- }
108
+ }
package/src/Docker.mjs CHANGED
@@ -17,8 +17,14 @@ import {
17
17
 
18
18
  const DIRNAME = path.dirname( url.fileURLToPath( import.meta.url ) )
19
19
 
20
- // TODO: change default to node 22 (jod) after 01/01/25
21
- const dockerfileNodeVersion = 'jod-alpine'
20
+ // TODO: This is a temporary band-aid while we assess the impact of the yarn
21
+ // and/or CI pipeline transition. This will at least allow us to lock down the
22
+ // specific versions of node/npm and facilitate testing via the env vars.
23
+ //
24
+ // See the latest docker images here => https://hub.docker.com/_/node
25
+ //
26
+ const dockerfileNodeVersion = process.env.DOCKER_BUILD_NODE_VERSION || '24.12.0-alpine3.23'
27
+ const dockerfileNpmVersion = process.env.DOCKER_BUILD_NPM_VERSION || '11.6.0'
22
28
 
23
29
  // The contents of these variables were initially located in files that live in
24
30
  // the build-tools repository, but it just became simpler to pull the contents
@@ -55,7 +61,7 @@ const defaultSettings = {
55
61
  nodeimage : 'jod-alpine',
56
62
  imageBase : 'node:jod-alpine',
57
63
  npmlogging : process.env.VERBOSE_NPM_LOGGING ? '--ddd' : '--silent',
58
- npmVersion : 'npm@11',
64
+ npmVersion : `npm@${dockerfileNpmVersion}`,
59
65
  pluginfile : '# NO PLUGIN',
60
66
  preInstallPluginfile : '# NO PRE-INSTALL PLUGIN',
61
67
  regvers : 'package.version',
package/src/Utils.mjs CHANGED
@@ -493,7 +493,7 @@ export const parseHelmChart = async ( helmroot = './helm' ) => {
493
493
 
494
494
  // See if this is a leaf chart and whether or not dependencies need updating
495
495
  const hasDependencies = chartYaml.dependencies !== undefined
496
- let depsOutOfSync
496
+ let depsOutOfSync = hasDependencies // assume unsynced if dependencies
497
497
  if ( chartFiles.includes( 'Chart.lock' ) ) {
498
498
  const chartLock = YAML.load( fs.readFileSync( `${helmroot}/Chart.lock`, 'utf8' ) )
499
499
  // Using Object.fromEntries will be able to handle multiple dependencies if ever needed
@@ -1,11 +1,15 @@
1
1
  import path from 'node:path'
2
+ import fs from 'node:fs'
2
3
 
3
4
  import { shellCmd, log, err, debug } from '../Utils.mjs'
4
5
 
5
6
  import {
6
7
  parseGenericArtifactFileToRemoteRelativePath,
7
8
  parseGenericArtifactSchemaToRemoteRelativePath,
8
- listGenericArtifactFiles
9
+ listGenericArtifactFiles,
10
+ shouldUseIgnoreConfiguration,
11
+ findIgnoreFilePath,
12
+ copyFilesWithIgnoreConfiguration
9
13
  } from './Utils.mjs'
10
14
 
11
15
  class ArtifactPusher {
@@ -29,8 +33,12 @@ class GenericArtifactPusher extends ArtifactPusher {
29
33
  return false
30
34
  }
31
35
 
32
- try {
33
-
36
+ if ( !this.artifact.local_path && !this.artifact.local_file_path ) {
37
+ err( 'Local path or local file path is required for pushing artifacts' )
38
+ return false
39
+ }
40
+
41
+ try {
34
42
  let command = ''
35
43
 
36
44
  command = `gcloud artifacts generic upload \
@@ -40,8 +48,25 @@ class GenericArtifactPusher extends ArtifactPusher {
40
48
  --package=${this.artifact.package} \
41
49
  --version=${this.artifact.version}`
42
50
 
51
+ let pathToDelete
52
+
43
53
  if ( this.artifact.local_path ) {
44
- const localPath = path.join( this.path, this.artifact.local_path )
54
+ const originalLocalPath = path.join( this.path, this.artifact.local_path )
55
+
56
+ let localPath = originalLocalPath
57
+
58
+ const useIgnoreConfiguration = await shouldUseIgnoreConfiguration()
59
+
60
+ if ( useIgnoreConfiguration ) {
61
+ const ignoreFilePath = await findIgnoreFilePath( this.path )
62
+ if ( ignoreFilePath ) {
63
+ const tmpLocalPath = path.join( '/', 'tmp', `${this.artifact.project}_${this.artifact.location}_${this.artifact.repository}_${this.artifact.package}_${this.artifact.version}` )
64
+ localPath = tmpLocalPath
65
+ pathToDelete = tmpLocalPath
66
+ await copyFilesWithIgnoreConfiguration( originalLocalPath, tmpLocalPath, ignoreFilePath )
67
+ }
68
+ }
69
+
45
70
  command += ` --source-directory=${localPath}`
46
71
  command += ' --skip-existing'
47
72
  } else if ( this.artifact.local_file_path ) {
@@ -66,7 +91,11 @@ class GenericArtifactPusher extends ArtifactPusher {
66
91
  debug( { command }, '<==Upload Command' )
67
92
 
68
93
  await shellCmd( command )
69
-
94
+
95
+ if ( pathToDelete ) {
96
+ await fs.promises.rm( pathToDelete, { recursive : true } )
97
+ }
98
+
70
99
  log( `Successfully pushed artifact: ${this.artifact.name}` )
71
100
 
72
101
  return true
@@ -8,7 +8,7 @@ import { parseYamlFile, err, warning, debug, shellCmd } from '../Utils.mjs'
8
8
 
9
9
  import { ArtifactTypeEnum, ArtifactsSchema, GenericArtifactSchema } from './Structs.mjs'
10
10
 
11
- const getArtifactsSchema = async ( artifactsFilePath = './artifacts.yml' ) => {
11
+ const getArtifactsSchema = async ( artifactsFilePath = './artifacts.yaml' ) => {
12
12
  const artifactsConfig = await parseYamlFile( artifactsFilePath )
13
13
  const [ error, result ] = validate( artifactsConfig, ArtifactsSchema )
14
14
  if ( error ) {
@@ -163,7 +163,6 @@ const calculateHash = async ( path ) => {
163
163
  hash.update( await fs.promises.readFile( path ) )
164
164
  return `${hash.digest( 'base64url' )}=`
165
165
  } catch ( error ) {
166
- console.error( error )
167
166
  throw new Error( `Failed to calculate hash for artifact ${path}: ${error.message}` )
168
167
  }
169
168
  }
@@ -186,7 +185,7 @@ const listGenericArtifactFiles = async ( artifact ) => {
186
185
  return files
187
186
  }
188
187
 
189
- const checkGenericArtifactMatch = async ( genericArtifactFiles, genericArtifactSchemaPath, genericArtifactSchema ) => {
188
+ const getMissingRemoteFiles = async ( genericArtifactFiles, genericArtifactSchemaPath, genericArtifactSchema ) => {
190
189
 
191
190
  const expectedRemoteRelativePath = parseGenericArtifactSchemaToRemoteRelativePath( genericArtifactSchema )
192
191
  const localRelativePath = parseGenericArtifactSchemaToLocalRelativePath( genericArtifactSchema )
@@ -221,8 +220,47 @@ const checkGenericArtifactMatch = async ( genericArtifactFiles, genericArtifactS
221
220
  const actualArray = Array.from( actualFiles )
222
221
  const expectedArray = Array.from( expectedFiles )
223
222
 
224
- return actualArray.length === expectedArray.length &&
225
- expectedArray.every( expected => actualArray.some( actual => actual.remoteFilePath === expected.remoteFilePath && actual.remoteFileHash === expected.remoteFileHash ) )
223
+ return expectedArray.filter( expected => !actualArray.some( actual => actual.remoteFilePath === expected.remoteFilePath && actual.remoteFileHash === expected.remoteFileHash ) )
224
+ }
225
+
226
+ const shouldUseIgnoreConfiguration = async () => {
227
+ let result
228
+ try {
229
+ result = await shellCmd( 'gcloud config get-value gcloudignore/enabled' )
230
+ if ( result.trim() === '' || result.includes( 'has no property' ) ) {
231
+ return false
232
+ }
233
+ return result.trim() === 'true'
234
+ } catch ( error ) {
235
+ if ( error.message && error.message.includes( 'Section [gcloudignore] has no property [enabled]' ) ) {
236
+ return false
237
+ }
238
+ // Re-throw other errors
239
+ throw error
240
+ }
241
+ }
242
+
243
+ const findIgnoreFilePath = async ( relativePath ) => {
244
+ if ( relativePath ) {
245
+ const ignoreFilePath = path.join( relativePath, '.gcloudignore' )
246
+ if ( fs.existsSync( ignoreFilePath ) ) {
247
+ return ignoreFilePath
248
+ }
249
+ }
250
+
251
+ const ignoreFilePath = path.join( process.cwd(), '.gcloudignore' )
252
+ if ( fs.existsSync( ignoreFilePath ) ) {
253
+ return ignoreFilePath
254
+ }
255
+
256
+ return null
257
+ }
258
+
259
+ const copyFilesWithIgnoreConfiguration = async ( sourcePath, destinationPath, ignoreFilePath ) => {
260
+ // Ensure sourcePath ends with / so rsync copies contents (not the folder itself) and exclude patterns work correctly
261
+ const normalizedSourcePath = sourcePath.endsWith( '/' ) ? sourcePath : `${sourcePath}/`
262
+ const command = `rsync -av --delete --exclude-from='${ignoreFilePath}' ${normalizedSourcePath} ${destinationPath}`
263
+ await shellCmd( command )
226
264
  }
227
265
 
228
266
  export {
@@ -232,7 +270,12 @@ export {
232
270
  parseGenericArtifactFileToRemoteRelativePath,
233
271
  parseGenericArtifactSchemaToRemoteRelativePath,
234
272
  parseGenericArtifactSchemaToLocalRelativePath,
273
+ parseGenericArtifactSchemaToArtifactNameBase,
274
+ parseGenericArtifactSchemaToArtifactNameFile,
235
275
  parseGenericArtifactSchemaToArtifactName,
236
- checkGenericArtifactMatch,
237
- listGenericArtifactFiles
276
+ getMissingRemoteFiles,
277
+ listGenericArtifactFiles,
278
+ shouldUseIgnoreConfiguration,
279
+ findIgnoreFilePath,
280
+ copyFilesWithIgnoreConfiguration
238
281
  }
@@ -13,12 +13,8 @@ import { GenericArtifactPuller } from './ArtifactPuller.mjs'
13
13
  program
14
14
  .name( 'pull-artifact' )
15
15
  .description( 'Pull artifacts from GCP Artifact Registry' )
16
- .requiredOption( '-f, --file-path <path>', 'Path to artifacts.yaml file' )
17
- .requiredOption( '-p, --project <project>', 'GCP project id' )
18
- .requiredOption( '-l, --location <location>', 'GCP location' )
19
- .requiredOption( '-r, --repository <repository>', 'GCP artifact registry repository' )
20
- .requiredOption( '-n --package <name>', 'GCP artifact registry package name' )
21
- .requiredOption( '-v, --version <version>', 'GCP artifact registry package version' )
16
+ .option( '-f, --file-path <file-path>', 'Path to artifacts.yaml file', './artifacts.yaml' )
17
+ .option( '-a, --artifact-path <artifact-path>', 'GCP artifact path' )
22
18
  .parse()
23
19
 
24
20
  const options = program.opts()
@@ -26,13 +22,26 @@ const options = program.opts()
26
22
  async function main() {
27
23
  const artifactsSchema = await getArtifactsSchema( options.filePath )
28
24
  const flattenedArtifactsSchema = flattenArtifactsSchema( artifactsSchema )
29
-
30
- const filteredArtifactsSchema = flattenedArtifactsSchema.filter(
31
- artifact => artifact.project === options.project &&
32
- artifact.location === options.location &&
33
- artifact.repository === options.repository &&
34
- artifact.package === options.package &&
35
- artifact.version === options.version )
25
+
26
+ let filteredArtifactsSchema = flattenedArtifactsSchema
27
+
28
+ if ( options.artifactPath ) {
29
+ const artifactPathElements = options.artifactPath.split( '/' )
30
+
31
+ if ( artifactPathElements.length !== 5 ) {
32
+ warning( 'Invalid artifact path, it must be in the format <project>/<location>/<repository>/<package>/<version>' )
33
+ return false
34
+ }
35
+
36
+ const [ project, location, repository, packageName, version ] = artifactPathElements
37
+
38
+ filteredArtifactsSchema = flattenedArtifactsSchema.filter(
39
+ artifact => artifact.project === project &&
40
+ artifact.location === location &&
41
+ artifact.repository === repository &&
42
+ artifact.package === packageName &&
43
+ artifact.version === version )
44
+ }
36
45
 
37
46
  let counter = 0
38
47
 
@@ -65,6 +74,8 @@ async function main() {
65
74
  counter = pullResults.filter( Boolean ).length
66
75
 
67
76
  log( `Successfully pulled ${counter}/${filteredArtifactsSchema.length} artifacts` )
77
+
78
+ return counter === filteredArtifactsSchema.length
68
79
  }
69
80
 
70
81
  main()
@@ -13,12 +13,8 @@ import { GenericArtifactPusher } from './ArtifactPusher.mjs'
13
13
  program
14
14
  .name( 'push-artifact' )
15
15
  .description( 'Push artifacts to GCP Artifact Registry' )
16
- .requiredOption( '-f, --file-path <path>', 'Path to artifacts.yaml file' )
17
- .requiredOption( '-p, --project <project>', 'GCP project id' )
18
- .requiredOption( '-l, --location <location>', 'GCP location' )
19
- .requiredOption( '-r, --repository <repository>', 'GCP artifact registry repository' )
20
- .requiredOption( '-n --package <name>', 'GCP artifact registry package name' )
21
- .requiredOption( '-v, --version <version>', 'GCP artifact registry package version' )
16
+ .option( '-f, --file-path <file-path>', 'Path to artifacts.yaml file', './artifacts.yaml' )
17
+ .option( '-a, --artifact-path <artifact-path>', 'GCP artifact path' )
22
18
  .parse()
23
19
 
24
20
  const options = program.opts()
@@ -26,13 +22,25 @@ const options = program.opts()
26
22
  async function main() {
27
23
  const artifactsSchema = await getArtifactsSchema( options.filePath )
28
24
  const flattenedArtifactsSchema = flattenArtifactsSchema( artifactsSchema )
29
-
30
- const filteredArtifactsSchema = flattenedArtifactsSchema.filter(
31
- artifact => artifact.project === options.project &&
32
- artifact.location === options.location &&
33
- artifact.repository === options.repository &&
34
- artifact.package === options.package &&
35
- artifact.version === options.version )
25
+
26
+ let filteredArtifactsSchema = flattenedArtifactsSchema
27
+
28
+ if ( options.artifactPath ) {
29
+ const artifactPathElements = options.artifactPath.split( '/' )
30
+ if ( artifactPathElements.length !== 5 ) {
31
+ warning( 'Invalid artifact path, it must be in the format <project>/<location>/<repository>/<package>/<version>' )
32
+ return false
33
+ }
34
+
35
+ const [ project, location, repository, packageName, version ] = artifactPathElements
36
+
37
+ filteredArtifactsSchema = flattenedArtifactsSchema.filter(
38
+ artifact => artifact.project === project &&
39
+ artifact.location === location &&
40
+ artifact.repository === repository &&
41
+ artifact.package === packageName &&
42
+ artifact.version === version )
43
+ }
36
44
 
37
45
  let counter = 0
38
46
 
@@ -65,6 +73,8 @@ async function main() {
65
73
  counter = pushResults.filter( Boolean ).length
66
74
 
67
75
  log( `Successfully pushed ${counter}/${filteredArtifactsSchema.length} artifacts` )
76
+
77
+ return counter === filteredArtifactsSchema.length
68
78
  }
69
79
 
70
80
  main()
@@ -8,14 +8,14 @@ showInstalling "The Prometheus Operator (kube-prometheus-stack)"
8
8
 
9
9
  # https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack
10
10
  OCI_CHART="oci://ghcr.io/prometheus-community/charts/kube-prometheus-stack"
11
- [ -z "$PROMETHEUS_STACK_CHART_VERSION" ] && PROMETHEUS_STACK_CHART_VERSION="80.2.0"
11
+ [ -z "$PROMETHEUS_STACK_CHART_VERSION" ] && PROMETHEUS_STACK_CHART_VERSION="80.4.1"
12
12
  helm upgrade $NS --install prometheus-stack $OCI_CHART \
13
13
  --values prom-operator/prometheus-stack.yaml \
14
14
  --version $PROMETHEUS_STACK_CHART_VERSION $HELM_WHAT
15
15
 
16
16
  # https://artifacthub.io/packages/helm/prometheus-community/prometheus-elasticsearch-exporter
17
17
  showInstalling "The Elasticsearch Exporter (prom-operator ES metrics exports)"
18
- [ -z "$ELASTICSEARCH_EXPORTER_CHART_VERSION" ] && ELASTICSEARCH_EXPORTER_CHART_VERSION="7.2.0"
18
+ [ -z "$ELASTICSEARCH_EXPORTER_CHART_VERSION" ] && ELASTICSEARCH_EXPORTER_CHART_VERSION="7.2.1"
19
19
  OCI_CHART="oci://ghcr.io/prometheus-community/charts/prometheus-elasticsearch-exporter"
20
20
  helm upgrade $NS --install elasticsearch8-exporter $OCI_CHART \
21
21
  --values prom-operator/elasticsearch-exporter.yaml \
@@ -1,3 +0,0 @@
1
- {
2
- "editor.formatOnSave": true
3
- }