at-builder 1.4.5 → 1.5.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/README.md +38 -0
- package/bin/constants/config.js +1 -1
- package/bin/index.js +153 -66
- package/bin/services/telemetry.js +320 -0
- package/package.json +12 -1
- package/.vscode/settings.json +0 -6
- package/DEVELOPMENT.md +0 -164
- package/at-builder-0.0.2.vsix +0 -0
- package/src/constants/config.ts +0 -321
- package/src/index.ts +0 -387
- package/src/services/doctor.ts +0 -724
- package/src/services/logger.ts +0 -84
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
13
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
39
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
|
+
};
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.trackTelemetry = void 0;
|
|
43
|
+
var os_1 = __importDefault(require("os"));
|
|
44
|
+
var fs_1 = __importDefault(require("fs"));
|
|
45
|
+
var path_1 = __importDefault(require("path"));
|
|
46
|
+
var crypto_1 = __importDefault(require("crypto"));
|
|
47
|
+
var child_process_1 = require("child_process");
|
|
48
|
+
var axios_1 = __importDefault(require("axios"));
|
|
49
|
+
var config_1 = require("../constants/config");
|
|
50
|
+
var logger_1 = __importDefault(require("./logger"));
|
|
51
|
+
// Generate a unique identifier for this CLI execution run
|
|
52
|
+
var eventId = crypto_1.default.randomUUID();
|
|
53
|
+
var startTime = performance.now();
|
|
54
|
+
// Cache user details to avoid multiple shell exec calls
|
|
55
|
+
var cachedUser = null;
|
|
56
|
+
var getUserDetails = function () {
|
|
57
|
+
if (cachedUser)
|
|
58
|
+
return cachedUser;
|
|
59
|
+
var osUsername = os_1.default.userInfo().username || process.env.USER || process.env.USERNAME || "unknown";
|
|
60
|
+
var gitEmail;
|
|
61
|
+
var gitName;
|
|
62
|
+
try {
|
|
63
|
+
gitEmail = (0, child_process_1.execSync)("git config user.email", { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
// Git config email not set or not in a git repo
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
gitName = (0, child_process_1.execSync)("git config user.name", { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
// Git config name not set or not in a git repo
|
|
73
|
+
}
|
|
74
|
+
cachedUser = {
|
|
75
|
+
osUsername: osUsername,
|
|
76
|
+
gitEmail: gitEmail || undefined,
|
|
77
|
+
gitName: gitName || undefined,
|
|
78
|
+
};
|
|
79
|
+
return cachedUser;
|
|
80
|
+
};
|
|
81
|
+
// Sanitize command line arguments to avoid logging sensitive information
|
|
82
|
+
var sanitizeArgs = function (args) {
|
|
83
|
+
return args.map(function (arg) {
|
|
84
|
+
// Redact any argument value that looks like a secret, token, key or password
|
|
85
|
+
var lowercaseArg = arg.toLowerCase();
|
|
86
|
+
if (lowercaseArg.includes("secret") ||
|
|
87
|
+
lowercaseArg.includes("token") ||
|
|
88
|
+
lowercaseArg.includes("key") ||
|
|
89
|
+
lowercaseArg.includes("password")) {
|
|
90
|
+
// If it's an option value like --token=12345 or similar, redact the value part
|
|
91
|
+
if (arg.includes("=")) {
|
|
92
|
+
var parts = arg.split("=");
|
|
93
|
+
return "".concat(parts[0], "=[REDACTED]");
|
|
94
|
+
}
|
|
95
|
+
return "[REDACTED]";
|
|
96
|
+
}
|
|
97
|
+
return arg;
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Retrieves or generates a persistent client ID for Google Analytics user identification.
|
|
102
|
+
*/
|
|
103
|
+
var getClientId = function () {
|
|
104
|
+
try {
|
|
105
|
+
var telemetryDir = path_1.default.join(os_1.default.homedir(), ".atb-builder");
|
|
106
|
+
var clientIdFile = path_1.default.join(telemetryDir, "client_id");
|
|
107
|
+
if (!fs_1.default.existsSync(telemetryDir)) {
|
|
108
|
+
fs_1.default.mkdirSync(telemetryDir, { recursive: true });
|
|
109
|
+
}
|
|
110
|
+
if (fs_1.default.existsSync(clientIdFile)) {
|
|
111
|
+
return fs_1.default.readFileSync(clientIdFile, "utf8").trim();
|
|
112
|
+
}
|
|
113
|
+
var newClientId = crypto_1.default.randomUUID();
|
|
114
|
+
fs_1.default.writeFileSync(clientIdFile, newClientId, "utf8");
|
|
115
|
+
return newClientId;
|
|
116
|
+
}
|
|
117
|
+
catch (e) {
|
|
118
|
+
return crypto_1.default.randomUUID(); // Fallback to transient random if file access fails
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Sends telemetry event to a remote custom endpoint if configured.
|
|
123
|
+
*/
|
|
124
|
+
var sendRemoteTelemetry = function (url, payload) { return __awaiter(void 0, void 0, void 0, function () {
|
|
125
|
+
var error_1;
|
|
126
|
+
return __generator(this, function (_a) {
|
|
127
|
+
switch (_a.label) {
|
|
128
|
+
case 0:
|
|
129
|
+
_a.trys.push([0, 2, , 3]);
|
|
130
|
+
logger_1.default.info("telemetry", "Sending telemetry to ".concat(url));
|
|
131
|
+
// We set a 500ms timeout to ensure we don't hold up CLI execution.
|
|
132
|
+
return [4 /*yield*/, axios_1.default.post(url, payload, {
|
|
133
|
+
headers: { "Content-Type": "application/json" },
|
|
134
|
+
timeout: 500,
|
|
135
|
+
})];
|
|
136
|
+
case 1:
|
|
137
|
+
// We set a 500ms timeout to ensure we don't hold up CLI execution.
|
|
138
|
+
_a.sent();
|
|
139
|
+
logger_1.default.info("telemetry", "Remote telemetry event successfully sent");
|
|
140
|
+
return [3 /*break*/, 3];
|
|
141
|
+
case 2:
|
|
142
|
+
error_1 = _a.sent();
|
|
143
|
+
// Silence telemetry send errors so the CLI never fails due to tracking issues
|
|
144
|
+
logger_1.default.error("telemetry", "Failed to send remote telemetry: ".concat(error_1.message));
|
|
145
|
+
return [3 /*break*/, 3];
|
|
146
|
+
case 3: return [2 /*return*/];
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}); };
|
|
150
|
+
/**
|
|
151
|
+
* Sends telemetry event to Google Analytics 4 via Measurement Protocol.
|
|
152
|
+
*/
|
|
153
|
+
var sendGA4Telemetry = function (measurementId, apiSecret, payload) { return __awaiter(void 0, void 0, void 0, function () {
|
|
154
|
+
var url, clientId, userEmail, gaPayload, error_2;
|
|
155
|
+
return __generator(this, function (_a) {
|
|
156
|
+
switch (_a.label) {
|
|
157
|
+
case 0:
|
|
158
|
+
// If placeholders are present, do not attempt to send
|
|
159
|
+
if (!measurementId ||
|
|
160
|
+
measurementId === "YOUR_GA_MEASUREMENT_ID" ||
|
|
161
|
+
!apiSecret ||
|
|
162
|
+
apiSecret === "YOUR_GA_API_SECRET") {
|
|
163
|
+
logger_1.default.info("telemetry", "GA4 telemetry credentials not configured");
|
|
164
|
+
return [2 /*return*/];
|
|
165
|
+
}
|
|
166
|
+
_a.label = 1;
|
|
167
|
+
case 1:
|
|
168
|
+
_a.trys.push([1, 3, , 4]);
|
|
169
|
+
url = "https://www.google-analytics.com/mp/collect?measurement_id=".concat(measurementId, "&api_secret=").concat(apiSecret);
|
|
170
|
+
clientId = getClientId();
|
|
171
|
+
userEmail = payload.user.gitEmail || payload.user.osUsername;
|
|
172
|
+
gaPayload = {
|
|
173
|
+
client_id: clientId,
|
|
174
|
+
user_id: userEmail,
|
|
175
|
+
events: [
|
|
176
|
+
{
|
|
177
|
+
name: "atb_command_run",
|
|
178
|
+
params: {
|
|
179
|
+
command: payload.command,
|
|
180
|
+
status: payload.execution.status,
|
|
181
|
+
duration_ms: payload.execution.durationMs || 0,
|
|
182
|
+
error_message: payload.execution.errorMessage || "",
|
|
183
|
+
os_username: payload.user.osUsername,
|
|
184
|
+
git_email: payload.user.gitEmail || "",
|
|
185
|
+
git_name: payload.user.gitName || "",
|
|
186
|
+
project_name: payload.project.projectName,
|
|
187
|
+
project_path: payload.project.projectPath,
|
|
188
|
+
tenant: payload.project.tenant,
|
|
189
|
+
platform: payload.system.platform,
|
|
190
|
+
node_version: payload.system.nodeVersion,
|
|
191
|
+
cli_version: payload.system.cliVersion,
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
};
|
|
196
|
+
logger_1.default.info("telemetry", "Sending GA4 event for command: ".concat(payload.command));
|
|
197
|
+
return [4 /*yield*/, axios_1.default.post(url, gaPayload, {
|
|
198
|
+
headers: { "Content-Type": "application/json" },
|
|
199
|
+
timeout: 800, // 800ms timeout for GA4 request
|
|
200
|
+
})];
|
|
201
|
+
case 2:
|
|
202
|
+
_a.sent();
|
|
203
|
+
logger_1.default.info("telemetry", "GA4 telemetry event successfully sent");
|
|
204
|
+
return [3 /*break*/, 4];
|
|
205
|
+
case 3:
|
|
206
|
+
error_2 = _a.sent();
|
|
207
|
+
logger_1.default.error("telemetry", "Failed to send GA4 telemetry: ".concat(error_2.message));
|
|
208
|
+
return [3 /*break*/, 4];
|
|
209
|
+
case 4: return [2 /*return*/];
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}); };
|
|
213
|
+
/**
|
|
214
|
+
* Appends telemetry event to a local log file in the user's home directory.
|
|
215
|
+
*/
|
|
216
|
+
var writeLocalTelemetry = function (payload) {
|
|
217
|
+
try {
|
|
218
|
+
var telemetryDir = path_1.default.join(os_1.default.homedir(), ".atb-builder");
|
|
219
|
+
var telemetryFile = path_1.default.join(telemetryDir, "telemetry.jsonl");
|
|
220
|
+
if (!fs_1.default.existsSync(telemetryDir)) {
|
|
221
|
+
fs_1.default.mkdirSync(telemetryDir, { recursive: true });
|
|
222
|
+
}
|
|
223
|
+
fs_1.default.appendFileSync(telemetryFile, JSON.stringify(payload) + "\n", "utf8");
|
|
224
|
+
logger_1.default.info("telemetry", "Appended telemetry to ".concat(telemetryFile));
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
logger_1.default.error("telemetry", "Failed to write local telemetry: ".concat(error.message));
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Tracks a CLI execution event.
|
|
232
|
+
*
|
|
233
|
+
* @param command - The command name
|
|
234
|
+
* @param status - Execution status
|
|
235
|
+
* @param err - Error if the status is "failed"
|
|
236
|
+
*/
|
|
237
|
+
var trackTelemetry = function (command, status, err) { return __awaiter(void 0, void 0, void 0, function () {
|
|
238
|
+
var cwd, envVars, e_1, isEnabled, cliVersion, userDetails, customUser, durationMs, tenant, projectName, payload, telemetryUrl, measurementId, apiSecret, e_2;
|
|
239
|
+
return __generator(this, function (_a) {
|
|
240
|
+
switch (_a.label) {
|
|
241
|
+
case 0:
|
|
242
|
+
_a.trys.push([0, 10, , 11]);
|
|
243
|
+
cwd = process.cwd();
|
|
244
|
+
envVars = {};
|
|
245
|
+
_a.label = 1;
|
|
246
|
+
case 1:
|
|
247
|
+
_a.trys.push([1, 3, , 4]);
|
|
248
|
+
return [4 /*yield*/, (0, config_1.getENV)(cwd)];
|
|
249
|
+
case 2:
|
|
250
|
+
envVars = _a.sent();
|
|
251
|
+
return [3 /*break*/, 4];
|
|
252
|
+
case 3:
|
|
253
|
+
e_1 = _a.sent();
|
|
254
|
+
return [3 /*break*/, 4];
|
|
255
|
+
case 4:
|
|
256
|
+
isEnabled = envVars.TELEMETRY_ENABLED !== "false" && process.env.TELEMETRY_ENABLED !== "false";
|
|
257
|
+
if (!isEnabled) {
|
|
258
|
+
logger_1.default.info("telemetry", "Telemetry is disabled via configuration");
|
|
259
|
+
return [2 /*return*/];
|
|
260
|
+
}
|
|
261
|
+
return [4 /*yield*/, (0, config_1.getVersion)()];
|
|
262
|
+
case 5:
|
|
263
|
+
cliVersion = _a.sent();
|
|
264
|
+
userDetails = getUserDetails();
|
|
265
|
+
customUser = envVars.TELEMETRY_USER || process.env.TELEMETRY_USER;
|
|
266
|
+
if (customUser) {
|
|
267
|
+
userDetails.customUser = customUser;
|
|
268
|
+
}
|
|
269
|
+
durationMs = status !== "started" ? Math.round(performance.now() - startTime) : undefined;
|
|
270
|
+
tenant = envVars.ADOBE_TENANT || "";
|
|
271
|
+
projectName = path_1.default.basename(cwd);
|
|
272
|
+
payload = {
|
|
273
|
+
eventId: eventId,
|
|
274
|
+
timestamp: new Date().toISOString(),
|
|
275
|
+
command: command,
|
|
276
|
+
args: sanitizeArgs(process.argv.slice(2)), // capture all command line arguments starting from index 2
|
|
277
|
+
user: userDetails,
|
|
278
|
+
project: {
|
|
279
|
+
projectName: projectName,
|
|
280
|
+
projectPath: cwd,
|
|
281
|
+
tenant: tenant,
|
|
282
|
+
},
|
|
283
|
+
execution: {
|
|
284
|
+
status: status,
|
|
285
|
+
durationMs: durationMs,
|
|
286
|
+
errorMessage: err === null || err === void 0 ? void 0 : err.message,
|
|
287
|
+
},
|
|
288
|
+
system: {
|
|
289
|
+
platform: os_1.default.platform(),
|
|
290
|
+
nodeVersion: process.version,
|
|
291
|
+
cliVersion: cliVersion,
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
// Always write to local log file for auditing
|
|
295
|
+
writeLocalTelemetry(payload);
|
|
296
|
+
if (!(status !== "started")) return [3 /*break*/, 9];
|
|
297
|
+
telemetryUrl = envVars.TELEMETRY_URL || process.env.TELEMETRY_URL;
|
|
298
|
+
if (!telemetryUrl) return [3 /*break*/, 7];
|
|
299
|
+
return [4 /*yield*/, sendRemoteTelemetry(telemetryUrl, payload)];
|
|
300
|
+
case 6:
|
|
301
|
+
_a.sent();
|
|
302
|
+
_a.label = 7;
|
|
303
|
+
case 7:
|
|
304
|
+
measurementId = envVars.GA_MEASUREMENT_ID || process.env.GA_MEASUREMENT_ID || "G-K9GL7DDSE5";
|
|
305
|
+
apiSecret = envVars.GA_API_SECRET || process.env.GA_API_SECRET || "WVnUPXFAR2GRtOEn_0EDPg";
|
|
306
|
+
return [4 /*yield*/, sendGA4Telemetry(measurementId, apiSecret, payload)];
|
|
307
|
+
case 8:
|
|
308
|
+
_a.sent();
|
|
309
|
+
_a.label = 9;
|
|
310
|
+
case 9: return [3 /*break*/, 11];
|
|
311
|
+
case 10:
|
|
312
|
+
e_2 = _a.sent();
|
|
313
|
+
// Catch-all to make sure telemetry never crashes CLI commands
|
|
314
|
+
logger_1.default.error("telemetry", "Error in telemetry tracker: ".concat(e_2.message));
|
|
315
|
+
return [3 /*break*/, 11];
|
|
316
|
+
case 11: return [2 /*return*/];
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
}); };
|
|
320
|
+
exports.trackTelemetry = trackTelemetry;
|
package/package.json
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "at-builder",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"main": "bin/index.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"atb": "bin/index.js"
|
|
7
7
|
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/",
|
|
10
|
+
"lib/",
|
|
11
|
+
".plop/",
|
|
12
|
+
"plopfile.js",
|
|
13
|
+
"puppeteer.js",
|
|
14
|
+
"webpack.config.js",
|
|
15
|
+
"babel.config.js",
|
|
16
|
+
"eslint.config.js",
|
|
17
|
+
"tsconfig.json"
|
|
18
|
+
],
|
|
8
19
|
"scripts": {
|
|
9
20
|
"build:atb": "tsc",
|
|
10
21
|
"build:atb:dev": "tsc -w",
|
package/.vscode/settings.json
DELETED
package/DEVELOPMENT.md
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
# Local Development
|
|
2
|
-
|
|
3
|
-
Notes-to-self on how to test `at-builder` against a real consumer project before publishing to npm. Not for end users — this is internal.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## TL;DR
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
# one-time setup in this repo
|
|
11
|
-
yarn link
|
|
12
|
-
yarn build:atb:dev & # leave running; recompiles TS on save
|
|
13
|
-
|
|
14
|
-
# work against the bundled test/ sandbox (already gitignored, pre-populated)
|
|
15
|
-
cd test
|
|
16
|
-
atb doctor
|
|
17
|
-
atb new
|
|
18
|
-
atb dev --browser
|
|
19
|
-
# ... etc
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
When done:
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
# in at-builder
|
|
26
|
-
yarn unlink
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## Two patterns
|
|
32
|
-
|
|
33
|
-
### 1. `yarn link` — active iteration
|
|
34
|
-
|
|
35
|
-
Symlinks the global `atb` binary to this repo. Edits to source are picked up on the next invocation (after rebuild for TypeScript).
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
# in at-builder
|
|
39
|
-
yarn link # or: npm link
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
`atb` is now a global command pointing at this repo. Run it from any directory.
|
|
43
|
-
|
|
44
|
-
To remove the symlink later:
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
yarn unlink # in at-builder
|
|
48
|
-
yarn unlink at-builder # in any project that also linked it (rarely needed)
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### 2. `npm pack` — pre-publish verification
|
|
52
|
-
|
|
53
|
-
`yarn link` uses symlinks, which can paper over packaging issues — forgotten `.npmignore` exclusions, missing bundled assets (like `at-builder-*.vsix`), files declared in `package.json` but not actually present, etc. Before `npm publish`, do this to test the **actual artifact** npm will ship:
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
# in at-builder
|
|
57
|
-
yarn build:atb # ensure bin/ is fresh
|
|
58
|
-
npm pack # produces at-builder-<version>.tgz
|
|
59
|
-
|
|
60
|
-
# in a clean throwaway directory
|
|
61
|
-
mkdir /tmp/atb-publish-test && cd /tmp/atb-publish-test
|
|
62
|
-
npm install -g /path/to/at-builder/at-builder-<version>.tgz
|
|
63
|
-
|
|
64
|
-
# verify the published surface
|
|
65
|
-
which atb
|
|
66
|
-
atb --version
|
|
67
|
-
atb install-extension --editor code # confirms the bundled .vsix actually shipped
|
|
68
|
-
atb init
|
|
69
|
-
# ... exercise other commands
|
|
70
|
-
|
|
71
|
-
# cleanup
|
|
72
|
-
npm uninstall -g at-builder
|
|
73
|
-
rm /path/to/at-builder/at-builder-<version>.tgz
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
If anything's missing from the tarball, this catches it before users do.
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
## What needs rebuilding
|
|
81
|
-
|
|
82
|
-
The single thing that bites people most often:
|
|
83
|
-
|
|
84
|
-
| You edit | Rebuild needed? |
|
|
85
|
-
|---|---|
|
|
86
|
-
| `src/**/*.ts` (CLI surface) | **Yes** — `yarn build:atb` (one-shot) or `yarn build:atb:dev` (`tsc -w`) running in another terminal |
|
|
87
|
-
| `lib/at-deploy.js`, `lib/at-sync.js` | No — plain JS, picked up directly on next `atb` invocation |
|
|
88
|
-
| `puppeteer.js` | No |
|
|
89
|
-
| `webpack.config.js` | No |
|
|
90
|
-
| `.plop/generators/*`, `.plop/templates/*` | No |
|
|
91
|
-
| `babel.config.js`, `eslint.config.js`, `tsconfig.json` | No |
|
|
92
|
-
|
|
93
|
-
Easiest workflow: open a second terminal in this repo and leave `yarn build:atb:dev` running. Then any `src/` edit is reflected on the next `atb` invocation in your test project.
|
|
94
|
-
|
|
95
|
-
---
|
|
96
|
-
|
|
97
|
-
## The bundled `test/` sandbox
|
|
98
|
-
|
|
99
|
-
This repo ships a `test/` directory (gitignored) pre-populated with `.env`, `adobe.config.js`, `.gitignore`, and `package.json` — basically what `atb init` would create. Use it as a throwaway consumer project:
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
cd test
|
|
103
|
-
atb doctor # exercises all checks
|
|
104
|
-
atb doctor --fix # exercises all auto-fixes (incl. watch-config migration)
|
|
105
|
-
atb new # exercise the prompts (AB/XT, page names)
|
|
106
|
-
atb sync # if you've set activityInfo.id
|
|
107
|
-
atb dev --browser # exercise the puppeteer fixes
|
|
108
|
-
atb deploy --dry-run # test deploy logic without PUTting to AT
|
|
109
|
-
atb install-extension # test the .vsix install flow
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
If `test/` ever gets corrupted, just delete its contents and re-run `atb init` — it'll regenerate from scratch.
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
## Publishing checklist
|
|
117
|
-
|
|
118
|
-
Before `npm publish`:
|
|
119
|
-
|
|
120
|
-
1. `yarn build:atb` — ensure `bin/` is fresh and committed.
|
|
121
|
-
2. Bump `version` in `package.json`. Use semver:
|
|
122
|
-
- **patch** — bug fixes, doc changes
|
|
123
|
-
- **minor** — new commands, new flags, backwards-compatible features
|
|
124
|
-
- **major** — breaking schema changes (e.g. requiring fields, renaming commands)
|
|
125
|
-
3. `npm pack` and run the verification block above.
|
|
126
|
-
4. `git tag v<version>` + `git push --tags`.
|
|
127
|
-
5. `npm publish`.
|
|
128
|
-
|
|
129
|
-
---
|
|
130
|
-
|
|
131
|
-
## Common gotchas
|
|
132
|
-
|
|
133
|
-
- **`atb` runs the wrong copy (link vs global)** — you have at-builder installed globally from npm AND linked locally. Run `which atb` and `atb --version`; if it doesn't match the local repo, `yarn unlink at-builder` (in linked consumer dirs) and `npm uninstall -g at-builder` to clear stale globals before re-linking.
|
|
134
|
-
- **`atb` resolves to a stale install in a *different* npm prefix** — distinct from the link-vs-global case above. It's easy to end up with at-builder installed at multiple locations (e.g. `/usr/local/lib/node_modules/at-builder` from a system npm AND `~/.nvm/versions/node/<v>/lib/node_modules/at-builder` from nvm). `npm install -g` only writes to the prefix the *currently-active* `npm` uses; a stale install in the *other* prefix can still come first on PATH. Diagnose:
|
|
135
|
-
|
|
136
|
-
```bash
|
|
137
|
-
which -a atb # every atb on PATH
|
|
138
|
-
npm config get prefix # where the active npm installs
|
|
139
|
-
ls -la $(which atb) # see where the symlink points
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
If `which -a` lists multiple entries, remove the stale one(s):
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
# active prefix is nvm but a stale /usr/local install is shadowing it:
|
|
146
|
-
rm -f /usr/local/bin/atb
|
|
147
|
-
rm -rf /usr/local/lib/node_modules/at-builder
|
|
148
|
-
|
|
149
|
-
# or the reverse:
|
|
150
|
-
rm -f ~/.nvm/versions/node/<v>/bin/atb
|
|
151
|
-
rm -rf ~/.nvm/versions/node/<v>/lib/node_modules/at-builder
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
Then reinstall once: `npm install -g /path/to/at-builder-<version>.tgz`.
|
|
155
|
-
|
|
156
|
-
- **`atb --version` shows the same number after re-packing** — `npm pack` honors whatever's in `package.json`; it doesn't bump for you. If you forgot to update the version before packing, the tarball reports the same number as the previously-published release and `atb --version` looks like nothing changed even though the code did. Always bump `package.json` first (see the publishing checklist for the semver rules), then re-pack. Verify before installing:
|
|
157
|
-
|
|
158
|
-
```bash
|
|
159
|
-
tar xzOf at-builder-<version>.tgz package/package.json | grep '"version"'
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
- **Edits to `src/` not reflected** — `bin/` is stale. Run `yarn build:atb` or check `yarn build:atb:dev` is actually running.
|
|
163
|
-
- **`npm install` exits 1** — historical issue, the `postinstall` script referenced a missing `bin/postinstall.js`. Already fixed (commit `d3fe05e`); if it resurfaces, drop the `postinstall` line from `package.json` again.
|
|
164
|
-
- **`.vsix` not in tarball** — verify with `npm pack --dry-run` before publishing. The `.vsix` lives at the repo root and is included by default (no `files` field, no `.npmignore` excluding it). If you ever add a `files` field, remember to whitelist `at-builder-*.vsix`.
|
package/at-builder-0.0.2.vsix
DELETED
|
Binary file
|