@leverege/build-tools 2.99.2-quinn.1 → 2.100.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.
Files changed (45) hide show
  1. package/package.json +10 -8
  2. package/src/circle-orb-it/circle-orb-it.mjs +178 -0
  3. package/src/circle-orb-it/circle-orb.yml +27 -0
  4. package/src/circle-orb-it/dependabot.yml +7 -0
  5. package/src/dashboard/DashboardManager.mjs +126 -0
  6. package/src/dashboard/dashboard.mjs +36 -0
  7. package/src/dashboard/json/standard/cloudnative-pg-20417-rev4.json +8827 -0
  8. package/src/dashboard/json/standard/elasticsearch8.json +6404 -0
  9. package/src/dashboard/json/standard/gpu-autoscaling-dashboard-ui-import.json +319 -0
  10. package/src/dashboard/json/standard/k8s-api-latency.json +166 -0
  11. package/src/dashboard/json/standard/noc-rows-12.json +1241 -0
  12. package/src/dashboard/json/standard/pvc-all-usage.json +943 -0
  13. package/src/dashboard/json/standard/redis.json +1513 -0
  14. package/src/dashboard/json/standard/stackdriver.json +624 -0
  15. package/src/dashboard/json/standard/traefik-op.json +1575 -0
  16. package/src/dashboard/json/standard/valkey.json +2373 -0
  17. package/src/helm-charts/eso/helmup.bootstrap +59 -0
  18. package/src/helm-charts/eso/helmup.plugin +34 -0
  19. package/src/helm-charts/prom-operator/expand-pvc.sh +54 -0
  20. package/src/helm-charts/prom-operator/helmup.plugin +1 -1
  21. package/src/helm-charts/prom-operator/prometheus-stack.yaml.ovh +4 -2
  22. package/leverege-build-tools-2.21.13.tgz +0 -0
  23. package/lib/server/build-tools.js +0 -138
  24. package/lib/server/firebaseDeploy.js +0 -195
  25. package/lib/server/firebaseServe.js +0 -116
  26. package/lib/server/getfbcfg.js +0 -25
  27. package/lib/server/getjson.js +0 -70
  28. package/lib/server/overwhelm.js +0 -447
  29. package/lib/server/push-my-chart.js +0 -18
  30. package/lib/server/refresh-npm-token.js +0 -135
  31. package/lib/server/tag-release.js +0 -681
  32. package/lib/server/unleash.js +0 -50
  33. package/lib/web/build-tools.js +0 -138
  34. package/lib/web/firebaseDeploy.js +0 -195
  35. package/lib/web/firebaseServe.js +0 -116
  36. package/lib/web/getfbcfg.js +0 -25
  37. package/lib/web/getjson.js +0 -70
  38. package/lib/web/overwhelm.js +0 -447
  39. package/lib/web/push-my-chart.js +0 -18
  40. package/lib/web/refresh-npm-token.js +0 -135
  41. package/lib/web/tag-release.js +0 -681
  42. package/lib/web/unleash.js +0 -50
  43. package/src/circle-orb/circle-orb.mjs +0 -55
  44. package/src/circle-orb/circle-orb.yml +0 -24
  45. package/src/circleate.sh +0 -599
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leverege/build-tools",
3
- "version": "2.99.2-quinn.1",
3
+ "version": "2.100.0",
4
4
  "description": "A collection of build / support tools for Leverege developers",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -16,18 +16,20 @@
16
16
  "prepack": "npm exec prepack"
17
17
  },
