@leverege/build-tools 2.93.0-beta.14 → 2.93.0-beta.16

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/README.md CHANGED
@@ -65,6 +65,15 @@ Usage: k8scale <up|dn|down|roll> [services]
65
65
  * **prune-git**: performs maintenance on a list of git repositories.
66
66
  * **pull-git**: walks a list of git repos, pulling each if they are clean.
67
67
 
68
+ ### Troubleshooting:
69
+
70
+ For additional debug level logging, set the environment variable `BUILD_TOOLS_DEBUG=1` before running the tool.
71
+
72
+ Example:
73
+ ```bash
74
+ BUILD_TOOLS_DEBUG=1 docker-to-registry
75
+ ```
76
+
68
77
  ## Authors
69
78
 
70
79
  * **DevOps and Friends**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leverege/build-tools",
3
- "version": "2.93.0-beta.14",
3
+ "version": "2.93.0-beta.16",
4
4
  "description": "A collection of build / support tools for Leverege developers",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -3,7 +3,7 @@ import fs from 'node:fs'
3
3
 
4
4
  import { shellCmd, log, debug, err } from '../Utils.mjs'
5
5
 
6
- import { toExplicitPath } from './Utils.mjs'
6
+ import { toExplicitPath, deduplicatePath, convertGenericArtifactNameToPath } from './Utils.mjs'
7
7
 
