configorama 0.9.13 → 0.9.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2063 -389
- package/cli.js +1 -1
- package/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/main.js +17 -19
- package/src/resolvers/valueFromEnv.js +3 -6
- package/src/resolvers/valueFromFile.js +4 -4
- package/src/resolvers/valueFromGit.js +101 -62
- package/src/resolvers/valueFromOptions.js +3 -7
- package/src/resolvers/valueFromParam.js +2 -1
- package/src/utils/lodash.js +19 -19
- package/src/utils/parsing/parse.js +1 -1
- package/src/utils/parsing/preProcess.js +16 -10
- package/src/utils/regex/index.js +1 -9
- package/src/utils/variables/cleanVariable.js +0 -21
- package/types/src/main.d.ts.map +1 -1
- package/types/src/resolvers/valueFromEnv.d.ts +1 -1
- package/types/src/resolvers/valueFromEnv.d.ts.map +1 -1
- package/types/src/resolvers/valueFromGit.d.ts.map +1 -1
- package/types/src/resolvers/valueFromOptions.d.ts.map +1 -1
- package/types/src/resolvers/valueFromParam.d.ts.map +1 -1
- package/types/src/utils/parsing/preProcess.d.ts.map +1 -1
- package/types/src/utils/regex/index.d.ts +3 -6
- package/types/src/utils/regex/index.d.ts.map +1 -1
- package/types/src/utils/variables/cleanVariable.d.ts.map +1 -1
- package/src/resolvers/valueFromSelf.js +0 -0
package/cli.js
CHANGED
package/index.d.ts
CHANGED
package/package.json
CHANGED
package/src/main.js
CHANGED
|
@@ -94,15 +94,15 @@ const deepPrefixReplacePattern = /(?:^deep:)\d+\.?/g
|
|
|
94
94
|
// TODO update file regex ^file\((~?[a-zA-Z0-9._\-\/, ]+?)\)
|
|
95
95
|
// To match file(asyncValue.js, lol) input params
|
|
96
96
|
const selfRefSyntax = RegExp(/^self:/g)
|
|
97
|
-
const base64WrapperRegex = /\[_\[([A-Za-z0-9+/=\s]*)\]_\]/g
|
|
98
97
|
const logLines = '─────────────────────────────────────────────────'
|
|
98
|
+
const evalIfPattern = /\b(eval|if)\s*\(/
|
|
99
|
+
const functionPrefixPattern = /^> function /
|
|
99
100
|
|
|
100
101
|
let DEBUG = process.argv.includes('--debug') ? true : false
|
|
101
102
|
let VERBOSE = process.argv.includes('--verbose') ? true : false
|
|
102
103
|
let SETUP_MODE = process.argv.includes('--setup') ? true : false
|
|
103
104
|
// DEBUG = true
|
|
104
105
|
let DEBUG_TYPE = false
|
|
105
|
-
const ENABLE_FUNCTIONS = true
|
|
106
106
|
|
|
107
107
|
class Configorama {
|
|
108
108
|
constructor(fileOrObject, opts) {
|
|
@@ -562,7 +562,6 @@ class Configorama {
|
|
|
562
562
|
}
|
|
563
563
|
return assign({}, value, otherValue)
|
|
564
564
|
},
|
|
565
|
-
math: () => {},
|
|
566
565
|
upperKeys: (o) => {
|
|
567
566
|
return Object.keys(o).reduce((c, k) => ((c[k.toUpperCase()] = o[k]), c), {}) // eslint-disable-line
|
|
568
567
|
},
|
|
@@ -1235,8 +1234,7 @@ class Configorama {
|
|
|
1235
1234
|
|
|
1236
1235
|
/* Exit early if list or info flag is set */
|
|
1237
1236
|
if (showFoundVariables) {
|
|
1238
|
-
|
|
1239
|
-
// process.exit(0)
|
|
1237
|
+
return Promise.resolve(this.config)
|
|
1240
1238
|
}
|
|
1241
1239
|
}
|
|
1242
1240
|
|
|
@@ -1262,7 +1260,7 @@ class Configorama {
|
|
|
1262
1260
|
}
|
|
1263
1261
|
const stage = cliOpts.stage || providerStage || process.env.NODE_ENV || 'dev'
|
|
1264
1262
|
/* Load env variables into process.env */
|
|
1265
|
-
|
|
1263
|
+
require('env-stage-loader')({
|
|
1266
1264
|
// silent: true,
|
|
1267
1265
|
// debug: true,
|
|
1268
1266
|
env: stage,
|
|
@@ -1311,7 +1309,7 @@ class Configorama {
|
|
|
1311
1309
|
if (typeof rawValue === 'string') {
|
|
1312
1310
|
// console.log('rawValue', rawValue)
|
|
1313
1311
|
/* Process inline functions like merge() */
|
|
1314
|
-
if (
|
|
1312
|
+
if (rawValue.match(functionPrefixPattern)) {
|
|
1315
1313
|
// console.log('RAW FUNCTION', rawFunction)
|
|
1316
1314
|
const funcString = rawValue.replace(/> function /g, '')
|
|
1317
1315
|
// console.log('funcString', funcString)
|
|
@@ -2152,9 +2150,6 @@ class Configorama {
|
|
|
2152
2150
|
|
|
2153
2151
|
historyEntry.match = matches[i].match
|
|
2154
2152
|
historyEntry.variable = matches[i].variable
|
|
2155
|
-
if (historyEntry.resultType === 'string' && historyEntry.result.match(/^>passthrough\[/)) {
|
|
2156
|
-
historyEntry.variableType = 'encodedUnknown'
|
|
2157
|
-
}
|
|
2158
2153
|
if (resolverType) {
|
|
2159
2154
|
historyEntry.variableType = resolverType
|
|
2160
2155
|
}
|
|
@@ -2167,6 +2162,9 @@ class Configorama {
|
|
|
2167
2162
|
}
|
|
2168
2163
|
|
|
2169
2164
|
historyEntry.resultType = typeof finalResult
|
|
2165
|
+
if (historyEntry.resultType === 'string' && typeof cleanResult === 'string' && cleanResult.match(/^>passthrough\[/)) {
|
|
2166
|
+
historyEntry.variableType = 'encodedUnknown'
|
|
2167
|
+
}
|
|
2170
2168
|
historyEntry.valueBeforeResolution = valueBeforeResolution
|
|
2171
2169
|
historyEntry.from = 'renderMatches'
|
|
2172
2170
|
if (isDeepResult) {
|
|
@@ -2383,7 +2381,7 @@ class Configorama {
|
|
|
2383
2381
|
}
|
|
2384
2382
|
}
|
|
2385
2383
|
|
|
2386
|
-
const originalSrc = valueObject.originalSource
|
|
2384
|
+
const originalSrc = (typeof valueObject.originalSource === 'string') ? valueObject.originalSource : ''
|
|
2387
2385
|
const hasFilters = originalSrc.match(this.filterMatch)
|
|
2388
2386
|
let foundFilters = []
|
|
2389
2387
|
if (hasFilters) {
|
|
@@ -2485,7 +2483,7 @@ class Configorama {
|
|
|
2485
2483
|
|
|
2486
2484
|
// For eval/if expressions, string values need quotes unless already quoted
|
|
2487
2485
|
// BUT don't quote strings that contain variable refs (they need further resolution)
|
|
2488
|
-
if (
|
|
2486
|
+
if (evalIfPattern.test(property) && !valueToPopulate.match(this.variableSyntax)) {
|
|
2489
2487
|
const matchIdx = property.indexOf(currentMatchedString)
|
|
2490
2488
|
const charBefore = matchIdx > 0 ? property[matchIdx - 1] : ''
|
|
2491
2489
|
// Always escape quotes in values for eval/if context
|
|
@@ -2513,7 +2511,7 @@ class Configorama {
|
|
|
2513
2511
|
if (DEBUG_TYPE) console.log('DEBUG_TYPE isObject')
|
|
2514
2512
|
|
|
2515
2513
|
// For eval/if expressions, encode objects to avoid {} breaking variable syntax
|
|
2516
|
-
const isEvalOrIf =
|
|
2514
|
+
const isEvalOrIf = evalIfPattern.test(property)
|
|
2517
2515
|
if (isEvalOrIf) {
|
|
2518
2516
|
const encoded = encodeValueForEval(valueToPopulate)
|
|
2519
2517
|
property = replaceAll(matchedString, encoded, property)
|
|
@@ -2550,12 +2548,12 @@ class Configorama {
|
|
|
2550
2548
|
// console.log('other new prop', property)
|
|
2551
2549
|
|
|
2552
2550
|
// partial replacement, boolean inside eval/if expressions
|
|
2553
|
-
} else if (typeof valueToPopulate === 'boolean' &&
|
|
2551
|
+
} else if (typeof valueToPopulate === 'boolean' && evalIfPattern.test(property)) {
|
|
2554
2552
|
if (DEBUG_TYPE) console.log('DEBUG_TYPE isBoolean in eval/if')
|
|
2555
2553
|
property = replaceAll(matchedString, String(valueToPopulate), property)
|
|
2556
2554
|
|
|
2557
2555
|
// partial replacement, null inside eval/if expressions
|
|
2558
|
-
} else if (valueToPopulate === null &&
|
|
2556
|
+
} else if (valueToPopulate === null && evalIfPattern.test(property)) {
|
|
2559
2557
|
if (DEBUG_TYPE) console.log('DEBUG_TYPE isNull in eval/if')
|
|
2560
2558
|
property = replaceAll(matchedString, '__NULL__', property)
|
|
2561
2559
|
|
|
@@ -2655,13 +2653,13 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
2655
2653
|
}
|
|
2656
2654
|
|
|
2657
2655
|
// console.log('prop', prop)
|
|
2658
|
-
if (property.match(
|
|
2656
|
+
if (property.match(functionPrefixPattern) && prop) {
|
|
2659
2657
|
// console.log('func prop', property)
|
|
2660
2658
|
// console.log('Prop', prop)
|
|
2661
2659
|
}
|
|
2662
2660
|
const func = funcRegex.exec(property)
|
|
2663
2661
|
// console.log('func', func)
|
|
2664
|
-
if (func && property.match(
|
|
2662
|
+
if (func && property.match(functionPrefixPattern)) {
|
|
2665
2663
|
/* IMPORTANT fix `finalProp` for nested function reference
|
|
2666
2664
|
nestedOne: 'hi'
|
|
2667
2665
|
nestedTwo: ${merge('nice', 'wow')}
|
|
@@ -2712,7 +2710,7 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
2712
2710
|
/* if matches function signature like ${merge('foo', 'bar')}
|
|
2713
2711
|
rewrite the variable to run the function after inputs resolved
|
|
2714
2712
|
*/
|
|
2715
|
-
const rep = property.replace(
|
|
2713
|
+
const rep = property.replace(functionPrefixPattern, '')
|
|
2716
2714
|
property = `> function ${rep}`
|
|
2717
2715
|
}
|
|
2718
2716
|
// if (prop.match(/\s\|/)) {
|
|
@@ -3624,7 +3622,7 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
3624
3622
|
return variableString
|
|
3625
3623
|
}
|
|
3626
3624
|
// console.log('runFunction', variableString)
|
|
3627
|
-
|
|
3625
|
+
const hasFunc = funcRegex.exec(variableString)
|
|
3628
3626
|
// TODO finish Function handling. Need to move this down below resolver to resolve inner refs first
|
|
3629
3627
|
// console.log('hasFunc', hasFunc)
|
|
3630
3628
|
// Skip special expressions (cron, eval, if) - these aren't user functions
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Resolves values from process.env environment variables
|
|
2
|
+
// Matches ${env:VAR_NAME} syntax with optional fallback values
|
|
1
3
|
|
|
2
4
|
const envRefSyntax = RegExp(/^env:/g)
|
|
3
5
|
|
|
@@ -13,12 +15,7 @@ Example: \${env:MY_ENV_VAR}
|
|
|
13
15
|
`)
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
if (requestedEnvVar !== '' || '' in process.env) {
|
|
18
|
-
valueToPopulate = process.env[requestedEnvVar]
|
|
19
|
-
} else {
|
|
20
|
-
valueToPopulate = process.env
|
|
21
|
-
}
|
|
18
|
+
const valueToPopulate = process.env[requestedEnvVar]
|
|
22
19
|
return Promise.resolve(valueToPopulate)
|
|
23
20
|
}
|
|
24
21
|
|
|
@@ -121,9 +121,9 @@ async function getValueFromFile(ctx, variableString, options) {
|
|
|
121
121
|
|
|
122
122
|
// Get function input params if any supplied https://regex101.com/r/qlNFVm/1
|
|
123
123
|
// var funcParamsRegex = /(\w+)\s*\(((?:[^()]+)*)?\s*\)\s*/g
|
|
124
|
-
|
|
124
|
+
const funcParamsRegex = /(\w+)\s*\(((?:[^()]+)*)?\s*\)/g
|
|
125
125
|
// tighter (?<![.\w-])\b(\w+)\s*\(((?:[^()]+)*)?\s*\)\s*
|
|
126
|
-
|
|
126
|
+
const hasParams = funcParamsRegex.exec(matchedFileString)
|
|
127
127
|
|
|
128
128
|
let argsToPass = []
|
|
129
129
|
if (hasParams) {
|
|
@@ -257,8 +257,8 @@ ${JSON.stringify(options.context, null, 2)}`,
|
|
|
257
257
|
const variableFileContents = fs.readFileSync(fullFilePath, 'utf-8')
|
|
258
258
|
|
|
259
259
|
/* handle case for referencing raw JS files to inline them */
|
|
260
|
-
if (argsToPass.length
|
|
261
|
-
&&
|
|
260
|
+
if ((argsToPass.length
|
|
261
|
+
&& argsToPass[0] && typeof argsToPass[0] === 'string' && argsToPass[0].toLowerCase() === 'raw')
|
|
262
262
|
|| opts.asRawText
|
|
263
263
|
) {
|
|
264
264
|
// Encode foo() to foo__PH_PAREN_OPEN__) to avoid function collisions
|
|
@@ -10,6 +10,21 @@ const { findProjectRoot } = require('../utils/paths/findProjectRoot')
|
|
|
10
10
|
const GIT_PREFIX = 'git'
|
|
11
11
|
const gitVariableSyntax = RegExp(/^git:/g)
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Check if a directory is inside a git repository.
|
|
15
|
+
* @param {string} [dir] - Directory to check (defaults to process.cwd())
|
|
16
|
+
* @returns {boolean}
|
|
17
|
+
*/
|
|
18
|
+
function isGitRepo(dir) {
|
|
19
|
+
const start = dir || process.cwd()
|
|
20
|
+
try {
|
|
21
|
+
if (!fs.existsSync(start)) return false
|
|
22
|
+
return findProjectRoot(start) !== null
|
|
23
|
+
} catch (err) {
|
|
24
|
+
return false
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
13
28
|
/**
|
|
14
29
|
* Execute a shell command
|
|
15
30
|
* @param {string} cmd - Command to execute
|
|
@@ -45,6 +60,25 @@ async function _execFile(command, args, options = { timeout: 1000 }) {
|
|
|
45
60
|
})
|
|
46
61
|
}
|
|
47
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Run a git command and return undefined on failure. This lets the variable
|
|
65
|
+
* resolver fall through to user-provided fallbacks (e.g. `${git:branch, "main"}`)
|
|
66
|
+
* when not in a git repo, without surfacing raw `fatal: not a git repository`
|
|
67
|
+
* errors. When no fallback is provided, the outer resolver in main.js still
|
|
68
|
+
* produces a clear "Unable to resolve config variable" error pointing at the
|
|
69
|
+
* exact config path.
|
|
70
|
+
*
|
|
71
|
+
* @param {() => Promise<string>} cmdFn - Function that runs the git command
|
|
72
|
+
* @returns {Promise<string|undefined>}
|
|
73
|
+
*/
|
|
74
|
+
async function _safeGit(cmdFn) {
|
|
75
|
+
try {
|
|
76
|
+
return await cmdFn()
|
|
77
|
+
} catch (err) {
|
|
78
|
+
return undefined
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
48
82
|
// TODO denote computed fields in metadata
|
|
49
83
|
/*
|
|
50
84
|
{
|
|
@@ -83,14 +117,21 @@ function createResolver(cwd) {
|
|
|
83
117
|
const variable = variableString.split(`${GIT_PREFIX}:`)[1]
|
|
84
118
|
let value = null
|
|
85
119
|
// console.log('createResolver variableString', variableString)
|
|
120
|
+
|
|
121
|
+
// If we're not inside a git repository, every git: variable resolves to
|
|
122
|
+
// undefined. This lets fallbacks like `${git:branch, "main"}` work, and
|
|
123
|
+
// when there's no fallback the outer resolver throws a clear "Unable to
|
|
124
|
+
// resolve config variable" error pointing at the config path.
|
|
125
|
+
if (!isGitRepo(cwd)) {
|
|
126
|
+
return undefined
|
|
127
|
+
}
|
|
128
|
+
|
|
86
129
|
if (variable.match(/^remote/i)) {
|
|
87
130
|
const hasParams = functionRegex.exec(variableString)
|
|
88
131
|
const remoteName = (hasParams && hasParams[2]) ? formatFunctionArgs(hasParams[2]) : 'origin'
|
|
89
|
-
|
|
90
|
-
return value
|
|
132
|
+
return _safeGit(() => getGitRemote(remoteName))
|
|
91
133
|
}
|
|
92
134
|
|
|
93
|
-
const verifyMsg = `Verify the cwd has a .git directory\n`
|
|
94
135
|
const normalizedVar = (variable || '').toLowerCase()
|
|
95
136
|
// console.log('normalizedVar', normalizedVar)
|
|
96
137
|
|
|
@@ -100,7 +141,7 @@ function createResolver(cwd) {
|
|
|
100
141
|
const funcName = argsMatch[1]
|
|
101
142
|
const args = argsMatch[2]
|
|
102
143
|
if (funcName === 'timestamp' && args) {
|
|
103
|
-
value = await getGitTimestamp(args, cwd)
|
|
144
|
+
value = await getGitTimestamp(args, cwd, false)
|
|
104
145
|
}
|
|
105
146
|
}
|
|
106
147
|
|
|
@@ -109,55 +150,61 @@ function createResolver(cwd) {
|
|
|
109
150
|
case GIT_KEYS.repo:
|
|
110
151
|
case 'repository':
|
|
111
152
|
case 'reposlug':
|
|
112
|
-
case 'repo-slug':
|
|
113
|
-
const urla = await getGitRemote()
|
|
153
|
+
case 'repo-slug': {
|
|
154
|
+
const urla = await _safeGit(() => getGitRemote())
|
|
155
|
+
if (!urla) return undefined
|
|
114
156
|
const parseda = GitUrlParse(urla)
|
|
115
157
|
value = parseda.full_name
|
|
116
|
-
break
|
|
158
|
+
break
|
|
159
|
+
}
|
|
117
160
|
// Repo name
|
|
118
161
|
case GIT_KEYS.name:
|
|
119
162
|
case 'reponame': // repoName
|
|
120
|
-
case 'repo-name':
|
|
121
|
-
|
|
122
|
-
|
|
163
|
+
case 'repo-name': {
|
|
164
|
+
const toplevel = await _safeGit(() => _execFile('git', ['rev-parse', '--show-toplevel']))
|
|
165
|
+
if (!toplevel) return undefined
|
|
166
|
+
value = path.basename(toplevel)
|
|
167
|
+
break
|
|
168
|
+
}
|
|
123
169
|
// Repo org or owner
|
|
124
170
|
case GIT_KEYS.org:
|
|
125
171
|
case 'owner':
|
|
126
172
|
case 'organization':
|
|
127
173
|
case 'repoowner': // repoOwner
|
|
128
|
-
case 'repo-owner':
|
|
129
|
-
const url = await getGitRemote()
|
|
174
|
+
case 'repo-owner': {
|
|
175
|
+
const url = await _safeGit(() => getGitRemote())
|
|
176
|
+
if (!url) return undefined
|
|
130
177
|
const parsed = GitUrlParse(url)
|
|
131
178
|
value = parsed.organization || parsed.owner
|
|
132
|
-
break
|
|
179
|
+
break
|
|
180
|
+
}
|
|
133
181
|
// Repo name
|
|
134
182
|
case GIT_KEYS.dir:
|
|
135
183
|
case 'directory':
|
|
136
184
|
case 'dirpath': // dirPath
|
|
137
185
|
case 'dir-path':
|
|
138
|
-
case 'dir_path':
|
|
139
|
-
const gitBasePath = await
|
|
186
|
+
case 'dir_path': {
|
|
187
|
+
const gitBasePath = await _safeGit(() => _execFile('git', ['rev-parse', '--show-toplevel']))
|
|
188
|
+
if (!gitBasePath) return undefined
|
|
140
189
|
if (cwd) {
|
|
141
190
|
const subPath = cwd.replace(gitBasePath, '')
|
|
142
|
-
const branch = await
|
|
143
|
-
const url = await getGitRemote()
|
|
144
|
-
|
|
191
|
+
const branch = await _safeGit(() => _execFile('git', ['rev-parse', '--abbrev-ref', 'HEAD']))
|
|
192
|
+
const url = await _safeGit(() => getGitRemote())
|
|
193
|
+
if (!url) return undefined
|
|
194
|
+
value = (subPath && branch) ? `${url}/tree/${branch}${subPath}` : url
|
|
145
195
|
}
|
|
146
|
-
break
|
|
196
|
+
break
|
|
197
|
+
}
|
|
147
198
|
// Repo url
|
|
148
199
|
case GIT_KEYS.url:
|
|
149
200
|
case 'repourl': // repoUrl
|
|
150
201
|
case 'repo-url':
|
|
151
|
-
value = await getGitRemote()
|
|
152
|
-
break
|
|
202
|
+
value = await _safeGit(() => getGitRemote())
|
|
203
|
+
break
|
|
153
204
|
// Current commit sha
|
|
154
205
|
case 'sha':
|
|
155
206
|
case 'sha1':
|
|
156
|
-
|
|
157
|
-
value = await _exec('git rev-parse --short HEAD')
|
|
158
|
-
} catch (err) {
|
|
159
|
-
throw new Error(`\${git:sha1} error ${verifyMsg}`)
|
|
160
|
-
}
|
|
207
|
+
value = await _safeGit(() => _execFile('git', ['rev-parse', '--short', 'HEAD']))
|
|
161
208
|
break
|
|
162
209
|
// Current commit full sha
|
|
163
210
|
case GIT_KEYS.commit:
|
|
@@ -165,11 +212,7 @@ function createResolver(cwd) {
|
|
|
165
212
|
case 'commit-sha':
|
|
166
213
|
case 'commithash':
|
|
167
214
|
case 'commit-hash':
|
|
168
|
-
|
|
169
|
-
value = await _exec('git rev-parse HEAD')
|
|
170
|
-
} catch (err) {
|
|
171
|
-
throw new Error(`\${git:commit} error. ${verifyMsg}`)
|
|
172
|
-
}
|
|
215
|
+
value = await _safeGit(() => _execFile('git', ['rev-parse', 'HEAD']))
|
|
173
216
|
break
|
|
174
217
|
// Branches
|
|
175
218
|
case GIT_KEYS.branch:
|
|
@@ -177,11 +220,7 @@ function createResolver(cwd) {
|
|
|
177
220
|
case 'branch-name':
|
|
178
221
|
case 'currentbranch': // currentBranch
|
|
179
222
|
case 'current-branch':
|
|
180
|
-
|
|
181
|
-
value = await _exec('git rev-parse --abbrev-ref HEAD')
|
|
182
|
-
} catch (err) {
|
|
183
|
-
throw new Error(`\${git:branch} error. ${verifyMsg}`)
|
|
184
|
-
}
|
|
223
|
+
value = await _safeGit(() => _execFile('git', ['rev-parse', '--abbrev-ref', 'HEAD']))
|
|
185
224
|
break
|
|
186
225
|
// Commit msg
|
|
187
226
|
case GIT_KEYS.message:
|
|
@@ -190,42 +229,36 @@ function createResolver(cwd) {
|
|
|
190
229
|
case 'commit-message':
|
|
191
230
|
case 'commitmsg': // commitMsg
|
|
192
231
|
case 'commit-msg':
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
} catch (err) {
|
|
196
|
-
throw new Error(`\${git:message} error. ${verifyMsg}`)
|
|
197
|
-
}
|
|
198
|
-
break;
|
|
232
|
+
value = await _safeGit(() => _execFile('git', ['log', '-1', '--pretty=%B']))
|
|
233
|
+
break
|
|
199
234
|
// Git tags
|
|
200
235
|
case GIT_KEYS.tag:
|
|
201
236
|
case 'describe':
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
} catch (err) {
|
|
205
|
-
throw new Error(`\${git:describeLight} error. ${verifyMsg}`)
|
|
206
|
-
}
|
|
207
|
-
break;
|
|
237
|
+
value = await _safeGit(() => _execFile('git', ['describe', '--always']))
|
|
238
|
+
break
|
|
208
239
|
// Git tags
|
|
209
240
|
case 'describeLight':
|
|
210
241
|
case 'describelight':
|
|
211
242
|
case 'describe-light':
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
} catch (err) {
|
|
215
|
-
throw new Error(`\${git:describeLight} error. ${verifyMsg}`)
|
|
216
|
-
}
|
|
217
|
-
break;
|
|
243
|
+
value = await _safeGit(() => _execFile('git', ['describe', '--always', '--tags']))
|
|
244
|
+
break
|
|
218
245
|
// Is branch dirty
|
|
219
246
|
case 'isDirty':
|
|
220
247
|
case 'isdirty':
|
|
221
|
-
case 'is-dirty':
|
|
222
|
-
const writeTree = await
|
|
223
|
-
|
|
248
|
+
case 'is-dirty': {
|
|
249
|
+
const writeTree = await _safeGit(() => _execFile('git', ['write-tree']))
|
|
250
|
+
if (!writeTree) return undefined
|
|
251
|
+
const changes = await _safeGit(() => _execFile('git', ['diff-index', writeTree.trim(), '--']))
|
|
252
|
+
if (changes === undefined) return undefined
|
|
224
253
|
value = `${changes.length > 0}`
|
|
225
254
|
break
|
|
255
|
+
}
|
|
226
256
|
default:
|
|
227
257
|
if (!value) {
|
|
228
|
-
|
|
258
|
+
// Unknown variable name (likely a typo). This is a config error,
|
|
259
|
+
// not an environment one, so throw a helpful message listing the
|
|
260
|
+
// valid keys.
|
|
261
|
+
throw new Error(`Git variable "${variable}" is unknown. Valid options: ${Object.values(GIT_KEYS).join(', ')}`)
|
|
229
262
|
}
|
|
230
263
|
}
|
|
231
264
|
return value
|
|
@@ -299,14 +332,19 @@ async function getGitTimestamp(_file, cwd, throwOnMissing = true) {
|
|
|
299
332
|
}
|
|
300
333
|
}
|
|
301
334
|
|
|
335
|
+
const remoteCache = new Map()
|
|
336
|
+
|
|
302
337
|
async function getGitRemote(name = 'origin') {
|
|
303
|
-
|
|
338
|
+
if (remoteCache.has(name)) {
|
|
339
|
+
return remoteCache.get(name)
|
|
340
|
+
}
|
|
341
|
+
const remoteValues = await _execFile('git', ['remote', '-v'])
|
|
304
342
|
const remotes = remoteValues.toString().split(os.EOL)
|
|
305
343
|
.filter(function filterOnlyFetchRows(remote) {
|
|
306
344
|
return remote.match('(fetch)')
|
|
307
345
|
})
|
|
308
346
|
.map(function mapRemoteLineToObject(remote) {
|
|
309
|
-
|
|
347
|
+
const parts = remote.split('\t')
|
|
310
348
|
if (parts.length < 2) {
|
|
311
349
|
return
|
|
312
350
|
}
|
|
@@ -326,7 +364,6 @@ async function getGitRemote(name = 'origin') {
|
|
|
326
364
|
|
|
327
365
|
if (!originUrl) {
|
|
328
366
|
throw new Error(`No git remote "${name}" found. Please double check your remote names`)
|
|
329
|
-
return
|
|
330
367
|
}
|
|
331
368
|
// console.log('originUrl', originUrl)
|
|
332
369
|
const parsed = GitUrlParse(originUrl)
|
|
@@ -334,7 +371,9 @@ async function getGitRemote(name = 'origin') {
|
|
|
334
371
|
// @TODO finish git api
|
|
335
372
|
// console.log('parsed', parsed)
|
|
336
373
|
if (parsed && parsed.source && parsed.full_name) {
|
|
337
|
-
|
|
374
|
+
const result = `https://${parsed.source}/${parsed.full_name}`
|
|
375
|
+
remoteCache.set(name, result)
|
|
376
|
+
return result
|
|
338
377
|
}
|
|
339
378
|
}
|
|
340
379
|
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
// Resolves values from CLI option flags
|
|
2
|
+
// Matches ${opt:FLAG_NAME} syntax with optional fallback values
|
|
2
3
|
const optRefSyntax = RegExp(/^opt:/g)
|
|
3
4
|
|
|
4
5
|
function getValueFromOptions(variableString, options) {
|
|
5
6
|
const requestedOption = variableString.split(':')[1]
|
|
6
|
-
|
|
7
|
-
if (requestedOption !== '' || '' in options) {
|
|
8
|
-
valueToPopulate = options[requestedOption]
|
|
9
|
-
} else {
|
|
10
|
-
valueToPopulate = options
|
|
11
|
-
}
|
|
7
|
+
const valueToPopulate = options[requestedOption]
|
|
12
8
|
return Promise.resolve(valueToPopulate)
|
|
13
9
|
}
|
|
14
10
|
|
package/src/utils/lodash.js
CHANGED
|
@@ -67,34 +67,34 @@ function mapValues(obj, fn) {
|
|
|
67
67
|
*/
|
|
68
68
|
function set(object, path, value) {
|
|
69
69
|
if (object === null || typeof object !== 'object') {
|
|
70
|
-
return object
|
|
70
|
+
return object
|
|
71
71
|
}
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
const keys = Array.isArray(path) ? path : String(path)
|
|
74
74
|
.split('.')
|
|
75
75
|
.map(key => {
|
|
76
|
-
const numKey = Number(key)
|
|
77
|
-
return Number.isInteger(numKey) && numKey >= 0 ? numKey : key
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
let current = object
|
|
81
|
-
const lastIndex = keys.length - 1
|
|
82
|
-
|
|
76
|
+
const numKey = Number(key)
|
|
77
|
+
return Number.isInteger(numKey) && numKey >= 0 ? numKey : key
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
let current = object
|
|
81
|
+
const lastIndex = keys.length - 1
|
|
82
|
+
|
|
83
83
|
for (let i = 0; i < lastIndex; i++) {
|
|
84
84
|
const key = keys[i]
|
|
85
|
-
|
|
85
|
+
|
|
86
86
|
// Check if value is undefined, null, or not an object (primitives can't have properties)
|
|
87
87
|
if (current[key] == null || typeof current[key] !== 'object') {
|
|
88
88
|
// Create appropriate container based on next key type
|
|
89
89
|
const nextKey = keys[i + 1]
|
|
90
90
|
current[key] = Number.isInteger(nextKey) && /** @type {number} */ (nextKey) >= 0 ? [] : {}
|
|
91
91
|
}
|
|
92
|
-
|
|
92
|
+
|
|
93
93
|
current = current[key]
|
|
94
94
|
}
|
|
95
|
-
|
|
96
|
-
current[keys[lastIndex]] = value
|
|
97
|
-
return object
|
|
95
|
+
|
|
96
|
+
current[keys[lastIndex]] = value
|
|
97
|
+
return object
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
// Cache for trim regex patterns (perf: avoid recompilation)
|
|
@@ -107,18 +107,18 @@ const trimRegexCache = new Map()
|
|
|
107
107
|
*/
|
|
108
108
|
function trim(string, chars) {
|
|
109
109
|
if (string === null || string === undefined) {
|
|
110
|
-
return ''
|
|
110
|
+
return ''
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
string = String(string)
|
|
113
|
+
string = String(string)
|
|
114
114
|
|
|
115
115
|
if (!chars && String.prototype.trim) {
|
|
116
|
-
return string.trim()
|
|
116
|
+
return string.trim()
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
if (!chars) {
|
|
120
120
|
// Default characters to trim (whitespace)
|
|
121
|
-
chars = ' \t\n\r\f\v\u00a0\u1680\u2000\u200a\u2028\u2029\u202f\u205f\u3000\ufeff'
|
|
121
|
+
chars = ' \t\n\r\f\v\u00a0\u1680\u2000\u200a\u2028\u2029\u202f\u205f\u3000\ufeff'
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
// Check cache first
|
|
@@ -132,7 +132,7 @@ function trim(string, chars) {
|
|
|
132
132
|
|
|
133
133
|
// Reset lastIndex for global regex reuse
|
|
134
134
|
pattern.lastIndex = 0
|
|
135
|
-
return string.replace(pattern, '')
|
|
135
|
+
return string.replace(pattern, '')
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
module.exports = {
|