@leverege/build-tools 2.94.0-quinn.8 → 2.94.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 +12 -7
- package/src/Docker.mjs +9 -3
- package/src/Utils.mjs +1 -1
- package/src/artifacts/ArtifactPuller.mjs +56 -102
- package/src/artifacts/ArtifactPusher.mjs +63 -38
- package/src/artifacts/Structs.mjs +32 -25
- package/src/artifacts/Utils.mjs +216 -27
- package/src/artifacts/pull-artifact.mjs +27 -20
- package/src/artifacts/push-artifact.mjs +25 -19
- package/src/bash-funcs +10 -1
- package/src/change-up.sh +27 -0
- package/src/chart-compass.mjs +22 -9
- package/src/chart-compass.yaml +23 -18
- package/src/docker-to-registry.mjs +1 -1
- package/src/helm-charts/prom-operator/helmup.plugin +9 -9
- package/src/helmup.sh +3 -0
- package/src/k8scale.sh +6 -2
- package/src/yarn-build.mjs +60 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leverege/build-tools",
|
|
3
|
-
"version": "2.94.0
|
|
3
|
+
"version": "2.94.0",
|
|
4
4
|
"description": "A collection of build / support tools for Leverege developers",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -11,13 +11,16 @@
|
|
|
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
|
|
14
|
+
"test": "mocha -t 60000 --exit 'test/**/!(*e2e*).mjs'",
|
|
15
|
+
"test-all": "mocha -t 60000 --exit 'test/**/**.mjs'",
|
|
15
16
|
"prepack": "npm exec prepack"
|
|
16
17
|
},
|
|
17
18
|
"bin": {
|
|
18
19
|
"build-and-push": "src/build-and-push.sh",
|
|
19
20
|
"build-cnpg-image": "src/build-cnpg-image.mjs",
|
|
20
21
|
"build-tools": "src/build-tools.mjs",
|
|
22
|
+
"change-up": "src/change-up.sh",
|
|
23
|
+
"chart-compass": "src/chart-compass.mjs",
|
|
21
24
|
"chart-to-museum": "src/chart-to-museum.mjs",
|
|
22
25
|
"chart-to-registry": "src/chart-to-registry.mjs",
|
|
23
26
|
"circleate": "src/circleate.sh",
|
|
@@ -64,7 +67,8 @@
|
|
|
64
67
|
"snapshot-cnpg": "src/snapshot-cnpg.mjs",
|
|
65
68
|
"tag-release": "src/tag-release.mjs",
|
|
66
69
|
"unleash": "src/unleash.mjs",
|
|
67
|
-
"upload-model": "src/upload-model.sh"
|
|
70
|
+
"upload-model": "src/upload-model.sh",
|
|
71
|
+
"yarn-build": "src/yarn-build.mjs"
|
|
68
72
|
},
|
|
69
73
|
"author": "Leverege Devs",
|
|
70
74
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
@@ -76,11 +80,12 @@
|
|
|
76
80
|
"commander": "^14.0.2",
|
|
77
81
|
"deepmerge": "^4.3.1",
|
|
78
82
|
"enquirer": "^2.4.1",
|
|
79
|
-
"execa": "^9.6.
|
|
80
|
-
"
|
|
83
|
+
"execa": "^9.6.1",
|
|
84
|
+
"find-yarn-workspace-root": "^2.0.0",
|
|
85
|
+
"glob": "^13.0.0",
|
|
81
86
|
"handlebars": "^4.7.8",
|
|
82
87
|
"ignore": "^7.0.5",
|
|
83
|
-
"inquirer": "^
|
|
88
|
+
"inquirer": "^13.1.0",
|
|
84
89
|
"js-yaml": "^4.1.1",
|
|
85
90
|
"jsdoc": "^4.0.5",
|
|
86
91
|
"ms": "^2.1.3",
|
|
@@ -101,6 +106,6 @@
|
|
|
101
106
|
"@leverege/eslint-config-leverege": "^5.1.1",
|
|
102
107
|
"chai": "^6.2.1",
|
|
103
108
|
"mocha": "^11.7.5",
|
|
104
|
-
"npm": "^11.
|
|
109
|
+
"npm": "^11.7.0"
|
|
105
110
|
}
|
|
106
111
|
}
|
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:
|
|
21
|
-
|
|
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 || '22.21.1-alpine3.23' // TODO: 1/1/26 => '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 :
|
|
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,12 +1,14 @@
|
|
|
1
1
|
/* eslint-disable security/detect-non-literal-fs-filename */
|
|
2
2
|
import fs from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
3
4
|
|
|
4
5
|
import { shellCmd, log, debug, err } from '../Utils.mjs'
|
|
5
6
|
|
|
6
|
-
import {
|
|
7
|
+
import { parseGenericArtifactSchemaToRemoteRelativePath, parseGenericArtifactSchemaToLocalRelativePath, listGenericArtifactFiles, parseGenericArtifactFileToRemoteRelativePath, parseGenericArtifactSchemaToArtifactName } from './Utils.mjs'
|
|
7
8
|
|
|
8
9
|
class ArtifactPuller {
|
|
9
|
-
constructor( artifact ) {
|
|
10
|
+
constructor( path, artifact ) {
|
|
11
|
+
this.path = path
|
|
10
12
|
this.artifact = artifact
|
|
11
13
|
}
|
|
12
14
|
|
|
@@ -17,142 +19,101 @@ class ArtifactPuller {
|
|
|
17
19
|
|
|
18
20
|
class GenericArtifactPuller extends ArtifactPuller {
|
|
19
21
|
|
|
20
|
-
async
|
|
21
|
-
|
|
22
|
-
fs.mkdirSync( tmpFilePath.split( '/' ).slice( 0, -1 ).join( '/' ), { recursive : true } )
|
|
23
|
-
fs.mkdirSync( finalFilePath.split( '/' ).slice( 0, -1 ).join( '/' ), { recursive : true } )
|
|
22
|
+
async downloadFile( remoteRelativePath, localRelativePath ) {
|
|
23
|
+
debug( { remoteRelativePath, localRelativePath }, '<==Download File' )
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
if ( fs.existsSync( tmpFilePath ) ) {
|
|
27
|
-
fs.unlinkSync( tmpFilePath )
|
|
28
|
-
}
|
|
29
|
-
if ( fs.existsSync( finalFilePath ) ) {
|
|
30
|
-
fs.unlinkSync( finalFilePath )
|
|
31
|
-
}
|
|
32
|
-
}
|
|
25
|
+
const isBasenameDifferent = path.basename( localRelativePath ) !== path.basename( remoteRelativePath )
|
|
33
26
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
let tmpLocalRelativePath
|
|
28
|
+
if ( isBasenameDifferent ) {
|
|
29
|
+
const artifactName = parseGenericArtifactSchemaToArtifactName( this.artifact )
|
|
30
|
+
const encodedArtifactName = artifactName.replaceAll( ':', '_' ).replaceAll( '/', '_' ).replaceAll( '%2F', '_' )
|
|
31
|
+
tmpLocalRelativePath = path.join( '/', 'tmp', encodedArtifactName )
|
|
32
|
+
} else {
|
|
33
|
+
await fs.promises.mkdir( path.dirname( localRelativePath ), { recursive : true } )
|
|
38
34
|
}
|
|
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 )
|
|
53
35
|
|
|
54
36
|
try {
|
|
55
37
|
const command = `gcloud artifacts generic download \
|
|
56
|
-
--project=${this.artifact.
|
|
57
|
-
--location=${this.artifact.
|
|
58
|
-
--repository=${this.artifact.
|
|
59
|
-
--package=${this.artifact.
|
|
60
|
-
--version=${this.artifact.
|
|
61
|
-
--name="${
|
|
62
|
-
--destination=${
|
|
38
|
+
--project=${this.artifact.project} \
|
|
39
|
+
--location=${this.artifact.location} \
|
|
40
|
+
--repository=${this.artifact.repository} \
|
|
41
|
+
--package=${this.artifact.package} \
|
|
42
|
+
--version=${this.artifact.version} \
|
|
43
|
+
--name="${remoteRelativePath}" \
|
|
44
|
+
--destination=${tmpLocalRelativePath || path.dirname( localRelativePath )}`
|
|
63
45
|
|
|
64
46
|
debug( { command }, '<==Download File Command' )
|
|
65
47
|
|
|
66
48
|
await shellCmd( command )
|
|
67
49
|
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
50
|
+
if ( isBasenameDifferent ) {
|
|
51
|
+
await fs.promises.rename( tmpLocalRelativePath, localRelativePath )
|
|
82
52
|
}
|
|
83
53
|
|
|
84
54
|
return true
|
|
85
55
|
} catch ( error ) {
|
|
86
|
-
err( `Error downloading file: ${
|
|
56
|
+
err( `Error downloading file: ${localRelativePath} ${error.message}` )
|
|
87
57
|
return false
|
|
88
|
-
} finally {
|
|
89
|
-
await this.cleanupFileDownload( tmpFilePath, localFilePath )
|
|
90
58
|
}
|
|
91
59
|
}
|
|
92
60
|
|
|
93
|
-
async downloadDirectory(
|
|
61
|
+
async downloadDirectory( remoteRelativePath, localRelativePath ) {
|
|
62
|
+
|
|
63
|
+
await fs.promises.mkdir( localRelativePath, { recursive : true } )
|
|
64
|
+
|
|
94
65
|
const command = `gcloud artifacts generic download \
|
|
95
|
-
--project=${this.artifact.
|
|
96
|
-
--location=${this.artifact.
|
|
97
|
-
--repository=${this.artifact.
|
|
98
|
-
--package=${this.artifact.
|
|
99
|
-
--version=${this.artifact.
|
|
100
|
-
--destination=${
|
|
66
|
+
--project=${this.artifact.project} \
|
|
67
|
+
--location=${this.artifact.location} \
|
|
68
|
+
--repository=${this.artifact.repository} \
|
|
69
|
+
--package=${this.artifact.package} \
|
|
70
|
+
--version=${this.artifact.version} \
|
|
71
|
+
--destination=${localRelativePath}`
|
|
101
72
|
|
|
102
73
|
debug( { command }, '<==Download Directory Command' )
|
|
103
74
|
|
|
104
75
|
await shellCmd( command )
|
|
105
76
|
}
|
|
106
77
|
|
|
107
|
-
async downloadNestedDirectory(
|
|
108
|
-
debug( {
|
|
78
|
+
async downloadNestedDirectory( remoteRelativePath, localRelativePath ) {
|
|
79
|
+
debug( { remoteRelativePath, localRelativePath }, '<==Download Nested Directory' )
|
|
109
80
|
|
|
110
|
-
const files = await this.
|
|
81
|
+
const files = await listGenericArtifactFiles( this.artifact )
|
|
111
82
|
await Promise.all(
|
|
112
83
|
files
|
|
113
|
-
.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
84
|
+
.reduce( ( acc, file ) => {
|
|
85
|
+
const remoteFilePath = parseGenericArtifactFileToRemoteRelativePath( file )
|
|
86
|
+
if ( remoteFilePath.startsWith( remoteRelativePath ) ) {
|
|
87
|
+
const remoteFilePathWithoutRemotePath = remoteFilePath.replace( remoteRelativePath, '' )
|
|
88
|
+
const localFilePath = path.join( localRelativePath, remoteFilePathWithoutRemotePath )
|
|
89
|
+
acc.push( this.downloadFile( remoteFilePath, localFilePath ) )
|
|
90
|
+
}
|
|
91
|
+
return acc
|
|
92
|
+
}, [] )
|
|
119
93
|
)
|
|
120
94
|
}
|
|
121
95
|
|
|
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
|
-
|
|
140
96
|
async pull() {
|
|
141
97
|
log( `Pulling artifact: ${this.artifact.name}` )
|
|
142
98
|
|
|
143
99
|
debug( { artifact : this.artifact }, '<==Pulling artifact' )
|
|
144
100
|
|
|
145
101
|
try {
|
|
102
|
+
const remoteRelativePath = parseGenericArtifactSchemaToRemoteRelativePath( this.artifact )
|
|
103
|
+
const localRelativePath = parseGenericArtifactSchemaToLocalRelativePath( this.artifact )
|
|
104
|
+
|
|
146
105
|
if ( this.artifact.remote_file_path && this.artifact.local_file_path ) {
|
|
147
|
-
await this.downloadFile(
|
|
106
|
+
await this.downloadFile( remoteRelativePath, path.join( this.path, localRelativePath ) )
|
|
148
107
|
} else if ( this.artifact.remote_file_path && this.artifact.local_path ) {
|
|
149
|
-
await this.downloadFile(
|
|
108
|
+
await this.downloadFile( remoteRelativePath, path.join( this.path, localRelativePath ) )
|
|
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 ) )
|
|
150
111
|
} else if ( this.artifact.remote_path && this.artifact.local_path ) {
|
|
151
|
-
await this.downloadNestedDirectory(
|
|
112
|
+
await this.downloadNestedDirectory( remoteRelativePath, path.join( this.path, localRelativePath ) )
|
|
152
113
|
} else if ( this.artifact.remote_path && !this.artifact.local_path && !this.artifact.local_file_path ) {
|
|
153
|
-
await this.downloadDirectory( this.
|
|
114
|
+
await this.downloadDirectory( remoteRelativePath, path.join( this.path, localRelativePath ) )
|
|
154
115
|
} else if ( !this.artifact.remote_path && !this.artifact.remote_file_path && this.artifact.local_path ) {
|
|
155
|
-
await this.downloadDirectory(
|
|
116
|
+
await this.downloadDirectory( remoteRelativePath, path.join( this.path, localRelativePath ) )
|
|
156
117
|
} else {
|
|
157
118
|
throw new Error( 'No valid paths provided' )
|
|
158
119
|
}
|
|
@@ -160,13 +121,6 @@ class GenericArtifactPuller extends ArtifactPuller {
|
|
|
160
121
|
log( `Successfully pulled artifact: ${this.artifact.name}` )
|
|
161
122
|
|
|
162
123
|
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
|
-
// }
|
|
170
124
|
} catch ( error ) {
|
|
171
125
|
err( `Error pulling artifact: ${this.artifact.name} ${error.message}` )
|
|
172
126
|
}
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
|
|
1
4
|
import { shellCmd, log, err, debug } from '../Utils.mjs'
|
|
2
5
|
|
|
3
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
parseGenericArtifactFileToRemoteRelativePath,
|
|
8
|
+
parseGenericArtifactSchemaToRemoteRelativePath,
|
|
9
|
+
listGenericArtifactFiles,
|
|
10
|
+
shouldUseIgnoreConfiguration,
|
|
11
|
+
findIgnoreFilePath,
|
|
12
|
+
copyFilesWithIgnoreConfiguration
|
|
13
|
+
} from './Utils.mjs'
|
|
4
14
|
|
|
5
15
|
class ArtifactPusher {
|
|
6
|
-
constructor( artifact ) {
|
|
16
|
+
constructor( path, artifact ) {
|
|
17
|
+
this.path = path
|
|
7
18
|
this.artifact = artifact
|
|
8
19
|
}
|
|
9
20
|
|
|
@@ -13,25 +24,6 @@ class ArtifactPusher {
|
|
|
13
24
|
}
|
|
14
25
|
|
|
15
26
|
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
|
-
}
|
|
35
27
|
|
|
36
28
|
async push() {
|
|
37
29
|
log( `Pushing artifact: ${this.artifact.name}` )
|
|
@@ -41,41 +33,74 @@ class GenericArtifactPusher extends ArtifactPusher {
|
|
|
41
33
|
return false
|
|
42
34
|
}
|
|
43
35
|
|
|
44
|
-
|
|
45
|
-
|
|
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 {
|
|
46
42
|
let command = ''
|
|
47
43
|
|
|
48
44
|
command = `gcloud artifacts generic upload \
|
|
49
|
-
--project=${this.artifact.
|
|
50
|
-
--location=${this.artifact.
|
|
51
|
-
--repository=${this.artifact.
|
|
52
|
-
--package=${this.artifact.
|
|
53
|
-
--version=${this.artifact.
|
|
45
|
+
--project=${this.artifact.project} \
|
|
46
|
+
--location=${this.artifact.location} \
|
|
47
|
+
--repository=${this.artifact.repository} \
|
|
48
|
+
--package=${this.artifact.package} \
|
|
49
|
+
--version=${this.artifact.version}`
|
|
50
|
+
|
|
51
|
+
let pathToDelete
|
|
54
52
|
|
|
55
53
|
if ( this.artifact.local_path ) {
|
|
56
|
-
|
|
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
|
+
|
|
70
|
+
command += ` --source-directory=${localPath}`
|
|
57
71
|
command += ' --skip-existing'
|
|
58
72
|
} else if ( this.artifact.local_file_path ) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
73
|
+
|
|
74
|
+
const files = await listGenericArtifactFiles( this.artifact )
|
|
75
|
+
|
|
76
|
+
const newRemoteFilePath = parseGenericArtifactSchemaToRemoteRelativePath( this.artifact )
|
|
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
|
+
}
|
|
63
84
|
}
|
|
64
|
-
|
|
85
|
+
const localPath = path.join( this.path, this.artifact.local_file_path )
|
|
86
|
+
command += ` --source=${localPath}`
|
|
65
87
|
}
|
|
66
88
|
|
|
67
|
-
|
|
68
|
-
command += ` --destination-path=${this.artifact.remote_path}`
|
|
69
|
-
}
|
|
89
|
+
command += ` --destination-path=${this.artifact.remote_path || '""'}`
|
|
70
90
|
|
|
71
91
|
debug( { command }, '<==Upload Command' )
|
|
72
92
|
|
|
73
93
|
await shellCmd( command )
|
|
74
|
-
|
|
94
|
+
|
|
95
|
+
if ( pathToDelete ) {
|
|
96
|
+
await fs.promises.rm( pathToDelete, { recursive : true } )
|
|
97
|
+
}
|
|
98
|
+
|
|
75
99
|
log( `Successfully pushed artifact: ${this.artifact.name}` )
|
|
76
100
|
|
|
77
101
|
return true
|
|
78
102
|
} catch ( error ) {
|
|
103
|
+
debug( { error }, '<==Error Pushing Artifact' )
|
|
79
104
|
err( `Error pushing artifact: ${this.artifact.name} ${error.message}` )
|
|
80
105
|
return false
|
|
81
106
|
}
|
|
@@ -1,37 +1,42 @@
|
|
|
1
|
-
import
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
|
|
3
|
+
import { assign, coerce, object, string, array, optional, enums, union, refine } from 'superstruct'
|
|
2
4
|
|
|
3
5
|
const ArtifactTypeEnum = {
|
|
4
6
|
GENERIC : 'generic',
|
|
5
7
|
}
|
|
6
8
|
|
|
7
|
-
//
|
|
9
|
+
// Structs with the Partial prefix come from the need to validate the schema with defaults and without defaults merged together.
|
|
10
|
+
|
|
8
11
|
const PartialArtifactSchema = object( {
|
|
9
12
|
name : string(),
|
|
10
13
|
type : optional( enums( Object.values( ArtifactTypeEnum ) ) ),
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
project : optional( string() ),
|
|
15
|
+
location : optional( string() ),
|
|
16
|
+
repository : optional( string() ),
|
|
17
|
+
package : optional( string() ),
|
|
18
|
+
version : optional( string() )
|
|
16
19
|
} )
|
|
17
20
|
|
|
18
21
|
const ArtifactSchema = assign( PartialArtifactSchema, object( {
|
|
19
22
|
name : string(),
|
|
20
23
|
type : enums( Object.values( ArtifactTypeEnum ) ),
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
project : string(),
|
|
25
|
+
location : string(),
|
|
26
|
+
repository : string(),
|
|
27
|
+
package : string(),
|
|
28
|
+
version : string()
|
|
26
29
|
} ) )
|
|
27
30
|
|
|
31
|
+
const normalizedPath = () => coerce( string(), string(), value => path.normalize( value ) )
|
|
32
|
+
|
|
28
33
|
const PartialGenericArtifactSchema = refine(
|
|
29
34
|
assign( PartialArtifactSchema, object( {
|
|
30
|
-
type : enums( [ ArtifactTypeEnum.GENERIC ] ),
|
|
31
|
-
local_file_path : optional(
|
|
32
|
-
local_path : optional(
|
|
33
|
-
remote_file_path : optional(
|
|
34
|
-
remote_path : optional(
|
|
35
|
+
type : optional( enums( [ ArtifactTypeEnum.GENERIC ] ) ),
|
|
36
|
+
local_file_path : optional( normalizedPath() ),
|
|
37
|
+
local_path : optional( normalizedPath() ),
|
|
38
|
+
remote_file_path : optional( normalizedPath() ),
|
|
39
|
+
remote_path : optional( normalizedPath() )
|
|
35
40
|
} ) ),
|
|
36
41
|
'ConflictingPaths',
|
|
37
42
|
( value ) => {
|
|
@@ -47,10 +52,11 @@ const PartialGenericArtifactSchema = refine(
|
|
|
47
52
|
|
|
48
53
|
const GenericArtifactSchema = refine(
|
|
49
54
|
assign( ArtifactSchema, object( {
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
type : enums( [ ArtifactTypeEnum.GENERIC ] ),
|
|
56
|
+
local_file_path : optional( normalizedPath() ),
|
|
57
|
+
local_path : optional( normalizedPath() ),
|
|
52
58
|
remote_file_path : optional( string() ),
|
|
53
|
-
remote_path : optional(
|
|
59
|
+
remote_path : optional( normalizedPath() )
|
|
54
60
|
} ) ),
|
|
55
61
|
'ConflictingPaths',
|
|
56
62
|
( value ) => {
|
|
@@ -66,11 +72,12 @@ const GenericArtifactSchema = refine(
|
|
|
66
72
|
|
|
67
73
|
// Superstruct schema for defaults configuration
|
|
68
74
|
const DefaultsSchema = object( {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
75
|
+
type : optional( enums( Object.values( ArtifactTypeEnum ) ) ),
|
|
76
|
+
project : optional( string() ),
|
|
77
|
+
location : optional( string() ),
|
|
78
|
+
repository : optional( string() ),
|
|
79
|
+
package : optional( string() ),
|
|
80
|
+
version : optional( string() )
|
|
74
81
|
} )
|
|
75
82
|
|
|
76
83
|
// Superstruct schema for the complete artifacts configuration with defaults
|
package/src/artifacts/Utils.mjs
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
2
3
|
import crypto from 'crypto'
|
|
3
4
|
|
|
4
5
|
import { validate } from 'superstruct'
|
|
5
6
|
|
|
6
|
-
import { parseYamlFile, warning } from '../Utils.mjs'
|
|
7
|
+
import { parseYamlFile, err, warning, debug, shellCmd } from '../Utils.mjs'
|
|
7
8
|
|
|
8
9
|
import { ArtifactTypeEnum, ArtifactsSchema, GenericArtifactSchema } from './Structs.mjs'
|
|
9
10
|
|
|
10
|
-
const getArtifactsSchema = async ( artifactsFilePath = './artifacts.
|
|
11
|
+
const getArtifactsSchema = async ( artifactsFilePath = './artifacts.yaml' ) => {
|
|
11
12
|
const artifactsConfig = await parseYamlFile( artifactsFilePath )
|
|
12
13
|
const [ error, result ] = validate( artifactsConfig, ArtifactsSchema )
|
|
13
14
|
if ( error ) {
|
|
15
|
+
err( `Invalid artifacts.yml file: ${error.message}` )
|
|
16
|
+
debug( { err : error }, '<==Invalid artifacts.yml file' )
|
|
14
17
|
throw new Error( `Invalid artifacts.yml file: ${error.message}` )
|
|
15
18
|
}
|
|
16
19
|
return result
|
|
@@ -22,7 +25,7 @@ const flattenArtifactsSchema = ( artifactsSchema ) => {
|
|
|
22
25
|
...artifactsSchema.defaults,
|
|
23
26
|
...artifact
|
|
24
27
|
}
|
|
25
|
-
|
|
28
|
+
|
|
26
29
|
let schema
|
|
27
30
|
switch ( mergedArtifact.type ) {
|
|
28
31
|
case ArtifactTypeEnum.GENERIC:
|
|
@@ -49,44 +52,230 @@ const flattenArtifactsSchema = ( artifactsSchema ) => {
|
|
|
49
52
|
}, [] )
|
|
50
53
|
}
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
// Example of a file in a generic artifact registry repository:
|
|
56
|
+
// [
|
|
57
|
+
// {
|
|
58
|
+
// "createTime": "2025-12-02T17:52:08.694541Z",
|
|
59
|
+
// "hashes": [
|
|
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 )
|
|
57
95
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
96
|
+
if ( !genericArtifactSchema.remote_file_path && !genericArtifactSchema.remote_path && genericArtifactSchema.local_file_path ) {
|
|
97
|
+
return path.basename( genericArtifactSchema.local_file_path )
|
|
98
|
+
}
|
|
99
|
+
if ( genericArtifactSchema.remote_path && genericArtifactSchema.local_path ) {
|
|
100
|
+
return path.normalize( genericArtifactSchema.remote_path )
|
|
101
|
+
}
|
|
102
|
+
return path.normalize( '.' )
|
|
61
103
|
}
|
|
62
104
|
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
105
|
+
const parseGenericArtifactSchemaToLocalRelativePath = ( genericArtifactSchema ) => {
|
|
106
|
+
if ( genericArtifactSchema.local_file_path ) {
|
|
107
|
+
return path.normalize( genericArtifactSchema.local_file_path )
|
|
108
|
+
}
|
|
109
|
+
if ( genericArtifactSchema.local_path && genericArtifactSchema.remote_file_path ) {
|
|
110
|
+
return path.join( genericArtifactSchema.local_path, path.basename( genericArtifactSchema.remote_file_path ) )
|
|
69
111
|
}
|
|
70
|
-
|
|
112
|
+
if ( genericArtifactSchema.local_path && !genericArtifactSchema.remote_file_path && !genericArtifactSchema.remote_file_path ) {
|
|
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( '.' )
|
|
71
119
|
}
|
|
72
120
|
|
|
73
|
-
const
|
|
74
|
-
|
|
121
|
+
const parseGenericArtifactSchemaToArtifactNameBase = ( genericArtifactSchema ) => {
|
|
122
|
+
const elements = [
|
|
123
|
+
'projecs',
|
|
124
|
+
genericArtifactSchema.project,
|
|
125
|
+
'locations',
|
|
126
|
+
genericArtifactSchema.location,
|
|
127
|
+
'repositories',
|
|
128
|
+
genericArtifactSchema.repository,
|
|
129
|
+
'files',
|
|
130
|
+
]
|
|
131
|
+
return elements.join( '/' )
|
|
75
132
|
}
|
|
76
133
|
|
|
77
|
-
const
|
|
78
|
-
|
|
134
|
+
const parseGenericArtifactSchemaToArtifactNameFile = ( genericArtifactSchema ) => {
|
|
135
|
+
// // "name": "projects/leverege-registry/locations/us-central1/repositories/test-generic-registry/files/sweet-poems-1-1764697925426:v1:test1.txt%2Ftest1.txt",
|
|
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( ':' )
|
|
79
158
|
}
|
|
80
159
|
|
|
81
|
-
const calculateHash = ( path ) => {
|
|
160
|
+
const calculateHash = async ( path ) => {
|
|
82
161
|
try {
|
|
83
162
|
const hash = crypto.createHash( 'sha256' )
|
|
84
|
-
hash.update( fs.
|
|
85
|
-
return hash.digest( 'base64url' )
|
|
163
|
+
hash.update( await fs.promises.readFile( path ) )
|
|
164
|
+
return `${hash.digest( 'base64url' )}=`
|
|
86
165
|
} catch ( error ) {
|
|
87
|
-
console.error( error )
|
|
88
166
|
throw new Error( `Failed to calculate hash for artifact ${path}: ${error.message}` )
|
|
89
167
|
}
|
|
90
168
|
}
|
|
91
169
|
|
|
92
|
-
|
|
170
|
+
const listGenericArtifactFiles = async ( artifact ) => {
|
|
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
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
3
5
|
import { program } from 'commander'
|
|
4
6
|
|
|
5
7
|
import { warning, log } from '../Utils.mjs'
|
|
@@ -11,33 +13,36 @@ import { GenericArtifactPuller } from './ArtifactPuller.mjs'
|
|
|
11
13
|
program
|
|
12
14
|
.name( 'pull-artifact' )
|
|
13
15
|
.description( 'Pull artifacts from GCP Artifact Registry' )
|
|
14
|
-
.
|
|
15
|
-
.option( '-
|
|
16
|
-
.option( '-v, --package-version <version>', 'Filter by artifact 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' )
|
|
17
18
|
.parse()
|
|
18
19
|
|
|
19
20
|
const options = program.opts()
|
|
20
21
|
|
|
21
22
|
async function main() {
|
|
22
|
-
const
|
|
23
|
-
const artifactPackageName = options.packageName || null
|
|
24
|
-
const artifactPackageVersion = options.packageVersion || null
|
|
25
|
-
|
|
26
|
-
const artifactsSchema = await getArtifactsSchema( artifactsYmlPath )
|
|
23
|
+
const artifactsSchema = await getArtifactsSchema( options.filePath )
|
|
27
24
|
const flattenedArtifactsSchema = flattenArtifactsSchema( artifactsSchema )
|
|
28
|
-
|
|
29
|
-
let filteredArtifactsSchema
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
+
|
|
35
38
|
filteredArtifactsSchema = flattenedArtifactsSchema.filter(
|
|
36
|
-
artifact => artifact.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
artifact => artifact.project === project &&
|
|
40
|
+
artifact.location === location &&
|
|
41
|
+
artifact.repository === repository &&
|
|
42
|
+
artifact.package === packageName &&
|
|
43
|
+
artifact.version === version )
|
|
39
44
|
}
|
|
40
|
-
|
|
45
|
+
|
|
41
46
|
let counter = 0
|
|
42
47
|
|
|
43
48
|
const pullResults = await Promise.all(
|
|
@@ -45,7 +50,7 @@ async function main() {
|
|
|
45
50
|
let puller
|
|
46
51
|
switch ( artifactSchema.type ) {
|
|
47
52
|
case ArtifactTypeEnum.GENERIC:
|
|
48
|
-
puller = new GenericArtifactPuller( artifactSchema )
|
|
53
|
+
puller = new GenericArtifactPuller( path.dirname( options.filePath ), artifactSchema )
|
|
49
54
|
break
|
|
50
55
|
default:
|
|
51
56
|
warning( `Unsupported artifact type "${artifactSchema.type}"` )
|
|
@@ -69,6 +74,8 @@ async function main() {
|
|
|
69
74
|
counter = pullResults.filter( Boolean ).length
|
|
70
75
|
|
|
71
76
|
log( `Successfully pulled ${counter}/${filteredArtifactsSchema.length} artifacts` )
|
|
77
|
+
|
|
78
|
+
return counter === filteredArtifactsSchema.length
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
main()
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
3
5
|
import { program } from 'commander'
|
|
4
6
|
|
|
5
7
|
import { warning, log } from '../Utils.mjs'
|
|
@@ -11,31 +13,33 @@ import { GenericArtifactPusher } from './ArtifactPusher.mjs'
|
|
|
11
13
|
program
|
|
12
14
|
.name( 'push-artifact' )
|
|
13
15
|
.description( 'Push artifacts to GCP Artifact Registry' )
|
|
14
|
-
.
|
|
15
|
-
.option( '-
|
|
16
|
-
.option( '-v, --package-version <version>', 'Filter by artifact 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' )
|
|
17
18
|
.parse()
|
|
18
19
|
|
|
19
20
|
const options = program.opts()
|
|
20
21
|
|
|
21
22
|
async function main() {
|
|
22
|
-
const
|
|
23
|
-
const artifactPackageName = options.packageName || null
|
|
24
|
-
const artifactPackageVersion = options.packageVersion || null
|
|
25
|
-
|
|
26
|
-
const artifactsSchema = await getArtifactsSchema( artifactsYmlPath )
|
|
23
|
+
const artifactsSchema = await getArtifactsSchema( options.filePath )
|
|
27
24
|
const flattenedArtifactsSchema = flattenArtifactsSchema( artifactsSchema )
|
|
28
|
-
|
|
29
|
-
let filteredArtifactsSchema
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
+
|
|
35
37
|
filteredArtifactsSchema = flattenedArtifactsSchema.filter(
|
|
36
|
-
artifact => artifact.
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
artifact => artifact.project === project &&
|
|
39
|
+
artifact.location === location &&
|
|
40
|
+
artifact.repository === repository &&
|
|
41
|
+
artifact.package === packageName &&
|
|
42
|
+
artifact.version === version )
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
let counter = 0
|
|
@@ -45,7 +49,7 @@ async function main() {
|
|
|
45
49
|
let pusher
|
|
46
50
|
switch ( artifactSchema.type ) {
|
|
47
51
|
case ArtifactTypeEnum.GENERIC:
|
|
48
|
-
pusher = new GenericArtifactPusher( artifactSchema )
|
|
52
|
+
pusher = new GenericArtifactPusher( path.dirname( options.filePath ), artifactSchema )
|
|
49
53
|
break
|
|
50
54
|
default:
|
|
51
55
|
warning( `Unsupported artifact type "${artifactSchema.type}"` )
|
|
@@ -69,6 +73,8 @@ async function main() {
|
|
|
69
73
|
counter = pushResults.filter( Boolean ).length
|
|
70
74
|
|
|
71
75
|
log( `Successfully pushed ${counter}/${filteredArtifactsSchema.length} artifacts` )
|
|
76
|
+
|
|
77
|
+
return counter === filteredArtifactsSchema.length
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
main()
|
package/src/bash-funcs
CHANGED
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
# see exactly what certain commands are complaining about. Running any of the
|
|
10
10
|
# build-tools with BUILD_TOOLS_DEBUG=1 will redirect the noise to stdout.
|
|
11
11
|
DEVNULL="/dev/null"
|
|
12
|
-
[
|
|
12
|
+
if [[ -n "${BUILD_TOOLS_DEBUG:-}" ]];
|
|
13
|
+
then
|
|
14
|
+
DEVNULL="/dev/stdout"
|
|
15
|
+
printf "\n***BUILD_TOOLS_DEBUG DEVNULL=>$DEVNULL\n"
|
|
16
|
+
fi
|
|
13
17
|
|
|
14
18
|
GITTOP=`git rev-parse --show-toplevel`
|
|
15
19
|
DTS="`date +\"%Y%m%d-%H%M%S\"`"
|
|
@@ -305,6 +309,11 @@ PLUGIN_NOEXEC
|
|
|
305
309
|
return 0
|
|
306
310
|
}
|
|
307
311
|
|
|
312
|
+
# simple check to allow us to avoid double settings the k8s context
|
|
313
|
+
function fromHelmup() {
|
|
314
|
+
[[ "${_FROM_HELMUP_-false}" == "true" ]]
|
|
315
|
+
}
|
|
316
|
+
|
|
308
317
|
# simple enforcement of necessary tools that need to be installed
|
|
309
318
|
function commandChecker() {
|
|
310
319
|
for cmd in "$@"; do
|
package/src/change-up.sh
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
version="${1:-X.Y.Z}"
|
|
5
|
+
date_str=$(date +%F) # e.g., 2025-12-11
|
|
6
|
+
|
|
7
|
+
cat <<EOF
|
|
8
|
+
## [$version] - $date_str
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
-
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
-
|
|
15
|
+
|
|
16
|
+
### Deprecated
|
|
17
|
+
-
|
|
18
|
+
|
|
19
|
+
### Removed
|
|
20
|
+
-
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
-
|
|
24
|
+
|
|
25
|
+
### Migration Notes
|
|
26
|
+
-
|
|
27
|
+
EOF
|
package/src/chart-compass.mjs
CHANGED
|
@@ -79,7 +79,7 @@ if ( args._unknown ) {
|
|
|
79
79
|
|
|
80
80
|
const minNodejsVersion = '22.13.0'
|
|
81
81
|
if ( semverLt( process.version, minNodejsVersion ) ) {
|
|
82
|
-
warning( `suggest upgrading to at least node ${minNodejsVersion}
|
|
82
|
+
warning( `suggest upgrading to at least node ${minNodejsVersion}\n` )
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// First of all, fail if we are not in a git repository
|
|
@@ -116,20 +116,33 @@ const buildCompassMap = ( yamlFile ) => {
|
|
|
116
116
|
return chartCompass
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
const findChartUrl = ( config,
|
|
120
|
-
if ( !
|
|
121
|
-
errorExit( 'no chart name speficied' )
|
|
122
|
-
}
|
|
119
|
+
const findChartUrl = ( config, serviceName ) => {
|
|
120
|
+
if ( !serviceName ) errorExit( 'no chart name specified' )
|
|
123
121
|
|
|
124
122
|
for ( const entry of config ) {
|
|
125
123
|
const chartsPath = entry.charts_path || 'charts'
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
124
|
+
const repositories = entry.repositories || {}
|
|
125
|
+
|
|
126
|
+
for ( const [ repo, chartMap ] of Object.entries( repositories ) ) {
|
|
127
|
+
debug( { repo, chartMap }, `<== findChartUrl in repo ${repo}` )
|
|
128
|
+
|
|
129
|
+
for ( const [ chartKey, cfg ] of Object.entries( chartMap ) ) {
|
|
130
|
+
const chartCfg = cfg || {}
|
|
131
|
+
const chartArtifact = chartCfg.chart || chartKey
|
|
132
|
+
const aliases = chartCfg.aliases || []
|
|
133
|
+
|
|
134
|
+
const matches =
|
|
135
|
+
serviceName === chartKey ||
|
|
136
|
+
serviceName === chartArtifact ||
|
|
137
|
+
aliases.includes( serviceName )
|
|
138
|
+
|
|
139
|
+
if ( matches ) {
|
|
140
|
+
return `${entry.registry}/${repo}/${chartsPath}/${chartArtifact}`
|
|
141
|
+
}
|
|
130
142
|
}
|
|
131
143
|
}
|
|
132
144
|
}
|
|
145
|
+
|
|
133
146
|
return undefined
|
|
134
147
|
}
|
|
135
148
|
|
package/src/chart-compass.yaml
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
- registry: us-docker.pkg.dev/leverege-registry
|
|
2
|
+
charts_path: charts
|
|
2
3
|
repositories:
|
|
3
4
|
stack:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
5
|
+
api-server: {}
|
|
6
|
+
authz-server: {}
|
|
7
|
+
db-curator: {}
|
|
8
|
+
emailer: {}
|
|
9
|
+
imagine: {}
|
|
10
|
+
message-processor: {}
|
|
11
|
+
messenger: {}
|
|
12
|
+
reason: {}
|
|
13
|
+
resource-server: {}
|
|
14
|
+
rule-enginer: {}
|
|
15
|
+
scheduler: {}
|
|
16
|
+
|
|
17
|
+
transponder:
|
|
18
|
+
aliases:
|
|
19
|
+
- transponder-bq
|
|
20
|
+
- transponder-dh
|
|
21
|
+
- transponder-rt
|
|
22
|
+
- transponder-tsdb
|
|
23
|
+
|
|
19
24
|
leverege:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
pubsub-pulse: {}
|
|
26
|
+
pusher: {}
|
|
27
|
+
overdose: {}
|
|
@@ -117,7 +117,7 @@ if ( requiresHelmChart ) {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
const leveregeStanza = isNodeJsProject ? closestPackageJson.leverege : repoDescr.rootPyProjectToml.leverege
|
|
120
|
-
const runCommand = isYarnProject ? 'node entrypoint.
|
|
120
|
+
const runCommand = isYarnProject ? 'node entrypoint.js' : 'node index.js'
|
|
121
121
|
|
|
122
122
|
const dockerfileOptions = {
|
|
123
123
|
isNodeJsProject,
|
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
#
|
|
3
3
|
showInstalling "The Prometheus Operator and Components"
|
|
4
|
-
addHelmRepo prometheus-community https://prometheus-community.github.io/helm-charts
|
|
5
4
|
|
|
6
5
|
NS="--namespace prometheus"
|
|
7
6
|
|
|
8
7
|
showInstalling "The Prometheus Operator (kube-prometheus-stack)"
|
|
9
|
-
[ -z "$PROMETHEUS_STACK_CHART_VERSION" ] && PROMETHEUS_STACK_CHART_VERSION="79"
|
|
10
8
|
|
|
11
9
|
# https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack
|
|
12
|
-
|
|
10
|
+
OCI_CHART="oci://ghcr.io/prometheus-community/charts/kube-prometheus-stack"
|
|
11
|
+
[ -z "$PROMETHEUS_STACK_CHART_VERSION" ] && PROMETHEUS_STACK_CHART_VERSION="80.4.1"
|
|
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="
|
|
19
|
-
|
|
18
|
+
[ -z "$ELASTICSEARCH_EXPORTER_CHART_VERSION" ] && ELASTICSEARCH_EXPORTER_CHART_VERSION="7.2.1"
|
|
19
|
+
OCI_CHART="oci://ghcr.io/prometheus-community/charts/prometheus-elasticsearch-exporter"
|
|
20
|
+
helm upgrade $NS --install elasticsearch8-exporter $OCI_CHART \
|
|
20
21
|
--values prom-operator/elasticsearch-exporter.yaml \
|
|
21
22
|
--version $ELASTICSEARCH_EXPORTER_CHART_VERSION $HELM_WHAT
|
|
22
23
|
|
|
23
24
|
# https://artifacthub.io/packages/helm/prometheus-community/prometheus-stackdriver-exporter
|
|
24
25
|
showInstalling "The Stackdriver Exporter (prom-operator GKE metrics exports)"
|
|
25
|
-
[ -z "$STACKDRIVER_EXPORTER_CHART_VERSION" ] && STACKDRIVER_EXPORTER_CHART_VERSION="4"
|
|
26
|
-
|
|
26
|
+
[ -z "$STACKDRIVER_EXPORTER_CHART_VERSION" ] && STACKDRIVER_EXPORTER_CHART_VERSION="4.12.2"
|
|
27
|
+
OCI_CHART="oci://ghcr.io/prometheus-community/charts/prometheus-stackdriver-exporter"
|
|
28
|
+
helm upgrade $NS --install stackdriver-exporter $OCI_CHART \
|
|
27
29
|
--values prom-operator/stackdriver-exporter.yaml \
|
|
28
30
|
--version $STACKDRIVER_EXPORTER_CHART_VERSION $HELM_WHAT
|
|
29
|
-
|
|
30
|
-
removeHelmRepo prometheus-community
|
package/src/helmup.sh
CHANGED
package/src/k8scale.sh
CHANGED
|
@@ -29,8 +29,12 @@ K8SCALE_HELP
|
|
|
29
29
|
exit 1
|
|
30
30
|
esac
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
if fromHelmup;
|
|
33
|
+
then
|
|
34
|
+
printf "Skipping overwhelm because invoked from helmup\n"
|
|
35
|
+
else
|
|
36
|
+
overwhelm || { printf "\n\n***Aborting helmup...\n\n"; exit 1; }
|
|
37
|
+
fi
|
|
34
38
|
|
|
35
39
|
printf " scaled => `color g $DIRECTION`\n\n"
|
|
36
40
|
for DEPLOYMENT in "$@";
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#! /usr/bin/env node
|
|
2
|
+
import { $, chalk, fs, path } from 'zx'
|
|
3
|
+
import findYarnWorkspaceRoot from 'find-yarn-workspace-root'
|
|
4
|
+
|
|
5
|
+
import { log } from './Utils.mjs'
|
|
6
|
+
|
|
7
|
+
const $$ = $( {
|
|
8
|
+
stdio : 'inherit',
|
|
9
|
+
quiet : true
|
|
10
|
+
} )
|
|
11
|
+
|
|
12
|
+
const generateEntrypointFile = main => `
|
|
13
|
+
import path from 'node:path'
|
|
14
|
+
import { fileURLToPath } from 'url'
|
|
15
|
+
import { createRequire } from 'node:module'
|
|
16
|
+
|
|
17
|
+
const require = createRequire( import.meta.url )
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
19
|
+
const __dirname = path.dirname(__filename)
|
|
20
|
+
|
|
21
|
+
const indexPath = path.normalize(path.resolve( __dirname,'${main}'))
|
|
22
|
+
process.env.APP_PLUGIN_ROOT = path.dirname( indexPath )
|
|
23
|
+
const index = require(indexPath)
|
|
24
|
+
|
|
25
|
+
export default index
|
|
26
|
+
`
|
|
27
|
+
|
|
28
|
+
const packageJson = await fs.readJSON( path.join( process.env.INIT_CWD, './package.json' ) )
|
|
29
|
+
log( `Building ${packageJson.name}...` )
|
|
30
|
+
const mainEntry = path.join( process.env.INIT_CWD, packageJson.main )
|
|
31
|
+
const root = findYarnWorkspaceRoot( process.env.INIT_CWD ) || process.env.INIT_CWD
|
|
32
|
+
|
|
33
|
+
const main = path.relative( root, mainEntry )
|
|
34
|
+
|
|
35
|
+
const packageName = process.env.npm_package_name
|
|
36
|
+
const buildDir = `/tmp/${packageName.replace( '@leverege/', '' )}/build`
|
|
37
|
+
log( `Output directory: ${buildDir}` )
|
|
38
|
+
|
|
39
|
+
// await $$`yarn clean`
|
|
40
|
+
log( 'Cleaning previous build output...' )
|
|
41
|
+
await $$`rm -rf ${buildDir}`.catch( () => {} )
|
|
42
|
+
await $$`mkdir -p ${buildDir}`.catch( () => {} )
|
|
43
|
+
log( 'Ensuring yarn bundle plugin...' )
|
|
44
|
+
let plugins = $.sync`yarn plugin runtime --json`.stdout.trim()
|
|
45
|
+
plugins = plugins != null && plugins.length > 0 ? plugins.split( '\n' ).map( JSON.parse ) : []
|
|
46
|
+
if ( !plugins.some( plg => plg.name === '@yarn.build/plugin-bundle' ) ) {
|
|
47
|
+
log( chalk.redBright( 'Yarn bundle plugin missing!' ) )
|
|
48
|
+
log( `To fix this error, run:\n${chalk.green( 'yarn plugin import https://yarn.build/latest/bundle' )}` )
|
|
49
|
+
process.exit( 1 )
|
|
50
|
+
}
|
|
51
|
+
log( 'Installing focused yarn dependencies...' )
|
|
52
|
+
await $$`yarn workspaces focus ${packageName}`
|
|
53
|
+
log( 'Bundling...' )
|
|
54
|
+
await $$`yarn bundle --no-compress --output-directory ${buildDir}`
|
|
55
|
+
log( 'Cleaning up unnecessary output files...' )
|
|
56
|
+
await $$`rm -rf ${buildDir}/node_modules ${buildDir}/entrypoint.js`
|
|
57
|
+
|
|
58
|
+
const entrypointFile = generateEntrypointFile( main )
|
|
59
|
+
|
|
60
|
+
await fs.writeFile( `${buildDir}/entrypoint.js`, entrypointFile, 'utf-8' )
|