8
8
  class ArtifactPuller {
9
9
  constructor( artifact ) {
@@ -17,55 +17,77 @@ class ArtifactPuller {
17
17
 
18
18
  class GenericArtifactPuller extends ArtifactPuller {
19
19
 
20
+ async prepareFileDownload( tmpFilePath, finalFilePath ) {
21
+ // Ensure directories exist
22
+ fs.mkdirSync( tmpFilePath.split( '/' ).slice( 0, -1 ).join( '/' ), { recursive : true } )
23
+ fs.mkdirSync( finalFilePath.split( '/' ).slice( 0, -1 ).join( '/' ), { recursive : true } )
24
+
25
+ // Ensure no conflicting files exist
26
+ if ( fs.existsSync( tmpFilePath ) ) {
27
+ fs.unlinkSync( tmpFilePath )
28
+ }
29
+ if ( fs.existsSync( finalFilePath ) ) {
30
+ fs.unlinkSync( finalFilePath )
31
+ }
32
+ }
33
+
34
+ async cleanupFileDownload( tmpFilePath, finalFilePath ) {
35
+ // Delete the tmp file
36
+ if ( fs.existsSync( tmpFilePath ) ) {
37
+ fs.unlinkSync( tmpFilePath )
38
+ }
39
+ // Delete the tmp directory
40
+ if ( fs.existsSync( tmpFilePath.split( '/' ).slice( 0, -1 ).join( '/' ) ) ) {
41
+ fs.rmdirSync( tmpFilePath.split( '/' ).slice( 0, -1 ).join( '/' ), { recursive : true } )
42
+ }
43
+ }
44
+
20
45
  async downloadFile( remoteFilePath, localFilePath ) {
21
46
  debug( { remoteFilePath, localFilePath }, '<==Download File' )
22
47
 
23
- const command = `gcloud artifacts generic download \
48
+ // 1 dir per file to avoid dir creation and deletion race conditions for files in the same directory
49
+ const tmpDirectoryPath = `/tmp/${remoteFilePath.replaceAll( '/', '_' ).replaceAll( '.', '_' )}`
50
+ const tmpFilePath = `${tmpDirectoryPath}/${remoteFilePath.split( '/' ).pop()}`
51
+
52
+ await this.prepareFileDownload( tmpFilePath, localFilePath )
53
+
54
+ try {
55
+ const command = `gcloud artifacts generic download \
24
56
  --project=${this.artifact.gcp_project_id} \
25
57
  --location=${this.artifact.gcp_location} \
26
58
  --repository=${this.artifact.gcp_artifact_registry_repository_name} \
27
59
  --package=${this.artifact.gcp_artifact_registry_package_name} \
28
60
  --version=${this.artifact.gcp_artifact_registry_package_version} \
29
61
  --name="${remoteFilePath}" \
30
- --destination=/tmp`
62
+ --destination=${tmpDirectoryPath}`
31
63
 
32
- const oldPath = `/tmp/${remoteFilePath.split( '/' ).pop()}`
33
- const newPath = localFilePath
64
+ debug( { command }, '<==Download File Command' )
34
65
 
35
- // if file exists in the old path, delete it
36
- if ( fs.existsSync( oldPath ) ) {
37
- fs.unlinkSync( oldPath )
38
- }
66
+ await shellCmd( command )
39
67
 
40
- // if file exists in the new path, delete it
41
- if ( fs.existsSync( newPath ) ) {
42
- fs.unlinkSync( newPath )
43
- }
68
+ // Check if file exists in the tmp path
69
+ if ( !fs.existsSync( tmpFilePath ) ) {
70
+ debug( { tmpFilePath }, '<==File does not exist in the tmp path' )
71
+ return false
72
+ }
44
73
 
45
- // Ensure the directory exists
46
- fs.mkdirSync( localFilePath.split( '/' ).slice( 0, -1 ).join( '/' ), { recursive : true } )
74
+ debug( { tmpFilePath, localFilePath }, '<==Copying file' )
47
75
 
48
- debug( { command }, '<==Download File Command' )
76
+ await fs.copyFileSync( tmpFilePath, localFilePath )
49
77
 
50
- await shellCmd( command )
51
-
52
- // Check if file exists in the old path
53
- if ( !fs.existsSync( oldPath ) ) {
54
- debug( { oldPath }, '<==File does not exist in the old path' )
55
- return false
56
- }
57
-
58
- debug( { oldPath, newPath }, '<==Copying file' )
59
-
60
- await fs.copyFileSync( oldPath, newPath )
78
+ // Check if file exists in the local path
79
+ if ( !fs.existsSync( localFilePath ) ) {
80
+ debug( { localFilePath }, '<==File does not exist in the local path' )
81
+ return false
82
+ }
61
83
 
62
- // Check if file exists in the new path
63
- if ( !fs.existsSync( newPath ) ) {
64
- debug( { newPath }, '<==File does not exist in the new path' )
84
+ return true
85
+ } catch ( error ) {
86
+ err( `Error downloading file: ${remoteFilePath} ${error.message}` )
65
87
  return false
88
+ } finally {
89
+ await this.cleanupFileDownload( tmpFilePath, localFilePath )
66
90
  }
67
-
68
- return true
69
91
  }
70
92
 
71
93
  async downloadDirectory( localPath ) {
@@ -88,10 +110,10 @@ class GenericArtifactPuller extends ArtifactPuller {
88
110
  const files = await this.listFiles( remotePath )
89
111
  await Promise.all(
90
112
  files
91
- .filter( file => file.name.split( ':' ).pop().replaceAll( '%2F', '/' ).startsWith( remotePath ) )
113
+ .filter( file => convertGenericArtifactNameToPath( file.name ).startsWith( remotePath ) )
92
114
  .map( async ( file ) => {
93
- const remoteFilePath = file.name.split( ':' ).pop().replaceAll( '%2F', '/' )
94
- const localFilePath = `${localPath}${remoteFilePath.replaceAll( remotePath, '' )}`
115
+ const remoteFilePath = convertGenericArtifactNameToPath( file.name )
116
+ const localFilePath = deduplicatePath( `${localPath}/${remoteFilePath.replaceAll( remotePath, '' )}` )
95
117
  await this.downloadFile( remoteFilePath, localFilePath )
96
118
  } )
97
119
  )
@@ -1,6 +1,8 @@
1
1
  /* eslint-disable security/detect-non-literal-fs-filename */
2
2
  import { shellCmd, log, err, debug } from '../Utils.mjs'
3
3
 
4
+ import { convertGenericArtifactNameToPath } from './Utils.mjs'
5
+
4
6
  class ArtifactPusher {
5
7
  constructor( artifact ) {
6
8
  this.artifact = artifact
@@ -56,7 +58,7 @@ class GenericArtifactPusher extends ArtifactPusher {
56
58
  command += ' --skip-existing'
57
59
  } else if ( this.artifact.local_file_path ) {
58
60
  const files = await this.listFiles( this.artifact.remote_path )
59
- if ( files.some( file => file.name.split( ':' ).pop().replaceAll( '%2F', '/' ) === `${this.artifact.remote_path}/${this.artifact.remote_file_path.split( '/' ).pop()}` ) ) {
61
+ if ( files.some( file => convertGenericArtifactNameToPath( file.name ) === `${this.artifact.remote_path}/${this.artifact.remote_file_path.split( '/' ).pop()}` ) ) {
60
62
  err( `File already exists in the artifact registry: ${this.artifact.local_file_path}` )
61
63
  return false
62
64
  }
@@ -70,6 +70,14 @@ const toExplicitPath = ( path ) => {
70
70
  return explicitPath
71
71
  }
72
72
 
73
+ const deduplicatePath = ( path ) => {
74
+ return path.replaceAll( '//', '/' )
75
+ }
76
+
77
+ const convertGenericArtifactNameToPath = ( genericArtifactName ) => {
78
+ return genericArtifactName.split( ':' ).pop().replaceAll( '%2F', '/' ).replaceAll( '%3A', ':' )
79
+ }
80
+
73
81
  const calculateHash = ( path ) => {
74
82
  try {
75
83
  const hash = crypto.createHash( 'sha256' )
@@ -81,4 +89,4 @@ const calculateHash = ( path ) => {
81
89
  }
82
90
  }
83
91
 
84
- export { calculateHash, getArtifactsSchema, flattenArtifactsSchema, toImplicitPath, toExplicitPath }
92
+ export { calculateHash, getArtifactsSchema, flattenArtifactsSchema, toImplicitPath, toExplicitPath, deduplicatePath, convertGenericArtifactNameToPath }
@@ -19,7 +19,7 @@ program
19
19
  const options = program.opts()
20
20
 
21
21
  async function main() {
22
- const artifactsYmlPath = options.artifactsFilePath
22
+ const artifactsYmlPath = options.filePath
23
23
  const artifactPackageName = options.packageName || null
24
24
  const artifactPackageVersion = options.packageVersion || null
25
25
 
@@ -19,7 +19,7 @@ program
19
19
  const options = program.opts()
20
20
 
21
21
  async function main() {
22
- const artifactsYmlPath = options.artifactsFilePath
22
+ const artifactsYmlPath = options.filePath
23
23
  const artifactPackageName = options.packageName || null
24
24
  const artifactPackageVersion = options.packageVersion || null
25
25