@leverege/build-tools 2.59.2 → 2.61.0-alpha.1
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 +2 -1
- package/src/Docker.mjs +12 -131
- package/src/DockerPy.mjs +257 -0
- package/src/docker-to-registry-py.mjs +291 -0
- package/src/docker-to-registry.mjs +4 -1
- package/src/templates/bashrcTemplate.hbs +46 -0
- package/src/templates/bashrcTemplatePy.hbs +1 -0
- package/src/templates/cloudBuildSteps.hbs +13 -0
- package/src/templates/dockerfileTemplate.hbs +86 -0
- package/src/templates/dockerfileTemplatePy.hbs +75 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leverege/build-tools",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.61.0-alpha.1",
|
|
4
4
|
"description": "A collection of build / support tools for Leverege developers",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -81,6 +81,7 @@
|
|
|
81
81
|
"readline-sync": "^1.4.10",
|
|
82
82
|
"semver": "^7.6.3",
|
|
83
83
|
"simple-git": "^3.27.0",
|
|
84
|
+
"toml": "^3.0.0",
|
|
84
85
|
"zx": "^8.3.0"
|
|
85
86
|
},
|
|
86
87
|
"devDependencies": {
|
package/src/Docker.mjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import url from 'node:url'
|
|
2
4
|
|
|
3
5
|
import chalk from 'chalk'
|
|
4
6
|
import handlebars from 'handlebars'
|
|
@@ -13,6 +15,8 @@ import {
|
|
|
13
15
|
warning
|
|
14
16
|
} from './Utils.mjs'
|
|
15
17
|
|
|
18
|
+
const DIRNAME = path.dirname( url.fileURLToPath( import.meta.url ) )
|
|
19
|
+
|
|
16
20
|
// TODO: change default to node 22 (jod) after 01/01/25
|
|
17
21
|
const dockerfileNodeVersion = 'jod-alpine'
|
|
18
22
|
|
|
@@ -20,74 +24,6 @@ const dockerfileNodeVersion = 'jod-alpine'
|
|
|
20
24
|
// the build-tools repository, but it just became simpler to pull the contents
|
|
21
25
|
// directly into these variables and forego the file loading.
|
|
22
26
|
//
|
|
23
|
-
const dockerfileTemplate = `
|
|
24
|
-
# Version {{regvers}} @ {{date}}
|
|
25
|
-
#
|
|
26
|
-
# The FROM directive sets the Base Image for subsequent instructions
|
|
27
|
-
FROM node:{{nodeimage}} as intermediate
|
|
28
|
-
ENV NODE_ENV production
|
|
29
|
-
ENV NPM_CONFIG_USERCONFIG /usr/src/app/.npmrc
|
|
30
|
-
|
|
31
|
-
RUN mkdir -p /usr/src/app
|
|
32
|
-
WORKDIR /usr/src/app
|
|
33
|
-
|
|
34
|
-
# Install app dependencies
|
|
35
|
-
COPY ./workspace/ /usr/src/app/
|
|
36
|
-
ENV GRPC_VERBOSITY ERROR
|
|
37
|
-
|
|
38
|
-
# --------------------------------------------------------------
|
|
39
|
-
# copy the ssh keys into place, npm install, and remove them
|
|
40
|
-
# --------------------------------------------------------------
|
|
41
|
-
|
|
42
|
-
# Install packages to install private repos with ssh keys
|
|
43
|
-
COPY ./.npmrc \${NPM_CONFIG_USERCONFIG}
|
|
44
|
-
RUN apk --no-cache add openssh-client && \
|
|
45
|
-
apk --update add --no-cache --virtual build-dep g++ gcc libgcc \\
|
|
46
|
-
libstdc++ linux-headers make {{apkadds}} && \
|
|
47
|
-
npm install -g {{npmVersion}}
|
|
48
|
-
|
|
49
|
-
{{preInstallPluginfile}}
|
|
50
|
-
|
|
51
|
-
# Use SKIP_PREPARE to disable hook-and-release from running
|
|
52
|
-
ENV SKIP_PREPARE=true
|
|
53
|
-
|
|
54
|
-
# Do the clean install and remove the npm token and ssh keys from the image
|
|
55
|
-
RUN npm ci --only=production --no-optional {{npmlogging}} && \
|
|
56
|
-
rm -f \${NPM_CONFIG_USERCONFIG} /root/.ssh/*
|
|
57
|
-
|
|
58
|
-
# --------------------------------------------------------------
|
|
59
|
-
# Docker Final Stage
|
|
60
|
-
# --------------------------------------------------------------
|
|
61
|
-
|
|
62
|
-
FROM node:{{nodeimage}}
|
|
63
|
-
ENV NPM_CONFIG_USERCONFIG /usr/src/app/.npmrc
|
|
64
|
-
|
|
65
|
-
# Copy .npmrc for private registry access for access to any @leverege packages
|
|
66
|
-
COPY ./.npmrc \${NPM_CONFIG_USERCONFIG}
|
|
67
|
-
|
|
68
|
-
# Install tini for PID 1 and replace shell with bash so we can source files
|
|
69
|
-
RUN apk update && \
|
|
70
|
-
apk add --no-cache bash curl tini vim {{apkadds}} && \
|
|
71
|
-
npm install -g {{npmVersion}} && \
|
|
72
|
-
rm /bin/sh && ln -s /bin/bash /bin/sh && \
|
|
73
|
-
mkdir -p /usr/src/app /tmp/levlog && \
|
|
74
|
-
chown node:node /usr/src/app
|
|
75
|
-
|
|
76
|
-
# Pull in the Dockerfile.plugin file here
|
|
77
|
-
{{pluginfile}}
|
|
78
|
-
|
|
79
|
-
# Eliminate the sensitive info from the final stage image
|
|
80
|
-
RUN rm -f \${NPM_CONFIG_USERCONFIG}
|
|
81
|
-
|
|
82
|
-
ENTRYPOINT [ "/sbin/tini", "--" ]
|
|
83
|
-
WORKDIR /usr/src/app
|
|
84
|
-
|
|
85
|
-
COPY --chown=node:node --from=intermediate /usr/src/app /usr/src/app
|
|
86
|
-
|
|
87
|
-
USER {{runuser}}
|
|
88
|
-
COPY ./bashrc /home/node/.bashrc
|
|
89
|
-
CMD [ "/bin/bash", "-c", "source /home/node/.bashrc && node index.js" ]`
|
|
90
|
-
|
|
91
27
|
const pluginTemplate = `
|
|
92
28
|
# Dockerfile.plugin - optionally extend the service Docker image
|
|
93
29
|
#
|
|
@@ -103,72 +39,15 @@ const pluginTemplate = `
|
|
|
103
39
|
# footprints small, so adding packages "just because" is not considered
|
|
104
40
|
# a best practice.
|
|
105
41
|
`
|
|
106
|
-
|
|
107
|
-
const bashrcTemplate =
|
|
108
|
-
|
|
109
|
-
alias h=history
|
|
110
|
-
|
|
111
|
-
alias ls='ls -CF --color=auto'
|
|
112
|
-
alias ll='ls -lh'
|
|
113
|
-
alias lla='ls -lha'
|
|
114
|
-
alias glep='grep -l -s'
|
|
115
|
-
alias m=less
|
|
116
|
-
alias menv='env | sort | less'
|
|
117
|
-
|
|
118
|
-
alias whatsmyip='wget -qO- ifconfig.co'
|
|
119
|
-
|
|
120
|
-
alias err='wget -q -O- localhost:5111/logLevel/error'
|
|
121
|
-
alias wrn='wget -q -O- localhost:5111/logLevel/warn'
|
|
122
|
-
alias inf='wget -q -O- localhost:5111/logLevel/info'
|
|
123
|
-
alias dbg='wget -q -O- localhost:5111/logLevel/debug'
|
|
124
|
-
alias trc='wget -q -O- localhost:5111/logLevel/trace'
|
|
125
|
-
|
|
126
|
-
socks()
|
|
127
|
-
{
|
|
128
|
-
netstat -ant | awk '{print }' | sort | uniq -c | sort -n
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
cmetrics()
|
|
132
|
-
{
|
|
133
|
-
wget -qO- localhost:5111/metrics
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
cmstat()
|
|
137
|
-
{
|
|
138
|
-
wget -qO- localhost:5111
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
cmclear()
|
|
142
|
-
{
|
|
143
|
-
# Future me - thinking this should call cmstat and pass in the endpoint as
|
|
144
|
-
# a parameter? Well don't do it - without escaping the dollar 1 in cmstat
|
|
145
|
-
# the port will end up being 51111, and escaping causes linting to complain
|
|
146
|
-
# about unnecessary escaping.
|
|
147
|
-
wget -qO- localhost:5111/status/clear
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
dunm() {
|
|
151
|
-
du -sh /usr/src/app/node_modules/* | sort -sh
|
|
152
|
-
}
|
|
153
|
-
`
|
|
154
|
-
const cloudBuildSteps = `
|
|
155
|
-
steps:
|
|
156
|
-
- name: 'gcr.io/cloud-builders/docker'
|
|
157
|
-
args:
|
|
158
|
-
- 'build'
|
|
159
|
-
- '--network=cloudbuild'
|
|
160
|
-
- '-t'
|
|
161
|
-
- '{{imageName}}:{{imageVersion}}'
|
|
162
|
-
- '.'
|
|
163
|
-
|
|
164
|
-
images:
|
|
165
|
-
- '{{imageName}}:{{imageVersion}}'
|
|
166
|
-
`
|
|
42
|
+
const dockerfileTemplate = fs.readFileSync( path.join( DIRNAME, './templates/dockerfileTemplate.hbs' ), 'utf8' )
|
|
43
|
+
const bashrcTemplate = fs.readFileSync( path.join( DIRNAME, './templates/bashrcTemplate.hbs' ), 'utf8' )
|
|
44
|
+
const cloudBuildSteps = fs.readFileSync( path.join( DIRNAME, './templates/cloudBuildSteps.hbs' ), 'utf8' )
|
|
167
45
|
|
|
168
46
|
const defaultSettings = {
|
|
169
47
|
apkadds : '',
|
|
170
48
|
date : getDateTimestamp(),
|
|
171
49
|
nodeimage : 'jod-alpine',
|
|
50
|
+
imageBase : 'node:jod-alpine',
|
|
172
51
|
npmlogging : process.env.VERBOSE_NPM_LOGGING ? '--ddd' : '--silent',
|
|
173
52
|
npmVersion : 'npm@11',
|
|
174
53
|
pluginfile : '# NO PLUGIN',
|
|
@@ -196,7 +75,7 @@ const generateDockerfile = ( options ) => {
|
|
|
196
75
|
if ( !settings.nodeimage ) {
|
|
197
76
|
settings.nodeimage = dockerfileNodeVersion
|
|
198
77
|
}
|
|
199
|
-
|
|
78
|
+
settings.image = settings.imageBase || `node:${settings.nodeimage}`
|
|
200
79
|
// Ensure the presence of the docker dir with plugin and bashrc - this code
|
|
201
80
|
// could probably be tightened up a little.
|
|
202
81
|
try {
|
|
@@ -207,7 +86,9 @@ const generateDockerfile = ( options ) => {
|
|
|
207
86
|
}
|
|
208
87
|
|
|
209
88
|
const bashrcFile = './docker/bashrc'
|
|
210
|
-
|
|
89
|
+
// DISABLING exists check. With addition of Bashrc.plugin, the file needs to
|
|
90
|
+
// always be created or we encounter errors
|
|
91
|
+
if ( true ) { // !fs.existsSync( bashrcFile ) ) {
|
|
211
92
|
let bf = bashrcTemplate
|
|
212
93
|
const bashrcFilePlugin = './docker/Bashrc.plugin'
|
|
213
94
|
if ( fs.existsSync( bashrcFilePlugin ) ) {
|
package/src/DockerPy.mjs
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import url from 'node:url'
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk'
|
|
6
|
+
import handlebars from 'handlebars'
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
errorExit,
|
|
10
|
+
getDateTimestamp,
|
|
11
|
+
getGcpAccount,
|
|
12
|
+
log,
|
|
13
|
+
proceed,
|
|
14
|
+
shellCmd,
|
|
15
|
+
warning
|
|
16
|
+
} from './Utils.mjs'
|
|
17
|
+
|
|
18
|
+
const DIRNAME = path.dirname( url.fileURLToPath( import.meta.url ) )
|
|
19
|
+
|
|
20
|
+
// The contents of these variables were initially located in files that live in
|
|
21
|
+
// the build-tools repository, but it just became simpler to pull the contents
|
|
22
|
+
// directly into these variables and forego the file loading.
|
|
23
|
+
//
|
|
24
|
+
const pluginTemplate = `
|
|
25
|
+
# Dockerfile.plugin - optionally extend the service Docker image
|
|
26
|
+
#
|
|
27
|
+
# This file may be used to run additional docker commands during the image
|
|
28
|
+
# build process without needing to maintain a local custom Dockerfile. For
|
|
29
|
+
# example, uncommenting the following docker RUN command will cause the
|
|
30
|
+
# curl and vim packages to be added to the deployed image thus making them
|
|
31
|
+
# available from the pod's command line on k8s:
|
|
32
|
+
#
|
|
33
|
+
# RUN apk update && apk add --no-cache curl vim
|
|
34
|
+
#
|
|
35
|
+
# Keep in mind that the Alpine Linux base image is used to keep image
|
|
36
|
+
# footprints small, so adding packages "just because" is not considered
|
|
37
|
+
# a best practice.
|
|
38
|
+
`
|
|
39
|
+
const dockerfileTemplate = fs.readFileSync( path.join( DIRNAME, './templates/dockerfileTemplatePy.hbs' ), 'utf8' )
|
|
40
|
+
const cloudBuildSteps = fs.readFileSync( path.join( DIRNAME, './templates/cloudBuildSteps.hbs' ), 'utf8' )
|
|
41
|
+
let bashrcTemplate = fs.readFileSync( path.join( DIRNAME, './templates/bashrcTemplate.hbs' ), 'utf8' )
|
|
42
|
+
const bashrcPyTemplate = fs.readFileSync( path.join( DIRNAME, './templates/bashrcTemplatePy.hbs' ), 'utf8' )
|
|
43
|
+
bashrcTemplate = `${bashrcTemplate}\n${bashrcPyTemplate}`
|
|
44
|
+
|
|
45
|
+
const defaultSettings = {
|
|
46
|
+
apkadds : '',
|
|
47
|
+
date : getDateTimestamp(),
|
|
48
|
+
nodeimage : 'jod-alpine',
|
|
49
|
+
imageBase : 'node:jod-alpine',
|
|
50
|
+
npmlogging : process.env.VERBOSE_NPM_LOGGING ? '--ddd' : '--silent',
|
|
51
|
+
npmVersion : 'npm@11',
|
|
52
|
+
pluginfile : '# NO PLUGIN',
|
|
53
|
+
preInstallPluginfile : '# NO PRE-INSTALL PLUGIN',
|
|
54
|
+
regvers : 'package.version',
|
|
55
|
+
runuser : 'python',
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const previousFile = './docker/.previous'
|
|
59
|
+
|
|
60
|
+
function getPreviousBuild() {
|
|
61
|
+
return fs.existsSync( previousFile ) ? fs.readFileSync( previousFile ) : 'FIRST BUILD'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const setPreviousBuild = ( version ) => {
|
|
65
|
+
fs.writeFileSync( previousFile, version )
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function generateDockerfile( options ) {
|
|
69
|
+
|
|
70
|
+
const settings = { ...defaultSettings, ...options }
|
|
71
|
+
|
|
72
|
+
if ( !settings.imageBase ) {
|
|
73
|
+
warning( 'using the default image python:3.12.8-slim-bookworm' )
|
|
74
|
+
settings.image = 'python:3.12.8-slim-bookworm'
|
|
75
|
+
} else {
|
|
76
|
+
settings.image = settings.imageBase
|
|
77
|
+
}
|
|
78
|
+
// Ensure the presence of the docker dir with plugin and bashrc - this code
|
|
79
|
+
// could probably be tightened up a little.
|
|
80
|
+
try {
|
|
81
|
+
fs.readdirSync( './docker' )
|
|
82
|
+
} catch ( err ) {
|
|
83
|
+
warning( 'creating the missing ./docker directory' )
|
|
84
|
+
fs.mkdirSync( './docker' ) // TODO: blindly assumes the mkdir succeeds
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const bashrcFile = './docker/bashrc'
|
|
88
|
+
let bf = bashrcTemplate
|
|
89
|
+
const bashrcFilePlugin = './docker/Bashrc.plugin'
|
|
90
|
+
if ( fs.existsSync( bashrcFilePlugin ) ) {
|
|
91
|
+
const bashrcAdditions = fs.readFileSync( bashrcFilePlugin )
|
|
92
|
+
bf = `${bf}\n${bashrcAdditions}`
|
|
93
|
+
}
|
|
94
|
+
fs.writeFileSync( bashrcFile, bf )
|
|
95
|
+
|
|
96
|
+
const dockerfilePlugin = './docker/Dockerfile.plugin'
|
|
97
|
+
if ( !fs.existsSync( dockerfilePlugin ) ) {
|
|
98
|
+
warning( `creating the missing ${dockerfilePlugin} file in the docker directory` )
|
|
99
|
+
fs.writeFileSync( dockerfilePlugin, pluginTemplate )
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const build = fs.readdirSync( './build' )
|
|
104
|
+
if ( build.length === 0 ) {
|
|
105
|
+
errorExit( `***ERROR: the build dir is empty - ${chalk.yellow( 'npm run build' )}` )
|
|
106
|
+
}
|
|
107
|
+
} catch ( err ) {
|
|
108
|
+
errorExit( `***ERROR: expected the ./build directory to exist - ${chalk.yellow( 'npm run build' )}` )
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// At this point the plugin template better exist.
|
|
112
|
+
if ( fs.existsSync( dockerfilePlugin ) ) {
|
|
113
|
+
settings.pluginfile = fs.readFileSync( dockerfilePlugin )
|
|
114
|
+
} else {
|
|
115
|
+
errorExit( `***ERROR: something went wrong with ${dockerfilePlugin} plugin creation` )
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const dockerfilePrePlugin = './docker/DockerfilePreInstall.plugin'
|
|
119
|
+
if ( fs.existsSync( dockerfilePrePlugin ) ) {
|
|
120
|
+
settings.preInstallPluginfile = fs.readFileSync( dockerfilePrePlugin )
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const compiled = handlebars.compile( dockerfileTemplate, { noEscape : true } )
|
|
124
|
+
const replaced = compiled( settings )
|
|
125
|
+
// temporarily write the Dockerfile to the docker subdir - the container
|
|
126
|
+
// image builder will then move it over to the build subdir
|
|
127
|
+
const dockerfile = './docker/Dockerfile'
|
|
128
|
+
try {
|
|
129
|
+
fs.writeFileSync( dockerfile, replaced )
|
|
130
|
+
} catch ( err ) {
|
|
131
|
+
errorExit( `***ERROR: failed to write ${dockerfile}\n${err}` )
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const previousBuild = getPreviousBuild()
|
|
135
|
+
const dockerInfo = { dockerfile, previousBuild, ...settings }
|
|
136
|
+
delete dockerInfo.pluginfile
|
|
137
|
+
return dockerInfo
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function formCloudBuildBucketName( artifactProject ) {
|
|
141
|
+
return `gs://${artifactProject}_cloudbuild`
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function validateCloudBuildBucket( artifactProject ) {
|
|
145
|
+
const bucketName = formCloudBuildBucketName( artifactProject )
|
|
146
|
+
log( chalk.green.bold( `Verifying the build bucket exists => ${bucketName}\n` ) )
|
|
147
|
+
try {
|
|
148
|
+
await shellCmd( `gsutil ls -p ${artifactProject} -b ${bucketName}` )
|
|
149
|
+
} catch ( error ) {
|
|
150
|
+
errorExit( `***Error: from Docker.validateCloudBuildBucket\n\n${chalk.bold.yellow( error )}` )
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function buildContainerImage( {
|
|
155
|
+
artifactProject, artifactRegistry, containerName,
|
|
156
|
+
imageVersion, isNpmWorkspace, options
|
|
157
|
+
} ) {
|
|
158
|
+
|
|
159
|
+
const useCloudBuildFile = true
|
|
160
|
+
const buildWorkspace = './build/workspace' // passed to Cloud Build
|
|
161
|
+
const imageName = `${artifactRegistry}/images/${containerName}`
|
|
162
|
+
|
|
163
|
+
if ( fs.existsSync( buildWorkspace ) ) {
|
|
164
|
+
fs.renameSync( buildWorkspace, `${buildWorkspace}-junk` )
|
|
165
|
+
try {
|
|
166
|
+
fs.rmSync( `${buildWorkspace}-junk`, { recursive : true } )
|
|
167
|
+
} catch ( err ) {
|
|
168
|
+
log( `***Error: from Docker.buildContainerImage\n\n${chalk.bold.yellow( err )}` )
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// TODO: This is janky - I'll clean it up later
|
|
173
|
+
// NOTE: this is dealing with raw filesystem interactions, meaning there
|
|
174
|
+
// isn't a shell performing file globbing and what not
|
|
175
|
+
fs.renameSync( './build', './workspace' )
|
|
176
|
+
fs.mkdirSync( './build' )
|
|
177
|
+
fs.renameSync( './workspace', buildWorkspace )
|
|
178
|
+
fs.renameSync( './docker/Dockerfile', './build/Dockerfile' )
|
|
179
|
+
// fs.cpSync( `${process.env.HOME}/.npmrc`, './build/.npmrc' )
|
|
180
|
+
fs.cpSync( './docker/bashrc', './build/bashrc' )
|
|
181
|
+
// fs.cpSync( './package.json', `${buildWorkspace}/package.json` )
|
|
182
|
+
// fs.cpSync( './package-lock.json', `${buildWorkspace}/package-lock.json` )
|
|
183
|
+
|
|
184
|
+
setPreviousBuild( imageVersion ) // update docker/.previous file
|
|
185
|
+
|
|
186
|
+
const { fullName : builderName } = await getGcpAccount()
|
|
187
|
+
const gcloudBuild = `time gcloud builds submit --project ${artifactProject}`
|
|
188
|
+
const gcloudLogs = `--gcs-log-dir ${formCloudBuildBucketName( artifactProject )}/log`
|
|
189
|
+
let gcloudTags = `--tag ${imageName}:${imageVersion}`
|
|
190
|
+
|
|
191
|
+
if ( useCloudBuildFile ) {
|
|
192
|
+
const cloudBuildFile = './docker/cloudbuild.yaml'
|
|
193
|
+
gcloudTags = `--config=${cloudBuildFile}`
|
|
194
|
+
const cbst = handlebars.compile( cloudBuildSteps, { noEscape : true } )
|
|
195
|
+
const cbs = cbst( { artifactProject, artifactRegistry, imageName, imageVersion } )
|
|
196
|
+
|
|
197
|
+
fs.writeFileSync( cloudBuildFile, cbs )
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
log( chalk.green.bold( `
|
|
201
|
+
*** Submitting Build as ${builderName} ***
|
|
202
|
+
${gcloudBuild} \\
|
|
203
|
+
${gcloudLogs} \\
|
|
204
|
+
${gcloudTags}
|
|
205
|
+
` ) )
|
|
206
|
+
|
|
207
|
+
if ( process.env.DRY_RUN === '1' ) {
|
|
208
|
+
log( '\n***Exiting from DRY_RUN\n' )
|
|
209
|
+
process.exit( 0 )
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
await shellCmd( `${gcloudBuild} ${gcloudLogs} ${gcloudTags} ./build`, { stdio : 'inherit' } )
|
|
213
|
+
|
|
214
|
+
const imageTag = `${defaultSettings.date}-${defaultSettings.nodeimage}-${builderName}`
|
|
215
|
+
await shellCmd(
|
|
216
|
+
`gcloud container images add-tag --quiet ${imageName}:${imageVersion} ${imageName}:${imageTag} ${imageName}:latest`
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async function repoCleanup() {
|
|
221
|
+
// remove garbage from the repo
|
|
222
|
+
const garbage = [
|
|
223
|
+
'build',
|
|
224
|
+
'dist',
|
|
225
|
+
'docker/.keep',
|
|
226
|
+
'docker/.npmrc',
|
|
227
|
+
'docker/.gitignore',
|
|
228
|
+
'docker/.previous',
|
|
229
|
+
'docker/Dockerfile',
|
|
230
|
+
'docker/workspace',
|
|
231
|
+
]
|
|
232
|
+
garbage.forEach( trash => fs.rmSync( trash, { recursive : true, force : true } ) )
|
|
233
|
+
|
|
234
|
+
fs.writeFileSync( 'docker/.gitignore', '.previous\nDockerfile' )
|
|
235
|
+
|
|
236
|
+
await shellCmd( 'git add -f docker' )
|
|
237
|
+
|
|
238
|
+
log( `
|
|
239
|
+
${chalk.yellow.bold( '***IMPORTANT***' )}
|
|
240
|
+
|
|
241
|
+
Please make the following .gitignore modifications:
|
|
242
|
+
change /dist to ${chalk.green.bold( '/build' )}
|
|
243
|
+
remove ${chalk.red.bold( '/docker' )}
|
|
244
|
+
` )
|
|
245
|
+
|
|
246
|
+
await proceed( 'Acknowledge the .gitignore mods were made?' )
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export default {
|
|
250
|
+
getPreviousBuild,
|
|
251
|
+
setPreviousBuild,
|
|
252
|
+
generateDockerfile,
|
|
253
|
+
formCloudBuildBucketName,
|
|
254
|
+
validateCloudBuildBucket,
|
|
255
|
+
buildContainerImage,
|
|
256
|
+
repoCleanup,
|
|
257
|
+
}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* docker-to-registry-py will...
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'node:fs'
|
|
6
|
+
import toml from 'toml'
|
|
7
|
+
import chalk from 'chalk'
|
|
8
|
+
import semver from 'semver'
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
debug, log,
|
|
12
|
+
errorExit,
|
|
13
|
+
getGitBranchAndUpstream,
|
|
14
|
+
gitRepoIsDirty,
|
|
15
|
+
parseHelmChart,
|
|
16
|
+
proceed,
|
|
17
|
+
shellCmd } from './Utils.mjs'
|
|
18
|
+
|
|
19
|
+
import Docker from './DockerPy.mjs'
|
|
20
|
+
import { version } from 'node:os'
|
|
21
|
+
|
|
22
|
+
// refresh-npm-token does not throw so no need to try, but it emits in debug
|
|
23
|
+
const refreshErr = await shellCmd( 'refresh-npm-token' )
|
|
24
|
+
if ( refreshErr && !process.env.BUILD_TOOLS_DEBUG ) {
|
|
25
|
+
log( refreshErr ) // most likely the token refreshed message
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const options = {}
|
|
29
|
+
let imageVersion = null
|
|
30
|
+
// Expected to be invoked like docker-to-registry-py v1.2.3
|
|
31
|
+
for ( let n = 2; n < process.argv.length; n++ ) {
|
|
32
|
+
if ( process.argv[n].startsWith( '-' ) ) {
|
|
33
|
+
const str = process.argv[n].slice( 1 )
|
|
34
|
+
if ( str.indexOf( '=' ) > 0 ) {
|
|
35
|
+
const [ key, value ] = str.split( '=' )
|
|
36
|
+
options[key] = value
|
|
37
|
+
} else {
|
|
38
|
+
options[str] = true
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
imageVersion = process.argv[n]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if ( !imageVersion ) {
|
|
45
|
+
errorExit( '***Error: docker-to-registry-py requires an image version' )
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// may use guide-me or guideme to ask for update guidance
|
|
49
|
+
const giveGuidance = imageVersion.match( /^guide-*me$/ ) !== null
|
|
50
|
+
|
|
51
|
+
const willGitTag = imageVersion.match( /^v\d+\.\d+\.\d+$/ )
|
|
52
|
+
const tagInfo = willGitTag ?
|
|
53
|
+
chalk.yellow.bold( 'will be git tagged' ) :
|
|
54
|
+
chalk.red( 'BETA RELEASE WILL NOT BE GIT TAGGED' )
|
|
55
|
+
|
|
56
|
+
const repoIsDirty = await gitRepoIsDirty()
|
|
57
|
+
|
|
58
|
+
if ( willGitTag && repoIsDirty ) {
|
|
59
|
+
log( `
|
|
60
|
+
${chalk.red.bold( '***ERROR: attempting to tag a non-beta dirty git repository' )}
|
|
61
|
+
|
|
62
|
+
You are attempting to create a tagged release image for pushing to the
|
|
63
|
+
artifact registry, but there are locally modified files. This is not
|
|
64
|
+
allowed since the applied tag will not be relevant to the image version
|
|
65
|
+
due to the pending commits.
|
|
66
|
+
|
|
67
|
+
Available options for proceeding:
|
|
68
|
+
|
|
69
|
+
${chalk.green.bold( '1) cleanly commit all local mods assuming relevance' )}
|
|
70
|
+
${chalk.yellow( '2) eliminate unwanted local modifications' )}
|
|
71
|
+
${chalk.red( '3) stash anything that is irrelevant to this release' )}
|
|
72
|
+
|
|
73
|
+
` )
|
|
74
|
+
process.exit( 1 )
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if ( !fs.existsSync( './helm' ) ) {
|
|
78
|
+
log( `
|
|
79
|
+
${chalk.red.bold( '***Error: docker-to-registry-py requires a helm chart directory' )}
|
|
80
|
+
|
|
81
|
+
The docker-to-registry-py script expects to run in the root directory of a k8s
|
|
82
|
+
service, which requires a helm chart directory. It also expects a docker
|
|
83
|
+
directory but will build that if it does not exist.
|
|
84
|
+
|
|
85
|
+
` )
|
|
86
|
+
process.exit( 1 )
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const tomlStr = fs.readFileSync( './pyproject.toml' )
|
|
90
|
+
const pyproject = toml.parse( tomlStr )
|
|
91
|
+
const repoDescr = {
|
|
92
|
+
artifactProject : pyproject.leverege.project || 'leverege-registry',
|
|
93
|
+
containerName : pyproject.project.name,
|
|
94
|
+
packageVersion : pyproject.project.version,
|
|
95
|
+
artifactRegistry : pyproject.leverege.registry || 'us-docker.pkg.dev/leverege-registry/stack',
|
|
96
|
+
registryFolder : `${pyproject.leverege.registryFolder}/images/${pyproject.project.name}`,
|
|
97
|
+
imageBase : pyproject.leverege.imageBase,
|
|
98
|
+
helmChart : await parseHelmChart(),
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
debug( { repoDescr }, '<==The Repo Description' )
|
|
102
|
+
|
|
103
|
+
const dockerInfo = await Docker.generateDockerfile( {
|
|
104
|
+
regvers : repoDescr.packageVersion,
|
|
105
|
+
imageBase : repoDescr.imageBase,
|
|
106
|
+
} )
|
|
107
|
+
|
|
108
|
+
debug( { dockerInfo }, '<==The Docker Info' )
|
|
109
|
+
|
|
110
|
+
const {
|
|
111
|
+
artifactProject,
|
|
112
|
+
artifactRegistry,
|
|
113
|
+
packageVersion,
|
|
114
|
+
containerName,
|
|
115
|
+
helmChart,
|
|
116
|
+
} = repoDescr
|
|
117
|
+
|
|
118
|
+
if ( giveGuidance ) {
|
|
119
|
+
await Docker.validateCloudBuildBucket( artifactProject )
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const registryFolder = `${artifactRegistry}/images/${containerName}`
|
|
123
|
+
|
|
124
|
+
// Update the dependencies...
|
|
125
|
+
log( chalk.green.bold( 'Updating dependencies and workspace...' ) )
|
|
126
|
+
await shellCmd( 'npm install', { stdio : 'inherit' } )
|
|
127
|
+
|
|
128
|
+
if ( giveGuidance ) {
|
|
129
|
+
await Docker.repoCleanup() // adjusts gitignore files and removes cruft
|
|
130
|
+
log( `
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
${chalk.green.bold( '-------------------- Potential Helm Chart Mods --------------------' )}
|
|
135
|
+
|
|
136
|
+
Moving to the new artifact-registry for docker image storage may also require
|
|
137
|
+
helm chart changes depending on how old the current charts are. Charts that
|
|
138
|
+
contain helm/values.yaml files resembling this:
|
|
139
|
+
|
|
140
|
+
${chalk.yellow.bold( `serviceConfig:
|
|
141
|
+
VERSION: v1.2.3
|
|
142
|
+
PREEMPTIBLE: true` )}
|
|
143
|
+
|
|
144
|
+
are pre-ignition era charts and should be replaced with the latest helm chart
|
|
145
|
+
templates available from ignition. Either dive in or ask devops for a hand
|
|
146
|
+
when converting these legacy / deprecated charts.
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
More recently updated helm charts that have already started the migration to
|
|
150
|
+
the new approach may already have registry entries like this:
|
|
151
|
+
|
|
152
|
+
${chalk.yellow.bold( `image:
|
|
153
|
+
registry: gcr.io/leverege-docker-images
|
|
154
|
+
tag: ""` )}
|
|
155
|
+
|
|
156
|
+
The above registry statement references the deprecated container registry and
|
|
157
|
+
must be adjusted accordingly to resemble:
|
|
158
|
+
|
|
159
|
+
${chalk.green.bold( 'registry: us-docker.pkg.dev/leverege-registry/<registry folder>/images' )}
|
|
160
|
+
|
|
161
|
+
There may also be a corresponding modification needed in the helm template
|
|
162
|
+
deployment.yaml file in the container image spec:
|
|
163
|
+
|
|
164
|
+
${chalk.green.bold( 'image: {{ .Values.image.registry }}/{{ .Chart.Name }}:{{ default .Chart.AppVersion .Values.image.tag }}' )}
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
${chalk.magenta.bold( `Upgrading all service charts to the latest ignition template chart structure
|
|
168
|
+
is the preferred approach to making the transition to the artifact registry.` )}
|
|
169
|
+
|
|
170
|
+
` )
|
|
171
|
+
log( chalk.green.bold( '*** docker-to-registry-py guidance complete - ready for dockerization ***' ) )
|
|
172
|
+
process.exit( 0 )
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// do chart checks too
|
|
176
|
+
let chart
|
|
177
|
+
if ( options?.helmChecks !== 'false' ) {
|
|
178
|
+
chart = helmChart?.yaml['Chart.yaml']
|
|
179
|
+
const values = helmChart?.yaml['values.yaml']
|
|
180
|
+
const expectedChartRegistry = `${artifactRegistry}/images`
|
|
181
|
+
|
|
182
|
+
if ( expectedChartRegistry !== values.image?.registry ) {
|
|
183
|
+
log( `
|
|
184
|
+
${chalk.red.bold( '***Error: mismatched package.json registry and helm/values.yaml' )}
|
|
185
|
+
|
|
186
|
+
The registry specified in the package.json leverege stanza does not line up
|
|
187
|
+
with the image registry in helm/values.yaml. The naming convention expects
|
|
188
|
+
the '/images' suffix to be added to the package.json registry string and then
|
|
189
|
+
stored as the image.registry in the helm/values.yaml file.
|
|
190
|
+
|
|
191
|
+
Current settings:
|
|
192
|
+
package.json registry => ${chalk.yellow.bold( artifactRegistry )}
|
|
193
|
+
values.yaml registry => ${chalk.red.bold( values.image?.registry )}
|
|
194
|
+
|
|
195
|
+
Expected settings:
|
|
196
|
+
values.yaml registry => ${chalk.green.bold( expectedChartRegistry )}
|
|
197
|
+
` )
|
|
198
|
+
|
|
199
|
+
if ( !values.image ) {
|
|
200
|
+
log( `
|
|
201
|
+
${chalk.yellow.bold( '***DEPRECATED: legacy helm charts detected' )}
|
|
202
|
+
|
|
203
|
+
The helm chart structure appears to be based on the pre-ignition helm chart
|
|
204
|
+
layouts. The charts must be upgraded before docker-to-registry-py can complete
|
|
205
|
+
its job.
|
|
206
|
+
|
|
207
|
+
` )
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
process.exit( 1 )
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Give the summary and the user a chance to proceed or not
|
|
215
|
+
log( `
|
|
216
|
+
${chalk.green.bold( 'Build Information:' )}
|
|
217
|
+
Container: ${chalk.green.bold( containerName )}
|
|
218
|
+
Version: ${chalk.green.bold( imageVersion )} ${tagInfo}
|
|
219
|
+
Registry: ${chalk.green.bold( artifactRegistry )}
|
|
220
|
+
Image Folder: ${chalk.yellow.bold( registryFolder )}
|
|
221
|
+
|
|
222
|
+
${chalk.green.bold( 'Docker Information:' )}
|
|
223
|
+
NodeImage: ${chalk.green.bold( dockerInfo.imageBase || `node:${dockerInfo.nodeimage}` )}
|
|
224
|
+
Docker Env: ${chalk.green.bold( dockerInfo.buildEnv || 'alpine' )}
|
|
225
|
+
AddedPkgs: ${chalk.green.bold( dockerInfo.apkadds )}
|
|
226
|
+
Run User: ${chalk.green.bold( dockerInfo.runuser )}
|
|
227
|
+
Previous: ${chalk.yellow.bold( dockerInfo.previousBuild )}
|
|
228
|
+
DateStamp: ${chalk.green.bold( dockerInfo.date )}
|
|
229
|
+
NPM Logs: ${chalk.green.bold( dockerInfo.npmlogging )}
|
|
230
|
+
NPM Version: ${chalk.green.bold( dockerInfo.npmVersion )}
|
|
231
|
+
` )
|
|
232
|
+
|
|
233
|
+
if ( imageVersion !== packageVersion ) {
|
|
234
|
+
log( `
|
|
235
|
+
${chalk.yellow.bold( '***WARNING: specified version does not match project version' )}
|
|
236
|
+
pyproject.toml => ${chalk.green.bold( packageVersion )}
|
|
237
|
+
specified => ${chalk.red.bold( imageVersion )}
|
|
238
|
+
` )
|
|
239
|
+
|
|
240
|
+
if ( willGitTag ) {
|
|
241
|
+
log( chalk.red.bold( '... and you are attempting to release so NOPE!\n' ) )
|
|
242
|
+
process.exit( 1 )
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const repoInfo = await getGitBranchAndUpstream()
|
|
247
|
+
|
|
248
|
+
if ( willGitTag && !repoInfo.upstream ) {
|
|
249
|
+
log( `
|
|
250
|
+
${chalk.red.bold( '***ERROR: the current branch must have an upstream' )}
|
|
251
|
+
|
|
252
|
+
You are attempting to create a tagged release image for pushing to the
|
|
253
|
+
container registry, but the current branch does not have an upstream to
|
|
254
|
+
push to. This is not allowed since the applied tag will be stranded here
|
|
255
|
+
in your local repository, which is what we are trying to avoid.
|
|
256
|
+
|
|
257
|
+
Available options for proceeding:` )
|
|
258
|
+
log( chalk.green.bold( `
|
|
259
|
+
1) set an upstream for this branch via:
|
|
260
|
+
git push --set-upstream origin ${repoInfo.branch}` ) )
|
|
261
|
+
log( chalk.yellow.bold( `
|
|
262
|
+
2) rebase, squash and merge onto a branch with an upstream` ) )
|
|
263
|
+
|
|
264
|
+
process.exit( 1 )
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if ( willGitTag ) {
|
|
268
|
+
if ( chart.appVersion !== imageVersion ) {
|
|
269
|
+
log( `
|
|
270
|
+
${chalk.yellow.bold( '***WARNING: the helm/Chart.yaml appVersion does not align with the build version' )}
|
|
271
|
+
chart appVersion => ${chalk.yellow.bold( chart.appVersion )}
|
|
272
|
+
build version => ${chalk.green.bold( imageVersion )}
|
|
273
|
+
` )
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
await proceed()
|
|
278
|
+
|
|
279
|
+
log( chalk.green.bold( 'Building the Docker Image...' ) )
|
|
280
|
+
await Docker.buildContainerImage( { ...repoDescr, options, imageVersion } )
|
|
281
|
+
log( chalk.blue.bold( 'Done Building the Docker Image...' ) )
|
|
282
|
+
|
|
283
|
+
if ( willGitTag ) {
|
|
284
|
+
const tagDescription = `docker_${imageVersion}`
|
|
285
|
+
const fullImageTag = repoDescr.isMonoRepo ? `${repoDescr.containerName}/${imageVersion}` : imageVersion
|
|
286
|
+
log( chalk.green.bold( `Tagging and Pushing ${fullImageTag}\n` ) )
|
|
287
|
+
await shellCmd( `git tag -f ${fullImageTag} -m ${tagDescription}` )
|
|
288
|
+
await shellCmd( 'git push --follow-tags' )
|
|
289
|
+
} else {
|
|
290
|
+
log( chalk.yellow.bold( '\n*** Skipped git tagging - beta release' ) )
|
|
291
|
+
}
|
|
@@ -96,6 +96,8 @@ if ( semver.lt( semver.coerce( repoDescr.buildToolsVersion ), minimumBuildToolsV
|
|
|
96
96
|
|
|
97
97
|
const dockerInfo = await docker.generateDockerfile( {
|
|
98
98
|
regvers : repoDescr.packageVersion,
|
|
99
|
+
imageBase : repoDescr.closestPackageJson.leverege?.imageBase,
|
|
100
|
+
buildEnv : repoDescr.closestPackageJson.leverege?.buildEnv || 'alpine',
|
|
99
101
|
nodeimage : repoDescr.closestPackageJson.leverege?.nodeimg, // left as nodeimg in package.json for backwards compat
|
|
100
102
|
} )
|
|
101
103
|
debug( { dockerInfo }, '<==The Docker Info' )
|
|
@@ -216,7 +218,8 @@ log( `
|
|
|
216
218
|
NPM Workspace: ${chalk.green.bold( isNpmWorkspace )}
|
|
217
219
|
|
|
218
220
|
${chalk.green.bold( 'Docker Information:' )}
|
|
219
|
-
NodeImage: ${chalk.green.bold( dockerInfo.nodeimage )}
|
|
221
|
+
NodeImage: ${chalk.green.bold( dockerInfo.imageBase || `node:${dockerInfo.nodeimage}` )}
|
|
222
|
+
Docker Env: ${chalk.green.bold( dockerInfo.buildEnv || 'alpine' )}
|
|
220
223
|
AddedPkgs: ${chalk.green.bold( dockerInfo.apkadds )}
|
|
221
224
|
Run User: ${chalk.green.bold( dockerInfo.runuser )}
|
|
222
225
|
Previous: ${chalk.yellow.bold( dockerInfo.previousBuild )}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
alias h=history
|
|
4
|
+
|
|
5
|
+
alias ls='ls -CF --color=auto'
|
|
6
|
+
alias ll='ls -lh'
|
|
7
|
+
alias lla='ls -lha'
|
|
8
|
+
alias glep='grep -l -s'
|
|
9
|
+
alias m=less
|
|
10
|
+
alias menv='env | sort | less'
|
|
11
|
+
|
|
12
|
+
alias whatsmyip='wget -qO- ifconfig.co'
|
|
13
|
+
|
|
14
|
+
alias err='wget -q -O- localhost:5111/logLevel/error'
|
|
15
|
+
alias wrn='wget -q -O- localhost:5111/logLevel/warn'
|
|
16
|
+
alias inf='wget -q -O- localhost:5111/logLevel/info'
|
|
17
|
+
alias dbg='wget -q -O- localhost:5111/logLevel/debug'
|
|
18
|
+
alias trc='wget -q -O- localhost:5111/logLevel/trace'
|
|
19
|
+
|
|
20
|
+
socks()
|
|
21
|
+
{
|
|
22
|
+
netstat -ant | awk '{print }' | sort | uniq -c | sort -n
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
cmetrics()
|
|
26
|
+
{
|
|
27
|
+
wget -qO- localhost:5111/metrics
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
cmstat()
|
|
31
|
+
{
|
|
32
|
+
wget -qO- localhost:5111
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
cmclear()
|
|
36
|
+
{
|
|
37
|
+
# Future me - thinking this should call cmstat and pass in the endpoint as
|
|
38
|
+
# a parameter? Well don't do it - without escaping the dollar 1 in cmstat
|
|
39
|
+
# the port will end up being 51111, and escaping causes linting to complain
|
|
40
|
+
# about unnecessary escaping.
|
|
41
|
+
wget -qO- localhost:5111/status/clear
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
dunm() {
|
|
45
|
+
du -sh /usr/src/app/node_modules/* | sort -sh
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export PYTHONPYCACHEPREFIX=/tmp/pycache
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
steps:
|
|
2
|
+
- name: 'gcr.io/cloud-builders/docker'
|
|
3
|
+
args:
|
|
4
|
+
- 'build'
|
|
5
|
+
- '--network=cloudbuild'
|
|
6
|
+
- '-t'
|
|
7
|
+
- '{{imageName}}:{{imageVersion}}'
|
|
8
|
+
- '--cache-from'
|
|
9
|
+
- '{{imageName}}:{{imageVersion}}'
|
|
10
|
+
- '.'
|
|
11
|
+
|
|
12
|
+
images:
|
|
13
|
+
- '{{imageName}}:{{imageVersion}}'
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Version {{regvers}} @ {{date}}
|
|
2
|
+
|
|
3
|
+
# The FROM directive sets the Base Image for subsequent instructions
|
|
4
|
+
FROM {{image}} as intermediate
|
|
5
|
+
|
|
6
|
+
ARG BUILD_ENV="{{buildEnv}}"
|
|
7
|
+
|
|
8
|
+
ENV NODE_ENV production
|
|
9
|
+
ENV NPM_CONFIG_USERCONFIG /usr/src/app/.npmrc
|
|
10
|
+
|
|
11
|
+
RUN mkdir -p /usr/src/app
|
|
12
|
+
WORKDIR /usr/src/app
|
|
13
|
+
|
|
14
|
+
# Install app dependencies
|
|
15
|
+
COPY ./workspace/ /usr/src/app/
|
|
16
|
+
ENV GRPC_VERBOSITY ERROR
|
|
17
|
+
|
|
18
|
+
# --------------------------------------------------------------
|
|
19
|
+
# copy the ssh keys into place, npm install, and remove them
|
|
20
|
+
# --------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
# Install packages to install private repos with ssh keys
|
|
23
|
+
COPY ./.npmrc ${NPM_CONFIG_USERCONFIG}
|
|
24
|
+
|
|
25
|
+
RUN if [ "${BUILD_ENV}" = "debian" ]; then \
|
|
26
|
+
apt-get update && \
|
|
27
|
+
apt-get install -y wget openssh-client build-essential && \
|
|
28
|
+
npm install -g npm@10; \
|
|
29
|
+
else \
|
|
30
|
+
apk --no-cache add openssh-client && \
|
|
31
|
+
apk --update add --no-cache --virtual build-dep g++ gcc libgcc libstdc++ linux-headers make {{apkadds}} && \
|
|
32
|
+
npm install -g npm@10; \
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Use SKIP_PREPARE to disable hook-and-release from running
|
|
36
|
+
ENV SKIP_PREPARE=true
|
|
37
|
+
|
|
38
|
+
{{preInstallPluginfile}}
|
|
39
|
+
|
|
40
|
+
# Do the clean install and remove the npm token and ssh keys from the image
|
|
41
|
+
RUN npm ci --only=production --no-optional {{npmlogging}} && \
|
|
42
|
+
rm -f ${NPM_CONFIG_USERCONFIG} /root/.ssh/*
|
|
43
|
+
|
|
44
|
+
# --------------------------------------------------------------
|
|
45
|
+
# Docker Final Stage
|
|
46
|
+
# --------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
FROM {{image}}
|
|
49
|
+
|
|
50
|
+
ARG BUILD_ENV="{{buildEnv}}"
|
|
51
|
+
|
|
52
|
+
ENV NPM_CONFIG_USERCONFIG /usr/src/app/.npmrc
|
|
53
|
+
|
|
54
|
+
# Copy .npmrc for private registry access for access to any @leverege packages
|
|
55
|
+
COPY ./.npmrc ${NPM_CONFIG_USERCONFIG}
|
|
56
|
+
|
|
57
|
+
# Install tini for PID 1 and replace shell with bash so we can source files
|
|
58
|
+
RUN if [ "$BUILD_ENV" = "debian" ]; then \
|
|
59
|
+
apt-get update && \
|
|
60
|
+
apt-get install -y tini bash curl vim {{apkadds}} && \
|
|
61
|
+
npm install -g npm@10 && \
|
|
62
|
+
rm /bin/sh && ln -s /bin/bash /bin/sh && \
|
|
63
|
+
mkdir -p /usr/src/app /tmp/levlog && chown node:node /usr/src/app; \
|
|
64
|
+
else \
|
|
65
|
+
apk update && \
|
|
66
|
+
apk add --no-cache bash curl tini vim {{apkadds}} && \
|
|
67
|
+
npm install -g npm@10 && \
|
|
68
|
+
rm /bin/sh && ln -s /bin/bash /bin/sh && \
|
|
69
|
+
mkdir -p /usr/src/app /tmp/levlog && chown node:node /usr/src/app; \
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# Pull in the Dockerfile.plugin file here
|
|
74
|
+
{{pluginfile}}
|
|
75
|
+
|
|
76
|
+
# Eliminate the sensitive info from the final stage image
|
|
77
|
+
RUN rm -f ${NPM_CONFIG_USERCONFIG}
|
|
78
|
+
|
|
79
|
+
ENTRYPOINT [ "tini", "--" ]
|
|
80
|
+
WORKDIR /usr/src/app
|
|
81
|
+
|
|
82
|
+
COPY --chown=node:node --from=intermediate /usr/src/app /usr/src/app
|
|
83
|
+
|
|
84
|
+
USER {{runuser}}
|
|
85
|
+
COPY ./bashrc /home/node/.bashrc
|
|
86
|
+
CMD [ "/bin/bash", "-c", "source /home/node/.bashrc && node index.js" ]
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Version {{regvers}} @ {{date}}
|
|
2
|
+
|
|
3
|
+
# The FROM directive sets the Base Image for subsequent instructions
|
|
4
|
+
FROM {{image}} as intermediate
|
|
5
|
+
|
|
6
|
+
ARG BUILD_ENV="{{buildEnv}}"
|
|
7
|
+
|
|
8
|
+
RUN apt-get update && apt-get install -y \
|
|
9
|
+
wget
|
|
10
|
+
# gcc \
|
|
11
|
+
# git \
|
|
12
|
+
# libgl1 \
|
|
13
|
+
# libglib2.0-0 \
|
|
14
|
+
# && rm -rf /var/lib/apt/lists/*
|
|
15
|
+
|
|
16
|
+
# Copy uv binary from the official distroless Docker image.
|
|
17
|
+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
|
|
18
|
+
|
|
19
|
+
# Define the working directory.
|
|
20
|
+
WORKDIR /usr/src/app
|
|
21
|
+
|
|
22
|
+
#ENV NODE_ENV production
|
|
23
|
+
#ENV NPM_CONFIG_USERCONFIG /usr/src/app/.npmrc
|
|
24
|
+
|
|
25
|
+
RUN mkdir -p /usr/src/app
|
|
26
|
+
WORKDIR /usr/src/app
|
|
27
|
+
|
|
28
|
+
# Install app dependencies
|
|
29
|
+
COPY ./workspace/ /usr/src/app/
|
|
30
|
+
ENV GRPC_VERBOSITY ERROR
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Setup pip.conf for Google Artifact Registry.
|
|
34
|
+
RUN mkdir -p ~/.config/pip
|
|
35
|
+
RUN echo "[global]" >> ~/.config/pip/pip.conf && \
|
|
36
|
+
echo "break-system-packages = true" >> ~/.config/pip/pip.conf
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Set up the virtual environment.
|
|
40
|
+
RUN uv venv
|
|
41
|
+
ENV VIRTUAL_ENV=/usr/src/app/.venv
|
|
42
|
+
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
|
43
|
+
|
|
44
|
+
# Setup keyring for Google Artifact Registry.
|
|
45
|
+
RUN uv pip install keyring
|
|
46
|
+
RUN uv pip install keyrings.google-artifactregistry-auth
|
|
47
|
+
|
|
48
|
+
RUN echo "extra-index-url = https://oauth2accesstoken@us-python.pkg.dev/leverege-registry/leverege-python-packages/simple" >> ~/.config/pip/pip.conf
|
|
49
|
+
|
|
50
|
+
ENV UV_EXTRA_INDEX_URL "https://oauth2accesstoken@us-python.pkg.dev/leverege-registry/leverege-python-packages/simple"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
{{preInstallPluginfile}}
|
|
54
|
+
|
|
55
|
+
RUN cat
|
|
56
|
+
|
|
57
|
+
RUN ls -al
|
|
58
|
+
# Install uv
|
|
59
|
+
RUN uv sync --keyring-provider subprocess --extra-index-url https://oauth2accesstoken@us-python.pkg.dev/leverege-registry/leverege-python-packages/simple
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# Pull in the Dockerfile.plugin file here
|
|
63
|
+
{{pluginfile}}
|
|
64
|
+
|
|
65
|
+
# Eliminate the sensitive info from the final stage image
|
|
66
|
+
#RUN rm -f ${NPM_CONFIG_USERCONFIG}
|
|
67
|
+
|
|
68
|
+
# ENTRYPOINT [ "tini", "--" ]
|
|
69
|
+
#WORKDIR /usr/src/app
|
|
70
|
+
|
|
71
|
+
#COPY --chown=node:node --from=intermediate /usr/src/app /usr/src/app
|
|
72
|
+
|
|
73
|
+
USER {{runuser}}
|
|
74
|
+
COPY ./bashrc /home/{{runuser}}/.bashrc
|
|
75
|
+
CMD [ "/bin/bash", "-c", "source /home/{{runuser}}/.bashrc && python scripts/server.py" ]
|