@automatify-au/cli 0.1.15 → 0.1.17
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/automatify.cjs +842 -214
- package/package.json +1 -1
package/dist/automatify.cjs
CHANGED
|
@@ -2894,8 +2894,8 @@ var require_utils = __commonJS({
|
|
|
2894
2894
|
var result = transform[inputType][outputType](input);
|
|
2895
2895
|
return result;
|
|
2896
2896
|
};
|
|
2897
|
-
exports2.resolve = function(
|
|
2898
|
-
var parts =
|
|
2897
|
+
exports2.resolve = function(path17) {
|
|
2898
|
+
var parts = path17.split("/");
|
|
2899
2899
|
var result = [];
|
|
2900
2900
|
for (var index = 0; index < parts.length; index++) {
|
|
2901
2901
|
var part = parts[index];
|
|
@@ -8748,18 +8748,18 @@ var require_object = __commonJS({
|
|
|
8748
8748
|
var object = new ZipObject(name, zipObjectContent, o);
|
|
8749
8749
|
this.files[name] = object;
|
|
8750
8750
|
};
|
|
8751
|
-
var parentFolder = function(
|
|
8752
|
-
if (
|
|
8753
|
-
|
|
8751
|
+
var parentFolder = function(path17) {
|
|
8752
|
+
if (path17.slice(-1) === "/") {
|
|
8753
|
+
path17 = path17.substring(0, path17.length - 1);
|
|
8754
8754
|
}
|
|
8755
|
-
var lastSlash =
|
|
8756
|
-
return lastSlash > 0 ?
|
|
8755
|
+
var lastSlash = path17.lastIndexOf("/");
|
|
8756
|
+
return lastSlash > 0 ? path17.substring(0, lastSlash) : "";
|
|
8757
8757
|
};
|
|
8758
|
-
var forceTrailingSlash = function(
|
|
8759
|
-
if (
|
|
8760
|
-
|
|
8758
|
+
var forceTrailingSlash = function(path17) {
|
|
8759
|
+
if (path17.slice(-1) !== "/") {
|
|
8760
|
+
path17 += "/";
|
|
8761
8761
|
}
|
|
8762
|
-
return
|
|
8762
|
+
return path17;
|
|
8763
8763
|
};
|
|
8764
8764
|
var folderAdd = function(name, createFolders) {
|
|
8765
8765
|
createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
|
|
@@ -10720,8 +10720,8 @@ function toJsonLine(data) {
|
|
|
10720
10720
|
}
|
|
10721
10721
|
|
|
10722
10722
|
// src/auto.ts
|
|
10723
|
-
var
|
|
10724
|
-
var
|
|
10723
|
+
var import_node_fs5 = require("node:fs");
|
|
10724
|
+
var import_node_path6 = __toESM(require("node:path"), 1);
|
|
10725
10725
|
|
|
10726
10726
|
// ../../packages/shared-types/dist/entitlements.js
|
|
10727
10727
|
var UNLIMITED_ACTIVE_USERS = Number.MAX_SAFE_INTEGER;
|
|
@@ -10764,12 +10764,19 @@ var Priority;
|
|
|
10764
10764
|
})(Priority || (Priority = {}));
|
|
10765
10765
|
|
|
10766
10766
|
// src/config.ts
|
|
10767
|
-
var
|
|
10768
|
-
var
|
|
10767
|
+
var import_node_fs2 = require("node:fs");
|
|
10768
|
+
var import_node_path2 = __toESM(require("node:path"), 1);
|
|
10769
10769
|
|
|
10770
10770
|
// src/secureStore.ts
|
|
10771
10771
|
var import_node_child_process = require("node:child_process");
|
|
10772
|
+
var import_node_crypto = require("node:crypto");
|
|
10773
|
+
var import_node_fs = require("node:fs");
|
|
10774
|
+
var import_node_path = __toESM(require("node:path"), 1);
|
|
10772
10775
|
var KEYCHAIN_SERVICE = "automatify-testops-cli";
|
|
10776
|
+
var LINUX_SECURE_STORE_ENV = "TESTOPS_LINUX_SECURE_STORE";
|
|
10777
|
+
var LINUX_PROTECTED_PREFIX = "linux-aes-gcm-v1";
|
|
10778
|
+
var LINUX_JIRA_ACCOUNT_SUFFIX = ":jiraApiToken";
|
|
10779
|
+
var LINUX_KEY_FILENAME = ".testops-cli.key";
|
|
10773
10780
|
function errorText(error) {
|
|
10774
10781
|
return error instanceof Error && error.message ? error.message : "unknown secure-storage error";
|
|
10775
10782
|
}
|
|
@@ -10809,11 +10816,85 @@ function macOSKeychainWriteInput(account, value) {
|
|
|
10809
10816
|
quoteSecurityInteractiveArg(value)
|
|
10810
10817
|
].join(" ") + "\n";
|
|
10811
10818
|
}
|
|
10819
|
+
function linuxSecureStoreEnabled() {
|
|
10820
|
+
return process.env[LINUX_SECURE_STORE_ENV]?.trim() === "1";
|
|
10821
|
+
}
|
|
10822
|
+
function isLinuxProtectedValue(value) {
|
|
10823
|
+
return Boolean(value?.startsWith(`${LINUX_PROTECTED_PREFIX}.`));
|
|
10824
|
+
}
|
|
10825
|
+
function linuxConfigPathForAccount(account) {
|
|
10826
|
+
const normalized = account.endsWith(LINUX_JIRA_ACCOUNT_SUFFIX) ? account.slice(0, -LINUX_JIRA_ACCOUNT_SUFFIX.length) : account;
|
|
10827
|
+
return import_node_path.default.resolve(normalized);
|
|
10828
|
+
}
|
|
10829
|
+
function linuxKeyPathForAccount(account) {
|
|
10830
|
+
return import_node_path.default.join(import_node_path.default.dirname(linuxConfigPathForAccount(account)), LINUX_KEY_FILENAME);
|
|
10831
|
+
}
|
|
10832
|
+
function readLinuxKey(keyPath) {
|
|
10833
|
+
const key = (0, import_node_fs.readFileSync)(keyPath);
|
|
10834
|
+
if (key.length !== 32) {
|
|
10835
|
+
throw new Error(`Linux secure-store key at ${keyPath} must be exactly 32 bytes.`);
|
|
10836
|
+
}
|
|
10837
|
+
return key;
|
|
10838
|
+
}
|
|
10839
|
+
function readOrCreateLinuxKey(keyPath) {
|
|
10840
|
+
const directory = import_node_path.default.dirname(keyPath);
|
|
10841
|
+
(0, import_node_fs.mkdirSync)(directory, { recursive: true, mode: 448 });
|
|
10842
|
+
(0, import_node_fs.chmodSync)(directory, 448);
|
|
10843
|
+
if ((0, import_node_fs.existsSync)(keyPath)) {
|
|
10844
|
+
(0, import_node_fs.chmodSync)(keyPath, 384);
|
|
10845
|
+
return readLinuxKey(keyPath);
|
|
10846
|
+
}
|
|
10847
|
+
const key = (0, import_node_crypto.randomBytes)(32);
|
|
10848
|
+
try {
|
|
10849
|
+
(0, import_node_fs.writeFileSync)(keyPath, key, { flag: "wx", mode: 384 });
|
|
10850
|
+
} catch (error) {
|
|
10851
|
+
if (error.code !== "EEXIST") {
|
|
10852
|
+
throw error;
|
|
10853
|
+
}
|
|
10854
|
+
}
|
|
10855
|
+
(0, import_node_fs.chmodSync)(keyPath, 384);
|
|
10856
|
+
return readLinuxKey(keyPath);
|
|
10857
|
+
}
|
|
10858
|
+
function protectLinuxSecret(account, value) {
|
|
10859
|
+
const keyPath = linuxKeyPathForAccount(account);
|
|
10860
|
+
const key = readOrCreateLinuxKey(keyPath);
|
|
10861
|
+
const iv = (0, import_node_crypto.randomBytes)(12);
|
|
10862
|
+
const cipher = (0, import_node_crypto.createCipheriv)("aes-256-gcm", key, iv);
|
|
10863
|
+
const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
10864
|
+
const tag = cipher.getAuthTag();
|
|
10865
|
+
const encodedKeyPath = Buffer.from(keyPath, "utf8").toString("base64url");
|
|
10866
|
+
return [
|
|
10867
|
+
LINUX_PROTECTED_PREFIX,
|
|
10868
|
+
encodedKeyPath,
|
|
10869
|
+
iv.toString("base64url"),
|
|
10870
|
+
tag.toString("base64url"),
|
|
10871
|
+
ciphertext.toString("base64url")
|
|
10872
|
+
].join(".");
|
|
10873
|
+
}
|
|
10874
|
+
function unprotectLinuxSecret(protectedValue) {
|
|
10875
|
+
const parts = protectedValue.split(".");
|
|
10876
|
+
if (parts.length !== 5 || parts[0] !== LINUX_PROTECTED_PREFIX) {
|
|
10877
|
+
throw new Error("Unsupported Linux secure-store payload.");
|
|
10878
|
+
}
|
|
10879
|
+
const [, encodedKeyPath, encodedIv, encodedTag, encodedCiphertext] = parts;
|
|
10880
|
+
const keyPath = Buffer.from(encodedKeyPath, "base64url").toString("utf8");
|
|
10881
|
+
const key = readLinuxKey(keyPath);
|
|
10882
|
+
const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", key, Buffer.from(encodedIv, "base64url"));
|
|
10883
|
+
decipher.setAuthTag(Buffer.from(encodedTag, "base64url"));
|
|
10884
|
+
const plaintext = Buffer.concat([decipher.update(Buffer.from(encodedCiphertext, "base64url")), decipher.final()]);
|
|
10885
|
+
return plaintext.toString("utf8");
|
|
10886
|
+
}
|
|
10812
10887
|
function readSecureSecret(locator, deps = {}) {
|
|
10813
10888
|
const platform = deps.platform ?? process.platform;
|
|
10814
10889
|
const run = deps.execFileSync ?? import_node_child_process.execFileSync;
|
|
10815
10890
|
if (platform === "darwin") {
|
|
10816
10891
|
if (!locator.account) {
|
|
10892
|
+
if (isLinuxProtectedValue(locator.protectedValue)) {
|
|
10893
|
+
return {
|
|
10894
|
+
status: "error",
|
|
10895
|
+
message: "This config contains a Linux encrypted local-store value, but it is being read on macOS. Re-store the secret on this machine."
|
|
10896
|
+
};
|
|
10897
|
+
}
|
|
10817
10898
|
return locator.protectedValue ? {
|
|
10818
10899
|
status: "error",
|
|
10819
10900
|
message: "This config contains a Windows DPAPI value, but it is being read on macOS. Re-store the secret on this machine."
|
|
@@ -10836,6 +10917,12 @@ function readSecureSecret(locator, deps = {}) {
|
|
|
10836
10917
|
}
|
|
10837
10918
|
}
|
|
10838
10919
|
if (platform === "win32") {
|
|
10920
|
+
if (isLinuxProtectedValue(locator.protectedValue)) {
|
|
10921
|
+
return {
|
|
10922
|
+
status: "error",
|
|
10923
|
+
message: "This config contains a Linux encrypted local-store value, but it is being read on Windows. Re-store the secret for the current Windows user."
|
|
10924
|
+
};
|
|
10925
|
+
}
|
|
10839
10926
|
if (!locator.protectedValue) {
|
|
10840
10927
|
return locator.account ? {
|
|
10841
10928
|
status: "error",
|
|
@@ -10855,14 +10942,40 @@ function readSecureSecret(locator, deps = {}) {
|
|
|
10855
10942
|
} catch (error) {
|
|
10856
10943
|
return {
|
|
10857
10944
|
status: "error",
|
|
10858
|
-
message: `Unable to decrypt the Windows DPAPI value for the current user: ${errorText(error)}. Re-store the secret.`
|
|
10945
|
+
message: `Unable to decrypt the Windows DPAPI value for the current user: ${errorText(error)}. Re-store the secret for the current Windows user.`
|
|
10946
|
+
};
|
|
10947
|
+
}
|
|
10948
|
+
}
|
|
10949
|
+
if (platform === "linux" && linuxSecureStoreEnabled()) {
|
|
10950
|
+
if (locator.account && !locator.protectedValue) {
|
|
10951
|
+
return {
|
|
10952
|
+
status: "error",
|
|
10953
|
+
message: "This config references a macOS Keychain secret, but it is being read on Linux. Re-store the secret on this machine."
|
|
10954
|
+
};
|
|
10955
|
+
}
|
|
10956
|
+
if (!locator.protectedValue) {
|
|
10957
|
+
return { status: "missing" };
|
|
10958
|
+
}
|
|
10959
|
+
if (!isLinuxProtectedValue(locator.protectedValue)) {
|
|
10960
|
+
return {
|
|
10961
|
+
status: "error",
|
|
10962
|
+
message: "This config contains a foreign protected value that cannot be decrypted by the Linux local store. Re-store the secret on this machine."
|
|
10963
|
+
};
|
|
10964
|
+
}
|
|
10965
|
+
try {
|
|
10966
|
+
const value = unprotectLinuxSecret(locator.protectedValue);
|
|
10967
|
+
return value ? { status: "available", value, source: "secure-store" } : { status: "error", message: "Linux encrypted local store returned an empty value. Re-store the secret." };
|
|
10968
|
+
} catch (error) {
|
|
10969
|
+
return {
|
|
10970
|
+
status: "error",
|
|
10971
|
+
message: `Unable to decrypt the Linux encrypted local-store value: ${errorText(error)} Re-store the secret.`
|
|
10859
10972
|
};
|
|
10860
10973
|
}
|
|
10861
10974
|
}
|
|
10862
10975
|
if (locator.account || locator.protectedValue) {
|
|
10863
10976
|
return {
|
|
10864
10977
|
status: "error",
|
|
10865
|
-
message: "This config contains local secure-store metadata, but local secure storage is
|
|
10978
|
+
message: "This config contains local secure-store metadata, but local secure storage is disabled on Linux/CI. Use environment variables or explicitly enable TESTOPS_LINUX_SECURE_STORE=1 on a trusted persistent Linux host."
|
|
10866
10979
|
};
|
|
10867
10980
|
}
|
|
10868
10981
|
return { status: "missing" };
|
|
@@ -10898,8 +11011,15 @@ function writeSecureSecret(account, value, deps = {}) {
|
|
|
10898
11011
|
}
|
|
10899
11012
|
return { source: "secure-store", protectedValue };
|
|
10900
11013
|
}
|
|
11014
|
+
if (platform === "linux" && linuxSecureStoreEnabled()) {
|
|
11015
|
+
try {
|
|
11016
|
+
return { source: "secure-store", protectedValue: protectLinuxSecret(account, value) };
|
|
11017
|
+
} catch {
|
|
11018
|
+
throw new Error("Unable to store the secret in the Linux encrypted local store.");
|
|
11019
|
+
}
|
|
11020
|
+
}
|
|
10901
11021
|
throw new Error(
|
|
10902
|
-
"Secure local secret storage is supported on macOS and Windows
|
|
11022
|
+
"Secure local secret storage is supported on macOS and Windows. On trusted persistent Linux hosts, set TESTOPS_LINUX_SECURE_STORE=1; otherwise keep secrets in environment variables."
|
|
10903
11023
|
);
|
|
10904
11024
|
}
|
|
10905
11025
|
function deleteSecureSecret(locator, deps = {}) {
|
|
@@ -10924,6 +11044,9 @@ function deleteSecureSecret(locator, deps = {}) {
|
|
|
10924
11044
|
if (platform === "win32") {
|
|
10925
11045
|
return locator.protectedValue ? { status: "deleted" } : { status: "missing" };
|
|
10926
11046
|
}
|
|
11047
|
+
if (platform === "linux") {
|
|
11048
|
+
return locator.account || locator.protectedValue ? { status: "deleted" } : { status: "missing" };
|
|
11049
|
+
}
|
|
10927
11050
|
return locator.account || locator.protectedValue ? { status: "deleted" } : { status: "missing" };
|
|
10928
11051
|
}
|
|
10929
11052
|
|
|
@@ -10972,11 +11095,11 @@ function firstStringValue(...values) {
|
|
|
10972
11095
|
return "";
|
|
10973
11096
|
}
|
|
10974
11097
|
function readConfigFile(configPath) {
|
|
10975
|
-
if (!(0,
|
|
11098
|
+
if (!(0, import_node_fs2.existsSync)(configPath)) {
|
|
10976
11099
|
return {};
|
|
10977
11100
|
}
|
|
10978
11101
|
try {
|
|
10979
|
-
const raw = (0,
|
|
11102
|
+
const raw = (0, import_node_fs2.readFileSync)(configPath, "utf8");
|
|
10980
11103
|
const parsed = JSON.parse(raw);
|
|
10981
11104
|
return typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
10982
11105
|
} catch {
|
|
@@ -10984,11 +11107,11 @@ function readConfigFile(configPath) {
|
|
|
10984
11107
|
}
|
|
10985
11108
|
}
|
|
10986
11109
|
function readConfigFileForMutation(configPath) {
|
|
10987
|
-
if (!(0,
|
|
11110
|
+
if (!(0, import_node_fs2.existsSync)(configPath)) {
|
|
10988
11111
|
return { file: {} };
|
|
10989
11112
|
}
|
|
10990
11113
|
try {
|
|
10991
|
-
const raw = (0,
|
|
11114
|
+
const raw = (0, import_node_fs2.readFileSync)(configPath, "utf8");
|
|
10992
11115
|
const parsed = JSON.parse(raw);
|
|
10993
11116
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
10994
11117
|
return {
|
|
@@ -11005,10 +11128,10 @@ function readConfigFileForMutation(configPath) {
|
|
|
11005
11128
|
}
|
|
11006
11129
|
}
|
|
11007
11130
|
function writeConfigFile(configPath, file) {
|
|
11008
|
-
(0,
|
|
11131
|
+
(0, import_node_fs2.writeFileSync)(configPath, `${JSON.stringify(file, null, 2)}
|
|
11009
11132
|
`, "utf8");
|
|
11010
11133
|
if (process.platform !== "win32") {
|
|
11011
|
-
(0,
|
|
11134
|
+
(0, import_node_fs2.chmodSync)(configPath, 384);
|
|
11012
11135
|
}
|
|
11013
11136
|
}
|
|
11014
11137
|
function normalizeBaseUrl(value) {
|
|
@@ -11021,10 +11144,10 @@ function readAuthMode(input) {
|
|
|
11021
11144
|
return input === "api-token" ? "api-token" : "none";
|
|
11022
11145
|
}
|
|
11023
11146
|
function defaultKeychainAccount(configPath) {
|
|
11024
|
-
return
|
|
11147
|
+
return import_node_path2.default.resolve(configPath);
|
|
11025
11148
|
}
|
|
11026
11149
|
function defaultJiraApiTokenKeychainAccount(configPath) {
|
|
11027
|
-
return `${
|
|
11150
|
+
return `${import_node_path2.default.resolve(configPath)}:jiraApiToken`;
|
|
11028
11151
|
}
|
|
11029
11152
|
function valueFromPrecedence(key, flagValue, envValue, fileValue, fallback = "") {
|
|
11030
11153
|
if (flagValue) {
|
|
@@ -11101,9 +11224,9 @@ function clearSecretFields(file, key) {
|
|
|
11101
11224
|
}
|
|
11102
11225
|
function resolveCliConfig(args, env = process.env, cwd = process.cwd()) {
|
|
11103
11226
|
const { flags, unknownFlags } = parseFlags(args);
|
|
11104
|
-
const configPathFlag = flags["--config"] ?
|
|
11227
|
+
const configPathFlag = flags["--config"] ? import_node_path2.default.resolve(cwd, flags["--config"]) : "";
|
|
11105
11228
|
const configPathEnv = toStringValue(env.TESTOPS_CONFIG_PATH);
|
|
11106
|
-
const configPath = configPathFlag || (configPathEnv ?
|
|
11229
|
+
const configPath = configPathFlag || (configPathEnv ? import_node_path2.default.resolve(cwd, configPathEnv) : import_node_path2.default.resolve(cwd, DEFAULT_CONFIG_FILENAME));
|
|
11107
11230
|
const configPathSource = configPathFlag ? "flag" : configPathEnv ? "env" : "default";
|
|
11108
11231
|
const file = readConfigFile(configPath);
|
|
11109
11232
|
const baseUrlResolved = valueFromPrecedence(
|
|
@@ -11194,7 +11317,7 @@ function resolveCliConfig(args, env = process.env, cwd = process.cwd()) {
|
|
|
11194
11317
|
if (unknownFlags.length > 0) {
|
|
11195
11318
|
warnings.push(`Ignored unknown config flags: ${unknownFlags.join(", ")}`);
|
|
11196
11319
|
}
|
|
11197
|
-
if (!(0,
|
|
11320
|
+
if (!(0, import_node_fs2.existsSync)(configPath) && sources.configPath !== "default") {
|
|
11198
11321
|
warnings.push(`Config file not found at ${configPath}; using env/flags/defaults.`);
|
|
11199
11322
|
}
|
|
11200
11323
|
return { values: config, sources, warnings, errors };
|
|
@@ -11458,7 +11581,7 @@ function createConfigHandler(env = process.env, cwd = process.cwd()) {
|
|
|
11458
11581
|
const [key, ...valueParts] = stripConfigPathArgs(subArgs);
|
|
11459
11582
|
const normalizedKey = normalizeConfigSetKey(key ?? "");
|
|
11460
11583
|
const readValueFromStdin = (normalizedKey === "forgeAuthToken" || normalizedKey === "jiraApiToken") && valueParts.length === 1 && valueParts[0] === "--stdin";
|
|
11461
|
-
const value = readValueFromStdin ? (0,
|
|
11584
|
+
const value = readValueFromStdin ? (0, import_node_fs2.readFileSync)(0, "utf8").trim() : valueParts.join(" ").trim();
|
|
11462
11585
|
if (!key || !value) {
|
|
11463
11586
|
return {
|
|
11464
11587
|
exitCode: ExitCode.UsageError,
|
|
@@ -11773,7 +11896,7 @@ function diagExecutionNoWork() {
|
|
|
11773
11896
|
}
|
|
11774
11897
|
|
|
11775
11898
|
// src/autoDiscover.ts
|
|
11776
|
-
var
|
|
11899
|
+
var import_node_path3 = __toESM(require("node:path"), 1);
|
|
11777
11900
|
var RESULT_FOLDER_NAMES = /* @__PURE__ */ new Set([
|
|
11778
11901
|
"results",
|
|
11779
11902
|
"result",
|
|
@@ -11785,7 +11908,7 @@ var RESULT_FOLDER_NAMES = /* @__PURE__ */ new Set([
|
|
|
11785
11908
|
"failsafe-reports"
|
|
11786
11909
|
]);
|
|
11787
11910
|
function toPosix2(value) {
|
|
11788
|
-
return value.split(
|
|
11911
|
+
return value.split(import_node_path3.default.sep).join("/");
|
|
11789
11912
|
}
|
|
11790
11913
|
function isJUnitXmlArtifact(filePath) {
|
|
11791
11914
|
const normalized = toPosix2(filePath);
|
|
@@ -11879,8 +12002,8 @@ function discoverFeatureFiles(scannedFiles) {
|
|
|
11879
12002
|
}
|
|
11880
12003
|
|
|
11881
12004
|
// src/autoMapping.ts
|
|
11882
|
-
var
|
|
11883
|
-
var
|
|
12005
|
+
var import_node_fs3 = require("node:fs");
|
|
12006
|
+
var import_node_path4 = __toESM(require("node:path"), 1);
|
|
11884
12007
|
var ISSUE_KEY_REGEX = /\b[A-Z][A-Z0-9]+-\d+\b/g;
|
|
11885
12008
|
function normalizeName(value) {
|
|
11886
12009
|
return value.trim().toLowerCase();
|
|
@@ -11890,10 +12013,10 @@ function extractIssueKeysFromText(text) {
|
|
|
11890
12013
|
return [...new Set(found)].sort((a, b) => a.localeCompare(b));
|
|
11891
12014
|
}
|
|
11892
12015
|
function parseFeatureFile(featureAbsolutePath) {
|
|
11893
|
-
if (!(0,
|
|
12016
|
+
if (!(0, import_node_fs3.existsSync)(featureAbsolutePath)) {
|
|
11894
12017
|
return { names: [], issueKeys: [] };
|
|
11895
12018
|
}
|
|
11896
|
-
const raw = (0,
|
|
12019
|
+
const raw = (0, import_node_fs3.readFileSync)(featureAbsolutePath, "utf8");
|
|
11897
12020
|
const lines = raw.split(/\r?\n/);
|
|
11898
12021
|
const names = /* @__PURE__ */ new Set();
|
|
11899
12022
|
const issueKeys = new Set(extractIssueKeysFromText(raw));
|
|
@@ -11951,7 +12074,7 @@ function buildMappingPreview(input) {
|
|
|
11951
12074
|
const localNamesSet = /* @__PURE__ */ new Set();
|
|
11952
12075
|
const issueCandidatesSet = /* @__PURE__ */ new Set();
|
|
11953
12076
|
for (const featurePath of input.featurePaths) {
|
|
11954
|
-
const absolutePath =
|
|
12077
|
+
const absolutePath = import_node_path4.default.join(input.rootPath, featurePath);
|
|
11955
12078
|
const parsed = parseFeatureFile(absolutePath);
|
|
11956
12079
|
for (const name of parsed.names) {
|
|
11957
12080
|
localNamesSet.add(name);
|
|
@@ -12000,8 +12123,8 @@ function buildMappingPreview(input) {
|
|
|
12000
12123
|
}
|
|
12001
12124
|
|
|
12002
12125
|
// src/autoScanner.ts
|
|
12003
|
-
var
|
|
12004
|
-
var
|
|
12126
|
+
var import_node_fs4 = require("node:fs");
|
|
12127
|
+
var import_node_path5 = __toESM(require("node:path"), 1);
|
|
12005
12128
|
var DEFAULT_MAX_DEPTH = 5;
|
|
12006
12129
|
var DEFAULT_EXCLUDED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
12007
12130
|
".cache",
|
|
@@ -12016,7 +12139,7 @@ var DEFAULT_EXCLUDED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
|
12016
12139
|
"node_modules"
|
|
12017
12140
|
]);
|
|
12018
12141
|
function normalizeRelativePath(value) {
|
|
12019
|
-
return value.split(
|
|
12142
|
+
return value.split(import_node_path5.default.sep).join("/");
|
|
12020
12143
|
}
|
|
12021
12144
|
function normalizePatterns(values) {
|
|
12022
12145
|
if (!values) {
|
|
@@ -12070,16 +12193,16 @@ function scanProjectFiles(options) {
|
|
|
12070
12193
|
const includePatterns = normalizePatterns(options.includePatterns);
|
|
12071
12194
|
const excludePatterns = normalizePatterns(options.excludePatterns);
|
|
12072
12195
|
const maxDepth = safeMaxDepth(options.maxDepth);
|
|
12073
|
-
const rootPath =
|
|
12196
|
+
const rootPath = import_node_path5.default.resolve(options.rootPath);
|
|
12074
12197
|
const scannedFiles = [];
|
|
12075
12198
|
const visit = (absoluteDirectory, depth) => {
|
|
12076
12199
|
if (depth > maxDepth) {
|
|
12077
12200
|
return;
|
|
12078
12201
|
}
|
|
12079
|
-
const entries = (0,
|
|
12202
|
+
const entries = (0, import_node_fs4.readdirSync)(absoluteDirectory, { withFileTypes: true }).slice().sort((a, b) => a.name.localeCompare(b.name));
|
|
12080
12203
|
for (const entry of entries) {
|
|
12081
|
-
const absoluteEntryPath =
|
|
12082
|
-
const relativeEntryPath = normalizeRelativePath(
|
|
12204
|
+
const absoluteEntryPath = import_node_path5.default.join(absoluteDirectory, entry.name);
|
|
12205
|
+
const relativeEntryPath = normalizeRelativePath(import_node_path5.default.relative(rootPath, absoluteEntryPath));
|
|
12083
12206
|
if (!relativeEntryPath) {
|
|
12084
12207
|
continue;
|
|
12085
12208
|
}
|
|
@@ -12181,10 +12304,10 @@ function resolveRoot(rootFlag, cwd) {
|
|
|
12181
12304
|
if (!rootFlag.trim()) {
|
|
12182
12305
|
return cwd;
|
|
12183
12306
|
}
|
|
12184
|
-
return
|
|
12307
|
+
return import_node_path6.default.resolve(cwd, rootFlag);
|
|
12185
12308
|
}
|
|
12186
12309
|
function countScenariosInFeatureFile(featurePath) {
|
|
12187
|
-
const lines = (0,
|
|
12310
|
+
const lines = (0, import_node_fs5.readFileSync)(featurePath, "utf8").split(/\r?\n/).map((line) => line.trim().toLowerCase());
|
|
12188
12311
|
let count = 0;
|
|
12189
12312
|
for (const line of lines) {
|
|
12190
12313
|
if (line.startsWith("scenario:") || line.startsWith("scenario outline:")) {
|
|
@@ -12196,8 +12319,8 @@ function countScenariosInFeatureFile(featurePath) {
|
|
|
12196
12319
|
function buildScenarioStats(rootPath, featurePaths) {
|
|
12197
12320
|
let scenarios = 0;
|
|
12198
12321
|
for (const relativePath of featurePaths) {
|
|
12199
|
-
const absolutePath =
|
|
12200
|
-
if (!(0,
|
|
12322
|
+
const absolutePath = import_node_path6.default.join(rootPath, relativePath);
|
|
12323
|
+
if (!(0, import_node_fs5.existsSync)(absolutePath)) {
|
|
12201
12324
|
continue;
|
|
12202
12325
|
}
|
|
12203
12326
|
scenarios += countScenariosInFeatureFile(absolutePath);
|
|
@@ -12237,7 +12360,7 @@ function deriveFeatureNameFromGherkin(gherkin, fallbackPath) {
|
|
|
12237
12360
|
return match[1].trim();
|
|
12238
12361
|
}
|
|
12239
12362
|
}
|
|
12240
|
-
return
|
|
12363
|
+
return import_node_path6.default.basename(fallbackPath, import_node_path6.default.extname(fallbackPath));
|
|
12241
12364
|
}
|
|
12242
12365
|
function mapStepStatus(status) {
|
|
12243
12366
|
const normalized = status.trim().toLowerCase();
|
|
@@ -12253,13 +12376,13 @@ function mapStepStatus(status) {
|
|
|
12253
12376
|
return StepResult.Blocked;
|
|
12254
12377
|
}
|
|
12255
12378
|
function parseCucumberRunsFromArtifact(rootPath, artifactPath) {
|
|
12256
|
-
const absolutePath =
|
|
12257
|
-
if (!(0,
|
|
12379
|
+
const absolutePath = import_node_path6.default.join(rootPath, artifactPath);
|
|
12380
|
+
if (!(0, import_node_fs5.existsSync)(absolutePath)) {
|
|
12258
12381
|
return [];
|
|
12259
12382
|
}
|
|
12260
12383
|
let parsed;
|
|
12261
12384
|
try {
|
|
12262
|
-
parsed = JSON.parse((0,
|
|
12385
|
+
parsed = JSON.parse((0, import_node_fs5.readFileSync)(absolutePath, "utf8"));
|
|
12263
12386
|
} catch {
|
|
12264
12387
|
return [];
|
|
12265
12388
|
}
|
|
@@ -12354,15 +12477,15 @@ async function executeUploadFlow(input) {
|
|
|
12354
12477
|
const diagnostics = [];
|
|
12355
12478
|
const orderedFeatures = [...input.featurePaths].sort((a, b) => a.localeCompare(b));
|
|
12356
12479
|
for (const featurePath of orderedFeatures) {
|
|
12357
|
-
const absolutePath =
|
|
12358
|
-
if (!(0,
|
|
12480
|
+
const absolutePath = import_node_path6.default.join(input.rootPath, featurePath);
|
|
12481
|
+
if (!(0, import_node_fs5.existsSync)(absolutePath)) {
|
|
12359
12482
|
featureFailed += 1;
|
|
12360
12483
|
const diagnostic = diagUploadFeatureFailure(featurePath, "missing feature file");
|
|
12361
12484
|
errors.push(diagnostic.message);
|
|
12362
12485
|
diagnostics.push(diagnostic);
|
|
12363
12486
|
continue;
|
|
12364
12487
|
}
|
|
12365
|
-
const gherkin = (0,
|
|
12488
|
+
const gherkin = (0, import_node_fs5.readFileSync)(absolutePath, "utf8");
|
|
12366
12489
|
const name = deriveFeatureNameFromGherkin(gherkin, featurePath);
|
|
12367
12490
|
const payload = {
|
|
12368
12491
|
context: {
|
|
@@ -12701,7 +12824,7 @@ function createAutoHandler(deps = {}) {
|
|
|
12701
12824
|
stderr: ["ERROR: --output must be either 'plain' or 'json'."]
|
|
12702
12825
|
};
|
|
12703
12826
|
}
|
|
12704
|
-
if (!(0,
|
|
12827
|
+
if (!(0, import_node_fs5.existsSync)(rootPath)) {
|
|
12705
12828
|
return {
|
|
12706
12829
|
exitCode: ExitCode.ValidationError,
|
|
12707
12830
|
stderr: [`ERROR: Root path does not exist: ${rootPath}`]
|
|
@@ -12804,8 +12927,8 @@ function createAutoHandler(deps = {}) {
|
|
|
12804
12927
|
}
|
|
12805
12928
|
|
|
12806
12929
|
// src/allure.ts
|
|
12807
|
-
var
|
|
12808
|
-
var
|
|
12930
|
+
var import_node_path11 = __toESM(require("node:path"), 1);
|
|
12931
|
+
var import_node_fs10 = require("node:fs");
|
|
12809
12932
|
|
|
12810
12933
|
// ../../packages/jira-client/dist/adf.js
|
|
12811
12934
|
function paragraph(text = "") {
|
|
@@ -12851,8 +12974,8 @@ function buildAllureEvidenceCommentAdf(input) {
|
|
|
12851
12974
|
}
|
|
12852
12975
|
|
|
12853
12976
|
// ../../packages/jira-client/dist/jiraClient.js
|
|
12854
|
-
var
|
|
12855
|
-
var
|
|
12977
|
+
var import_node_fs6 = require("node:fs");
|
|
12978
|
+
var import_node_path7 = __toESM(require("node:path"), 1);
|
|
12856
12979
|
function normalizeSite(site) {
|
|
12857
12980
|
return site.trim().replace(/\/+$/, "");
|
|
12858
12981
|
}
|
|
@@ -12865,10 +12988,10 @@ async function readError(response) {
|
|
|
12865
12988
|
}
|
|
12866
12989
|
async function uploadIssueAttachment(params) {
|
|
12867
12990
|
const site = normalizeSite(params.site);
|
|
12868
|
-
const filename =
|
|
12869
|
-
const fileStat = (0,
|
|
12991
|
+
const filename = import_node_path7.default.basename(params.filePath);
|
|
12992
|
+
const fileStat = (0, import_node_fs6.statSync)(params.filePath);
|
|
12870
12993
|
const body = new FormData();
|
|
12871
|
-
const fileBuffer = (0,
|
|
12994
|
+
const fileBuffer = (0, import_node_fs6.readFileSync)(params.filePath);
|
|
12872
12995
|
body.append("file", new Blob([new Uint8Array(fileBuffer)], { type: "application/zip" }), filename);
|
|
12873
12996
|
const response = await fetch(`${site}/rest/api/3/issue/${encodeURIComponent(params.issueKey)}/attachments`, {
|
|
12874
12997
|
method: "POST",
|
|
@@ -12937,7 +13060,7 @@ async function downloadIssueAttachment(params) {
|
|
|
12937
13060
|
throw new Error(`Jira attachment download failed: ${await readError(response)}`);
|
|
12938
13061
|
}
|
|
12939
13062
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
12940
|
-
(0,
|
|
13063
|
+
(0, import_node_fs6.writeFileSync)(params.outputPath, bytes);
|
|
12941
13064
|
return {
|
|
12942
13065
|
filename: params.attachment.filename,
|
|
12943
13066
|
id: params.attachment.id,
|
|
@@ -12977,8 +13100,8 @@ async function addIssueComment(params) {
|
|
|
12977
13100
|
}
|
|
12978
13101
|
|
|
12979
13102
|
// ../../packages/allure-core/dist/parseAllureZip.js
|
|
12980
|
-
var
|
|
12981
|
-
var
|
|
13103
|
+
var import_node_fs7 = require("node:fs");
|
|
13104
|
+
var import_node_path8 = __toESM(require("node:path"), 1);
|
|
12982
13105
|
var import_jszip = __toESM(require_lib3(), 1);
|
|
12983
13106
|
var SUMMARY_PATH_SUFFIX = "/widgets/summary.json";
|
|
12984
13107
|
var MISSING_SUMMARY_ERROR = "Could not find widgets/summary.json. Please upload a generated Allure HTML report ZIP, not raw allure-results.";
|
|
@@ -13006,13 +13129,13 @@ function parseCounts(summary) {
|
|
|
13006
13129
|
};
|
|
13007
13130
|
}
|
|
13008
13131
|
async function parseAllureReportZip(zipPath) {
|
|
13009
|
-
if (!(0,
|
|
13132
|
+
if (!(0, import_node_fs7.existsSync)(zipPath)) {
|
|
13010
13133
|
throw new Error(`Allure report ZIP not found: ${zipPath}`);
|
|
13011
13134
|
}
|
|
13012
|
-
if (
|
|
13135
|
+
if (import_node_path8.default.extname(zipPath).toLowerCase() !== ".zip") {
|
|
13013
13136
|
throw new Error("Allure report must be a .zip file.");
|
|
13014
13137
|
}
|
|
13015
|
-
const zip = await import_jszip.default.loadAsync((0,
|
|
13138
|
+
const zip = await import_jszip.default.loadAsync((0, import_node_fs7.readFileSync)(zipPath));
|
|
13016
13139
|
const filePaths = Object.keys(zip.files).filter((entryPath) => !zip.files[entryPath]?.dir);
|
|
13017
13140
|
const summaryPath = findSummaryPath(filePaths);
|
|
13018
13141
|
if (!summaryPath) {
|
|
@@ -13039,8 +13162,8 @@ async function parseAllureReportZip(zipPath) {
|
|
|
13039
13162
|
}
|
|
13040
13163
|
|
|
13041
13164
|
// ../../packages/allure-core/dist/extractAllureZip.js
|
|
13042
|
-
var
|
|
13043
|
-
var
|
|
13165
|
+
var import_node_fs8 = require("node:fs");
|
|
13166
|
+
var import_node_path9 = __toESM(require("node:path"), 1);
|
|
13044
13167
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
13045
13168
|
var INDEX_HTML = "index.html";
|
|
13046
13169
|
var MISSING_INDEX_ERROR = "Could not find index.html. Please provide a generated Allure HTML report ZIP, not raw allure-results.";
|
|
@@ -13048,41 +13171,41 @@ function findIndexEntry(paths) {
|
|
|
13048
13171
|
return paths.find((entryPath) => entryPath === INDEX_HTML) ?? paths.find((entryPath) => entryPath === `allure-report/${INDEX_HTML}`) ?? paths.find((entryPath) => entryPath.endsWith(`/${INDEX_HTML}`));
|
|
13049
13172
|
}
|
|
13050
13173
|
function ensureInsideRoot(rootDir, candidate) {
|
|
13051
|
-
const resolved =
|
|
13052
|
-
const rootWithSep = rootDir.endsWith(
|
|
13174
|
+
const resolved = import_node_path9.default.resolve(rootDir, candidate);
|
|
13175
|
+
const rootWithSep = rootDir.endsWith(import_node_path9.default.sep) ? rootDir : rootDir + import_node_path9.default.sep;
|
|
13053
13176
|
if (resolved !== rootDir && !resolved.startsWith(rootWithSep)) {
|
|
13054
13177
|
throw new Error(`Refusing to write zip entry outside extract directory: ${candidate}`);
|
|
13055
13178
|
}
|
|
13056
13179
|
return resolved;
|
|
13057
13180
|
}
|
|
13058
13181
|
async function extractAllureReportZip(zipPath, extractDir) {
|
|
13059
|
-
if (!(0,
|
|
13182
|
+
if (!(0, import_node_fs8.existsSync)(zipPath)) {
|
|
13060
13183
|
throw new Error(`Allure report ZIP not found: ${zipPath}`);
|
|
13061
13184
|
}
|
|
13062
|
-
if (
|
|
13185
|
+
if (import_node_path9.default.extname(zipPath).toLowerCase() !== ".zip") {
|
|
13063
13186
|
throw new Error("Allure report must be a .zip file.");
|
|
13064
13187
|
}
|
|
13065
|
-
const zip = await import_jszip2.default.loadAsync((0,
|
|
13188
|
+
const zip = await import_jszip2.default.loadAsync((0, import_node_fs8.readFileSync)(zipPath));
|
|
13066
13189
|
const entries = Object.entries(zip.files);
|
|
13067
13190
|
const filePaths = entries.filter(([, file]) => !file.dir).map(([entryPath]) => entryPath);
|
|
13068
13191
|
const indexEntry = findIndexEntry(filePaths);
|
|
13069
13192
|
if (!indexEntry) {
|
|
13070
13193
|
throw new Error(MISSING_INDEX_ERROR);
|
|
13071
13194
|
}
|
|
13072
|
-
const resolvedExtract =
|
|
13073
|
-
(0,
|
|
13195
|
+
const resolvedExtract = import_node_path9.default.resolve(extractDir);
|
|
13196
|
+
(0, import_node_fs8.mkdirSync)(resolvedExtract, { recursive: true });
|
|
13074
13197
|
for (const [entryPath, file] of entries) {
|
|
13075
13198
|
const target = ensureInsideRoot(resolvedExtract, entryPath);
|
|
13076
13199
|
if (file.dir) {
|
|
13077
|
-
(0,
|
|
13200
|
+
(0, import_node_fs8.mkdirSync)(target, { recursive: true });
|
|
13078
13201
|
continue;
|
|
13079
13202
|
}
|
|
13080
|
-
(0,
|
|
13203
|
+
(0, import_node_fs8.mkdirSync)(import_node_path9.default.dirname(target), { recursive: true });
|
|
13081
13204
|
const buffer = await file.async("nodebuffer");
|
|
13082
|
-
(0,
|
|
13205
|
+
(0, import_node_fs8.writeFileSync)(target, buffer);
|
|
13083
13206
|
}
|
|
13084
|
-
const indexAbsolute =
|
|
13085
|
-
const rootDir =
|
|
13207
|
+
const indexAbsolute = import_node_path9.default.join(resolvedExtract, indexEntry);
|
|
13208
|
+
const rootDir = import_node_path9.default.dirname(indexAbsolute);
|
|
13086
13209
|
return {
|
|
13087
13210
|
zipPath,
|
|
13088
13211
|
extractDir: resolvedExtract,
|
|
@@ -13095,11 +13218,11 @@ async function extractAllureReportZip(zipPath, extractDir) {
|
|
|
13095
13218
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
13096
13219
|
|
|
13097
13220
|
// src/allureOpen.ts
|
|
13098
|
-
var
|
|
13221
|
+
var import_node_fs9 = require("node:fs");
|
|
13099
13222
|
var import_promises = require("node:fs/promises");
|
|
13100
13223
|
var import_node_http = __toESM(require("node:http"), 1);
|
|
13101
13224
|
var import_node_os = require("node:os");
|
|
13102
|
-
var
|
|
13225
|
+
var import_node_path10 = __toESM(require("node:path"), 1);
|
|
13103
13226
|
var import_node_child_process2 = require("node:child_process");
|
|
13104
13227
|
function parseArgs2(args) {
|
|
13105
13228
|
const positionals = [];
|
|
@@ -13168,7 +13291,7 @@ var MIME_TYPES = {
|
|
|
13168
13291
|
".txt": "text/plain; charset=utf-8"
|
|
13169
13292
|
};
|
|
13170
13293
|
function contentTypeFor(filePath) {
|
|
13171
|
-
return MIME_TYPES[
|
|
13294
|
+
return MIME_TYPES[import_node_path10.default.extname(filePath).toLowerCase()] ?? "application/octet-stream";
|
|
13172
13295
|
}
|
|
13173
13296
|
function resolveRequestedPath(rootDir, urlPath) {
|
|
13174
13297
|
let decoded;
|
|
@@ -13177,10 +13300,10 @@ function resolveRequestedPath(rootDir, urlPath) {
|
|
|
13177
13300
|
} catch {
|
|
13178
13301
|
return void 0;
|
|
13179
13302
|
}
|
|
13180
|
-
const normalized =
|
|
13303
|
+
const normalized = import_node_path10.default.normalize(decoded);
|
|
13181
13304
|
const trimmed = normalized.replace(/^[/\\]+/, "");
|
|
13182
|
-
const candidate =
|
|
13183
|
-
const rootWithSep = rootDir.endsWith(
|
|
13305
|
+
const candidate = import_node_path10.default.resolve(rootDir, trimmed);
|
|
13306
|
+
const rootWithSep = rootDir.endsWith(import_node_path10.default.sep) ? rootDir : rootDir + import_node_path10.default.sep;
|
|
13184
13307
|
if (candidate !== rootDir && !candidate.startsWith(rootWithSep)) {
|
|
13185
13308
|
return void 0;
|
|
13186
13309
|
}
|
|
@@ -13202,16 +13325,16 @@ function createStaticServerStarter() {
|
|
|
13202
13325
|
}
|
|
13203
13326
|
let target = resolved;
|
|
13204
13327
|
try {
|
|
13205
|
-
const stat = (0,
|
|
13328
|
+
const stat = (0, import_node_fs9.statSync)(target);
|
|
13206
13329
|
if (stat.isDirectory()) {
|
|
13207
|
-
target =
|
|
13330
|
+
target = import_node_path10.default.join(target, "index.html");
|
|
13208
13331
|
}
|
|
13209
13332
|
} catch {
|
|
13210
13333
|
res.statusCode = 404;
|
|
13211
13334
|
res.end("Not Found");
|
|
13212
13335
|
return;
|
|
13213
13336
|
}
|
|
13214
|
-
if (!(0,
|
|
13337
|
+
if (!(0, import_node_fs9.existsSync)(target)) {
|
|
13215
13338
|
res.statusCode = 404;
|
|
13216
13339
|
res.end("Not Found");
|
|
13217
13340
|
return;
|
|
@@ -13222,7 +13345,7 @@ function createStaticServerStarter() {
|
|
|
13222
13345
|
res.end();
|
|
13223
13346
|
return;
|
|
13224
13347
|
}
|
|
13225
|
-
const stream = (0,
|
|
13348
|
+
const stream = (0, import_node_fs9.createReadStream)(target);
|
|
13226
13349
|
stream.on("error", () => {
|
|
13227
13350
|
res.statusCode = 500;
|
|
13228
13351
|
res.end("Internal Server Error");
|
|
@@ -13327,7 +13450,7 @@ function createAllureOpenHandler(deps = {}) {
|
|
|
13327
13450
|
const cwd = deps.cwd ?? process.cwd();
|
|
13328
13451
|
const parseZip = deps.parseAllureReportZip ?? parseAllureReportZip;
|
|
13329
13452
|
const extractZip = deps.extractAllureReportZip ?? extractAllureReportZip;
|
|
13330
|
-
const createTempDir = deps.createTempDir ?? (() => (0, import_promises.mkdtemp)(
|
|
13453
|
+
const createTempDir = deps.createTempDir ?? (() => (0, import_promises.mkdtemp)(import_node_path10.default.join((0, import_node_os.tmpdir)(), "automatify-allure-open-")));
|
|
13331
13454
|
const startServer = deps.startServer ?? createStaticServerStarter();
|
|
13332
13455
|
const openInBrowser = deps.openInBrowser ?? createOsBrowserOpener();
|
|
13333
13456
|
const waitForShutdown = deps.waitForShutdown ?? waitForProcessSignals;
|
|
@@ -13362,7 +13485,7 @@ function createAllureOpenHandler(deps = {}) {
|
|
|
13362
13485
|
};
|
|
13363
13486
|
}
|
|
13364
13487
|
const host = parsed.flags["--host"] ?? "127.0.0.1";
|
|
13365
|
-
const zipPath =
|
|
13488
|
+
const zipPath = import_node_path10.default.resolve(cwd, zipPathInput);
|
|
13366
13489
|
let summary;
|
|
13367
13490
|
try {
|
|
13368
13491
|
summary = await parseZip(zipPath);
|
|
@@ -13372,9 +13495,9 @@ function createAllureOpenHandler(deps = {}) {
|
|
|
13372
13495
|
stderr: [`ERROR: ${normalizeError(error)}`]
|
|
13373
13496
|
};
|
|
13374
13497
|
}
|
|
13375
|
-
const extractDirRaw = userExtractDir ?
|
|
13498
|
+
const extractDirRaw = userExtractDir ? import_node_path10.default.resolve(cwd, userExtractDir) : await createTempDir();
|
|
13376
13499
|
if (dryRun) {
|
|
13377
|
-
const indexPath =
|
|
13500
|
+
const indexPath = import_node_path10.default.join(extractDirRaw, "index.html");
|
|
13378
13501
|
if (useJson) {
|
|
13379
13502
|
return {
|
|
13380
13503
|
exitCode: ExitCode.Success,
|
|
@@ -13739,8 +13862,8 @@ function createAllureHandler(deps = {}) {
|
|
|
13739
13862
|
attachmentId: parsed.flags["--attachment-id"],
|
|
13740
13863
|
filename: parsed.flags["--filename"]
|
|
13741
13864
|
});
|
|
13742
|
-
const outputDir =
|
|
13743
|
-
const outputPath =
|
|
13865
|
+
const outputDir = import_node_path11.default.resolve(cwd, parsed.flags["--output-dir"] ?? ".");
|
|
13866
|
+
const outputPath = import_node_path11.default.join(outputDir, selected.filename);
|
|
13744
13867
|
if (dryRun) {
|
|
13745
13868
|
if (useJson) {
|
|
13746
13869
|
return {
|
|
@@ -13765,8 +13888,8 @@ function createAllureHandler(deps = {}) {
|
|
|
13765
13888
|
]
|
|
13766
13889
|
};
|
|
13767
13890
|
}
|
|
13768
|
-
(0,
|
|
13769
|
-
if ((0,
|
|
13891
|
+
(0, import_node_fs10.mkdirSync)(outputDir, { recursive: true });
|
|
13892
|
+
if ((0, import_node_fs10.existsSync)(outputPath) && !force) {
|
|
13770
13893
|
return {
|
|
13771
13894
|
exitCode: ExitCode.ValidationError,
|
|
13772
13895
|
stderr: [`ERROR: Output file already exists: ${outputPath}. Pass --force to overwrite.`]
|
|
@@ -13815,8 +13938,8 @@ function createAllureHandler(deps = {}) {
|
|
|
13815
13938
|
};
|
|
13816
13939
|
}
|
|
13817
13940
|
const resolved = resolveJiraConfig(parsed, env, cwd);
|
|
13818
|
-
const zipPath =
|
|
13819
|
-
const attachmentFilename =
|
|
13941
|
+
const zipPath = import_node_path11.default.resolve(cwd, zipPathInput);
|
|
13942
|
+
const attachmentFilename = import_node_path11.default.basename(zipPath);
|
|
13820
13943
|
if (!resolved.config) {
|
|
13821
13944
|
return {
|
|
13822
13945
|
exitCode: ExitCode.ValidationError,
|
|
@@ -13914,7 +14037,7 @@ function createAllureHandler(deps = {}) {
|
|
|
13914
14037
|
}
|
|
13915
14038
|
|
|
13916
14039
|
// src/auth.ts
|
|
13917
|
-
var
|
|
14040
|
+
var import_node_fs11 = require("node:fs");
|
|
13918
14041
|
function parseAuthArgs(args, allowLoginFlags) {
|
|
13919
14042
|
const configArgs = [];
|
|
13920
14043
|
const unknown = [];
|
|
@@ -13980,7 +14103,7 @@ function sourceLabel(source) {
|
|
|
13980
14103
|
case "keychain":
|
|
13981
14104
|
return "macOS Keychain";
|
|
13982
14105
|
case "secure-store":
|
|
13983
|
-
return "
|
|
14106
|
+
return "local secure store";
|
|
13984
14107
|
case "env":
|
|
13985
14108
|
return "environment";
|
|
13986
14109
|
case "file":
|
|
@@ -14059,12 +14182,13 @@ function createLoginResponse(args, env, cwd, readStdin, platform, context) {
|
|
|
14059
14182
|
]
|
|
14060
14183
|
};
|
|
14061
14184
|
}
|
|
14062
|
-
|
|
14185
|
+
const linuxSecureStoreEnabled2 = platform === "linux" && env.TESTOPS_LINUX_SECURE_STORE?.trim() === "1";
|
|
14186
|
+
if (parsed.tokenStdin && platform !== "darwin" && platform !== "win32" && !linuxSecureStoreEnabled2) {
|
|
14063
14187
|
return {
|
|
14064
14188
|
exitCode: ExitCode.UsageError,
|
|
14065
14189
|
stderr: [
|
|
14066
|
-
"Local secure secret storage is
|
|
14067
|
-
"On Linux/CI, set JIRA_API_TOKEN in the environment and run auth login without --token-stdin."
|
|
14190
|
+
"Local secure secret storage is enabled by default only on macOS and Windows.",
|
|
14191
|
+
"On Linux/CI, either set TESTOPS_LINUX_SECURE_STORE=1 on a trusted persistent host or keep JIRA_API_TOKEN in the environment and run auth login without --token-stdin."
|
|
14068
14192
|
]
|
|
14069
14193
|
};
|
|
14070
14194
|
}
|
|
@@ -14091,7 +14215,7 @@ function createLoginResponse(args, env, cwd, readStdin, platform, context) {
|
|
|
14091
14215
|
}
|
|
14092
14216
|
completed.push(step.label);
|
|
14093
14217
|
}
|
|
14094
|
-
const tokenSource = stdinToken ? platform === "darwin" ? "macOS Keychain" : "Windows DPAPI" : sourceLabel(
|
|
14218
|
+
const tokenSource = stdinToken ? platform === "darwin" ? "macOS Keychain" : platform === "win32" ? "Windows DPAPI" : "Linux encrypted local store" : sourceLabel(
|
|
14095
14219
|
current.sources.jiraApiToken === "default" && env.JIRA_API_TOKEN ? "env" : current.sources.jiraApiToken
|
|
14096
14220
|
);
|
|
14097
14221
|
return {
|
|
@@ -14140,7 +14264,7 @@ function createLogoutResponse(args, env, cwd, context) {
|
|
|
14140
14264
|
function createAuthHandler(deps = {}) {
|
|
14141
14265
|
const env = deps.env ?? process.env;
|
|
14142
14266
|
const cwd = deps.cwd ?? process.cwd();
|
|
14143
|
-
const readStdin = deps.readStdin ?? (() => (0,
|
|
14267
|
+
const readStdin = deps.readStdin ?? (() => (0, import_node_fs11.readFileSync)(0, "utf8"));
|
|
14144
14268
|
const platform = deps.platform ?? process.platform;
|
|
14145
14269
|
return (request, context) => {
|
|
14146
14270
|
const [subcommand, ...args] = request.args;
|
|
@@ -14161,8 +14285,8 @@ function createAuthHandler(deps = {}) {
|
|
|
14161
14285
|
}
|
|
14162
14286
|
|
|
14163
14287
|
// src/bdd.ts
|
|
14164
|
-
var
|
|
14165
|
-
var
|
|
14288
|
+
var import_node_fs12 = require("node:fs");
|
|
14289
|
+
var import_node_path12 = __toESM(require("node:path"), 1);
|
|
14166
14290
|
var import_yazl = __toESM(require_yazl(), 1);
|
|
14167
14291
|
|
|
14168
14292
|
// src/forgeClient.ts
|
|
@@ -14466,7 +14590,7 @@ function parseScenarioFeatureFile(raw, filePath) {
|
|
|
14466
14590
|
}
|
|
14467
14591
|
const scenarioTag = tags.map((tag) => tag.match(SCENARIO_METADATA_PATTERN)).find((match) => Boolean(match));
|
|
14468
14592
|
const scenarioKeyFromTag = scenarioTag?.[1]?.trim().toUpperCase() ?? "";
|
|
14469
|
-
const fileKeyMatch =
|
|
14593
|
+
const fileKeyMatch = import_node_path12.default.basename(filePath ?? "").match(/^(SC-\d+)\.feature$/i);
|
|
14470
14594
|
const scenarioKeyFromFile = fileKeyMatch?.[1]?.trim().toUpperCase() ?? "";
|
|
14471
14595
|
const scenarioKey = scenarioKeyFromTag || scenarioKeyFromFile;
|
|
14472
14596
|
if (scenarioKeyFromTag && scenarioKeyFromFile && scenarioKeyFromTag !== scenarioKeyFromFile) {
|
|
@@ -14586,7 +14710,7 @@ function toJsonExportManifestItems(items) {
|
|
|
14586
14710
|
async function defaultCreateZipArchive(archivePath, entries) {
|
|
14587
14711
|
await new Promise((resolve, reject) => {
|
|
14588
14712
|
const zip = new import_yazl.ZipFile();
|
|
14589
|
-
const output = zip.outputStream.pipe((0,
|
|
14713
|
+
const output = zip.outputStream.pipe((0, import_node_fs12.createWriteStream)(archivePath));
|
|
14590
14714
|
output.on("close", () => resolve());
|
|
14591
14715
|
output.on("error", reject);
|
|
14592
14716
|
zip.outputStream.on("error", reject);
|
|
@@ -14616,10 +14740,10 @@ function missingProjectResponse() {
|
|
|
14616
14740
|
function createBddHandler(deps = {}) {
|
|
14617
14741
|
const cwd = deps.cwd ?? process.cwd();
|
|
14618
14742
|
const env = deps.env ?? process.env;
|
|
14619
|
-
const mkdir = deps.mkdir ?? ((targetPath, options) => (0,
|
|
14620
|
-
const readFile = deps.readFile ?? ((filePath) => (0,
|
|
14621
|
-
const readDir = deps.readDir ?? ((dirPath) => (0,
|
|
14622
|
-
const writeFile = deps.writeFile ?? ((filePath, content) => (0,
|
|
14743
|
+
const mkdir = deps.mkdir ?? ((targetPath, options) => (0, import_node_fs12.mkdirSync)(targetPath, options));
|
|
14744
|
+
const readFile = deps.readFile ?? ((filePath) => (0, import_node_fs12.readFileSync)(filePath, "utf8"));
|
|
14745
|
+
const readDir = deps.readDir ?? ((dirPath) => (0, import_node_fs12.readdirSync)(dirPath));
|
|
14746
|
+
const writeFile = deps.writeFile ?? ((filePath, content) => (0, import_node_fs12.writeFileSync)(filePath, content, "utf8"));
|
|
14623
14747
|
const createZipArchive = deps.createZipArchive ?? defaultCreateZipArchive;
|
|
14624
14748
|
return async (request, context) => {
|
|
14625
14749
|
const [subcommand, ...restArgs] = request.args;
|
|
@@ -14663,7 +14787,7 @@ function createBddHandler(deps = {}) {
|
|
|
14663
14787
|
stderr: ["ERROR: Missing required --output-dir for testops bdd features export."]
|
|
14664
14788
|
};
|
|
14665
14789
|
}
|
|
14666
|
-
const outputDir =
|
|
14790
|
+
const outputDir = import_node_path12.default.resolve(cwd, outputDirFlag);
|
|
14667
14791
|
try {
|
|
14668
14792
|
const result = await context.invokeForgeContract("listBddFeatures", {
|
|
14669
14793
|
context: {
|
|
@@ -14701,7 +14825,7 @@ function createBddHandler(deps = {}) {
|
|
|
14701
14825
|
`
|
|
14702
14826
|
}));
|
|
14703
14827
|
for (let index = 0; index < features.length; index += 1) {
|
|
14704
|
-
writeFile(
|
|
14828
|
+
writeFile(import_node_path12.default.join(outputDir, fileNames[index]), exportEntries[index].content);
|
|
14705
14829
|
}
|
|
14706
14830
|
const manifest = {
|
|
14707
14831
|
action: "bdd-features-export",
|
|
@@ -14714,7 +14838,7 @@ function createBddHandler(deps = {}) {
|
|
|
14714
14838
|
};
|
|
14715
14839
|
const manifestContent = `${JSON.stringify(manifest, null, 2)}
|
|
14716
14840
|
`;
|
|
14717
|
-
writeFile(
|
|
14841
|
+
writeFile(import_node_path12.default.join(outputDir, "manifest.json"), manifestContent);
|
|
14718
14842
|
const archivePath = `${outputDir}.zip`;
|
|
14719
14843
|
if (zipRequested) {
|
|
14720
14844
|
await createZipArchive(archivePath, [
|
|
@@ -14784,7 +14908,7 @@ function createBddHandler(deps = {}) {
|
|
|
14784
14908
|
stderr: ["ERROR: Missing required --output-dir for testops bdd scenarios export."]
|
|
14785
14909
|
};
|
|
14786
14910
|
}
|
|
14787
|
-
const outputDir =
|
|
14911
|
+
const outputDir = import_node_path12.default.resolve(cwd, outputDirFlag);
|
|
14788
14912
|
const zipRequested = parsed.boolFlags.has("--zip");
|
|
14789
14913
|
try {
|
|
14790
14914
|
const scenariosResult = await context.invokeForgeContract("listBddScenarios", {
|
|
@@ -14821,7 +14945,7 @@ function createBddHandler(deps = {}) {
|
|
|
14821
14945
|
const fileName = scenarioToExportFileName(scenario);
|
|
14822
14946
|
const fileContent = `${scenarioToFeatureLines(scenario).join("\n")}
|
|
14823
14947
|
`;
|
|
14824
|
-
writeFile(
|
|
14948
|
+
writeFile(import_node_path12.default.join(outputDir, fileName), fileContent);
|
|
14825
14949
|
return {
|
|
14826
14950
|
scenario,
|
|
14827
14951
|
fileName,
|
|
@@ -14847,7 +14971,7 @@ function createBddHandler(deps = {}) {
|
|
|
14847
14971
|
};
|
|
14848
14972
|
const manifestContent = `${JSON.stringify(manifest, null, 2)}
|
|
14849
14973
|
`;
|
|
14850
|
-
writeFile(
|
|
14974
|
+
writeFile(import_node_path12.default.join(outputDir, "manifest.json"), manifestContent);
|
|
14851
14975
|
const archivePath = `${outputDir}.zip`;
|
|
14852
14976
|
if (zipRequested) {
|
|
14853
14977
|
await createZipArchive(archivePath, [
|
|
@@ -14895,8 +15019,8 @@ function createBddHandler(deps = {}) {
|
|
|
14895
15019
|
}
|
|
14896
15020
|
}
|
|
14897
15021
|
if (nested === "import") {
|
|
14898
|
-
const sourceFile = parsed.flags["--file"] ?
|
|
14899
|
-
const inputDir = parsed.flags["--input-dir"] ?
|
|
15022
|
+
const sourceFile = parsed.flags["--file"] ? import_node_path12.default.resolve(cwd, parsed.flags["--file"]) : "";
|
|
15023
|
+
const inputDir = parsed.flags["--input-dir"] ? import_node_path12.default.resolve(cwd, parsed.flags["--input-dir"]) : "";
|
|
14900
15024
|
const dryRun = parsed.boolFlags.has("--dry-run");
|
|
14901
15025
|
if (!sourceFile && !inputDir) {
|
|
14902
15026
|
return {
|
|
@@ -14905,7 +15029,7 @@ function createBddHandler(deps = {}) {
|
|
|
14905
15029
|
};
|
|
14906
15030
|
}
|
|
14907
15031
|
try {
|
|
14908
|
-
const scenarioFiles = sourceFile ? [sourceFile] : readDir(inputDir).filter((entry) => entry.toLowerCase().endsWith(".feature")).map((entry) =>
|
|
15032
|
+
const scenarioFiles = sourceFile ? [sourceFile] : readDir(inputDir).filter((entry) => entry.toLowerCase().endsWith(".feature")).map((entry) => import_node_path12.default.join(inputDir, entry)).sort((left, right) => left.localeCompare(right));
|
|
14909
15033
|
if (scenarioFiles.length === 0) {
|
|
14910
15034
|
return {
|
|
14911
15035
|
exitCode: ExitCode.ValidationError,
|
|
@@ -15954,8 +16078,8 @@ function createDoctorHandler(deps = {}) {
|
|
|
15954
16078
|
}
|
|
15955
16079
|
|
|
15956
16080
|
// src/ingestFeature.ts
|
|
15957
|
-
var
|
|
15958
|
-
var
|
|
16081
|
+
var import_node_fs13 = require("node:fs");
|
|
16082
|
+
var import_node_path13 = __toESM(require("node:path"), 1);
|
|
15959
16083
|
var CONFIG_FLAGS6 = /* @__PURE__ */ new Set([
|
|
15960
16084
|
"--config",
|
|
15961
16085
|
"--base-url",
|
|
@@ -16041,8 +16165,8 @@ function normalizeError5(error) {
|
|
|
16041
16165
|
return "Unknown ingest error.";
|
|
16042
16166
|
}
|
|
16043
16167
|
function createIngestFeatureHandler(deps = {}) {
|
|
16044
|
-
const readFile = deps.readFile ?? ((filePath) => (0,
|
|
16045
|
-
const readStdin = deps.readStdin ?? (() => (0,
|
|
16168
|
+
const readFile = deps.readFile ?? ((filePath) => (0, import_node_fs13.readFileSync)(filePath, "utf8"));
|
|
16169
|
+
const readStdin = deps.readStdin ?? (() => (0, import_node_fs13.readFileSync)(0, "utf8"));
|
|
16046
16170
|
const cwd = deps.cwd ?? process.cwd();
|
|
16047
16171
|
const env = deps.env ?? process.env;
|
|
16048
16172
|
return async (request, context) => {
|
|
@@ -16052,7 +16176,7 @@ function createIngestFeatureHandler(deps = {}) {
|
|
|
16052
16176
|
const config = resolveCliConfig(configArgs, env, cwd);
|
|
16053
16177
|
const projectKey = config.values.projectKey;
|
|
16054
16178
|
const issueKey = config.values.issueKey;
|
|
16055
|
-
const sourceFile = parsed.flags["--file"] ?
|
|
16179
|
+
const sourceFile = parsed.flags["--file"] ? import_node_path13.default.resolve(cwd, parsed.flags["--file"]) : "";
|
|
16056
16180
|
const useStdin = parsed.boolFlags.has("--stdin");
|
|
16057
16181
|
const useJson = parsed.boolFlags.has("--json");
|
|
16058
16182
|
const sourceCount = Number(Boolean(sourceFile)) + Number(useStdin);
|
|
@@ -16172,16 +16296,514 @@ function createIngestFeatureHandler(deps = {}) {
|
|
|
16172
16296
|
};
|
|
16173
16297
|
}
|
|
16174
16298
|
|
|
16299
|
+
// src/profiles.ts
|
|
16300
|
+
var CONFIG_FLAGS7 = /* @__PURE__ */ new Set([
|
|
16301
|
+
"--config",
|
|
16302
|
+
"--base-url",
|
|
16303
|
+
"--project-key",
|
|
16304
|
+
"--issue-key",
|
|
16305
|
+
"--auth-mode",
|
|
16306
|
+
"--jira-email",
|
|
16307
|
+
"--jira-api-token"
|
|
16308
|
+
]);
|
|
16309
|
+
function parseArgs8(args) {
|
|
16310
|
+
const flags = {};
|
|
16311
|
+
const boolFlags = /* @__PURE__ */ new Set();
|
|
16312
|
+
const unknownFlags = [];
|
|
16313
|
+
const supportedValueFlags = /* @__PURE__ */ new Set(["--id", ...CONFIG_FLAGS7]);
|
|
16314
|
+
const supportedBoolFlags = /* @__PURE__ */ new Set(["--json", "--dry-run", "--confirm", "--detach"]);
|
|
16315
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
16316
|
+
const token = args[index];
|
|
16317
|
+
if (!token.startsWith("--")) {
|
|
16318
|
+
unknownFlags.push(token);
|
|
16319
|
+
continue;
|
|
16320
|
+
}
|
|
16321
|
+
if (supportedBoolFlags.has(token)) {
|
|
16322
|
+
boolFlags.add(token);
|
|
16323
|
+
continue;
|
|
16324
|
+
}
|
|
16325
|
+
if (!supportedValueFlags.has(token)) {
|
|
16326
|
+
unknownFlags.push(token);
|
|
16327
|
+
continue;
|
|
16328
|
+
}
|
|
16329
|
+
const value = args[index + 1];
|
|
16330
|
+
if (!value || value.startsWith("--")) {
|
|
16331
|
+
unknownFlags.push(token);
|
|
16332
|
+
continue;
|
|
16333
|
+
}
|
|
16334
|
+
flags[token] = value;
|
|
16335
|
+
index += 1;
|
|
16336
|
+
}
|
|
16337
|
+
return { flags, boolFlags, unknownFlags };
|
|
16338
|
+
}
|
|
16339
|
+
function pickConfigArgs7(parsed) {
|
|
16340
|
+
const args = [];
|
|
16341
|
+
for (const [flag, value] of Object.entries(parsed.flags)) {
|
|
16342
|
+
if (CONFIG_FLAGS7.has(flag)) {
|
|
16343
|
+
args.push(flag, value);
|
|
16344
|
+
}
|
|
16345
|
+
}
|
|
16346
|
+
return args;
|
|
16347
|
+
}
|
|
16348
|
+
function normalizeError6(error) {
|
|
16349
|
+
if (error instanceof ForgeClientError) {
|
|
16350
|
+
return `${error.code}: ${error.message}`;
|
|
16351
|
+
}
|
|
16352
|
+
if (error instanceof Error) {
|
|
16353
|
+
return error.message;
|
|
16354
|
+
}
|
|
16355
|
+
return "Unknown profiles command error.";
|
|
16356
|
+
}
|
|
16357
|
+
function missingProjectResponse3() {
|
|
16358
|
+
return {
|
|
16359
|
+
exitCode: ExitCode.ValidationError,
|
|
16360
|
+
stderr: ["ERROR: Missing project context. Set JIRA_PROJECT_KEY or pass --project-key."]
|
|
16361
|
+
};
|
|
16362
|
+
}
|
|
16363
|
+
function confirmationRequiredResponse(command) {
|
|
16364
|
+
return {
|
|
16365
|
+
exitCode: ExitCode.ValidationError,
|
|
16366
|
+
stderr: [`ERROR: ${command} is destructive and requires --confirm. Use --dry-run first to preview the cleanup.`]
|
|
16367
|
+
};
|
|
16368
|
+
}
|
|
16369
|
+
function remoteErrorResponse(code, message, stdout = []) {
|
|
16370
|
+
return {
|
|
16371
|
+
exitCode: ExitCode.RemoteError,
|
|
16372
|
+
stdout,
|
|
16373
|
+
stderr: [`ERROR: ${code}: ${message}`]
|
|
16374
|
+
};
|
|
16375
|
+
}
|
|
16376
|
+
function toJsonProfile(profile) {
|
|
16377
|
+
return {
|
|
16378
|
+
id: profile.id,
|
|
16379
|
+
label: profile.label,
|
|
16380
|
+
provider: profile.provider,
|
|
16381
|
+
endpointSummary: profile.endpointSummary,
|
|
16382
|
+
enabled: profile.enabled,
|
|
16383
|
+
isProjectDefault: profile.isProjectDefault ?? false,
|
|
16384
|
+
hasSecret: profile.hasSecret,
|
|
16385
|
+
createdAt: profile.createdAt,
|
|
16386
|
+
updatedAt: profile.updatedAt,
|
|
16387
|
+
lastUsedAt: profile.lastUsedAt ?? null
|
|
16388
|
+
};
|
|
16389
|
+
}
|
|
16390
|
+
function toJsonBinding(automation) {
|
|
16391
|
+
return {
|
|
16392
|
+
id: automation.id,
|
|
16393
|
+
scenarioId: automation.scenarioId,
|
|
16394
|
+
label: automation.label,
|
|
16395
|
+
profileId: automation.profileId ?? null,
|
|
16396
|
+
profileLabel: automation.profileLabel ?? null,
|
|
16397
|
+
materializedFromProjectDefault: automation.materializedFromProjectDefault ?? false,
|
|
16398
|
+
enabled: automation.enabled
|
|
16399
|
+
};
|
|
16400
|
+
}
|
|
16401
|
+
function profileLines(profiles) {
|
|
16402
|
+
if (profiles.length === 0) {
|
|
16403
|
+
return ["Automation profiles: 0", "No automation profiles found for the selected project."];
|
|
16404
|
+
}
|
|
16405
|
+
return [
|
|
16406
|
+
`Automation profiles: ${profiles.length}`,
|
|
16407
|
+
...profiles.map((profile) => {
|
|
16408
|
+
const flags = [profile.isProjectDefault ? "default" : "", profile.enabled ? "enabled" : "disabled"].filter(
|
|
16409
|
+
Boolean
|
|
16410
|
+
);
|
|
16411
|
+
return `- ${profile.id} [${flags.join(", ")}] ${profile.label} (${profile.provider}) ${profile.endpointSummary}`;
|
|
16412
|
+
})
|
|
16413
|
+
];
|
|
16414
|
+
}
|
|
16415
|
+
function bindingLines(bindings) {
|
|
16416
|
+
return bindings.map(
|
|
16417
|
+
(binding) => `- ${binding.id} scenario=${binding.scenarioId} ${binding.label}` + (binding.materializedFromProjectDefault ? " [project-default materialized]" : "")
|
|
16418
|
+
);
|
|
16419
|
+
}
|
|
16420
|
+
function profileBindings(automations, profileId) {
|
|
16421
|
+
return automations.filter((automation) => automation.mode === "profile" && automation.profileId === profileId);
|
|
16422
|
+
}
|
|
16423
|
+
function allProfileBindings(automations) {
|
|
16424
|
+
return automations.filter((automation) => automation.mode === "profile");
|
|
16425
|
+
}
|
|
16426
|
+
function createProfilesHandler(deps = {}) {
|
|
16427
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
16428
|
+
const env = deps.env ?? process.env;
|
|
16429
|
+
return async (request, context) => {
|
|
16430
|
+
const [subcommand, ...restArgs] = request.args;
|
|
16431
|
+
const parsed = parseArgs8(restArgs);
|
|
16432
|
+
const useJson = parsed.boolFlags.has("--json");
|
|
16433
|
+
const dryRun = parsed.boolFlags.has("--dry-run");
|
|
16434
|
+
const confirmed = parsed.boolFlags.has("--confirm");
|
|
16435
|
+
const detach = parsed.boolFlags.has("--detach");
|
|
16436
|
+
const profileId = parsed.flags["--id"]?.trim() ?? "";
|
|
16437
|
+
const config = resolveCliConfig(pickConfigArgs7(parsed), env, cwd);
|
|
16438
|
+
const projectKey = config.values.projectKey;
|
|
16439
|
+
const issueKey = config.values.issueKey;
|
|
16440
|
+
const forgeContext = {
|
|
16441
|
+
projectKey: projectKey || void 0,
|
|
16442
|
+
issueKey: issueKey || void 0
|
|
16443
|
+
};
|
|
16444
|
+
if (parsed.unknownFlags.length > 0) {
|
|
16445
|
+
return {
|
|
16446
|
+
exitCode: ExitCode.UsageError,
|
|
16447
|
+
stderr: [`ERROR: Unknown or invalid arguments: ${parsed.unknownFlags.join(", ")}`]
|
|
16448
|
+
};
|
|
16449
|
+
}
|
|
16450
|
+
if (!projectKey) {
|
|
16451
|
+
return missingProjectResponse3();
|
|
16452
|
+
}
|
|
16453
|
+
if (dryRun && confirmed) {
|
|
16454
|
+
return {
|
|
16455
|
+
exitCode: ExitCode.UsageError,
|
|
16456
|
+
stderr: ["ERROR: Use either --dry-run or --confirm, not both."]
|
|
16457
|
+
};
|
|
16458
|
+
}
|
|
16459
|
+
if (subcommand === "list") {
|
|
16460
|
+
if (dryRun || confirmed || detach || profileId) {
|
|
16461
|
+
return {
|
|
16462
|
+
exitCode: ExitCode.UsageError,
|
|
16463
|
+
stderr: ["ERROR: profiles list accepts only configuration flags and optional --json."]
|
|
16464
|
+
};
|
|
16465
|
+
}
|
|
16466
|
+
try {
|
|
16467
|
+
const result = await context.invokeForgeContract("listScenarioAutomationProfiles", {
|
|
16468
|
+
context: forgeContext
|
|
16469
|
+
});
|
|
16470
|
+
if (!result.ok) {
|
|
16471
|
+
return remoteErrorResponse(result.error.code, result.error.message);
|
|
16472
|
+
}
|
|
16473
|
+
if (useJson) {
|
|
16474
|
+
return {
|
|
16475
|
+
exitCode: ExitCode.Success,
|
|
16476
|
+
stdout: toJsonLine({
|
|
16477
|
+
action: "profiles-list",
|
|
16478
|
+
projectKey,
|
|
16479
|
+
count: result.data.length,
|
|
16480
|
+
items: result.data.map(toJsonProfile)
|
|
16481
|
+
})
|
|
16482
|
+
};
|
|
16483
|
+
}
|
|
16484
|
+
return {
|
|
16485
|
+
exitCode: ExitCode.Success,
|
|
16486
|
+
stdout: profileLines(result.data)
|
|
16487
|
+
};
|
|
16488
|
+
} catch (error) {
|
|
16489
|
+
return {
|
|
16490
|
+
exitCode: ExitCode.TransportError,
|
|
16491
|
+
stderr: [`ERROR: ${normalizeError6(error)}`]
|
|
16492
|
+
};
|
|
16493
|
+
}
|
|
16494
|
+
}
|
|
16495
|
+
if (subcommand === "detach") {
|
|
16496
|
+
if (!profileId) {
|
|
16497
|
+
return {
|
|
16498
|
+
exitCode: ExitCode.ValidationError,
|
|
16499
|
+
stderr: ["ERROR: Missing required --id <PROFILE_ID> for profiles detach."]
|
|
16500
|
+
};
|
|
16501
|
+
}
|
|
16502
|
+
if (detach) {
|
|
16503
|
+
return {
|
|
16504
|
+
exitCode: ExitCode.UsageError,
|
|
16505
|
+
stderr: [
|
|
16506
|
+
"ERROR: --detach is only valid with profiles delete; profiles detach already performs that operation."
|
|
16507
|
+
]
|
|
16508
|
+
};
|
|
16509
|
+
}
|
|
16510
|
+
if (!dryRun && !confirmed) {
|
|
16511
|
+
return confirmationRequiredResponse("profiles detach");
|
|
16512
|
+
}
|
|
16513
|
+
try {
|
|
16514
|
+
const result = await context.invokeForgeContract("listScenarioAutomations", {
|
|
16515
|
+
context: forgeContext
|
|
16516
|
+
});
|
|
16517
|
+
if (!result.ok) {
|
|
16518
|
+
return remoteErrorResponse(result.error.code, result.error.message);
|
|
16519
|
+
}
|
|
16520
|
+
const bindings = profileBindings(result.data, profileId);
|
|
16521
|
+
if (dryRun) {
|
|
16522
|
+
if (useJson) {
|
|
16523
|
+
return {
|
|
16524
|
+
exitCode: ExitCode.Success,
|
|
16525
|
+
stdout: toJsonLine({
|
|
16526
|
+
action: "profiles-detach",
|
|
16527
|
+
dryRun: true,
|
|
16528
|
+
projectKey,
|
|
16529
|
+
profileId,
|
|
16530
|
+
bindingCount: bindings.length,
|
|
16531
|
+
bindings: bindings.map(toJsonBinding)
|
|
16532
|
+
})
|
|
16533
|
+
};
|
|
16534
|
+
}
|
|
16535
|
+
return {
|
|
16536
|
+
exitCode: ExitCode.Success,
|
|
16537
|
+
stdout: [
|
|
16538
|
+
`Dry run: profile ${profileId} has ${bindings.length} automation binding(s) to detach.`,
|
|
16539
|
+
...bindingLines(bindings),
|
|
16540
|
+
"No changes made."
|
|
16541
|
+
]
|
|
16542
|
+
};
|
|
16543
|
+
}
|
|
16544
|
+
const deletedIds = [];
|
|
16545
|
+
for (const binding of bindings) {
|
|
16546
|
+
const deleted = await context.invokeForgeContract("deleteScenarioAutomation", {
|
|
16547
|
+
context: forgeContext,
|
|
16548
|
+
automationId: binding.id
|
|
16549
|
+
});
|
|
16550
|
+
if (!deleted.ok) {
|
|
16551
|
+
return remoteErrorResponse(deleted.error.code, deleted.error.message, [
|
|
16552
|
+
`Detached ${deletedIds.length} of ${bindings.length} automation binding(s) before the failure.`
|
|
16553
|
+
]);
|
|
16554
|
+
}
|
|
16555
|
+
deletedIds.push(binding.id);
|
|
16556
|
+
}
|
|
16557
|
+
if (useJson) {
|
|
16558
|
+
return {
|
|
16559
|
+
exitCode: ExitCode.Success,
|
|
16560
|
+
stdout: toJsonLine({
|
|
16561
|
+
action: "profiles-detach",
|
|
16562
|
+
dryRun: false,
|
|
16563
|
+
projectKey,
|
|
16564
|
+
profileId,
|
|
16565
|
+
detachedCount: deletedIds.length,
|
|
16566
|
+
detachedAutomationIds: deletedIds
|
|
16567
|
+
})
|
|
16568
|
+
};
|
|
16569
|
+
}
|
|
16570
|
+
return {
|
|
16571
|
+
exitCode: ExitCode.Success,
|
|
16572
|
+
stdout: [`Detached ${deletedIds.length} automation binding(s) from profile ${profileId}.`]
|
|
16573
|
+
};
|
|
16574
|
+
} catch (error) {
|
|
16575
|
+
return {
|
|
16576
|
+
exitCode: ExitCode.TransportError,
|
|
16577
|
+
stderr: [`ERROR: ${normalizeError6(error)}`]
|
|
16578
|
+
};
|
|
16579
|
+
}
|
|
16580
|
+
}
|
|
16581
|
+
if (subcommand === "delete") {
|
|
16582
|
+
if (!profileId) {
|
|
16583
|
+
return {
|
|
16584
|
+
exitCode: ExitCode.ValidationError,
|
|
16585
|
+
stderr: ["ERROR: Missing required --id <PROFILE_ID> for profiles delete."]
|
|
16586
|
+
};
|
|
16587
|
+
}
|
|
16588
|
+
if (!dryRun && !confirmed) {
|
|
16589
|
+
return confirmationRequiredResponse("profiles delete");
|
|
16590
|
+
}
|
|
16591
|
+
try {
|
|
16592
|
+
let bindings = [];
|
|
16593
|
+
if (detach || dryRun) {
|
|
16594
|
+
const automations = await context.invokeForgeContract("listScenarioAutomations", {
|
|
16595
|
+
context: forgeContext
|
|
16596
|
+
});
|
|
16597
|
+
if (!automations.ok) {
|
|
16598
|
+
return remoteErrorResponse(automations.error.code, automations.error.message);
|
|
16599
|
+
}
|
|
16600
|
+
bindings = profileBindings(automations.data, profileId);
|
|
16601
|
+
}
|
|
16602
|
+
if (dryRun) {
|
|
16603
|
+
if (useJson) {
|
|
16604
|
+
return {
|
|
16605
|
+
exitCode: ExitCode.Success,
|
|
16606
|
+
stdout: toJsonLine({
|
|
16607
|
+
action: "profiles-delete",
|
|
16608
|
+
dryRun: true,
|
|
16609
|
+
projectKey,
|
|
16610
|
+
profileId,
|
|
16611
|
+
detach,
|
|
16612
|
+
bindingCount: bindings.length,
|
|
16613
|
+
bindings: bindings.map(toJsonBinding)
|
|
16614
|
+
})
|
|
16615
|
+
};
|
|
16616
|
+
}
|
|
16617
|
+
return {
|
|
16618
|
+
exitCode: ExitCode.Success,
|
|
16619
|
+
stdout: [
|
|
16620
|
+
`Dry run: profile ${profileId} would be deleted.`,
|
|
16621
|
+
detach ? `The command would first detach ${bindings.length} automation binding(s).` : `The command would not detach ${bindings.length} automation binding(s); Forge will block deletion if the profile is in use.`,
|
|
16622
|
+
...bindingLines(bindings),
|
|
16623
|
+
"No changes made."
|
|
16624
|
+
]
|
|
16625
|
+
};
|
|
16626
|
+
}
|
|
16627
|
+
const detachedIds = [];
|
|
16628
|
+
if (detach) {
|
|
16629
|
+
for (const binding of bindings) {
|
|
16630
|
+
const deletedBinding = await context.invokeForgeContract("deleteScenarioAutomation", {
|
|
16631
|
+
context: forgeContext,
|
|
16632
|
+
automationId: binding.id
|
|
16633
|
+
});
|
|
16634
|
+
if (!deletedBinding.ok) {
|
|
16635
|
+
return remoteErrorResponse(deletedBinding.error.code, deletedBinding.error.message, [
|
|
16636
|
+
`Detached ${detachedIds.length} of ${bindings.length} automation binding(s); profile ${profileId} was not deleted.`
|
|
16637
|
+
]);
|
|
16638
|
+
}
|
|
16639
|
+
detachedIds.push(binding.id);
|
|
16640
|
+
}
|
|
16641
|
+
}
|
|
16642
|
+
const deletedProfile = await context.invokeForgeContract("deleteScenarioAutomationProfile", {
|
|
16643
|
+
context: forgeContext,
|
|
16644
|
+
profileId
|
|
16645
|
+
});
|
|
16646
|
+
if (!deletedProfile.ok) {
|
|
16647
|
+
return remoteErrorResponse(deletedProfile.error.code, deletedProfile.error.message, [
|
|
16648
|
+
`Detached ${detachedIds.length} automation binding(s) before profile deletion was rejected.`
|
|
16649
|
+
]);
|
|
16650
|
+
}
|
|
16651
|
+
if (useJson) {
|
|
16652
|
+
return {
|
|
16653
|
+
exitCode: ExitCode.Success,
|
|
16654
|
+
stdout: toJsonLine({
|
|
16655
|
+
action: "profiles-delete",
|
|
16656
|
+
dryRun: false,
|
|
16657
|
+
projectKey,
|
|
16658
|
+
profileId,
|
|
16659
|
+
detachedCount: detachedIds.length,
|
|
16660
|
+
detachedAutomationIds: detachedIds,
|
|
16661
|
+
deleted: true
|
|
16662
|
+
})
|
|
16663
|
+
};
|
|
16664
|
+
}
|
|
16665
|
+
return {
|
|
16666
|
+
exitCode: ExitCode.Success,
|
|
16667
|
+
stdout: [
|
|
16668
|
+
`Deleted automation profile ${profileId}.`,
|
|
16669
|
+
detach ? `Detached ${detachedIds.length} automation binding(s) first.` : "No automatic detach was requested."
|
|
16670
|
+
]
|
|
16671
|
+
};
|
|
16672
|
+
} catch (error) {
|
|
16673
|
+
return {
|
|
16674
|
+
exitCode: ExitCode.TransportError,
|
|
16675
|
+
stderr: [`ERROR: ${normalizeError6(error)}`]
|
|
16676
|
+
};
|
|
16677
|
+
}
|
|
16678
|
+
}
|
|
16679
|
+
if (subcommand === "clear") {
|
|
16680
|
+
if (profileId || detach) {
|
|
16681
|
+
return {
|
|
16682
|
+
exitCode: ExitCode.UsageError,
|
|
16683
|
+
stderr: [
|
|
16684
|
+
"ERROR: profiles clear clears every profile and profile-based binding; do not pass --id or --detach."
|
|
16685
|
+
]
|
|
16686
|
+
};
|
|
16687
|
+
}
|
|
16688
|
+
if (!dryRun && !confirmed) {
|
|
16689
|
+
return confirmationRequiredResponse("profiles clear");
|
|
16690
|
+
}
|
|
16691
|
+
try {
|
|
16692
|
+
const [profilesResult, automationsResult] = await Promise.all([
|
|
16693
|
+
context.invokeForgeContract("listScenarioAutomationProfiles", {
|
|
16694
|
+
context: forgeContext
|
|
16695
|
+
}),
|
|
16696
|
+
context.invokeForgeContract("listScenarioAutomations", {
|
|
16697
|
+
context: forgeContext
|
|
16698
|
+
})
|
|
16699
|
+
]);
|
|
16700
|
+
if (!profilesResult.ok) {
|
|
16701
|
+
return remoteErrorResponse(profilesResult.error.code, profilesResult.error.message);
|
|
16702
|
+
}
|
|
16703
|
+
if (!automationsResult.ok) {
|
|
16704
|
+
return remoteErrorResponse(automationsResult.error.code, automationsResult.error.message);
|
|
16705
|
+
}
|
|
16706
|
+
const profiles = profilesResult.data;
|
|
16707
|
+
const bindings = allProfileBindings(automationsResult.data);
|
|
16708
|
+
if (dryRun) {
|
|
16709
|
+
if (useJson) {
|
|
16710
|
+
return {
|
|
16711
|
+
exitCode: ExitCode.Success,
|
|
16712
|
+
stdout: toJsonLine({
|
|
16713
|
+
action: "profiles-clear",
|
|
16714
|
+
dryRun: true,
|
|
16715
|
+
projectKey,
|
|
16716
|
+
profileCount: profiles.length,
|
|
16717
|
+
bindingCount: bindings.length,
|
|
16718
|
+
profiles: profiles.map(toJsonProfile),
|
|
16719
|
+
bindings: bindings.map(toJsonBinding)
|
|
16720
|
+
})
|
|
16721
|
+
};
|
|
16722
|
+
}
|
|
16723
|
+
return {
|
|
16724
|
+
exitCode: ExitCode.Success,
|
|
16725
|
+
stdout: [
|
|
16726
|
+
`Dry run: would detach ${bindings.length} profile-based automation binding(s) and delete ${profiles.length} automation profile(s).`,
|
|
16727
|
+
...bindingLines(bindings),
|
|
16728
|
+
...profiles.map((profile) => `- profile ${profile.id} ${profile.label}`),
|
|
16729
|
+
"Direct (non-profile) scenario automations are preserved.",
|
|
16730
|
+
"No changes made."
|
|
16731
|
+
]
|
|
16732
|
+
};
|
|
16733
|
+
}
|
|
16734
|
+
const detachedIds = [];
|
|
16735
|
+
for (const binding of bindings) {
|
|
16736
|
+
const deletedBinding = await context.invokeForgeContract("deleteScenarioAutomation", {
|
|
16737
|
+
context: forgeContext,
|
|
16738
|
+
automationId: binding.id
|
|
16739
|
+
});
|
|
16740
|
+
if (!deletedBinding.ok) {
|
|
16741
|
+
return remoteErrorResponse(deletedBinding.error.code, deletedBinding.error.message, [
|
|
16742
|
+
`Detached ${detachedIds.length} of ${bindings.length} profile-based automation binding(s). No profiles were deleted after the failure.`
|
|
16743
|
+
]);
|
|
16744
|
+
}
|
|
16745
|
+
detachedIds.push(binding.id);
|
|
16746
|
+
}
|
|
16747
|
+
const deletedProfileIds = [];
|
|
16748
|
+
for (const profile of profiles) {
|
|
16749
|
+
const deletedProfile = await context.invokeForgeContract("deleteScenarioAutomationProfile", {
|
|
16750
|
+
context: forgeContext,
|
|
16751
|
+
profileId: profile.id
|
|
16752
|
+
});
|
|
16753
|
+
if (!deletedProfile.ok) {
|
|
16754
|
+
return remoteErrorResponse(deletedProfile.error.code, deletedProfile.error.message, [
|
|
16755
|
+
`Detached ${detachedIds.length} profile-based automation binding(s).`,
|
|
16756
|
+
`Deleted ${deletedProfileIds.length} of ${profiles.length} automation profile(s) before the failure.`
|
|
16757
|
+
]);
|
|
16758
|
+
}
|
|
16759
|
+
deletedProfileIds.push(profile.id);
|
|
16760
|
+
}
|
|
16761
|
+
if (useJson) {
|
|
16762
|
+
return {
|
|
16763
|
+
exitCode: ExitCode.Success,
|
|
16764
|
+
stdout: toJsonLine({
|
|
16765
|
+
action: "profiles-clear",
|
|
16766
|
+
dryRun: false,
|
|
16767
|
+
projectKey,
|
|
16768
|
+
detachedCount: detachedIds.length,
|
|
16769
|
+
detachedAutomationIds: detachedIds,
|
|
16770
|
+
deletedProfileCount: deletedProfileIds.length,
|
|
16771
|
+
deletedProfileIds
|
|
16772
|
+
})
|
|
16773
|
+
};
|
|
16774
|
+
}
|
|
16775
|
+
return {
|
|
16776
|
+
exitCode: ExitCode.Success,
|
|
16777
|
+
stdout: [
|
|
16778
|
+
`Detached ${detachedIds.length} profile-based automation binding(s).`,
|
|
16779
|
+
`Deleted ${deletedProfileIds.length} automation profile(s).`,
|
|
16780
|
+
"Direct (non-profile) scenario automations were preserved."
|
|
16781
|
+
]
|
|
16782
|
+
};
|
|
16783
|
+
} catch (error) {
|
|
16784
|
+
return {
|
|
16785
|
+
exitCode: ExitCode.TransportError,
|
|
16786
|
+
stderr: [`ERROR: ${normalizeError6(error)}`]
|
|
16787
|
+
};
|
|
16788
|
+
}
|
|
16789
|
+
}
|
|
16790
|
+
return {
|
|
16791
|
+
exitCode: ExitCode.UsageError,
|
|
16792
|
+
stderr: ["ERROR: Unsupported profiles subcommand. Use: list, detach, delete, or clear."]
|
|
16793
|
+
};
|
|
16794
|
+
};
|
|
16795
|
+
}
|
|
16796
|
+
|
|
16175
16797
|
// src/runUpload.ts
|
|
16176
|
-
var
|
|
16177
|
-
var
|
|
16798
|
+
var import_node_fs14 = require("node:fs");
|
|
16799
|
+
var import_node_path14 = __toESM(require("node:path"), 1);
|
|
16178
16800
|
var STEP_RESULTS = [
|
|
16179
16801
|
StepResult.Passed,
|
|
16180
16802
|
StepResult.Failed,
|
|
16181
16803
|
StepResult.Skipped,
|
|
16182
16804
|
StepResult.Blocked
|
|
16183
16805
|
];
|
|
16184
|
-
var
|
|
16806
|
+
var CONFIG_FLAGS8 = /* @__PURE__ */ new Set([
|
|
16185
16807
|
"--config",
|
|
16186
16808
|
"--base-url",
|
|
16187
16809
|
"--project-key",
|
|
@@ -16190,7 +16812,7 @@ var CONFIG_FLAGS7 = /* @__PURE__ */ new Set([
|
|
|
16190
16812
|
"--jira-email",
|
|
16191
16813
|
"--jira-api-token"
|
|
16192
16814
|
]);
|
|
16193
|
-
function
|
|
16815
|
+
function parseArgs9(args) {
|
|
16194
16816
|
const flags = {};
|
|
16195
16817
|
const boolFlags = /* @__PURE__ */ new Set();
|
|
16196
16818
|
const unknownFlags = [];
|
|
@@ -16201,7 +16823,7 @@ function parseArgs8(args) {
|
|
|
16201
16823
|
"--feature-name",
|
|
16202
16824
|
"--scenario-name",
|
|
16203
16825
|
"--executed-at",
|
|
16204
|
-
...
|
|
16826
|
+
...CONFIG_FLAGS8
|
|
16205
16827
|
]);
|
|
16206
16828
|
const supportedBoolFlags = /* @__PURE__ */ new Set(["--stdin", "--json"]);
|
|
16207
16829
|
for (let i = 0; i < args.length; i += 1) {
|
|
@@ -16227,16 +16849,16 @@ function parseArgs8(args) {
|
|
|
16227
16849
|
}
|
|
16228
16850
|
return { flags, boolFlags, unknownFlags };
|
|
16229
16851
|
}
|
|
16230
|
-
function
|
|
16852
|
+
function pickConfigArgs8(parsed) {
|
|
16231
16853
|
const configArgs = [];
|
|
16232
16854
|
for (const [flag, value] of Object.entries(parsed.flags)) {
|
|
16233
|
-
if (
|
|
16855
|
+
if (CONFIG_FLAGS8.has(flag)) {
|
|
16234
16856
|
configArgs.push(flag, value);
|
|
16235
16857
|
}
|
|
16236
16858
|
}
|
|
16237
16859
|
return configArgs;
|
|
16238
16860
|
}
|
|
16239
|
-
function
|
|
16861
|
+
function normalizeError7(error) {
|
|
16240
16862
|
if (error instanceof ForgeClientError) {
|
|
16241
16863
|
return `${error.code}: ${error.message}`;
|
|
16242
16864
|
}
|
|
@@ -16315,19 +16937,19 @@ function summarizeRunResult(result) {
|
|
|
16315
16937
|
return lines;
|
|
16316
16938
|
}
|
|
16317
16939
|
function createRunUploadHandler(deps = {}) {
|
|
16318
|
-
const readFile = deps.readFile ?? ((filePath) => (0,
|
|
16319
|
-
const readStdin = deps.readStdin ?? (() => (0,
|
|
16940
|
+
const readFile = deps.readFile ?? ((filePath) => (0, import_node_fs14.readFileSync)(filePath, "utf8"));
|
|
16941
|
+
const readStdin = deps.readStdin ?? (() => (0, import_node_fs14.readFileSync)(0, "utf8"));
|
|
16320
16942
|
const cwd = deps.cwd ?? process.cwd();
|
|
16321
16943
|
const env = deps.env ?? process.env;
|
|
16322
16944
|
return async (request, context) => {
|
|
16323
16945
|
const [, ...subArgs] = request.args;
|
|
16324
|
-
const parsed =
|
|
16325
|
-
const configArgs =
|
|
16946
|
+
const parsed = parseArgs9(subArgs);
|
|
16947
|
+
const configArgs = pickConfigArgs8(parsed);
|
|
16326
16948
|
const config = resolveCliConfig(configArgs, env, cwd);
|
|
16327
16949
|
const projectKey = config.values.projectKey;
|
|
16328
16950
|
const issueKey = config.values.issueKey;
|
|
16329
16951
|
const testCaseKey = parsed.flags["--test-case-key"]?.trim().toUpperCase() ?? "";
|
|
16330
|
-
const sourceFile = parsed.flags["--file"] ?
|
|
16952
|
+
const sourceFile = parsed.flags["--file"] ? import_node_path14.default.resolve(cwd, parsed.flags["--file"]) : "";
|
|
16331
16953
|
const useStdin = parsed.boolFlags.has("--stdin");
|
|
16332
16954
|
const useJson = parsed.boolFlags.has("--json");
|
|
16333
16955
|
const sourceCount = Number(Boolean(sourceFile)) + Number(useStdin);
|
|
@@ -16355,7 +16977,7 @@ function createRunUploadHandler(deps = {}) {
|
|
|
16355
16977
|
} catch (error) {
|
|
16356
16978
|
return {
|
|
16357
16979
|
exitCode: ExitCode.InternalError,
|
|
16358
|
-
stderr: [`ERROR: Failed to read run payload source: ${
|
|
16980
|
+
stderr: [`ERROR: Failed to read run payload source: ${normalizeError7(error)}`]
|
|
16359
16981
|
};
|
|
16360
16982
|
}
|
|
16361
16983
|
const payloadFromSource = parseRunPayload(raw);
|
|
@@ -16385,7 +17007,7 @@ function createRunUploadHandler(deps = {}) {
|
|
|
16385
17007
|
} catch (error) {
|
|
16386
17008
|
return {
|
|
16387
17009
|
exitCode: ExitCode.TransportError,
|
|
16388
|
-
stderr: [`ERROR: ${
|
|
17010
|
+
stderr: [`ERROR: ${normalizeError7(error)}`]
|
|
16389
17011
|
};
|
|
16390
17012
|
}
|
|
16391
17013
|
}
|
|
@@ -16465,7 +17087,7 @@ function createRunUploadHandler(deps = {}) {
|
|
|
16465
17087
|
featureName: runInput.featureName,
|
|
16466
17088
|
scenarioName: runInput.scenarioName,
|
|
16467
17089
|
executedAt: runInput.executedAt,
|
|
16468
|
-
errorMessage:
|
|
17090
|
+
errorMessage: normalizeError7(error)
|
|
16469
17091
|
})
|
|
16470
17092
|
};
|
|
16471
17093
|
}
|
|
@@ -16477,14 +17099,14 @@ function createRunUploadHandler(deps = {}) {
|
|
|
16477
17099
|
`Scenario: ${runInput.scenarioName}`,
|
|
16478
17100
|
`ExecutedAt: ${runInput.executedAt}`
|
|
16479
17101
|
],
|
|
16480
|
-
stderr: [`ERROR: ${
|
|
17102
|
+
stderr: [`ERROR: ${normalizeError7(error)}`]
|
|
16481
17103
|
};
|
|
16482
17104
|
}
|
|
16483
17105
|
};
|
|
16484
17106
|
}
|
|
16485
17107
|
|
|
16486
17108
|
// src/runs.ts
|
|
16487
|
-
var
|
|
17109
|
+
var CONFIG_FLAGS9 = /* @__PURE__ */ new Set([
|
|
16488
17110
|
"--config",
|
|
16489
17111
|
"--base-url",
|
|
16490
17112
|
"--project-key",
|
|
@@ -16493,11 +17115,11 @@ var CONFIG_FLAGS8 = /* @__PURE__ */ new Set([
|
|
|
16493
17115
|
"--jira-email",
|
|
16494
17116
|
"--jira-api-token"
|
|
16495
17117
|
]);
|
|
16496
|
-
function
|
|
17118
|
+
function parseArgs10(args) {
|
|
16497
17119
|
const flags = {};
|
|
16498
17120
|
const boolFlags = /* @__PURE__ */ new Set();
|
|
16499
17121
|
const unknownFlags = [];
|
|
16500
|
-
const supportedValueFlags = /* @__PURE__ */ new Set(["--id", ...
|
|
17122
|
+
const supportedValueFlags = /* @__PURE__ */ new Set(["--id", ...CONFIG_FLAGS9]);
|
|
16501
17123
|
const supportedBoolFlags = /* @__PURE__ */ new Set(["--json"]);
|
|
16502
17124
|
for (let index = 0; index < args.length; index += 1) {
|
|
16503
17125
|
const token = args[index];
|
|
@@ -16522,16 +17144,16 @@ function parseArgs9(args) {
|
|
|
16522
17144
|
}
|
|
16523
17145
|
return { flags, boolFlags, unknownFlags };
|
|
16524
17146
|
}
|
|
16525
|
-
function
|
|
17147
|
+
function pickConfigArgs9(parsed) {
|
|
16526
17148
|
const args = [];
|
|
16527
17149
|
for (const [flag, value] of Object.entries(parsed.flags)) {
|
|
16528
|
-
if (
|
|
17150
|
+
if (CONFIG_FLAGS9.has(flag)) {
|
|
16529
17151
|
args.push(flag, value);
|
|
16530
17152
|
}
|
|
16531
17153
|
}
|
|
16532
17154
|
return args;
|
|
16533
17155
|
}
|
|
16534
|
-
function
|
|
17156
|
+
function normalizeError8(error) {
|
|
16535
17157
|
if (error instanceof ForgeClientError) {
|
|
16536
17158
|
return `${error.code}: ${error.message}`;
|
|
16537
17159
|
}
|
|
@@ -16584,7 +17206,7 @@ function runToLines(run) {
|
|
|
16584
17206
|
}
|
|
16585
17207
|
return lines;
|
|
16586
17208
|
}
|
|
16587
|
-
function
|
|
17209
|
+
function missingProjectResponse4() {
|
|
16588
17210
|
return {
|
|
16589
17211
|
exitCode: ExitCode.ValidationError,
|
|
16590
17212
|
stderr: ["ERROR: Missing project context. Set JIRA_PROJECT_KEY or pass --project-key."]
|
|
@@ -16595,9 +17217,9 @@ function createRunsHandler(deps = {}) {
|
|
|
16595
17217
|
const env = deps.env ?? process.env;
|
|
16596
17218
|
return async (request, context) => {
|
|
16597
17219
|
const [subcommand, ...restArgs] = request.args;
|
|
16598
|
-
const parsed =
|
|
17220
|
+
const parsed = parseArgs10(restArgs);
|
|
16599
17221
|
const useJson = parsed.boolFlags.has("--json");
|
|
16600
|
-
const config = resolveCliConfig(
|
|
17222
|
+
const config = resolveCliConfig(pickConfigArgs9(parsed), env, cwd);
|
|
16601
17223
|
const projectKey = config.values.projectKey;
|
|
16602
17224
|
const issueKey = config.values.issueKey;
|
|
16603
17225
|
if (parsed.unknownFlags.length > 0) {
|
|
@@ -16607,7 +17229,7 @@ function createRunsHandler(deps = {}) {
|
|
|
16607
17229
|
};
|
|
16608
17230
|
}
|
|
16609
17231
|
if (!projectKey) {
|
|
16610
|
-
return
|
|
17232
|
+
return missingProjectResponse4();
|
|
16611
17233
|
}
|
|
16612
17234
|
if (subcommand === "show") {
|
|
16613
17235
|
const runId = parsed.flags["--id"]?.trim() ?? "";
|
|
@@ -16649,7 +17271,7 @@ function createRunsHandler(deps = {}) {
|
|
|
16649
17271
|
} catch (error) {
|
|
16650
17272
|
return {
|
|
16651
17273
|
exitCode: ExitCode.TransportError,
|
|
16652
|
-
stderr: [`ERROR: ${
|
|
17274
|
+
stderr: [`ERROR: ${normalizeError8(error)}`]
|
|
16653
17275
|
};
|
|
16654
17276
|
}
|
|
16655
17277
|
}
|
|
@@ -16661,16 +17283,16 @@ function createRunsHandler(deps = {}) {
|
|
|
16661
17283
|
}
|
|
16662
17284
|
|
|
16663
17285
|
// src/setup.ts
|
|
16664
|
-
var
|
|
16665
|
-
var
|
|
17286
|
+
var import_node_fs16 = require("node:fs");
|
|
17287
|
+
var import_node_path16 = __toESM(require("node:path"), 1);
|
|
16666
17288
|
|
|
16667
17289
|
// src/githubSetupProvider.ts
|
|
16668
17290
|
var import_node_child_process4 = require("node:child_process");
|
|
16669
17291
|
|
|
16670
17292
|
// src/setupPlan.ts
|
|
16671
|
-
var
|
|
16672
|
-
var
|
|
16673
|
-
var
|
|
17293
|
+
var import_node_crypto2 = require("node:crypto");
|
|
17294
|
+
var import_node_fs15 = require("node:fs");
|
|
17295
|
+
var import_node_path15 = __toESM(require("node:path"), 1);
|
|
16674
17296
|
var SETUP_PLAN_SCHEMA_VERSION = "automatify.testops.setup/v1";
|
|
16675
17297
|
var SETUP_PLAN_KIND = "AutomatifyTestOpsSetupPlan";
|
|
16676
17298
|
var ACTIONS = [
|
|
@@ -16694,7 +17316,7 @@ var ACTIONS = [
|
|
|
16694
17316
|
}
|
|
16695
17317
|
];
|
|
16696
17318
|
function sha256(value) {
|
|
16697
|
-
return (0,
|
|
17319
|
+
return (0, import_node_crypto2.createHash)("sha256").update(value, "utf8").digest("hex");
|
|
16698
17320
|
}
|
|
16699
17321
|
function sortJsonValue(value) {
|
|
16700
17322
|
if (Array.isArray(value)) {
|
|
@@ -16739,8 +17361,8 @@ function normalizeRepoPart(value, field) {
|
|
|
16739
17361
|
return normalized;
|
|
16740
17362
|
}
|
|
16741
17363
|
function normalizeWorkflowPath(value) {
|
|
16742
|
-
const normalized =
|
|
16743
|
-
if (
|
|
17364
|
+
const normalized = import_node_path15.default.posix.normalize(requireNonEmpty(value, "workflow path").replaceAll("\\", "/"));
|
|
17365
|
+
if (import_node_path15.default.posix.isAbsolute(normalized) || normalized.startsWith("../") || !normalized.startsWith(".github/workflows/") || !/\.ya?ml$/i.test(normalized)) {
|
|
16744
17366
|
throw new Error("workflow path must be a relative .github/workflows/*.yml or *.yaml path without '..'.");
|
|
16745
17367
|
}
|
|
16746
17368
|
return normalized;
|
|
@@ -16765,7 +17387,7 @@ function buildGitHubSetupPlan(input) {
|
|
|
16765
17387
|
if (endpointSecretName === tokenSecretName) {
|
|
16766
17388
|
throw new Error("callback endpoint and auth token secret names must be different.");
|
|
16767
17389
|
}
|
|
16768
|
-
const workflowContent = (0,
|
|
17390
|
+
const workflowContent = (0, import_node_fs15.readFileSync)(input.workflowSourcePath, "utf8");
|
|
16769
17391
|
if (!workflowContent.trim()) {
|
|
16770
17392
|
throw new Error("workflow source file is empty.");
|
|
16771
17393
|
}
|
|
@@ -16798,7 +17420,7 @@ function buildGitHubSetupPlan(input) {
|
|
|
16798
17420
|
workflowId,
|
|
16799
17421
|
workflowFile: {
|
|
16800
17422
|
path: workflowPath,
|
|
16801
|
-
sourcePath:
|
|
17423
|
+
sourcePath: import_node_path15.default.resolve(input.workflowSourcePath),
|
|
16802
17424
|
sha256: sha256(workflowContent)
|
|
16803
17425
|
},
|
|
16804
17426
|
requiredSecrets: [
|
|
@@ -17399,8 +18021,8 @@ var AzureDevOpsSetupProvider = class {
|
|
|
17399
18021
|
};
|
|
17400
18022
|
|
|
17401
18023
|
// src/azureDevOpsSetupPlan.ts
|
|
17402
|
-
var
|
|
17403
|
-
var digest = (v) => `sha256:${(0,
|
|
18024
|
+
var import_node_crypto3 = require("node:crypto");
|
|
18025
|
+
var digest = (v) => `sha256:${(0, import_node_crypto3.createHash)("sha256").update(JSON.stringify(v)).digest("hex")}`;
|
|
17404
18026
|
var required = (value, field) => {
|
|
17405
18027
|
const result = value.trim();
|
|
17406
18028
|
if (!result) throw new Error(`${field} is required.`);
|
|
@@ -17629,7 +18251,7 @@ function defaultReadStdin() {
|
|
|
17629
18251
|
process.stdin.on("error", reject);
|
|
17630
18252
|
});
|
|
17631
18253
|
}
|
|
17632
|
-
function
|
|
18254
|
+
function parseArgs11(args, valueFlags, boolFlags, allowApplyCollections = false) {
|
|
17633
18255
|
const flags = {};
|
|
17634
18256
|
const enabled = /* @__PURE__ */ new Set();
|
|
17635
18257
|
const approvals = [];
|
|
@@ -17708,10 +18330,10 @@ function errorCodeForError(error, fallback) {
|
|
|
17708
18330
|
return error instanceof SetupCommandError ? error.cliCode : fallback;
|
|
17709
18331
|
}
|
|
17710
18332
|
function loadPlan(planPath, cwd) {
|
|
17711
|
-
const absolute =
|
|
18333
|
+
const absolute = import_node_path16.default.resolve(cwd, planPath);
|
|
17712
18334
|
let parsed;
|
|
17713
18335
|
try {
|
|
17714
|
-
parsed = JSON.parse((0,
|
|
18336
|
+
parsed = JSON.parse((0, import_node_fs16.readFileSync)(absolute, "utf8"));
|
|
17715
18337
|
} catch (error) {
|
|
17716
18338
|
throw new SetupCommandError(
|
|
17717
18339
|
"PLAN_READ_ERROR",
|
|
@@ -17936,9 +18558,9 @@ function rollbackFor(plan, actionId) {
|
|
|
17936
18558
|
return plan.rollback.find((item) => item.actionId === actionId)?.strategy ?? "Review the plan rollback section.";
|
|
17937
18559
|
}
|
|
17938
18560
|
function resolveWorkflowTarget(plan, repoRoot) {
|
|
17939
|
-
const resolvedRoot =
|
|
17940
|
-
const target =
|
|
17941
|
-
if (target !== resolvedRoot && !target.startsWith(`${resolvedRoot}${
|
|
18561
|
+
const resolvedRoot = import_node_path16.default.resolve(repoRoot);
|
|
18562
|
+
const target = import_node_path16.default.resolve(resolvedRoot, plan.github.workflowFile.path);
|
|
18563
|
+
if (target !== resolvedRoot && !target.startsWith(`${resolvedRoot}${import_node_path16.default.sep}`)) {
|
|
17942
18564
|
throw new SetupCommandError(
|
|
17943
18565
|
"WORKFLOW_PATH_ERROR",
|
|
17944
18566
|
"Workflow target escapes the approved repository root.",
|
|
@@ -17950,7 +18572,7 @@ function resolveWorkflowTarget(plan, repoRoot) {
|
|
|
17950
18572
|
function applyWorkflowFile(plan, repoRoot, dryRun) {
|
|
17951
18573
|
const action = plan.actions.find((item) => item.scope === "workflow-file");
|
|
17952
18574
|
const target = resolveWorkflowTarget(plan, repoRoot);
|
|
17953
|
-
const sourceContent = (0,
|
|
18575
|
+
const sourceContent = (0, import_node_fs16.readFileSync)(plan.github.workflowFile.sourcePath, "utf8");
|
|
17954
18576
|
if (hashSetupContent(sourceContent) !== plan.github.workflowFile.sha256) {
|
|
17955
18577
|
throw new SetupCommandError(
|
|
17956
18578
|
"WORKFLOW_SOURCE_CHANGED",
|
|
@@ -17958,8 +18580,8 @@ function applyWorkflowFile(plan, repoRoot, dryRun) {
|
|
|
17958
18580
|
ExitCode.ValidationError
|
|
17959
18581
|
);
|
|
17960
18582
|
}
|
|
17961
|
-
const existed = (0,
|
|
17962
|
-
const previousContent = existed ? (0,
|
|
18583
|
+
const existed = (0, import_node_fs16.existsSync)(target);
|
|
18584
|
+
const previousContent = existed ? (0, import_node_fs16.readFileSync)(target, "utf8") : void 0;
|
|
17963
18585
|
const matches = previousContent !== void 0 && hashSetupContent(previousContent) === plan.github.workflowFile.sha256;
|
|
17964
18586
|
if (matches) {
|
|
17965
18587
|
return {
|
|
@@ -17971,8 +18593,8 @@ function applyWorkflowFile(plan, repoRoot, dryRun) {
|
|
|
17971
18593
|
};
|
|
17972
18594
|
}
|
|
17973
18595
|
if (!dryRun) {
|
|
17974
|
-
(0,
|
|
17975
|
-
(0,
|
|
18596
|
+
(0, import_node_fs16.mkdirSync)(import_node_path16.default.dirname(target), { recursive: true });
|
|
18597
|
+
(0, import_node_fs16.writeFileSync)(target, sourceContent, { encoding: "utf8", mode: 420 });
|
|
17976
18598
|
}
|
|
17977
18599
|
return {
|
|
17978
18600
|
id: action.id,
|
|
@@ -18142,14 +18764,14 @@ async function inspectPlan(plan, repoRoot, secrets, context, deps) {
|
|
|
18142
18764
|
const checks = [];
|
|
18143
18765
|
let forgeTransportFailure = false;
|
|
18144
18766
|
const target = resolveWorkflowTarget(plan, repoRoot);
|
|
18145
|
-
if (!(0,
|
|
18767
|
+
if (!(0, import_node_fs16.existsSync)(target)) {
|
|
18146
18768
|
checks.push({
|
|
18147
18769
|
id: "workflow-local",
|
|
18148
18770
|
status: "fail",
|
|
18149
18771
|
message: `Local workflow file ${plan.github.workflowFile.path} does not exist.`
|
|
18150
18772
|
});
|
|
18151
18773
|
} else {
|
|
18152
|
-
const localHash = hashSetupContent((0,
|
|
18774
|
+
const localHash = hashSetupContent((0, import_node_fs16.readFileSync)(target, "utf8"));
|
|
18153
18775
|
checks.push({
|
|
18154
18776
|
id: "workflow-local",
|
|
18155
18777
|
status: localHash === plan.github.workflowFile.sha256 ? "pass" : "fail",
|
|
@@ -18239,7 +18861,7 @@ async function inspectPlan(plan, repoRoot, secrets, context, deps) {
|
|
|
18239
18861
|
};
|
|
18240
18862
|
}
|
|
18241
18863
|
function planCommand(args, deps) {
|
|
18242
|
-
const parsed =
|
|
18864
|
+
const parsed = parseArgs11(args, PLAN_VALUE_FLAGS, /* @__PURE__ */ new Set(["--set-default", "--disabled"]));
|
|
18243
18865
|
if (parsed.errors.length > 0) {
|
|
18244
18866
|
return jsonResponse(
|
|
18245
18867
|
errorPayload("USAGE_ERROR", "Invalid setup plan arguments.", parsed.errors),
|
|
@@ -18285,7 +18907,7 @@ function planCommand(args, deps) {
|
|
|
18285
18907
|
ref: parsed.flags["--ref"] ?? "main",
|
|
18286
18908
|
workflowId: parsed.flags["--workflow-id"] ?? "scenario-dispatch.yml",
|
|
18287
18909
|
workflowPath: parsed.flags["--workflow-path"] ?? ".github/workflows/scenario-dispatch.yml",
|
|
18288
|
-
workflowSourcePath:
|
|
18910
|
+
workflowSourcePath: import_node_path16.default.resolve(deps.cwd, parsed.flags["--workflow-source"] ?? ""),
|
|
18289
18911
|
profileLabel: parsed.flags["--profile-label"] ?? "GitHub Actions",
|
|
18290
18912
|
enabled: !parsed.boolFlags.has("--disabled"),
|
|
18291
18913
|
setProjectDefault: parsed.boolFlags.has("--set-default"),
|
|
@@ -18298,7 +18920,7 @@ function planCommand(args, deps) {
|
|
|
18298
18920
|
}
|
|
18299
18921
|
}
|
|
18300
18922
|
async function applyCommand(args, context, deps) {
|
|
18301
|
-
const parsed =
|
|
18923
|
+
const parsed = parseArgs11(args, APPLY_VALUE_FLAGS, APPLY_BOOL_FLAGS, true);
|
|
18302
18924
|
if (parsed.errors.length > 0) {
|
|
18303
18925
|
return jsonResponse(
|
|
18304
18926
|
errorPayload("USAGE_ERROR", "Invalid setup apply arguments.", parsed.errors),
|
|
@@ -18325,7 +18947,7 @@ async function applyCommand(args, context, deps) {
|
|
|
18325
18947
|
return await applyAzureCommand(plan, parsed, context, deps, secrets);
|
|
18326
18948
|
}
|
|
18327
18949
|
const scopes = approvedScopes(plan, parsed, dryRun);
|
|
18328
|
-
const repoRoot =
|
|
18950
|
+
const repoRoot = import_node_path16.default.resolve(deps.cwd, parsed.flags["--repo-root"] ?? ".");
|
|
18329
18951
|
const rotateSecrets = parsed.boolFlags.has("--rotate-secrets");
|
|
18330
18952
|
const rotateProviderToken = parsed.boolFlags.has("--rotate-provider-token");
|
|
18331
18953
|
const state = await prepareState(plan, scopes, secrets, context, deps, dryRun, rotateSecrets, rotateProviderToken);
|
|
@@ -18541,7 +19163,7 @@ async function applyAzureCommand(plan, parsed, context, deps, secrets) {
|
|
|
18541
19163
|
});
|
|
18542
19164
|
}
|
|
18543
19165
|
async function doctorCommand(args, context, deps) {
|
|
18544
|
-
const parsed =
|
|
19166
|
+
const parsed = parseArgs11(args, APPLY_VALUE_FLAGS, /* @__PURE__ */ new Set(["--secrets-stdin"]), true);
|
|
18545
19167
|
if (parsed.approvals.length > 0) {
|
|
18546
19168
|
return jsonResponse(errorPayload("USAGE_ERROR", "setup doctor does not accept --approve."), ExitCode.UsageError);
|
|
18547
19169
|
}
|
|
@@ -18617,7 +19239,7 @@ async function doctorCommand(args, context, deps) {
|
|
|
18617
19239
|
exitCode
|
|
18618
19240
|
);
|
|
18619
19241
|
}
|
|
18620
|
-
const repoRoot =
|
|
19242
|
+
const repoRoot = import_node_path16.default.resolve(deps.cwd, parsed.flags["--repo-root"] ?? ".");
|
|
18621
19243
|
const summary = await inspectPlan(plan, repoRoot, secrets, context, deps);
|
|
18622
19244
|
return jsonResponse(summary, summary.exitCode);
|
|
18623
19245
|
} catch (error) {
|
|
@@ -18654,7 +19276,7 @@ function createSetupHandler(overrides = {}) {
|
|
|
18654
19276
|
}
|
|
18655
19277
|
|
|
18656
19278
|
// src/suites.ts
|
|
18657
|
-
var
|
|
19279
|
+
var CONFIG_FLAGS10 = /* @__PURE__ */ new Set([
|
|
18658
19280
|
"--config",
|
|
18659
19281
|
"--base-url",
|
|
18660
19282
|
"--project-key",
|
|
@@ -18663,11 +19285,11 @@ var CONFIG_FLAGS9 = /* @__PURE__ */ new Set([
|
|
|
18663
19285
|
"--jira-email",
|
|
18664
19286
|
"--jira-api-token"
|
|
18665
19287
|
]);
|
|
18666
|
-
function
|
|
19288
|
+
function parseArgs12(args) {
|
|
18667
19289
|
const flags = {};
|
|
18668
19290
|
const boolFlags = /* @__PURE__ */ new Set();
|
|
18669
19291
|
const unknownFlags = [];
|
|
18670
|
-
const supportedValueFlags = /* @__PURE__ */ new Set(["--key", ...
|
|
19292
|
+
const supportedValueFlags = /* @__PURE__ */ new Set(["--key", ...CONFIG_FLAGS10]);
|
|
18671
19293
|
const supportedBoolFlags = /* @__PURE__ */ new Set(["--json"]);
|
|
18672
19294
|
for (let index = 0; index < args.length; index += 1) {
|
|
18673
19295
|
const token = args[index];
|
|
@@ -18692,16 +19314,16 @@ function parseArgs11(args) {
|
|
|
18692
19314
|
}
|
|
18693
19315
|
return { flags, boolFlags, unknownFlags };
|
|
18694
19316
|
}
|
|
18695
|
-
function
|
|
19317
|
+
function pickConfigArgs10(parsed) {
|
|
18696
19318
|
const args = [];
|
|
18697
19319
|
for (const [flag, value] of Object.entries(parsed.flags)) {
|
|
18698
|
-
if (
|
|
19320
|
+
if (CONFIG_FLAGS10.has(flag)) {
|
|
18699
19321
|
args.push(flag, value);
|
|
18700
19322
|
}
|
|
18701
19323
|
}
|
|
18702
19324
|
return args;
|
|
18703
19325
|
}
|
|
18704
|
-
function
|
|
19326
|
+
function normalizeError9(error) {
|
|
18705
19327
|
if (error instanceof ForgeClientError) {
|
|
18706
19328
|
return `${error.code}: ${error.message}`;
|
|
18707
19329
|
}
|
|
@@ -18763,7 +19385,7 @@ function suiteCasesToLines(suite, items) {
|
|
|
18763
19385
|
...items.map((item) => `- ${item.key} [${item.status}] ${item.title}`)
|
|
18764
19386
|
];
|
|
18765
19387
|
}
|
|
18766
|
-
function
|
|
19388
|
+
function missingProjectResponse5() {
|
|
18767
19389
|
return {
|
|
18768
19390
|
exitCode: ExitCode.ValidationError,
|
|
18769
19391
|
stderr: ["ERROR: Missing project context. Set JIRA_PROJECT_KEY or pass --project-key."]
|
|
@@ -18780,9 +19402,9 @@ function createSuitesHandler(deps = {}) {
|
|
|
18780
19402
|
const env = deps.env ?? process.env;
|
|
18781
19403
|
return async (request, context) => {
|
|
18782
19404
|
const [subcommand, ...restArgs] = request.args;
|
|
18783
|
-
const parsed =
|
|
19405
|
+
const parsed = parseArgs12(restArgs);
|
|
18784
19406
|
const useJson = parsed.boolFlags.has("--json");
|
|
18785
|
-
const config = resolveCliConfig(
|
|
19407
|
+
const config = resolveCliConfig(pickConfigArgs10(parsed), env, cwd);
|
|
18786
19408
|
const projectKey = config.values.projectKey;
|
|
18787
19409
|
const issueKey = config.values.issueKey;
|
|
18788
19410
|
if (parsed.unknownFlags.length > 0) {
|
|
@@ -18792,7 +19414,7 @@ function createSuitesHandler(deps = {}) {
|
|
|
18792
19414
|
};
|
|
18793
19415
|
}
|
|
18794
19416
|
if (!projectKey) {
|
|
18795
|
-
return
|
|
19417
|
+
return missingProjectResponse5();
|
|
18796
19418
|
}
|
|
18797
19419
|
if (subcommand === "list") {
|
|
18798
19420
|
try {
|
|
@@ -18827,7 +19449,7 @@ function createSuitesHandler(deps = {}) {
|
|
|
18827
19449
|
} catch (error) {
|
|
18828
19450
|
return {
|
|
18829
19451
|
exitCode: ExitCode.TransportError,
|
|
18830
|
-
stderr: [`ERROR: ${
|
|
19452
|
+
stderr: [`ERROR: ${normalizeError9(error)}`]
|
|
18831
19453
|
};
|
|
18832
19454
|
}
|
|
18833
19455
|
}
|
|
@@ -18868,7 +19490,7 @@ function createSuitesHandler(deps = {}) {
|
|
|
18868
19490
|
} catch (error) {
|
|
18869
19491
|
return {
|
|
18870
19492
|
exitCode: ExitCode.TransportError,
|
|
18871
|
-
stderr: [`ERROR: ${
|
|
19493
|
+
stderr: [`ERROR: ${normalizeError9(error)}`]
|
|
18872
19494
|
};
|
|
18873
19495
|
}
|
|
18874
19496
|
}
|
|
@@ -18932,7 +19554,7 @@ function createSuitesHandler(deps = {}) {
|
|
|
18932
19554
|
} catch (error) {
|
|
18933
19555
|
return {
|
|
18934
19556
|
exitCode: ExitCode.TransportError,
|
|
18935
|
-
stderr: [`ERROR: ${
|
|
19557
|
+
stderr: [`ERROR: ${normalizeError9(error)}`]
|
|
18936
19558
|
};
|
|
18937
19559
|
}
|
|
18938
19560
|
}
|
|
@@ -18944,7 +19566,7 @@ function createSuitesHandler(deps = {}) {
|
|
|
18944
19566
|
}
|
|
18945
19567
|
|
|
18946
19568
|
// src/sync.ts
|
|
18947
|
-
var
|
|
19569
|
+
var CONFIG_FLAGS11 = /* @__PURE__ */ new Set([
|
|
18948
19570
|
"--config",
|
|
18949
19571
|
"--base-url",
|
|
18950
19572
|
"--project-key",
|
|
@@ -18954,11 +19576,11 @@ var CONFIG_FLAGS10 = /* @__PURE__ */ new Set([
|
|
|
18954
19576
|
"--jira-api-token"
|
|
18955
19577
|
]);
|
|
18956
19578
|
var RECONCILE_CONFIRM_TOKEN = "RECONCILE";
|
|
18957
|
-
function
|
|
19579
|
+
function parseArgs13(args) {
|
|
18958
19580
|
const flags = {};
|
|
18959
19581
|
const boolFlags = /* @__PURE__ */ new Set();
|
|
18960
19582
|
const unknownFlags = [];
|
|
18961
|
-
const supportedValueFlags = /* @__PURE__ */ new Set(["--confirm", ...
|
|
19583
|
+
const supportedValueFlags = /* @__PURE__ */ new Set(["--confirm", ...CONFIG_FLAGS11]);
|
|
18962
19584
|
const supportedBoolFlags = /* @__PURE__ */ new Set(["--json"]);
|
|
18963
19585
|
for (let i = 0; i < args.length; i += 1) {
|
|
18964
19586
|
const token = args[i];
|
|
@@ -18983,16 +19605,16 @@ function parseArgs12(args) {
|
|
|
18983
19605
|
}
|
|
18984
19606
|
return { flags, boolFlags, unknownFlags };
|
|
18985
19607
|
}
|
|
18986
|
-
function
|
|
19608
|
+
function pickConfigArgs11(parsed) {
|
|
18987
19609
|
const args = [];
|
|
18988
19610
|
for (const [flag, value] of Object.entries(parsed.flags)) {
|
|
18989
|
-
if (
|
|
19611
|
+
if (CONFIG_FLAGS11.has(flag)) {
|
|
18990
19612
|
args.push(flag, value);
|
|
18991
19613
|
}
|
|
18992
19614
|
}
|
|
18993
19615
|
return args;
|
|
18994
19616
|
}
|
|
18995
|
-
function
|
|
19617
|
+
function normalizeError10(error) {
|
|
18996
19618
|
if (error instanceof ForgeClientError) {
|
|
18997
19619
|
return `${error.code}: ${error.message}`;
|
|
18998
19620
|
}
|
|
@@ -19043,9 +19665,9 @@ function createSyncHandler(deps = {}) {
|
|
|
19043
19665
|
const env = deps.env ?? process.env;
|
|
19044
19666
|
return async (request, context) => {
|
|
19045
19667
|
const [subcommand, ...restArgs] = request.args;
|
|
19046
|
-
const parsed =
|
|
19668
|
+
const parsed = parseArgs13(restArgs);
|
|
19047
19669
|
const useJson = parsed.boolFlags.has("--json");
|
|
19048
|
-
const config = resolveCliConfig(
|
|
19670
|
+
const config = resolveCliConfig(pickConfigArgs11(parsed), env, cwd);
|
|
19049
19671
|
const projectKey = config.values.projectKey;
|
|
19050
19672
|
const issueKey = config.values.issueKey;
|
|
19051
19673
|
if (parsed.unknownFlags.length > 0) {
|
|
@@ -19095,7 +19717,7 @@ function createSyncHandler(deps = {}) {
|
|
|
19095
19717
|
} catch (error) {
|
|
19096
19718
|
return {
|
|
19097
19719
|
exitCode: ExitCode.TransportError,
|
|
19098
|
-
stderr: [`ERROR: ${
|
|
19720
|
+
stderr: [`ERROR: ${normalizeError10(error)}`]
|
|
19099
19721
|
};
|
|
19100
19722
|
}
|
|
19101
19723
|
}
|
|
@@ -19141,7 +19763,7 @@ function createSyncHandler(deps = {}) {
|
|
|
19141
19763
|
} catch (error) {
|
|
19142
19764
|
return {
|
|
19143
19765
|
exitCode: ExitCode.TransportError,
|
|
19144
|
-
stderr: [`ERROR: ${
|
|
19766
|
+
stderr: [`ERROR: ${normalizeError10(error)}`]
|
|
19145
19767
|
};
|
|
19146
19768
|
}
|
|
19147
19769
|
}
|
|
@@ -19197,6 +19819,12 @@ var COMMAND_REGISTRY = [
|
|
|
19197
19819
|
subcommands: ["feature"],
|
|
19198
19820
|
handler: createIngestFeatureHandler()
|
|
19199
19821
|
},
|
|
19822
|
+
{
|
|
19823
|
+
name: "profiles",
|
|
19824
|
+
description: "Automation profile inspection, detach, delete, and demo cleanup commands",
|
|
19825
|
+
subcommands: ["list", "detach", "delete", "clear"],
|
|
19826
|
+
handler: createProfilesHandler()
|
|
19827
|
+
},
|
|
19200
19828
|
{
|
|
19201
19829
|
name: "run",
|
|
19202
19830
|
description: "Execution result upload commands",
|