@minecraft-docker/mcctl 2.3.4 → 2.4.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.
@@ -0,0 +1,219 @@
1
+ /**
2
+ * .env template diffing library for upgrade command.
3
+ * Compares a template .env.example with a user's .env file
4
+ * and identifies new variables that need to be added.
5
+ *
6
+ * Key principle: NEVER modify existing user values (append-only).
7
+ */
8
+ /**
9
+ * Check if a comment line is a section header.
10
+ * Section headers are lines like "# Section Name" that describe groups of variables.
11
+ * Excludes lines that are commented-out variables (# VAR=value),
12
+ * delimiter lines (# ====), and multi-line description comments.
13
+ */
14
+ function isSectionHeader(line) {
15
+ const trimmed = line.trim();
16
+ // Must start with # but not be a delimiter line
17
+ if (!trimmed.startsWith('#'))
18
+ return false;
19
+ const content = trimmed.slice(1).trim();
20
+ // Skip empty comments
21
+ if (!content)
22
+ return false;
23
+ // Skip delimiter lines
24
+ if (/^[=\-*]+$/.test(content))
25
+ return false;
26
+ // Skip commented-out variables
27
+ if (/^[A-Z_][A-Z0-9_]*=/.test(content))
28
+ return false;
29
+ // Skip description-like lines (start with lowercase, or contain typical description patterns)
30
+ if (/^[a-z]/.test(content))
31
+ return false;
32
+ if (content.startsWith('Setup:') || content.startsWith('Create') || content.startsWith('Token') || content.startsWith('Start') || content.startsWith('Or ') || content.startsWith('docker '))
33
+ return false;
34
+ // Skip lines that reference URLs or are instructions
35
+ if (content.includes('http://') || content.includes('https://'))
36
+ return false;
37
+ // Skip numbered list items
38
+ if (/^\d+\./.test(content))
39
+ return false;
40
+ // Skip lines starting with "e.g." or containing example patterns
41
+ if (content.startsWith('e.g.') || content.includes('e.g.'))
42
+ return false;
43
+ // Skip "Copy this", "Use pre-built", "Generate with", etc. instructional lines
44
+ if (/^(Copy |Use |Generate |Backup |Web-|Allowed |Public |JWT |NextAuth |Access |API |Or |Enables |If )/.test(content))
45
+ return false;
46
+ // Accept if it looks like a section title (capitalized words, short-ish)
47
+ if (/^[A-Z]/.test(content) && content.length < 80)
48
+ return true;
49
+ return false;
50
+ }
51
+ /**
52
+ * Check if a comment line is a commented-out variable definition.
53
+ * Pattern: # KEY=value (where KEY is uppercase with underscores/numbers)
54
+ */
55
+ function isCommentedVar(line) {
56
+ const trimmed = line.trim();
57
+ const match = trimmed.match(/^#\s*([A-Z_][A-Z0-9_]*)=(.*)$/);
58
+ if (match) {
59
+ return { key: match[1], value: match[2] };
60
+ }
61
+ return null;
62
+ }
63
+ /**
64
+ * Parse a .env template file into structured format.
65
+ * Preserves section headers and distinguishes between
66
+ * active variables and commented-out variables.
67
+ */
68
+ export function parseEnvTemplate(content) {
69
+ const lines = content.split('\n');
70
+ const vars = [];
71
+ let currentSection = '';
72
+ for (const line of lines) {
73
+ const trimmed = line.trim();
74
+ // Empty line - skip
75
+ if (!trimmed)
76
+ continue;
77
+ // Check for section header
78
+ if (isSectionHeader(trimmed)) {
79
+ currentSection = trimmed.slice(1).trim();
80
+ continue;
81
+ }
82
+ // Check for commented-out variable
83
+ const commentedVar = isCommentedVar(trimmed);
84
+ if (commentedVar) {
85
+ vars.push({
86
+ key: commentedVar.key,
87
+ defaultValue: commentedVar.value,
88
+ commented: true,
89
+ section: currentSection,
90
+ });
91
+ continue;
92
+ }
93
+ // Check for active variable
94
+ if (!trimmed.startsWith('#')) {
95
+ const eqIndex = trimmed.indexOf('=');
96
+ if (eqIndex > 0) {
97
+ const key = trimmed.slice(0, eqIndex).trim();
98
+ // Validate key is a valid env var name
99
+ if (/^[A-Z_][A-Z0-9_]*$/.test(key)) {
100
+ const value = trimmed.slice(eqIndex + 1);
101
+ vars.push({
102
+ key,
103
+ defaultValue: value,
104
+ commented: false,
105
+ section: currentSection,
106
+ });
107
+ }
108
+ }
109
+ }
110
+ }
111
+ return { vars };
112
+ }
113
+ /**
114
+ * Extract all variable keys from user .env file content.
115
+ * Returns a Set of keys that exist in the user's file,
116
+ * whether active or commented out.
117
+ */
118
+ function extractUserKeys(userEnv) {
119
+ const keys = new Set();
120
+ const lines = userEnv.split('\n');
121
+ for (const line of lines) {
122
+ const trimmed = line.trim();
123
+ if (!trimmed)
124
+ continue;
125
+ // Active variable
126
+ if (!trimmed.startsWith('#')) {
127
+ const eqIndex = trimmed.indexOf('=');
128
+ if (eqIndex > 0) {
129
+ const key = trimmed.slice(0, eqIndex).trim();
130
+ keys.add(key);
131
+ }
132
+ continue;
133
+ }
134
+ // Commented-out variable
135
+ const commentedVar = isCommentedVar(trimmed);
136
+ if (commentedVar) {
137
+ keys.add(commentedVar.key);
138
+ }
139
+ }
140
+ return keys;
141
+ }
142
+ /**
143
+ * Diff template .env against user .env to find new variables.
144
+ * Returns variables that exist in the template but not in the user's file.
145
+ */
146
+ export function diffEnvFiles(template, userEnv) {
147
+ const parsed = parseEnvTemplate(template);
148
+ const userKeys = extractUserKeys(userEnv);
149
+ const newVars = [];
150
+ const newCommentedVars = [];
151
+ for (const v of parsed.vars) {
152
+ if (userKeys.has(v.key))
153
+ continue;
154
+ if (v.commented) {
155
+ newCommentedVars.push({
156
+ key: v.key,
157
+ defaultValue: v.defaultValue,
158
+ section: v.section,
159
+ });
160
+ }
161
+ else {
162
+ newVars.push({
163
+ key: v.key,
164
+ defaultValue: v.defaultValue,
165
+ section: v.section,
166
+ });
167
+ }
168
+ }
169
+ return { newVars, newCommentedVars };
170
+ }
171
+ /**
172
+ * Apply env diff to user's .env file content.
173
+ * Appends new variables at the end, grouped by section.
174
+ * Uses values from the `values` map when available, otherwise uses defaults.
175
+ *
176
+ * @param userEnv - Current user .env file content
177
+ * @param diff - Diff result from diffEnvFiles
178
+ * @param values - User-provided values for new variables
179
+ * @returns Updated .env file content
180
+ */
181
+ export function applyEnvDiff(userEnv, diff, values) {
182
+ const allItems = [
183
+ ...diff.newVars.map((v) => ({ ...v, commented: false })),
184
+ ...diff.newCommentedVars.map((v) => ({ ...v, commented: true })),
185
+ ];
186
+ if (allItems.length === 0) {
187
+ return userEnv;
188
+ }
189
+ // Group by section
190
+ const sections = new Map();
191
+ for (const item of allItems) {
192
+ const section = item.section;
193
+ if (!sections.has(section)) {
194
+ sections.set(section, []);
195
+ }
196
+ sections.get(section).push(item);
197
+ }
198
+ const lines = [];
199
+ // Add separator
200
+ lines.push('');
201
+ lines.push('# --- Added by mcctl upgrade ---');
202
+ for (const [section, items] of sections) {
203
+ if (section) {
204
+ lines.push('');
205
+ lines.push(`# ${section}`);
206
+ }
207
+ for (const item of items) {
208
+ const value = values.get(item.key) ?? item.defaultValue;
209
+ if (item.commented) {
210
+ lines.push(`# ${item.key}=${item.defaultValue}`);
211
+ }
212
+ else {
213
+ lines.push(`${item.key}=${value}`);
214
+ }
215
+ }
216
+ }
217
+ return userEnv + lines.join('\n');
218
+ }
219
+ //# sourceMappingURL=env-diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-diff.js","sourceRoot":"","sources":["../../src/lib/env-diff.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minecraft-docker/mcctl",
3
- "version": "2.3.4",
3
+ "version": "2.4.0",
4
4
  "description": "CLI tool for managing Docker Minecraft servers with mc-router",
5
5
  "type": "module",
6
6
  "bin": {
@@ -55,8 +55,8 @@
55
55
  ],
56
56
  "dependencies": {
57
57
  "@clack/prompts": "^0.8.0",
58
- "@minecraft-docker/mod-source-modrinth": "^2.3.4",
59
- "@minecraft-docker/shared": "^2.3.4",
58
+ "@minecraft-docker/mod-source-modrinth": "^2.4.0",
59
+ "@minecraft-docker/shared": "^2.4.0",
60
60
  "better-sqlite3": "^12.6.2",
61
61
  "commander": "^12.0.0",
62
62
  "js-yaml": "^4.1.0",