uva-cli 1.0.1 → 1.1.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/commands/branch.mjs +25 -27
- package/commands/commit.mjs +33 -23
- package/commands/init.mjs +44 -32
- package/commands/push.mjs +7 -6
- package/commands/start.mjs +20 -15
- package/lib/banner.mjs +2 -2
- package/lib/git.mjs +2 -2
- package/lib/i18n.mjs +9 -0
- package/lib/locales/en.mjs +96 -0
- package/lib/locales/pt-br.mjs +101 -0
- package/package.json +1 -1
package/commands/branch.mjs
CHANGED
|
@@ -4,20 +4,20 @@ import { hasUncommittedChanges, checkout, pull, createBranch } from '../lib/git.
|
|
|
4
4
|
import { COMMIT_TYPES } from '../lib/types.mjs'
|
|
5
5
|
import { requireConfig } from '../lib/config.mjs'
|
|
6
6
|
import { buildBranchName } from '../lib/format.mjs'
|
|
7
|
+
import { getLocale } from '../lib/i18n.mjs'
|
|
7
8
|
|
|
8
9
|
// Called by commander as runBranch(options, command)
|
|
9
10
|
export async function runBranch(opts = {}) {
|
|
10
11
|
bannerIntro('branch')
|
|
11
12
|
|
|
12
13
|
const config = requireConfig()
|
|
14
|
+
const t = getLocale(config)
|
|
13
15
|
|
|
14
16
|
if (hasUncommittedChanges()) {
|
|
15
|
-
const proceed = await confirm({
|
|
16
|
-
message: 'You have uncommitted changes. They will carry over to the new branch. Continue?',
|
|
17
|
-
})
|
|
17
|
+
const proceed = await confirm({ message: t.branch.uncommittedConfirm })
|
|
18
18
|
if (isCancel(proceed) || !proceed) {
|
|
19
|
-
log.info(
|
|
20
|
-
bannerCancelled()
|
|
19
|
+
log.info(t.branch.uncommittedTip)
|
|
20
|
+
bannerCancelled(t.common.cancelled)
|
|
21
21
|
process.exit(0)
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -26,16 +26,16 @@ export async function runBranch(opts = {}) {
|
|
|
26
26
|
let source
|
|
27
27
|
if (opts.source) {
|
|
28
28
|
if (!config.branch.sources.includes(opts.source)) {
|
|
29
|
-
log.warn(
|
|
29
|
+
log.warn(t.branch.sourceWarn(opts.source))
|
|
30
30
|
}
|
|
31
31
|
source = opts.source
|
|
32
32
|
} else {
|
|
33
33
|
source = await select({
|
|
34
|
-
message:
|
|
34
|
+
message: t.branch.source,
|
|
35
35
|
options: config.branch.sources.map((b) => ({ value: b, label: b })),
|
|
36
36
|
})
|
|
37
37
|
if (isCancel(source)) {
|
|
38
|
-
bannerCancelled()
|
|
38
|
+
bannerCancelled(t.common.cancelled)
|
|
39
39
|
process.exit(0)
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -47,12 +47,12 @@ export async function runBranch(opts = {}) {
|
|
|
47
47
|
ticket = opts.ticket
|
|
48
48
|
} else {
|
|
49
49
|
const input = await text({
|
|
50
|
-
message:
|
|
50
|
+
message: t.branch.ticket,
|
|
51
51
|
placeholder: config.commit.ticketPlaceholder,
|
|
52
|
-
validate: (v) => (v.trim() ? undefined :
|
|
52
|
+
validate: (v) => (v.trim() ? undefined : t.branch.ticketError),
|
|
53
53
|
})
|
|
54
54
|
if (isCancel(input)) {
|
|
55
|
-
bannerCancelled()
|
|
55
|
+
bannerCancelled(t.common.cancelled)
|
|
56
56
|
process.exit(0)
|
|
57
57
|
}
|
|
58
58
|
ticket = input.trim()
|
|
@@ -64,14 +64,14 @@ export async function runBranch(opts = {}) {
|
|
|
64
64
|
if (opts.type) {
|
|
65
65
|
const valid = COMMIT_TYPES.map((t) => t.value)
|
|
66
66
|
if (!valid.includes(opts.type)) {
|
|
67
|
-
log.error(
|
|
67
|
+
log.error(t.branch.invalidType(opts.type, valid.join(', ')))
|
|
68
68
|
process.exit(1)
|
|
69
69
|
}
|
|
70
70
|
type = opts.type
|
|
71
71
|
} else {
|
|
72
|
-
type = await select({ message:
|
|
72
|
+
type = await select({ message: t.branch.type, options: COMMIT_TYPES })
|
|
73
73
|
if (isCancel(type)) {
|
|
74
|
-
bannerCancelled()
|
|
74
|
+
bannerCancelled(t.common.cancelled)
|
|
75
75
|
process.exit(0)
|
|
76
76
|
}
|
|
77
77
|
}
|
|
@@ -82,12 +82,12 @@ export async function runBranch(opts = {}) {
|
|
|
82
82
|
taskName = opts.name
|
|
83
83
|
} else {
|
|
84
84
|
const input = await text({
|
|
85
|
-
message:
|
|
86
|
-
placeholder:
|
|
87
|
-
validate: (v) => (v.trim() ? undefined :
|
|
85
|
+
message: t.branch.name,
|
|
86
|
+
placeholder: t.branch.namePlaceholder,
|
|
87
|
+
validate: (v) => (v.trim() ? undefined : t.branch.nameError),
|
|
88
88
|
})
|
|
89
89
|
if (isCancel(input)) {
|
|
90
|
-
bannerCancelled()
|
|
90
|
+
bannerCancelled(t.common.cancelled)
|
|
91
91
|
process.exit(0)
|
|
92
92
|
}
|
|
93
93
|
taskName = input.trim()
|
|
@@ -100,9 +100,9 @@ export async function runBranch(opts = {}) {
|
|
|
100
100
|
const scripted = opts.source && opts.type && opts.name && ticketDone
|
|
101
101
|
|
|
102
102
|
if (!scripted) {
|
|
103
|
-
const confirmed = await confirm({ message:
|
|
103
|
+
const confirmed = await confirm({ message: t.branch.confirm(branchName) })
|
|
104
104
|
if (isCancel(confirmed) || !confirmed) {
|
|
105
|
-
bannerCancelled()
|
|
105
|
+
bannerCancelled(t.common.cancelled)
|
|
106
106
|
process.exit(0)
|
|
107
107
|
}
|
|
108
108
|
}
|
|
@@ -110,16 +110,14 @@ export async function runBranch(opts = {}) {
|
|
|
110
110
|
try {
|
|
111
111
|
checkout(source)
|
|
112
112
|
} catch {
|
|
113
|
-
log.error(
|
|
114
|
-
`Could not switch to ${source}. Make sure the branch exists and there are no conflicts.`,
|
|
115
|
-
)
|
|
113
|
+
log.error(t.branch.checkoutError(source))
|
|
116
114
|
process.exit(1)
|
|
117
115
|
}
|
|
118
116
|
|
|
119
117
|
try {
|
|
120
118
|
pull()
|
|
121
119
|
} catch {
|
|
122
|
-
log.error(
|
|
120
|
+
log.error(t.branch.pullError)
|
|
123
121
|
process.exit(1)
|
|
124
122
|
}
|
|
125
123
|
|
|
@@ -127,12 +125,12 @@ export async function runBranch(opts = {}) {
|
|
|
127
125
|
createBranch(branchName)
|
|
128
126
|
} catch (err) {
|
|
129
127
|
if (err.stderr?.includes('already exists') || err.message?.includes('already exists')) {
|
|
130
|
-
log.error(
|
|
128
|
+
log.error(t.branch.branchExists(branchName))
|
|
131
129
|
} else {
|
|
132
|
-
log.error(
|
|
130
|
+
log.error(t.branch.createError)
|
|
133
131
|
}
|
|
134
132
|
process.exit(1)
|
|
135
133
|
}
|
|
136
134
|
|
|
137
|
-
bannerOutro(
|
|
135
|
+
bannerOutro(t.branch.done(branchName))
|
|
138
136
|
}
|
package/commands/commit.mjs
CHANGED
|
@@ -4,16 +4,18 @@ import { getChangedFiles, addFiles, commit } from '../lib/git.mjs'
|
|
|
4
4
|
import { COMMIT_TYPES } from '../lib/types.mjs'
|
|
5
5
|
import { requireConfig } from '../lib/config.mjs'
|
|
6
6
|
import { buildCommitMessage } from '../lib/format.mjs'
|
|
7
|
+
import { getLocale } from '../lib/i18n.mjs'
|
|
7
8
|
|
|
8
9
|
// Called by commander as runCommit(options, command)
|
|
9
10
|
export async function runCommit(opts = {}) {
|
|
10
11
|
bannerIntro('commit')
|
|
11
12
|
|
|
12
13
|
const config = requireConfig()
|
|
14
|
+
const t = getLocale(config)
|
|
13
15
|
const files = getChangedFiles()
|
|
14
16
|
|
|
15
17
|
if (files.length === 0) {
|
|
16
|
-
log.info(
|
|
18
|
+
log.info(t.commit.nothingToCommit)
|
|
17
19
|
process.exit(0)
|
|
18
20
|
}
|
|
19
21
|
|
|
@@ -22,14 +24,14 @@ export async function runCommit(opts = {}) {
|
|
|
22
24
|
if (opts.type) {
|
|
23
25
|
const valid = COMMIT_TYPES.map((t) => t.value)
|
|
24
26
|
if (!valid.includes(opts.type)) {
|
|
25
|
-
log.error(
|
|
27
|
+
log.error(t.commit.invalidType(opts.type, valid.join(', ')))
|
|
26
28
|
process.exit(1)
|
|
27
29
|
}
|
|
28
30
|
type = opts.type
|
|
29
31
|
} else {
|
|
30
|
-
type = await select({ message:
|
|
32
|
+
type = await select({ message: t.commit.type, options: COMMIT_TYPES })
|
|
31
33
|
if (isCancel(type)) {
|
|
32
|
-
bannerCancelled()
|
|
34
|
+
bannerCancelled(t.common.cancelled)
|
|
33
35
|
process.exit(0)
|
|
34
36
|
}
|
|
35
37
|
}
|
|
@@ -41,12 +43,12 @@ export async function runCommit(opts = {}) {
|
|
|
41
43
|
ticket = opts.ticket
|
|
42
44
|
} else {
|
|
43
45
|
const input = await text({
|
|
44
|
-
message:
|
|
46
|
+
message: t.commit.ticket,
|
|
45
47
|
placeholder: config.commit.ticketPlaceholder,
|
|
46
|
-
validate: (v) => (v.trim() ? undefined :
|
|
48
|
+
validate: (v) => (v.trim() ? undefined : t.commit.ticketError),
|
|
47
49
|
})
|
|
48
50
|
if (isCancel(input)) {
|
|
49
|
-
bannerCancelled()
|
|
51
|
+
bannerCancelled(t.common.cancelled)
|
|
50
52
|
process.exit(0)
|
|
51
53
|
}
|
|
52
54
|
ticket = input.trim()
|
|
@@ -64,21 +66,19 @@ export async function runCommit(opts = {}) {
|
|
|
64
66
|
message = opts.message.trim().toLowerCase()
|
|
65
67
|
} else {
|
|
66
68
|
const input = await text({
|
|
67
|
-
message:
|
|
68
|
-
placeholder:
|
|
69
|
-
validate: (v) => (v.trim() ? undefined :
|
|
69
|
+
message: t.commit.message,
|
|
70
|
+
placeholder: t.commit.messagePlaceholder,
|
|
71
|
+
validate: (v) => (v.trim() ? undefined : t.commit.messageError),
|
|
70
72
|
})
|
|
71
73
|
if (isCancel(input)) {
|
|
72
|
-
bannerCancelled()
|
|
74
|
+
bannerCancelled(t.common.cancelled)
|
|
73
75
|
process.exit(0)
|
|
74
76
|
}
|
|
75
77
|
message = input.trim().toLowerCase()
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
if (message.length > 72) {
|
|
79
|
-
log.warn(
|
|
80
|
-
'Message is long (over 72 characters). Shorter messages are recommended, but you can continue.',
|
|
81
|
-
)
|
|
81
|
+
log.warn(t.commit.messageLong)
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
// Files
|
|
@@ -92,20 +92,30 @@ export async function runCommit(opts = {}) {
|
|
|
92
92
|
const availablePaths = files.map((f) => f.path)
|
|
93
93
|
const invalid = paths.filter((p) => !availablePaths.includes(p))
|
|
94
94
|
if (invalid.length) {
|
|
95
|
-
log.error(
|
|
95
|
+
log.error(t.commit.unknownFiles(invalid.join(', '), availablePaths.join(', ')))
|
|
96
96
|
process.exit(1)
|
|
97
97
|
}
|
|
98
98
|
selected = paths
|
|
99
99
|
} else {
|
|
100
|
+
const fileOptions = files.map((f) => ({
|
|
101
|
+
value: f.path,
|
|
102
|
+
label: `${f.status.padEnd(2)} ${f.path}`,
|
|
103
|
+
}))
|
|
104
|
+
if (files.length >= 5) {
|
|
105
|
+
fileOptions.unshift({ value: '__all__', label: t.commit.allFiles })
|
|
106
|
+
}
|
|
100
107
|
selected = await multiselect({
|
|
101
|
-
message:
|
|
102
|
-
options:
|
|
108
|
+
message: t.commit.files,
|
|
109
|
+
options: fileOptions,
|
|
103
110
|
required: true,
|
|
104
111
|
})
|
|
105
112
|
if (isCancel(selected)) {
|
|
106
|
-
bannerCancelled()
|
|
113
|
+
bannerCancelled(t.common.cancelled)
|
|
107
114
|
process.exit(0)
|
|
108
115
|
}
|
|
116
|
+
if (selected.includes('__all__')) {
|
|
117
|
+
selected = files.map((f) => f.path)
|
|
118
|
+
}
|
|
109
119
|
}
|
|
110
120
|
|
|
111
121
|
finalMessage = buildCommitMessage(config.commit.format, { type, ticket, message })
|
|
@@ -114,23 +124,23 @@ export async function runCommit(opts = {}) {
|
|
|
114
124
|
const scripted = opts.type && opts.message && (opts.all || opts.files)
|
|
115
125
|
if (scripted) break
|
|
116
126
|
|
|
117
|
-
const confirmed = await confirm({ message:
|
|
127
|
+
const confirmed = await confirm({ message: t.commit.confirm(finalMessage) })
|
|
118
128
|
if (isCancel(confirmed)) {
|
|
119
|
-
bannerCancelled()
|
|
129
|
+
bannerCancelled(t.common.cancelled)
|
|
120
130
|
process.exit(0)
|
|
121
131
|
}
|
|
122
132
|
if (confirmed) break
|
|
123
133
|
|
|
124
134
|
if (opts.message) break // message is fixed, can't retry
|
|
125
|
-
log.info(
|
|
135
|
+
log.info(t.commit.retry)
|
|
126
136
|
}
|
|
127
137
|
|
|
128
138
|
try {
|
|
129
139
|
addFiles(selected)
|
|
130
140
|
commit(finalMessage)
|
|
131
|
-
bannerOutro(
|
|
141
|
+
bannerOutro(t.commit.done(finalMessage))
|
|
132
142
|
} catch (e) {
|
|
133
|
-
log.error(
|
|
143
|
+
log.error(t.commit.failed(e.message))
|
|
134
144
|
process.exit(1)
|
|
135
145
|
}
|
|
136
146
|
}
|
package/commands/init.mjs
CHANGED
|
@@ -2,36 +2,48 @@ import { text, select, confirm, isCancel } from '@clack/prompts'
|
|
|
2
2
|
import { bannerIntro, bannerOutro, bannerCancelled } from '../lib/banner.mjs'
|
|
3
3
|
import { loadConfig, saveConfig } from '../lib/config.mjs'
|
|
4
4
|
import { COMMIT_FORMATS, BRANCH_FORMATS } from '../lib/types.mjs'
|
|
5
|
+
import { getLocale } from '../lib/i18n.mjs'
|
|
5
6
|
|
|
6
7
|
export async function runInit() {
|
|
7
8
|
bannerIntro('init')
|
|
8
9
|
|
|
10
|
+
// Language is always the first question — no config exists yet
|
|
11
|
+
const lang = await select({
|
|
12
|
+
message: 'Language / Idioma',
|
|
13
|
+
options: [
|
|
14
|
+
{ value: 'en', label: 'English' },
|
|
15
|
+
{ value: 'pt-br', label: 'Português (BR)' },
|
|
16
|
+
],
|
|
17
|
+
})
|
|
18
|
+
if (isCancel(lang)) {
|
|
19
|
+
bannerCancelled()
|
|
20
|
+
process.exit(0)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const t = getLocale({ project: { lang } })
|
|
24
|
+
|
|
9
25
|
const existing = loadConfig()
|
|
10
26
|
if (existing) {
|
|
11
|
-
const overwrite = await confirm({
|
|
12
|
-
message: 'A configuration already exists. Do you want to overwrite it?',
|
|
13
|
-
})
|
|
27
|
+
const overwrite = await confirm({ message: t.init.overwriteConfirm })
|
|
14
28
|
if (isCancel(overwrite) || !overwrite) {
|
|
15
|
-
bannerCancelled()
|
|
29
|
+
bannerCancelled(t.common.cancelled)
|
|
16
30
|
process.exit(0)
|
|
17
31
|
}
|
|
18
32
|
}
|
|
19
33
|
|
|
20
34
|
const projectName = await text({
|
|
21
|
-
message:
|
|
22
|
-
placeholder:
|
|
23
|
-
validate: (v) => (v.trim() ? undefined :
|
|
35
|
+
message: t.init.projectName,
|
|
36
|
+
placeholder: t.init.projectNamePlaceholder,
|
|
37
|
+
validate: (v) => (v.trim() ? undefined : t.init.projectNameError),
|
|
24
38
|
})
|
|
25
39
|
if (isCancel(projectName)) {
|
|
26
|
-
bannerCancelled()
|
|
40
|
+
bannerCancelled(t.common.cancelled)
|
|
27
41
|
process.exit(0)
|
|
28
42
|
}
|
|
29
43
|
|
|
30
|
-
const useTicket = await confirm({
|
|
31
|
-
message: 'Do you use a ticket/issue tracker? (e.g., Jira, Linear, GitHub Issues)',
|
|
32
|
-
})
|
|
44
|
+
const useTicket = await confirm({ message: t.init.ticketConfirm })
|
|
33
45
|
if (isCancel(useTicket)) {
|
|
34
|
-
bannerCancelled()
|
|
46
|
+
bannerCancelled(t.common.cancelled)
|
|
35
47
|
process.exit(0)
|
|
36
48
|
}
|
|
37
49
|
|
|
@@ -40,12 +52,12 @@ export async function runInit() {
|
|
|
40
52
|
|
|
41
53
|
if (useTicket) {
|
|
42
54
|
const prefix = await text({
|
|
43
|
-
message:
|
|
44
|
-
placeholder:
|
|
45
|
-
validate: (v) => (v.trim() ? undefined :
|
|
55
|
+
message: t.init.ticketPrefix,
|
|
56
|
+
placeholder: t.init.ticketPrefixPlaceholder,
|
|
57
|
+
validate: (v) => (v.trim() ? undefined : t.init.ticketPrefixError),
|
|
46
58
|
})
|
|
47
59
|
if (isCancel(prefix)) {
|
|
48
|
-
bannerCancelled()
|
|
60
|
+
bannerCancelled(t.common.cancelled)
|
|
49
61
|
process.exit(0)
|
|
50
62
|
}
|
|
51
63
|
ticketPrefix = prefix.trim().toUpperCase()
|
|
@@ -55,33 +67,33 @@ export async function runInit() {
|
|
|
55
67
|
let commitFormat = 'conventional'
|
|
56
68
|
if (useTicket) {
|
|
57
69
|
const selectedCommitFormat = await select({
|
|
58
|
-
message:
|
|
70
|
+
message: t.init.commitFormat,
|
|
59
71
|
options: COMMIT_FORMATS.map((f) => ({
|
|
60
72
|
...f,
|
|
61
73
|
label: f.label.replace(/PROJ/g, ticketPrefix || 'PROJ'),
|
|
62
74
|
})),
|
|
63
75
|
})
|
|
64
76
|
if (isCancel(selectedCommitFormat)) {
|
|
65
|
-
bannerCancelled()
|
|
77
|
+
bannerCancelled(t.common.cancelled)
|
|
66
78
|
process.exit(0)
|
|
67
79
|
}
|
|
68
80
|
commitFormat = selectedCommitFormat
|
|
69
81
|
}
|
|
70
82
|
|
|
71
83
|
const sourcesInput = await text({
|
|
72
|
-
message:
|
|
73
|
-
placeholder:
|
|
84
|
+
message: t.init.sources,
|
|
85
|
+
placeholder: t.init.sourcesPlaceholder,
|
|
74
86
|
initialValue: 'main,develop',
|
|
75
87
|
validate: (v) => {
|
|
76
88
|
const parts = v
|
|
77
89
|
.split(',')
|
|
78
90
|
.map((s) => s.trim())
|
|
79
91
|
.filter(Boolean)
|
|
80
|
-
return parts.length > 0 ? undefined :
|
|
92
|
+
return parts.length > 0 ? undefined : t.init.sourcesError
|
|
81
93
|
},
|
|
82
94
|
})
|
|
83
95
|
if (isCancel(sourcesInput)) {
|
|
84
|
-
bannerCancelled()
|
|
96
|
+
bannerCancelled(t.common.cancelled)
|
|
85
97
|
process.exit(0)
|
|
86
98
|
}
|
|
87
99
|
const sources = sourcesInput
|
|
@@ -92,44 +104,44 @@ export async function runInit() {
|
|
|
92
104
|
let branchFormat = 'type-name'
|
|
93
105
|
if (useTicket) {
|
|
94
106
|
const selectedBranchFormat = await select({
|
|
95
|
-
message:
|
|
107
|
+
message: t.init.branchFormat,
|
|
96
108
|
options: BRANCH_FORMATS.map((f) => ({
|
|
97
109
|
...f,
|
|
98
110
|
label: f.label.replace(/PROJ/g, ticketPrefix || 'PROJ'),
|
|
99
111
|
})),
|
|
100
112
|
})
|
|
101
113
|
if (isCancel(selectedBranchFormat)) {
|
|
102
|
-
bannerCancelled()
|
|
114
|
+
bannerCancelled(t.common.cancelled)
|
|
103
115
|
process.exit(0)
|
|
104
116
|
}
|
|
105
117
|
branchFormat = selectedBranchFormat
|
|
106
118
|
}
|
|
107
119
|
|
|
108
120
|
const useArea = await confirm({
|
|
109
|
-
message:
|
|
121
|
+
message: t.init.areaConfirm,
|
|
110
122
|
initialValue: false,
|
|
111
123
|
})
|
|
112
124
|
if (isCancel(useArea)) {
|
|
113
|
-
bannerCancelled()
|
|
125
|
+
bannerCancelled(t.common.cancelled)
|
|
114
126
|
process.exit(0)
|
|
115
127
|
}
|
|
116
128
|
|
|
117
129
|
let areas = []
|
|
118
130
|
if (useArea) {
|
|
119
131
|
const areasInput = await text({
|
|
120
|
-
message:
|
|
121
|
-
placeholder:
|
|
132
|
+
message: t.init.areaLabels,
|
|
133
|
+
placeholder: t.init.areaLabelsPlaceholder,
|
|
122
134
|
initialValue: 'FE,BE,DOC',
|
|
123
135
|
validate: (v) => {
|
|
124
136
|
const parts = v
|
|
125
137
|
.split(',')
|
|
126
138
|
.map((s) => s.trim())
|
|
127
139
|
.filter(Boolean)
|
|
128
|
-
return parts.length > 0 ? undefined :
|
|
140
|
+
return parts.length > 0 ? undefined : t.init.areaLabelsError
|
|
129
141
|
},
|
|
130
142
|
})
|
|
131
143
|
if (isCancel(areasInput)) {
|
|
132
|
-
bannerCancelled()
|
|
144
|
+
bannerCancelled(t.common.cancelled)
|
|
133
145
|
process.exit(0)
|
|
134
146
|
}
|
|
135
147
|
areas = areasInput
|
|
@@ -139,7 +151,7 @@ export async function runInit() {
|
|
|
139
151
|
}
|
|
140
152
|
|
|
141
153
|
const config = {
|
|
142
|
-
project: { name: projectName.trim() },
|
|
154
|
+
project: { name: projectName.trim(), lang },
|
|
143
155
|
commit: {
|
|
144
156
|
ticketEnabled: Boolean(useTicket),
|
|
145
157
|
ticketPrefix,
|
|
@@ -156,5 +168,5 @@ export async function runInit() {
|
|
|
156
168
|
}
|
|
157
169
|
|
|
158
170
|
const configPath = saveConfig(config)
|
|
159
|
-
bannerOutro(
|
|
171
|
+
bannerOutro(t.init.saved(configPath))
|
|
160
172
|
}
|
package/commands/push.mjs
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { confirm, isCancel, log } from '@clack/prompts'
|
|
2
2
|
import { bannerIntro, bannerOutro, bannerCancelled } from '../lib/banner.mjs'
|
|
3
3
|
import { getCurrentBranch } from '../lib/git.mjs'
|
|
4
|
+
import { loadConfig } from '../lib/config.mjs'
|
|
5
|
+
import { getLocale } from '../lib/i18n.mjs'
|
|
4
6
|
import { spawnSync } from 'child_process'
|
|
5
7
|
|
|
6
8
|
// Called by commander as runPush(options, command)
|
|
7
9
|
export async function runPush(opts = {}) {
|
|
8
10
|
bannerIntro('push')
|
|
9
11
|
|
|
12
|
+
const t = getLocale(loadConfig())
|
|
10
13
|
const branch = getCurrentBranch()
|
|
11
14
|
|
|
12
15
|
if (!opts.yes) {
|
|
13
|
-
const confirmed = await confirm({
|
|
14
|
-
message: `Push current branch: ${branch}?`,
|
|
15
|
-
})
|
|
16
|
+
const confirmed = await confirm({ message: t.push.confirm(branch) })
|
|
16
17
|
if (isCancel(confirmed) || !confirmed) {
|
|
17
|
-
bannerCancelled()
|
|
18
|
+
bannerCancelled(t.common.cancelled)
|
|
18
19
|
process.exit(0)
|
|
19
20
|
}
|
|
20
21
|
}
|
|
@@ -23,9 +24,9 @@ export async function runPush(opts = {}) {
|
|
|
23
24
|
stdio: ['pipe', 'inherit', 'inherit'],
|
|
24
25
|
})
|
|
25
26
|
if (result.status !== 0) {
|
|
26
|
-
log.error(
|
|
27
|
+
log.error(t.push.failed)
|
|
27
28
|
process.exit(1)
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
bannerOutro(
|
|
31
|
+
bannerOutro(t.push.done(branch))
|
|
31
32
|
}
|
package/commands/start.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { select, isCancel, intro, outro } from '@clack/prompts'
|
|
2
2
|
import pc from 'picocolors'
|
|
3
3
|
import { banner as brandBanner, uva, folha } from '../lib/colors.mjs'
|
|
4
|
+
import { loadConfig } from '../lib/config.mjs'
|
|
5
|
+
import { getLocale } from '../lib/i18n.mjs'
|
|
4
6
|
import { runInit } from './init.mjs'
|
|
5
7
|
import { runCommit } from './commit.mjs'
|
|
6
8
|
import { runBranch } from './branch.mjs'
|
|
@@ -8,48 +10,51 @@ import { runNewFile } from './new-file.mjs'
|
|
|
8
10
|
import { runPush } from './push.mjs'
|
|
9
11
|
|
|
10
12
|
export async function runStart() {
|
|
13
|
+
const t = getLocale(loadConfig())
|
|
14
|
+
|
|
11
15
|
intro(brandBanner('UVA CLI'))
|
|
12
16
|
|
|
13
17
|
console.log('')
|
|
14
|
-
console.log(uva(' uva-cli') + pc.dim(
|
|
18
|
+
console.log(uva(' uva-cli') + pc.dim(` ${t.start.tagline}`))
|
|
15
19
|
console.log('')
|
|
16
|
-
console.log(pc.dim(
|
|
17
|
-
console.log(pc.dim(
|
|
20
|
+
console.log(pc.dim(` ${t.start.desc1}`))
|
|
21
|
+
console.log(pc.dim(` ${t.start.desc2}`))
|
|
18
22
|
console.log('')
|
|
19
23
|
|
|
24
|
+
const o = t.start.options
|
|
20
25
|
const action = await select({
|
|
21
|
-
message:
|
|
26
|
+
message: t.start.prompt,
|
|
22
27
|
options: [
|
|
23
28
|
{
|
|
24
29
|
value: 'init',
|
|
25
|
-
label: folha('uva init') + '
|
|
26
|
-
hint:
|
|
30
|
+
label: folha('uva init') + ' ' + o.init.label,
|
|
31
|
+
hint: o.init.hint,
|
|
27
32
|
},
|
|
28
33
|
{
|
|
29
34
|
value: 'commit',
|
|
30
|
-
label: uva('uva commit') + '
|
|
31
|
-
hint:
|
|
35
|
+
label: uva('uva commit') + ' ' + o.commit.label,
|
|
36
|
+
hint: o.commit.hint,
|
|
32
37
|
},
|
|
33
38
|
{
|
|
34
39
|
value: 'branch',
|
|
35
|
-
label: uva('uva branch') + '
|
|
36
|
-
hint:
|
|
40
|
+
label: uva('uva branch') + ' ' + o.branch.label,
|
|
41
|
+
hint: o.branch.hint,
|
|
37
42
|
},
|
|
38
43
|
{
|
|
39
44
|
value: 'new-file',
|
|
40
|
-
label: uva('uva new-file') + '
|
|
41
|
-
hint:
|
|
45
|
+
label: uva('uva new-file') + ' ' + o.newFile.label,
|
|
46
|
+
hint: o.newFile.hint,
|
|
42
47
|
},
|
|
43
48
|
{
|
|
44
49
|
value: 'push',
|
|
45
|
-
label: uva('uva push') + '
|
|
46
|
-
hint:
|
|
50
|
+
label: uva('uva push') + ' ' + o.push.label,
|
|
51
|
+
hint: o.push.hint,
|
|
47
52
|
},
|
|
48
53
|
],
|
|
49
54
|
})
|
|
50
55
|
|
|
51
56
|
if (isCancel(action)) {
|
|
52
|
-
outro(pc.dim(
|
|
57
|
+
outro(pc.dim(t.common.cancelled))
|
|
53
58
|
process.exit(0)
|
|
54
59
|
}
|
|
55
60
|
|
package/lib/banner.mjs
CHANGED
package/lib/git.mjs
CHANGED
|
@@ -51,7 +51,7 @@ export function addFiles(paths) {
|
|
|
51
51
|
stdio: 'pipe',
|
|
52
52
|
})
|
|
53
53
|
if (result.status !== 0) {
|
|
54
|
-
throw new Error(result.stderr || 'git add
|
|
54
|
+
throw new Error(result.stderr || 'git add failed')
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -62,6 +62,6 @@ export function commit(message) {
|
|
|
62
62
|
stdio: ['pipe', 'inherit', 'inherit'],
|
|
63
63
|
})
|
|
64
64
|
if (result.status !== 0) {
|
|
65
|
-
throw new Error('git commit
|
|
65
|
+
throw new Error('git commit failed — see output above')
|
|
66
66
|
}
|
|
67
67
|
}
|
package/lib/i18n.mjs
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
init: {
|
|
3
|
+
langSelect: 'Language / Idioma',
|
|
4
|
+
overwriteConfirm: 'A configuration already exists. Do you want to overwrite it?',
|
|
5
|
+
projectName: 'Project name',
|
|
6
|
+
projectNamePlaceholder: 'My Awesome Project',
|
|
7
|
+
projectNameError: 'Project name cannot be empty.',
|
|
8
|
+
ticketConfirm: 'Do you use a ticket/issue tracker? (e.g., Jira, Linear, GitHub Issues)',
|
|
9
|
+
ticketPrefix: 'Ticket prefix',
|
|
10
|
+
ticketPrefixPlaceholder: 'PROJ',
|
|
11
|
+
ticketPrefixError: 'Ticket prefix cannot be empty.',
|
|
12
|
+
commitFormat: 'Choose a commit message format',
|
|
13
|
+
sources: 'Branches to branch from (comma-separated)',
|
|
14
|
+
sourcesPlaceholder: 'main,develop',
|
|
15
|
+
sourcesError: 'At least one branch is required.',
|
|
16
|
+
branchFormat: 'Choose a branch naming format',
|
|
17
|
+
areaConfirm: 'Do you want to categorize branches by area? (e.g., FE, BE, DOC)',
|
|
18
|
+
areaLabels: 'Area labels (comma-separated)',
|
|
19
|
+
areaLabelsPlaceholder: 'FE,BE,DOC',
|
|
20
|
+
areaLabelsError: 'At least one area is required.',
|
|
21
|
+
saved: (path) => `Configuration saved to ${path}`,
|
|
22
|
+
},
|
|
23
|
+
commit: {
|
|
24
|
+
nothingToCommit: 'Nothing to commit here.',
|
|
25
|
+
invalidType: (type, valid) => `Invalid --type "${type}". Valid values: ${valid}`,
|
|
26
|
+
unknownFiles: (invalid, available) => `Unknown file(s): ${invalid}\nAvailable: ${available}`,
|
|
27
|
+
type: 'Commit type',
|
|
28
|
+
ticket: 'Ticket',
|
|
29
|
+
ticketError: 'Ticket cannot be empty.',
|
|
30
|
+
message: 'Commit message',
|
|
31
|
+
messagePlaceholder: 'add login screen',
|
|
32
|
+
messageError: 'Message cannot be empty.',
|
|
33
|
+
messageLong:
|
|
34
|
+
'Message is long (over 72 characters). Shorter messages are recommended, but you can continue.',
|
|
35
|
+
files: 'Which files to include in the commit?',
|
|
36
|
+
allFiles: 'All files',
|
|
37
|
+
confirm: (msg) => `Commit with message:\n ${msg}`,
|
|
38
|
+
retry: "OK! Let's rewrite the message.",
|
|
39
|
+
failed: (err) => `Commit failed: ${err}`,
|
|
40
|
+
done: (msg) => `Committed: ${msg}`,
|
|
41
|
+
},
|
|
42
|
+
branch: {
|
|
43
|
+
uncommittedConfirm:
|
|
44
|
+
'You have uncommitted changes. They will carry over to the new branch. Continue?',
|
|
45
|
+
uncommittedTip: 'Tip: run `uva commit` first to commit pending changes.',
|
|
46
|
+
source: 'Branch from',
|
|
47
|
+
sourceWarn: (src) => `"${src}" is not in the configured source branches. Proceeding anyway.`,
|
|
48
|
+
ticket: 'Ticket',
|
|
49
|
+
ticketError: 'Ticket cannot be empty.',
|
|
50
|
+
type: 'Branch type',
|
|
51
|
+
invalidType: (type, valid) => `Invalid --type "${type}". Valid values: ${valid}`,
|
|
52
|
+
name: 'Task name',
|
|
53
|
+
namePlaceholder: 'add login screen',
|
|
54
|
+
nameError: 'Task name cannot be empty.',
|
|
55
|
+
confirm: (name) => `Create branch:\n ${name}`,
|
|
56
|
+
checkoutError: (src) =>
|
|
57
|
+
`Could not switch to ${src}. Make sure the branch exists and there are no conflicts.`,
|
|
58
|
+
pullError: 'Could not pull. Check your connection or resolve any conflicts.',
|
|
59
|
+
branchExists: (name) => `Branch "${name}" already exists. Choose a different name or ticket.`,
|
|
60
|
+
createError: 'Could not create the branch. Check if the name is valid.',
|
|
61
|
+
done: (name) => `Branch created: ${name}`,
|
|
62
|
+
},
|
|
63
|
+
push: {
|
|
64
|
+
confirm: (branch) => `Push current branch: ${branch}?`,
|
|
65
|
+
failed: 'Push failed — see the output above.',
|
|
66
|
+
done: (branch) => `Pushed: origin/${branch}`,
|
|
67
|
+
},
|
|
68
|
+
start: {
|
|
69
|
+
tagline: '— Git workflow automation',
|
|
70
|
+
desc1: 'Guides your team through branch creation and commits',
|
|
71
|
+
desc2: 'following whatever conventions your project defines.',
|
|
72
|
+
prompt: 'What do you want to do?',
|
|
73
|
+
options: {
|
|
74
|
+
init: {
|
|
75
|
+
label: 'Set up UVA CLI for this project',
|
|
76
|
+
hint: 'configure commit and branch patterns',
|
|
77
|
+
},
|
|
78
|
+
commit: {
|
|
79
|
+
label: 'Create an interactive commit',
|
|
80
|
+
hint: 'select files, type, ticket and message',
|
|
81
|
+
},
|
|
82
|
+
branch: { label: 'Create a new branch', hint: 'checks out source and pulls automatically' },
|
|
83
|
+
newFile: {
|
|
84
|
+
label: 'Scaffold a file from a template',
|
|
85
|
+
hint: 'docs and git workflow templates',
|
|
86
|
+
},
|
|
87
|
+
push: {
|
|
88
|
+
label: 'Push the current branch to origin',
|
|
89
|
+
hint: 'confirms the branch and runs git push',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
common: {
|
|
94
|
+
cancelled: 'Operation cancelled.',
|
|
95
|
+
},
|
|
96
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
init: {
|
|
3
|
+
langSelect: 'Language / Idioma',
|
|
4
|
+
overwriteConfirm: 'Já existe uma configuração. Deseja sobrescrever?',
|
|
5
|
+
projectName: 'Nome do projeto',
|
|
6
|
+
projectNamePlaceholder: 'Meu Projeto Incrível',
|
|
7
|
+
projectNameError: 'O nome do projeto não pode estar vazio.',
|
|
8
|
+
ticketConfirm: 'Você usa um rastreador de tickets? (ex.: Jira, Linear, GitHub Issues)',
|
|
9
|
+
ticketPrefix: 'Prefixo do ticket',
|
|
10
|
+
ticketPrefixPlaceholder: 'PROJ',
|
|
11
|
+
ticketPrefixError: 'O prefixo não pode estar vazio.',
|
|
12
|
+
commitFormat: 'Escolha o formato de mensagem de commit',
|
|
13
|
+
sources: 'Branches de origem (separados por vírgula)',
|
|
14
|
+
sourcesPlaceholder: 'main,develop',
|
|
15
|
+
sourcesError: 'Pelo menos um branch é obrigatório.',
|
|
16
|
+
branchFormat: 'Escolha o formato de nome de branch',
|
|
17
|
+
areaConfirm: 'Deseja categorizar os branches por área? (ex.: FE, BE, DOC)',
|
|
18
|
+
areaLabels: 'Áreas (separadas por vírgula)',
|
|
19
|
+
areaLabelsPlaceholder: 'FE,BE,DOC',
|
|
20
|
+
areaLabelsError: 'Pelo menos uma área é obrigatória.',
|
|
21
|
+
saved: (path) => `Configuração salva em ${path}`,
|
|
22
|
+
},
|
|
23
|
+
commit: {
|
|
24
|
+
nothingToCommit: 'Nada para commitar.',
|
|
25
|
+
invalidType: (type, valid) => `Tipo inválido "${type}". Valores válidos: ${valid}`,
|
|
26
|
+
unknownFiles: (invalid, available) =>
|
|
27
|
+
`Arquivo(s) desconhecido(s): ${invalid}\nDisponíveis: ${available}`,
|
|
28
|
+
type: 'Tipo do commit',
|
|
29
|
+
ticket: 'Ticket',
|
|
30
|
+
ticketError: 'O ticket não pode estar vazio.',
|
|
31
|
+
message: 'Mensagem do commit',
|
|
32
|
+
messagePlaceholder: 'adicionar tela de login',
|
|
33
|
+
messageError: 'A mensagem não pode estar vazia.',
|
|
34
|
+
messageLong:
|
|
35
|
+
'Mensagem longa (mais de 72 caracteres). Mensagens curtas são recomendadas, mas você pode continuar.',
|
|
36
|
+
files: 'Quais arquivos incluir no commit?',
|
|
37
|
+
allFiles: 'Todos os arquivos',
|
|
38
|
+
confirm: (msg) => `Commitar com a mensagem:\n ${msg}`,
|
|
39
|
+
retry: 'Ok! Vamos reescrever a mensagem.',
|
|
40
|
+
failed: (err) => `Commit falhou: ${err}`,
|
|
41
|
+
done: (msg) => `Commitado: ${msg}`,
|
|
42
|
+
},
|
|
43
|
+
branch: {
|
|
44
|
+
uncommittedConfirm:
|
|
45
|
+
'Você tem alterações não commitadas. Elas serão levadas para o novo branch. Continuar?',
|
|
46
|
+
uncommittedTip: 'Dica: rode `uva commit` primeiro para commitar as alterações pendentes.',
|
|
47
|
+
source: 'Branch de origem',
|
|
48
|
+
sourceWarn: (src) =>
|
|
49
|
+
`"${src}" não está nos branches de origem configurados. Continuando mesmo assim.`,
|
|
50
|
+
ticket: 'Ticket',
|
|
51
|
+
ticketError: 'O ticket não pode estar vazio.',
|
|
52
|
+
type: 'Tipo do branch',
|
|
53
|
+
invalidType: (type, valid) => `Tipo inválido "${type}". Valores válidos: ${valid}`,
|
|
54
|
+
name: 'Nome da tarefa',
|
|
55
|
+
namePlaceholder: 'adicionar tela de login',
|
|
56
|
+
nameError: 'O nome da tarefa não pode estar vazio.',
|
|
57
|
+
confirm: (name) => `Criar branch:\n ${name}`,
|
|
58
|
+
checkoutError: (src) =>
|
|
59
|
+
`Não foi possível mudar para ${src}. Verifique se o branch existe e não há conflitos.`,
|
|
60
|
+
pullError: 'Não foi possível fazer pull. Verifique sua conexão ou resolva conflitos.',
|
|
61
|
+
branchExists: (name) => `O branch "${name}" já existe. Escolha um nome ou ticket diferente.`,
|
|
62
|
+
createError: 'Não foi possível criar o branch. Verifique se o nome é válido.',
|
|
63
|
+
done: (name) => `Branch criado: ${name}`,
|
|
64
|
+
},
|
|
65
|
+
push: {
|
|
66
|
+
confirm: (branch) => `Fazer push do branch atual: ${branch}?`,
|
|
67
|
+
failed: 'Push falhou — veja a saída acima.',
|
|
68
|
+
done: (branch) => `Push realizado: origin/${branch}`,
|
|
69
|
+
},
|
|
70
|
+
start: {
|
|
71
|
+
tagline: '— Automação de workflow Git',
|
|
72
|
+
desc1: 'Guia seu time na criação de branches e commits',
|
|
73
|
+
desc2: 'seguindo as convenções definidas no seu projeto.',
|
|
74
|
+
prompt: 'O que você quer fazer?',
|
|
75
|
+
options: {
|
|
76
|
+
init: {
|
|
77
|
+
label: 'Configurar o UVA CLI para este projeto',
|
|
78
|
+
hint: 'configura padrões de commit e branch',
|
|
79
|
+
},
|
|
80
|
+
commit: {
|
|
81
|
+
label: 'Criar um commit interativo',
|
|
82
|
+
hint: 'selecione arquivos, tipo, ticket e mensagem',
|
|
83
|
+
},
|
|
84
|
+
branch: {
|
|
85
|
+
label: 'Criar um novo branch',
|
|
86
|
+
hint: 'faz checkout da origem e pull automaticamente',
|
|
87
|
+
},
|
|
88
|
+
newFile: {
|
|
89
|
+
label: 'Gerar um arquivo a partir de um template',
|
|
90
|
+
hint: 'templates de docs e workflow Git',
|
|
91
|
+
},
|
|
92
|
+
push: {
|
|
93
|
+
label: 'Fazer push do branch atual para a origin',
|
|
94
|
+
hint: 'confirma o branch e executa git push',
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
common: {
|
|
99
|
+
cancelled: 'Operação cancelada.',
|
|
100
|
+
},
|
|
101
|
+
}
|