@schwabyio/gta 0.10.0 → 0.11.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/dist/gta.js CHANGED
@@ -28,7 +28,7 @@ import {
28
28
  samePath,
29
29
  stepsForTags,
30
30
  unattempted
31
- } from "./chunks/chunk-MZSHHPAQ.js";
31
+ } from "./chunks/chunk-OM3ZR56O.js";
32
32
 
33
33
  // src/args.ts
34
34
  var UsageError = class extends Error {
@@ -1072,6 +1072,7 @@ function jsonListing(options) {
1072
1072
  project: options.project,
1073
1073
  environments: options.environments,
1074
1074
  environmentType: options.environmentType,
1075
+ settings: { values: options.settings.values, sources: options.settings.sources },
1075
1076
  flags: { values: options.flags.values, sources: options.flags.sources },
1076
1077
  tags: options.tags,
1077
1078
  notTags: options.notTags ?? [],
@@ -1168,13 +1169,11 @@ var SettingsError = class extends Error {
1168
1169
  name = "SettingsError";
1169
1170
  };
1170
1171
  var envNameOf = (key) => `GTA_${key.replace(/[A-Z]/g, (c) => `_${c}`).toUpperCase()}`;
1172
+ var DEFAULT_SOURCE = "default";
1171
1173
  async function loadSettings(options) {
1172
- const { root, overrides = {}, env = process.env } = options;
1173
- const file = path.join(root, SETTINGS_FILE);
1174
- let source;
1175
- try {
1176
- source = await fs.readFile(file, "utf8");
1177
- } catch {
1174
+ const { root, global = null, overrides = {}, env = process.env } = options;
1175
+ const own = await readSettingsFile(path.join(root, SETTINGS_FILE), SETTINGS_FILE);
1176
+ if (own === null) {
1178
1177
  throw new SettingsError(
1179
1178
  `There is no ${SETTINGS_FILE} in ${root}.
1180
1179
  gta runs from a project folder: the one holding collections/ and ${SETTINGS_FILE}.
@@ -1185,23 +1184,20 @@ A minimal ${SETTINGS_FILE}:
1185
1184
  `
1186
1185
  );
1187
1186
  }
1188
- let fromFile;
1189
- try {
1190
- fromFile = (0, import_yaml.parse)(source) ?? {};
1191
- } catch (cause) {
1192
- throw new SettingsError(`${SETTINGS_FILE} will not parse: ${cause.message}`);
1193
- }
1194
- if (typeof fromFile !== "object" || fromFile === null || Array.isArray(fromFile)) {
1195
- throw new SettingsError(
1196
- `${SETTINGS_FILE} must be a map of settings, such as limitConcurrency: 4`
1197
- );
1198
- }
1187
+ const sharedLabel = global ? path.posix.join(global.uses, SETTINGS_FILE) : "";
1188
+ const shared = global ? await readSettingsFile(path.join(global.root, SETTINGS_FILE), sharedLabel) : null;
1199
1189
  const origin = /* @__PURE__ */ new Map();
1200
1190
  const merged = {};
1201
- for (const [key, value] of Object.entries(fromFile)) {
1202
- checkKey(key, SETTINGS_FILE);
1203
- merged[key] = value;
1204
- origin.set(key, SETTINGS_FILE);
1191
+ const files = [
1192
+ [sharedLabel, shared],
1193
+ [SETTINGS_FILE, own]
1194
+ ];
1195
+ for (const [label, fromFile] of files) {
1196
+ for (const [key, value] of Object.entries(fromFile ?? {})) {
1197
+ checkKey(key, label);
1198
+ merged[key] = value;
1199
+ origin.set(key, label);
1200
+ }
1205
1201
  }
1206
1202
  for (const key of KEYS) {
1207
1203
  const value = env[envNameOf(key)];
@@ -1223,7 +1219,29 @@ A minimal ${SETTINGS_FILE}:
1223
1219
  throw new SettingsError(`Settings are not valid:
1224
1220
  ${lines.join("\n")}`);
1225
1221
  }
1226
- return result.data;
1222
+ const sources = Object.fromEntries(
1223
+ KEYS.map((key) => [key, origin.get(key) ?? DEFAULT_SOURCE])
1224
+ );
1225
+ return { settings: result.data, sources };
1226
+ }
1227
+ async function readSettingsFile(file, label) {
1228
+ let source;
1229
+ try {
1230
+ source = await fs.readFile(file, "utf8");
1231
+ } catch (cause) {
1232
+ if (cause.code === "ENOENT") return null;
1233
+ throw new SettingsError(`${label} cannot be read: ${cause.message}`);
1234
+ }
1235
+ let fromFile;
1236
+ try {
1237
+ fromFile = (0, import_yaml.parse)(source) ?? {};
1238
+ } catch (cause) {
1239
+ throw new SettingsError(`${label} will not parse: ${cause.message}`);
1240
+ }
1241
+ if (typeof fromFile !== "object" || fromFile === null || Array.isArray(fromFile)) {
1242
+ throw new SettingsError(`${label} must be a map of settings, such as limitConcurrency: 4`);
1243
+ }
1244
+ return fromFile;
1227
1245
  }
1228
1246
  function checkKey(key, where) {
1229
1247
  if (!Object.hasOwn(SETTING_KEYS, key)) {
@@ -1251,7 +1269,7 @@ function coerce(key, value, where) {
1251
1269
  }
1252
1270
 
1253
1271
  // src/main.ts
1254
- var VERSION = true ? "0.10.0" : "0.0.0-dev";
1272
+ var VERSION = true ? "0.11.0" : "0.0.0-dev";
1255
1273
  var JUNIT_DIR = "junit";
1256
1274
  var JUNIT_FILE = "junit.xml";
1257
1275
  var HTML_DIR = "html";
@@ -1294,6 +1312,7 @@ async function main(argv, cli) {
1294
1312
  project: { name: project.name, root: project.root },
1295
1313
  environments: project.environments,
1296
1314
  environmentType: project.settings.environmentType,
1315
+ settings: { values: project.settings, sources: project.settingSources },
1297
1316
  flags: project.flags,
1298
1317
  tags: project.settings.tags,
1299
1318
  notTags: project.settings.notTags,
@@ -1328,14 +1347,19 @@ async function openProject(ctx, overrides, flagArgs = []) {
1328
1347
  gta runs from a project folder: the one holding ${COLLECTIONS_DIR}/ and ${SETTINGS_FILE}.`
1329
1348
  );
1330
1349
  }
1331
- const settings = await loadSettings({ root, overrides, env: ctx.env });
1332
1350
  const info = await readProject(root);
1351
+ const { settings, sources: settingSources } = await loadSettings({
1352
+ root,
1353
+ global: info.global,
1354
+ overrides,
1355
+ env: ctx.env
1356
+ });
1333
1357
  const layout = await discoverProject(root);
1334
1358
  const collections = await loadProjectCollections(root, layout.files);
1335
1359
  const environments = environmentNames(await projectEnvironments(root, info.global));
1336
1360
  if (settings.environmentType && !environments.includes(settings.environmentType)) {
1337
1361
  throw new ProjectError(
1338
- `environmentType: there is no environment called "${settings.environmentType}". ` + (environments.length > 0 ? `This project has: ${environments.join(", ")}` : `This project has no environments/.`)
1362
+ `environmentType (from ${settingSources.environmentType}): there is no environment called "${settings.environmentType}". ` + (environments.length > 0 ? `This project has: ${environments.join(", ")}` : `This project has no environments/.`)
1339
1363
  );
1340
1364
  }
1341
1365
  let flags;
@@ -1360,6 +1384,7 @@ gta runs from a project folder: the one holding ${COLLECTIONS_DIR}/ and ${SETTIN
1360
1384
  root,
1361
1385
  name: info.doc?.name ?? path2.basename(root),
1362
1386
  settings,
1387
+ settingSources,
1363
1388
  flags,
1364
1389
  collections,
1365
1390
  environments,
@@ -1403,8 +1428,7 @@ function list(project, selection, problems, ctx, p) {
1403
1428
  );
1404
1429
  ctx.out(field("Flags", flagLine(project.flags, p)));
1405
1430
  if (project.caFiles.length > 0) ctx.out(field("CA files", project.caFiles.join(", ")));
1406
- if (settings.tags.length > 0) ctx.out(field("Tags", settings.tags.join(", ")));
1407
- if (settings.notTags.length > 0) ctx.out(field("Not tags", settings.notTags.join(", ")));
1431
+ for (const line of settingLines(project, p)) ctx.out(line);
1408
1432
  ctx.out("");
1409
1433
  const width = nameWidthFor(selection.targets.map((t) => t.id));
1410
1434
  const dirWidth = Math.max(
@@ -1445,6 +1469,19 @@ function list(project, selection, problems, ctx, p) {
1445
1469
  const steps = selection.targets.reduce((n, t) => n + stepCountOf(t), 0);
1446
1470
  ctx.out(field("Total", `${selection.targets.length} collections, ${steps} steps`));
1447
1471
  }
1472
+ function settingLines(project, p) {
1473
+ const rows = Object.keys(SETTING_KEYS).filter((key) => project.settingSources[key] !== DEFAULT_SOURCE).map((key) => ({
1474
+ cell: `${key}: ${shownSetting(project.settings[key])}`,
1475
+ source: project.settingSources[key]
1476
+ }));
1477
+ if (rows.length === 0) return [field("Settings", p.dim("all defaults"))];
1478
+ const width = Math.max(...rows.map((row) => row.cell.length));
1479
+ const indent = " ".repeat(field("Settings", "").length);
1480
+ return rows.map(
1481
+ (row, i) => `${i === 0 ? field("Settings", "") : indent}${row.cell.padEnd(width)} ${p.dim(row.source)}`
1482
+ );
1483
+ }
1484
+ var shownSetting = (value) => Array.isArray(value) ? value.length > 0 ? value.join(", ") : "[]" : String(value ?? "none");
1448
1485
  async function run(project, selection, ctx, p, emit = null) {
1449
1486
  const { settings } = project;
1450
1487
  const width = nameWidthFor(selection.targets.map((t) => t.id));
@@ -1632,8 +1669,8 @@ function usage(p) {
1632
1669
  "",
1633
1670
  ` ${p.bold("Commands:")}`,
1634
1671
  " g, get List the collections `gta all` would run, without running them.",
1635
- " Exits 1 on a broken collection, or a use: or extends: that",
1636
- " would stop a run, in any collection.",
1672
+ " Exits 1 on a broken collection, or a use:, extends: or body",
1673
+ " file that would stop a run, in any collection.",
1637
1674
  " a, all Run every collection, less those with exclude: true.",
1638
1675
  " <list> Run the collections and directories named, comma separated,",
1639
1676
  " in that order: smoke,checkout/sessions,payments",
@@ -1648,7 +1685,8 @@ function usage(p) {
1648
1685
  " Override a feature flag for this run; any number of them.",
1649
1686
  " GTA_FLAG_<name>=value does the same from the environment.",
1650
1687
  "",
1651
- ` ${p.bold("Settings")} (settings.yml, then GTA_* environment variables, then --flags):`,
1688
+ ` ${p.bold("Settings")} (the global project's settings.yml, then this project's, then`,
1689
+ " GTA_* environment variables, then --flags; gta get shows where each came from):",
1652
1690
  ...settings,
1653
1691
  "",
1654
1692
  ` ${p.bold("Exit code:")} 0 passed, 1 failed, 2 could not run (or write a report).`
package/dist/worker.js CHANGED
@@ -3,7 +3,7 @@ import { createRequire as __gtaCreateRequire } from 'node:module';
3
3
  const require = __gtaCreateRequire(import.meta.url);
4
4
  import {
5
5
  runJob
6
- } from "./chunks/chunk-MZSHHPAQ.js";
6
+ } from "./chunks/chunk-OM3ZR56O.js";
7
7
 
8
8
  // src/worker.ts
9
9
  import { parentPort, workerData } from "node:worker_threads";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schwabyio/gta",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "description": "gta, the Gravity Test Automation command-line runner: HTTP API tests written as YAML files, run from a terminal or CI.",
6
6
  "keywords": [