18
18
  "bin": {
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",
21
22
  "change-up": "src/change-up.sh",
22
23
  "chart-compass": "src/chart-compass.mjs",
23
24
  "chart-to-registry": "src/chart-to-registry.mjs",
24
- "circleate": "src/circleate.sh",
25
+ "circle-orb-it": "src/circle-orb-it/circle-orb-it.mjs",
25
26
  "clone-cnpg-from-snapshot": "src/clone-cnpg-from-snapshot.mjs",
26
27
  "count-lines-of-code": "src/count-lines-of-code.mjs",
27
28
  "decrypt-secrets": "src/decrypt-secrets.sh",
28
29
  "dirty-git": "src/dirty-git.sh",
29
30
  "docker-to-registry": "src/docker-to-registry.mjs",
30
31
  "docker-to-registry-py": "src/docker-to-registry-py.mjs",
32
+ "docker-to-registry.sh": "src/docker-to-registry.sh",
31
33
  "dockreate": "src/dockreate.sh",
32
34
  "encrypt-secrets": "src/encrypt-secrets.sh",
33
35
  "firebaseDeploy": "src/firebaseDeploy.mjs",
@@ -45,6 +47,7 @@
45
47
  "helmup": "src/helmup.sh",
46
48
  "helmwhat": "src/helmup.sh",
47
49
  "hook-and-release": "src/hook-and-release.mjs",
50
+ "dashboard": "src/dashboard/dashboard.mjs",
48
51
  "init-my-chart": "src/init-my-chart.mjs",
49
52
  "k8scale": "src/k8scale.sh",
50
53
  "k8x": "src/k8x.sh",
@@ -77,11 +80,10 @@
77
80
  },
78
81
  "license": "SEE LICENSE IN LICENSE.md",
79
82
  "dependencies": {
83
+ "@kubernetes/client-node": "^1.4.0",
80
84
  "@leverege/jsdoc-template": "^1.0.1",
81
- "@yarnpkg/core": "^4.5.0",
82
- "@yarnpkg/fslib": "^3.1.4",
83
85
  "chalk": "^5.6.2",
84
- "command-line-args": "^6.0.1",
86
+ "command-line-args": "^6.0.2",
85
87
  "command-line-usage": "^7.0.4",
86
88
  "commander": "^14.0.3",
87
89
  "deepmerge": "^4.3.1",
@@ -90,7 +92,7 @@
90
92
  "find-yarn-workspace-root": "^2.0.0",
91
93
  "glob": "^13.0.6",
92
94
  "googleapis": "^171.4.0",
93
- "handlebars": "^4.7.8",
95
+ "handlebars": "^4.7.9",
94
96
  "ignore": "^7.0.5",
95
97
  "inquirer": "^13.3.2",
96
98
  "js-yaml": "^4.1.1",
@@ -108,13 +110,13 @@
108
110
  "superstruct": "^2.0.2",
109
111
  "tmp": "^0.2.5",
110
112
  "toml": "^3.0.0",
111
- "yaml": "^2.8.2",
113
+ "yaml": "^2.8.3",
112
114
  "zx": "^8.8.5"
113
115
  },
114
116
  "devDependencies": {
115
117
  "@leverege/eslint-config-leverege": "^5.1.2",
116
118
  "chai": "^6.2.2",
117
119
  "mocha": "^11.7.5",
118
- "npm": "^11.11.1"
120
+ "npm": "^11.12.1"
119
121
  }
120
122
  }
