@leverege/build-tools 2.93.3-beta.2 → 2.93.3-beta.4

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.3-beta.2",
3
+ "version": "2.93.3-beta.4",
4
4
  "description": "A collection of build / support tools for Leverege developers",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -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
@@ -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>', 'Path to artifacts.yaml file', './artifacts.yaml' )
17
+ .option( '-a, --artifact <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.artifact ) {
29
+ const artifactPathElements = options.artifact.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>', 'Path to artifacts.yaml file', './artifacts.yaml' )
17
+ .option( '-a, --artifact <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.artifact ) {
29
+ const artifactPathElements = options.artifact.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()