@leverege/build-tools 2.93.3-beta.5 → 2.93.3
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 +3 -4
- package/src/artifacts/ArtifactPuller.mjs +102 -56
- package/src/artifacts/ArtifactPusher.mjs +38 -63
- package/src/artifacts/Structs.mjs +25 -32
- package/src/artifacts/Utils.mjs +27 -216
- package/src/artifacts/pull-artifact.mjs +20 -27
- package/src/artifacts/push-artifact.mjs +19 -25
- package/src/bash-funcs +5 -0
- package/src/helmup.sh +3 -0
- package/src/k8scale.sh +2 -1
- package/.vscode/settings.json +0 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leverege/build-tools",
|
|
3
|
-
"version": "2.93.3
|
|
3
|
+
"version": "2.93.3",
|
|
4
4
|
"description": "A collection of build / support tools for Leverege developers",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
"clean": "rm -rf lib",
|
|
12
12
|
"lint": "find ./src -name \\*.\\*js | xargs npx eslint --report-unused-disable-directives",
|
|
13
13
|
"prepare": "[ $SKIP_PREPARE ] && exit 0 || ./src/hook-and-release.mjs",
|
|
14
|
-
"test": "mocha -t 60000 --exit 'test
|
|
15
|
-
"test-all": "mocha -t 60000 --exit 'test/**/**.mjs'",
|
|
14
|
+
"test": "mocha -t 60000 --exit 'test/**/*.mjs'",
|
|
16
15
|
"prepack": "npm exec prepack"
|
|
17
16
|
},
|
|
18
17
|
"bin": {
|
|
@@ -104,4 +103,4 @@
|
|
|
104
103
|
"mocha": "^11.7.5",
|
|
105
104
|
"npm": "^11.6.2"
|
|
106
105
|
}
|
|
107
|
-
}
|
|
106
|
+
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
/* eslint-disable security/detect-non-literal-fs-filename */
|
|
2
2
|
import fs from 'node:fs'
|
|
3
|
-
import path from 'node:path'
|
|
4
3
|
|
|
5
4
|
import { shellCmd, log, debug, err } from '../Utils.mjs'
|
|
6
5
|
|
|
7
|
-
import {
|
|
6
|
+
import { toExplicitPath, deduplicatePath, convertGenericArtifactNameToPath } from './Utils.mjs'
|
|
8
7
|
|
|
9
8
|
class ArtifactPuller {
|
|
10
|
-
constructor(
|
|
11
|
-
this.path = path
|
|
9
|
+
constructor( artifact ) {
|
|
12
10
|
this.artifact = artifact
|
|
13
11
|
}
|
|
14
12
|
|
|
@@ -19,101 +17,142 @@ class ArtifactPuller {
|
|
|
19
17
|
|
|
20
18
|
class GenericArtifactPuller extends ArtifactPuller {
|
|
21
19
|
|
|
22
|
-
async
|
|
23
|
-
|
|
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
24
|
|
|
25
|
-
|
|
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
|
+
}
|
|
26
33
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
tmpLocalRelativePath = path.join( '/', 'tmp', encodedArtifactName )
|
|
32
|
-
} else {
|
|
33
|
-
await fs.promises.mkdir( path.dirname( localRelativePath ), { recursive : true } )
|
|
34
|
+
async cleanupFileDownload( tmpFilePath, finalFilePath ) {
|
|
35
|
+
// Delete the tmp file
|
|
36
|
+
if ( fs.existsSync( tmpFilePath ) ) {
|
|
37
|
+
fs.unlinkSync( tmpFilePath )
|
|
34
38
|
}
|
|
39
|
+
// Delete the tmp directory
|
|
40
|
+
if ( fs.existsSync( tmpFilePath.split( '/' ).slice( 0, -1 ).join( '/' ) ) ) {
|
|
41
|
+
fs.rmSync( tmpFilePath.split( '/' ).slice( 0, -1 ).join( '/' ), { recursive : true } )
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async downloadFile( remoteFilePath, localFilePath ) {
|
|
46
|
+
debug( { remoteFilePath, localFilePath }, '<==Download File' )
|
|
47
|
+
|
|
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 )
|
|
35
53
|
|
|
36
54
|
try {
|
|
37
55
|
const command = `gcloud artifacts generic download \
|
|
38
|
-
--project=${this.artifact.
|
|
39
|
-
--location=${this.artifact.
|
|
40
|
-
--repository=${this.artifact.
|
|
41
|
-
--package=${this.artifact.
|
|
42
|
-
--version=${this.artifact.
|
|
43
|
-
--name="${
|
|
44
|
-
--destination=${
|
|
56
|
+
--project=${this.artifact.gcp_project_id} \
|
|
57
|
+
--location=${this.artifact.gcp_location} \
|
|
58
|
+
--repository=${this.artifact.gcp_artifact_registry_repository_name} \
|
|
59
|
+
--package=${this.artifact.gcp_artifact_registry_package_name} \
|
|
60
|
+
--version=${this.artifact.gcp_artifact_registry_package_version} \
|
|
61
|
+
--name="${remoteFilePath}" \
|
|
62
|
+
--destination=${tmpDirectoryPath}`
|
|
45
63
|
|
|
46
64
|
debug( { command }, '<==Download File Command' )
|
|
47
65
|
|
|
48
66
|
await shellCmd( command )
|
|
49
67
|
|
|
50
|
-
if
|
|
51
|
-
|
|
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
|
+
}
|
|
73
|
+
|
|
74
|
+
debug( { tmpFilePath, localFilePath }, '<==Copying file' )
|
|
75
|
+
|
|
76
|
+
await fs.copyFileSync( tmpFilePath, localFilePath )
|
|
77
|
+
|
|
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
|
|
52
82
|
}
|
|
53
83
|
|
|
54
84
|
return true
|
|
55
85
|
} catch ( error ) {
|
|
56
|
-
err( `Error downloading file: ${
|
|
86
|
+
err( `Error downloading file: ${remoteFilePath} ${error.message}` )
|
|
57
87
|
return false
|
|
88
|
+
} finally {
|
|
89
|
+
await this.cleanupFileDownload( tmpFilePath, localFilePath )
|
|
58
90
|
}
|
|
59
91
|
}
|
|
60
92
|
|
|
61
|
-
async downloadDirectory(
|
|
62
|
-
|
|
63
|
-
await fs.promises.mkdir( localRelativePath, { recursive : true } )
|
|
64
|
-
|
|
93
|
+
async downloadDirectory( localPath ) {
|
|
65
94
|
const command = `gcloud artifacts generic download \
|
|
66
|
-
--project=${this.artifact.
|
|
67
|
-
--location=${this.artifact.
|
|
68
|
-
--repository=${this.artifact.
|
|
69
|
-
--package=${this.artifact.
|
|
70
|
-
--version=${this.artifact.
|
|
71
|
-
--destination=${
|
|
95
|
+
--project=${this.artifact.gcp_project_id} \
|
|
96
|
+
--location=${this.artifact.gcp_location} \
|
|
97
|
+
--repository=${this.artifact.gcp_artifact_registry_repository_name} \
|
|
98
|
+
--package=${this.artifact.gcp_artifact_registry_package_name} \
|
|
99
|
+
--version=${this.artifact.gcp_artifact_registry_package_version} \
|
|
100
|
+
--destination=${localPath}`
|
|
72
101
|
|
|
73
102
|
debug( { command }, '<==Download Directory Command' )
|
|
74
103
|
|
|
75
104
|
await shellCmd( command )
|
|
76
105
|
}
|
|
77
106
|
|
|
78
|
-
async downloadNestedDirectory(
|
|
79
|
-
debug( {
|
|
107
|
+
async downloadNestedDirectory( remotePath, localPath ) {
|
|
108
|
+
debug( { remotePath, localPath }, '<==Download Nested Directory' )
|
|
80
109
|
|
|
81
|
-
const files = await
|
|
110
|
+
const files = await this.listFiles( remotePath )
|
|
82
111
|
await Promise.all(
|
|
83
112
|
files
|
|
84
|
-
.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
return acc
|
|
92
|
-
}, [] )
|
|
113
|
+
.filter( file => convertGenericArtifactNameToPath( file.name ).startsWith( remotePath ) )
|
|
114
|
+
.map( async ( file ) => {
|
|
115
|
+
const remoteFilePath = convertGenericArtifactNameToPath( file.name )
|
|
116
|
+
const localFilePath = deduplicatePath( `${localPath}/${remoteFilePath.replaceAll( remotePath, '' )}` )
|
|
117
|
+
await this.downloadFile( remoteFilePath, localFilePath )
|
|
118
|
+
} )
|
|
93
119
|
)
|
|
94
120
|
}
|
|
95
121
|
|
|
122
|
+
async listFiles( ) {
|
|
123
|
+
const command = `gcloud artifacts files list \
|
|
124
|
+
--project=${this.artifact.gcp_project_id} \
|
|
125
|
+
--location=${this.artifact.gcp_location} \
|
|
126
|
+
--repository=${this.artifact.gcp_artifact_registry_repository_name} \
|
|
127
|
+
--package=${this.artifact.gcp_artifact_registry_package_name} \
|
|
128
|
+
--version ${this.artifact.gcp_artifact_registry_package_version} \
|
|
129
|
+
--format="json"`
|
|
130
|
+
|
|
131
|
+
debug( { command }, '<==List Files Command' )
|
|
132
|
+
|
|
133
|
+
const result = await shellCmd( command )
|
|
134
|
+
|
|
135
|
+
// Parse the JSON response
|
|
136
|
+
const files = JSON.parse( result )
|
|
137
|
+
return files
|
|
138
|
+
}
|
|
139
|
+
|
|
96
140
|
async pull() {
|
|
97
141
|
log( `Pulling artifact: ${this.artifact.name}` )
|
|
98
142
|
|
|
99
143
|
debug( { artifact : this.artifact }, '<==Pulling artifact' )
|
|
100
144
|
|
|
101
145
|
try {
|
|
102
|
-
const remoteRelativePath = parseGenericArtifactSchemaToRemoteRelativePath( this.artifact )
|
|
103
|
-
const localRelativePath = parseGenericArtifactSchemaToLocalRelativePath( this.artifact )
|
|
104
|
-
|
|
105
146
|
if ( this.artifact.remote_file_path && this.artifact.local_file_path ) {
|
|
106
|
-
await this.downloadFile(
|
|
147
|
+
await this.downloadFile( this.artifact.remote_file_path, toExplicitPath( this.artifact.local_file_path ) )
|
|
107
148
|
} else if ( this.artifact.remote_file_path && this.artifact.local_path ) {
|
|
108
|
-
await this.downloadFile(
|
|
109
|
-
} else if ( this.artifact.remote_file_path && !this.artifact.local_path && !this.artifact.local_file_path ) {
|
|
110
|
-
await this.downloadFile( remoteRelativePath, path.join( this.path, localRelativePath ) )
|
|
149
|
+
await this.downloadFile( this.artifact.remote_file_path, toExplicitPath( `${this.artifact.local_path}/${this.artifact.remote_file_path.split( '/' ).pop()}` ) )
|
|
111
150
|
} else if ( this.artifact.remote_path && this.artifact.local_path ) {
|
|
112
|
-
await this.downloadNestedDirectory(
|
|
151
|
+
await this.downloadNestedDirectory( this.artifact.remote_path, toExplicitPath( this.artifact.local_path ) )
|
|
113
152
|
} else if ( this.artifact.remote_path && !this.artifact.local_path && !this.artifact.local_file_path ) {
|
|
114
|
-
await this.downloadDirectory(
|
|
153
|
+
await this.downloadDirectory( this.artifact.remote_path, '.' )
|
|
115
154
|
} else if ( !this.artifact.remote_path && !this.artifact.remote_file_path && this.artifact.local_path ) {
|
|
116
|
-
await this.downloadDirectory(
|
|
155
|
+
await this.downloadDirectory( toExplicitPath( this.artifact.local_path ) )
|
|
117
156
|
} else {
|
|
118
157
|
throw new Error( 'No valid paths provided' )
|
|
119
158
|
}
|
|
@@ -121,6 +160,13 @@ class GenericArtifactPuller extends ArtifactPuller {
|
|
|
121
160
|
log( `Successfully pulled artifact: ${this.artifact.name}` )
|
|
122
161
|
|
|
123
162
|
return true
|
|
163
|
+
|
|
164
|
+
// if ( !this.artifact.remote_path && !this.artifact.remote_file_path && this.local_file_path ) {
|
|
165
|
+
// throw new Error( '' )
|
|
166
|
+
// }
|
|
167
|
+
// if ( this.artifact.remote_path && this.artifact.local_file_path ) {
|
|
168
|
+
// throw new Error( '' )
|
|
169
|
+
// }
|
|
124
170
|
} catch ( error ) {
|
|
125
171
|
err( `Error pulling artifact: ${this.artifact.name} ${error.message}` )
|
|
126
172
|
}
|
|
@@ -1,20 +1,9 @@
|
|
|
1
|
-
import path from 'node:path'
|
|
2
|
-
import fs from 'node:fs'
|
|
3
|
-
|
|
4
1
|
import { shellCmd, log, err, debug } from '../Utils.mjs'
|
|
5
2
|
|
|
6
|
-
import {
|
|
7
|
-
parseGenericArtifactFileToRemoteRelativePath,
|
|
8
|
-
parseGenericArtifactSchemaToRemoteRelativePath,
|
|
9
|
-
listGenericArtifactFiles,
|
|
10
|
-
shouldUseIgnoreConfiguration,
|
|
11
|
-
findIgnoreFilePath,
|
|
12
|
-
copyFilesWithIgnoreConfiguration
|
|
13
|
-
} from './Utils.mjs'
|
|
3
|
+
import { convertGenericArtifactNameToPath } from './Utils.mjs'
|
|
14
4
|
|
|
15
5
|
class ArtifactPusher {
|
|
16
|
-
constructor(
|
|
17
|
-
this.path = path
|
|
6
|
+
constructor( artifact ) {
|
|
18
7
|
this.artifact = artifact
|
|
19
8
|
}
|
|
20
9
|
|
|
@@ -24,6 +13,25 @@ class ArtifactPusher {
|
|
|
24
13
|
}
|
|
25
14
|
|
|
26
15
|
class GenericArtifactPusher extends ArtifactPusher {
|
|
16
|
+
|
|
17
|
+
async listFiles( ) {
|
|
18
|
+
const command = `gcloud artifacts files list \
|
|
19
|
+
--project=${this.artifact.gcp_project_id} \
|
|
20
|
+
--location=${this.artifact.gcp_location} \
|
|
21
|
+
--repository=${this.artifact.gcp_artifact_registry_repository_name} \
|
|
22
|
+
--package=${this.artifact.gcp_artifact_registry_package_name} \
|
|
23
|
+
--version ${this.artifact.gcp_artifact_registry_package_version} \
|
|
24
|
+
--format="json"`
|
|
25
|
+
|
|
26
|
+
debug( { command }, '<==List Files Command' )
|
|
27
|
+
|
|
28
|
+
const result = await shellCmd( command )
|
|
29
|
+
|
|
30
|
+
// Parse the JSON response
|
|
31
|
+
const files = JSON.parse( result )
|
|
32
|
+
|
|
33
|
+
return files
|
|
34
|
+
}
|
|
27
35
|
|
|
28
36
|
async push() {
|
|
29
37
|
log( `Pushing artifact: ${this.artifact.name}` )
|
|
@@ -33,74 +41,41 @@ class GenericArtifactPusher extends ArtifactPusher {
|
|
|
33
41
|
return false
|
|
34
42
|
}
|
|
35
43
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return false
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
try {
|
|
44
|
+
try {
|
|
45
|
+
|
|
42
46
|
let command = ''
|
|
43
47
|
|
|
44
48
|
command = `gcloud artifacts generic upload \
|
|
45
|
-
--project=${this.artifact.
|
|
46
|
-
--location=${this.artifact.
|
|
47
|
-
--repository=${this.artifact.
|
|
48
|
-
--package=${this.artifact.
|
|
49
|
-
--version=${this.artifact.
|
|
50
|
-
|
|
51
|
-
let pathToDelete
|
|
49
|
+
--project=${this.artifact.gcp_project_id} \
|
|
50
|
+
--location=${this.artifact.gcp_location} \
|
|
51
|
+
--repository=${this.artifact.gcp_artifact_registry_repository_name} \
|
|
52
|
+
--package=${this.artifact.gcp_artifact_registry_package_name} \
|
|
53
|
+
--version=${this.artifact.gcp_artifact_registry_package_version}`
|
|
52
54
|
|
|
53
55
|
if ( this.artifact.local_path ) {
|
|
54
|
-
|
|
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
|
-
|
|
70
|
-
command += ` --source-directory=${localPath}`
|
|
56
|
+
command += ` --source-directory=${this.artifact.local_path}`
|
|
71
57
|
command += ' --skip-existing'
|
|
72
58
|
} else if ( this.artifact.local_file_path ) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
for ( const file of files ) {
|
|
79
|
-
const existingRemoteFilePath = parseGenericArtifactFileToRemoteRelativePath( file )
|
|
80
|
-
if ( existingRemoteFilePath === newRemoteFilePath ) {
|
|
81
|
-
err( `File already exists in the artifact registry: ${existingRemoteFilePath}` )
|
|
82
|
-
return false
|
|
83
|
-
}
|
|
59
|
+
const files = await this.listFiles( this.artifact.remote_path )
|
|
60
|
+
if ( files.some( file => convertGenericArtifactNameToPath( file.name ) === `${this.artifact.remote_path}/${this.artifact.remote_file_path.split( '/' ).pop()}` ) ) {
|
|
61
|
+
err( `File already exists in the artifact registry: ${this.artifact.local_file_path}` )
|
|
62
|
+
return false
|
|
84
63
|
}
|
|
85
|
-
|
|
86
|
-
command += ` --source=${localPath}`
|
|
64
|
+
command += ` --source=${this.artifact.local_file_path}`
|
|
87
65
|
}
|
|
88
66
|
|
|
89
|
-
|
|
67
|
+
if ( this.artifact.remote_path ) {
|
|
68
|
+
command += ` --destination-path=${this.artifact.remote_path}`
|
|
69
|
+
}
|
|
90
70
|
|
|
91
71
|
debug( { command }, '<==Upload Command' )
|
|
92
72
|
|
|
93
73
|
await shellCmd( command )
|
|
94
|
-
|
|
95
|
-
if ( pathToDelete ) {
|
|
96
|
-
await fs.promises.rm( pathToDelete, { recursive : true } )
|
|
97
|
-
}
|
|
98
|
-
|
|
74
|
+
|
|
99
75
|
log( `Successfully pushed artifact: ${this.artifact.name}` )
|
|
100
76
|
|
|
101
77
|
return true
|
|
102
78
|
} catch ( error ) {
|
|
103
|
-
debug( { error }, '<==Error Pushing Artifact' )
|
|
104
79
|
err( `Error pushing artifact: ${this.artifact.name} ${error.message}` )
|
|
105
80
|
return false
|
|
106
81
|
}
|
|
@@ -1,42 +1,37 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { assign, coerce, object, string, array, optional, enums, union, refine } from 'superstruct'
|
|
1
|
+
import { assign, object, string, array, optional, enums, union, refine } from 'superstruct'
|
|
4
2
|
|
|
5
3
|
const ArtifactTypeEnum = {
|
|
6
4
|
GENERIC : 'generic',
|
|
7
5
|
}
|
|
8
6
|
|
|
9
|
-
//
|
|
10
|
-
|
|
7
|
+
// Superstruct schema for individual artifact configuration
|
|
11
8
|
const PartialArtifactSchema = object( {
|
|
12
9
|
name : string(),
|
|
13
10
|
type : optional( enums( Object.values( ArtifactTypeEnum ) ) ),
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
gcp_project_id : optional( string() ),
|
|
12
|
+
gcp_location : optional( string() ),
|
|
13
|
+
gcp_artifact_registry_repository_name : optional( string() ),
|
|
14
|
+
gcp_artifact_registry_package_name : optional( string() ),
|
|
15
|
+
gcp_artifact_registry_package_version : optional( string() )
|
|
19
16
|
} )
|
|
20
17
|
|
|
21
18
|
const ArtifactSchema = assign( PartialArtifactSchema, object( {
|
|
22
19
|
name : string(),
|
|
23
20
|
type : enums( Object.values( ArtifactTypeEnum ) ),
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
gcp_project_id : string(),
|
|
22
|
+
gcp_location : string(),
|
|
23
|
+
gcp_artifact_registry_repository_name : string(),
|
|
24
|
+
gcp_artifact_registry_package_name : string(),
|
|
25
|
+
gcp_artifact_registry_package_version : string()
|
|
29
26
|
} ) )
|
|
30
27
|
|
|
31
|
-
const normalizedPath = () => coerce( string(), string(), value => path.normalize( value ) )
|
|
32
|
-
|
|
33
28
|
const PartialGenericArtifactSchema = refine(
|
|
34
29
|
assign( PartialArtifactSchema, object( {
|
|
35
|
-
type :
|
|
36
|
-
local_file_path : optional(
|
|
37
|
-
local_path : optional(
|
|
38
|
-
remote_file_path : optional(
|
|
39
|
-
remote_path : optional(
|
|
30
|
+
type : enums( [ ArtifactTypeEnum.GENERIC ] ),
|
|
31
|
+
local_file_path : optional( string() ),
|
|
32
|
+
local_path : optional( string() ),
|
|
33
|
+
remote_file_path : optional( string() ),
|
|
34
|
+
remote_path : optional( string() )
|
|
40
35
|
} ) ),
|
|
41
36
|
'ConflictingPaths',
|
|
42
37
|
( value ) => {
|
|
@@ -52,11 +47,10 @@ const PartialGenericArtifactSchema = refine(
|
|
|
52
47
|
|
|
53
48
|
const GenericArtifactSchema = refine(
|
|
54
49
|
assign( ArtifactSchema, object( {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
local_path : optional( normalizedPath() ),
|
|
50
|
+
local_file_path : optional( string() ),
|
|
51
|
+
local_path : optional( string() ),
|
|
58
52
|
remote_file_path : optional( string() ),
|
|
59
|
-
remote_path : optional(
|
|
53
|
+
remote_path : optional( string() )
|
|
60
54
|
} ) ),
|
|
61
55
|
'ConflictingPaths',
|
|
62
56
|
( value ) => {
|
|
@@ -72,12 +66,11 @@ const GenericArtifactSchema = refine(
|
|
|
72
66
|
|
|
73
67
|
// Superstruct schema for defaults configuration
|
|
74
68
|
const DefaultsSchema = object( {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
version : optional( string() )
|
|
69
|
+
gcp_project_id : optional( string() ),
|
|
70
|
+
gcp_location : optional( string() ),
|
|
71
|
+
gcp_artifact_registry_repository_name : optional( string() ),
|
|
72
|
+
gcp_artifact_registry_package_name : optional( string() ),
|
|
73
|
+
gcp_artifact_registry_package_version : optional( string() )
|
|
81
74
|
} )
|
|
82
75
|
|
|
83
76
|
// Superstruct schema for the complete artifacts configuration with defaults
|
package/src/artifacts/Utils.mjs
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
|
-
import path from 'node:path'
|
|
3
2
|
import crypto from 'crypto'
|
|
4
3
|
|
|
5
4
|
import { validate } from 'superstruct'
|
|
6
5
|
|
|
7
|
-
import { parseYamlFile,
|
|
6
|
+
import { parseYamlFile, warning } from '../Utils.mjs'
|
|
8
7
|
|
|
9
8
|
import { ArtifactTypeEnum, ArtifactsSchema, GenericArtifactSchema } from './Structs.mjs'
|
|
10
9
|
|
|
11
|
-
const getArtifactsSchema = async ( artifactsFilePath = './artifacts.
|
|
10
|
+
const getArtifactsSchema = async ( artifactsFilePath = './artifacts.yml' ) => {
|
|
12
11
|
const artifactsConfig = await parseYamlFile( artifactsFilePath )
|
|
13
12
|
const [ error, result ] = validate( artifactsConfig, ArtifactsSchema )
|
|
14
13
|
if ( error ) {
|
|
15
|
-
err( `Invalid artifacts.yml file: ${error.message}` )
|
|
16
|
-
debug( { err : error }, '<==Invalid artifacts.yml file' )
|
|
17
14
|
throw new Error( `Invalid artifacts.yml file: ${error.message}` )
|
|
18
15
|
}
|
|
19
16
|
return result
|
|
@@ -25,7 +22,7 @@ const flattenArtifactsSchema = ( artifactsSchema ) => {
|
|
|
25
22
|
...artifactsSchema.defaults,
|
|
26
23
|
...artifact
|
|
27
24
|
}
|
|
28
|
-
|
|
25
|
+
|
|
29
26
|
let schema
|
|
30
27
|
switch ( mergedArtifact.type ) {
|
|
31
28
|
case ArtifactTypeEnum.GENERIC:
|
|
@@ -52,230 +49,44 @@ const flattenArtifactsSchema = ( artifactsSchema ) => {
|
|
|
52
49
|
}, [] )
|
|
53
50
|
}
|
|
54
51
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// {
|
|
61
|
-
// "type": "SHA256",
|
|
62
|
-
// "value": "wrBRqJOUjm-zy6B7YzgKlK0fv9Ad0EEjydBnsmc9Ruw="
|
|
63
|
-
// },
|
|
64
|
-
// {
|
|
65
|
-
// "type": "MD5",
|
|
66
|
-
// "value": "Ebp3fVphzrITJ461Mg7Kmg=="
|
|
67
|
-
// }
|
|
68
|
-
// ],
|
|
69
|
-
// "name": "projects/leverege-registry/locations/us-central1/repositories/test-generic-registry/files/sweet-poems-1-1764697925426:v1:test1.txt%2Ftest1.txt",
|
|
70
|
-
// "owner": "projects/leverege-registry/locations/us-central1/repositories/test-generic-registry/packages/sweet-poems-1-1764697925426/versions/v1",
|
|
71
|
-
// "sizeBytes": "40",
|
|
72
|
-
// "updateTime": "2025-12-02T17:52:08.694541Z"
|
|
73
|
-
// }
|
|
74
|
-
// ]
|
|
75
|
-
// Obtained with:gcloud artifacts files list --project=leverege-registry --location=us-central1 --repository=test-generic-registry --package=sweet-poems-1-1764697925426 --version v1 --format="json"
|
|
76
|
-
|
|
77
|
-
const parseGenericArtifactFileToRemoteRelativePath = ( genericArtifactFile ) => {
|
|
78
|
-
// "projects/leverege-registry/locations/us-central1/repositories/test-generic-registry/files/sweet-poems-1-1764697925426:v1:test1.txt%2Ftest1.txt"
|
|
79
|
-
const artifactFileName = genericArtifactFile.name.split( '/' ).pop()
|
|
80
|
-
// sweet-poems-1-1764697925426:v1:test1.txt%2Ftest1.txt
|
|
81
|
-
const remoteRelativePath = artifactFileName.split( ':' ).pop().replaceAll( '%2F', '/' )
|
|
82
|
-
// test1.txt
|
|
83
|
-
return path.normalize( remoteRelativePath )
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const parseGenericArtifactSchemaToRemoteRelativePath = ( genericArtifactSchema ) => {
|
|
87
|
-
if ( genericArtifactSchema.remote_file_path ) {
|
|
88
|
-
return path.normalize( genericArtifactSchema.remote_file_path )
|
|
89
|
-
}
|
|
90
|
-
if ( genericArtifactSchema.remote_path && genericArtifactSchema.local_file_path ) {
|
|
91
|
-
return path.join( genericArtifactSchema.remote_path, path.basename( genericArtifactSchema.local_file_path ) )
|
|
92
|
-
}
|
|
93
|
-
if ( !genericArtifactSchema.remote_file_path && !genericArtifactSchema.remote_path && genericArtifactSchema.local_file_path ) {
|
|
94
|
-
return path.basename( genericArtifactSchema.local_file_path )
|
|
52
|
+
const toImplicitPath = ( path ) => {
|
|
53
|
+
let implicitPath = path
|
|
54
|
+
// Remove leading './' if present
|
|
55
|
+
if ( implicitPath.startsWith( './' ) ) {
|
|
56
|
+
implicitPath = implicitPath.slice( 2 )
|
|
95
57
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if ( genericArtifactSchema.remote_path && genericArtifactSchema.local_path ) {
|
|
100
|
-
return path.normalize( genericArtifactSchema.remote_path )
|
|
101
|
-
}
|
|
102
|
-
return path.normalize( '.' )
|
|
58
|
+
// Remove trailing slash
|
|
59
|
+
implicitPath = implicitPath.replace( /\/+$/, '' )
|
|
60
|
+
return implicitPath
|
|
103
61
|
}
|
|
104
62
|
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
63
|
+
const toExplicitPath = ( path ) => {
|
|
64
|
+
let explicitPath = path
|
|
65
|
+
// Keep absolute paths and paths already starting with '.' as-is
|
|
66
|
+
if ( !path.startsWith( '/' ) && !path.startsWith( '.' ) ) {
|
|
67
|
+
// Add './' to relative paths
|
|
68
|
+
explicitPath = `./${explicitPath}`
|
|
111
69
|
}
|
|
112
|
-
|
|
113
|
-
return path.normalize( genericArtifactSchema.local_path )
|
|
114
|
-
}
|
|
115
|
-
if ( !genericArtifactSchema.local_file_path && !genericArtifactSchema.local_path && genericArtifactSchema.remote_file_path ) {
|
|
116
|
-
return path.basename( genericArtifactSchema.remote_file_path )
|
|
117
|
-
}
|
|
118
|
-
return path.normalize( '.' )
|
|
70
|
+
return explicitPath
|
|
119
71
|
}
|
|
120
72
|
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
'projecs',
|
|
124
|
-
genericArtifactSchema.project,
|
|
125
|
-
'locations',
|
|
126
|
-
genericArtifactSchema.location,
|
|
127
|
-
'repositories',
|
|
128
|
-
genericArtifactSchema.repository,
|
|
129
|
-
'files',
|
|
130
|
-
]
|
|
131
|
-
return elements.join( '/' )
|
|
73
|
+
const deduplicatePath = ( path ) => {
|
|
74
|
+
return path.replaceAll( '//', '/' )
|
|
132
75
|
}
|
|
133
76
|
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const remoteRelativePath = parseGenericArtifactSchemaToRemoteRelativePath( genericArtifactSchema )
|
|
138
|
-
const encodedRemoteRelativePath = encodeURIComponent( remoteRelativePath )
|
|
139
|
-
|
|
140
|
-
const elements = [
|
|
141
|
-
genericArtifactSchema.package,
|
|
142
|
-
genericArtifactSchema.version,
|
|
143
|
-
encodedRemoteRelativePath,
|
|
144
|
-
]
|
|
145
|
-
return elements.join( ':' )
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const parseGenericArtifactSchemaToArtifactName = ( genericArtifactSchema ) => {
|
|
149
|
-
const base = parseGenericArtifactSchemaToArtifactNameBase( genericArtifactSchema )
|
|
150
|
-
const file = parseGenericArtifactSchemaToArtifactNameFile( genericArtifactSchema )
|
|
151
|
-
|
|
152
|
-
const elements = [
|
|
153
|
-
base,
|
|
154
|
-
file,
|
|
155
|
-
]
|
|
156
|
-
|
|
157
|
-
return elements.join( ':' )
|
|
77
|
+
const convertGenericArtifactNameToPath = ( genericArtifactName ) => {
|
|
78
|
+
return genericArtifactName.split( ':' ).pop().replaceAll( '%2F', '/' ).replaceAll( '%3A', ':' )
|
|
158
79
|
}
|
|
159
80
|
|
|
160
|
-
const calculateHash =
|
|
81
|
+
const calculateHash = ( path ) => {
|
|
161
82
|
try {
|
|
162
83
|
const hash = crypto.createHash( 'sha256' )
|
|
163
|
-
hash.update(
|
|
164
|
-
return
|
|
84
|
+
hash.update( fs.readFileSync( path ) )
|
|
85
|
+
return hash.digest( 'base64url' )
|
|
165
86
|
} catch ( error ) {
|
|
87
|
+
console.error( error )
|
|
166
88
|
throw new Error( `Failed to calculate hash for artifact ${path}: ${error.message}` )
|
|
167
89
|
}
|
|
168
90
|
}
|
|
169
91
|
|
|
170
|
-
|
|
171
|
-
const command = `gcloud artifacts files list \
|
|
172
|
-
--project=${artifact.project} \
|
|
173
|
-
--location=${artifact.location} \
|
|
174
|
-
--repository=${artifact.repository} \
|
|
175
|
-
--package=${artifact.package} \
|
|
176
|
-
--version ${artifact.version} \
|
|
177
|
-
--format="json"`
|
|
178
|
-
|
|
179
|
-
debug( { command }, '<==List Files Command' )
|
|
180
|
-
|
|
181
|
-
const result = await shellCmd( command )
|
|
182
|
-
|
|
183
|
-
// Parse the JSON response
|
|
184
|
-
const files = JSON.parse( result )
|
|
185
|
-
return files
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const getMissingRemoteFiles = async ( genericArtifactFiles, genericArtifactSchemaPath, genericArtifactSchema ) => {
|
|
189
|
-
|
|
190
|
-
const expectedRemoteRelativePath = parseGenericArtifactSchemaToRemoteRelativePath( genericArtifactSchema )
|
|
191
|
-
const localRelativePath = parseGenericArtifactSchemaToLocalRelativePath( genericArtifactSchema )
|
|
192
|
-
const expectedLocalRelativePath = path.join( genericArtifactSchemaPath, localRelativePath )
|
|
193
|
-
|
|
194
|
-
const actualFiles = new Set()
|
|
195
|
-
const expectedFiles = new Set()
|
|
196
|
-
|
|
197
|
-
for ( const file of genericArtifactFiles ) {
|
|
198
|
-
const actualRemoteFilePath = parseGenericArtifactFileToRemoteRelativePath( file )
|
|
199
|
-
const actualRemoteFileHash = file.hashes.find( hash => hash.type === 'SHA256' ).value
|
|
200
|
-
actualFiles.add( { remoteFilePath : actualRemoteFilePath, remoteFileHash : actualRemoteFileHash } )
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if ( ( await fs.promises.stat( expectedLocalRelativePath ) ).isDirectory() ) {
|
|
204
|
-
const localFiles = await fs.promises.readdir( expectedLocalRelativePath, { withFileTypes : true, recursive : true } )
|
|
205
|
-
for ( const localFile of localFiles ) {
|
|
206
|
-
if ( localFile.isDirectory() ) {
|
|
207
|
-
continue
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const expectedRemoteFilePath = path.join( expectedRemoteRelativePath, localFile.path.replace( expectedLocalRelativePath, '' ), localFile.name )
|
|
211
|
-
const expectedLocalFilePath = path.join( localFile.path, localFile.name )
|
|
212
|
-
expectedFiles.add( { remoteFilePath : expectedRemoteFilePath, remoteFileHash : await calculateHash( expectedLocalFilePath ) } )
|
|
213
|
-
}
|
|
214
|
-
} else {
|
|
215
|
-
const expectedRemoteFilePath = expectedRemoteRelativePath
|
|
216
|
-
const expectedLocalFilePath = expectedLocalRelativePath
|
|
217
|
-
expectedFiles.add( { remoteFilePath : expectedRemoteFilePath, remoteFileHash : await calculateHash( expectedLocalFilePath ) } )
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const actualArray = Array.from( actualFiles )
|
|
221
|
-
const expectedArray = Array.from( expectedFiles )
|
|
222
|
-
|
|
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 )
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
export {
|
|
267
|
-
calculateHash,
|
|
268
|
-
getArtifactsSchema,
|
|
269
|
-
flattenArtifactsSchema,
|
|
270
|
-
parseGenericArtifactFileToRemoteRelativePath,
|
|
271
|
-
parseGenericArtifactSchemaToRemoteRelativePath,
|
|
272
|
-
parseGenericArtifactSchemaToLocalRelativePath,
|
|
273
|
-
parseGenericArtifactSchemaToArtifactNameBase,
|
|
274
|
-
parseGenericArtifactSchemaToArtifactNameFile,
|
|
275
|
-
parseGenericArtifactSchemaToArtifactName,
|
|
276
|
-
getMissingRemoteFiles,
|
|
277
|
-
listGenericArtifactFiles,
|
|
278
|
-
shouldUseIgnoreConfiguration,
|
|
279
|
-
findIgnoreFilePath,
|
|
280
|
-
copyFilesWithIgnoreConfiguration
|
|
281
|
-
}
|
|
92
|
+
export { calculateHash, getArtifactsSchema, flattenArtifactsSchema, toImplicitPath, toExplicitPath, deduplicatePath, convertGenericArtifactNameToPath }
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
|
|
5
3
|
import { program } from 'commander'
|
|
6
4
|
|
|
7
5
|
import { warning, log } from '../Utils.mjs'
|
|
@@ -13,36 +11,33 @@ import { GenericArtifactPuller } from './ArtifactPuller.mjs'
|
|
|
13
11
|
program
|
|
14
12
|
.name( 'pull-artifact' )
|
|
15
13
|
.description( 'Pull artifacts from GCP Artifact Registry' )
|
|
16
|
-
.
|
|
17
|
-
.option( '-
|
|
14
|
+
.requiredOption( '-f, --file-path <path>', 'Path to artifacts.yaml file' )
|
|
15
|
+
.option( '-n, --package-name <name>', 'Filter by artifact package name' )
|
|
16
|
+
.option( '-v, --package-version <version>', 'Filter by artifact package version' )
|
|
18
17
|
.parse()
|
|
19
18
|
|
|
20
19
|
const options = program.opts()
|
|
21
20
|
|
|
22
21
|
async function main() {
|
|
23
|
-
const
|
|
22
|
+
const artifactsYmlPath = options.filePath
|
|
23
|
+
const artifactPackageName = options.packageName || null
|
|
24
|
+
const artifactPackageVersion = options.packageVersion || null
|
|
25
|
+
|
|
26
|
+
const artifactsSchema = await getArtifactsSchema( artifactsYmlPath )
|
|
24
27
|
const flattenedArtifactsSchema = flattenArtifactsSchema( artifactsSchema )
|
|
25
|
-
|
|
26
|
-
let filteredArtifactsSchema
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
28
|
+
|
|
29
|
+
let filteredArtifactsSchema
|
|
30
|
+
if ( artifactPackageName && artifactPackageVersion ) {
|
|
31
|
+
filteredArtifactsSchema = flattenedArtifactsSchema.filter(
|
|
32
|
+
artifact => artifact.gcp_artifact_registry_package_name === artifactPackageName &&
|
|
33
|
+
artifact.gcp_artifact_registry_package_version === artifactPackageVersion )
|
|
34
|
+
} else if ( artifactPackageName ) {
|
|
38
35
|
filteredArtifactsSchema = flattenedArtifactsSchema.filter(
|
|
39
|
-
artifact => artifact.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
artifact.package === packageName &&
|
|
43
|
-
artifact.version === version )
|
|
36
|
+
artifact => artifact.gcp_artifact_registry_package_name === artifactPackageName )
|
|
37
|
+
} else {
|
|
38
|
+
filteredArtifactsSchema = flattenedArtifactsSchema
|
|
44
39
|
}
|
|
45
|
-
|
|
40
|
+
|
|
46
41
|
let counter = 0
|
|
47
42
|
|
|
48
43
|
const pullResults = await Promise.all(
|
|
@@ -50,7 +45,7 @@ async function main() {
|
|
|
50
45
|
let puller
|
|
51
46
|
switch ( artifactSchema.type ) {
|
|
52
47
|
case ArtifactTypeEnum.GENERIC:
|
|
53
|
-
puller = new GenericArtifactPuller(
|
|
48
|
+
puller = new GenericArtifactPuller( artifactSchema )
|
|
54
49
|
break
|
|
55
50
|
default:
|
|
56
51
|
warning( `Unsupported artifact type "${artifactSchema.type}"` )
|
|
@@ -74,8 +69,6 @@ async function main() {
|
|
|
74
69
|
counter = pullResults.filter( Boolean ).length
|
|
75
70
|
|
|
76
71
|
log( `Successfully pulled ${counter}/${filteredArtifactsSchema.length} artifacts` )
|
|
77
|
-
|
|
78
|
-
return counter === filteredArtifactsSchema.length
|
|
79
72
|
}
|
|
80
73
|
|
|
81
74
|
main()
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
|
|
5
3
|
import { program } from 'commander'
|
|
6
4
|
|
|
7
5
|
import { warning, log } from '../Utils.mjs'
|
|
@@ -13,33 +11,31 @@ import { GenericArtifactPusher } from './ArtifactPusher.mjs'
|
|
|
13
11
|
program
|
|
14
12
|
.name( 'push-artifact' )
|
|
15
13
|
.description( 'Push artifacts to GCP Artifact Registry' )
|
|
16
|
-
.
|
|
17
|
-
.option( '-
|
|
14
|
+
.requiredOption( '-f, --file-path <path>', 'Path to artifacts.yaml file' )
|
|
15
|
+
.option( '-n, --package-name <name>', 'Filter by artifact package name' )
|
|
16
|
+
.option( '-v, --package-version <version>', 'Filter by artifact package version' )
|
|
18
17
|
.parse()
|
|
19
18
|
|
|
20
19
|
const options = program.opts()
|
|
21
20
|
|
|
22
21
|
async function main() {
|
|
23
|
-
const
|
|
22
|
+
const artifactsYmlPath = options.filePath
|
|
23
|
+
const artifactPackageName = options.packageName || null
|
|
24
|
+
const artifactPackageVersion = options.packageVersion || null
|
|
25
|
+
|
|
26
|
+
const artifactsSchema = await getArtifactsSchema( artifactsYmlPath )
|
|
24
27
|
const flattenedArtifactsSchema = flattenArtifactsSchema( artifactsSchema )
|
|
25
|
-
|
|
26
|
-
let filteredArtifactsSchema
|
|
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
|
-
|
|
28
|
+
|
|
29
|
+
let filteredArtifactsSchema
|
|
30
|
+
if ( artifactPackageName && artifactPackageVersion ) {
|
|
37
31
|
filteredArtifactsSchema = flattenedArtifactsSchema.filter(
|
|
38
|
-
artifact => artifact.
|
|
39
|
-
artifact.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
artifact.
|
|
32
|
+
artifact => artifact.gcp_artifact_registry_package_name === artifactPackageName &&
|
|
33
|
+
artifact.gcp_artifact_registry_package_version === artifactPackageVersion )
|
|
34
|
+
} else if ( artifactPackageName ) {
|
|
35
|
+
filteredArtifactsSchema = flattenedArtifactsSchema.filter(
|
|
36
|
+
artifact => artifact.gcp_artifact_registry_package_name === artifactPackageName )
|
|
37
|
+
} else {
|
|
38
|
+
filteredArtifactsSchema = flattenedArtifactsSchema
|
|
43
39
|
}
|
|
44
40
|
|
|
45
41
|
let counter = 0
|
|
@@ -49,7 +45,7 @@ async function main() {
|
|
|
49
45
|
let pusher
|
|
50
46
|
switch ( artifactSchema.type ) {
|
|
51
47
|
case ArtifactTypeEnum.GENERIC:
|
|
52
|
-
pusher = new GenericArtifactPusher(
|
|
48
|
+
pusher = new GenericArtifactPusher( artifactSchema )
|
|
53
49
|
break
|
|
54
50
|
default:
|
|
55
51
|
warning( `Unsupported artifact type "${artifactSchema.type}"` )
|
|
@@ -73,8 +69,6 @@ async function main() {
|
|
|
73
69
|
counter = pushResults.filter( Boolean ).length
|
|
74
70
|
|
|
75
71
|
log( `Successfully pushed ${counter}/${filteredArtifactsSchema.length} artifacts` )
|
|
76
|
-
|
|
77
|
-
return counter === filteredArtifactsSchema.length
|
|
78
72
|
}
|
|
79
73
|
|
|
80
74
|
main()
|
package/src/bash-funcs
CHANGED
|
@@ -305,6 +305,11 @@ PLUGIN_NOEXEC
|
|
|
305
305
|
return 0
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
+
# simple check to allow us to avoid double settings the k8s context
|
|
309
|
+
function ifNotFromHelmup() {
|
|
310
|
+
[ "$_FROM_HELMUP_" != "true" ]
|
|
311
|
+
}
|
|
312
|
+
|
|
308
313
|
# simple enforcement of necessary tools that need to be installed
|
|
309
314
|
function commandChecker() {
|
|
310
315
|
for cmd in "$@"; do
|
package/src/helmup.sh
CHANGED
package/src/k8scale.sh
CHANGED
package/.vscode/settings.json
DELETED