@automatify-au/cli 0.1.16 → 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 +288 -164
- 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);
|
|
@@ -16671,8 +16795,8 @@ function createProfilesHandler(deps = {}) {
|
|
|
16671
16795
|
}
|
|
16672
16796
|
|
|
16673
16797
|
// src/runUpload.ts
|
|
16674
|
-
var
|
|
16675
|
-
var
|
|
16798
|
+
var import_node_fs14 = require("node:fs");
|
|
16799
|
+
var import_node_path14 = __toESM(require("node:path"), 1);
|
|
16676
16800
|
var STEP_RESULTS = [
|
|
16677
16801
|
StepResult.Passed,
|
|
16678
16802
|
StepResult.Failed,
|
|
@@ -16813,8 +16937,8 @@ function summarizeRunResult(result) {
|
|
|
16813
16937
|
return lines;
|
|
16814
16938
|
}
|
|
16815
16939
|
function createRunUploadHandler(deps = {}) {
|
|
16816
|
-
const readFile = deps.readFile ?? ((filePath) => (0,
|
|
16817
|
-
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"));
|
|
16818
16942
|
const cwd = deps.cwd ?? process.cwd();
|
|
16819
16943
|
const env = deps.env ?? process.env;
|
|
16820
16944
|
return async (request, context) => {
|
|
@@ -16825,7 +16949,7 @@ function createRunUploadHandler(deps = {}) {
|
|
|
16825
16949
|
const projectKey = config.values.projectKey;
|
|
16826
16950
|
const issueKey = config.values.issueKey;
|
|
16827
16951
|
const testCaseKey = parsed.flags["--test-case-key"]?.trim().toUpperCase() ?? "";
|
|
16828
|
-
const sourceFile = parsed.flags["--file"] ?
|
|
16952
|
+
const sourceFile = parsed.flags["--file"] ? import_node_path14.default.resolve(cwd, parsed.flags["--file"]) : "";
|
|
16829
16953
|
const useStdin = parsed.boolFlags.has("--stdin");
|
|
16830
16954
|
const useJson = parsed.boolFlags.has("--json");
|
|
16831
16955
|
const sourceCount = Number(Boolean(sourceFile)) + Number(useStdin);
|
|
@@ -17159,16 +17283,16 @@ function createRunsHandler(deps = {}) {
|
|
|
17159
17283
|
}
|
|
17160
17284
|
|
|
17161
17285
|
// src/setup.ts
|
|
17162
|
-
var
|
|
17163
|
-
var
|
|
17286
|
+
var import_node_fs16 = require("node:fs");
|
|
17287
|
+
var import_node_path16 = __toESM(require("node:path"), 1);
|
|
17164
17288
|
|
|
17165
17289
|
// src/githubSetupProvider.ts
|
|
17166
17290
|
var import_node_child_process4 = require("node:child_process");
|
|
17167
17291
|
|
|
17168
17292
|
// src/setupPlan.ts
|
|
17169
|
-
var
|
|
17170
|
-
var
|
|
17171
|
-
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);
|
|
17172
17296
|
var SETUP_PLAN_SCHEMA_VERSION = "automatify.testops.setup/v1";
|
|
17173
17297
|
var SETUP_PLAN_KIND = "AutomatifyTestOpsSetupPlan";
|
|
17174
17298
|
var ACTIONS = [
|
|
@@ -17192,7 +17316,7 @@ var ACTIONS = [
|
|
|
17192
17316
|
}
|
|
17193
17317
|
];
|
|
17194
17318
|
function sha256(value) {
|
|
17195
|
-
return (0,
|
|
17319
|
+
return (0, import_node_crypto2.createHash)("sha256").update(value, "utf8").digest("hex");
|
|
17196
17320
|
}
|
|
17197
17321
|
function sortJsonValue(value) {
|
|
17198
17322
|
if (Array.isArray(value)) {
|
|
@@ -17237,8 +17361,8 @@ function normalizeRepoPart(value, field) {
|
|
|
17237
17361
|
return normalized;
|
|
17238
17362
|
}
|
|
17239
17363
|
function normalizeWorkflowPath(value) {
|
|
17240
|
-
const normalized =
|
|
17241
|
-
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)) {
|
|
17242
17366
|
throw new Error("workflow path must be a relative .github/workflows/*.yml or *.yaml path without '..'.");
|
|
17243
17367
|
}
|
|
17244
17368
|
return normalized;
|
|
@@ -17263,7 +17387,7 @@ function buildGitHubSetupPlan(input) {
|
|
|
17263
17387
|
if (endpointSecretName === tokenSecretName) {
|
|
17264
17388
|
throw new Error("callback endpoint and auth token secret names must be different.");
|
|
17265
17389
|
}
|
|
17266
|
-
const workflowContent = (0,
|
|
17390
|
+
const workflowContent = (0, import_node_fs15.readFileSync)(input.workflowSourcePath, "utf8");
|
|
17267
17391
|
if (!workflowContent.trim()) {
|
|
17268
17392
|
throw new Error("workflow source file is empty.");
|
|
17269
17393
|
}
|
|
@@ -17296,7 +17420,7 @@ function buildGitHubSetupPlan(input) {
|
|
|
17296
17420
|
workflowId,
|
|
17297
17421
|
workflowFile: {
|
|
17298
17422
|
path: workflowPath,
|
|
17299
|
-
sourcePath:
|
|
17423
|
+
sourcePath: import_node_path15.default.resolve(input.workflowSourcePath),
|
|
17300
17424
|
sha256: sha256(workflowContent)
|
|
17301
17425
|
},
|
|
17302
17426
|
requiredSecrets: [
|
|
@@ -17897,8 +18021,8 @@ var AzureDevOpsSetupProvider = class {
|
|
|
17897
18021
|
};
|
|
17898
18022
|
|
|
17899
18023
|
// src/azureDevOpsSetupPlan.ts
|
|
17900
|
-
var
|
|
17901
|
-
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")}`;
|
|
17902
18026
|
var required = (value, field) => {
|
|
17903
18027
|
const result = value.trim();
|
|
17904
18028
|
if (!result) throw new Error(`${field} is required.`);
|
|
@@ -18206,10 +18330,10 @@ function errorCodeForError(error, fallback) {
|
|
|
18206
18330
|
return error instanceof SetupCommandError ? error.cliCode : fallback;
|
|
18207
18331
|
}
|
|
18208
18332
|
function loadPlan(planPath, cwd) {
|
|
18209
|
-
const absolute =
|
|
18333
|
+
const absolute = import_node_path16.default.resolve(cwd, planPath);
|
|
18210
18334
|
let parsed;
|
|
18211
18335
|
try {
|
|
18212
|
-
parsed = JSON.parse((0,
|
|
18336
|
+
parsed = JSON.parse((0, import_node_fs16.readFileSync)(absolute, "utf8"));
|
|
18213
18337
|
} catch (error) {
|
|
18214
18338
|
throw new SetupCommandError(
|
|
18215
18339
|
"PLAN_READ_ERROR",
|
|
@@ -18434,9 +18558,9 @@ function rollbackFor(plan, actionId) {
|
|
|
18434
18558
|
return plan.rollback.find((item) => item.actionId === actionId)?.strategy ?? "Review the plan rollback section.";
|
|
18435
18559
|
}
|
|
18436
18560
|
function resolveWorkflowTarget(plan, repoRoot) {
|
|
18437
|
-
const resolvedRoot =
|
|
18438
|
-
const target =
|
|
18439
|
-
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}`)) {
|
|
18440
18564
|
throw new SetupCommandError(
|
|
18441
18565
|
"WORKFLOW_PATH_ERROR",
|
|
18442
18566
|
"Workflow target escapes the approved repository root.",
|
|
@@ -18448,7 +18572,7 @@ function resolveWorkflowTarget(plan, repoRoot) {
|
|
|
18448
18572
|
function applyWorkflowFile(plan, repoRoot, dryRun) {
|
|
18449
18573
|
const action = plan.actions.find((item) => item.scope === "workflow-file");
|
|
18450
18574
|
const target = resolveWorkflowTarget(plan, repoRoot);
|
|
18451
|
-
const sourceContent = (0,
|
|
18575
|
+
const sourceContent = (0, import_node_fs16.readFileSync)(plan.github.workflowFile.sourcePath, "utf8");
|
|
18452
18576
|
if (hashSetupContent(sourceContent) !== plan.github.workflowFile.sha256) {
|
|
18453
18577
|
throw new SetupCommandError(
|
|
18454
18578
|
"WORKFLOW_SOURCE_CHANGED",
|
|
@@ -18456,8 +18580,8 @@ function applyWorkflowFile(plan, repoRoot, dryRun) {
|
|
|
18456
18580
|
ExitCode.ValidationError
|
|
18457
18581
|
);
|
|
18458
18582
|
}
|
|
18459
|
-
const existed = (0,
|
|
18460
|
-
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;
|
|
18461
18585
|
const matches = previousContent !== void 0 && hashSetupContent(previousContent) === plan.github.workflowFile.sha256;
|
|
18462
18586
|
if (matches) {
|
|
18463
18587
|
return {
|
|
@@ -18469,8 +18593,8 @@ function applyWorkflowFile(plan, repoRoot, dryRun) {
|
|
|
18469
18593
|
};
|
|
18470
18594
|
}
|
|
18471
18595
|
if (!dryRun) {
|
|
18472
|
-
(0,
|
|
18473
|
-
(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 });
|
|
18474
18598
|
}
|
|
18475
18599
|
return {
|
|
18476
18600
|
id: action.id,
|
|
@@ -18640,14 +18764,14 @@ async function inspectPlan(plan, repoRoot, secrets, context, deps) {
|
|
|
18640
18764
|
const checks = [];
|
|
18641
18765
|
let forgeTransportFailure = false;
|
|
18642
18766
|
const target = resolveWorkflowTarget(plan, repoRoot);
|
|
18643
|
-
if (!(0,
|
|
18767
|
+
if (!(0, import_node_fs16.existsSync)(target)) {
|
|
18644
18768
|
checks.push({
|
|
18645
18769
|
id: "workflow-local",
|
|
18646
18770
|
status: "fail",
|
|
18647
18771
|
message: `Local workflow file ${plan.github.workflowFile.path} does not exist.`
|
|
18648
18772
|
});
|
|
18649
18773
|
} else {
|
|
18650
|
-
const localHash = hashSetupContent((0,
|
|
18774
|
+
const localHash = hashSetupContent((0, import_node_fs16.readFileSync)(target, "utf8"));
|
|
18651
18775
|
checks.push({
|
|
18652
18776
|
id: "workflow-local",
|
|
18653
18777
|
status: localHash === plan.github.workflowFile.sha256 ? "pass" : "fail",
|
|
@@ -18783,7 +18907,7 @@ function planCommand(args, deps) {
|
|
|
18783
18907
|
ref: parsed.flags["--ref"] ?? "main",
|
|
18784
18908
|
workflowId: parsed.flags["--workflow-id"] ?? "scenario-dispatch.yml",
|
|
18785
18909
|
workflowPath: parsed.flags["--workflow-path"] ?? ".github/workflows/scenario-dispatch.yml",
|
|
18786
|
-
workflowSourcePath:
|
|
18910
|
+
workflowSourcePath: import_node_path16.default.resolve(deps.cwd, parsed.flags["--workflow-source"] ?? ""),
|
|
18787
18911
|
profileLabel: parsed.flags["--profile-label"] ?? "GitHub Actions",
|
|
18788
18912
|
enabled: !parsed.boolFlags.has("--disabled"),
|
|
18789
18913
|
setProjectDefault: parsed.boolFlags.has("--set-default"),
|
|
@@ -18823,7 +18947,7 @@ async function applyCommand(args, context, deps) {
|
|
|
18823
18947
|
return await applyAzureCommand(plan, parsed, context, deps, secrets);
|
|
18824
18948
|
}
|
|
18825
18949
|
const scopes = approvedScopes(plan, parsed, dryRun);
|
|
18826
|
-
const repoRoot =
|
|
18950
|
+
const repoRoot = import_node_path16.default.resolve(deps.cwd, parsed.flags["--repo-root"] ?? ".");
|
|
18827
18951
|
const rotateSecrets = parsed.boolFlags.has("--rotate-secrets");
|
|
18828
18952
|
const rotateProviderToken = parsed.boolFlags.has("--rotate-provider-token");
|
|
18829
18953
|
const state = await prepareState(plan, scopes, secrets, context, deps, dryRun, rotateSecrets, rotateProviderToken);
|
|
@@ -19115,7 +19239,7 @@ async function doctorCommand(args, context, deps) {
|
|
|
19115
19239
|
exitCode
|
|
19116
19240
|
);
|
|
19117
19241
|
}
|
|
19118
|
-
const repoRoot =
|
|
19242
|
+
const repoRoot = import_node_path16.default.resolve(deps.cwd, parsed.flags["--repo-root"] ?? ".");
|
|
19119
19243
|
const summary = await inspectPlan(plan, repoRoot, secrets, context, deps);
|
|
19120
19244
|
return jsonResponse(summary, summary.exitCode);
|
|
19121
19245
|
} catch (error) {
|