@@ -0,0 +1,178 @@
1
+ #!/usr/bin/env node
2
+ /* eslint-disable security/detect-non-literal-fs-filename */
3
+ import fs from 'node:fs'
4
+ import path from 'node:path'
5
+
6
+ import { program } from 'commander'
7
+ import chalk from 'chalk'
8
+
9
+ import {
10
+ BUILD_TOOLS_ROOT,
11
+ enableDebug,
12
+ errorExit,
13
+ getGitRootDirectory,
14
+ log,
15
+ proceed,
16
+ } from '../Utils.mjs'
17
+
18
+ const ORB_VERSION = '1.2.4' // Keep this in sync with the version in circle-orb.yml
19
+ const CIRCLE_DIR = '.circleci'
20
+ const CIRCLE_YML = `${CIRCLE_DIR}/config.yml`
21
+ const CIRCLE_YML_TEMPLATE = path.join( BUILD_TOOLS_ROOT, 'circle-orb-it', 'circle-orb.yml' )
22
+ const GITHUB_DIR = '.github'
23
+ const DEPENDABOT_FILE = `${GITHUB_DIR}/dependabot.yml`
24
+ const DEPENDABOT_TEMPLATE = path.join( BUILD_TOOLS_ROOT, 'circle-orb-it', 'dependabot.yml' )
25
+
26
+ program
27
+ .name( 'circle-orb-it' )
28
+ .description( chalk.green( 'Installs or updates CI and GitHub configuration files for a Leverege repository' ) )
29
+ .option( '--debug', 'Enable debug logging' )
30
+ .option( '--dry-run', 'Show what would be written without making changes' )
31
+ .addHelpText( 'after', `
32
+ ${chalk.yellow( 'Files managed:' )}
33
+ ${chalk.green( CIRCLE_YML )} leverege orb v${ORB_VERSION}
34
+ ${chalk.green( DEPENDABOT_FILE )} weekly npm dependency updates
35
+
36
+ ${chalk.yellow( 'Notes:' )}
37
+ Run from anywhere inside a git repository.
38
+ Python projects are not supported by the leverege orb.
39
+ The dependabot config is skipped if it already exists.
40
+
41
+ ${chalk.yellow( 'Examples:' )}
42
+ ${chalk.green( 'circle-orb-it' )} install or upgrade
43
+ ${chalk.green( 'circle-orb-it --dry-run' )} preview without writing files
44
+ ` )
45
+ .parse()
46
+
47
+ const options = program.opts()
48
+ if ( options.debug ) { enableDebug() }
49
+
50
+ const gitRoot = await getGitRootDirectory()
51
+ if ( !gitRoot ) {
52
+ errorExit( 'This command must be run inside a git repository' )
53
+ }
54
+
55
+ // Warn if this looks like a Python project since the orb is Node.js only
56
+ if ( fs.existsSync( path.join( gitRoot, 'pyproject.toml' ) ) ) {
57
+ log( chalk.yellow( '\n***WARNING: pyproject.toml detected - the leverege orb is Node.js only' ) )
58
+ await proceed( 'Continue anyway?' )
59
+ }
60
+
61
+ // ---------- Circle CI ----------
62
+
63
+ const circleDir = path.join( gitRoot, CIRCLE_DIR )
64
+ const circleYml = path.join( gitRoot, CIRCLE_YML )
65
+
66
+ let writeCircleYml = true
67
+ let redisEnabled = false
68
+ let githubTeam = null
69
+
70
+ if ( fs.existsSync( circleYml ) ) {
71
+ const existing = fs.readFileSync( circleYml, 'utf-8' )
72
+ const alreadyUsesOrb = existing.includes( 'leverege/circle-nodejs-ci' )
73
+
74
+ redisEnabled = existing.includes( 'enable-redis: true' )
75
+ if ( redisEnabled ) {
76
+ log( chalk.yellow( '\nRedis detected in existing config - will be preserved' ) )
77
+ }
78
+
79
+ const githubTeamMatch = existing.match( /github-team:\s+(\S+)/ )
80
+ githubTeam = githubTeamMatch ? githubTeamMatch[1] : null
81
+ if ( githubTeam ) {
82
+ log( chalk.yellow( `\ngithub-team "${githubTeam}" detected in existing config - will be preserved` ) )
83
+ }
84
+
85
+ if ( alreadyUsesOrb ) {
86
+ if ( existing.includes( `circle-nodejs-ci@${ORB_VERSION}` ) ) {
87
+ log( chalk.green( `\nCircleCI already using leverege/circle-nodejs-ci@${ORB_VERSION} - skipping` ) )
88
+ writeCircleYml = false
89
+ } else {
90
+ log( chalk.yellow( `\nExisting orb config found - will update to @${ORB_VERSION}` ) )
91
+ }
92
+ } else {
93
+ log( chalk.yellow( '\nLegacy CircleCI config detected - will replace with orb-based config' ) )
94
+ }
95
+
96
+ if ( writeCircleYml ) {
97
+ await proceed( `Overwrite ${CIRCLE_YML}?` )
98
+ }
99
+ }
100
+
101
+ // ---------- Dependabot ----------
102
+
103
+ const githubDir = path.join( gitRoot, GITHUB_DIR )
104
+ const dependabotPath = path.join( gitRoot, DEPENDABOT_FILE )
105
+
106
+ const writeDependabot = !fs.existsSync( dependabotPath )
107
+ if ( !writeDependabot ) {
108
+ log( chalk.green( '\nDependabot config already exists - skipping' ) )
109
+ }
110
+
111
+ // ---------- Nothing to do ----------
112
+
113
+ if ( !writeCircleYml && !writeDependabot ) {
114
+ log( chalk.green( '\nAll configs are up to date - nothing to do\n' ) )
115
+ process.exit( 0 )
116
+ }
117
+
118
+ // ---------- Dry run ----------
119
+
120
+ if ( options.dryRun ) {
121
+ if ( writeCircleYml ) {
122
+ let dryRunContent = fs.readFileSync( CIRCLE_YML_TEMPLATE, 'utf-8' )
123
+ if ( redisEnabled ) {
124
+ dryRunContent = dryRunContent
125
+ .replace( 'enable-redis: false', 'enable-redis: true' )
126
+ .replace( '\'{"type":"inMemory"}\'', '\'{"type":"redis"}\'' )
127
+ }
128
+ if ( githubTeam ) {
129
+ dryRunContent = dryRunContent
130
+ .replace( / {10}# github-team: \S+.*/, ` github-team: ${githubTeam}` )
131
+ }
132
+ log( chalk.green( `\n[dry-run] Would write ${CIRCLE_YML}:\n` ) )
133
+ log( dryRunContent )
134
+ }
135
+ if ( writeDependabot ) {
136
+ log( chalk.green( `\n[dry-run] Would write ${DEPENDABOT_FILE}:\n` ) )
137
+ log( fs.readFileSync( DEPENDABOT_TEMPLATE, 'utf-8' ) )
138
+ }
139
+ process.exit( 0 )
140
+ }
141
+
142
+ // ---------- Write ----------
143
+
144
+ if ( writeCircleYml ) {
145
+ if ( !fs.existsSync( circleDir ) ) {
146
+ fs.mkdirSync( circleDir, { recursive : true } )
147
+ }
148
+ let circleYmlContent = fs.readFileSync( CIRCLE_YML_TEMPLATE, 'utf-8' )
149
+ if ( redisEnabled ) {
150
+ circleYmlContent = circleYmlContent
151
+ .replace( 'enable-redis: false', 'enable-redis: true' )
152
+ .replace( '\'{"type":"inMemory"}\'', '\'{"type":"redis"}\'' )
153
+ log( chalk.yellow( 'Redis settings preserved in new config' ) )
154
+ }
155
+ if ( githubTeam ) {
156
+ circleYmlContent = circleYmlContent
157
+ .replace( / {10}# github-team: \S+.*/, ` github-team: ${githubTeam}` )
158
+ log( chalk.yellow( `github-team "${githubTeam}" preserved in new config` ) )
159
+ }
160
+ fs.writeFileSync( circleYml, circleYmlContent )
161
+ log( chalk.green( `\nCircleCI config written => ${circleYml}` ) )
162
+ }
163
+
164
+ if ( writeDependabot ) {
165
+ if ( !fs.existsSync( githubDir ) ) {
166
+ fs.mkdirSync( githubDir, { recursive : true } )
167
+ }
168
+ fs.writeFileSync( dependabotPath, fs.readFileSync( DEPENDABOT_TEMPLATE, 'utf-8' ) )
169
+ log( chalk.green( `Dependabot config written => ${dependabotPath}` ) )
170
+ }
171
+
172
+ const filesToAdd = [
173
+ writeCircleYml ? CIRCLE_YML : null,
174
+ writeDependabot ? DEPENDABOT_FILE : null,
175
+ ].filter( Boolean ).join( ' ' )
176
+
177
+ log( chalk.yellow( '\nNext steps:' ) )
178
+ log( ` git add ${filesToAdd} && git commit -m "CHORE migrate to leverege orb ${ORB_VERSION}"\n` )
@@ -0,0 +1,27 @@
1
+ version: 2.1
2
+
3
+ orbs:
4
+ leverege: leverege/circle-nodejs-ci@1.2.4
5
+
6
+ workflows:
7
+ build-and-verify:
8
+ jobs:
9
+ - leverege/install-dependencies:
10
+ context: npm
11
+ # pkg-manager: yarn-berry # uncomment for Yarn Berry repos
12
+ - leverege/code-analysis:
13
+ requires:
14
+ - leverege/install-dependencies
15
+ - leverege/code-coverage:
16
+ coverage-threshold: 70 # percentage threshold for functions, lines, and statements
17
+ context: leverege-gcp # provides GCP_SA_KEY_JSON for tests that need GCP access
18
+ enable-redis: false # set to true if tests require a live Redis connection
19
+ cache-config: '{"type":"inMemory"}' # sets CACHE_CONFIG env var - options: inMemory, redis
20
+ requires:
21
+ - leverege/install-dependencies
22
+ - leverege/generate-docs:
23
+ requires:
24
+ - leverege/install-dependencies
25
+ - leverege/mirror-to-github:
26
+ context: github-mirror
27
+ # github-team: platform # available teams: platform, utilities, pitcrew, recovr
@@ -0,0 +1,7 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "npm"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ open-pull-requests-limit: 10
@@ -0,0 +1,126 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+
4
+ import * as k8s from '@kubernetes/client-node'
5
+ import chalk from 'chalk'
6
+
7
+ import { debug, errorExit, log } from '../Utils.mjs'
8
+
9
+ // const FIELD_MANAGER = 'dashboard'
10
+ const GRAFANA_LABEL = 'grafana_dashboard'
11
+ const GRAFANA_FOLDER = 'grafana_folder'
12
+
13
+ /* eslint-disable security/detect-non-literal-fs-filename */
14
+ export default class DashboardManager {
15
+ #api
16
+ #namespace
17
+ #dryRun
18
+
19
+ constructor( { namespace = 'prometheus', dryRun = false } = {} ) {
20
+ this.#namespace = namespace
21
+ this.#dryRun = dryRun
22
+ }
23
+
24
+ #getApi() {
25
+ if ( !this.#api ) {
26
+ const kc = new k8s.KubeConfig()
27
+ kc.loadFromDefault()
28
+ this.#api = kc.makeApiClient( k8s.CoreV1Api )
29
+ }
30
+ return this.#api
31
+ }
32
+
33
+ async apply( dir ) {
34
+ const folders = fs.readdirSync( dir, { withFileTypes : true } )
35
+ .filter( e => e.isDirectory() )
36
+
37
+ if ( folders.length === 0 ) {
38
+ log( chalk.yellow( `No dashboard subfolders found in ${dir}` ) )
39
+ return
40
+ }
41
+
42
+ for ( const folder of folders ) {
43
+ const files = fs.readdirSync( path.join( dir, folder.name ) )
44
+ .filter( f => f.endsWith( '.json' ) )
45
+
46
+ for ( const file of files ) {
47
+ await this.#applyDashboard( folder.name, path.join( dir, folder.name, file ) )
48
+ }
49
+ }
50
+ }
51
+
52
+ async list( dir ) {
53
+ if ( !fs.existsSync( dir ) ) {
54
+ errorExit( `Dashboard directory not found: ${dir}` )
55
+ }
56
+
57
+ log( chalk.bold( `Dashboards in: ${dir}` ) )
58
+
59
+ const folders = fs.readdirSync( dir, { withFileTypes : true } )
60
+ .filter( e => e.isDirectory() )
61
+
62
+ if ( folders.length === 0 ) {
63
+ log( chalk.yellow( 'No subfolders found' ) )
64
+ return
65
+ }
66
+
67
+ for ( const folder of folders ) {
68
+ const files = fs.readdirSync( path.join( dir, folder.name ) )
69
+ .filter( f => f.endsWith( '.json' ) )
70
+
71
+ log( chalk.bold( `\n[${folder.name}]` ) )
72
+ if ( files.length === 0 ) {
73
+ log( chalk.dim( ' (empty)' ) )
74
+ } else {
75
+ files.forEach( f => log( ` ${chalk.cyan( path.basename( f, '.json' ) )}` ) )
76
+ }
77
+ }
78
+ }
79
+
80
+ async #applyDashboard( folder, filePath ) {
81
+ const filename = path.basename( filePath )
82
+ const name = `dashboard-${path.basename( filePath, '.json' )}`
83
+ const json = fs.readFileSync( filePath, 'utf8' )
84
+
85
+ const configMap = {
86
+ apiVersion : 'v1',
87
+ kind : 'ConfigMap',
88
+ metadata : {
89
+ name,
90
+ namespace : this.#namespace,
91
+ labels : { [GRAFANA_LABEL] : '1' },
92
+ annotations : { [GRAFANA_FOLDER] : folder },
93
+ },
94
+ data : { [filename] : json },
95
+ }
96
+
97
+ debug( configMap, `dashboard: ${name}` )
98
+
99
+ if ( this.#dryRun ) {
100
+ log( chalk.yellow( ` [dry-run] ${filename} → [${folder}]` ) )
101
+ return
102
+ }
103
+
104
+ const api = this.#getApi()
105
+ try {
106
+ let resourceVersion
107
+ try {
108
+ const existing = await api.readNamespacedConfigMap( { name, namespace : this.#namespace } )
109
+ resourceVersion = existing.metadata.resourceVersion
110
+ } catch {
111
+ // doesn't exist yet — will create below
112
+ }
113
+
114
+ if ( resourceVersion ) {
115
+ configMap.metadata.resourceVersion = resourceVersion
116
+ await api.replaceNamespacedConfigMap( { name, namespace : this.#namespace, body : configMap } )
117
+ } else {
118
+ await api.createNamespacedConfigMap( { namespace : this.#namespace, body : configMap } )
119
+ }
120
+ log( chalk.green( ` ✓ ${filename} → [${folder}]` ) )
121
+ } catch ( e ) {
122
+ errorExit( `Failed to apply ${name}: ${e.message}` )
123
+ }
124
+ }
125
+
126
+ }
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+
3
+ import path from 'node:path'
4
+
5
+ import { program } from 'commander'
6
+ import chalk from 'chalk'
7
+
8
+ import { enableDebug, errorExit, log } from '../Utils.mjs'
9
+
10
+ import DashboardManager from './DashboardManager.mjs'
11
+
12
+ const defaultJsonDir = path.join( import.meta.dirname, 'json' )
13
+
14
+ program
15
+ .name( 'dashboard' )
16
+ .description( 'Manage Grafana dashboards as Kubernetes ConfigMaps' )
17
+ .argument( '[dir]', 'Directory of dashboard subfolders', defaultJsonDir )
18
+ .option( '--list', 'List available dashboards without applying' )
19
+ .option( '--dry-run', 'Show what would be applied without making changes' )
20
+ .option( '--debug', 'Enable debug logging' )
21
+ .option( '-n, --namespace <ns>', 'Target Kubernetes namespace', 'prometheus' )
22
+ .parse()
23
+
24
+ const { list, dryRun, debug, namespace } = program.opts()
25
+ const [ dir = defaultJsonDir ] = program.args
26
+
27
+ if ( debug ) enableDebug()
28
+
29
+ const mgr = new DashboardManager( { namespace, dryRun } )
30
+
31
+ if ( list ) {
32
+ await mgr.list( dir ).catch( e => errorExit( e ) )
33
+ } else {
34
+ log( chalk.bold( `Applying dashboards from: ${dir}` ) )
35
+ await mgr.apply( dir ).catch( e => errorExit( e ) )
36
+ }