canopycms-cdk 0.0.65 → 0.0.66-int.82
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/lambda/asset-transform/dist/handler.js +3 -0
- package/lambda/asset-transform/dist/node_modules/.package-lock.json +39 -39
- package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/lib/glib-2.0/include/glibconfig.h +1 -1
- package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/lib/{libvips-cpp.so.8.18.3 → libvips-cpp.so.8.18.6} +0 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/package.json +2 -2
- package/lambda/asset-transform/dist/node_modules/@img/sharp-libvips-linux-arm64/versions.json +13 -13
- package/lambda/asset-transform/dist/node_modules/@img/sharp-linux-arm64/index.cjs +1 -1
- package/lambda/asset-transform/dist/node_modules/@img/sharp-linux-arm64/lib/{sharp-linux-arm64-0.35.3.node → sharp-linux-arm64-0.35.4.node} +0 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-linux-arm64/package.json +2 -2
- package/lambda/asset-transform/dist/node_modules/@img/sharp-wasm32/index.cjs +1 -1
- package/lambda/asset-transform/dist/node_modules/@img/sharp-wasm32/lib/sharp-wasm32-0.35.4.node.js +1 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-wasm32/lib/{sharp-wasm32-0.35.3.node.wasm → sharp-wasm32-0.35.4.node.wasm} +0 -0
- package/lambda/asset-transform/dist/node_modules/@img/sharp-wasm32/package.json +2 -2
- package/lambda/asset-transform/dist/node_modules/@img/sharp-wasm32/versions.json +9 -9
- package/lambda/asset-transform/dist/node_modules/sharp/dist/composite.cjs +6 -6
- package/lambda/asset-transform/dist/node_modules/sharp/dist/composite.mjs +6 -6
- package/lambda/asset-transform/dist/node_modules/sharp/dist/constructor.cjs +1 -1
- package/lambda/asset-transform/dist/node_modules/sharp/dist/constructor.mjs +1 -1
- package/lambda/asset-transform/dist/node_modules/sharp/dist/index.d.cts +2 -2
- package/lambda/asset-transform/dist/node_modules/sharp/dist/index.d.mts +2 -2
- package/lambda/asset-transform/dist/node_modules/sharp/dist/input.cjs +20 -15
- package/lambda/asset-transform/dist/node_modules/sharp/dist/input.mjs +20 -15
- package/lambda/asset-transform/dist/node_modules/sharp/dist/operation.cjs +4 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/operation.mjs +4 -0
- package/lambda/asset-transform/dist/node_modules/sharp/dist/output.cjs +4 -7
- package/lambda/asset-transform/dist/node_modules/sharp/dist/output.mjs +4 -7
- package/lambda/asset-transform/dist/node_modules/sharp/dist/resize.cjs +8 -8
- package/lambda/asset-transform/dist/node_modules/sharp/dist/resize.mjs +8 -8
- package/lambda/asset-transform/dist/node_modules/sharp/lib/index.d.ts +2 -2
- package/lambda/asset-transform/dist/node_modules/sharp/package.json +38 -38
- package/lambda/asset-transform/dist/node_modules/sharp/src/common.cc +7 -4
- package/lambda/asset-transform/dist/node_modules/sharp/src/common.h +2 -2
- package/package.json +4 -4
- package/worker/dist/index.js +1684 -1764
- package/lambda/asset-transform/dist/node_modules/@img/sharp-wasm32/lib/sharp-wasm32-0.35.3.node.js +0 -1
package/worker/dist/index.js
CHANGED
|
@@ -814,10 +814,10 @@ var require_src2 = __commonJS({
|
|
|
814
814
|
var fs_1 = __require("fs");
|
|
815
815
|
var debug_1 = __importDefault2(require_src());
|
|
816
816
|
var log7 = debug_1.default("@kwsites/file-exists");
|
|
817
|
-
function check(
|
|
818
|
-
log7(`checking %s`,
|
|
817
|
+
function check(path20, isFile, isDirectory) {
|
|
818
|
+
log7(`checking %s`, path20);
|
|
819
819
|
try {
|
|
820
|
-
const stat = fs_1.statSync(
|
|
820
|
+
const stat = fs_1.statSync(path20);
|
|
821
821
|
if (stat.isFile() && isFile) {
|
|
822
822
|
log7(`[OK] path represents a file`);
|
|
823
823
|
return true;
|
|
@@ -837,8 +837,8 @@ var require_src2 = __commonJS({
|
|
|
837
837
|
throw e5;
|
|
838
838
|
}
|
|
839
839
|
}
|
|
840
|
-
function exists2(
|
|
841
|
-
return check(
|
|
840
|
+
function exists2(path20, type = exports.READABLE) {
|
|
841
|
+
return check(path20, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
|
|
842
842
|
}
|
|
843
843
|
exports.exists = exists2;
|
|
844
844
|
exports.FILE = 1;
|
|
@@ -927,54 +927,54 @@ var require_polyfills = __commonJS({
|
|
|
927
927
|
}
|
|
928
928
|
var chdir;
|
|
929
929
|
module.exports = patch;
|
|
930
|
-
function patch(
|
|
930
|
+
function patch(fs15) {
|
|
931
931
|
if (constants2.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
|
|
932
|
-
patchLchmod(
|
|
933
|
-
}
|
|
934
|
-
if (!
|
|
935
|
-
patchLutimes(
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
if (
|
|
956
|
-
|
|
932
|
+
patchLchmod(fs15);
|
|
933
|
+
}
|
|
934
|
+
if (!fs15.lutimes) {
|
|
935
|
+
patchLutimes(fs15);
|
|
936
|
+
}
|
|
937
|
+
fs15.chown = chownFix(fs15.chown);
|
|
938
|
+
fs15.fchown = chownFix(fs15.fchown);
|
|
939
|
+
fs15.lchown = chownFix(fs15.lchown);
|
|
940
|
+
fs15.chmod = chmodFix(fs15.chmod);
|
|
941
|
+
fs15.fchmod = chmodFix(fs15.fchmod);
|
|
942
|
+
fs15.lchmod = chmodFix(fs15.lchmod);
|
|
943
|
+
fs15.chownSync = chownFixSync(fs15.chownSync);
|
|
944
|
+
fs15.fchownSync = chownFixSync(fs15.fchownSync);
|
|
945
|
+
fs15.lchownSync = chownFixSync(fs15.lchownSync);
|
|
946
|
+
fs15.chmodSync = chmodFixSync(fs15.chmodSync);
|
|
947
|
+
fs15.fchmodSync = chmodFixSync(fs15.fchmodSync);
|
|
948
|
+
fs15.lchmodSync = chmodFixSync(fs15.lchmodSync);
|
|
949
|
+
fs15.stat = statFix(fs15.stat);
|
|
950
|
+
fs15.fstat = statFix(fs15.fstat);
|
|
951
|
+
fs15.lstat = statFix(fs15.lstat);
|
|
952
|
+
fs15.statSync = statFixSync(fs15.statSync);
|
|
953
|
+
fs15.fstatSync = statFixSync(fs15.fstatSync);
|
|
954
|
+
fs15.lstatSync = statFixSync(fs15.lstatSync);
|
|
955
|
+
if (fs15.chmod && !fs15.lchmod) {
|
|
956
|
+
fs15.lchmod = function(path20, mode, cb) {
|
|
957
957
|
if (cb) process.nextTick(cb);
|
|
958
958
|
};
|
|
959
|
-
|
|
959
|
+
fs15.lchmodSync = function() {
|
|
960
960
|
};
|
|
961
961
|
}
|
|
962
|
-
if (
|
|
963
|
-
|
|
962
|
+
if (fs15.chown && !fs15.lchown) {
|
|
963
|
+
fs15.lchown = function(path20, uid, gid, cb) {
|
|
964
964
|
if (cb) process.nextTick(cb);
|
|
965
965
|
};
|
|
966
|
-
|
|
966
|
+
fs15.lchownSync = function() {
|
|
967
967
|
};
|
|
968
968
|
}
|
|
969
969
|
if (platform === "win32") {
|
|
970
|
-
|
|
970
|
+
fs15.rename = typeof fs15.rename !== "function" ? fs15.rename : (function(fs$rename) {
|
|
971
971
|
function rename(from, to, cb) {
|
|
972
972
|
var start = Date.now();
|
|
973
973
|
var backoff = 0;
|
|
974
974
|
fs$rename(from, to, function CB(er) {
|
|
975
975
|
if (er && (er.code === "EACCES" || er.code === "EPERM" || er.code === "EBUSY") && Date.now() - start < 6e4) {
|
|
976
976
|
setTimeout(function() {
|
|
977
|
-
|
|
977
|
+
fs15.stat(to, function(stater, st) {
|
|
978
978
|
if (stater && stater.code === "ENOENT")
|
|
979
979
|
fs$rename(from, to, CB);
|
|
980
980
|
else
|
|
@@ -990,9 +990,9 @@ var require_polyfills = __commonJS({
|
|
|
990
990
|
}
|
|
991
991
|
if (Object.setPrototypeOf) Object.setPrototypeOf(rename, fs$rename);
|
|
992
992
|
return rename;
|
|
993
|
-
})(
|
|
993
|
+
})(fs15.rename);
|
|
994
994
|
}
|
|
995
|
-
|
|
995
|
+
fs15.read = typeof fs15.read !== "function" ? fs15.read : (function(fs$read) {
|
|
996
996
|
function read(fd, buffer, offset, length, position, callback_) {
|
|
997
997
|
var callback;
|
|
998
998
|
if (callback_ && typeof callback_ === "function") {
|
|
@@ -1000,22 +1000,22 @@ var require_polyfills = __commonJS({
|
|
|
1000
1000
|
callback = function(er, _3, __) {
|
|
1001
1001
|
if (er && er.code === "EAGAIN" && eagCounter < 10) {
|
|
1002
1002
|
eagCounter++;
|
|
1003
|
-
return fs$read.call(
|
|
1003
|
+
return fs$read.call(fs15, fd, buffer, offset, length, position, callback);
|
|
1004
1004
|
}
|
|
1005
1005
|
callback_.apply(this, arguments);
|
|
1006
1006
|
};
|
|
1007
1007
|
}
|
|
1008
|
-
return fs$read.call(
|
|
1008
|
+
return fs$read.call(fs15, fd, buffer, offset, length, position, callback);
|
|
1009
1009
|
}
|
|
1010
1010
|
if (Object.setPrototypeOf) Object.setPrototypeOf(read, fs$read);
|
|
1011
1011
|
return read;
|
|
1012
|
-
})(
|
|
1013
|
-
|
|
1012
|
+
})(fs15.read);
|
|
1013
|
+
fs15.readSync = typeof fs15.readSync !== "function" ? fs15.readSync : /* @__PURE__ */ (function(fs$readSync) {
|
|
1014
1014
|
return function(fd, buffer, offset, length, position) {
|
|
1015
1015
|
var eagCounter = 0;
|
|
1016
1016
|
while (true) {
|
|
1017
1017
|
try {
|
|
1018
|
-
return fs$readSync.call(
|
|
1018
|
+
return fs$readSync.call(fs15, fd, buffer, offset, length, position);
|
|
1019
1019
|
} catch (er) {
|
|
1020
1020
|
if (er.code === "EAGAIN" && eagCounter < 10) {
|
|
1021
1021
|
eagCounter++;
|
|
@@ -1025,11 +1025,11 @@ var require_polyfills = __commonJS({
|
|
|
1025
1025
|
}
|
|
1026
1026
|
}
|
|
1027
1027
|
};
|
|
1028
|
-
})(
|
|
1029
|
-
function patchLchmod(
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1028
|
+
})(fs15.readSync);
|
|
1029
|
+
function patchLchmod(fs16) {
|
|
1030
|
+
fs16.lchmod = function(path20, mode, callback) {
|
|
1031
|
+
fs16.open(
|
|
1032
|
+
path20,
|
|
1033
1033
|
constants2.O_WRONLY | constants2.O_SYMLINK,
|
|
1034
1034
|
mode,
|
|
1035
1035
|
function(err, fd) {
|
|
@@ -1037,80 +1037,80 @@ var require_polyfills = __commonJS({
|
|
|
1037
1037
|
if (callback) callback(err);
|
|
1038
1038
|
return;
|
|
1039
1039
|
}
|
|
1040
|
-
|
|
1041
|
-
|
|
1040
|
+
fs16.fchmod(fd, mode, function(err2) {
|
|
1041
|
+
fs16.close(fd, function(err22) {
|
|
1042
1042
|
if (callback) callback(err2 || err22);
|
|
1043
1043
|
});
|
|
1044
1044
|
});
|
|
1045
1045
|
}
|
|
1046
1046
|
);
|
|
1047
1047
|
};
|
|
1048
|
-
|
|
1049
|
-
var fd =
|
|
1048
|
+
fs16.lchmodSync = function(path20, mode) {
|
|
1049
|
+
var fd = fs16.openSync(path20, constants2.O_WRONLY | constants2.O_SYMLINK, mode);
|
|
1050
1050
|
var threw = true;
|
|
1051
1051
|
var ret;
|
|
1052
1052
|
try {
|
|
1053
|
-
ret =
|
|
1053
|
+
ret = fs16.fchmodSync(fd, mode);
|
|
1054
1054
|
threw = false;
|
|
1055
1055
|
} finally {
|
|
1056
1056
|
if (threw) {
|
|
1057
1057
|
try {
|
|
1058
|
-
|
|
1058
|
+
fs16.closeSync(fd);
|
|
1059
1059
|
} catch (er) {
|
|
1060
1060
|
}
|
|
1061
1061
|
} else {
|
|
1062
|
-
|
|
1062
|
+
fs16.closeSync(fd);
|
|
1063
1063
|
}
|
|
1064
1064
|
}
|
|
1065
1065
|
return ret;
|
|
1066
1066
|
};
|
|
1067
1067
|
}
|
|
1068
|
-
function patchLutimes(
|
|
1069
|
-
if (constants2.hasOwnProperty("O_SYMLINK") &&
|
|
1070
|
-
|
|
1071
|
-
|
|
1068
|
+
function patchLutimes(fs16) {
|
|
1069
|
+
if (constants2.hasOwnProperty("O_SYMLINK") && fs16.futimes) {
|
|
1070
|
+
fs16.lutimes = function(path20, at, mt, cb) {
|
|
1071
|
+
fs16.open(path20, constants2.O_SYMLINK, function(er, fd) {
|
|
1072
1072
|
if (er) {
|
|
1073
1073
|
if (cb) cb(er);
|
|
1074
1074
|
return;
|
|
1075
1075
|
}
|
|
1076
|
-
|
|
1077
|
-
|
|
1076
|
+
fs16.futimes(fd, at, mt, function(er2) {
|
|
1077
|
+
fs16.close(fd, function(er22) {
|
|
1078
1078
|
if (cb) cb(er2 || er22);
|
|
1079
1079
|
});
|
|
1080
1080
|
});
|
|
1081
1081
|
});
|
|
1082
1082
|
};
|
|
1083
|
-
|
|
1084
|
-
var fd =
|
|
1083
|
+
fs16.lutimesSync = function(path20, at, mt) {
|
|
1084
|
+
var fd = fs16.openSync(path20, constants2.O_SYMLINK);
|
|
1085
1085
|
var ret;
|
|
1086
1086
|
var threw = true;
|
|
1087
1087
|
try {
|
|
1088
|
-
ret =
|
|
1088
|
+
ret = fs16.futimesSync(fd, at, mt);
|
|
1089
1089
|
threw = false;
|
|
1090
1090
|
} finally {
|
|
1091
1091
|
if (threw) {
|
|
1092
1092
|
try {
|
|
1093
|
-
|
|
1093
|
+
fs16.closeSync(fd);
|
|
1094
1094
|
} catch (er) {
|
|
1095
1095
|
}
|
|
1096
1096
|
} else {
|
|
1097
|
-
|
|
1097
|
+
fs16.closeSync(fd);
|
|
1098
1098
|
}
|
|
1099
1099
|
}
|
|
1100
1100
|
return ret;
|
|
1101
1101
|
};
|
|
1102
|
-
} else if (
|
|
1103
|
-
|
|
1102
|
+
} else if (fs16.futimes) {
|
|
1103
|
+
fs16.lutimes = function(_a3, _b, _c5, cb) {
|
|
1104
1104
|
if (cb) process.nextTick(cb);
|
|
1105
1105
|
};
|
|
1106
|
-
|
|
1106
|
+
fs16.lutimesSync = function() {
|
|
1107
1107
|
};
|
|
1108
1108
|
}
|
|
1109
1109
|
}
|
|
1110
1110
|
function chmodFix(orig) {
|
|
1111
1111
|
if (!orig) return orig;
|
|
1112
1112
|
return function(target, mode, cb) {
|
|
1113
|
-
return orig.call(
|
|
1113
|
+
return orig.call(fs15, target, mode, function(er) {
|
|
1114
1114
|
if (chownErOk(er)) er = null;
|
|
1115
1115
|
if (cb) cb.apply(this, arguments);
|
|
1116
1116
|
});
|
|
@@ -1120,7 +1120,7 @@ var require_polyfills = __commonJS({
|
|
|
1120
1120
|
if (!orig) return orig;
|
|
1121
1121
|
return function(target, mode) {
|
|
1122
1122
|
try {
|
|
1123
|
-
return orig.call(
|
|
1123
|
+
return orig.call(fs15, target, mode);
|
|
1124
1124
|
} catch (er) {
|
|
1125
1125
|
if (!chownErOk(er)) throw er;
|
|
1126
1126
|
}
|
|
@@ -1129,7 +1129,7 @@ var require_polyfills = __commonJS({
|
|
|
1129
1129
|
function chownFix(orig) {
|
|
1130
1130
|
if (!orig) return orig;
|
|
1131
1131
|
return function(target, uid, gid, cb) {
|
|
1132
|
-
return orig.call(
|
|
1132
|
+
return orig.call(fs15, target, uid, gid, function(er) {
|
|
1133
1133
|
if (chownErOk(er)) er = null;
|
|
1134
1134
|
if (cb) cb.apply(this, arguments);
|
|
1135
1135
|
});
|
|
@@ -1139,7 +1139,7 @@ var require_polyfills = __commonJS({
|
|
|
1139
1139
|
if (!orig) return orig;
|
|
1140
1140
|
return function(target, uid, gid) {
|
|
1141
1141
|
try {
|
|
1142
|
-
return orig.call(
|
|
1142
|
+
return orig.call(fs15, target, uid, gid);
|
|
1143
1143
|
} catch (er) {
|
|
1144
1144
|
if (!chownErOk(er)) throw er;
|
|
1145
1145
|
}
|
|
@@ -1159,13 +1159,13 @@ var require_polyfills = __commonJS({
|
|
|
1159
1159
|
}
|
|
1160
1160
|
if (cb) cb.apply(this, arguments);
|
|
1161
1161
|
}
|
|
1162
|
-
return options ? orig.call(
|
|
1162
|
+
return options ? orig.call(fs15, target, options, callback) : orig.call(fs15, target, callback);
|
|
1163
1163
|
};
|
|
1164
1164
|
}
|
|
1165
1165
|
function statFixSync(orig) {
|
|
1166
1166
|
if (!orig) return orig;
|
|
1167
1167
|
return function(target, options) {
|
|
1168
|
-
var stats = options ? orig.call(
|
|
1168
|
+
var stats = options ? orig.call(fs15, target, options) : orig.call(fs15, target);
|
|
1169
1169
|
if (stats) {
|
|
1170
1170
|
if (stats.uid < 0) stats.uid += 4294967296;
|
|
1171
1171
|
if (stats.gid < 0) stats.gid += 4294967296;
|
|
@@ -1194,16 +1194,16 @@ var require_legacy_streams = __commonJS({
|
|
|
1194
1194
|
"../../node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/legacy-streams.js"(exports, module) {
|
|
1195
1195
|
var Stream = __require("stream").Stream;
|
|
1196
1196
|
module.exports = legacy;
|
|
1197
|
-
function legacy(
|
|
1197
|
+
function legacy(fs15) {
|
|
1198
1198
|
return {
|
|
1199
1199
|
ReadStream,
|
|
1200
1200
|
WriteStream
|
|
1201
1201
|
};
|
|
1202
|
-
function ReadStream(
|
|
1203
|
-
if (!(this instanceof ReadStream)) return new ReadStream(
|
|
1202
|
+
function ReadStream(path20, options) {
|
|
1203
|
+
if (!(this instanceof ReadStream)) return new ReadStream(path20, options);
|
|
1204
1204
|
Stream.call(this);
|
|
1205
1205
|
var self2 = this;
|
|
1206
|
-
this.path =
|
|
1206
|
+
this.path = path20;
|
|
1207
1207
|
this.fd = null;
|
|
1208
1208
|
this.readable = true;
|
|
1209
1209
|
this.paused = false;
|
|
@@ -1237,7 +1237,7 @@ var require_legacy_streams = __commonJS({
|
|
|
1237
1237
|
});
|
|
1238
1238
|
return;
|
|
1239
1239
|
}
|
|
1240
|
-
|
|
1240
|
+
fs15.open(this.path, this.flags, this.mode, function(err, fd) {
|
|
1241
1241
|
if (err) {
|
|
1242
1242
|
self2.emit("error", err);
|
|
1243
1243
|
self2.readable = false;
|
|
@@ -1248,10 +1248,10 @@ var require_legacy_streams = __commonJS({
|
|
|
1248
1248
|
self2._read();
|
|
1249
1249
|
});
|
|
1250
1250
|
}
|
|
1251
|
-
function WriteStream(
|
|
1252
|
-
if (!(this instanceof WriteStream)) return new WriteStream(
|
|
1251
|
+
function WriteStream(path20, options) {
|
|
1252
|
+
if (!(this instanceof WriteStream)) return new WriteStream(path20, options);
|
|
1253
1253
|
Stream.call(this);
|
|
1254
|
-
this.path =
|
|
1254
|
+
this.path = path20;
|
|
1255
1255
|
this.fd = null;
|
|
1256
1256
|
this.writable = true;
|
|
1257
1257
|
this.flags = "w";
|
|
@@ -1276,7 +1276,7 @@ var require_legacy_streams = __commonJS({
|
|
|
1276
1276
|
this.busy = false;
|
|
1277
1277
|
this._queue = [];
|
|
1278
1278
|
if (this.fd === null) {
|
|
1279
|
-
this._open =
|
|
1279
|
+
this._open = fs15.open;
|
|
1280
1280
|
this._queue.push([this._open, this.path, this.flags, this.mode, void 0]);
|
|
1281
1281
|
this.flush();
|
|
1282
1282
|
}
|
|
@@ -1311,7 +1311,7 @@ var require_clone = __commonJS({
|
|
|
1311
1311
|
// ../../node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/graceful-fs.js
|
|
1312
1312
|
var require_graceful_fs = __commonJS({
|
|
1313
1313
|
"../../node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/graceful-fs.js"(exports, module) {
|
|
1314
|
-
var
|
|
1314
|
+
var fs15 = __require("fs");
|
|
1315
1315
|
var polyfills = require_polyfills();
|
|
1316
1316
|
var legacy = require_legacy_streams();
|
|
1317
1317
|
var clone = require_clone();
|
|
@@ -1343,12 +1343,12 @@ var require_graceful_fs = __commonJS({
|
|
|
1343
1343
|
m5 = "GFS4: " + m5.split(/\n/).join("\nGFS4: ");
|
|
1344
1344
|
console.error(m5);
|
|
1345
1345
|
};
|
|
1346
|
-
if (!
|
|
1346
|
+
if (!fs15[gracefulQueue]) {
|
|
1347
1347
|
queue = global[gracefulQueue] || [];
|
|
1348
|
-
publishQueue(
|
|
1349
|
-
|
|
1348
|
+
publishQueue(fs15, queue);
|
|
1349
|
+
fs15.close = (function(fs$close) {
|
|
1350
1350
|
function close(fd, cb) {
|
|
1351
|
-
return fs$close.call(
|
|
1351
|
+
return fs$close.call(fs15, fd, function(err) {
|
|
1352
1352
|
if (!err) {
|
|
1353
1353
|
resetQueue();
|
|
1354
1354
|
}
|
|
@@ -1360,48 +1360,48 @@ var require_graceful_fs = __commonJS({
|
|
|
1360
1360
|
value: fs$close
|
|
1361
1361
|
});
|
|
1362
1362
|
return close;
|
|
1363
|
-
})(
|
|
1364
|
-
|
|
1363
|
+
})(fs15.close);
|
|
1364
|
+
fs15.closeSync = (function(fs$closeSync) {
|
|
1365
1365
|
function closeSync(fd) {
|
|
1366
|
-
fs$closeSync.apply(
|
|
1366
|
+
fs$closeSync.apply(fs15, arguments);
|
|
1367
1367
|
resetQueue();
|
|
1368
1368
|
}
|
|
1369
1369
|
Object.defineProperty(closeSync, previousSymbol, {
|
|
1370
1370
|
value: fs$closeSync
|
|
1371
1371
|
});
|
|
1372
1372
|
return closeSync;
|
|
1373
|
-
})(
|
|
1373
|
+
})(fs15.closeSync);
|
|
1374
1374
|
if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) {
|
|
1375
1375
|
process.on("exit", function() {
|
|
1376
|
-
debug2(
|
|
1377
|
-
__require("assert").equal(
|
|
1376
|
+
debug2(fs15[gracefulQueue]);
|
|
1377
|
+
__require("assert").equal(fs15[gracefulQueue].length, 0);
|
|
1378
1378
|
});
|
|
1379
1379
|
}
|
|
1380
1380
|
}
|
|
1381
1381
|
var queue;
|
|
1382
1382
|
if (!global[gracefulQueue]) {
|
|
1383
|
-
publishQueue(global,
|
|
1384
|
-
}
|
|
1385
|
-
module.exports = patch(clone(
|
|
1386
|
-
if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !
|
|
1387
|
-
module.exports = patch(
|
|
1388
|
-
|
|
1389
|
-
}
|
|
1390
|
-
function patch(
|
|
1391
|
-
polyfills(
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
var fs$readFile =
|
|
1396
|
-
|
|
1397
|
-
function readFile(
|
|
1383
|
+
publishQueue(global, fs15[gracefulQueue]);
|
|
1384
|
+
}
|
|
1385
|
+
module.exports = patch(clone(fs15));
|
|
1386
|
+
if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs15.__patched) {
|
|
1387
|
+
module.exports = patch(fs15);
|
|
1388
|
+
fs15.__patched = true;
|
|
1389
|
+
}
|
|
1390
|
+
function patch(fs16) {
|
|
1391
|
+
polyfills(fs16);
|
|
1392
|
+
fs16.gracefulify = patch;
|
|
1393
|
+
fs16.createReadStream = createReadStream;
|
|
1394
|
+
fs16.createWriteStream = createWriteStream;
|
|
1395
|
+
var fs$readFile = fs16.readFile;
|
|
1396
|
+
fs16.readFile = readFile;
|
|
1397
|
+
function readFile(path20, options, cb) {
|
|
1398
1398
|
if (typeof options === "function")
|
|
1399
1399
|
cb = options, options = null;
|
|
1400
|
-
return go$readFile(
|
|
1401
|
-
function go$readFile(
|
|
1402
|
-
return fs$readFile(
|
|
1400
|
+
return go$readFile(path20, options, cb);
|
|
1401
|
+
function go$readFile(path21, options2, cb2, startTime) {
|
|
1402
|
+
return fs$readFile(path21, options2, function(err) {
|
|
1403
1403
|
if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
|
|
1404
|
-
enqueue([go$readFile, [
|
|
1404
|
+
enqueue([go$readFile, [path21, options2, cb2], err, startTime || Date.now(), Date.now()]);
|
|
1405
1405
|
else {
|
|
1406
1406
|
if (typeof cb2 === "function")
|
|
1407
1407
|
cb2.apply(this, arguments);
|
|
@@ -1409,16 +1409,16 @@ var require_graceful_fs = __commonJS({
|
|
|
1409
1409
|
});
|
|
1410
1410
|
}
|
|
1411
1411
|
}
|
|
1412
|
-
var fs$writeFile =
|
|
1413
|
-
|
|
1414
|
-
function writeFile(
|
|
1412
|
+
var fs$writeFile = fs16.writeFile;
|
|
1413
|
+
fs16.writeFile = writeFile;
|
|
1414
|
+
function writeFile(path20, data2, options, cb) {
|
|
1415
1415
|
if (typeof options === "function")
|
|
1416
1416
|
cb = options, options = null;
|
|
1417
|
-
return go$writeFile(
|
|
1418
|
-
function go$writeFile(
|
|
1419
|
-
return fs$writeFile(
|
|
1417
|
+
return go$writeFile(path20, data2, options, cb);
|
|
1418
|
+
function go$writeFile(path21, data3, options2, cb2, startTime) {
|
|
1419
|
+
return fs$writeFile(path21, data3, options2, function(err) {
|
|
1420
1420
|
if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
|
|
1421
|
-
enqueue([go$writeFile, [
|
|
1421
|
+
enqueue([go$writeFile, [path21, data3, options2, cb2], err, startTime || Date.now(), Date.now()]);
|
|
1422
1422
|
else {
|
|
1423
1423
|
if (typeof cb2 === "function")
|
|
1424
1424
|
cb2.apply(this, arguments);
|
|
@@ -1426,17 +1426,17 @@ var require_graceful_fs = __commonJS({
|
|
|
1426
1426
|
});
|
|
1427
1427
|
}
|
|
1428
1428
|
}
|
|
1429
|
-
var fs$appendFile =
|
|
1429
|
+
var fs$appendFile = fs16.appendFile;
|
|
1430
1430
|
if (fs$appendFile)
|
|
1431
|
-
|
|
1432
|
-
function appendFile(
|
|
1431
|
+
fs16.appendFile = appendFile;
|
|
1432
|
+
function appendFile(path20, data2, options, cb) {
|
|
1433
1433
|
if (typeof options === "function")
|
|
1434
1434
|
cb = options, options = null;
|
|
1435
|
-
return go$appendFile(
|
|
1436
|
-
function go$appendFile(
|
|
1437
|
-
return fs$appendFile(
|
|
1435
|
+
return go$appendFile(path20, data2, options, cb);
|
|
1436
|
+
function go$appendFile(path21, data3, options2, cb2, startTime) {
|
|
1437
|
+
return fs$appendFile(path21, data3, options2, function(err) {
|
|
1438
1438
|
if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
|
|
1439
|
-
enqueue([go$appendFile, [
|
|
1439
|
+
enqueue([go$appendFile, [path21, data3, options2, cb2], err, startTime || Date.now(), Date.now()]);
|
|
1440
1440
|
else {
|
|
1441
1441
|
if (typeof cb2 === "function")
|
|
1442
1442
|
cb2.apply(this, arguments);
|
|
@@ -1444,9 +1444,9 @@ var require_graceful_fs = __commonJS({
|
|
|
1444
1444
|
});
|
|
1445
1445
|
}
|
|
1446
1446
|
}
|
|
1447
|
-
var fs$copyFile =
|
|
1447
|
+
var fs$copyFile = fs16.copyFile;
|
|
1448
1448
|
if (fs$copyFile)
|
|
1449
|
-
|
|
1449
|
+
fs16.copyFile = copyFile;
|
|
1450
1450
|
function copyFile(src, dest, flags, cb) {
|
|
1451
1451
|
if (typeof flags === "function") {
|
|
1452
1452
|
cb = flags;
|
|
@@ -1464,34 +1464,34 @@ var require_graceful_fs = __commonJS({
|
|
|
1464
1464
|
});
|
|
1465
1465
|
}
|
|
1466
1466
|
}
|
|
1467
|
-
var fs$readdir =
|
|
1468
|
-
|
|
1467
|
+
var fs$readdir = fs16.readdir;
|
|
1468
|
+
fs16.readdir = readdir;
|
|
1469
1469
|
var noReaddirOptionVersions = /^v[0-5]\./;
|
|
1470
|
-
function readdir(
|
|
1470
|
+
function readdir(path20, options, cb) {
|
|
1471
1471
|
if (typeof options === "function")
|
|
1472
1472
|
cb = options, options = null;
|
|
1473
|
-
var go$readdir = noReaddirOptionVersions.test(process.version) ? function go$readdir2(
|
|
1474
|
-
return fs$readdir(
|
|
1475
|
-
|
|
1473
|
+
var go$readdir = noReaddirOptionVersions.test(process.version) ? function go$readdir2(path21, options2, cb2, startTime) {
|
|
1474
|
+
return fs$readdir(path21, fs$readdirCallback(
|
|
1475
|
+
path21,
|
|
1476
1476
|
options2,
|
|
1477
1477
|
cb2,
|
|
1478
1478
|
startTime
|
|
1479
1479
|
));
|
|
1480
|
-
} : function go$readdir2(
|
|
1481
|
-
return fs$readdir(
|
|
1482
|
-
|
|
1480
|
+
} : function go$readdir2(path21, options2, cb2, startTime) {
|
|
1481
|
+
return fs$readdir(path21, options2, fs$readdirCallback(
|
|
1482
|
+
path21,
|
|
1483
1483
|
options2,
|
|
1484
1484
|
cb2,
|
|
1485
1485
|
startTime
|
|
1486
1486
|
));
|
|
1487
1487
|
};
|
|
1488
|
-
return go$readdir(
|
|
1489
|
-
function fs$readdirCallback(
|
|
1488
|
+
return go$readdir(path20, options, cb);
|
|
1489
|
+
function fs$readdirCallback(path21, options2, cb2, startTime) {
|
|
1490
1490
|
return function(err, files) {
|
|
1491
1491
|
if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
|
|
1492
1492
|
enqueue([
|
|
1493
1493
|
go$readdir,
|
|
1494
|
-
[
|
|
1494
|
+
[path21, options2, cb2],
|
|
1495
1495
|
err,
|
|
1496
1496
|
startTime || Date.now(),
|
|
1497
1497
|
Date.now()
|
|
@@ -1506,21 +1506,21 @@ var require_graceful_fs = __commonJS({
|
|
|
1506
1506
|
}
|
|
1507
1507
|
}
|
|
1508
1508
|
if (process.version.substr(0, 4) === "v0.8") {
|
|
1509
|
-
var legStreams = legacy(
|
|
1509
|
+
var legStreams = legacy(fs16);
|
|
1510
1510
|
ReadStream = legStreams.ReadStream;
|
|
1511
1511
|
WriteStream = legStreams.WriteStream;
|
|
1512
1512
|
}
|
|
1513
|
-
var fs$ReadStream =
|
|
1513
|
+
var fs$ReadStream = fs16.ReadStream;
|
|
1514
1514
|
if (fs$ReadStream) {
|
|
1515
1515
|
ReadStream.prototype = Object.create(fs$ReadStream.prototype);
|
|
1516
1516
|
ReadStream.prototype.open = ReadStream$open;
|
|
1517
1517
|
}
|
|
1518
|
-
var fs$WriteStream =
|
|
1518
|
+
var fs$WriteStream = fs16.WriteStream;
|
|
1519
1519
|
if (fs$WriteStream) {
|
|
1520
1520
|
WriteStream.prototype = Object.create(fs$WriteStream.prototype);
|
|
1521
1521
|
WriteStream.prototype.open = WriteStream$open;
|
|
1522
1522
|
}
|
|
1523
|
-
Object.defineProperty(
|
|
1523
|
+
Object.defineProperty(fs16, "ReadStream", {
|
|
1524
1524
|
get: function() {
|
|
1525
1525
|
return ReadStream;
|
|
1526
1526
|
},
|
|
@@ -1530,7 +1530,7 @@ var require_graceful_fs = __commonJS({
|
|
|
1530
1530
|
enumerable: true,
|
|
1531
1531
|
configurable: true
|
|
1532
1532
|
});
|
|
1533
|
-
Object.defineProperty(
|
|
1533
|
+
Object.defineProperty(fs16, "WriteStream", {
|
|
1534
1534
|
get: function() {
|
|
1535
1535
|
return WriteStream;
|
|
1536
1536
|
},
|
|
@@ -1541,7 +1541,7 @@ var require_graceful_fs = __commonJS({
|
|
|
1541
1541
|
configurable: true
|
|
1542
1542
|
});
|
|
1543
1543
|
var FileReadStream = ReadStream;
|
|
1544
|
-
Object.defineProperty(
|
|
1544
|
+
Object.defineProperty(fs16, "FileReadStream", {
|
|
1545
1545
|
get: function() {
|
|
1546
1546
|
return FileReadStream;
|
|
1547
1547
|
},
|
|
@@ -1552,7 +1552,7 @@ var require_graceful_fs = __commonJS({
|
|
|
1552
1552
|
configurable: true
|
|
1553
1553
|
});
|
|
1554
1554
|
var FileWriteStream = WriteStream;
|
|
1555
|
-
Object.defineProperty(
|
|
1555
|
+
Object.defineProperty(fs16, "FileWriteStream", {
|
|
1556
1556
|
get: function() {
|
|
1557
1557
|
return FileWriteStream;
|
|
1558
1558
|
},
|
|
@@ -1562,7 +1562,7 @@ var require_graceful_fs = __commonJS({
|
|
|
1562
1562
|
enumerable: true,
|
|
1563
1563
|
configurable: true
|
|
1564
1564
|
});
|
|
1565
|
-
function ReadStream(
|
|
1565
|
+
function ReadStream(path20, options) {
|
|
1566
1566
|
if (this instanceof ReadStream)
|
|
1567
1567
|
return fs$ReadStream.apply(this, arguments), this;
|
|
1568
1568
|
else
|
|
@@ -1582,7 +1582,7 @@ var require_graceful_fs = __commonJS({
|
|
|
1582
1582
|
}
|
|
1583
1583
|
});
|
|
1584
1584
|
}
|
|
1585
|
-
function WriteStream(
|
|
1585
|
+
function WriteStream(path20, options) {
|
|
1586
1586
|
if (this instanceof WriteStream)
|
|
1587
1587
|
return fs$WriteStream.apply(this, arguments), this;
|
|
1588
1588
|
else
|
|
@@ -1600,22 +1600,22 @@ var require_graceful_fs = __commonJS({
|
|
|
1600
1600
|
}
|
|
1601
1601
|
});
|
|
1602
1602
|
}
|
|
1603
|
-
function createReadStream(
|
|
1604
|
-
return new
|
|
1603
|
+
function createReadStream(path20, options) {
|
|
1604
|
+
return new fs16.ReadStream(path20, options);
|
|
1605
1605
|
}
|
|
1606
|
-
function createWriteStream(
|
|
1607
|
-
return new
|
|
1606
|
+
function createWriteStream(path20, options) {
|
|
1607
|
+
return new fs16.WriteStream(path20, options);
|
|
1608
1608
|
}
|
|
1609
|
-
var fs$open =
|
|
1610
|
-
|
|
1611
|
-
function open(
|
|
1609
|
+
var fs$open = fs16.open;
|
|
1610
|
+
fs16.open = open;
|
|
1611
|
+
function open(path20, flags, mode, cb) {
|
|
1612
1612
|
if (typeof mode === "function")
|
|
1613
1613
|
cb = mode, mode = null;
|
|
1614
|
-
return go$open(
|
|
1615
|
-
function go$open(
|
|
1616
|
-
return fs$open(
|
|
1614
|
+
return go$open(path20, flags, mode, cb);
|
|
1615
|
+
function go$open(path21, flags2, mode2, cb2, startTime) {
|
|
1616
|
+
return fs$open(path21, flags2, mode2, function(err, fd) {
|
|
1617
1617
|
if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
|
|
1618
|
-
enqueue([go$open, [
|
|
1618
|
+
enqueue([go$open, [path21, flags2, mode2, cb2], err, startTime || Date.now(), Date.now()]);
|
|
1619
1619
|
else {
|
|
1620
1620
|
if (typeof cb2 === "function")
|
|
1621
1621
|
cb2.apply(this, arguments);
|
|
@@ -1623,20 +1623,20 @@ var require_graceful_fs = __commonJS({
|
|
|
1623
1623
|
});
|
|
1624
1624
|
}
|
|
1625
1625
|
}
|
|
1626
|
-
return
|
|
1626
|
+
return fs16;
|
|
1627
1627
|
}
|
|
1628
1628
|
function enqueue(elem) {
|
|
1629
1629
|
debug2("ENQUEUE", elem[0].name, elem[1]);
|
|
1630
|
-
|
|
1630
|
+
fs15[gracefulQueue].push(elem);
|
|
1631
1631
|
retry2();
|
|
1632
1632
|
}
|
|
1633
1633
|
var retryTimer;
|
|
1634
1634
|
function resetQueue() {
|
|
1635
1635
|
var now = Date.now();
|
|
1636
|
-
for (var i6 = 0; i6 <
|
|
1637
|
-
if (
|
|
1638
|
-
|
|
1639
|
-
|
|
1636
|
+
for (var i6 = 0; i6 < fs15[gracefulQueue].length; ++i6) {
|
|
1637
|
+
if (fs15[gracefulQueue][i6].length > 2) {
|
|
1638
|
+
fs15[gracefulQueue][i6][3] = now;
|
|
1639
|
+
fs15[gracefulQueue][i6][4] = now;
|
|
1640
1640
|
}
|
|
1641
1641
|
}
|
|
1642
1642
|
retry2();
|
|
@@ -1644,9 +1644,9 @@ var require_graceful_fs = __commonJS({
|
|
|
1644
1644
|
function retry2() {
|
|
1645
1645
|
clearTimeout(retryTimer);
|
|
1646
1646
|
retryTimer = void 0;
|
|
1647
|
-
if (
|
|
1647
|
+
if (fs15[gracefulQueue].length === 0)
|
|
1648
1648
|
return;
|
|
1649
|
-
var elem =
|
|
1649
|
+
var elem = fs15[gracefulQueue].shift();
|
|
1650
1650
|
var fn = elem[0];
|
|
1651
1651
|
var args = elem[1];
|
|
1652
1652
|
var err = elem[2];
|
|
@@ -1668,7 +1668,7 @@ var require_graceful_fs = __commonJS({
|
|
|
1668
1668
|
debug2("RETRY", fn.name, args);
|
|
1669
1669
|
fn.apply(null, args.concat([startTime]));
|
|
1670
1670
|
} else {
|
|
1671
|
-
|
|
1671
|
+
fs15[gracefulQueue].push(elem);
|
|
1672
1672
|
}
|
|
1673
1673
|
}
|
|
1674
1674
|
if (retryTimer === void 0) {
|
|
@@ -2103,10 +2103,10 @@ var require_mtime_precision = __commonJS({
|
|
|
2103
2103
|
"../../node_modules/.pnpm/proper-lockfile@4.1.2/node_modules/proper-lockfile/lib/mtime-precision.js"(exports, module) {
|
|
2104
2104
|
"use strict";
|
|
2105
2105
|
var cacheSymbol = Symbol();
|
|
2106
|
-
function probe(file,
|
|
2107
|
-
const cachedPrecision =
|
|
2106
|
+
function probe(file, fs15, callback) {
|
|
2107
|
+
const cachedPrecision = fs15[cacheSymbol];
|
|
2108
2108
|
if (cachedPrecision) {
|
|
2109
|
-
return
|
|
2109
|
+
return fs15.stat(file, (err, stat) => {
|
|
2110
2110
|
if (err) {
|
|
2111
2111
|
return callback(err);
|
|
2112
2112
|
}
|
|
@@ -2114,16 +2114,16 @@ var require_mtime_precision = __commonJS({
|
|
|
2114
2114
|
});
|
|
2115
2115
|
}
|
|
2116
2116
|
const mtime = new Date(Math.ceil(Date.now() / 1e3) * 1e3 + 5);
|
|
2117
|
-
|
|
2117
|
+
fs15.utimes(file, mtime, mtime, (err) => {
|
|
2118
2118
|
if (err) {
|
|
2119
2119
|
return callback(err);
|
|
2120
2120
|
}
|
|
2121
|
-
|
|
2121
|
+
fs15.stat(file, (err2, stat) => {
|
|
2122
2122
|
if (err2) {
|
|
2123
2123
|
return callback(err2);
|
|
2124
2124
|
}
|
|
2125
2125
|
const precision = stat.mtime.getTime() % 1e3 === 0 ? "s" : "ms";
|
|
2126
|
-
Object.defineProperty(
|
|
2126
|
+
Object.defineProperty(fs15, cacheSymbol, { value: precision });
|
|
2127
2127
|
callback(null, stat.mtime, precision);
|
|
2128
2128
|
});
|
|
2129
2129
|
});
|
|
@@ -2144,8 +2144,8 @@ var require_mtime_precision = __commonJS({
|
|
|
2144
2144
|
var require_lockfile = __commonJS({
|
|
2145
2145
|
"../../node_modules/.pnpm/proper-lockfile@4.1.2/node_modules/proper-lockfile/lib/lockfile.js"(exports, module) {
|
|
2146
2146
|
"use strict";
|
|
2147
|
-
var
|
|
2148
|
-
var
|
|
2147
|
+
var path20 = __require("path");
|
|
2148
|
+
var fs15 = require_graceful_fs();
|
|
2149
2149
|
var retry2 = require_retry2();
|
|
2150
2150
|
var onExit = require_signal_exit();
|
|
2151
2151
|
var mtimePrecision = require_mtime_precision();
|
|
@@ -2155,7 +2155,7 @@ var require_lockfile = __commonJS({
|
|
|
2155
2155
|
}
|
|
2156
2156
|
function resolveCanonicalPath(file, options, callback) {
|
|
2157
2157
|
if (!options.realpath) {
|
|
2158
|
-
return callback(null,
|
|
2158
|
+
return callback(null, path20.resolve(file));
|
|
2159
2159
|
}
|
|
2160
2160
|
options.fs.realpath(file, callback);
|
|
2161
2161
|
}
|
|
@@ -2276,7 +2276,7 @@ var require_lockfile = __commonJS({
|
|
|
2276
2276
|
update: null,
|
|
2277
2277
|
realpath: true,
|
|
2278
2278
|
retries: 0,
|
|
2279
|
-
fs:
|
|
2279
|
+
fs: fs15,
|
|
2280
2280
|
onCompromised: (err) => {
|
|
2281
2281
|
throw err;
|
|
2282
2282
|
},
|
|
@@ -2320,7 +2320,7 @@ var require_lockfile = __commonJS({
|
|
|
2320
2320
|
}
|
|
2321
2321
|
function unlock(file, options, callback) {
|
|
2322
2322
|
options = {
|
|
2323
|
-
fs:
|
|
2323
|
+
fs: fs15,
|
|
2324
2324
|
realpath: true,
|
|
2325
2325
|
...options
|
|
2326
2326
|
};
|
|
@@ -2342,7 +2342,7 @@ var require_lockfile = __commonJS({
|
|
|
2342
2342
|
options = {
|
|
2343
2343
|
stale: 1e4,
|
|
2344
2344
|
realpath: true,
|
|
2345
|
-
fs:
|
|
2345
|
+
fs: fs15,
|
|
2346
2346
|
...options
|
|
2347
2347
|
};
|
|
2348
2348
|
options.stale = Math.max(options.stale || 0, 2e3);
|
|
@@ -2381,16 +2381,16 @@ var require_lockfile = __commonJS({
|
|
|
2381
2381
|
var require_adapter = __commonJS({
|
|
2382
2382
|
"../../node_modules/.pnpm/proper-lockfile@4.1.2/node_modules/proper-lockfile/lib/adapter.js"(exports, module) {
|
|
2383
2383
|
"use strict";
|
|
2384
|
-
var
|
|
2385
|
-
function createSyncFs(
|
|
2384
|
+
var fs15 = require_graceful_fs();
|
|
2385
|
+
function createSyncFs(fs16) {
|
|
2386
2386
|
const methods = ["mkdir", "realpath", "stat", "rmdir", "utimes"];
|
|
2387
|
-
const newFs = { ...
|
|
2387
|
+
const newFs = { ...fs16 };
|
|
2388
2388
|
methods.forEach((method) => {
|
|
2389
2389
|
newFs[method] = (...args) => {
|
|
2390
2390
|
const callback = args.pop();
|
|
2391
2391
|
let ret;
|
|
2392
2392
|
try {
|
|
2393
|
-
ret =
|
|
2393
|
+
ret = fs16[`${method}Sync`](...args);
|
|
2394
2394
|
} catch (err) {
|
|
2395
2395
|
return callback(err);
|
|
2396
2396
|
}
|
|
@@ -2428,7 +2428,7 @@ var require_adapter = __commonJS({
|
|
|
2428
2428
|
}
|
|
2429
2429
|
function toSyncOptions(options) {
|
|
2430
2430
|
options = { ...options };
|
|
2431
|
-
options.fs = createSyncFs(options.fs ||
|
|
2431
|
+
options.fs = createSyncFs(options.fs || fs15);
|
|
2432
2432
|
if (typeof options.retries === "number" && options.retries > 0 || options.retries && typeof options.retries.retries === "number" && options.retries.retries > 0) {
|
|
2433
2433
|
throw Object.assign(new Error("Cannot use retries with the sync api"), { code: "ESYNC" });
|
|
2434
2434
|
}
|
|
@@ -3785,15 +3785,15 @@ var require_dist_node9 = __commonJS({
|
|
|
3785
3785
|
octokit.log.debug("request", options);
|
|
3786
3786
|
const start = Date.now();
|
|
3787
3787
|
const requestOptions = octokit.request.endpoint.parse(options);
|
|
3788
|
-
const
|
|
3788
|
+
const path20 = requestOptions.url.replace(options.baseUrl, "");
|
|
3789
3789
|
return request(options).then((response) => {
|
|
3790
3790
|
octokit.log.info(
|
|
3791
|
-
`${requestOptions.method} ${
|
|
3791
|
+
`${requestOptions.method} ${path20} - ${response.status} in ${Date.now() - start}ms`
|
|
3792
3792
|
);
|
|
3793
3793
|
return response;
|
|
3794
3794
|
}).catch((error2) => {
|
|
3795
3795
|
octokit.log.info(
|
|
3796
|
-
`${requestOptions.method} ${
|
|
3796
|
+
`${requestOptions.method} ${path20} - ${error2.status} in ${Date.now() - start}ms`
|
|
3797
3797
|
);
|
|
3798
3798
|
throw error2;
|
|
3799
3799
|
});
|
|
@@ -7887,7 +7887,7 @@ var require_dist_node13 = __commonJS({
|
|
|
7887
7887
|
];
|
|
7888
7888
|
function routeMatcher(paths) {
|
|
7889
7889
|
const regexes = paths.map(
|
|
7890
|
-
(
|
|
7890
|
+
(path20) => path20.split("/").map((c7) => c7.startsWith("{") ? "(?:.+?)" : c7).join("/")
|
|
7891
7891
|
);
|
|
7892
7892
|
const regex2 = `^(?:${regexes.map((r6) => `(?:${r6})`).join("|")})[^/]*$`;
|
|
7893
7893
|
return new RegExp(regex2, "i");
|
|
@@ -8517,8 +8517,8 @@ var init_parseUtil = __esm({
|
|
|
8517
8517
|
init_errors();
|
|
8518
8518
|
init_en();
|
|
8519
8519
|
makeIssue = (params) => {
|
|
8520
|
-
const { data: data2, path:
|
|
8521
|
-
const fullPath = [...
|
|
8520
|
+
const { data: data2, path: path20, errorMaps, issueData } = params;
|
|
8521
|
+
const fullPath = [...path20, ...issueData.path || []];
|
|
8522
8522
|
const fullIssue = {
|
|
8523
8523
|
...issueData,
|
|
8524
8524
|
path: fullPath
|
|
@@ -8826,11 +8826,11 @@ var init_types2 = __esm({
|
|
|
8826
8826
|
init_parseUtil();
|
|
8827
8827
|
init_util2();
|
|
8828
8828
|
ParseInputLazyPath = class {
|
|
8829
|
-
constructor(parent, value,
|
|
8829
|
+
constructor(parent, value, path20, key) {
|
|
8830
8830
|
this._cachedPath = [];
|
|
8831
8831
|
this.parent = parent;
|
|
8832
8832
|
this.data = value;
|
|
8833
|
-
this._path =
|
|
8833
|
+
this._path = path20;
|
|
8834
8834
|
this._key = key;
|
|
8835
8835
|
}
|
|
8836
8836
|
get path() {
|
|
@@ -12517,8 +12517,8 @@ var init_permissions = __esm({
|
|
|
12517
12517
|
});
|
|
12518
12518
|
|
|
12519
12519
|
// ../canopycms/src/paths/normalize.ts
|
|
12520
|
-
function normalizeFilesystemPath(
|
|
12521
|
-
return
|
|
12520
|
+
function normalizeFilesystemPath(path20) {
|
|
12521
|
+
return path20.split(/[\\/]+/).filter(Boolean).join("/");
|
|
12522
12522
|
}
|
|
12523
12523
|
var init_normalize = __esm({
|
|
12524
12524
|
"../canopycms/src/paths/normalize.ts"() {
|
|
@@ -13668,9 +13668,9 @@ var init_createPaginator = __esm({
|
|
|
13668
13668
|
command = withCommand(command) ?? command;
|
|
13669
13669
|
return await client.send(command, ...args);
|
|
13670
13670
|
};
|
|
13671
|
-
get = (fromObject,
|
|
13671
|
+
get = (fromObject, path20) => {
|
|
13672
13672
|
let cursor2 = fromObject;
|
|
13673
|
-
const pathComponents =
|
|
13673
|
+
const pathComponents = path20.split(".");
|
|
13674
13674
|
for (const step of pathComponents) {
|
|
13675
13675
|
if (!cursor2 || typeof cursor2 !== "object") {
|
|
13676
13676
|
return void 0;
|
|
@@ -14688,12 +14688,12 @@ or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler conf
|
|
|
14688
14688
|
const password = request.password ?? "";
|
|
14689
14689
|
auth = `${username}:${password}`;
|
|
14690
14690
|
}
|
|
14691
|
-
let
|
|
14691
|
+
let path20 = request.path;
|
|
14692
14692
|
if (queryString) {
|
|
14693
|
-
|
|
14693
|
+
path20 += `?${queryString}`;
|
|
14694
14694
|
}
|
|
14695
14695
|
if (request.fragment) {
|
|
14696
|
-
|
|
14696
|
+
path20 += `#${request.fragment}`;
|
|
14697
14697
|
}
|
|
14698
14698
|
let hostname = request.hostname ?? "";
|
|
14699
14699
|
if (hostname[0] === "[" && hostname.endsWith("]")) {
|
|
@@ -14705,7 +14705,7 @@ or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler conf
|
|
|
14705
14705
|
headers: request.headers,
|
|
14706
14706
|
host: hostname,
|
|
14707
14707
|
method: request.method,
|
|
14708
|
-
path:
|
|
14708
|
+
path: path20,
|
|
14709
14709
|
port: request.port,
|
|
14710
14710
|
agent,
|
|
14711
14711
|
auth
|
|
@@ -14988,16 +14988,16 @@ or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler conf
|
|
|
14988
14988
|
reject(err);
|
|
14989
14989
|
};
|
|
14990
14990
|
const queryString = querystringBuilder.buildQueryString(query || {});
|
|
14991
|
-
let
|
|
14991
|
+
let path20 = request.path;
|
|
14992
14992
|
if (queryString) {
|
|
14993
|
-
|
|
14993
|
+
path20 += `?${queryString}`;
|
|
14994
14994
|
}
|
|
14995
14995
|
if (request.fragment) {
|
|
14996
|
-
|
|
14996
|
+
path20 += `#${request.fragment}`;
|
|
14997
14997
|
}
|
|
14998
14998
|
const req = session.request({
|
|
14999
14999
|
...request.headers,
|
|
15000
|
-
[http2.constants.HTTP2_HEADER_PATH]:
|
|
15000
|
+
[http2.constants.HTTP2_HEADER_PATH]: path20,
|
|
15001
15001
|
[http2.constants.HTTP2_HEADER_METHOD]: method
|
|
15002
15002
|
});
|
|
15003
15003
|
session.ref();
|
|
@@ -15184,13 +15184,13 @@ var require_dist_cjs14 = __commonJS({
|
|
|
15184
15184
|
const abortError = buildAbortError(abortSignal);
|
|
15185
15185
|
return Promise.reject(abortError);
|
|
15186
15186
|
}
|
|
15187
|
-
let
|
|
15187
|
+
let path20 = request.path;
|
|
15188
15188
|
const queryString = querystringBuilder.buildQueryString(request.query || {});
|
|
15189
15189
|
if (queryString) {
|
|
15190
|
-
|
|
15190
|
+
path20 += `?${queryString}`;
|
|
15191
15191
|
}
|
|
15192
15192
|
if (request.fragment) {
|
|
15193
|
-
|
|
15193
|
+
path20 += `#${request.fragment}`;
|
|
15194
15194
|
}
|
|
15195
15195
|
let auth = "";
|
|
15196
15196
|
if (request.username != null || request.password != null) {
|
|
@@ -15199,7 +15199,7 @@ var require_dist_cjs14 = __commonJS({
|
|
|
15199
15199
|
auth = `${username}:${password}@`;
|
|
15200
15200
|
}
|
|
15201
15201
|
const { port, method } = request;
|
|
15202
|
-
const url = `${request.protocol}//${auth}${request.hostname}${port ? `:${port}` : ""}${
|
|
15202
|
+
const url = `${request.protocol}//${auth}${request.hostname}${port ? `:${port}` : ""}${path20}`;
|
|
15203
15203
|
const body = method === "GET" || method === "HEAD" ? void 0 : request.body;
|
|
15204
15204
|
const requestOptions = {
|
|
15205
15205
|
body,
|
|
@@ -17431,13 +17431,13 @@ function __disposeResources(env) {
|
|
|
17431
17431
|
}
|
|
17432
17432
|
return next();
|
|
17433
17433
|
}
|
|
17434
|
-
function __rewriteRelativeImportExtension(
|
|
17435
|
-
if (typeof
|
|
17436
|
-
return
|
|
17434
|
+
function __rewriteRelativeImportExtension(path20, preserveJsx) {
|
|
17435
|
+
if (typeof path20 === "string" && /^\.\.?\//.test(path20)) {
|
|
17436
|
+
return path20.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m5, tsx, d5, ext, cm) {
|
|
17437
17437
|
return tsx ? preserveJsx ? ".jsx" : ".js" : d5 && (!ext || !cm) ? m5 : d5 + ext + "." + cm.toLowerCase() + "js";
|
|
17438
17438
|
});
|
|
17439
17439
|
}
|
|
17440
|
-
return
|
|
17440
|
+
return path20;
|
|
17441
17441
|
}
|
|
17442
17442
|
var extendStatics, __assign, __createBinding, __setModuleDefault, ownKeys, _SuppressedError, tslib_es6_default;
|
|
17443
17443
|
var init_tslib_es6 = __esm({
|
|
@@ -18335,11 +18335,11 @@ var init_HttpBindingProtocol = __esm({
|
|
|
18335
18335
|
const opTraits = translateTraits(operationSchema.traits);
|
|
18336
18336
|
if (opTraits.http) {
|
|
18337
18337
|
request.method = opTraits.http[0];
|
|
18338
|
-
const [
|
|
18338
|
+
const [path20, search] = opTraits.http[1].split("?");
|
|
18339
18339
|
if (request.path == "/") {
|
|
18340
|
-
request.path =
|
|
18340
|
+
request.path = path20;
|
|
18341
18341
|
} else {
|
|
18342
|
-
request.path +=
|
|
18342
|
+
request.path += path20;
|
|
18343
18343
|
}
|
|
18344
18344
|
const traitSearchParams = new URLSearchParams(search ?? "");
|
|
18345
18345
|
Object.assign(query, Object.fromEntries(traitSearchParams));
|
|
@@ -18742,8 +18742,8 @@ var init_requestBuilder = __esm({
|
|
|
18742
18742
|
return this;
|
|
18743
18743
|
}
|
|
18744
18744
|
p(memberName, labelValueProvider, uriLabel, isGreedyLabel) {
|
|
18745
|
-
this.resolvePathStack.push((
|
|
18746
|
-
this.path = resolvedPath(
|
|
18745
|
+
this.resolvePathStack.push((path20) => {
|
|
18746
|
+
this.path = resolvedPath(path20, this.input, memberName, labelValueProvider, uriLabel, isGreedyLabel);
|
|
18747
18747
|
});
|
|
18748
18748
|
return this;
|
|
18749
18749
|
}
|
|
@@ -19393,18 +19393,18 @@ var require_dist_cjs20 = __commonJS({
|
|
|
19393
19393
|
}
|
|
19394
19394
|
};
|
|
19395
19395
|
var booleanEquals = (value1, value2) => value1 === value2;
|
|
19396
|
-
var getAttrPathList = (
|
|
19397
|
-
const parts =
|
|
19396
|
+
var getAttrPathList = (path20) => {
|
|
19397
|
+
const parts = path20.split(".");
|
|
19398
19398
|
const pathList = [];
|
|
19399
19399
|
for (const part of parts) {
|
|
19400
19400
|
const squareBracketIndex = part.indexOf("[");
|
|
19401
19401
|
if (squareBracketIndex !== -1) {
|
|
19402
19402
|
if (part.indexOf("]") !== part.length - 1) {
|
|
19403
|
-
throw new EndpointError(`Path: '${
|
|
19403
|
+
throw new EndpointError(`Path: '${path20}' does not end with ']'`);
|
|
19404
19404
|
}
|
|
19405
19405
|
const arrayIndex = part.slice(squareBracketIndex + 1, -1);
|
|
19406
19406
|
if (Number.isNaN(parseInt(arrayIndex))) {
|
|
19407
|
-
throw new EndpointError(`Invalid array index: '${arrayIndex}' in path: '${
|
|
19407
|
+
throw new EndpointError(`Invalid array index: '${arrayIndex}' in path: '${path20}'`);
|
|
19408
19408
|
}
|
|
19409
19409
|
if (squareBracketIndex !== 0) {
|
|
19410
19410
|
pathList.push(part.slice(0, squareBracketIndex));
|
|
@@ -19416,9 +19416,9 @@ var require_dist_cjs20 = __commonJS({
|
|
|
19416
19416
|
}
|
|
19417
19417
|
return pathList;
|
|
19418
19418
|
};
|
|
19419
|
-
var getAttr = (value,
|
|
19419
|
+
var getAttr = (value, path20) => getAttrPathList(path20).reduce((acc, index) => {
|
|
19420
19420
|
if (typeof acc !== "object") {
|
|
19421
|
-
throw new EndpointError(`Index '${index}' in '${
|
|
19421
|
+
throw new EndpointError(`Index '${index}' in '${path20}' not found in '${JSON.stringify(value)}'`);
|
|
19422
19422
|
} else if (Array.isArray(acc)) {
|
|
19423
19423
|
return acc[parseInt(index)];
|
|
19424
19424
|
}
|
|
@@ -19437,8 +19437,8 @@ var require_dist_cjs20 = __commonJS({
|
|
|
19437
19437
|
return value;
|
|
19438
19438
|
}
|
|
19439
19439
|
if (typeof value === "object" && "hostname" in value) {
|
|
19440
|
-
const { hostname: hostname2, port, protocol: protocol2 = "", path:
|
|
19441
|
-
const url = new URL(`${protocol2}//${hostname2}${port ? `:${port}` : ""}${
|
|
19440
|
+
const { hostname: hostname2, port, protocol: protocol2 = "", path: path20 = "", query = {} } = value;
|
|
19441
|
+
const url = new URL(`${protocol2}//${hostname2}${port ? `:${port}` : ""}${path20}`);
|
|
19442
19442
|
url.search = Object.entries(query).map(([k6, v5]) => `${k6}=${v5}`).join("&");
|
|
19443
19443
|
return url;
|
|
19444
19444
|
}
|
|
@@ -20883,10 +20883,10 @@ ${longDate}
|
|
|
20883
20883
|
${credentialScope}
|
|
20884
20884
|
${utilHexEncoding.toHex(hashedRequest)}`;
|
|
20885
20885
|
}
|
|
20886
|
-
getCanonicalPath({ path:
|
|
20886
|
+
getCanonicalPath({ path: path20 }) {
|
|
20887
20887
|
if (this.uriEscapePath) {
|
|
20888
20888
|
const normalizedPathSegments = [];
|
|
20889
|
-
for (const pathSegment of
|
|
20889
|
+
for (const pathSegment of path20.split("/")) {
|
|
20890
20890
|
if (pathSegment?.length === 0)
|
|
20891
20891
|
continue;
|
|
20892
20892
|
if (pathSegment === ".")
|
|
@@ -20897,11 +20897,11 @@ ${utilHexEncoding.toHex(hashedRequest)}`;
|
|
|
20897
20897
|
normalizedPathSegments.push(pathSegment);
|
|
20898
20898
|
}
|
|
20899
20899
|
}
|
|
20900
|
-
const normalizedPath = `${
|
|
20900
|
+
const normalizedPath = `${path20?.startsWith("/") ? "/" : ""}${normalizedPathSegments.join("/")}${normalizedPathSegments.length > 0 && path20?.endsWith("/") ? "/" : ""}`;
|
|
20901
20901
|
const doubleEncoded = utilUriEscape.escapeUri(normalizedPath);
|
|
20902
20902
|
return doubleEncoded.replace(/%2F/g, "/");
|
|
20903
20903
|
}
|
|
20904
|
-
return
|
|
20904
|
+
return path20;
|
|
20905
20905
|
}
|
|
20906
20906
|
validateResolvedCredentials(credentials) {
|
|
20907
20907
|
if (typeof credentials !== "object" || typeof credentials.accessKeyId !== "string" || typeof credentials.secretAccessKey !== "string") {
|
|
@@ -22205,11 +22205,11 @@ var init_SmithyRpcV2CborProtocol = __esm({
|
|
|
22205
22205
|
}
|
|
22206
22206
|
}
|
|
22207
22207
|
const { service, operation: operation2 } = (0, import_util_middleware5.getSmithyContext)(context);
|
|
22208
|
-
const
|
|
22208
|
+
const path20 = `/service/${service}/operation/${operation2}`;
|
|
22209
22209
|
if (request.path.endsWith("/")) {
|
|
22210
|
-
request.path +=
|
|
22210
|
+
request.path += path20.slice(1);
|
|
22211
22211
|
} else {
|
|
22212
|
-
request.path +=
|
|
22212
|
+
request.path += path20;
|
|
22213
22213
|
}
|
|
22214
22214
|
return request;
|
|
22215
22215
|
}
|
|
@@ -27854,14 +27854,14 @@ var require_readFile = __commonJS({
|
|
|
27854
27854
|
var promises_1 = __require("node:fs/promises");
|
|
27855
27855
|
exports.filePromises = {};
|
|
27856
27856
|
exports.fileIntercept = {};
|
|
27857
|
-
var readFile = (
|
|
27858
|
-
if (exports.fileIntercept[
|
|
27859
|
-
return exports.fileIntercept[
|
|
27857
|
+
var readFile = (path20, options) => {
|
|
27858
|
+
if (exports.fileIntercept[path20] !== void 0) {
|
|
27859
|
+
return exports.fileIntercept[path20];
|
|
27860
27860
|
}
|
|
27861
|
-
if (!exports.filePromises[
|
|
27862
|
-
exports.filePromises[
|
|
27861
|
+
if (!exports.filePromises[path20] || options?.ignoreCache) {
|
|
27862
|
+
exports.filePromises[path20] = (0, promises_1.readFile)(path20, "utf8");
|
|
27863
27863
|
}
|
|
27864
|
-
return exports.filePromises[
|
|
27864
|
+
return exports.filePromises[path20];
|
|
27865
27865
|
};
|
|
27866
27866
|
exports.readFile = readFile;
|
|
27867
27867
|
}
|
|
@@ -27874,7 +27874,7 @@ var require_dist_cjs33 = __commonJS({
|
|
|
27874
27874
|
var getHomeDir = require_getHomeDir();
|
|
27875
27875
|
var getSSOTokenFilepath = require_getSSOTokenFilepath();
|
|
27876
27876
|
var getSSOTokenFromFile = require_getSSOTokenFromFile();
|
|
27877
|
-
var
|
|
27877
|
+
var path20 = __require("path");
|
|
27878
27878
|
var types = require_dist_cjs();
|
|
27879
27879
|
var readFile = require_readFile();
|
|
27880
27880
|
var ENV_PROFILE = "AWS_PROFILE";
|
|
@@ -27896,9 +27896,9 @@ var require_dist_cjs33 = __commonJS({
|
|
|
27896
27896
|
...data2.default && { default: data2.default }
|
|
27897
27897
|
});
|
|
27898
27898
|
var ENV_CONFIG_PATH = "AWS_CONFIG_FILE";
|
|
27899
|
-
var getConfigFilepath = () => process.env[ENV_CONFIG_PATH] ||
|
|
27899
|
+
var getConfigFilepath = () => process.env[ENV_CONFIG_PATH] || path20.join(getHomeDir.getHomeDir(), ".aws", "config");
|
|
27900
27900
|
var ENV_CREDENTIALS_PATH = "AWS_SHARED_CREDENTIALS_FILE";
|
|
27901
|
-
var getCredentialsFilepath = () => process.env[ENV_CREDENTIALS_PATH] ||
|
|
27901
|
+
var getCredentialsFilepath = () => process.env[ENV_CREDENTIALS_PATH] || path20.join(getHomeDir.getHomeDir(), ".aws", "credentials");
|
|
27902
27902
|
var prefixKeyRegex = /^([\w-]+)\s(["'])?([\w-@\+\.%:/]+)\2$/;
|
|
27903
27903
|
var profileNameBlockList = ["__proto__", "profile __proto__"];
|
|
27904
27904
|
var parseIni = (iniData) => {
|
|
@@ -27953,11 +27953,11 @@ var require_dist_cjs33 = __commonJS({
|
|
|
27953
27953
|
const relativeHomeDirPrefix = "~/";
|
|
27954
27954
|
let resolvedFilepath = filepath;
|
|
27955
27955
|
if (filepath.startsWith(relativeHomeDirPrefix)) {
|
|
27956
|
-
resolvedFilepath =
|
|
27956
|
+
resolvedFilepath = path20.join(homeDir, filepath.slice(2));
|
|
27957
27957
|
}
|
|
27958
27958
|
let resolvedConfigFilepath = configFilepath;
|
|
27959
27959
|
if (configFilepath.startsWith(relativeHomeDirPrefix)) {
|
|
27960
|
-
resolvedConfigFilepath =
|
|
27960
|
+
resolvedConfigFilepath = path20.join(homeDir, configFilepath.slice(2));
|
|
27961
27961
|
}
|
|
27962
27962
|
const parsedFiles = await Promise.all([
|
|
27963
27963
|
readFile.readFile(resolvedConfigFilepath, {
|
|
@@ -27996,8 +27996,8 @@ var require_dist_cjs33 = __commonJS({
|
|
|
27996
27996
|
getFileRecord() {
|
|
27997
27997
|
return readFile.fileIntercept;
|
|
27998
27998
|
},
|
|
27999
|
-
interceptFile(
|
|
28000
|
-
readFile.fileIntercept[
|
|
27999
|
+
interceptFile(path21, contents) {
|
|
28000
|
+
readFile.fileIntercept[path21] = Promise.resolve(contents);
|
|
28001
28001
|
},
|
|
28002
28002
|
getTokenRecord() {
|
|
28003
28003
|
return getSSOTokenFromFile.tokenIntercept;
|
|
@@ -28322,8 +28322,8 @@ var require_dist_cjs36 = __commonJS({
|
|
|
28322
28322
|
return endpoint.url.href;
|
|
28323
28323
|
}
|
|
28324
28324
|
if ("hostname" in endpoint) {
|
|
28325
|
-
const { protocol, hostname, port, path:
|
|
28326
|
-
return `${protocol}//${hostname}${port ? ":" + port : ""}${
|
|
28325
|
+
const { protocol, hostname, port, path: path20 } = endpoint;
|
|
28326
|
+
return `${protocol}//${hostname}${port ? ":" + port : ""}${path20}`;
|
|
28327
28327
|
}
|
|
28328
28328
|
}
|
|
28329
28329
|
return endpoint;
|
|
@@ -36520,8 +36520,8 @@ var require_dist_cjs53 = __commonJS({
|
|
|
36520
36520
|
});
|
|
36521
36521
|
|
|
36522
36522
|
// ../canopycms/src/worker/cms-worker.ts
|
|
36523
|
-
import
|
|
36524
|
-
import
|
|
36523
|
+
import fs13 from "node:fs/promises";
|
|
36524
|
+
import path17 from "node:path";
|
|
36525
36525
|
|
|
36526
36526
|
// ../../node_modules/.pnpm/simple-git@3.36.0/node_modules/simple-git/dist/esm/index.js
|
|
36527
36527
|
var import_file_exists = __toESM(require_dist(), 1);
|
|
@@ -37112,8 +37112,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
37112
37112
|
function forEachLineWithContent(input, callback) {
|
|
37113
37113
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
37114
37114
|
}
|
|
37115
|
-
function folderExists(
|
|
37116
|
-
return (0, import_file_exists.exists)(
|
|
37115
|
+
function folderExists(path20) {
|
|
37116
|
+
return (0, import_file_exists.exists)(path20, import_file_exists.FOLDER);
|
|
37117
37117
|
}
|
|
37118
37118
|
function append(target, item) {
|
|
37119
37119
|
if (Array.isArray(target)) {
|
|
@@ -37515,8 +37515,8 @@ function checkIsRepoRootTask() {
|
|
|
37515
37515
|
commands: commands5,
|
|
37516
37516
|
format: "utf-8",
|
|
37517
37517
|
onError,
|
|
37518
|
-
parser(
|
|
37519
|
-
return /^\.(git)?$/.test(
|
|
37518
|
+
parser(path20) {
|
|
37519
|
+
return /^\.(git)?$/.test(path20.trim());
|
|
37520
37520
|
}
|
|
37521
37521
|
};
|
|
37522
37522
|
}
|
|
@@ -37950,11 +37950,11 @@ function parseGrep(grep) {
|
|
|
37950
37950
|
const paths = /* @__PURE__ */ new Set();
|
|
37951
37951
|
const results = {};
|
|
37952
37952
|
forEachLineWithContent(grep, (input) => {
|
|
37953
|
-
const [
|
|
37954
|
-
paths.add(
|
|
37955
|
-
(results[
|
|
37953
|
+
const [path20, line, preview] = input.split(NULL);
|
|
37954
|
+
paths.add(path20);
|
|
37955
|
+
(results[path20] = results[path20] || []).push({
|
|
37956
37956
|
line: asNumber(line),
|
|
37957
|
-
path:
|
|
37957
|
+
path: path20,
|
|
37958
37958
|
preview
|
|
37959
37959
|
});
|
|
37960
37960
|
});
|
|
@@ -38718,14 +38718,14 @@ var init_hash_object = __esm2({
|
|
|
38718
38718
|
init_task();
|
|
38719
38719
|
}
|
|
38720
38720
|
});
|
|
38721
|
-
function parseInit(bare,
|
|
38721
|
+
function parseInit(bare, path20, text) {
|
|
38722
38722
|
const response = String(text).trim();
|
|
38723
38723
|
let result;
|
|
38724
38724
|
if (result = initResponseRegex.exec(response)) {
|
|
38725
|
-
return new InitSummary(bare,
|
|
38725
|
+
return new InitSummary(bare, path20, false, result[1]);
|
|
38726
38726
|
}
|
|
38727
38727
|
if (result = reInitResponseRegex.exec(response)) {
|
|
38728
|
-
return new InitSummary(bare,
|
|
38728
|
+
return new InitSummary(bare, path20, true, result[1]);
|
|
38729
38729
|
}
|
|
38730
38730
|
let gitDir = "";
|
|
38731
38731
|
const tokens = response.split(" ");
|
|
@@ -38736,7 +38736,7 @@ function parseInit(bare, path16, text) {
|
|
|
38736
38736
|
break;
|
|
38737
38737
|
}
|
|
38738
38738
|
}
|
|
38739
|
-
return new InitSummary(bare,
|
|
38739
|
+
return new InitSummary(bare, path20, /^re/i.test(response), gitDir);
|
|
38740
38740
|
}
|
|
38741
38741
|
var InitSummary;
|
|
38742
38742
|
var initResponseRegex;
|
|
@@ -38745,9 +38745,9 @@ var init_InitSummary = __esm2({
|
|
|
38745
38745
|
"src/lib/responses/InitSummary.ts"() {
|
|
38746
38746
|
"use strict";
|
|
38747
38747
|
InitSummary = class {
|
|
38748
|
-
constructor(bare,
|
|
38748
|
+
constructor(bare, path20, existing, gitDir) {
|
|
38749
38749
|
this.bare = bare;
|
|
38750
|
-
this.path =
|
|
38750
|
+
this.path = path20;
|
|
38751
38751
|
this.existing = existing;
|
|
38752
38752
|
this.gitDir = gitDir;
|
|
38753
38753
|
}
|
|
@@ -38759,7 +38759,7 @@ var init_InitSummary = __esm2({
|
|
|
38759
38759
|
function hasBareCommand(command) {
|
|
38760
38760
|
return command.includes(bareCommand);
|
|
38761
38761
|
}
|
|
38762
|
-
function initTask(bare = false,
|
|
38762
|
+
function initTask(bare = false, path20, customArgs) {
|
|
38763
38763
|
const commands5 = ["init", ...customArgs];
|
|
38764
38764
|
if (bare && !hasBareCommand(commands5)) {
|
|
38765
38765
|
commands5.splice(1, 0, bareCommand);
|
|
@@ -38768,7 +38768,7 @@ function initTask(bare = false, path16, customArgs) {
|
|
|
38768
38768
|
commands: commands5,
|
|
38769
38769
|
format: "utf-8",
|
|
38770
38770
|
parser(text) {
|
|
38771
|
-
return parseInit(commands5.includes("--bare"),
|
|
38771
|
+
return parseInit(commands5.includes("--bare"), path20, text);
|
|
38772
38772
|
}
|
|
38773
38773
|
};
|
|
38774
38774
|
}
|
|
@@ -39583,12 +39583,12 @@ var init_FileStatusSummary = __esm2({
|
|
|
39583
39583
|
"use strict";
|
|
39584
39584
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
39585
39585
|
FileStatusSummary = class {
|
|
39586
|
-
constructor(
|
|
39587
|
-
this.path =
|
|
39586
|
+
constructor(path20, index, working_dir) {
|
|
39587
|
+
this.path = path20;
|
|
39588
39588
|
this.index = index;
|
|
39589
39589
|
this.working_dir = working_dir;
|
|
39590
39590
|
if (index === "R" || working_dir === "R") {
|
|
39591
|
-
const detail = fromPathRegex.exec(
|
|
39591
|
+
const detail = fromPathRegex.exec(path20) || [null, path20, path20];
|
|
39592
39592
|
this.from = detail[2] || "";
|
|
39593
39593
|
this.path = detail[1] || "";
|
|
39594
39594
|
}
|
|
@@ -39619,14 +39619,14 @@ function splitLine(result, lineStr) {
|
|
|
39619
39619
|
default:
|
|
39620
39620
|
return;
|
|
39621
39621
|
}
|
|
39622
|
-
function data2(index, workingDir,
|
|
39622
|
+
function data2(index, workingDir, path20) {
|
|
39623
39623
|
const raw = `${index}${workingDir}`;
|
|
39624
39624
|
const handler = parsers6.get(raw);
|
|
39625
39625
|
if (handler) {
|
|
39626
|
-
handler(result,
|
|
39626
|
+
handler(result, path20);
|
|
39627
39627
|
}
|
|
39628
39628
|
if (raw !== "##" && raw !== "!!") {
|
|
39629
|
-
result.files.push(new FileStatusSummary(
|
|
39629
|
+
result.files.push(new FileStatusSummary(path20, index, workingDir));
|
|
39630
39630
|
}
|
|
39631
39631
|
}
|
|
39632
39632
|
}
|
|
@@ -39977,9 +39977,9 @@ var init_simple_git_api = __esm2({
|
|
|
39977
39977
|
next
|
|
39978
39978
|
);
|
|
39979
39979
|
}
|
|
39980
|
-
hashObject(
|
|
39980
|
+
hashObject(path20, write) {
|
|
39981
39981
|
return this._runTask(
|
|
39982
|
-
hashObjectTask(
|
|
39982
|
+
hashObjectTask(path20, write === true),
|
|
39983
39983
|
trailingFunctionArgument(arguments)
|
|
39984
39984
|
);
|
|
39985
39985
|
}
|
|
@@ -40333,8 +40333,8 @@ var init_branch = __esm2({
|
|
|
40333
40333
|
}
|
|
40334
40334
|
});
|
|
40335
40335
|
function toPath(input) {
|
|
40336
|
-
const
|
|
40337
|
-
return
|
|
40336
|
+
const path20 = input.trim().replace(/^["']|["']$/g, "");
|
|
40337
|
+
return path20 && normalize(path20);
|
|
40338
40338
|
}
|
|
40339
40339
|
var parseCheckIgnore;
|
|
40340
40340
|
var init_CheckIgnore = __esm2({
|
|
@@ -40619,8 +40619,8 @@ __export2(sub_module_exports, {
|
|
|
40619
40619
|
subModuleTask: () => subModuleTask,
|
|
40620
40620
|
updateSubModuleTask: () => updateSubModuleTask
|
|
40621
40621
|
});
|
|
40622
|
-
function addSubModuleTask(repo,
|
|
40623
|
-
return subModuleTask(["add", repo,
|
|
40622
|
+
function addSubModuleTask(repo, path20) {
|
|
40623
|
+
return subModuleTask(["add", repo, path20]);
|
|
40624
40624
|
}
|
|
40625
40625
|
function initSubModuleTask(customArgs) {
|
|
40626
40626
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -40934,8 +40934,8 @@ var require_git = __commonJS2({
|
|
|
40934
40934
|
}
|
|
40935
40935
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
40936
40936
|
};
|
|
40937
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
40938
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
40937
|
+
Git2.prototype.submoduleAdd = function(repo, path20, then) {
|
|
40938
|
+
return this._runTask(addSubModuleTask2(repo, path20), trailingFunctionArgument2(arguments));
|
|
40939
40939
|
};
|
|
40940
40940
|
Git2.prototype.submoduleUpdate = function(args, then) {
|
|
40941
40941
|
return this._runTask(
|
|
@@ -42040,23 +42040,97 @@ async function createOrUpdatePullRequest(params) {
|
|
|
42040
42040
|
return { number: pr.data.number, url: pr.data.html_url, created: true };
|
|
42041
42041
|
}
|
|
42042
42042
|
|
|
42043
|
+
// ../canopycms/src/paths/branch-name.ts
|
|
42044
|
+
function sanitizeBranchName(branchName) {
|
|
42045
|
+
const replaced = branchName.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
42046
|
+
const squashed = replaced.replace(/-+/g, "-");
|
|
42047
|
+
const trimmedDots = squashed.replace(/^\.+/, "").replace(/(?<!\.)\.+$/, "");
|
|
42048
|
+
return trimmedDots || "branch";
|
|
42049
|
+
}
|
|
42050
|
+
var RESERVED_SETTINGS_BRANCH_PREFIX = "canopycms-settings-";
|
|
42051
|
+
|
|
42052
|
+
// ../canopycms/src/worker/cms-worker.ts
|
|
42053
|
+
init_deployment_name();
|
|
42054
|
+
|
|
42055
|
+
// ../canopycms/src/worker/worker-status.ts
|
|
42056
|
+
import path4 from "node:path";
|
|
42057
|
+
var WORKER_STATUS_FILE = "worker-status.json";
|
|
42058
|
+
async function writeWorkerStatus(taskDir, report) {
|
|
42059
|
+
const filePath = path4.join(taskDir, WORKER_STATUS_FILE);
|
|
42060
|
+
const stamped = { ...report, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
42061
|
+
await atomicWriteFile(filePath, JSON.stringify(stamped, null, 2));
|
|
42062
|
+
}
|
|
42063
|
+
|
|
42064
|
+
// ../canopycms/src/worker/log.ts
|
|
42065
|
+
init_logger();
|
|
42066
|
+
function timestamp() {
|
|
42067
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
42068
|
+
}
|
|
42069
|
+
function workerLog(...args) {
|
|
42070
|
+
console.log(timestamp(), "INFO", ...args);
|
|
42071
|
+
}
|
|
42072
|
+
function workerLogWarn(...args) {
|
|
42073
|
+
console.warn(timestamp(), "WARN", ...args);
|
|
42074
|
+
}
|
|
42075
|
+
function workerLogError(...args) {
|
|
42076
|
+
console.error(timestamp(), "ERROR", ...args);
|
|
42077
|
+
}
|
|
42078
|
+
function installWorkerLogger() {
|
|
42079
|
+
setCanopyLogger({ log: workerLog, warn: workerLogWarn, error: workerLogError });
|
|
42080
|
+
}
|
|
42081
|
+
|
|
42082
|
+
// ../canopycms/src/worker/task-runner.ts
|
|
42083
|
+
import fs10 from "node:fs/promises";
|
|
42084
|
+
|
|
42043
42085
|
// ../canopycms/src/branch-metadata.ts
|
|
42044
|
-
import
|
|
42045
|
-
import
|
|
42086
|
+
import fs7 from "node:fs/promises";
|
|
42087
|
+
import path9 from "node:path";
|
|
42046
42088
|
|
|
42047
42089
|
// ../canopycms/src/branch-registry.ts
|
|
42048
|
-
import
|
|
42090
|
+
import fs5 from "node:fs/promises";
|
|
42091
|
+
import path7 from "node:path";
|
|
42092
|
+
|
|
42093
|
+
// ../canopycms/src/branch-metadata-file.ts
|
|
42094
|
+
import fs3 from "node:fs/promises";
|
|
42049
42095
|
import path5 from "node:path";
|
|
42096
|
+
var BRANCH_META_DIR = ".canopy-meta";
|
|
42097
|
+
var BRANCH_META_FILE = "branch.json";
|
|
42098
|
+
var BranchMetadataCorruptError = class extends Error {
|
|
42099
|
+
constructor(branchRoot, cause) {
|
|
42100
|
+
super(`Corrupt branch metadata in '${branchRoot}': ${cause}`);
|
|
42101
|
+
this.name = "BranchMetadataCorruptError";
|
|
42102
|
+
this.branchRoot = branchRoot;
|
|
42103
|
+
this.parseCause = cause;
|
|
42104
|
+
}
|
|
42105
|
+
};
|
|
42106
|
+
var branchMetadataFilePath = (branchRoot) => path5.join(path5.resolve(branchRoot), BRANCH_META_DIR, BRANCH_META_FILE);
|
|
42107
|
+
async function readBranchMetadataFile(branchRoot) {
|
|
42108
|
+
const resolvedRoot = path5.resolve(branchRoot);
|
|
42109
|
+
try {
|
|
42110
|
+
const raw = await fs3.readFile(branchMetadataFilePath(resolvedRoot), "utf8");
|
|
42111
|
+
return JSON.parse(raw);
|
|
42112
|
+
} catch (err) {
|
|
42113
|
+
if (isNotFoundError2(err)) {
|
|
42114
|
+
return null;
|
|
42115
|
+
}
|
|
42116
|
+
if (err instanceof SyntaxError) {
|
|
42117
|
+
throw new BranchMetadataCorruptError(resolvedRoot, err.message);
|
|
42118
|
+
}
|
|
42119
|
+
throw err;
|
|
42120
|
+
}
|
|
42121
|
+
}
|
|
42122
|
+
|
|
42123
|
+
// ../canopycms/src/branch-registry.ts
|
|
42050
42124
|
init_logger();
|
|
42051
42125
|
|
|
42052
42126
|
// ../canopycms/src/resource-generation.ts
|
|
42053
|
-
import
|
|
42054
|
-
import
|
|
42127
|
+
import fs4 from "node:fs/promises";
|
|
42128
|
+
import path6 from "node:path";
|
|
42055
42129
|
import { randomUUID } from "node:crypto";
|
|
42056
42130
|
var log = createDebugLogger({ prefix: "ResourceGeneration" });
|
|
42057
42131
|
var META_DIR = ".canopy-meta";
|
|
42058
42132
|
function resourceGenerationPath(root, resource) {
|
|
42059
|
-
return
|
|
42133
|
+
return path6.resolve(root, META_DIR, `${resource}.generation`);
|
|
42060
42134
|
}
|
|
42061
42135
|
async function bumpResourceGeneration(root, resource, options) {
|
|
42062
42136
|
const token = randomUUID();
|
|
@@ -42073,7 +42147,7 @@ async function bumpResourceGeneration(root, resource, options) {
|
|
|
42073
42147
|
}
|
|
42074
42148
|
async function readResourceGeneration(root, resource) {
|
|
42075
42149
|
try {
|
|
42076
|
-
const token = await
|
|
42150
|
+
const token = await fs4.readFile(resourceGenerationPath(root, resource), "utf-8");
|
|
42077
42151
|
return { ok: true, token };
|
|
42078
42152
|
} catch (err) {
|
|
42079
42153
|
if (isNodeError(err) && err.code === "ENOENT") return { ok: true, token: null };
|
|
@@ -42101,10 +42175,10 @@ var BranchRegistry = class {
|
|
|
42101
42175
|
this.regenInFlight = null;
|
|
42102
42176
|
/** Throttle clock for the get() suspicious-miss backstop. */
|
|
42103
42177
|
this.lastForcedRefreshMs = 0;
|
|
42104
|
-
this.root =
|
|
42105
|
-
this.registryPath =
|
|
42106
|
-
this.stalePath =
|
|
42107
|
-
this.tempPath =
|
|
42178
|
+
this.root = path7.resolve(root);
|
|
42179
|
+
this.registryPath = path7.join(this.root, REGISTRY_FILE);
|
|
42180
|
+
this.stalePath = path7.join(this.root, REGISTRY_STALE_FILE);
|
|
42181
|
+
this.tempPath = path7.join(this.root, REGISTRY_TEMP_FILE);
|
|
42108
42182
|
}
|
|
42109
42183
|
/**
|
|
42110
42184
|
* Returns all branches. Uses the cached snapshot if its embedded generation
|
|
@@ -42113,7 +42187,7 @@ var BranchRegistry = class {
|
|
|
42113
42187
|
async list() {
|
|
42114
42188
|
let parsed;
|
|
42115
42189
|
try {
|
|
42116
|
-
const raw = await
|
|
42190
|
+
const raw = await fs5.readFile(this.registryPath, "utf8");
|
|
42117
42191
|
parsed = JSON.parse(raw);
|
|
42118
42192
|
} catch (err) {
|
|
42119
42193
|
if (isNotFoundError2(err)) {
|
|
@@ -42197,7 +42271,7 @@ var BranchRegistry = class {
|
|
|
42197
42271
|
async performRegenerate() {
|
|
42198
42272
|
const read = await readResourceGeneration(this.root, RESOURCE);
|
|
42199
42273
|
const branches = await this.scanBranchDirectories();
|
|
42200
|
-
await
|
|
42274
|
+
await fs5.unlink(this.stalePath).catch(() => {
|
|
42201
42275
|
});
|
|
42202
42276
|
if (!read.ok) {
|
|
42203
42277
|
return branches;
|
|
@@ -42208,12 +42282,12 @@ var BranchRegistry = class {
|
|
|
42208
42282
|
generation: read.token
|
|
42209
42283
|
};
|
|
42210
42284
|
const uniqueTempPath = `${this.tempPath}.${Date.now()}.${Math.random().toString(36).slice(2)}`;
|
|
42211
|
-
await
|
|
42212
|
-
await
|
|
42285
|
+
await fs5.mkdir(this.root, { recursive: true });
|
|
42286
|
+
await fs5.writeFile(uniqueTempPath, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
|
|
42213
42287
|
try {
|
|
42214
|
-
await
|
|
42288
|
+
await fs5.rename(uniqueTempPath, this.registryPath);
|
|
42215
42289
|
} catch (err) {
|
|
42216
|
-
await
|
|
42290
|
+
await fs5.unlink(uniqueTempPath).catch(() => {
|
|
42217
42291
|
});
|
|
42218
42292
|
throw err;
|
|
42219
42293
|
}
|
|
@@ -42227,15 +42301,15 @@ var BranchRegistry = class {
|
|
|
42227
42301
|
async scanBranchDirectories() {
|
|
42228
42302
|
const branches = [];
|
|
42229
42303
|
try {
|
|
42230
|
-
const entries = await
|
|
42304
|
+
const entries = await fs5.readdir(this.root, { withFileTypes: true });
|
|
42231
42305
|
for (const entry of entries) {
|
|
42232
42306
|
if (!entry.isDirectory() || entry.name.startsWith(".")) {
|
|
42233
42307
|
continue;
|
|
42234
42308
|
}
|
|
42235
|
-
const branchRoot =
|
|
42309
|
+
const branchRoot = path7.join(this.root, entry.name);
|
|
42236
42310
|
let meta;
|
|
42237
42311
|
try {
|
|
42238
|
-
meta = await
|
|
42312
|
+
meta = await readBranchMetadataFile(branchRoot);
|
|
42239
42313
|
} catch (err) {
|
|
42240
42314
|
canopyLogWarn(
|
|
42241
42315
|
`CanopyCMS: Skipping branch directory '${entry.name}' during registry scan: ${getErrorMessage2(err)}`
|
|
@@ -42275,15 +42349,6 @@ function isValidContentId(id) {
|
|
|
42275
42349
|
// ../canopycms/src/paths/branch.ts
|
|
42276
42350
|
init_operating_mode();
|
|
42277
42351
|
|
|
42278
|
-
// ../canopycms/src/paths/branch-name.ts
|
|
42279
|
-
function sanitizeBranchName(branchName) {
|
|
42280
|
-
const replaced = branchName.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
42281
|
-
const squashed = replaced.replace(/-+/g, "-");
|
|
42282
|
-
const trimmedDots = squashed.replace(/^\.+/, "").replace(/(?<!\.)\.+$/, "");
|
|
42283
|
-
return trimmedDots || "branch";
|
|
42284
|
-
}
|
|
42285
|
-
var RESERVED_SETTINGS_BRANCH_PREFIX = "canopycms-settings-";
|
|
42286
|
-
|
|
42287
42352
|
// ../canopycms/src/utils/async-mutex.ts
|
|
42288
42353
|
var locks = /* @__PURE__ */ new Map();
|
|
42289
42354
|
async function withLock(key, fn) {
|
|
@@ -42304,8 +42369,8 @@ async function withLock(key, fn) {
|
|
|
42304
42369
|
|
|
42305
42370
|
// ../canopycms/src/utils/occ-json-write.ts
|
|
42306
42371
|
var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
|
|
42307
|
-
import
|
|
42308
|
-
import
|
|
42372
|
+
import fs6 from "node:fs/promises";
|
|
42373
|
+
import path8 from "node:path";
|
|
42309
42374
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
42310
42375
|
var log3 = createDebugLogger({ prefix: "OccJsonWrite" });
|
|
42311
42376
|
var OccWriteConflictError = class extends Error {
|
|
@@ -42320,29 +42385,29 @@ async function writeOccJsonFile(filePath, payload2, options) {
|
|
|
42320
42385
|
const writeId = randomUUID2();
|
|
42321
42386
|
const data2 = { ...payload2, version: newVersion, writeId };
|
|
42322
42387
|
const content = JSON.stringify(data2, null, 2) + (trailingNewline ? "\n" : "");
|
|
42323
|
-
const dir =
|
|
42324
|
-
await
|
|
42388
|
+
const dir = path8.dirname(filePath);
|
|
42389
|
+
await fs6.mkdir(dir, { recursive: true });
|
|
42325
42390
|
const tempPath = `${filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
42326
|
-
await
|
|
42391
|
+
await fs6.writeFile(tempPath, content, "utf-8");
|
|
42327
42392
|
if (expectedVersion === null) {
|
|
42328
42393
|
try {
|
|
42329
|
-
await
|
|
42394
|
+
await fs6.link(tempPath, filePath);
|
|
42330
42395
|
} catch (err) {
|
|
42331
|
-
await
|
|
42396
|
+
await fs6.unlink(tempPath).catch(() => {
|
|
42332
42397
|
});
|
|
42333
42398
|
if (isFileExistsError(err)) {
|
|
42334
42399
|
throw new OccWriteConflictError();
|
|
42335
42400
|
}
|
|
42336
42401
|
throw err;
|
|
42337
42402
|
}
|
|
42338
|
-
await
|
|
42403
|
+
await fs6.unlink(tempPath).catch(() => {
|
|
42339
42404
|
});
|
|
42340
42405
|
return { version: newVersion, writeId };
|
|
42341
42406
|
}
|
|
42342
42407
|
try {
|
|
42343
42408
|
let currentVersion = null;
|
|
42344
42409
|
try {
|
|
42345
|
-
const current = JSON.parse(await
|
|
42410
|
+
const current = JSON.parse(await fs6.readFile(filePath, "utf-8"));
|
|
42346
42411
|
currentVersion = current.version ?? 0;
|
|
42347
42412
|
} catch {
|
|
42348
42413
|
currentVersion = null;
|
|
@@ -42350,16 +42415,16 @@ async function writeOccJsonFile(filePath, payload2, options) {
|
|
|
42350
42415
|
if (currentVersion !== expectedVersion) {
|
|
42351
42416
|
throw new OccWriteConflictError();
|
|
42352
42417
|
}
|
|
42353
|
-
await
|
|
42418
|
+
await fs6.rename(tempPath, filePath);
|
|
42354
42419
|
if (settleMs > 0) {
|
|
42355
42420
|
await new Promise((resolve2) => setTimeout(resolve2, settleMs));
|
|
42356
42421
|
}
|
|
42357
|
-
const afterWrite = JSON.parse(await
|
|
42422
|
+
const afterWrite = JSON.parse(await fs6.readFile(filePath, "utf-8"));
|
|
42358
42423
|
if (afterWrite.writeId !== writeId) {
|
|
42359
42424
|
throw new OccWriteConflictError();
|
|
42360
42425
|
}
|
|
42361
42426
|
} catch (err) {
|
|
42362
|
-
await
|
|
42427
|
+
await fs6.unlink(tempPath).catch(() => {
|
|
42363
42428
|
});
|
|
42364
42429
|
throw err;
|
|
42365
42430
|
}
|
|
@@ -42385,8 +42450,8 @@ async function withOccRetry(operation2, options) {
|
|
|
42385
42450
|
throw new Error("Unreachable");
|
|
42386
42451
|
}
|
|
42387
42452
|
async function withOccFileLock(filePath, fn) {
|
|
42388
|
-
const dir =
|
|
42389
|
-
await
|
|
42453
|
+
const dir = path8.dirname(filePath);
|
|
42454
|
+
await fs6.mkdir(dir, { recursive: true });
|
|
42390
42455
|
const MAX_ATTEMPTS = 14;
|
|
42391
42456
|
let release = null;
|
|
42392
42457
|
for (let attempt = 1; release === null; attempt++) {
|
|
@@ -42429,8 +42494,6 @@ async function withOccFileLock(filePath, fn) {
|
|
|
42429
42494
|
}
|
|
42430
42495
|
|
|
42431
42496
|
// ../canopycms/src/branch-metadata.ts
|
|
42432
|
-
var BRANCH_META_DIR = ".canopy-meta";
|
|
42433
|
-
var BRANCH_META_FILE = "branch.json";
|
|
42434
42497
|
var CURRENT_SCHEMA_VERSION = 1;
|
|
42435
42498
|
var BranchMetadataConflictError = class extends Error {
|
|
42436
42499
|
constructor(message = "Concurrent modification detected in branch metadata") {
|
|
@@ -42438,18 +42501,10 @@ var BranchMetadataConflictError = class extends Error {
|
|
|
42438
42501
|
this.name = "BranchMetadataConflictError";
|
|
42439
42502
|
}
|
|
42440
42503
|
};
|
|
42441
|
-
var BranchMetadataCorruptError = class extends Error {
|
|
42442
|
-
constructor(branchRoot, cause) {
|
|
42443
|
-
super(`Corrupt branch metadata in '${branchRoot}': ${cause}`);
|
|
42444
|
-
this.name = "BranchMetadataCorruptError";
|
|
42445
|
-
this.branchRoot = branchRoot;
|
|
42446
|
-
this.parseCause = cause;
|
|
42447
|
-
}
|
|
42448
|
-
};
|
|
42449
42504
|
var BranchMetadataFileManager = class _BranchMetadataFileManager {
|
|
42450
42505
|
constructor(branchRoot, baseRoot, options) {
|
|
42451
|
-
this.branchRoot =
|
|
42452
|
-
this.filePath =
|
|
42506
|
+
this.branchRoot = path9.resolve(branchRoot);
|
|
42507
|
+
this.filePath = path9.join(this.branchRoot, BRANCH_META_DIR, BRANCH_META_FILE);
|
|
42453
42508
|
this.baseRoot = baseRoot;
|
|
42454
42509
|
this.settleMs = options?.settleMs;
|
|
42455
42510
|
}
|
|
@@ -42458,20 +42513,7 @@ var BranchMetadataFileManager = class _BranchMetadataFileManager {
|
|
|
42458
42513
|
* Use this for read-only access (e.g., in registry scanning or loadBranchContext).
|
|
42459
42514
|
*/
|
|
42460
42515
|
static async loadOnly(branchRoot) {
|
|
42461
|
-
|
|
42462
|
-
const filePath = path7.join(resolvedRoot, BRANCH_META_DIR, BRANCH_META_FILE);
|
|
42463
|
-
try {
|
|
42464
|
-
const raw = await fs6.readFile(filePath, "utf8");
|
|
42465
|
-
return JSON.parse(raw);
|
|
42466
|
-
} catch (err) {
|
|
42467
|
-
if (isNotFoundError2(err)) {
|
|
42468
|
-
return null;
|
|
42469
|
-
}
|
|
42470
|
-
if (err instanceof SyntaxError) {
|
|
42471
|
-
throw new BranchMetadataCorruptError(resolvedRoot, err.message);
|
|
42472
|
-
}
|
|
42473
|
-
throw err;
|
|
42474
|
-
}
|
|
42516
|
+
return readBranchMetadataFile(branchRoot);
|
|
42475
42517
|
}
|
|
42476
42518
|
/**
|
|
42477
42519
|
* Get a BranchMetadataFileManager instance configured for registry invalidation.
|
|
@@ -42482,7 +42524,7 @@ var BranchMetadataFileManager = class _BranchMetadataFileManager {
|
|
|
42482
42524
|
}
|
|
42483
42525
|
async load() {
|
|
42484
42526
|
try {
|
|
42485
|
-
const raw = await
|
|
42527
|
+
const raw = await fs7.readFile(this.filePath, "utf8");
|
|
42486
42528
|
const parsed = JSON.parse(raw);
|
|
42487
42529
|
const version = parsed.version ?? 0;
|
|
42488
42530
|
return { meta: parsed, version };
|
|
@@ -42544,7 +42586,7 @@ var BranchMetadataFileManager = class _BranchMetadataFileManager {
|
|
|
42544
42586
|
*/
|
|
42545
42587
|
async save(incoming) {
|
|
42546
42588
|
try {
|
|
42547
|
-
await
|
|
42589
|
+
await fs7.stat(this.branchRoot);
|
|
42548
42590
|
} catch (err) {
|
|
42549
42591
|
if (isNotFoundError2(err)) {
|
|
42550
42592
|
throw new BranchMetadataConflictError("Branch no longer exists");
|
|
@@ -42626,30 +42668,8 @@ var getBranchMetadataFileManager = (branchRoot, baseRoot, options) => {
|
|
|
42626
42668
|
return BranchMetadataFileManager.get(branchRoot, baseRoot, options);
|
|
42627
42669
|
};
|
|
42628
42670
|
|
|
42629
|
-
// ../canopycms/src/id.ts
|
|
42630
|
-
var import_short_uuid = __toESM(require_dist3(), 1);
|
|
42631
|
-
var isValidId = isValidContentId;
|
|
42632
|
-
|
|
42633
|
-
// ../canopycms/src/content-id-index.ts
|
|
42634
|
-
init_logger();
|
|
42635
|
-
function extractIdFromFilename(filename) {
|
|
42636
|
-
if (filename.startsWith(".")) {
|
|
42637
|
-
return null;
|
|
42638
|
-
}
|
|
42639
|
-
const parts = filename.split(".");
|
|
42640
|
-
if (parts.length >= 3) {
|
|
42641
|
-
const candidate = parts[parts.length - 2];
|
|
42642
|
-
if (isValidId(candidate)) return candidate;
|
|
42643
|
-
}
|
|
42644
|
-
if (parts.length === 2) {
|
|
42645
|
-
const candidate = parts[parts.length - 1];
|
|
42646
|
-
if (isValidId(candidate)) return candidate;
|
|
42647
|
-
}
|
|
42648
|
-
return null;
|
|
42649
|
-
}
|
|
42650
|
-
|
|
42651
42671
|
// ../canopycms/src/content-index-registry.ts
|
|
42652
|
-
import
|
|
42672
|
+
import path10 from "node:path";
|
|
42653
42673
|
var registry = /* @__PURE__ */ new Map();
|
|
42654
42674
|
var finalization = new FinalizationRegistry(({ rootKey, ref }) => {
|
|
42655
42675
|
const refs = registry.get(rootKey);
|
|
@@ -42658,8 +42678,8 @@ var finalization = new FinalizationRegistry(({ rootKey, ref }) => {
|
|
|
42658
42678
|
if (refs.size === 0) registry.delete(rootKey);
|
|
42659
42679
|
});
|
|
42660
42680
|
function invalidateContentIndexesForRoot(root) {
|
|
42661
|
-
const rootKey =
|
|
42662
|
-
const prefix = rootKey +
|
|
42681
|
+
const rootKey = path10.resolve(root);
|
|
42682
|
+
const prefix = rootKey + path10.sep;
|
|
42663
42683
|
for (const [storeRoot, refs] of registry) {
|
|
42664
42684
|
if (storeRoot !== rootKey && !storeRoot.startsWith(prefix)) continue;
|
|
42665
42685
|
for (const ref of refs) {
|
|
@@ -42677,6 +42697,30 @@ function invalidateContentIndexesForRoot(root) {
|
|
|
42677
42697
|
// ../canopycms/src/schema/meta-loader.ts
|
|
42678
42698
|
init_zod();
|
|
42679
42699
|
init_logger();
|
|
42700
|
+
|
|
42701
|
+
// ../canopycms/src/id.ts
|
|
42702
|
+
var import_short_uuid = __toESM(require_dist3(), 1);
|
|
42703
|
+
var isValidId = isValidContentId;
|
|
42704
|
+
|
|
42705
|
+
// ../canopycms/src/content-id-index.ts
|
|
42706
|
+
init_logger();
|
|
42707
|
+
function extractIdFromFilename(filename) {
|
|
42708
|
+
if (filename.startsWith(".")) {
|
|
42709
|
+
return null;
|
|
42710
|
+
}
|
|
42711
|
+
const parts = filename.split(".");
|
|
42712
|
+
if (parts.length >= 3) {
|
|
42713
|
+
const candidate = parts[parts.length - 2];
|
|
42714
|
+
if (isValidId(candidate)) return candidate;
|
|
42715
|
+
}
|
|
42716
|
+
if (parts.length === 2) {
|
|
42717
|
+
const candidate = parts[parts.length - 1];
|
|
42718
|
+
if (isValidId(candidate)) return candidate;
|
|
42719
|
+
}
|
|
42720
|
+
return null;
|
|
42721
|
+
}
|
|
42722
|
+
|
|
42723
|
+
// ../canopycms/src/schema/meta-loader.ts
|
|
42680
42724
|
var entryTypeMetaSchema = external_exports.object({
|
|
42681
42725
|
name: external_exports.string().min(1),
|
|
42682
42726
|
format: external_exports.enum(["md", "mdx", "json", "yaml"]),
|
|
@@ -42733,13 +42777,9 @@ async function invalidateBranchContentCaches(root) {
|
|
|
42733
42777
|
invalidateContentIndexesForRoot(root);
|
|
42734
42778
|
}
|
|
42735
42779
|
|
|
42736
|
-
// ../canopycms/src/worker/cms-worker.ts
|
|
42737
|
-
init_types3();
|
|
42738
|
-
init_normalize();
|
|
42739
|
-
|
|
42740
42780
|
// ../canopycms/src/utils/git.ts
|
|
42741
|
-
import
|
|
42742
|
-
import
|
|
42781
|
+
import fs8 from "node:fs/promises";
|
|
42782
|
+
import path11 from "node:path";
|
|
42743
42783
|
var REJECTED_MARKER = "[rejected]";
|
|
42744
42784
|
var NON_FAST_FORWARD_REASONS = ["non-fast-forward", "fetch first"];
|
|
42745
42785
|
var REJECTION_HINT = "Updates were rejected because";
|
|
@@ -42752,20 +42792,20 @@ function isStaleLeaseRejection(message) {
|
|
|
42752
42792
|
return message.includes(REJECTED_MARKER) && message.includes(STALE_LEASE_REASON);
|
|
42753
42793
|
}
|
|
42754
42794
|
async function resolveGitDir(repoPath) {
|
|
42755
|
-
const dotGit =
|
|
42795
|
+
const dotGit = path11.join(repoPath, ".git");
|
|
42756
42796
|
let stat;
|
|
42757
42797
|
try {
|
|
42758
|
-
stat = await
|
|
42798
|
+
stat = await fs8.stat(dotGit);
|
|
42759
42799
|
} catch {
|
|
42760
42800
|
return null;
|
|
42761
42801
|
}
|
|
42762
42802
|
if (stat.isDirectory()) return dotGit;
|
|
42763
42803
|
try {
|
|
42764
|
-
const pointer = await
|
|
42804
|
+
const pointer = await fs8.readFile(dotGit, "utf-8");
|
|
42765
42805
|
const match2 = /^gitdir:\s*(.+)$/m.exec(pointer);
|
|
42766
42806
|
if (!match2) return null;
|
|
42767
42807
|
const target = match2[1].trim();
|
|
42768
|
-
return
|
|
42808
|
+
return path11.isAbsolute(target) ? target : path11.resolve(repoPath, target);
|
|
42769
42809
|
} catch {
|
|
42770
42810
|
return null;
|
|
42771
42811
|
}
|
|
@@ -42775,7 +42815,7 @@ async function isRebaseInProgress(repoPath) {
|
|
|
42775
42815
|
if (!gitDir) return false;
|
|
42776
42816
|
const results = await Promise.all(
|
|
42777
42817
|
["rebase-merge", "rebase-apply"].map(
|
|
42778
|
-
(dir) =>
|
|
42818
|
+
(dir) => fs8.stat(path11.join(gitDir, dir)).then(() => true).catch(() => false)
|
|
42779
42819
|
)
|
|
42780
42820
|
);
|
|
42781
42821
|
return results.some(Boolean);
|
|
@@ -42783,8 +42823,8 @@ async function isRebaseInProgress(repoPath) {
|
|
|
42783
42823
|
|
|
42784
42824
|
// ../canopycms/src/utils/provisioning-lock.ts
|
|
42785
42825
|
var import_proper_lockfile2 = __toESM(require_proper_lockfile(), 1);
|
|
42786
|
-
import
|
|
42787
|
-
import
|
|
42826
|
+
import fs9 from "node:fs/promises";
|
|
42827
|
+
import path12 from "node:path";
|
|
42788
42828
|
init_logger();
|
|
42789
42829
|
function provisioningLockOptions(lockPath, retries, onCompromised) {
|
|
42790
42830
|
return {
|
|
@@ -42836,8 +42876,8 @@ function releaseIgnoringAlreadyReleased(release, lockPath) {
|
|
|
42836
42876
|
};
|
|
42837
42877
|
}
|
|
42838
42878
|
async function tryAcquireProvisioningLock(lockTargetDir2, lockName, onCompromised) {
|
|
42839
|
-
await
|
|
42840
|
-
const lockPath =
|
|
42879
|
+
await fs9.mkdir(lockTargetDir2, { recursive: true });
|
|
42880
|
+
const lockPath = path12.join(lockTargetDir2, lockName);
|
|
42841
42881
|
const release = await import_proper_lockfile2.default.lock(lockPath, provisioningLockOptions(lockPath, 0, onCompromised));
|
|
42842
42882
|
return releaseIgnoringAlreadyReleased(release, lockPath);
|
|
42843
42883
|
}
|
|
@@ -42864,65 +42904,110 @@ function gitNetworkChildEnv() {
|
|
|
42864
42904
|
}
|
|
42865
42905
|
var GITHUB_TRACKING_REF_PREFIX = "refs/remotes/github/";
|
|
42866
42906
|
|
|
42867
|
-
// ../canopycms/src/worker/
|
|
42868
|
-
|
|
42869
|
-
|
|
42870
|
-
|
|
42871
|
-
|
|
42872
|
-
|
|
42873
|
-
|
|
42874
|
-
|
|
42875
|
-
|
|
42876
|
-
|
|
42877
|
-
|
|
42878
|
-
|
|
42879
|
-
|
|
42880
|
-
|
|
42881
|
-
|
|
42882
|
-
|
|
42883
|
-
);
|
|
42907
|
+
// ../canopycms/src/worker/history-rewrite.ts
|
|
42908
|
+
import path13 from "node:path";
|
|
42909
|
+
async function readPublishedSha(ctx, branchRef) {
|
|
42910
|
+
try {
|
|
42911
|
+
const out = await simpleGit().raw([
|
|
42912
|
+
"--git-dir",
|
|
42913
|
+
ctx.remoteGitPath,
|
|
42914
|
+
"rev-parse",
|
|
42915
|
+
"--verify",
|
|
42916
|
+
`refs/heads/${branchRef}`
|
|
42917
|
+
]);
|
|
42918
|
+
const sha = out.trim();
|
|
42919
|
+
return sha.length > 0 ? sha : null;
|
|
42920
|
+
} catch {
|
|
42921
|
+
return null;
|
|
42922
|
+
}
|
|
42884
42923
|
}
|
|
42885
|
-
|
|
42886
|
-
|
|
42887
|
-
|
|
42888
|
-
|
|
42889
|
-
|
|
42890
|
-
const filePath = path12.join(taskDir, WORKER_STATUS_FILE);
|
|
42891
|
-
const stamped = { ...report, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
42892
|
-
await atomicWriteFile(filePath, JSON.stringify(stamped, null, 2));
|
|
42924
|
+
async function markHistoryRewritten(ctx, branchPath, branchDir, expectedSha) {
|
|
42925
|
+
const current = await BranchMetadataFileManager.loadOnly(branchPath);
|
|
42926
|
+
if (typeof current?.branch.historyRewrittenFrom === "string") return;
|
|
42927
|
+
const meta = getBranchMetadataFileManager(branchPath, ctx.contentBranchesPath);
|
|
42928
|
+
await meta.save({ branch: { name: branchDir, historyRewrittenFrom: expectedSha } });
|
|
42893
42929
|
}
|
|
42894
|
-
|
|
42895
|
-
|
|
42896
|
-
|
|
42897
|
-
|
|
42898
|
-
|
|
42930
|
+
async function clearHistoryRewrittenMarker(ctx, branchPath, branchDir) {
|
|
42931
|
+
try {
|
|
42932
|
+
const meta = getBranchMetadataFileManager(branchPath, ctx.contentBranchesPath);
|
|
42933
|
+
await meta.save({ branch: { name: branchDir, historyRewrittenFrom: void 0 } });
|
|
42934
|
+
} catch (err) {
|
|
42935
|
+
workerLogWarn(
|
|
42936
|
+
` Failed to clear history-rewrite marker for ${branchDir}: ${getErrorMessage2(err)}`
|
|
42937
|
+
);
|
|
42938
|
+
}
|
|
42899
42939
|
}
|
|
42900
|
-
function
|
|
42901
|
-
|
|
42940
|
+
async function forcePublishToLocalRemote(ctx, branchPath, branchRef, expectedSha) {
|
|
42941
|
+
const pushGit = simpleGit({ baseDir: branchPath, timeout: { block: ctx.taskTimeoutMs } });
|
|
42942
|
+
pushGit.env(gitChildEnv({}));
|
|
42943
|
+
try {
|
|
42944
|
+
await pushGit.raw([
|
|
42945
|
+
"push",
|
|
42946
|
+
`--force-with-lease=${branchRef}:${expectedSha}`,
|
|
42947
|
+
// Real flags must precede --end-of-options; everything after it is
|
|
42948
|
+
// positional (see GitManager.push() for the same guard).
|
|
42949
|
+
"--end-of-options",
|
|
42950
|
+
"origin",
|
|
42951
|
+
`${branchRef}:${branchRef}`
|
|
42952
|
+
]);
|
|
42953
|
+
workerLog(` Published rebased ${branchRef} into remote.git`);
|
|
42954
|
+
return true;
|
|
42955
|
+
} catch (err) {
|
|
42956
|
+
const message = redactCredentials(getErrorMessage2(err));
|
|
42957
|
+
workerLogWarn(
|
|
42958
|
+
isStaleLeaseRejection(message) ? ` Did not publish rebased ${branchRef} into remote.git: it moved since this cycle read it (concurrent submit?) -- retrying next cycle` : ` Failed to publish rebased ${branchRef} into remote.git: ${message}`
|
|
42959
|
+
);
|
|
42960
|
+
return false;
|
|
42961
|
+
}
|
|
42902
42962
|
}
|
|
42903
|
-
function
|
|
42904
|
-
|
|
42963
|
+
async function enqueueGitHubPush(ctx, branchRef) {
|
|
42964
|
+
try {
|
|
42965
|
+
for (const status of ["pending", "processing"]) {
|
|
42966
|
+
const inFlight = await listTasks(ctx.taskDir, status);
|
|
42967
|
+
if (inFlight.some((t6) => t6.action === "push-branch" && t6.payload.branch === branchRef)) {
|
|
42968
|
+
return;
|
|
42969
|
+
}
|
|
42970
|
+
}
|
|
42971
|
+
await enqueueTask(ctx.taskDir, { action: "push-branch", payload: { branch: branchRef } });
|
|
42972
|
+
workerLog(` Queued GitHub push for ${branchRef}`);
|
|
42973
|
+
} catch (err) {
|
|
42974
|
+
workerLogWarn(` Failed to queue GitHub push for ${branchRef}: ${getErrorMessage2(err)}`);
|
|
42975
|
+
}
|
|
42905
42976
|
}
|
|
42906
|
-
function
|
|
42907
|
-
|
|
42977
|
+
async function reconcilePendingRewrite(ctx, options) {
|
|
42978
|
+
const { branchPath, branchDir, branchRef, headSha, marker } = options;
|
|
42979
|
+
const publishedSha = await readPublishedSha(ctx, branchRef);
|
|
42980
|
+
if (publishedSha === null) {
|
|
42981
|
+
workerLogWarn(
|
|
42982
|
+
` ${branchDir}: a rewritten history is recorded but ${branchRef} is gone from remote.git -- nothing to publish`
|
|
42983
|
+
);
|
|
42984
|
+
return;
|
|
42985
|
+
}
|
|
42986
|
+
if (publishedSha === headSha) {
|
|
42987
|
+
await enqueueGitHubPush(ctx, branchRef);
|
|
42988
|
+
return;
|
|
42989
|
+
}
|
|
42990
|
+
if (publishedSha === marker) {
|
|
42991
|
+
if (await forcePublishToLocalRemote(ctx, branchPath, branchRef, marker)) {
|
|
42992
|
+
await enqueueGitHubPush(ctx, branchRef);
|
|
42993
|
+
}
|
|
42994
|
+
return;
|
|
42995
|
+
}
|
|
42996
|
+
workerLogWarn(
|
|
42997
|
+
` ${branchDir}: remote.git is at ${publishedSha} for ${branchRef}, which is neither the rebased tip nor the commit the rewrite replaced -- left untouched`
|
|
42998
|
+
);
|
|
42908
42999
|
}
|
|
42909
|
-
function
|
|
42910
|
-
|
|
43000
|
+
async function hasPendingHistoryRewrite(ctx, branchName) {
|
|
43001
|
+
try {
|
|
43002
|
+
const branchPath = path13.join(ctx.contentBranchesPath, sanitizeBranchName(branchName));
|
|
43003
|
+
const metaFile = await BranchMetadataFileManager.loadOnly(branchPath);
|
|
43004
|
+
return typeof metaFile?.branch.historyRewrittenFrom === "string";
|
|
43005
|
+
} catch {
|
|
43006
|
+
return false;
|
|
43007
|
+
}
|
|
42911
43008
|
}
|
|
42912
43009
|
|
|
42913
|
-
// ../canopycms/src/worker/
|
|
42914
|
-
var DEFAULT_TASK_TIMEOUT = 6e4;
|
|
42915
|
-
var DEFAULT_MAX_RETRIES2 = 3;
|
|
42916
|
-
var DEFAULT_LOCK_STALE_MS = 6e4;
|
|
42917
|
-
var TRASH_RETENTION_MS = 30 * 24 * 60 * 6e4;
|
|
42918
|
-
var TRASH_DIR_STAMP_RE = /-(\d{8}T\d{6}Z)$/;
|
|
42919
|
-
function parseTrashStamp(stamp) {
|
|
42920
|
-
const match2 = /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z$/.exec(stamp);
|
|
42921
|
-
if (!match2) return null;
|
|
42922
|
-
const [, year2, month, day, hour, minute, second] = match2;
|
|
42923
|
-
const date2 = /* @__PURE__ */ new Date(`${year2}-${month}-${day}T${hour}:${minute}:${second}.000Z`);
|
|
42924
|
-
return Number.isNaN(date2.getTime()) ? null : date2;
|
|
42925
|
-
}
|
|
43010
|
+
// ../canopycms/src/worker/task-runner.ts
|
|
42926
43011
|
var PermanentTaskError = class extends Error {
|
|
42927
43012
|
};
|
|
42928
43013
|
function isPermanentTaskFailure(err) {
|
|
@@ -42973,6 +43058,1019 @@ function optionalString(payload2, key, fallback) {
|
|
|
42973
43058
|
const val = payload2[key];
|
|
42974
43059
|
return typeof val === "string" ? val : fallback;
|
|
42975
43060
|
}
|
|
43061
|
+
function orphanRecoveryMaxAgeMs(ctx) {
|
|
43062
|
+
return Math.max(5 * 6e4, ctx.taskTimeoutMs * 2);
|
|
43063
|
+
}
|
|
43064
|
+
async function processTaskQueue(ctx) {
|
|
43065
|
+
if (!ctx.isRunning()) return;
|
|
43066
|
+
const recovered = await recoverOrphanedTasks(ctx.taskDir, orphanRecoveryMaxAgeMs(ctx), ctx.log);
|
|
43067
|
+
if (recovered > 0) {
|
|
43068
|
+
workerLog(`Recovered ${recovered} orphaned task(s)`);
|
|
43069
|
+
}
|
|
43070
|
+
let processed = 0;
|
|
43071
|
+
let task;
|
|
43072
|
+
while (processed < ctx.maxTasksPerCycle && (task = await dequeueTask(ctx.taskDir, ctx.log)) !== null) {
|
|
43073
|
+
try {
|
|
43074
|
+
const result = await executeTaskWithTimeout(ctx, task);
|
|
43075
|
+
await completeTask(ctx.taskDir, task.id, result, ctx.log);
|
|
43076
|
+
await updateBranchMetadata(ctx, task, result);
|
|
43077
|
+
} catch (err) {
|
|
43078
|
+
const message = getErrorMessage2(err);
|
|
43079
|
+
workerLogError(`Task ${task.id} (${task.action}) failed:`, message);
|
|
43080
|
+
const persistedMessage = redactCredentials(message);
|
|
43081
|
+
const permanent = isPermanentTaskFailure(err);
|
|
43082
|
+
const retryCount = task.retryCount ?? 0;
|
|
43083
|
+
const maxRetries = task.maxRetries ?? ctx.maxRetries;
|
|
43084
|
+
if (!permanent && retryCount < maxRetries) {
|
|
43085
|
+
await retryTask(ctx.taskDir, task.id, persistedMessage, ctx.log);
|
|
43086
|
+
workerLog(` Will retry (attempt ${retryCount + 1}/${maxRetries})`);
|
|
43087
|
+
} else {
|
|
43088
|
+
await failTask(ctx.taskDir, task.id, persistedMessage, ctx.log);
|
|
43089
|
+
await updateBranchMetadataOnFailure(ctx, task, persistedMessage);
|
|
43090
|
+
workerLogError(
|
|
43091
|
+
permanent ? " Permanently failed (non-retryable error)" : ` Permanently failed after ${maxRetries} retries`
|
|
43092
|
+
);
|
|
43093
|
+
}
|
|
43094
|
+
}
|
|
43095
|
+
processed++;
|
|
43096
|
+
}
|
|
43097
|
+
if (processed > 0) {
|
|
43098
|
+
const report = ctx.ensureStatusReport();
|
|
43099
|
+
report.lastTaskCycleAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
43100
|
+
await writeWorkerStatus(ctx.taskDir, report).catch(
|
|
43101
|
+
(writeErr) => workerLogError("Failed to write worker status:", getErrorMessage2(writeErr))
|
|
43102
|
+
);
|
|
43103
|
+
}
|
|
43104
|
+
}
|
|
43105
|
+
async function executeTaskWithTimeout(ctx, task) {
|
|
43106
|
+
const controller = new AbortController();
|
|
43107
|
+
const timer = setTimeout(() => controller.abort(), ctx.taskTimeoutMs);
|
|
43108
|
+
try {
|
|
43109
|
+
const work = ctx.executeTask(task, controller.signal);
|
|
43110
|
+
work.catch(() => {
|
|
43111
|
+
});
|
|
43112
|
+
const timedOut = new Promise((_3, reject) => {
|
|
43113
|
+
controller.signal.addEventListener(
|
|
43114
|
+
"abort",
|
|
43115
|
+
() => reject(new Error(`Task timed out after ${ctx.taskTimeoutMs}ms`)),
|
|
43116
|
+
{ once: true }
|
|
43117
|
+
);
|
|
43118
|
+
});
|
|
43119
|
+
return await Promise.race([work, timedOut]);
|
|
43120
|
+
} finally {
|
|
43121
|
+
clearTimeout(timer);
|
|
43122
|
+
}
|
|
43123
|
+
}
|
|
43124
|
+
async function executeTask(ctx, task, signal) {
|
|
43125
|
+
const { action, payload: payload2 } = task;
|
|
43126
|
+
switch (action) {
|
|
43127
|
+
case "push-branch": {
|
|
43128
|
+
const branch = requireString(payload2, "branch");
|
|
43129
|
+
await ctx.pushBranchToGitHub(branch);
|
|
43130
|
+
return { pushed: true };
|
|
43131
|
+
}
|
|
43132
|
+
case "push-and-create-pr": {
|
|
43133
|
+
const branch = requireString(payload2, "branch");
|
|
43134
|
+
await ctx.pushBranchToGitHub(branch);
|
|
43135
|
+
const pr = await ctx.octokit().pulls.create({
|
|
43136
|
+
owner: ctx.githubOwner,
|
|
43137
|
+
repo: ctx.githubRepo,
|
|
43138
|
+
head: branch,
|
|
43139
|
+
base: optionalString(payload2, "baseBranch", ctx.baseBranch),
|
|
43140
|
+
title: optionalString(payload2, "title", `Submit ${branch}`),
|
|
43141
|
+
body: optionalString(payload2, "body", ""),
|
|
43142
|
+
request: { signal }
|
|
43143
|
+
});
|
|
43144
|
+
workerLog(`Created PR #${pr.data.number} for ${branch}`);
|
|
43145
|
+
return { prUrl: pr.data.html_url, prNumber: pr.data.number };
|
|
43146
|
+
}
|
|
43147
|
+
case "push-and-update-pr": {
|
|
43148
|
+
const branch = requireString(payload2, "branch");
|
|
43149
|
+
const prNumber = requireNumber(payload2, "pullRequestNumber");
|
|
43150
|
+
await ctx.pushBranchToGitHub(branch);
|
|
43151
|
+
await ctx.octokit().pulls.update({
|
|
43152
|
+
owner: ctx.githubOwner,
|
|
43153
|
+
repo: ctx.githubRepo,
|
|
43154
|
+
pull_number: prNumber,
|
|
43155
|
+
title: optionalString(payload2, "title", `Submit ${branch}`),
|
|
43156
|
+
body: optionalString(payload2, "body", ""),
|
|
43157
|
+
request: { signal }
|
|
43158
|
+
});
|
|
43159
|
+
workerLog(`Updated PR #${prNumber} for ${branch}`);
|
|
43160
|
+
return { prNumber };
|
|
43161
|
+
}
|
|
43162
|
+
case "push-and-create-or-update-pr": {
|
|
43163
|
+
const branch = requireString(payload2, "branch");
|
|
43164
|
+
const base = optionalString(payload2, "baseBranch", ctx.baseBranch);
|
|
43165
|
+
if (sanitizeBranchName(branch) === ctx.sanitizedBaseBranch) {
|
|
43166
|
+
throw new PermanentTaskError(
|
|
43167
|
+
`Refusing to push-and-create-or-update-pr for "${branch}": it is the base branch -- submitting the base branch is never valid`
|
|
43168
|
+
);
|
|
43169
|
+
}
|
|
43170
|
+
await ctx.pushBranchToGitHub(branch);
|
|
43171
|
+
const result = await createOrUpdatePullRequest({
|
|
43172
|
+
octokit: ctx.octokit(),
|
|
43173
|
+
owner: ctx.githubOwner,
|
|
43174
|
+
repo: ctx.githubRepo,
|
|
43175
|
+
head: branch,
|
|
43176
|
+
base,
|
|
43177
|
+
title: optionalString(payload2, "title", `Submit ${branch}`),
|
|
43178
|
+
body: optionalString(payload2, "body", ""),
|
|
43179
|
+
// Content submits (api/github-sync.ts) set this; settings-branch
|
|
43180
|
+
// syncs (services.ts) deliberately don't.
|
|
43181
|
+
markReadyIfDraft: payload2.markReadyIfDraft === true,
|
|
43182
|
+
signal
|
|
43183
|
+
});
|
|
43184
|
+
workerLog(
|
|
43185
|
+
result.created ? `Created PR #${result.number} for ${branch}` : `Updated existing PR #${result.number} for ${branch}`
|
|
43186
|
+
);
|
|
43187
|
+
return { prUrl: result.url, prNumber: result.number };
|
|
43188
|
+
}
|
|
43189
|
+
case "convert-to-draft": {
|
|
43190
|
+
const draftPrNumber = requireNumber(payload2, "pullRequestNumber");
|
|
43191
|
+
const { data: pr } = await ctx.octokit().pulls.get({
|
|
43192
|
+
owner: ctx.githubOwner,
|
|
43193
|
+
repo: ctx.githubRepo,
|
|
43194
|
+
pull_number: draftPrNumber,
|
|
43195
|
+
request: { signal }
|
|
43196
|
+
});
|
|
43197
|
+
await ctx.octokit().graphql(
|
|
43198
|
+
`mutation($id: ID!) { convertPullRequestToDraft(input: { pullRequestId: $id }) { pullRequest { isDraft } } }`,
|
|
43199
|
+
{ id: pr.node_id, request: { signal } }
|
|
43200
|
+
);
|
|
43201
|
+
workerLog(`Converted PR #${draftPrNumber} to draft`);
|
|
43202
|
+
return { prNumber: draftPrNumber, draft: true };
|
|
43203
|
+
}
|
|
43204
|
+
case "close-pr": {
|
|
43205
|
+
const closePrNumber = requireNumber(payload2, "pullRequestNumber");
|
|
43206
|
+
await ctx.octokit().pulls.update({
|
|
43207
|
+
owner: ctx.githubOwner,
|
|
43208
|
+
repo: ctx.githubRepo,
|
|
43209
|
+
pull_number: closePrNumber,
|
|
43210
|
+
state: "closed",
|
|
43211
|
+
request: { signal }
|
|
43212
|
+
});
|
|
43213
|
+
return { closed: true };
|
|
43214
|
+
}
|
|
43215
|
+
case "delete-remote-branch": {
|
|
43216
|
+
const branch = requireString(payload2, "branch");
|
|
43217
|
+
await ctx.octokit().git.deleteRef({
|
|
43218
|
+
owner: ctx.githubOwner,
|
|
43219
|
+
repo: ctx.githubRepo,
|
|
43220
|
+
ref: `heads/${branch}`,
|
|
43221
|
+
request: { signal }
|
|
43222
|
+
});
|
|
43223
|
+
return { deleted: true };
|
|
43224
|
+
}
|
|
43225
|
+
default:
|
|
43226
|
+
throw new PermanentTaskError(`Unknown task action: ${action}`);
|
|
43227
|
+
}
|
|
43228
|
+
}
|
|
43229
|
+
async function updateBranchMetadata(ctx, task, result) {
|
|
43230
|
+
const branch = typeof task.payload.branch === "string" ? task.payload.branch : null;
|
|
43231
|
+
if (!branch) return;
|
|
43232
|
+
const branchPath = ctx.branchWorkspacePath(branch);
|
|
43233
|
+
try {
|
|
43234
|
+
await fs10.stat(branchPath);
|
|
43235
|
+
} catch {
|
|
43236
|
+
return;
|
|
43237
|
+
}
|
|
43238
|
+
try {
|
|
43239
|
+
const meta = getBranchMetadataFileManager(branchPath, ctx.contentBranchesPath);
|
|
43240
|
+
const updates = {
|
|
43241
|
+
name: branch,
|
|
43242
|
+
syncStatus: "synced",
|
|
43243
|
+
// save()'s merge only overwrites keys present in this update (see
|
|
43244
|
+
// BranchMetadataFileManager.save()'s spread order) -- a prior
|
|
43245
|
+
// syncFailureReason from an earlier failed attempt would otherwise
|
|
43246
|
+
// survive forever past this successful sync. Explicitly clear it,
|
|
43247
|
+
// same pattern as rebaseFailure's PR-W2 M2 clearing elsewhere.
|
|
43248
|
+
syncFailureReason: void 0
|
|
43249
|
+
};
|
|
43250
|
+
if (result.prUrl) updates.pullRequestUrl = result.prUrl;
|
|
43251
|
+
if (result.prNumber) {
|
|
43252
|
+
updates.pullRequestNumber = result.prNumber;
|
|
43253
|
+
const current = await BranchMetadataFileManager.loadOnly(branchPath);
|
|
43254
|
+
const terminal = current?.branch.status === "archived" || current?.branch.pullRequestState === "merged" || current?.branch.pullRequestState === "closed";
|
|
43255
|
+
if (!terminal) {
|
|
43256
|
+
updates.pullRequestState = "open";
|
|
43257
|
+
}
|
|
43258
|
+
}
|
|
43259
|
+
await meta.save({ branch: updates });
|
|
43260
|
+
} catch (err) {
|
|
43261
|
+
workerLogError(
|
|
43262
|
+
`Failed to update metadata for ${branch}:`,
|
|
43263
|
+
err instanceof Error ? err.message : err
|
|
43264
|
+
);
|
|
43265
|
+
}
|
|
43266
|
+
}
|
|
43267
|
+
async function updateBranchMetadataOnFailure(ctx, task, error2) {
|
|
43268
|
+
const branch = typeof task.payload.branch === "string" ? task.payload.branch : null;
|
|
43269
|
+
if (!branch) return;
|
|
43270
|
+
const branchPath = ctx.branchWorkspacePath(branch);
|
|
43271
|
+
try {
|
|
43272
|
+
await fs10.stat(branchPath);
|
|
43273
|
+
} catch {
|
|
43274
|
+
return;
|
|
43275
|
+
}
|
|
43276
|
+
try {
|
|
43277
|
+
const meta = getBranchMetadataFileManager(branchPath, ctx.contentBranchesPath);
|
|
43278
|
+
await meta.save({
|
|
43279
|
+
branch: { name: branch, syncStatus: "sync-failed", syncFailureReason: error2 }
|
|
43280
|
+
});
|
|
43281
|
+
} catch (err) {
|
|
43282
|
+
workerLogError(
|
|
43283
|
+
`Failed to update failure metadata for ${branch}:`,
|
|
43284
|
+
err instanceof Error ? err.message : err
|
|
43285
|
+
);
|
|
43286
|
+
}
|
|
43287
|
+
}
|
|
43288
|
+
async function pushBranchToGitHub(ctx, branch) {
|
|
43289
|
+
const git = simpleGit({
|
|
43290
|
+
baseDir: ctx.remoteGitPath,
|
|
43291
|
+
// DEP-H1: kill the git process if it produces no output for
|
|
43292
|
+
// taskTimeoutMs (network stall, credential prompt) instead of letting
|
|
43293
|
+
// it hang past the task timeout.
|
|
43294
|
+
timeout: { block: ctx.taskTimeoutMs }
|
|
43295
|
+
});
|
|
43296
|
+
git.env(gitNetworkChildEnv());
|
|
43297
|
+
const branchPath = ctx.branchWorkspacePath(branch);
|
|
43298
|
+
const metaFile = await BranchMetadataFileManager.loadOnly(branchPath).catch(() => null);
|
|
43299
|
+
const marker = metaFile?.branch.historyRewrittenFrom;
|
|
43300
|
+
const outgoingSha = await readPublishedSha(ctx, branch);
|
|
43301
|
+
try {
|
|
43302
|
+
if (marker) {
|
|
43303
|
+
await git.raw([
|
|
43304
|
+
"push",
|
|
43305
|
+
`--force-with-lease=${branch}:${marker}`,
|
|
43306
|
+
"--end-of-options",
|
|
43307
|
+
ctx.buildGitHubUrl(),
|
|
43308
|
+
`${branch}:${branch}`
|
|
43309
|
+
]);
|
|
43310
|
+
} else {
|
|
43311
|
+
await git.push(ctx.buildGitHubUrl(), branch);
|
|
43312
|
+
}
|
|
43313
|
+
} catch (err) {
|
|
43314
|
+
const message = getErrorMessage2(err);
|
|
43315
|
+
if (marker && isStaleLeaseRejection(message)) {
|
|
43316
|
+
try {
|
|
43317
|
+
await git.push(ctx.buildGitHubUrl(), branch);
|
|
43318
|
+
} catch (retryErr) {
|
|
43319
|
+
const retryMessage = getErrorMessage2(retryErr);
|
|
43320
|
+
if (isNonFastForwardRejection(retryMessage)) {
|
|
43321
|
+
throw new PermanentTaskError(
|
|
43322
|
+
`Push rejected for branch "${branch}": GitHub's tip is neither the commit this deployment last published nor an ancestor of what it is pushing, so the branch has genuinely diverged and nothing was overwritten. Something else moved it on GitHub -- a direct push, or another CanopyCMS deployment sharing this repository.`
|
|
43323
|
+
);
|
|
43324
|
+
}
|
|
43325
|
+
throw retryErr;
|
|
43326
|
+
}
|
|
43327
|
+
await clearHistoryRewrittenMarker(ctx, branchPath, branch);
|
|
43328
|
+
workerLog(`Pushed ${branch} to GitHub (GitHub had already moved past the rewritten commit)`);
|
|
43329
|
+
return;
|
|
43330
|
+
}
|
|
43331
|
+
if (isNonFastForwardRejection(message)) {
|
|
43332
|
+
throw new PermanentTaskError(
|
|
43333
|
+
`Push rejected for branch "${branch}": GitHub's tip is not what this deployment last published, so the branch has diverged and needs reconciling. Something else moved it on GitHub -- a direct push, or another CanopyCMS deployment sharing this repository.`
|
|
43334
|
+
);
|
|
43335
|
+
}
|
|
43336
|
+
throw err;
|
|
43337
|
+
}
|
|
43338
|
+
if (marker && outgoingSha && outgoingSha !== marker) {
|
|
43339
|
+
await clearHistoryRewrittenMarker(ctx, branchPath, branch);
|
|
43340
|
+
}
|
|
43341
|
+
workerLog(`Pushed ${branch} to GitHub`);
|
|
43342
|
+
}
|
|
43343
|
+
|
|
43344
|
+
// ../canopycms/src/worker/rebase.ts
|
|
43345
|
+
import fs11 from "node:fs/promises";
|
|
43346
|
+
import path15 from "node:path";
|
|
43347
|
+
init_normalize();
|
|
43348
|
+
init_types3();
|
|
43349
|
+
|
|
43350
|
+
// ../canopycms/src/utils/content-write-lock.ts
|
|
43351
|
+
import path14 from "node:path";
|
|
43352
|
+
init_logger();
|
|
43353
|
+
var META_DIR2 = ".canopy-meta";
|
|
43354
|
+
var CONTENT_WRITE_LOCK_NAME = "content-write.lock";
|
|
43355
|
+
function lockTargetDir(branchRoot) {
|
|
43356
|
+
return path14.join(path14.resolve(branchRoot), META_DIR2);
|
|
43357
|
+
}
|
|
43358
|
+
function tryAcquireContentWriteLock(branchRoot, onCompromised) {
|
|
43359
|
+
return tryAcquireProvisioningLock(
|
|
43360
|
+
lockTargetDir(branchRoot),
|
|
43361
|
+
CONTENT_WRITE_LOCK_NAME,
|
|
43362
|
+
onCompromised
|
|
43363
|
+
);
|
|
43364
|
+
}
|
|
43365
|
+
|
|
43366
|
+
// ../canopycms/src/worker/rebase.ts
|
|
43367
|
+
async function pollMergeState(ctx, branchDir, branchPath, metaFile) {
|
|
43368
|
+
const prNumber = metaFile?.branch.pullRequestNumber;
|
|
43369
|
+
if (!prNumber) return;
|
|
43370
|
+
const controller = new AbortController();
|
|
43371
|
+
const timer = setTimeout(() => controller.abort(), ctx.taskTimeoutMs);
|
|
43372
|
+
try {
|
|
43373
|
+
const { data: data2 } = await ctx.octokit().pulls.get({
|
|
43374
|
+
owner: ctx.githubOwner,
|
|
43375
|
+
repo: ctx.githubRepo,
|
|
43376
|
+
pull_number: prNumber,
|
|
43377
|
+
request: { signal: controller.signal }
|
|
43378
|
+
});
|
|
43379
|
+
if (data2.merged) {
|
|
43380
|
+
const meta2 = getBranchMetadataFileManager(branchPath, ctx.contentBranchesPath);
|
|
43381
|
+
await meta2.save({
|
|
43382
|
+
branch: buildMergedBranchUpdate(
|
|
43383
|
+
branchDir,
|
|
43384
|
+
data2.merged_at ? new Date(data2.merged_at) : void 0
|
|
43385
|
+
)
|
|
43386
|
+
});
|
|
43387
|
+
workerLog(` PR #${prNumber} for ${branchDir} is merged -> archived`);
|
|
43388
|
+
return;
|
|
43389
|
+
}
|
|
43390
|
+
const newState = data2.state === "closed" ? "closed" : "open";
|
|
43391
|
+
const currentMeta = await BranchMetadataFileManager.loadOnly(branchPath);
|
|
43392
|
+
if (currentMeta?.branch.pullRequestState === newState) return;
|
|
43393
|
+
const meta = getBranchMetadataFileManager(branchPath, ctx.contentBranchesPath);
|
|
43394
|
+
await meta.save({ branch: { name: branchDir, pullRequestState: newState } });
|
|
43395
|
+
workerLog(` PR #${prNumber} for ${branchDir}: pullRequestState -> ${newState}`);
|
|
43396
|
+
} catch (err) {
|
|
43397
|
+
workerLogWarn(` Failed to poll PR #${prNumber} for ${branchDir}: ${getErrorMessage2(err)}`);
|
|
43398
|
+
} finally {
|
|
43399
|
+
clearTimeout(timer);
|
|
43400
|
+
}
|
|
43401
|
+
}
|
|
43402
|
+
async function recordRebaseFailure(ctx, branchPath, branchDir, message) {
|
|
43403
|
+
const RECORD_REFRESH_MS = 60 * 60 * 1e3;
|
|
43404
|
+
const redactedMessage = redactCredentials(message);
|
|
43405
|
+
try {
|
|
43406
|
+
const existing = await BranchMetadataFileManager.loadOnly(branchPath);
|
|
43407
|
+
const prior = existing?.branch.rebaseFailure;
|
|
43408
|
+
const sameMessage = prior?.message === redactedMessage;
|
|
43409
|
+
const now = /* @__PURE__ */ new Date();
|
|
43410
|
+
if (sameMessage) {
|
|
43411
|
+
const lastAtMs = Date.parse(prior.lastAt);
|
|
43412
|
+
if (!Number.isNaN(lastAtMs) && now.getTime() - lastAtMs < RECORD_REFRESH_MS) {
|
|
43413
|
+
return;
|
|
43414
|
+
}
|
|
43415
|
+
}
|
|
43416
|
+
const nowIso = now.toISOString();
|
|
43417
|
+
const firstAt = sameMessage ? prior.firstAt : nowIso;
|
|
43418
|
+
const meta = getBranchMetadataFileManager(branchPath, ctx.contentBranchesPath);
|
|
43419
|
+
await meta.save({
|
|
43420
|
+
branch: {
|
|
43421
|
+
name: branchDir,
|
|
43422
|
+
rebaseFailure: { message: redactedMessage, firstAt, lastAt: nowIso }
|
|
43423
|
+
}
|
|
43424
|
+
});
|
|
43425
|
+
} catch (err) {
|
|
43426
|
+
workerLogWarn(` Failed to record rebase failure for ${branchDir}: ${getErrorMessage2(err)}`);
|
|
43427
|
+
}
|
|
43428
|
+
}
|
|
43429
|
+
var MAX_REBASE_ROUNDS = 50;
|
|
43430
|
+
async function runRebaseRounds(ctx, branchGit, branchDir, fetchedBaseTip, isLockCompromised) {
|
|
43431
|
+
const conflictedFiles = [];
|
|
43432
|
+
let nextAction = "start";
|
|
43433
|
+
let completed = false;
|
|
43434
|
+
let failureReason;
|
|
43435
|
+
for (let round = 0; round < MAX_REBASE_ROUNDS && !completed; round++) {
|
|
43436
|
+
if (isLockCompromised()) break;
|
|
43437
|
+
try {
|
|
43438
|
+
if (nextAction === "start") {
|
|
43439
|
+
await branchGit.rebase([fetchedBaseTip]);
|
|
43440
|
+
} else if (nextAction === "continue") {
|
|
43441
|
+
await branchGit.rebase(["--continue"]);
|
|
43442
|
+
} else {
|
|
43443
|
+
await branchGit.rebase(["--skip"]);
|
|
43444
|
+
}
|
|
43445
|
+
completed = true;
|
|
43446
|
+
} catch (rebaseErr) {
|
|
43447
|
+
nextAction = "continue";
|
|
43448
|
+
const st = await branchGit.status();
|
|
43449
|
+
if (st.conflicted.length > 0) {
|
|
43450
|
+
await ctx.afterConflictDetectedForTesting();
|
|
43451
|
+
const conflictKind = new Map(st.files.map((f5) => [f5.path, `${f5.index}${f5.working_dir}`]));
|
|
43452
|
+
let resolutionFailure;
|
|
43453
|
+
for (const file of st.conflicted) {
|
|
43454
|
+
const kind = conflictKind.get(file);
|
|
43455
|
+
try {
|
|
43456
|
+
if (kind === "UD") {
|
|
43457
|
+
await branchGit.raw(["rm", "-f", "--", file]);
|
|
43458
|
+
} else if (kind === "DU") {
|
|
43459
|
+
await branchGit.add(file);
|
|
43460
|
+
} else {
|
|
43461
|
+
await branchGit.raw(["checkout", "--theirs", file]);
|
|
43462
|
+
await branchGit.add(file);
|
|
43463
|
+
}
|
|
43464
|
+
} catch (resolveErr) {
|
|
43465
|
+
resolutionFailure = `failed to resolve conflicted file '${file}' (status ${kind ?? "??"}): ` + getErrorMessage2(resolveErr);
|
|
43466
|
+
break;
|
|
43467
|
+
}
|
|
43468
|
+
conflictedFiles.push(file);
|
|
43469
|
+
}
|
|
43470
|
+
if (resolutionFailure !== void 0) {
|
|
43471
|
+
failureReason = resolutionFailure;
|
|
43472
|
+
break;
|
|
43473
|
+
}
|
|
43474
|
+
} else {
|
|
43475
|
+
const msg = rebaseErr instanceof Error ? rebaseErr.message : "";
|
|
43476
|
+
if (msg.toLowerCase().includes("nothing to commit") || msg.toLowerCase().includes("apply --skip")) {
|
|
43477
|
+
nextAction = "skip";
|
|
43478
|
+
} else {
|
|
43479
|
+
workerLogWarn(` Unexpected rebase error in ${branchDir}: ${msg || "Unknown error"}`);
|
|
43480
|
+
failureReason = msg || "Unknown error";
|
|
43481
|
+
await branchGit.rebase(["--abort"]).catch(() => {
|
|
43482
|
+
});
|
|
43483
|
+
break;
|
|
43484
|
+
}
|
|
43485
|
+
}
|
|
43486
|
+
}
|
|
43487
|
+
}
|
|
43488
|
+
return { completed, conflictedFiles, failureReason };
|
|
43489
|
+
}
|
|
43490
|
+
function conflictFilesToContentIds(conflictedFiles, contentRoot) {
|
|
43491
|
+
const normalizedContentRoot = normalizeFilesystemPath(contentRoot);
|
|
43492
|
+
const conflictIds = [...new Set(conflictedFiles)].map((f5) => {
|
|
43493
|
+
const fileId = extractIdFromFilename(path15.basename(f5));
|
|
43494
|
+
if (fileId) return fileId;
|
|
43495
|
+
const parentDir = path15.basename(path15.dirname(f5));
|
|
43496
|
+
const dirId = extractIdFromFilename(parentDir);
|
|
43497
|
+
if (dirId) return dirId;
|
|
43498
|
+
const parentPath = normalizeFilesystemPath(path15.dirname(f5));
|
|
43499
|
+
if (path15.basename(f5) === ".collection.json" && parentPath === normalizedContentRoot) {
|
|
43500
|
+
return ROOT_COLLECTION_ID;
|
|
43501
|
+
}
|
|
43502
|
+
return null;
|
|
43503
|
+
}).filter((id) => id !== null);
|
|
43504
|
+
return [...new Set(conflictIds)];
|
|
43505
|
+
}
|
|
43506
|
+
async function carryForwardRewrittenHistory(ctx, args) {
|
|
43507
|
+
const { branchPath, branchDir, branchRef, publishedSha, preRebaseHead } = args;
|
|
43508
|
+
if (publishedSha !== null) {
|
|
43509
|
+
if (publishedSha === preRebaseHead) {
|
|
43510
|
+
await markHistoryRewritten(ctx, branchPath, branchDir, publishedSha);
|
|
43511
|
+
if (await forcePublishToLocalRemote(ctx, branchPath, branchRef, publishedSha)) {
|
|
43512
|
+
await enqueueGitHubPush(ctx, branchRef);
|
|
43513
|
+
}
|
|
43514
|
+
} else {
|
|
43515
|
+
const divergence = `rebased locally, but remote.git holds ${publishedSha} for ${branchRef}, which this clone never had (a direct push to the branch?). Left untouched -- reconcile it before submitting again.`;
|
|
43516
|
+
workerLogWarn(` ${branchDir}: ${divergence}`);
|
|
43517
|
+
await recordRebaseFailure(ctx, branchPath, branchDir, divergence);
|
|
43518
|
+
}
|
|
43519
|
+
}
|
|
43520
|
+
}
|
|
43521
|
+
async function runRebaseCycle(ctx) {
|
|
43522
|
+
const rebased = [];
|
|
43523
|
+
const skippedDirty = [];
|
|
43524
|
+
const skippedLocked = [];
|
|
43525
|
+
const failed = [];
|
|
43526
|
+
let branchDirs;
|
|
43527
|
+
try {
|
|
43528
|
+
branchDirs = await fs11.readdir(ctx.contentBranchesPath);
|
|
43529
|
+
} catch {
|
|
43530
|
+
return { rebased, skippedDirty, skippedLocked, failed };
|
|
43531
|
+
}
|
|
43532
|
+
for (const branchDir of branchDirs) {
|
|
43533
|
+
if (branchDir.startsWith(".") || branchDir === "branches.json") {
|
|
43534
|
+
continue;
|
|
43535
|
+
}
|
|
43536
|
+
const branchPath = path15.join(ctx.contentBranchesPath, branchDir);
|
|
43537
|
+
const gitDir = path15.join(branchPath, ".git");
|
|
43538
|
+
try {
|
|
43539
|
+
const stat = await fs11.stat(gitDir);
|
|
43540
|
+
if (!stat.isDirectory()) {
|
|
43541
|
+
workerLog(` Skipping ${branchDir}: .git is not a directory`);
|
|
43542
|
+
continue;
|
|
43543
|
+
}
|
|
43544
|
+
} catch {
|
|
43545
|
+
workerLog(` Skipping ${branchDir}: no .git directory (not a branch workspace)`);
|
|
43546
|
+
continue;
|
|
43547
|
+
}
|
|
43548
|
+
if (branchDir === ctx.sanitizedBaseBranch) {
|
|
43549
|
+
workerLog(` Skipping ${branchDir}: base branch (refreshed separately)`);
|
|
43550
|
+
continue;
|
|
43551
|
+
}
|
|
43552
|
+
const outcome = await rebaseOneBranch(ctx, branchDir, branchPath);
|
|
43553
|
+
if (outcome.kind === "rebased") rebased.push(branchDir);
|
|
43554
|
+
else if (outcome.kind === "skippedDirty") skippedDirty.push(branchDir);
|
|
43555
|
+
else if (outcome.kind === "skippedLocked") skippedLocked.push(branchDir);
|
|
43556
|
+
else if (outcome.kind === "failed") {
|
|
43557
|
+
if (outcome.rebased) rebased.push(branchDir);
|
|
43558
|
+
failed.push({ branch: branchDir, error: outcome.error });
|
|
43559
|
+
}
|
|
43560
|
+
}
|
|
43561
|
+
return { rebased, skippedDirty, skippedLocked, failed };
|
|
43562
|
+
}
|
|
43563
|
+
async function rebaseOneBranch(ctx, branchDir, branchPath) {
|
|
43564
|
+
let didRebase = false;
|
|
43565
|
+
try {
|
|
43566
|
+
const metaFile = await BranchMetadataFileManager.loadOnly(branchPath);
|
|
43567
|
+
const branchStatus2 = metaFile?.branch.status;
|
|
43568
|
+
if (branchStatus2 === "submitted" || branchStatus2 === "approved") {
|
|
43569
|
+
workerLog(` Skipping ${branchDir} (${branchStatus2})`);
|
|
43570
|
+
await pollMergeState(ctx, branchDir, branchPath, metaFile);
|
|
43571
|
+
return { kind: "none" };
|
|
43572
|
+
}
|
|
43573
|
+
if (branchStatus2 === "archived") {
|
|
43574
|
+
workerLog(` Skipping ${branchDir} (${branchStatus2})`);
|
|
43575
|
+
return { kind: "none" };
|
|
43576
|
+
}
|
|
43577
|
+
const branchGit = simpleGit({
|
|
43578
|
+
baseDir: branchPath,
|
|
43579
|
+
// Keep git non-interactive during rebase/merge so it never blocks on an editor.
|
|
43580
|
+
// simple-git >=3.32 blocks setting core.editor unless explicitly opted in; the
|
|
43581
|
+
// value here is a hardcoded literal ("true", the shell no-op), not user input,
|
|
43582
|
+
// so enabling allowUnsafeEditor carries no injection risk.
|
|
43583
|
+
config: ["core.editor=true"],
|
|
43584
|
+
unsafe: { allowUnsafeEditor: true }
|
|
43585
|
+
});
|
|
43586
|
+
let releaseContentLock;
|
|
43587
|
+
let contentLockCompromised = false;
|
|
43588
|
+
try {
|
|
43589
|
+
releaseContentLock = await tryAcquireContentWriteLock(branchPath, (lockErr) => {
|
|
43590
|
+
contentLockCompromised = true;
|
|
43591
|
+
workerLogWarn(
|
|
43592
|
+
` Content-write lock compromised mid-rebase for ${branchDir}: ${getErrorMessage2(lockErr)}`
|
|
43593
|
+
);
|
|
43594
|
+
});
|
|
43595
|
+
} catch (lockErr) {
|
|
43596
|
+
if (isNodeError(lockErr) && lockErr.code === "ELOCKED") {
|
|
43597
|
+
workerLog(` Skipping ${branchDir}: content write in progress (retrying next cycle)`);
|
|
43598
|
+
return { kind: "skippedLocked" };
|
|
43599
|
+
}
|
|
43600
|
+
throw lockErr;
|
|
43601
|
+
}
|
|
43602
|
+
try {
|
|
43603
|
+
if (await isRebaseInProgress(branchPath)) {
|
|
43604
|
+
const preAbort = await branchGit.status().catch(() => null);
|
|
43605
|
+
const collateral = (preAbort?.files ?? []).filter((f5) => !preAbort?.conflicted.includes(f5.path)).filter((f5) => f5.working_dir !== " " && f5.working_dir !== "?").map((f5) => f5.path);
|
|
43606
|
+
if (collateral.length > 0) {
|
|
43607
|
+
workerLogWarn(
|
|
43608
|
+
` ${branchDir}: aborting the interrupted rebase will DISCARD working-tree changes to ${collateral.length} file(s) saved while the worker was down: ${collateral.join(", ")}`
|
|
43609
|
+
);
|
|
43610
|
+
}
|
|
43611
|
+
workerLogWarn(
|
|
43612
|
+
` ${branchDir}: found an interrupted rebase (this worker's own abandoned work) -- aborting it to recover the branch`
|
|
43613
|
+
);
|
|
43614
|
+
try {
|
|
43615
|
+
await branchGit.rebase(["--abort"]);
|
|
43616
|
+
} catch (abortErr) {
|
|
43617
|
+
const reason = redactCredentials(
|
|
43618
|
+
`could not abort interrupted rebase: ${getErrorMessage2(abortErr)}`
|
|
43619
|
+
);
|
|
43620
|
+
workerLogWarn(` Skipping ${branchDir}: ${reason}`);
|
|
43621
|
+
await recordRebaseFailure(ctx, branchPath, branchDir, reason);
|
|
43622
|
+
return { kind: "failed", error: reason };
|
|
43623
|
+
}
|
|
43624
|
+
}
|
|
43625
|
+
const dirtyCheck = await branchGit.status();
|
|
43626
|
+
if (dirtyCheck.files.length > 0) {
|
|
43627
|
+
workerLog(` Skipping ${branchDir}: has uncommitted changes`);
|
|
43628
|
+
return { kind: "skippedDirty" };
|
|
43629
|
+
}
|
|
43630
|
+
const branchRef = (await branchGit.revparse(["--abbrev-ref", "HEAD"])).trim();
|
|
43631
|
+
const canPublish = branchRef.length > 0 && branchRef !== "HEAD";
|
|
43632
|
+
if (canPublish && metaFile?.branch.historyRewrittenFrom) {
|
|
43633
|
+
await reconcilePendingRewrite(ctx, {
|
|
43634
|
+
branchPath,
|
|
43635
|
+
branchDir,
|
|
43636
|
+
branchRef,
|
|
43637
|
+
headSha: (await branchGit.revparse(["HEAD"])).trim(),
|
|
43638
|
+
marker: metaFile.branch.historyRewrittenFrom
|
|
43639
|
+
});
|
|
43640
|
+
}
|
|
43641
|
+
await branchGit.fetch("origin", ctx.baseBranch);
|
|
43642
|
+
const fetchedBaseTip = (await branchGit.revparse(["FETCH_HEAD"])).trim();
|
|
43643
|
+
const behindCount = parseInt(
|
|
43644
|
+
(await branchGit.raw(["rev-list", "--count", `HEAD..${fetchedBaseTip}`])).trim(),
|
|
43645
|
+
10
|
|
43646
|
+
);
|
|
43647
|
+
const meta = getBranchMetadataFileManager(branchPath, ctx.contentBranchesPath);
|
|
43648
|
+
if (behindCount === 0) {
|
|
43649
|
+
const currentMeta = await BranchMetadataFileManager.loadOnly(branchPath);
|
|
43650
|
+
const conflictStatus = currentMeta?.branch.conflictStatus;
|
|
43651
|
+
const conflictFiles = currentMeta?.branch.conflictFiles;
|
|
43652
|
+
const conflictAlreadyClean = (conflictStatus === void 0 || conflictStatus === "clean") && (conflictFiles === void 0 || conflictFiles.length === 0);
|
|
43653
|
+
const alreadyClean = conflictAlreadyClean && currentMeta?.branch.rebaseFailure === void 0;
|
|
43654
|
+
if (alreadyClean) {
|
|
43655
|
+
return { kind: "none" };
|
|
43656
|
+
}
|
|
43657
|
+
await meta.save({
|
|
43658
|
+
branch: {
|
|
43659
|
+
name: branchDir,
|
|
43660
|
+
conflictStatus: "clean",
|
|
43661
|
+
conflictFiles: [],
|
|
43662
|
+
rebaseFailure: void 0
|
|
43663
|
+
}
|
|
43664
|
+
});
|
|
43665
|
+
return { kind: "none" };
|
|
43666
|
+
}
|
|
43667
|
+
workerLog(`Rebasing ${branchDir} (${behindCount} commits behind)...`);
|
|
43668
|
+
const preRebaseHead = (await branchGit.revparse(["HEAD"])).trim();
|
|
43669
|
+
const publishedSha = canPublish ? await readPublishedSha(ctx, branchRef) : null;
|
|
43670
|
+
const { completed, conflictedFiles, failureReason } = await runRebaseRounds(
|
|
43671
|
+
ctx,
|
|
43672
|
+
branchGit,
|
|
43673
|
+
branchDir,
|
|
43674
|
+
fetchedBaseTip,
|
|
43675
|
+
() => contentLockCompromised
|
|
43676
|
+
);
|
|
43677
|
+
if (completed) await ctx.afterRebaseCompletedForTesting();
|
|
43678
|
+
if (contentLockCompromised && !completed) {
|
|
43679
|
+
workerLogWarn(
|
|
43680
|
+
` Skipping ${branchDir}: content-write lock was compromised mid-rebase (retrying next cycle)`
|
|
43681
|
+
);
|
|
43682
|
+
await branchGit.rebase(["--abort"]).catch(() => {
|
|
43683
|
+
});
|
|
43684
|
+
return { kind: "skippedLocked" };
|
|
43685
|
+
}
|
|
43686
|
+
if (contentLockCompromised) {
|
|
43687
|
+
workerLogWarn(
|
|
43688
|
+
` Content-write lock for ${branchDir} was compromised, but its rebase had already completed -- finishing the sync (history is rewritten; skipping now would strand the history-rewrite marker and wedge the branch)`
|
|
43689
|
+
);
|
|
43690
|
+
}
|
|
43691
|
+
if (!completed) {
|
|
43692
|
+
workerLogWarn(
|
|
43693
|
+
failureReason !== void 0 ? ` Rebase of ${branchDir} aborted due to unexpected error: ${failureReason}` : ` Rebase of ${branchDir} did not complete within ${MAX_REBASE_ROUNDS} rounds, aborting`
|
|
43694
|
+
);
|
|
43695
|
+
await branchGit.rebase(["--abort"]).catch(() => {
|
|
43696
|
+
});
|
|
43697
|
+
const rebaseFailureMessage = failureReason ?? `did not complete within ${MAX_REBASE_ROUNDS} rounds`;
|
|
43698
|
+
const redactedRebaseFailureMessage = redactCredentials(rebaseFailureMessage);
|
|
43699
|
+
await recordRebaseFailure(ctx, branchPath, branchDir, redactedRebaseFailureMessage);
|
|
43700
|
+
return { kind: "failed", error: redactedRebaseFailureMessage };
|
|
43701
|
+
}
|
|
43702
|
+
await invalidateBranchContentCaches(branchPath);
|
|
43703
|
+
const conflictIdsDeduped = conflictFilesToContentIds(conflictedFiles, ctx.contentRoot);
|
|
43704
|
+
const hadConflicts = conflictIdsDeduped.length > 0;
|
|
43705
|
+
workerLog(
|
|
43706
|
+
hadConflicts ? ` Rebased ${branchDir} (kept branch version for ${conflictIdsDeduped.length} conflicting file(s))` : ` Rebased ${branchDir} successfully`
|
|
43707
|
+
);
|
|
43708
|
+
await meta.save({
|
|
43709
|
+
branch: {
|
|
43710
|
+
name: branchDir,
|
|
43711
|
+
conflictStatus: hadConflicts ? "conflicts-detected" : "clean",
|
|
43712
|
+
conflictFiles: conflictIdsDeduped,
|
|
43713
|
+
// PR-W2: the cycle completed successfully -- clear any prior
|
|
43714
|
+
// failure record regardless of conflict outcome.
|
|
43715
|
+
rebaseFailure: void 0
|
|
43716
|
+
}
|
|
43717
|
+
});
|
|
43718
|
+
didRebase = true;
|
|
43719
|
+
await carryForwardRewrittenHistory(ctx, {
|
|
43720
|
+
branchPath,
|
|
43721
|
+
branchDir,
|
|
43722
|
+
branchRef,
|
|
43723
|
+
publishedSha,
|
|
43724
|
+
preRebaseHead
|
|
43725
|
+
});
|
|
43726
|
+
return { kind: "rebased" };
|
|
43727
|
+
} finally {
|
|
43728
|
+
try {
|
|
43729
|
+
if (await isRebaseInProgress(branchPath)) {
|
|
43730
|
+
workerLogWarn(
|
|
43731
|
+
` ${branchDir}: rebase still in progress on exit -- aborting so the clone is not left wedged`
|
|
43732
|
+
);
|
|
43733
|
+
await branchGit.rebase(["--abort"]);
|
|
43734
|
+
}
|
|
43735
|
+
} catch (abortErr) {
|
|
43736
|
+
workerLogWarn(
|
|
43737
|
+
` Failed to abort in-progress rebase for ${branchDir}: ${getErrorMessage2(abortErr)}`
|
|
43738
|
+
);
|
|
43739
|
+
}
|
|
43740
|
+
await releaseContentLock?.().catch((releaseErr) => {
|
|
43741
|
+
workerLogWarn(
|
|
43742
|
+
` Failed to release content-write lock for ${branchDir}: ${getErrorMessage2(releaseErr)}`
|
|
43743
|
+
);
|
|
43744
|
+
});
|
|
43745
|
+
}
|
|
43746
|
+
} catch (err) {
|
|
43747
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
43748
|
+
workerLogWarn(` Failed to sync ${branchDir}: ${message}`);
|
|
43749
|
+
const redactedMessage = redactCredentials(message);
|
|
43750
|
+
await recordRebaseFailure(ctx, branchPath, branchDir, redactedMessage);
|
|
43751
|
+
return { kind: "failed", error: redactedMessage, rebased: didRebase };
|
|
43752
|
+
}
|
|
43753
|
+
}
|
|
43754
|
+
|
|
43755
|
+
// ../canopycms/src/worker/git-sync.ts
|
|
43756
|
+
import fs12 from "node:fs/promises";
|
|
43757
|
+
import path16 from "node:path";
|
|
43758
|
+
var TRASH_RETENTION_MS = 30 * 24 * 60 * 6e4;
|
|
43759
|
+
var TRASH_DIR_STAMP_RE = /-(\d{8}T\d{6}Z)$/;
|
|
43760
|
+
function parseTrashStamp(stamp) {
|
|
43761
|
+
const match2 = /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z$/.exec(stamp);
|
|
43762
|
+
if (!match2) return null;
|
|
43763
|
+
const [, year2, month, day, hour, minute, second] = match2;
|
|
43764
|
+
const date2 = /* @__PURE__ */ new Date(`${year2}-${month}-${day}T${hour}:${minute}:${second}.000Z`);
|
|
43765
|
+
return Number.isNaN(date2.getTime()) ? null : date2;
|
|
43766
|
+
}
|
|
43767
|
+
async function pushSettingsBranches(ctx, git, trackedNames) {
|
|
43768
|
+
try {
|
|
43769
|
+
const settingsBranch = ctx.ensureSettingsBranch();
|
|
43770
|
+
const branches = await git.branch();
|
|
43771
|
+
const settingsBranches = branches.all.filter(
|
|
43772
|
+
(b6) => b6.startsWith(RESERVED_SETTINGS_BRANCH_PREFIX)
|
|
43773
|
+
);
|
|
43774
|
+
const foreign = settingsBranches.filter((b6) => b6 !== settingsBranch);
|
|
43775
|
+
if (foreign.length > 0) {
|
|
43776
|
+
workerLogWarn(
|
|
43777
|
+
`Found settings branch(es) not owned by this deployment (${settingsBranch}): ${foreign.join(", ")}. Another CanopyCMS deployment may share this GitHub repo. Not pushing them.`
|
|
43778
|
+
);
|
|
43779
|
+
}
|
|
43780
|
+
const ownBranchMissing = !branches.all.includes(settingsBranch);
|
|
43781
|
+
const strandedLocal = foreign.filter((b6) => !trackedNames.has(b6));
|
|
43782
|
+
if (strandedLocal.length > 0) {
|
|
43783
|
+
workerLogWarn(
|
|
43784
|
+
ownBranchMissing ? `Settings branch mismatch: this worker owns "${settingsBranch}", which does not exist in remote.git, while ${strandedLocal.join(", ")} exist(s) here and has never been pushed to GitHub. The API and this worker disagree about deploymentName, so settings changes are NOT reaching GitHub. Set CANOPYCMS_DEPLOYMENT_NAME (or settingsBranch) on this worker to match what the API resolves.` : `Settings branch(es) ${strandedLocal.join(", ")} exist in remote.git but not on GitHub, and are not owned by this deployment (${settingsBranch}) -- nothing will ever push them onward. Check that deploymentName matches across this deployment's API and worker.`
|
|
43785
|
+
);
|
|
43786
|
+
}
|
|
43787
|
+
if (ownBranchMissing) {
|
|
43788
|
+
return;
|
|
43789
|
+
}
|
|
43790
|
+
try {
|
|
43791
|
+
await git.push(ctx.buildGitHubUrl(), settingsBranch);
|
|
43792
|
+
workerLog(`Pushed settings branch ${settingsBranch} to GitHub`);
|
|
43793
|
+
} catch (err) {
|
|
43794
|
+
const message = getErrorMessage2(err);
|
|
43795
|
+
if (isNonFastForwardRejection(message)) {
|
|
43796
|
+
workerLogWarn(
|
|
43797
|
+
`Settings push for ${settingsBranch} was rejected (non-fast-forward): another CanopyCMS deployment appears to own this settings branch on GitHub. Settings from that deployment will NOT be overwritten; this deployment's local settings changes were not pushed. Rename this deployment's settings branch (config.settingsBranch or deploymentName) to resolve the collision.`
|
|
43798
|
+
);
|
|
43799
|
+
} else {
|
|
43800
|
+
workerLogWarn(`Settings push for ${settingsBranch}:`, message);
|
|
43801
|
+
}
|
|
43802
|
+
}
|
|
43803
|
+
} catch (err) {
|
|
43804
|
+
workerLogWarn(
|
|
43805
|
+
"Failed to list branches for settings push:",
|
|
43806
|
+
err instanceof Error ? err.message : err
|
|
43807
|
+
);
|
|
43808
|
+
}
|
|
43809
|
+
}
|
|
43810
|
+
async function reconcileTrackedBranches(ctx, git) {
|
|
43811
|
+
const GIT_ZERO_OID = "0000000000000000000000000000000000000000";
|
|
43812
|
+
const created = [];
|
|
43813
|
+
const fastForwarded = [];
|
|
43814
|
+
const ahead = [];
|
|
43815
|
+
const diverged = [];
|
|
43816
|
+
const rewritten = [];
|
|
43817
|
+
const raw = await git.raw([
|
|
43818
|
+
"for-each-ref",
|
|
43819
|
+
"--format=%(refname) %(objectname)",
|
|
43820
|
+
"refs/heads/",
|
|
43821
|
+
GITHUB_TRACKING_REF_PREFIX
|
|
43822
|
+
]);
|
|
43823
|
+
const heads = /* @__PURE__ */ new Map();
|
|
43824
|
+
const tracked = /* @__PURE__ */ new Map();
|
|
43825
|
+
for (const line of raw.split("\n")) {
|
|
43826
|
+
const trimmed2 = line.trim();
|
|
43827
|
+
if (!trimmed2) continue;
|
|
43828
|
+
const [refname, sha] = trimmed2.split(" ");
|
|
43829
|
+
if (refname.startsWith("refs/heads/")) {
|
|
43830
|
+
heads.set(refname.slice("refs/heads/".length), sha);
|
|
43831
|
+
} else if (refname.startsWith(GITHUB_TRACKING_REF_PREFIX)) {
|
|
43832
|
+
tracked.set(refname.slice(GITHUB_TRACKING_REF_PREFIX.length), sha);
|
|
43833
|
+
}
|
|
43834
|
+
}
|
|
43835
|
+
for (const [name, trackedSha] of tracked) {
|
|
43836
|
+
const localSha = heads.get(name);
|
|
43837
|
+
const localRef = `refs/heads/${name}`;
|
|
43838
|
+
if (!localSha) {
|
|
43839
|
+
try {
|
|
43840
|
+
await git.raw(["update-ref", localRef, trackedSha, GIT_ZERO_OID]);
|
|
43841
|
+
created.push(name);
|
|
43842
|
+
} catch (err) {
|
|
43843
|
+
workerLogWarn(
|
|
43844
|
+
` Tracked-branch reconcile: failed to create local ref for ${name} (concurrent update?): ${getErrorMessage2(err)}`
|
|
43845
|
+
);
|
|
43846
|
+
}
|
|
43847
|
+
continue;
|
|
43848
|
+
}
|
|
43849
|
+
if (localSha === trackedSha) continue;
|
|
43850
|
+
try {
|
|
43851
|
+
const counts = (await git.raw(["rev-list", "--left-right", "--count", `${localSha}...${trackedSha}`])).trim();
|
|
43852
|
+
const [leftStr, rightStr] = counts.split(/\s+/);
|
|
43853
|
+
const localAheadCount = parseInt(leftStr, 10);
|
|
43854
|
+
const localBehindCount = parseInt(rightStr, 10);
|
|
43855
|
+
if (!Number.isInteger(localAheadCount) || !Number.isInteger(localBehindCount)) {
|
|
43856
|
+
workerLogWarn(
|
|
43857
|
+
` Tracked-branch reconcile: skipping ${name}: unparseable rev-list output ${JSON.stringify(counts)}`
|
|
43858
|
+
);
|
|
43859
|
+
continue;
|
|
43860
|
+
}
|
|
43861
|
+
if (localAheadCount === 0 && localBehindCount > 0) {
|
|
43862
|
+
try {
|
|
43863
|
+
await git.raw(["update-ref", localRef, trackedSha, localSha]);
|
|
43864
|
+
fastForwarded.push(name);
|
|
43865
|
+
} catch (err) {
|
|
43866
|
+
workerLogWarn(
|
|
43867
|
+
` Tracked-branch reconcile: failed to fast-forward ${name} (concurrent update?): ${getErrorMessage2(err)}`
|
|
43868
|
+
);
|
|
43869
|
+
}
|
|
43870
|
+
} else if (localBehindCount === 0 && localAheadCount > 0) {
|
|
43871
|
+
ahead.push(name);
|
|
43872
|
+
} else if (await hasPendingHistoryRewrite(ctx, name)) {
|
|
43873
|
+
rewritten.push(name);
|
|
43874
|
+
} else {
|
|
43875
|
+
diverged.push(name);
|
|
43876
|
+
}
|
|
43877
|
+
} catch (err) {
|
|
43878
|
+
workerLogWarn(
|
|
43879
|
+
` Tracked-branch reconcile: skipping ${name} (unreadable ref or object?): ${getErrorMessage2(err)}`
|
|
43880
|
+
);
|
|
43881
|
+
continue;
|
|
43882
|
+
}
|
|
43883
|
+
}
|
|
43884
|
+
if (diverged.length > 0) {
|
|
43885
|
+
workerLogWarn(
|
|
43886
|
+
` Tracked-branch reconcile: ${diverged.length} branch(es) diverged from GitHub and were left untouched: ${diverged.join(", ")}`
|
|
43887
|
+
);
|
|
43888
|
+
}
|
|
43889
|
+
if (rewritten.length > 0) {
|
|
43890
|
+
workerLog(
|
|
43891
|
+
` Tracked-branch reconcile: ${rewritten.length} branch(es) rebased locally with the GitHub push still pending: ${rewritten.join(", ")}`
|
|
43892
|
+
);
|
|
43893
|
+
}
|
|
43894
|
+
return {
|
|
43895
|
+
summary: { created, fastForwarded, ahead, diverged, rewritten },
|
|
43896
|
+
trackedNames: new Set(tracked.keys())
|
|
43897
|
+
};
|
|
43898
|
+
}
|
|
43899
|
+
async function syncGit(ctx) {
|
|
43900
|
+
if (!ctx.isRunning()) return;
|
|
43901
|
+
workerLog("Syncing git...");
|
|
43902
|
+
const cycleStartedAt = Date.now();
|
|
43903
|
+
const git = simpleGit({
|
|
43904
|
+
baseDir: ctx.remoteGitPath,
|
|
43905
|
+
// DEP-H1: a hung fetch/push would stall the sync loop forever
|
|
43906
|
+
// (scheduleLoop only reschedules after completion). The block timeout
|
|
43907
|
+
// is inactivity-based, so a slow-but-flowing transfer is unaffected.
|
|
43908
|
+
timeout: { block: ctx.taskTimeoutMs }
|
|
43909
|
+
});
|
|
43910
|
+
git.env(gitNetworkChildEnv());
|
|
43911
|
+
try {
|
|
43912
|
+
await git.raw([
|
|
43913
|
+
"fetch",
|
|
43914
|
+
ctx.buildGitHubUrl(),
|
|
43915
|
+
"--prune",
|
|
43916
|
+
`+refs/heads/*:${GITHUB_TRACKING_REF_PREFIX}*`
|
|
43917
|
+
]);
|
|
43918
|
+
workerLog("Fetched from GitHub");
|
|
43919
|
+
const { summary: trackedSummary, trackedNames } = await reconcileTrackedBranches(ctx, git);
|
|
43920
|
+
await pushSettingsBranches(ctx, git, trackedNames);
|
|
43921
|
+
await refreshBaseBranchWorkspace(ctx);
|
|
43922
|
+
const rebaseSummary = await runRebaseCycle(ctx);
|
|
43923
|
+
await cleanupOldTasks(ctx.taskDir, void 0, ctx.log);
|
|
43924
|
+
const trashRemoved = await cleanupTrashedBranchDirs(ctx);
|
|
43925
|
+
if (trashRemoved > 0) {
|
|
43926
|
+
workerLog(`Removed ${trashRemoved} expired trashed branch dir(s)`);
|
|
43927
|
+
}
|
|
43928
|
+
const report = ctx.ensureStatusReport();
|
|
43929
|
+
report.lastGitSyncAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
43930
|
+
delete report.lastGitSyncError;
|
|
43931
|
+
report.lastGitSync = {
|
|
43932
|
+
durationMs: Date.now() - cycleStartedAt,
|
|
43933
|
+
rebased: rebaseSummary.rebased,
|
|
43934
|
+
skippedDirty: rebaseSummary.skippedDirty,
|
|
43935
|
+
skippedLocked: rebaseSummary.skippedLocked,
|
|
43936
|
+
failed: rebaseSummary.failed,
|
|
43937
|
+
tracked: trackedSummary
|
|
43938
|
+
};
|
|
43939
|
+
await writeWorkerStatus(ctx.taskDir, report).catch(
|
|
43940
|
+
(writeErr) => workerLogError("Failed to write worker status:", getErrorMessage2(writeErr))
|
|
43941
|
+
);
|
|
43942
|
+
} catch (err) {
|
|
43943
|
+
const report = ctx.ensureStatusReport();
|
|
43944
|
+
report.lastGitSyncError = {
|
|
43945
|
+
message: redactCredentials(getErrorMessage2(err)),
|
|
43946
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
43947
|
+
};
|
|
43948
|
+
await writeWorkerStatus(ctx.taskDir, report).catch(
|
|
43949
|
+
(writeErr) => workerLogError("Failed to write worker status:", getErrorMessage2(writeErr))
|
|
43950
|
+
);
|
|
43951
|
+
throw err;
|
|
43952
|
+
}
|
|
43953
|
+
}
|
|
43954
|
+
async function cleanupTrashedBranchDirs(ctx) {
|
|
43955
|
+
let entries;
|
|
43956
|
+
try {
|
|
43957
|
+
entries = await fs12.readdir(ctx.contentBranchesPath);
|
|
43958
|
+
} catch (err) {
|
|
43959
|
+
if (isNodeError(err) && err.code === "ENOENT") return 0;
|
|
43960
|
+
throw err;
|
|
43961
|
+
}
|
|
43962
|
+
const now = Date.now();
|
|
43963
|
+
let removed = 0;
|
|
43964
|
+
let loggedUnparseable = false;
|
|
43965
|
+
for (const name of entries) {
|
|
43966
|
+
if (!name.startsWith(".trash-")) continue;
|
|
43967
|
+
const stampMatch = TRASH_DIR_STAMP_RE.exec(name);
|
|
43968
|
+
const stampDate = stampMatch ? parseTrashStamp(stampMatch[1]) : null;
|
|
43969
|
+
if (!stampDate) {
|
|
43970
|
+
if (!loggedUnparseable) {
|
|
43971
|
+
workerLog(`CanopyCMS: Skipping trash dir with unparseable stamp: ${name}`);
|
|
43972
|
+
loggedUnparseable = true;
|
|
43973
|
+
}
|
|
43974
|
+
continue;
|
|
43975
|
+
}
|
|
43976
|
+
if (now - stampDate.getTime() < TRASH_RETENTION_MS) continue;
|
|
43977
|
+
try {
|
|
43978
|
+
await fs12.rm(path16.join(ctx.contentBranchesPath, name), {
|
|
43979
|
+
recursive: true,
|
|
43980
|
+
force: true,
|
|
43981
|
+
maxRetries: 3,
|
|
43982
|
+
retryDelay: 100
|
|
43983
|
+
});
|
|
43984
|
+
removed++;
|
|
43985
|
+
} catch (err) {
|
|
43986
|
+
workerLogError(
|
|
43987
|
+
`CanopyCMS: Failed to remove trashed branch dir ${name}:`,
|
|
43988
|
+
getErrorMessage2(err)
|
|
43989
|
+
);
|
|
43990
|
+
}
|
|
43991
|
+
}
|
|
43992
|
+
return removed;
|
|
43993
|
+
}
|
|
43994
|
+
async function refreshBaseBranchWorkspace(ctx) {
|
|
43995
|
+
const basePath32 = path16.join(ctx.contentBranchesPath, ctx.sanitizedBaseBranch);
|
|
43996
|
+
const gitDir = path16.join(basePath32, ".git");
|
|
43997
|
+
try {
|
|
43998
|
+
let gitDirStat;
|
|
43999
|
+
try {
|
|
44000
|
+
gitDirStat = await fs12.stat(gitDir);
|
|
44001
|
+
} catch {
|
|
44002
|
+
gitDirStat = null;
|
|
44003
|
+
}
|
|
44004
|
+
if (!gitDirStat || !gitDirStat.isDirectory()) {
|
|
44005
|
+
workerLog(`Base branch workspace (${ctx.baseBranch}): not yet provisioned, skipping refresh`);
|
|
44006
|
+
return;
|
|
44007
|
+
}
|
|
44008
|
+
const baseGit = simpleGit({
|
|
44009
|
+
baseDir: basePath32,
|
|
44010
|
+
// Keep git non-interactive during the merge so it never blocks on an
|
|
44011
|
+
// editor. simple-git >=3.32 blocks setting core.editor unless
|
|
44012
|
+
// explicitly opted in; the value here is a hardcoded literal
|
|
44013
|
+
// ("true", the shell no-op), not user input, so enabling
|
|
44014
|
+
// allowUnsafeEditor carries no injection risk (same as the rebase
|
|
44015
|
+
// loop's git config below).
|
|
44016
|
+
config: ["core.editor=true"],
|
|
44017
|
+
unsafe: { allowUnsafeEditor: true },
|
|
44018
|
+
// DEP-H1: a hung fetch/merge against this EFS-backed clone would
|
|
44019
|
+
// stall the sync loop forever (scheduleLoop only reschedules after
|
|
44020
|
+
// completion). The block timeout is inactivity-based, so a
|
|
44021
|
+
// slow-but-flowing transfer is unaffected -- same as syncGit()'s
|
|
44022
|
+
// remote handle.
|
|
44023
|
+
timeout: { block: ctx.taskTimeoutMs }
|
|
44024
|
+
});
|
|
44025
|
+
const status = await baseGit.status();
|
|
44026
|
+
const trackedDirty = status.files.filter((f5) => f5.index !== "?" || f5.working_dir !== "?");
|
|
44027
|
+
if (trackedDirty.length > 0) {
|
|
44028
|
+
workerLogError(
|
|
44029
|
+
`Base branch workspace (${ctx.baseBranch}) has uncommitted changes -- skipping refresh. Dirty files: ${trackedDirty.map((f5) => f5.path).join(", ")}`
|
|
44030
|
+
);
|
|
44031
|
+
return;
|
|
44032
|
+
}
|
|
44033
|
+
await baseGit.fetch("origin", ctx.baseBranch);
|
|
44034
|
+
const fetchedTip = (await baseGit.revparse(["FETCH_HEAD"])).trim();
|
|
44035
|
+
const behindCount = parseInt(
|
|
44036
|
+
(await baseGit.raw(["rev-list", "--count", `HEAD..${fetchedTip}`])).trim(),
|
|
44037
|
+
10
|
|
44038
|
+
);
|
|
44039
|
+
if (behindCount > 0) {
|
|
44040
|
+
try {
|
|
44041
|
+
await baseGit.merge(["--ff-only", fetchedTip]);
|
|
44042
|
+
} catch (err) {
|
|
44043
|
+
workerLogError(
|
|
44044
|
+
`Base branch workspace (${ctx.baseBranch}) failed to fast-forward (diverged local history?): ${getErrorMessage2(err)}`
|
|
44045
|
+
);
|
|
44046
|
+
return;
|
|
44047
|
+
}
|
|
44048
|
+
await invalidateBranchContentCaches(basePath32);
|
|
44049
|
+
}
|
|
44050
|
+
const currentMeta = await BranchMetadataFileManager.loadOnly(basePath32);
|
|
44051
|
+
const conflictStatus = currentMeta?.branch.conflictStatus;
|
|
44052
|
+
const conflictFiles = currentMeta?.branch.conflictFiles;
|
|
44053
|
+
const alreadyClean = (conflictStatus === void 0 || conflictStatus === "clean") && (conflictFiles === void 0 || conflictFiles.length === 0);
|
|
44054
|
+
if (!alreadyClean) {
|
|
44055
|
+
const meta = getBranchMetadataFileManager(basePath32, ctx.contentBranchesPath);
|
|
44056
|
+
await meta.save({
|
|
44057
|
+
branch: { name: ctx.baseBranch, conflictStatus: "clean", conflictFiles: [] }
|
|
44058
|
+
});
|
|
44059
|
+
}
|
|
44060
|
+
workerLog(
|
|
44061
|
+
behindCount > 0 ? `Base branch workspace (${ctx.baseBranch}): fast-forwarded ${behindCount} commit(s)` : `Base branch workspace (${ctx.baseBranch}): up to date`
|
|
44062
|
+
);
|
|
44063
|
+
} catch (err) {
|
|
44064
|
+
workerLogError(
|
|
44065
|
+
`Base branch workspace (${ctx.baseBranch}) refresh failed: ${getErrorMessage2(err)}`
|
|
44066
|
+
);
|
|
44067
|
+
}
|
|
44068
|
+
}
|
|
44069
|
+
|
|
44070
|
+
// ../canopycms/src/worker/cms-worker.ts
|
|
44071
|
+
var DEFAULT_TASK_TIMEOUT = 6e4;
|
|
44072
|
+
var DEFAULT_MAX_RETRIES2 = 3;
|
|
44073
|
+
var DEFAULT_LOCK_STALE_MS = 6e4;
|
|
42976
44074
|
var CmsWorker = class {
|
|
42977
44075
|
constructor(config) {
|
|
42978
44076
|
this.config = config;
|
|
@@ -42982,15 +44080,15 @@ var CmsWorker = class {
|
|
|
42982
44080
|
this.releaseLockFn = null;
|
|
42983
44081
|
this.log = cmsTaskQueueLogger;
|
|
42984
44082
|
this.octokit = createCanopyOctokit({ auth: config.githubToken });
|
|
42985
|
-
this.taskDir =
|
|
42986
|
-
this.remoteGitPath =
|
|
42987
|
-
this.contentBranchesPath =
|
|
44083
|
+
this.taskDir = path17.join(config.workspacePath, ".tasks");
|
|
44084
|
+
this.remoteGitPath = path17.join(config.workspacePath, "remote.git");
|
|
44085
|
+
this.contentBranchesPath = path17.join(config.workspacePath, "content-branches");
|
|
42988
44086
|
this.baseBranch = config.baseBranch ?? "main";
|
|
42989
44087
|
this.sanitizedBaseBranch = sanitizeBranchName(this.baseBranch);
|
|
42990
44088
|
this.maxTasksPerCycle = config.maxTasksPerCycle ?? 10;
|
|
42991
44089
|
this.taskTimeoutMs = config.taskTimeoutMs ?? DEFAULT_TASK_TIMEOUT;
|
|
42992
44090
|
this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES2;
|
|
42993
|
-
this.lockFilePath =
|
|
44091
|
+
this.lockFilePath = path17.join(config.workspacePath, ".tasks", ".worker-lock");
|
|
42994
44092
|
this.lockStaleMs = config.lockStaleMs ?? DEFAULT_LOCK_STALE_MS;
|
|
42995
44093
|
this.contentRoot = config.contentRoot ?? "content";
|
|
42996
44094
|
}
|
|
@@ -43043,6 +44141,46 @@ var CmsWorker = class {
|
|
|
43043
44141
|
}
|
|
43044
44142
|
return this.settingsBranchResolved;
|
|
43045
44143
|
}
|
|
44144
|
+
/**
|
|
44145
|
+
* Build the {@link WorkerContext} handed to the extracted clusters
|
|
44146
|
+
* (task-runner.ts, git-sync.ts, rebase.ts, history-rewrite.ts).
|
|
44147
|
+
*
|
|
44148
|
+
* Built FRESH on every call rather than once in the constructor, and the
|
|
44149
|
+
* instance-backed members are functions rather than copied values. Both
|
|
44150
|
+
* choices exist for the same reason: the test suite drives this class by
|
|
44151
|
+
* replacing `octokit` and `buildGitHubUrl` ON THE INSTANCE, by setting
|
|
44152
|
+
* `running` directly, and by subclassing to override the two rebase test
|
|
44153
|
+
* hooks. A context that captured any of those at construction time would hand
|
|
44154
|
+
* the extracted code the pre-test value -- which for `buildGitHubUrl` means a
|
|
44155
|
+
* test's push going to github.com for real instead of its local fixture repo.
|
|
44156
|
+
* See WorkerContext's doc comment for the full list.
|
|
44157
|
+
*/
|
|
44158
|
+
ctx() {
|
|
44159
|
+
return {
|
|
44160
|
+
githubOwner: this.config.githubOwner,
|
|
44161
|
+
githubRepo: this.config.githubRepo,
|
|
44162
|
+
baseBranch: this.baseBranch,
|
|
44163
|
+
sanitizedBaseBranch: this.sanitizedBaseBranch,
|
|
44164
|
+
taskDir: this.taskDir,
|
|
44165
|
+
remoteGitPath: this.remoteGitPath,
|
|
44166
|
+
contentBranchesPath: this.contentBranchesPath,
|
|
44167
|
+
contentRoot: this.contentRoot,
|
|
44168
|
+
taskTimeoutMs: this.taskTimeoutMs,
|
|
44169
|
+
maxTasksPerCycle: this.maxTasksPerCycle,
|
|
44170
|
+
maxRetries: this.maxRetries,
|
|
44171
|
+
log: this.log,
|
|
44172
|
+
octokit: () => this.octokit,
|
|
44173
|
+
buildGitHubUrl: () => this.buildGitHubUrl(),
|
|
44174
|
+
branchWorkspacePath: (branchRefName) => this.branchWorkspacePath(branchRefName),
|
|
44175
|
+
executeTask: (task, signal) => this.executeTask(task, signal),
|
|
44176
|
+
pushBranchToGitHub: (branch) => this.pushBranchToGitHub(branch),
|
|
44177
|
+
isRunning: () => this.running,
|
|
44178
|
+
ensureStatusReport: () => this.ensureStatusReport(),
|
|
44179
|
+
ensureSettingsBranch: () => this.ensureSettingsBranch(),
|
|
44180
|
+
afterConflictDetectedForTesting: () => this.afterConflictDetectedForTesting(),
|
|
44181
|
+
afterRebaseCompletedForTesting: () => this.afterRebaseCompletedForTesting()
|
|
44182
|
+
};
|
|
44183
|
+
}
|
|
43046
44184
|
async start() {
|
|
43047
44185
|
this.running = true;
|
|
43048
44186
|
workerLog("CMS Worker starting...");
|
|
@@ -43053,7 +44191,7 @@ var CmsWorker = class {
|
|
|
43053
44191
|
await this.ensureRemoteGit();
|
|
43054
44192
|
const recovered = await recoverOrphanedTasks(
|
|
43055
44193
|
this.taskDir,
|
|
43056
|
-
this.
|
|
44194
|
+
orphanRecoveryMaxAgeMs(this.ctx()),
|
|
43057
44195
|
this.log
|
|
43058
44196
|
);
|
|
43059
44197
|
if (recovered > 0) {
|
|
@@ -43067,7 +44205,7 @@ var CmsWorker = class {
|
|
|
43067
44205
|
} catch (err) {
|
|
43068
44206
|
const report = this.ensureStatusReport();
|
|
43069
44207
|
report.lastFatalError = {
|
|
43070
|
-
// [
|
|
44208
|
+
// [REDACT] Persisted to worker-status.json and served to the
|
|
43071
44209
|
// browser by the admin panel -- must never carry the bot token
|
|
43072
44210
|
// that a poisoned/failed git URL (buildGitHubUrl()) can embed.
|
|
43073
44211
|
message: redactCredentials(getErrorMessage2(err)),
|
|
@@ -43141,7 +44279,7 @@ var CmsWorker = class {
|
|
|
43141
44279
|
* negligible, but a host with a badly wrong clock could misjudge liveness.
|
|
43142
44280
|
*/
|
|
43143
44281
|
async acquireLock() {
|
|
43144
|
-
await
|
|
44282
|
+
await fs13.mkdir(this.taskDir, { recursive: true });
|
|
43145
44283
|
try {
|
|
43146
44284
|
this.releaseLockFn = await import_proper_lockfile3.default.lock(this.taskDir, {
|
|
43147
44285
|
lockfilePath: this.lockFilePath,
|
|
@@ -43287,7 +44425,7 @@ var CmsWorker = class {
|
|
|
43287
44425
|
async ensureRemoteGit() {
|
|
43288
44426
|
let exists2;
|
|
43289
44427
|
try {
|
|
43290
|
-
await
|
|
44428
|
+
await fs13.stat(this.remoteGitPath);
|
|
43291
44429
|
exists2 = true;
|
|
43292
44430
|
} catch {
|
|
43293
44431
|
exists2 = false;
|
|
@@ -43307,286 +44445,52 @@ var CmsWorker = class {
|
|
|
43307
44445
|
workerLog("Initializing remote.git from GitHub...");
|
|
43308
44446
|
const git = simpleGit();
|
|
43309
44447
|
const stagingPath = `${this.remoteGitPath}.cloning`;
|
|
43310
|
-
await
|
|
44448
|
+
await fs13.rm(stagingPath, { recursive: true, force: true });
|
|
43311
44449
|
try {
|
|
43312
44450
|
await git.clone(this.buildGitHubUrl(), stagingPath, ["--bare"]);
|
|
43313
44451
|
await this.scrubPersistedRemote(stagingPath);
|
|
43314
44452
|
await this.verifyBaseBranchExists(stagingPath);
|
|
43315
44453
|
} catch (err) {
|
|
43316
44454
|
workerLogError(`remote.git clone failed: ${redactCredentials(getErrorMessage2(err))}`);
|
|
43317
|
-
await
|
|
44455
|
+
await fs13.rm(stagingPath, { recursive: true, force: true });
|
|
43318
44456
|
throw new Error(
|
|
43319
44457
|
`remote.git clone of ${this.config.githubOwner}/${this.config.githubRepo} failed or has no branch '${this.baseBranch}' - the GitHub repository may be empty, or the base branch may not exist. Push an initial commit to '${this.baseBranch}' and restart the worker (systemd will retry automatically).`
|
|
43320
44458
|
);
|
|
43321
44459
|
}
|
|
43322
|
-
await
|
|
44460
|
+
await fs13.rename(stagingPath, this.remoteGitPath);
|
|
43323
44461
|
workerLog("remote.git initialized");
|
|
43324
44462
|
}
|
|
43325
|
-
|
|
43326
|
-
|
|
43327
|
-
|
|
43328
|
-
|
|
43329
|
-
|
|
43330
|
-
|
|
43331
|
-
|
|
43332
|
-
|
|
43333
|
-
|
|
43334
|
-
|
|
43335
|
-
|
|
43336
|
-
|
|
43337
|
-
|
|
43338
|
-
|
|
43339
|
-
|
|
43340
|
-
|
|
43341
|
-
|
|
43342
|
-
|
|
43343
|
-
|
|
44463
|
+
// --- Task-queue cluster (worker/task-runner.ts) ------------------------
|
|
44464
|
+
//
|
|
44465
|
+
// READ THIS BEFORE STUBBING ANY DELEGATOR BELOW. They are not all the same,
|
|
44466
|
+
// and the difference is invisible from here.
|
|
44467
|
+
//
|
|
44468
|
+
// `processTaskQueue` is the public loop entry `scheduleLoop` drives, so it IS
|
|
44469
|
+
// on the production path. The other three exist ONLY so the existing test
|
|
44470
|
+
// files can reach the implementations through the instance; production never
|
|
44471
|
+
// dispatches through them, because the extracted modules call each other
|
|
44472
|
+
// directly at module level.
|
|
44473
|
+
//
|
|
44474
|
+
// The consequence: replacing one of these on an instance only affects
|
|
44475
|
+
// production behaviour if the context also routes it. Two do --
|
|
44476
|
+
// `executeTask` and `pushBranchToGitHub` are on `WorkerContext` precisely
|
|
44477
|
+
// because cms-worker.test.ts assigns over them and then asserts on the
|
|
44478
|
+
// replacement. `updateBranchMetadata` is NOT: the test calls it directly and
|
|
44479
|
+
// never stubs it, so a stub installed there today would be a silent no-op.
|
|
44480
|
+
// If you need to stub a method that isn't on the context, add it to
|
|
44481
|
+
// WorkerContext and route the internal caller through `ctx` -- do not assume
|
|
44482
|
+
// the delegator's existence means the stub takes effect.
|
|
43344
44483
|
async processTaskQueue() {
|
|
43345
|
-
|
|
43346
|
-
const recovered = await recoverOrphanedTasks(
|
|
43347
|
-
this.taskDir,
|
|
43348
|
-
this.orphanRecoveryMaxAgeMs(),
|
|
43349
|
-
this.log
|
|
43350
|
-
);
|
|
43351
|
-
if (recovered > 0) {
|
|
43352
|
-
workerLog(`Recovered ${recovered} orphaned task(s)`);
|
|
43353
|
-
}
|
|
43354
|
-
let processed = 0;
|
|
43355
|
-
let task;
|
|
43356
|
-
while (processed < this.maxTasksPerCycle && (task = await dequeueTask(this.taskDir, this.log)) !== null) {
|
|
43357
|
-
try {
|
|
43358
|
-
const result = await this.executeTaskWithTimeout(task);
|
|
43359
|
-
await completeTask(this.taskDir, task.id, result, this.log);
|
|
43360
|
-
await this.updateBranchMetadata(task, result);
|
|
43361
|
-
} catch (err) {
|
|
43362
|
-
const message = getErrorMessage2(err);
|
|
43363
|
-
workerLogError(`Task ${task.id} (${task.action}) failed:`, message);
|
|
43364
|
-
const persistedMessage = redactCredentials(message);
|
|
43365
|
-
const permanent = isPermanentTaskFailure(err);
|
|
43366
|
-
const retryCount = task.retryCount ?? 0;
|
|
43367
|
-
const maxRetries = task.maxRetries ?? this.maxRetries;
|
|
43368
|
-
if (!permanent && retryCount < maxRetries) {
|
|
43369
|
-
await retryTask(this.taskDir, task.id, persistedMessage, this.log);
|
|
43370
|
-
workerLog(` Will retry (attempt ${retryCount + 1}/${maxRetries})`);
|
|
43371
|
-
} else {
|
|
43372
|
-
await failTask(this.taskDir, task.id, persistedMessage, this.log);
|
|
43373
|
-
await this.updateBranchMetadataOnFailure(task, persistedMessage);
|
|
43374
|
-
workerLogError(
|
|
43375
|
-
permanent ? " Permanently failed (non-retryable error)" : ` Permanently failed after ${maxRetries} retries`
|
|
43376
|
-
);
|
|
43377
|
-
}
|
|
43378
|
-
}
|
|
43379
|
-
processed++;
|
|
43380
|
-
}
|
|
43381
|
-
if (processed > 0) {
|
|
43382
|
-
const report = this.ensureStatusReport();
|
|
43383
|
-
report.lastTaskCycleAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
43384
|
-
await writeWorkerStatus(this.taskDir, report).catch(
|
|
43385
|
-
(writeErr) => workerLogError("Failed to write worker status:", getErrorMessage2(writeErr))
|
|
43386
|
-
);
|
|
43387
|
-
}
|
|
43388
|
-
}
|
|
43389
|
-
/**
|
|
43390
|
-
* Execute a task bounded by taskTimeoutMs (DEP-H1). Two layers:
|
|
43391
|
-
* - An AbortSignal cancels Octokit HTTP calls promptly.
|
|
43392
|
-
* - A Promise.race rejects when the timeout fires, so work that cannot
|
|
43393
|
-
* observe the signal (git subprocesses via simple-git) still fails the
|
|
43394
|
-
* attempt and the worker moves on instead of stalling forever.
|
|
43395
|
-
* pushBranchToGitHub additionally kills stalled git processes via
|
|
43396
|
-
* simple-git's block timeout, so a hung push doesn't leak a process.
|
|
43397
|
-
*/
|
|
43398
|
-
async executeTaskWithTimeout(task) {
|
|
43399
|
-
const controller = new AbortController();
|
|
43400
|
-
const timer = setTimeout(() => controller.abort(), this.taskTimeoutMs);
|
|
43401
|
-
try {
|
|
43402
|
-
const work = this.executeTask(task, controller.signal);
|
|
43403
|
-
work.catch(() => {
|
|
43404
|
-
});
|
|
43405
|
-
const timedOut = new Promise((_3, reject) => {
|
|
43406
|
-
controller.signal.addEventListener(
|
|
43407
|
-
"abort",
|
|
43408
|
-
() => reject(new Error(`Task timed out after ${this.taskTimeoutMs}ms`)),
|
|
43409
|
-
{ once: true }
|
|
43410
|
-
);
|
|
43411
|
-
});
|
|
43412
|
-
return await Promise.race([work, timedOut]);
|
|
43413
|
-
} finally {
|
|
43414
|
-
clearTimeout(timer);
|
|
43415
|
-
}
|
|
44484
|
+
return processTaskQueue(this.ctx());
|
|
43416
44485
|
}
|
|
43417
44486
|
async executeTask(task, signal) {
|
|
43418
|
-
|
|
43419
|
-
switch (action) {
|
|
43420
|
-
case "push-branch": {
|
|
43421
|
-
const branch = requireString(payload2, "branch");
|
|
43422
|
-
await this.pushBranchToGitHub(branch);
|
|
43423
|
-
return { pushed: true };
|
|
43424
|
-
}
|
|
43425
|
-
case "push-and-create-pr": {
|
|
43426
|
-
const branch = requireString(payload2, "branch");
|
|
43427
|
-
await this.pushBranchToGitHub(branch);
|
|
43428
|
-
const pr = await this.octokit.pulls.create({
|
|
43429
|
-
owner: this.config.githubOwner,
|
|
43430
|
-
repo: this.config.githubRepo,
|
|
43431
|
-
head: branch,
|
|
43432
|
-
base: optionalString(payload2, "baseBranch", this.baseBranch),
|
|
43433
|
-
title: optionalString(payload2, "title", `Submit ${branch}`),
|
|
43434
|
-
body: optionalString(payload2, "body", ""),
|
|
43435
|
-
request: { signal }
|
|
43436
|
-
});
|
|
43437
|
-
workerLog(`Created PR #${pr.data.number} for ${branch}`);
|
|
43438
|
-
return { prUrl: pr.data.html_url, prNumber: pr.data.number };
|
|
43439
|
-
}
|
|
43440
|
-
case "push-and-update-pr": {
|
|
43441
|
-
const branch = requireString(payload2, "branch");
|
|
43442
|
-
const prNumber = requireNumber(payload2, "pullRequestNumber");
|
|
43443
|
-
await this.pushBranchToGitHub(branch);
|
|
43444
|
-
await this.octokit.pulls.update({
|
|
43445
|
-
owner: this.config.githubOwner,
|
|
43446
|
-
repo: this.config.githubRepo,
|
|
43447
|
-
pull_number: prNumber,
|
|
43448
|
-
title: optionalString(payload2, "title", `Submit ${branch}`),
|
|
43449
|
-
body: optionalString(payload2, "body", ""),
|
|
43450
|
-
request: { signal }
|
|
43451
|
-
});
|
|
43452
|
-
workerLog(`Updated PR #${prNumber} for ${branch}`);
|
|
43453
|
-
return { prNumber };
|
|
43454
|
-
}
|
|
43455
|
-
case "push-and-create-or-update-pr": {
|
|
43456
|
-
const branch = requireString(payload2, "branch");
|
|
43457
|
-
const base = optionalString(payload2, "baseBranch", this.baseBranch);
|
|
43458
|
-
if (sanitizeBranchName(branch) === this.sanitizedBaseBranch) {
|
|
43459
|
-
throw new PermanentTaskError(
|
|
43460
|
-
`Refusing to push-and-create-or-update-pr for "${branch}": it is the base branch -- submitting the base branch is never valid`
|
|
43461
|
-
);
|
|
43462
|
-
}
|
|
43463
|
-
await this.pushBranchToGitHub(branch);
|
|
43464
|
-
const result = await createOrUpdatePullRequest({
|
|
43465
|
-
octokit: this.octokit,
|
|
43466
|
-
owner: this.config.githubOwner,
|
|
43467
|
-
repo: this.config.githubRepo,
|
|
43468
|
-
head: branch,
|
|
43469
|
-
base,
|
|
43470
|
-
title: optionalString(payload2, "title", `Submit ${branch}`),
|
|
43471
|
-
body: optionalString(payload2, "body", ""),
|
|
43472
|
-
// Content submits (api/github-sync.ts) set this; settings-branch
|
|
43473
|
-
// syncs (services.ts) deliberately don't.
|
|
43474
|
-
markReadyIfDraft: payload2.markReadyIfDraft === true,
|
|
43475
|
-
signal
|
|
43476
|
-
});
|
|
43477
|
-
workerLog(
|
|
43478
|
-
result.created ? `Created PR #${result.number} for ${branch}` : `Updated existing PR #${result.number} for ${branch}`
|
|
43479
|
-
);
|
|
43480
|
-
return { prUrl: result.url, prNumber: result.number };
|
|
43481
|
-
}
|
|
43482
|
-
case "convert-to-draft": {
|
|
43483
|
-
const draftPrNumber = requireNumber(payload2, "pullRequestNumber");
|
|
43484
|
-
const { data: pr } = await this.octokit.pulls.get({
|
|
43485
|
-
owner: this.config.githubOwner,
|
|
43486
|
-
repo: this.config.githubRepo,
|
|
43487
|
-
pull_number: draftPrNumber,
|
|
43488
|
-
request: { signal }
|
|
43489
|
-
});
|
|
43490
|
-
await this.octokit.graphql(
|
|
43491
|
-
`mutation($id: ID!) { convertPullRequestToDraft(input: { pullRequestId: $id }) { pullRequest { isDraft } } }`,
|
|
43492
|
-
{ id: pr.node_id, request: { signal } }
|
|
43493
|
-
);
|
|
43494
|
-
workerLog(`Converted PR #${draftPrNumber} to draft`);
|
|
43495
|
-
return { prNumber: draftPrNumber, draft: true };
|
|
43496
|
-
}
|
|
43497
|
-
case "close-pr": {
|
|
43498
|
-
const closePrNumber = requireNumber(payload2, "pullRequestNumber");
|
|
43499
|
-
await this.octokit.pulls.update({
|
|
43500
|
-
owner: this.config.githubOwner,
|
|
43501
|
-
repo: this.config.githubRepo,
|
|
43502
|
-
pull_number: closePrNumber,
|
|
43503
|
-
state: "closed",
|
|
43504
|
-
request: { signal }
|
|
43505
|
-
});
|
|
43506
|
-
return { closed: true };
|
|
43507
|
-
}
|
|
43508
|
-
case "delete-remote-branch": {
|
|
43509
|
-
const branch = requireString(payload2, "branch");
|
|
43510
|
-
await this.octokit.git.deleteRef({
|
|
43511
|
-
owner: this.config.githubOwner,
|
|
43512
|
-
repo: this.config.githubRepo,
|
|
43513
|
-
ref: `heads/${branch}`,
|
|
43514
|
-
request: { signal }
|
|
43515
|
-
});
|
|
43516
|
-
return { deleted: true };
|
|
43517
|
-
}
|
|
43518
|
-
default:
|
|
43519
|
-
throw new PermanentTaskError(`Unknown task action: ${action}`);
|
|
43520
|
-
}
|
|
44487
|
+
return executeTask(this.ctx(), task, signal);
|
|
43521
44488
|
}
|
|
43522
|
-
/**
|
|
43523
|
-
* Update branch metadata after successful task completion.
|
|
43524
|
-
* Writes PR URL/number and sets syncStatus to 'synced'.
|
|
43525
|
-
*/
|
|
43526
44489
|
async updateBranchMetadata(task, result) {
|
|
43527
|
-
|
|
43528
|
-
if (!branch) return;
|
|
43529
|
-
const branchPath = this.branchWorkspacePath(branch);
|
|
43530
|
-
try {
|
|
43531
|
-
await fs9.stat(branchPath);
|
|
43532
|
-
} catch {
|
|
43533
|
-
return;
|
|
43534
|
-
}
|
|
43535
|
-
try {
|
|
43536
|
-
const meta = getBranchMetadataFileManager(branchPath, this.contentBranchesPath);
|
|
43537
|
-
const updates = {
|
|
43538
|
-
name: branch,
|
|
43539
|
-
syncStatus: "synced",
|
|
43540
|
-
// save()'s merge only overwrites keys present in this update (see
|
|
43541
|
-
// BranchMetadataFileManager.save()'s spread order) -- a prior
|
|
43542
|
-
// syncFailureReason from an earlier failed attempt would otherwise
|
|
43543
|
-
// survive forever past this successful sync. Explicitly clear it,
|
|
43544
|
-
// same pattern as rebaseFailure's PR-W2 M2 clearing elsewhere.
|
|
43545
|
-
syncFailureReason: void 0
|
|
43546
|
-
};
|
|
43547
|
-
if (result.prUrl) updates.pullRequestUrl = result.prUrl;
|
|
43548
|
-
if (result.prNumber) {
|
|
43549
|
-
updates.pullRequestNumber = result.prNumber;
|
|
43550
|
-
const current = await BranchMetadataFileManager.loadOnly(branchPath);
|
|
43551
|
-
const terminal = current?.branch.status === "archived" || current?.branch.pullRequestState === "merged" || current?.branch.pullRequestState === "closed";
|
|
43552
|
-
if (!terminal) {
|
|
43553
|
-
updates.pullRequestState = "open";
|
|
43554
|
-
}
|
|
43555
|
-
}
|
|
43556
|
-
await meta.save({ branch: updates });
|
|
43557
|
-
} catch (err) {
|
|
43558
|
-
workerLogError(
|
|
43559
|
-
`Failed to update metadata for ${branch}:`,
|
|
43560
|
-
err instanceof Error ? err.message : err
|
|
43561
|
-
);
|
|
43562
|
-
}
|
|
44490
|
+
return updateBranchMetadata(this.ctx(), task, result);
|
|
43563
44491
|
}
|
|
43564
|
-
|
|
43565
|
-
|
|
43566
|
-
* Sets syncStatus to 'sync-failed' and records `error` (already redacted
|
|
43567
|
-
* by the caller, processTaskQueue -- see [HIGH-1] there) as
|
|
43568
|
-
* syncFailureReason, so the editor can show WHY, not just that it failed.
|
|
43569
|
-
*/
|
|
43570
|
-
async updateBranchMetadataOnFailure(task, error2) {
|
|
43571
|
-
const branch = typeof task.payload.branch === "string" ? task.payload.branch : null;
|
|
43572
|
-
if (!branch) return;
|
|
43573
|
-
const branchPath = this.branchWorkspacePath(branch);
|
|
43574
|
-
try {
|
|
43575
|
-
await fs9.stat(branchPath);
|
|
43576
|
-
} catch {
|
|
43577
|
-
return;
|
|
43578
|
-
}
|
|
43579
|
-
try {
|
|
43580
|
-
const meta = getBranchMetadataFileManager(branchPath, this.contentBranchesPath);
|
|
43581
|
-
await meta.save({
|
|
43582
|
-
branch: { name: branch, syncStatus: "sync-failed", syncFailureReason: error2 }
|
|
43583
|
-
});
|
|
43584
|
-
} catch (err) {
|
|
43585
|
-
workerLogError(
|
|
43586
|
-
`Failed to update failure metadata for ${branch}:`,
|
|
43587
|
-
err instanceof Error ? err.message : err
|
|
43588
|
-
);
|
|
43589
|
-
}
|
|
44492
|
+
async pushBranchToGitHub(branch) {
|
|
44493
|
+
return pushBranchToGitHub(this.ctx(), branch);
|
|
43590
44494
|
}
|
|
43591
44495
|
buildGitHubUrl() {
|
|
43592
44496
|
return `https://x-access-token:${this.config.githubToken}@github.com/${this.config.githubOwner}/${this.config.githubRepo}.git`;
|
|
@@ -43608,732 +44512,7 @@ var CmsWorker = class {
|
|
|
43608
44512
|
* is sanitized.
|
|
43609
44513
|
*/
|
|
43610
44514
|
branchWorkspacePath(branchRefName) {
|
|
43611
|
-
return
|
|
43612
|
-
}
|
|
43613
|
-
/**
|
|
43614
|
-
* What `remote.git` currently holds for `branchRef`, or null when this
|
|
43615
|
-
* branch was never published there (never submitted) or the ref is
|
|
43616
|
-
* unreadable.
|
|
43617
|
-
*
|
|
43618
|
-
* Uses the same explicit `--git-dir` shape as verifyBaseBranchExists()
|
|
43619
|
-
* above: reading a bare repo that way does not depend on
|
|
43620
|
-
* `safe.bareRepository` being permissive, which is why prod code takes
|
|
43621
|
-
* this route rather than the config override the test-only `openBareRepo`
|
|
43622
|
-
* helper uses.
|
|
43623
|
-
*/
|
|
43624
|
-
async readPublishedSha(branchRef) {
|
|
43625
|
-
try {
|
|
43626
|
-
const out = await simpleGit().raw([
|
|
43627
|
-
"--git-dir",
|
|
43628
|
-
this.remoteGitPath,
|
|
43629
|
-
"rev-parse",
|
|
43630
|
-
"--verify",
|
|
43631
|
-
`refs/heads/${branchRef}`
|
|
43632
|
-
]);
|
|
43633
|
-
const sha = out.trim();
|
|
43634
|
-
return sha.length > 0 ? sha : null;
|
|
43635
|
-
} catch {
|
|
43636
|
-
return null;
|
|
43637
|
-
}
|
|
43638
|
-
}
|
|
43639
|
-
/**
|
|
43640
|
-
* Record that this worker rewrote `expectedSha` out of a branch's already
|
|
43641
|
-
* published history (see BranchMetadata.historyRewrittenFrom).
|
|
43642
|
-
*
|
|
43643
|
-
* Set-once: if a marker is already present it is LEFT ALONE. Across two
|
|
43644
|
-
* rebases before any GitHub push lands, GitHub still holds the commit the
|
|
43645
|
-
* FIRST rebase replaced, so advancing the marker would aim the lease at a
|
|
43646
|
-
* commit GitHub never had and permanently wedge the branch.
|
|
43647
|
-
*
|
|
43648
|
-
* Re-reads metadata rather than trusting the caller's loop-top snapshot:
|
|
43649
|
-
* the task loop runs concurrently with syncGit() (see scheduleLoop) and may
|
|
43650
|
-
* have cleared the marker while this branch was rebasing.
|
|
43651
|
-
*/
|
|
43652
|
-
async markHistoryRewritten(branchPath, branchDir, expectedSha) {
|
|
43653
|
-
const current = await BranchMetadataFileManager.loadOnly(branchPath);
|
|
43654
|
-
if (typeof current?.branch.historyRewrittenFrom === "string") return;
|
|
43655
|
-
const meta = getBranchMetadataFileManager(branchPath, this.contentBranchesPath);
|
|
43656
|
-
await meta.save({ branch: { name: branchDir, historyRewrittenFrom: expectedSha } });
|
|
43657
|
-
}
|
|
43658
|
-
/**
|
|
43659
|
-
* Clear the marker once GitHub is confirmed to hold the rewritten history.
|
|
43660
|
-
*
|
|
43661
|
-
* Best-effort only in the sense that a failure here destroys nothing: the
|
|
43662
|
-
* lease still refuses anything unexpected, and the plain-push fallback only
|
|
43663
|
-
* ever fast-forwards. It is NOT harmless. A marker that outlives its
|
|
43664
|
-
* episode can wedge the NEXT one -- if the base advances before the stale
|
|
43665
|
-
* marker is revisited, the queued push leases a commit GitHub has already
|
|
43666
|
-
* moved off, falls back to a plain push of a rebased (non-ancestor)
|
|
43667
|
-
* history, and fails permanently with a "something else moved it on
|
|
43668
|
-
* GitHub" diagnosis that is false: we did.
|
|
43669
|
-
*
|
|
43670
|
-
* Tracked, with the concurrent-clear race that can drop a marker
|
|
43671
|
-
* mid-arming, in
|
|
43672
|
-
* .claude/future-tasks/worker-history-rewrite-marker-races.md.
|
|
43673
|
-
*/
|
|
43674
|
-
async clearHistoryRewrittenMarker(branchPath, branchDir) {
|
|
43675
|
-
try {
|
|
43676
|
-
const meta = getBranchMetadataFileManager(branchPath, this.contentBranchesPath);
|
|
43677
|
-
await meta.save({ branch: { name: branchDir, historyRewrittenFrom: void 0 } });
|
|
43678
|
-
} catch (err) {
|
|
43679
|
-
workerLogWarn(
|
|
43680
|
-
` Failed to clear history-rewrite marker for ${branchDir}: ${getErrorMessage2(err)}`
|
|
43681
|
-
);
|
|
43682
|
-
}
|
|
43683
|
-
}
|
|
43684
|
-
/**
|
|
43685
|
-
* Publish a branch clone's rebased history into `remote.git`, replacing
|
|
43686
|
-
* EXACTLY `expectedSha` and nothing else.
|
|
43687
|
-
*
|
|
43688
|
-
* The lease is the entire safety argument. `--force-with-lease=<ref>:<sha>`
|
|
43689
|
-
* refuses unless `remote.git` still stands at `<sha>`, so this can only
|
|
43690
|
-
* ever undo the commit our own rebase rewrote away. Callers must never pass
|
|
43691
|
-
* "whatever remote.git currently holds" -- see the arming guard in
|
|
43692
|
-
* rebaseActiveBranches() for the interleaving where that would silently
|
|
43693
|
-
* delete a reviewer's direct push.
|
|
43694
|
-
*
|
|
43695
|
-
* Returns whether the push landed. A refused lease means a concurrent
|
|
43696
|
-
* Lambda push moved the ref; that is logged and retried by the self-heal
|
|
43697
|
-
* pass on a later cycle, never thrown.
|
|
43698
|
-
*/
|
|
43699
|
-
async forcePublishToLocalRemote(branchPath, branchRef, expectedSha) {
|
|
43700
|
-
const pushGit = simpleGit({ baseDir: branchPath, timeout: { block: this.taskTimeoutMs } });
|
|
43701
|
-
pushGit.env(gitChildEnv({}));
|
|
43702
|
-
try {
|
|
43703
|
-
await pushGit.raw([
|
|
43704
|
-
"push",
|
|
43705
|
-
`--force-with-lease=${branchRef}:${expectedSha}`,
|
|
43706
|
-
// Real flags must precede --end-of-options; everything after it is
|
|
43707
|
-
// positional (see GitManager.push() for the same guard).
|
|
43708
|
-
"--end-of-options",
|
|
43709
|
-
"origin",
|
|
43710
|
-
`${branchRef}:${branchRef}`
|
|
43711
|
-
]);
|
|
43712
|
-
workerLog(` Published rebased ${branchRef} into remote.git`);
|
|
43713
|
-
return true;
|
|
43714
|
-
} catch (err) {
|
|
43715
|
-
const message = redactCredentials(getErrorMessage2(err));
|
|
43716
|
-
workerLogWarn(
|
|
43717
|
-
isStaleLeaseRejection(message) ? ` Did not publish rebased ${branchRef} into remote.git: it moved since this cycle read it (concurrent submit?) -- retrying next cycle` : ` Failed to publish rebased ${branchRef} into remote.git: ${message}`
|
|
43718
|
-
);
|
|
43719
|
-
return false;
|
|
43720
|
-
}
|
|
43721
|
-
}
|
|
43722
|
-
/**
|
|
43723
|
-
* Queue the GitHub hop for a branch whose rewritten history now sits in
|
|
43724
|
-
* `remote.git`, so an open PR's head follows the rebase within a cycle
|
|
43725
|
-
* instead of waiting for the editor's next submit.
|
|
43726
|
-
*
|
|
43727
|
-
* Deliberately NOT skipped when a marker is already set: inferring "a task
|
|
43728
|
-
* must already be queued" from the marker starves this hop whenever a task
|
|
43729
|
-
* was lost, failed permanently, or was never written. Duplicate push tasks
|
|
43730
|
-
* are bounded by base-branch advances and are idempotent (a repeat push is
|
|
43731
|
-
* a no-op once GitHub holds the tip).
|
|
43732
|
-
*/
|
|
43733
|
-
async enqueueGitHubPush(branchRef) {
|
|
43734
|
-
try {
|
|
43735
|
-
for (const status of ["pending", "processing"]) {
|
|
43736
|
-
const inFlight = await listTasks(this.taskDir, status);
|
|
43737
|
-
if (inFlight.some((t6) => t6.action === "push-branch" && t6.payload.branch === branchRef)) {
|
|
43738
|
-
return;
|
|
43739
|
-
}
|
|
43740
|
-
}
|
|
43741
|
-
await enqueueTask(this.taskDir, { action: "push-branch", payload: { branch: branchRef } });
|
|
43742
|
-
workerLog(` Queued GitHub push for ${branchRef}`);
|
|
43743
|
-
} catch (err) {
|
|
43744
|
-
workerLogWarn(` Failed to queue GitHub push for ${branchRef}: ${getErrorMessage2(err)}`);
|
|
43745
|
-
}
|
|
43746
|
-
}
|
|
43747
|
-
/**
|
|
43748
|
-
* Complete a rewrite this worker started but did not finish: get the
|
|
43749
|
-
* rebased history into `remote.git` and queued for GitHub.
|
|
43750
|
-
*
|
|
43751
|
-
* Runs from the rebase loop for any branch carrying a marker, whether or
|
|
43752
|
-
* not it is behind base this cycle, so an interrupted publish converges
|
|
43753
|
-
* without waiting for the next base-branch advance.
|
|
43754
|
-
*
|
|
43755
|
-
* Always leases on the MARKER, never on remote.git's current tip -- the
|
|
43756
|
-
* marker is the one commit we know our own rebase replaced.
|
|
43757
|
-
*/
|
|
43758
|
-
async reconcilePendingRewrite(options) {
|
|
43759
|
-
const { branchPath, branchDir, branchRef, headSha, marker } = options;
|
|
43760
|
-
const publishedSha = await this.readPublishedSha(branchRef);
|
|
43761
|
-
if (publishedSha === null) {
|
|
43762
|
-
workerLogWarn(
|
|
43763
|
-
` ${branchDir}: a rewritten history is recorded but ${branchRef} is gone from remote.git -- nothing to publish`
|
|
43764
|
-
);
|
|
43765
|
-
return;
|
|
43766
|
-
}
|
|
43767
|
-
if (publishedSha === headSha) {
|
|
43768
|
-
await this.enqueueGitHubPush(branchRef);
|
|
43769
|
-
return;
|
|
43770
|
-
}
|
|
43771
|
-
if (publishedSha === marker) {
|
|
43772
|
-
if (await this.forcePublishToLocalRemote(branchPath, branchRef, marker)) {
|
|
43773
|
-
await this.enqueueGitHubPush(branchRef);
|
|
43774
|
-
}
|
|
43775
|
-
return;
|
|
43776
|
-
}
|
|
43777
|
-
workerLogWarn(
|
|
43778
|
-
` ${branchDir}: remote.git is at ${publishedSha} for ${branchRef}, which is neither the rebased tip nor the commit the rewrite replaced -- left untouched`
|
|
43779
|
-
);
|
|
43780
|
-
}
|
|
43781
|
-
async pushBranchToGitHub(branch) {
|
|
43782
|
-
const git = simpleGit({
|
|
43783
|
-
baseDir: this.remoteGitPath,
|
|
43784
|
-
// DEP-H1: kill the git process if it produces no output for
|
|
43785
|
-
// taskTimeoutMs (network stall, credential prompt) instead of letting
|
|
43786
|
-
// it hang past the task timeout.
|
|
43787
|
-
timeout: { block: this.taskTimeoutMs }
|
|
43788
|
-
});
|
|
43789
|
-
git.env(gitNetworkChildEnv());
|
|
43790
|
-
const branchPath = this.branchWorkspacePath(branch);
|
|
43791
|
-
const metaFile = await BranchMetadataFileManager.loadOnly(branchPath).catch(() => null);
|
|
43792
|
-
const marker = metaFile?.branch.historyRewrittenFrom;
|
|
43793
|
-
const outgoingSha = await this.readPublishedSha(branch);
|
|
43794
|
-
try {
|
|
43795
|
-
if (marker) {
|
|
43796
|
-
await git.raw([
|
|
43797
|
-
"push",
|
|
43798
|
-
`--force-with-lease=${branch}:${marker}`,
|
|
43799
|
-
"--end-of-options",
|
|
43800
|
-
this.buildGitHubUrl(),
|
|
43801
|
-
`${branch}:${branch}`
|
|
43802
|
-
]);
|
|
43803
|
-
} else {
|
|
43804
|
-
await git.push(this.buildGitHubUrl(), branch);
|
|
43805
|
-
}
|
|
43806
|
-
} catch (err) {
|
|
43807
|
-
const message = getErrorMessage2(err);
|
|
43808
|
-
if (marker && isStaleLeaseRejection(message)) {
|
|
43809
|
-
try {
|
|
43810
|
-
await git.push(this.buildGitHubUrl(), branch);
|
|
43811
|
-
} catch (retryErr) {
|
|
43812
|
-
const retryMessage = getErrorMessage2(retryErr);
|
|
43813
|
-
if (isNonFastForwardRejection(retryMessage)) {
|
|
43814
|
-
throw new PermanentTaskError(
|
|
43815
|
-
`Push rejected for branch "${branch}": GitHub's tip is neither the commit this deployment last published nor an ancestor of what it is pushing, so the branch has genuinely diverged and nothing was overwritten. Something else moved it on GitHub -- a direct push, or another CanopyCMS deployment sharing this repository.`
|
|
43816
|
-
);
|
|
43817
|
-
}
|
|
43818
|
-
throw retryErr;
|
|
43819
|
-
}
|
|
43820
|
-
await this.clearHistoryRewrittenMarker(branchPath, branch);
|
|
43821
|
-
workerLog(`Pushed ${branch} to GitHub (GitHub had already moved past the rewritten commit)`);
|
|
43822
|
-
return;
|
|
43823
|
-
}
|
|
43824
|
-
if (isNonFastForwardRejection(message)) {
|
|
43825
|
-
throw new PermanentTaskError(
|
|
43826
|
-
`Push rejected for branch "${branch}": GitHub's tip is not what this deployment last published, so the branch has diverged and needs reconciling. Something else moved it on GitHub -- a direct push, or another CanopyCMS deployment sharing this repository.`
|
|
43827
|
-
);
|
|
43828
|
-
}
|
|
43829
|
-
throw err;
|
|
43830
|
-
}
|
|
43831
|
-
if (marker && outgoingSha && outgoingSha !== marker) {
|
|
43832
|
-
await this.clearHistoryRewrittenMarker(branchPath, branch);
|
|
43833
|
-
}
|
|
43834
|
-
workerLog(`Pushed ${branch} to GitHub`);
|
|
43835
|
-
}
|
|
43836
|
-
/**
|
|
43837
|
-
* Push THIS deployment's own settings branch (`ensureSettingsBranch()`) from
|
|
43838
|
-
* remote.git to GitHub. Non-fatal: a no-op push for an up-to-date branch
|
|
43839
|
-
* just succeeds quietly.
|
|
43840
|
-
*
|
|
43841
|
-
* Deliberately narrowed to one branch — this used to push EVERY local
|
|
43842
|
-
* branch matching `canopycms-settings-*`. With the tracking-namespace fetch
|
|
43843
|
-
* fix (GITHUB_TRACKING_REF_PREFIX), `reconcileTrackedBranches` creates local
|
|
43844
|
-
* heads for branches that exist on GitHub, so ANOTHER deployment's settings
|
|
43845
|
-
* branch (sharing this same GitHub repo) can legitimately show up as a
|
|
43846
|
-
* local head here too. Pushing it would be this deployment shipping
|
|
43847
|
-
* settings state it doesn't own.
|
|
43848
|
-
*/
|
|
43849
|
-
async pushSettingsBranches(git, trackedNames) {
|
|
43850
|
-
try {
|
|
43851
|
-
const settingsBranch = this.ensureSettingsBranch();
|
|
43852
|
-
const branches = await git.branch();
|
|
43853
|
-
const settingsBranches = branches.all.filter(
|
|
43854
|
-
(b6) => b6.startsWith(RESERVED_SETTINGS_BRANCH_PREFIX)
|
|
43855
|
-
);
|
|
43856
|
-
const foreign = settingsBranches.filter((b6) => b6 !== settingsBranch);
|
|
43857
|
-
if (foreign.length > 0) {
|
|
43858
|
-
workerLogWarn(
|
|
43859
|
-
`Found settings branch(es) not owned by this deployment (${settingsBranch}): ${foreign.join(", ")}. Another CanopyCMS deployment may share this GitHub repo. Not pushing them.`
|
|
43860
|
-
);
|
|
43861
|
-
}
|
|
43862
|
-
const ownBranchMissing = !branches.all.includes(settingsBranch);
|
|
43863
|
-
const strandedLocal = foreign.filter((b6) => !trackedNames.has(b6));
|
|
43864
|
-
if (strandedLocal.length > 0) {
|
|
43865
|
-
workerLogWarn(
|
|
43866
|
-
ownBranchMissing ? `Settings branch mismatch: this worker owns "${settingsBranch}", which does not exist in remote.git, while ${strandedLocal.join(", ")} exist(s) here and has never been pushed to GitHub. The API and this worker disagree about deploymentName, so settings changes are NOT reaching GitHub. Set CANOPYCMS_DEPLOYMENT_NAME (or settingsBranch) on this worker to match what the API resolves.` : `Settings branch(es) ${strandedLocal.join(", ")} exist in remote.git but not on GitHub, and are not owned by this deployment (${settingsBranch}) -- nothing will ever push them onward. Check that deploymentName matches across this deployment's API and worker.`
|
|
43867
|
-
);
|
|
43868
|
-
}
|
|
43869
|
-
if (ownBranchMissing) {
|
|
43870
|
-
return;
|
|
43871
|
-
}
|
|
43872
|
-
try {
|
|
43873
|
-
await git.push(this.buildGitHubUrl(), settingsBranch);
|
|
43874
|
-
workerLog(`Pushed settings branch ${settingsBranch} to GitHub`);
|
|
43875
|
-
} catch (err) {
|
|
43876
|
-
const message = getErrorMessage2(err);
|
|
43877
|
-
if (isNonFastForwardRejection(message)) {
|
|
43878
|
-
workerLogWarn(
|
|
43879
|
-
`Settings push for ${settingsBranch} was rejected (non-fast-forward): another CanopyCMS deployment appears to own this settings branch on GitHub. Settings from that deployment will NOT be overwritten; this deployment's local settings changes were not pushed. Rename this deployment's settings branch (config.settingsBranch or deploymentName) to resolve the collision.`
|
|
43880
|
-
);
|
|
43881
|
-
} else {
|
|
43882
|
-
workerLogWarn(`Settings push for ${settingsBranch}:`, message);
|
|
43883
|
-
}
|
|
43884
|
-
}
|
|
43885
|
-
} catch (err) {
|
|
43886
|
-
workerLogWarn(
|
|
43887
|
-
"Failed to list branches for settings push:",
|
|
43888
|
-
err instanceof Error ? err.message : err
|
|
43889
|
-
);
|
|
43890
|
-
}
|
|
43891
|
-
}
|
|
43892
|
-
/**
|
|
43893
|
-
* Bring `refs/heads/*` in `remote.git` toward what was just fetched into
|
|
43894
|
-
* `GITHUB_TRACKING_REF_PREFIX` -- WITHOUT ever force-rewinding or deleting
|
|
43895
|
-
* a local head. This is the non-destructive replacement for what the old
|
|
43896
|
-
* `+refs/heads/*:refs/heads/*` fetch refspec used to do implicitly (and
|
|
43897
|
-
* destructively) as part of the fetch itself; see
|
|
43898
|
-
* `GITHUB_TRACKING_REF_PREFIX`'s doc comment for the two failure modes
|
|
43899
|
-
* that refspec caused.
|
|
43900
|
-
*
|
|
43901
|
-
* Per tracked branch:
|
|
43902
|
-
* - no local `refs/heads/<name>` yet -> create it at the tracked commit
|
|
43903
|
-
* (a branch created on GitHub, or by another deployment sharing this
|
|
43904
|
-
* GitHub repo, becomes visible locally).
|
|
43905
|
-
* - local is a strict ancestor of tracked (behind) -> fast-forward it.
|
|
43906
|
-
* - local === tracked -> nothing to do.
|
|
43907
|
-
* - tracked is a strict ancestor of local (ahead) -> LEAVE IT ALONE. This
|
|
43908
|
-
* is unpushed editor/settings work; the queued push task (or
|
|
43909
|
-
* pushSettingsBranches) ships it. This is exactly the branch state the
|
|
43910
|
-
* old refspec used to destroy.
|
|
43911
|
-
* - neither is an ancestor of the other (diverged) -> LEAVE IT ALONE and
|
|
43912
|
-
* count/log it. A real collision (e.g. another deployment moved the
|
|
43913
|
-
* same branch name on GitHub); the next push attempt will be rejected
|
|
43914
|
-
* non-fast-forward, which is the correct, visible outcome -- this
|
|
43915
|
-
* method must never silently pick a winner. The one expected, benign
|
|
43916
|
-
* form of this -- our own rebase loop having published a rewrite into
|
|
43917
|
-
* remote.git with the GitHub push still queued -- is split out into the
|
|
43918
|
-
* `rewritten` bucket so the collision warning stays meaningful.
|
|
43919
|
-
*
|
|
43920
|
-
* Never deletes a local head: a branch removed on GitHub simply stops
|
|
43921
|
-
* being tracked here; the local ref persists until removed through its
|
|
43922
|
-
* own explicit path (the sync loop must not be one of them).
|
|
43923
|
-
*
|
|
43924
|
-
* `remote.git` is bare, so there is no worktree to invalidate by moving
|
|
43925
|
-
* these refs -- unlike a non-bare repo, updating the ref that happens to
|
|
43926
|
-
* be "checked out" is a non-issue here.
|
|
43927
|
-
*
|
|
43928
|
-
* Concurrency: `remote.git` is bare and on EFS, and the Lambda pushes into
|
|
43929
|
-
* it concurrently (`GitManager.push()`'s `target:target` refspec) while
|
|
43930
|
-
* this runs. Every `update-ref` below passes the expected old value (the
|
|
43931
|
-
* all-zeros OID for "must not exist yet" on creation, the previously-read
|
|
43932
|
-
* SHA for the fast-forward case) so a concurrent Lambda write landing in
|
|
43933
|
-
* the gap between the read and the write loses the ref update instead of
|
|
43934
|
-
* being silently clobbered -- the branch is simply revisited next cycle.
|
|
43935
|
-
*/
|
|
43936
|
-
async reconcileTrackedBranches(git) {
|
|
43937
|
-
const GIT_ZERO_OID = "0000000000000000000000000000000000000000";
|
|
43938
|
-
const created = [];
|
|
43939
|
-
const fastForwarded = [];
|
|
43940
|
-
const ahead = [];
|
|
43941
|
-
const diverged = [];
|
|
43942
|
-
const rewritten = [];
|
|
43943
|
-
const raw = await git.raw([
|
|
43944
|
-
"for-each-ref",
|
|
43945
|
-
"--format=%(refname) %(objectname)",
|
|
43946
|
-
"refs/heads/",
|
|
43947
|
-
GITHUB_TRACKING_REF_PREFIX
|
|
43948
|
-
]);
|
|
43949
|
-
const heads = /* @__PURE__ */ new Map();
|
|
43950
|
-
const tracked = /* @__PURE__ */ new Map();
|
|
43951
|
-
for (const line of raw.split("\n")) {
|
|
43952
|
-
const trimmed2 = line.trim();
|
|
43953
|
-
if (!trimmed2) continue;
|
|
43954
|
-
const [refname, sha] = trimmed2.split(" ");
|
|
43955
|
-
if (refname.startsWith("refs/heads/")) {
|
|
43956
|
-
heads.set(refname.slice("refs/heads/".length), sha);
|
|
43957
|
-
} else if (refname.startsWith(GITHUB_TRACKING_REF_PREFIX)) {
|
|
43958
|
-
tracked.set(refname.slice(GITHUB_TRACKING_REF_PREFIX.length), sha);
|
|
43959
|
-
}
|
|
43960
|
-
}
|
|
43961
|
-
for (const [name, trackedSha] of tracked) {
|
|
43962
|
-
const localSha = heads.get(name);
|
|
43963
|
-
const localRef = `refs/heads/${name}`;
|
|
43964
|
-
if (!localSha) {
|
|
43965
|
-
try {
|
|
43966
|
-
await git.raw(["update-ref", localRef, trackedSha, GIT_ZERO_OID]);
|
|
43967
|
-
created.push(name);
|
|
43968
|
-
} catch (err) {
|
|
43969
|
-
workerLogWarn(
|
|
43970
|
-
` Tracked-branch reconcile: failed to create local ref for ${name} (concurrent update?): ${getErrorMessage2(err)}`
|
|
43971
|
-
);
|
|
43972
|
-
}
|
|
43973
|
-
continue;
|
|
43974
|
-
}
|
|
43975
|
-
if (localSha === trackedSha) continue;
|
|
43976
|
-
try {
|
|
43977
|
-
const counts = (await git.raw(["rev-list", "--left-right", "--count", `${localSha}...${trackedSha}`])).trim();
|
|
43978
|
-
const [leftStr, rightStr] = counts.split(/\s+/);
|
|
43979
|
-
const localAheadCount = parseInt(leftStr, 10);
|
|
43980
|
-
const localBehindCount = parseInt(rightStr, 10);
|
|
43981
|
-
if (!Number.isInteger(localAheadCount) || !Number.isInteger(localBehindCount)) {
|
|
43982
|
-
workerLogWarn(
|
|
43983
|
-
` Tracked-branch reconcile: skipping ${name}: unparseable rev-list output ${JSON.stringify(counts)}`
|
|
43984
|
-
);
|
|
43985
|
-
continue;
|
|
43986
|
-
}
|
|
43987
|
-
if (localAheadCount === 0 && localBehindCount > 0) {
|
|
43988
|
-
try {
|
|
43989
|
-
await git.raw(["update-ref", localRef, trackedSha, localSha]);
|
|
43990
|
-
fastForwarded.push(name);
|
|
43991
|
-
} catch (err) {
|
|
43992
|
-
workerLogWarn(
|
|
43993
|
-
` Tracked-branch reconcile: failed to fast-forward ${name} (concurrent update?): ${getErrorMessage2(err)}`
|
|
43994
|
-
);
|
|
43995
|
-
}
|
|
43996
|
-
} else if (localBehindCount === 0 && localAheadCount > 0) {
|
|
43997
|
-
ahead.push(name);
|
|
43998
|
-
} else if (await this.hasPendingHistoryRewrite(name)) {
|
|
43999
|
-
rewritten.push(name);
|
|
44000
|
-
} else {
|
|
44001
|
-
diverged.push(name);
|
|
44002
|
-
}
|
|
44003
|
-
} catch (err) {
|
|
44004
|
-
workerLogWarn(
|
|
44005
|
-
` Tracked-branch reconcile: skipping ${name} (unreadable ref or object?): ${getErrorMessage2(err)}`
|
|
44006
|
-
);
|
|
44007
|
-
continue;
|
|
44008
|
-
}
|
|
44009
|
-
}
|
|
44010
|
-
if (diverged.length > 0) {
|
|
44011
|
-
workerLogWarn(
|
|
44012
|
-
` Tracked-branch reconcile: ${diverged.length} branch(es) diverged from GitHub and were left untouched: ${diverged.join(", ")}`
|
|
44013
|
-
);
|
|
44014
|
-
}
|
|
44015
|
-
if (rewritten.length > 0) {
|
|
44016
|
-
workerLog(
|
|
44017
|
-
` Tracked-branch reconcile: ${rewritten.length} branch(es) rebased locally with the GitHub push still pending: ${rewritten.join(", ")}`
|
|
44018
|
-
);
|
|
44019
|
-
}
|
|
44020
|
-
return {
|
|
44021
|
-
summary: { created, fastForwarded, ahead, diverged, rewritten },
|
|
44022
|
-
trackedNames: new Set(tracked.keys())
|
|
44023
|
-
};
|
|
44024
|
-
}
|
|
44025
|
-
/**
|
|
44026
|
-
* Whether this branch carries a pending history rewrite -- the rebase loop
|
|
44027
|
-
* rewrote already-published history and the GitHub push has not landed yet
|
|
44028
|
-
* (see BranchMetadata.historyRewrittenFrom).
|
|
44029
|
-
*
|
|
44030
|
-
* `branchName` is a git ref name; branch workspaces are directories named
|
|
44031
|
-
* with the sanitized form, hence the conversion. Best-effort: a settings
|
|
44032
|
-
* branch (no workspace at all), a missing directory or an unreadable
|
|
44033
|
-
* branch.json all mean "no known rewrite", which is the conservative
|
|
44034
|
-
* answer -- it keeps the branch in the louder `diverged` bucket.
|
|
44035
|
-
*/
|
|
44036
|
-
async hasPendingHistoryRewrite(branchName) {
|
|
44037
|
-
try {
|
|
44038
|
-
const branchPath = path13.join(this.contentBranchesPath, sanitizeBranchName(branchName));
|
|
44039
|
-
const metaFile = await BranchMetadataFileManager.loadOnly(branchPath);
|
|
44040
|
-
return typeof metaFile?.branch.historyRewrittenFrom === "string";
|
|
44041
|
-
} catch {
|
|
44042
|
-
return false;
|
|
44043
|
-
}
|
|
44044
|
-
}
|
|
44045
|
-
async syncGit() {
|
|
44046
|
-
if (!this.running) return;
|
|
44047
|
-
workerLog("Syncing git...");
|
|
44048
|
-
const cycleStartedAt = Date.now();
|
|
44049
|
-
const git = simpleGit({
|
|
44050
|
-
baseDir: this.remoteGitPath,
|
|
44051
|
-
// DEP-H1: a hung fetch/push would stall the sync loop forever
|
|
44052
|
-
// (scheduleLoop only reschedules after completion). The block timeout
|
|
44053
|
-
// is inactivity-based, so a slow-but-flowing transfer is unaffected.
|
|
44054
|
-
timeout: { block: this.taskTimeoutMs }
|
|
44055
|
-
});
|
|
44056
|
-
git.env(gitNetworkChildEnv());
|
|
44057
|
-
try {
|
|
44058
|
-
await git.raw([
|
|
44059
|
-
"fetch",
|
|
44060
|
-
this.buildGitHubUrl(),
|
|
44061
|
-
"--prune",
|
|
44062
|
-
`+refs/heads/*:${GITHUB_TRACKING_REF_PREFIX}*`
|
|
44063
|
-
]);
|
|
44064
|
-
workerLog("Fetched from GitHub");
|
|
44065
|
-
const { summary: trackedSummary, trackedNames } = await this.reconcileTrackedBranches(git);
|
|
44066
|
-
await this.pushSettingsBranches(git, trackedNames);
|
|
44067
|
-
await this.refreshBaseBranchWorkspace();
|
|
44068
|
-
const rebaseSummary = await this.rebaseActiveBranches();
|
|
44069
|
-
await cleanupOldTasks(this.taskDir, void 0, this.log);
|
|
44070
|
-
const trashRemoved = await this.cleanupTrashedBranchDirs();
|
|
44071
|
-
if (trashRemoved > 0) {
|
|
44072
|
-
workerLog(`Removed ${trashRemoved} expired trashed branch dir(s)`);
|
|
44073
|
-
}
|
|
44074
|
-
const report = this.ensureStatusReport();
|
|
44075
|
-
report.lastGitSyncAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
44076
|
-
delete report.lastGitSyncError;
|
|
44077
|
-
report.lastGitSync = {
|
|
44078
|
-
durationMs: Date.now() - cycleStartedAt,
|
|
44079
|
-
rebased: rebaseSummary.rebased,
|
|
44080
|
-
skippedDirty: rebaseSummary.skippedDirty,
|
|
44081
|
-
skippedLocked: rebaseSummary.skippedLocked,
|
|
44082
|
-
failed: rebaseSummary.failed,
|
|
44083
|
-
tracked: trackedSummary
|
|
44084
|
-
};
|
|
44085
|
-
await writeWorkerStatus(this.taskDir, report).catch(
|
|
44086
|
-
(writeErr) => workerLogError("Failed to write worker status:", getErrorMessage2(writeErr))
|
|
44087
|
-
);
|
|
44088
|
-
} catch (err) {
|
|
44089
|
-
const report = this.ensureStatusReport();
|
|
44090
|
-
report.lastGitSyncError = {
|
|
44091
|
-
message: redactCredentials(getErrorMessage2(err)),
|
|
44092
|
-
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
44093
|
-
};
|
|
44094
|
-
await writeWorkerStatus(this.taskDir, report).catch(
|
|
44095
|
-
(writeErr) => workerLogError("Failed to write worker status:", getErrorMessage2(writeErr))
|
|
44096
|
-
);
|
|
44097
|
-
throw err;
|
|
44098
|
-
}
|
|
44099
|
-
}
|
|
44100
|
-
/**
|
|
44101
|
-
* [C1] Remove `.trash-*` branch directories (created by the admin purge
|
|
44102
|
-
* action, api/admin-branch-health.ts) whose name-embedded stamp is older
|
|
44103
|
-
* than {@link TRASH_RETENTION_MS}. Names that don't match the expected
|
|
44104
|
-
* `.trash-{dirName}-{STAMP}` shape, or whose stamp fails to parse, are
|
|
44105
|
-
* left alone (logged once per cycle, not per file, to avoid flooding logs
|
|
44106
|
-
* if something odd accumulates) -- purge is the only writer of this
|
|
44107
|
-
* naming scheme, so an unparseable name is unexpected and worth a human
|
|
44108
|
-
* looking rather than a silent skip.
|
|
44109
|
-
*/
|
|
44110
|
-
async cleanupTrashedBranchDirs() {
|
|
44111
|
-
let entries;
|
|
44112
|
-
try {
|
|
44113
|
-
entries = await fs9.readdir(this.contentBranchesPath);
|
|
44114
|
-
} catch (err) {
|
|
44115
|
-
if (isNodeError(err) && err.code === "ENOENT") return 0;
|
|
44116
|
-
throw err;
|
|
44117
|
-
}
|
|
44118
|
-
const now = Date.now();
|
|
44119
|
-
let removed = 0;
|
|
44120
|
-
let loggedUnparseable = false;
|
|
44121
|
-
for (const name of entries) {
|
|
44122
|
-
if (!name.startsWith(".trash-")) continue;
|
|
44123
|
-
const stampMatch = TRASH_DIR_STAMP_RE.exec(name);
|
|
44124
|
-
const stampDate = stampMatch ? parseTrashStamp(stampMatch[1]) : null;
|
|
44125
|
-
if (!stampDate) {
|
|
44126
|
-
if (!loggedUnparseable) {
|
|
44127
|
-
workerLog(`CanopyCMS: Skipping trash dir with unparseable stamp: ${name}`);
|
|
44128
|
-
loggedUnparseable = true;
|
|
44129
|
-
}
|
|
44130
|
-
continue;
|
|
44131
|
-
}
|
|
44132
|
-
if (now - stampDate.getTime() < TRASH_RETENTION_MS) continue;
|
|
44133
|
-
try {
|
|
44134
|
-
await fs9.rm(path13.join(this.contentBranchesPath, name), {
|
|
44135
|
-
recursive: true,
|
|
44136
|
-
force: true,
|
|
44137
|
-
maxRetries: 3,
|
|
44138
|
-
retryDelay: 100
|
|
44139
|
-
});
|
|
44140
|
-
removed++;
|
|
44141
|
-
} catch (err) {
|
|
44142
|
-
workerLogError(
|
|
44143
|
-
`CanopyCMS: Failed to remove trashed branch dir ${name}:`,
|
|
44144
|
-
getErrorMessage2(err)
|
|
44145
|
-
);
|
|
44146
|
-
}
|
|
44147
|
-
}
|
|
44148
|
-
return removed;
|
|
44149
|
-
}
|
|
44150
|
-
/**
|
|
44151
|
-
* Fast-forward the base branch's own working-tree clone
|
|
44152
|
-
* (content-branches/<baseBranch>) to match origin/<baseBranch>.
|
|
44153
|
-
*
|
|
44154
|
-
* Previously this clone was refreshed only incidentally, by the generic
|
|
44155
|
-
* rebase loop below (rebaseActiveBranches): for a branch with status
|
|
44156
|
-
* 'editing', rebasing onto origin/<baseBranch> degenerates to a
|
|
44157
|
-
* fast-forward when the clone IS the base branch. But that loop's skip
|
|
44158
|
-
* paths -- a dirty tree, a missing .git -- are silent, which is the
|
|
44159
|
-
* suspected live failure mode: a wedged base clone with no diagnosable
|
|
44160
|
-
* signal in the logs. This dedicated step makes the refresh explicit,
|
|
44161
|
-
* ff-only, and loud, so a stuck base view (an editor forking a new branch
|
|
44162
|
-
* "from base" that's actually a stale snapshot) is diagnosable from logs.
|
|
44163
|
-
* This runs every sync cycle so the drift window is bounded by
|
|
44164
|
-
* gitSyncInterval.
|
|
44165
|
-
*
|
|
44166
|
-
* ff-only on purpose: this clone must stay a linear mirror of
|
|
44167
|
-
* origin/<baseBranch>, so a merge that isn't a fast-forward (diverged
|
|
44168
|
-
* local history) is treated as a should-never-happen condition and left
|
|
44169
|
-
* untouched rather than force-resolved.
|
|
44170
|
-
*/
|
|
44171
|
-
async refreshBaseBranchWorkspace() {
|
|
44172
|
-
const basePath32 = path13.join(this.contentBranchesPath, this.sanitizedBaseBranch);
|
|
44173
|
-
const gitDir = path13.join(basePath32, ".git");
|
|
44174
|
-
try {
|
|
44175
|
-
let gitDirStat;
|
|
44176
|
-
try {
|
|
44177
|
-
gitDirStat = await fs9.stat(gitDir);
|
|
44178
|
-
} catch {
|
|
44179
|
-
gitDirStat = null;
|
|
44180
|
-
}
|
|
44181
|
-
if (!gitDirStat || !gitDirStat.isDirectory()) {
|
|
44182
|
-
workerLog(
|
|
44183
|
-
`Base branch workspace (${this.baseBranch}): not yet provisioned, skipping refresh`
|
|
44184
|
-
);
|
|
44185
|
-
return;
|
|
44186
|
-
}
|
|
44187
|
-
const baseGit = simpleGit({
|
|
44188
|
-
baseDir: basePath32,
|
|
44189
|
-
// Keep git non-interactive during the merge so it never blocks on an
|
|
44190
|
-
// editor. simple-git >=3.32 blocks setting core.editor unless
|
|
44191
|
-
// explicitly opted in; the value here is a hardcoded literal
|
|
44192
|
-
// ("true", the shell no-op), not user input, so enabling
|
|
44193
|
-
// allowUnsafeEditor carries no injection risk (same as the rebase
|
|
44194
|
-
// loop's git config below).
|
|
44195
|
-
config: ["core.editor=true"],
|
|
44196
|
-
unsafe: { allowUnsafeEditor: true },
|
|
44197
|
-
// DEP-H1: a hung fetch/merge against this EFS-backed clone would
|
|
44198
|
-
// stall the sync loop forever (scheduleLoop only reschedules after
|
|
44199
|
-
// completion). The block timeout is inactivity-based, so a
|
|
44200
|
-
// slow-but-flowing transfer is unaffected -- same as syncGit()'s
|
|
44201
|
-
// remote handle.
|
|
44202
|
-
timeout: { block: this.taskTimeoutMs }
|
|
44203
|
-
});
|
|
44204
|
-
const status = await baseGit.status();
|
|
44205
|
-
const trackedDirty = status.files.filter((f5) => f5.index !== "?" || f5.working_dir !== "?");
|
|
44206
|
-
if (trackedDirty.length > 0) {
|
|
44207
|
-
workerLogError(
|
|
44208
|
-
`Base branch workspace (${this.baseBranch}) has uncommitted changes -- skipping refresh. Dirty files: ${trackedDirty.map((f5) => f5.path).join(", ")}`
|
|
44209
|
-
);
|
|
44210
|
-
return;
|
|
44211
|
-
}
|
|
44212
|
-
await baseGit.fetch("origin", this.baseBranch);
|
|
44213
|
-
const fetchedTip = (await baseGit.revparse(["FETCH_HEAD"])).trim();
|
|
44214
|
-
const behindCount = parseInt(
|
|
44215
|
-
(await baseGit.raw(["rev-list", "--count", `HEAD..${fetchedTip}`])).trim(),
|
|
44216
|
-
10
|
|
44217
|
-
);
|
|
44218
|
-
if (behindCount > 0) {
|
|
44219
|
-
try {
|
|
44220
|
-
await baseGit.merge(["--ff-only", fetchedTip]);
|
|
44221
|
-
} catch (err) {
|
|
44222
|
-
workerLogError(
|
|
44223
|
-
`Base branch workspace (${this.baseBranch}) failed to fast-forward (diverged local history?): ${getErrorMessage2(err)}`
|
|
44224
|
-
);
|
|
44225
|
-
return;
|
|
44226
|
-
}
|
|
44227
|
-
await invalidateBranchContentCaches(basePath32);
|
|
44228
|
-
}
|
|
44229
|
-
const currentMeta = await BranchMetadataFileManager.loadOnly(basePath32);
|
|
44230
|
-
const conflictStatus = currentMeta?.branch.conflictStatus;
|
|
44231
|
-
const conflictFiles = currentMeta?.branch.conflictFiles;
|
|
44232
|
-
const alreadyClean = (conflictStatus === void 0 || conflictStatus === "clean") && (conflictFiles === void 0 || conflictFiles.length === 0);
|
|
44233
|
-
if (!alreadyClean) {
|
|
44234
|
-
const meta = getBranchMetadataFileManager(basePath32, this.contentBranchesPath);
|
|
44235
|
-
await meta.save({
|
|
44236
|
-
branch: { name: this.baseBranch, conflictStatus: "clean", conflictFiles: [] }
|
|
44237
|
-
});
|
|
44238
|
-
}
|
|
44239
|
-
workerLog(
|
|
44240
|
-
behindCount > 0 ? `Base branch workspace (${this.baseBranch}): fast-forwarded ${behindCount} commit(s)` : `Base branch workspace (${this.baseBranch}): up to date`
|
|
44241
|
-
);
|
|
44242
|
-
} catch (err) {
|
|
44243
|
-
workerLogError(
|
|
44244
|
-
`Base branch workspace (${this.baseBranch}) refresh failed: ${getErrorMessage2(err)}`
|
|
44245
|
-
);
|
|
44246
|
-
}
|
|
44247
|
-
}
|
|
44248
|
-
/**
|
|
44249
|
-
* Poll GitHub for a submitted/approved branch's PR resolution.
|
|
44250
|
-
*
|
|
44251
|
-
* submitted/approved branches sit outside the rebase loop and get no
|
|
44252
|
-
* other signal that their PR resolved on GitHub -- nothing pushes a
|
|
44253
|
-
* merge/close webhook back into the branch workspace. merged ->
|
|
44254
|
-
* auto-archive via buildMergedBranchUpdate (shared with the manual
|
|
44255
|
-
* markAsMerged API so both paths produce identical archived-branch
|
|
44256
|
-
* metadata). closed-without-merge -> record pullRequestState only; an
|
|
44257
|
-
* admin decides the workflow transition from there. Best-effort: any
|
|
44258
|
-
* failure here is logged and swallowed, retried next sync cycle.
|
|
44259
|
-
*/
|
|
44260
|
-
async pollMergeState(branchDir, branchPath, metaFile) {
|
|
44261
|
-
const prNumber = metaFile?.branch.pullRequestNumber;
|
|
44262
|
-
if (!prNumber) return;
|
|
44263
|
-
const controller = new AbortController();
|
|
44264
|
-
const timer = setTimeout(() => controller.abort(), this.taskTimeoutMs);
|
|
44265
|
-
try {
|
|
44266
|
-
const { data: data2 } = await this.octokit.pulls.get({
|
|
44267
|
-
owner: this.config.githubOwner,
|
|
44268
|
-
repo: this.config.githubRepo,
|
|
44269
|
-
pull_number: prNumber,
|
|
44270
|
-
request: { signal: controller.signal }
|
|
44271
|
-
});
|
|
44272
|
-
if (data2.merged) {
|
|
44273
|
-
const meta2 = getBranchMetadataFileManager(branchPath, this.contentBranchesPath);
|
|
44274
|
-
await meta2.save({
|
|
44275
|
-
branch: buildMergedBranchUpdate(
|
|
44276
|
-
branchDir,
|
|
44277
|
-
data2.merged_at ? new Date(data2.merged_at) : void 0
|
|
44278
|
-
)
|
|
44279
|
-
});
|
|
44280
|
-
workerLog(` PR #${prNumber} for ${branchDir} is merged -> archived`);
|
|
44281
|
-
return;
|
|
44282
|
-
}
|
|
44283
|
-
const newState = data2.state === "closed" ? "closed" : "open";
|
|
44284
|
-
const currentMeta = await BranchMetadataFileManager.loadOnly(branchPath);
|
|
44285
|
-
if (currentMeta?.branch.pullRequestState === newState) return;
|
|
44286
|
-
const meta = getBranchMetadataFileManager(branchPath, this.contentBranchesPath);
|
|
44287
|
-
await meta.save({ branch: { name: branchDir, pullRequestState: newState } });
|
|
44288
|
-
workerLog(` PR #${prNumber} for ${branchDir}: pullRequestState -> ${newState}`);
|
|
44289
|
-
} catch (err) {
|
|
44290
|
-
workerLogWarn(` Failed to poll PR #${prNumber} for ${branchDir}: ${getErrorMessage2(err)}`);
|
|
44291
|
-
} finally {
|
|
44292
|
-
clearTimeout(timer);
|
|
44293
|
-
}
|
|
44294
|
-
}
|
|
44295
|
-
/**
|
|
44296
|
-
* Persist a per-branch rebase failure to branch.json (PR-W2), bounded to
|
|
44297
|
-
* roughly one save per failing branch per hour: a branch stuck failing
|
|
44298
|
-
* every cycle must not turn into unbounded save-per-cycle x N-failing-
|
|
44299
|
-
* branches write amplification -- save() eager-regenerates the branch
|
|
44300
|
-
* registry (branch-metadata.ts's invalidateRegistry(), O(branch count) EFS
|
|
44301
|
-
* reads), the same concern the `alreadyClean` no-op guard above exists
|
|
44302
|
-
* for.
|
|
44303
|
-
*
|
|
44304
|
-
* Best-effort and non-fatal like every other metadata write in this
|
|
44305
|
-
* loop's error paths: a corrupt branch.json, a lock-contention error, or
|
|
44306
|
-
* any other save failure here must never abort the per-branch iteration.
|
|
44307
|
-
* This matters doubly at the two call sites -- one is inside the outer
|
|
44308
|
-
* per-branch catch, with no further catch of its own around this call --
|
|
44309
|
-
* so the whole method is wrapped, not just the load.
|
|
44310
|
-
*/
|
|
44311
|
-
async recordRebaseFailure(branchPath, branchDir, message) {
|
|
44312
|
-
const RECORD_REFRESH_MS = 60 * 60 * 1e3;
|
|
44313
|
-
const redactedMessage = redactCredentials(message);
|
|
44314
|
-
try {
|
|
44315
|
-
const existing = await BranchMetadataFileManager.loadOnly(branchPath);
|
|
44316
|
-
const prior = existing?.branch.rebaseFailure;
|
|
44317
|
-
const sameMessage = prior?.message === redactedMessage;
|
|
44318
|
-
const now = /* @__PURE__ */ new Date();
|
|
44319
|
-
if (sameMessage) {
|
|
44320
|
-
const lastAtMs = Date.parse(prior.lastAt);
|
|
44321
|
-
if (!Number.isNaN(lastAtMs) && now.getTime() - lastAtMs < RECORD_REFRESH_MS) {
|
|
44322
|
-
return;
|
|
44323
|
-
}
|
|
44324
|
-
}
|
|
44325
|
-
const nowIso = now.toISOString();
|
|
44326
|
-
const firstAt = sameMessage ? prior.firstAt : nowIso;
|
|
44327
|
-
const meta = getBranchMetadataFileManager(branchPath, this.contentBranchesPath);
|
|
44328
|
-
await meta.save({
|
|
44329
|
-
branch: {
|
|
44330
|
-
name: branchDir,
|
|
44331
|
-
rebaseFailure: { message: redactedMessage, firstAt, lastAt: nowIso }
|
|
44332
|
-
}
|
|
44333
|
-
});
|
|
44334
|
-
} catch (err) {
|
|
44335
|
-
workerLogWarn(` Failed to record rebase failure for ${branchDir}: ${getErrorMessage2(err)}`);
|
|
44336
|
-
}
|
|
44515
|
+
return path17.join(this.contentBranchesPath, sanitizeBranchName(branchRefName));
|
|
44337
44516
|
}
|
|
44338
44517
|
/**
|
|
44339
44518
|
* Test-only seam: awaited inside `rebaseActiveBranches()`'s conflict round,
|
|
@@ -44358,307 +44537,48 @@ var CmsWorker = class {
|
|
|
44358
44537
|
*/
|
|
44359
44538
|
async afterRebaseCompletedForTesting() {
|
|
44360
44539
|
}
|
|
44540
|
+
// --- Rebase loop (worker/rebase.ts) ------------------------------------
|
|
44541
|
+
//
|
|
44542
|
+
// Both are TEST-ONLY entry points -- see the task-queue block above. Nothing
|
|
44543
|
+
// in production dispatches through either; `syncGit` calls `runRebaseCycle`
|
|
44544
|
+
// directly and that calls `pollMergeState` directly. Four test files call
|
|
44545
|
+
// `rebaseActiveBranches` (it is the single entry point the whole rebase suite
|
|
44546
|
+
// drives), and cms-worker-merge-poll.test.ts calls `pollMergeState` with a
|
|
44547
|
+
// mocked `octokit`. Neither is on WorkerContext, so a stub installed on
|
|
44548
|
+
// either is a no-op as far as a production code path is concerned.
|
|
44549
|
+
//
|
|
44550
|
+
// `rebaseActiveBranches` also has a consumer outside the test files:
|
|
44551
|
+
// apps/test-app/app/api/e2e-test/rebase/route.ts drives it from an e2e
|
|
44552
|
+
// fixture route. Do not delete it as unused.
|
|
44361
44553
|
async rebaseActiveBranches() {
|
|
44362
|
-
|
|
44363
|
-
|
|
44364
|
-
|
|
44365
|
-
|
|
44366
|
-
|
|
44367
|
-
|
|
44368
|
-
|
|
44369
|
-
|
|
44370
|
-
|
|
44371
|
-
|
|
44372
|
-
|
|
44373
|
-
|
|
44374
|
-
|
|
44375
|
-
|
|
44376
|
-
|
|
44377
|
-
|
|
44378
|
-
|
|
44379
|
-
|
|
44380
|
-
|
|
44381
|
-
|
|
44382
|
-
|
|
44383
|
-
|
|
44384
|
-
|
|
44385
|
-
|
|
44386
|
-
|
|
44387
|
-
|
|
44388
|
-
|
|
44389
|
-
|
|
44390
|
-
continue;
|
|
44391
|
-
}
|
|
44392
|
-
try {
|
|
44393
|
-
const metaFile = await BranchMetadataFileManager.loadOnly(branchPath);
|
|
44394
|
-
const branchStatus2 = metaFile?.branch.status;
|
|
44395
|
-
if (branchStatus2 === "submitted" || branchStatus2 === "approved") {
|
|
44396
|
-
workerLog(` Skipping ${branchDir} (${branchStatus2})`);
|
|
44397
|
-
await this.pollMergeState(branchDir, branchPath, metaFile);
|
|
44398
|
-
continue;
|
|
44399
|
-
}
|
|
44400
|
-
if (branchStatus2 === "archived") {
|
|
44401
|
-
workerLog(` Skipping ${branchDir} (${branchStatus2})`);
|
|
44402
|
-
continue;
|
|
44403
|
-
}
|
|
44404
|
-
const branchGit = simpleGit({
|
|
44405
|
-
baseDir: branchPath,
|
|
44406
|
-
// Keep git non-interactive during rebase/merge so it never blocks on an editor.
|
|
44407
|
-
// simple-git >=3.32 blocks setting core.editor unless explicitly opted in; the
|
|
44408
|
-
// value here is a hardcoded literal ("true", the shell no-op), not user input,
|
|
44409
|
-
// so enabling allowUnsafeEditor carries no injection risk.
|
|
44410
|
-
config: ["core.editor=true"],
|
|
44411
|
-
unsafe: { allowUnsafeEditor: true }
|
|
44412
|
-
});
|
|
44413
|
-
let releaseContentLock;
|
|
44414
|
-
let contentLockCompromised = false;
|
|
44415
|
-
try {
|
|
44416
|
-
releaseContentLock = await tryAcquireContentWriteLock(branchPath, (lockErr) => {
|
|
44417
|
-
contentLockCompromised = true;
|
|
44418
|
-
workerLogWarn(
|
|
44419
|
-
` Content-write lock compromised mid-rebase for ${branchDir}: ${getErrorMessage2(lockErr)}`
|
|
44420
|
-
);
|
|
44421
|
-
});
|
|
44422
|
-
} catch (lockErr) {
|
|
44423
|
-
if (isNodeError(lockErr) && lockErr.code === "ELOCKED") {
|
|
44424
|
-
workerLog(` Skipping ${branchDir}: content write in progress (retrying next cycle)`);
|
|
44425
|
-
skippedLocked.push(branchDir);
|
|
44426
|
-
continue;
|
|
44427
|
-
}
|
|
44428
|
-
throw lockErr;
|
|
44429
|
-
}
|
|
44430
|
-
try {
|
|
44431
|
-
if (await isRebaseInProgress(branchPath)) {
|
|
44432
|
-
const preAbort = await branchGit.status().catch(() => null);
|
|
44433
|
-
const collateral = (preAbort?.files ?? []).filter((f5) => !preAbort?.conflicted.includes(f5.path)).filter((f5) => f5.working_dir !== " " && f5.working_dir !== "?").map((f5) => f5.path);
|
|
44434
|
-
if (collateral.length > 0) {
|
|
44435
|
-
workerLogWarn(
|
|
44436
|
-
` ${branchDir}: aborting the interrupted rebase will DISCARD working-tree changes to ${collateral.length} file(s) saved while the worker was down: ${collateral.join(", ")}`
|
|
44437
|
-
);
|
|
44438
|
-
}
|
|
44439
|
-
workerLogWarn(
|
|
44440
|
-
` ${branchDir}: found an interrupted rebase (this worker's own abandoned work) -- aborting it to recover the branch`
|
|
44441
|
-
);
|
|
44442
|
-
try {
|
|
44443
|
-
await branchGit.rebase(["--abort"]);
|
|
44444
|
-
} catch (abortErr) {
|
|
44445
|
-
const reason = redactCredentials(
|
|
44446
|
-
`could not abort interrupted rebase: ${getErrorMessage2(abortErr)}`
|
|
44447
|
-
);
|
|
44448
|
-
workerLogWarn(` Skipping ${branchDir}: ${reason}`);
|
|
44449
|
-
failed.push({ branch: branchDir, error: reason });
|
|
44450
|
-
await this.recordRebaseFailure(branchPath, branchDir, reason);
|
|
44451
|
-
continue;
|
|
44452
|
-
}
|
|
44453
|
-
}
|
|
44454
|
-
const dirtyCheck = await branchGit.status();
|
|
44455
|
-
if (dirtyCheck.files.length > 0) {
|
|
44456
|
-
workerLog(` Skipping ${branchDir}: has uncommitted changes`);
|
|
44457
|
-
skippedDirty.push(branchDir);
|
|
44458
|
-
continue;
|
|
44459
|
-
}
|
|
44460
|
-
const branchRef = (await branchGit.revparse(["--abbrev-ref", "HEAD"])).trim();
|
|
44461
|
-
const canPublish = branchRef.length > 0 && branchRef !== "HEAD";
|
|
44462
|
-
if (canPublish && metaFile?.branch.historyRewrittenFrom) {
|
|
44463
|
-
await this.reconcilePendingRewrite({
|
|
44464
|
-
branchPath,
|
|
44465
|
-
branchDir,
|
|
44466
|
-
branchRef,
|
|
44467
|
-
headSha: (await branchGit.revparse(["HEAD"])).trim(),
|
|
44468
|
-
marker: metaFile.branch.historyRewrittenFrom
|
|
44469
|
-
});
|
|
44470
|
-
}
|
|
44471
|
-
await branchGit.fetch("origin", this.baseBranch);
|
|
44472
|
-
const fetchedBaseTip = (await branchGit.revparse(["FETCH_HEAD"])).trim();
|
|
44473
|
-
const behindCount = parseInt(
|
|
44474
|
-
(await branchGit.raw(["rev-list", "--count", `HEAD..${fetchedBaseTip}`])).trim(),
|
|
44475
|
-
10
|
|
44476
|
-
);
|
|
44477
|
-
const meta = getBranchMetadataFileManager(branchPath, this.contentBranchesPath);
|
|
44478
|
-
if (behindCount === 0) {
|
|
44479
|
-
const currentMeta = await BranchMetadataFileManager.loadOnly(branchPath);
|
|
44480
|
-
const conflictStatus = currentMeta?.branch.conflictStatus;
|
|
44481
|
-
const conflictFiles = currentMeta?.branch.conflictFiles;
|
|
44482
|
-
const conflictAlreadyClean = (conflictStatus === void 0 || conflictStatus === "clean") && (conflictFiles === void 0 || conflictFiles.length === 0);
|
|
44483
|
-
const alreadyClean = conflictAlreadyClean && currentMeta?.branch.rebaseFailure === void 0;
|
|
44484
|
-
if (alreadyClean) {
|
|
44485
|
-
continue;
|
|
44486
|
-
}
|
|
44487
|
-
await meta.save({
|
|
44488
|
-
branch: {
|
|
44489
|
-
name: branchDir,
|
|
44490
|
-
conflictStatus: "clean",
|
|
44491
|
-
conflictFiles: [],
|
|
44492
|
-
rebaseFailure: void 0
|
|
44493
|
-
}
|
|
44494
|
-
});
|
|
44495
|
-
continue;
|
|
44496
|
-
}
|
|
44497
|
-
workerLog(`Rebasing ${branchDir} (${behindCount} commits behind)...`);
|
|
44498
|
-
const preRebaseHead = (await branchGit.revparse(["HEAD"])).trim();
|
|
44499
|
-
const publishedSha = canPublish ? await this.readPublishedSha(branchRef) : null;
|
|
44500
|
-
const conflictedFiles = [];
|
|
44501
|
-
let nextAction = "start";
|
|
44502
|
-
let completed = false;
|
|
44503
|
-
let failureReason;
|
|
44504
|
-
const MAX_ROUNDS = 50;
|
|
44505
|
-
for (let round = 0; round < MAX_ROUNDS && !completed; round++) {
|
|
44506
|
-
if (contentLockCompromised) break;
|
|
44507
|
-
try {
|
|
44508
|
-
if (nextAction === "start") {
|
|
44509
|
-
await branchGit.rebase([fetchedBaseTip]);
|
|
44510
|
-
} else if (nextAction === "continue") {
|
|
44511
|
-
await branchGit.rebase(["--continue"]);
|
|
44512
|
-
} else {
|
|
44513
|
-
await branchGit.rebase(["--skip"]);
|
|
44514
|
-
}
|
|
44515
|
-
completed = true;
|
|
44516
|
-
} catch (rebaseErr) {
|
|
44517
|
-
nextAction = "continue";
|
|
44518
|
-
const st = await branchGit.status();
|
|
44519
|
-
if (st.conflicted.length > 0) {
|
|
44520
|
-
await this.afterConflictDetectedForTesting();
|
|
44521
|
-
const conflictKind = new Map(
|
|
44522
|
-
st.files.map((f5) => [f5.path, `${f5.index}${f5.working_dir}`])
|
|
44523
|
-
);
|
|
44524
|
-
let resolutionFailure;
|
|
44525
|
-
for (const file of st.conflicted) {
|
|
44526
|
-
const kind = conflictKind.get(file);
|
|
44527
|
-
try {
|
|
44528
|
-
if (kind === "UD") {
|
|
44529
|
-
await branchGit.raw(["rm", "-f", "--", file]);
|
|
44530
|
-
} else if (kind === "DU") {
|
|
44531
|
-
await branchGit.add(file);
|
|
44532
|
-
} else {
|
|
44533
|
-
await branchGit.raw(["checkout", "--theirs", file]);
|
|
44534
|
-
await branchGit.add(file);
|
|
44535
|
-
}
|
|
44536
|
-
} catch (resolveErr) {
|
|
44537
|
-
resolutionFailure = `failed to resolve conflicted file '${file}' (status ${kind ?? "??"}): ` + getErrorMessage2(resolveErr);
|
|
44538
|
-
break;
|
|
44539
|
-
}
|
|
44540
|
-
conflictedFiles.push(file);
|
|
44541
|
-
}
|
|
44542
|
-
if (resolutionFailure !== void 0) {
|
|
44543
|
-
failureReason = resolutionFailure;
|
|
44544
|
-
break;
|
|
44545
|
-
}
|
|
44546
|
-
} else {
|
|
44547
|
-
const msg = rebaseErr instanceof Error ? rebaseErr.message : "";
|
|
44548
|
-
if (msg.toLowerCase().includes("nothing to commit") || msg.toLowerCase().includes("apply --skip")) {
|
|
44549
|
-
nextAction = "skip";
|
|
44550
|
-
} else {
|
|
44551
|
-
workerLogWarn(
|
|
44552
|
-
` Unexpected rebase error in ${branchDir}: ${msg || "Unknown error"}`
|
|
44553
|
-
);
|
|
44554
|
-
failureReason = msg || "Unknown error";
|
|
44555
|
-
await branchGit.rebase(["--abort"]).catch(() => {
|
|
44556
|
-
});
|
|
44557
|
-
break;
|
|
44558
|
-
}
|
|
44559
|
-
}
|
|
44560
|
-
}
|
|
44561
|
-
}
|
|
44562
|
-
if (completed) await this.afterRebaseCompletedForTesting();
|
|
44563
|
-
if (contentLockCompromised && !completed) {
|
|
44564
|
-
workerLogWarn(
|
|
44565
|
-
` Skipping ${branchDir}: content-write lock was compromised mid-rebase (retrying next cycle)`
|
|
44566
|
-
);
|
|
44567
|
-
await branchGit.rebase(["--abort"]).catch(() => {
|
|
44568
|
-
});
|
|
44569
|
-
skippedLocked.push(branchDir);
|
|
44570
|
-
continue;
|
|
44571
|
-
}
|
|
44572
|
-
if (contentLockCompromised) {
|
|
44573
|
-
workerLogWarn(
|
|
44574
|
-
` Content-write lock for ${branchDir} was compromised, but its rebase had already completed -- finishing the sync (history is rewritten; skipping now would strand the history-rewrite marker and wedge the branch)`
|
|
44575
|
-
);
|
|
44576
|
-
}
|
|
44577
|
-
if (!completed) {
|
|
44578
|
-
workerLogWarn(
|
|
44579
|
-
failureReason !== void 0 ? ` Rebase of ${branchDir} aborted due to unexpected error: ${failureReason}` : ` Rebase of ${branchDir} did not complete within ${MAX_ROUNDS} rounds, aborting`
|
|
44580
|
-
);
|
|
44581
|
-
await branchGit.rebase(["--abort"]).catch(() => {
|
|
44582
|
-
});
|
|
44583
|
-
const rebaseFailureMessage = failureReason ?? `did not complete within ${MAX_ROUNDS} rounds`;
|
|
44584
|
-
const redactedRebaseFailureMessage = redactCredentials(rebaseFailureMessage);
|
|
44585
|
-
failed.push({
|
|
44586
|
-
branch: branchDir,
|
|
44587
|
-
error: redactedRebaseFailureMessage
|
|
44588
|
-
});
|
|
44589
|
-
await this.recordRebaseFailure(branchPath, branchDir, redactedRebaseFailureMessage);
|
|
44590
|
-
continue;
|
|
44591
|
-
}
|
|
44592
|
-
await invalidateBranchContentCaches(branchPath);
|
|
44593
|
-
const normalizedContentRoot = normalizeFilesystemPath(this.contentRoot);
|
|
44594
|
-
const conflictIds = [...new Set(conflictedFiles)].map((f5) => {
|
|
44595
|
-
const fileId = extractIdFromFilename(path13.basename(f5));
|
|
44596
|
-
if (fileId) return fileId;
|
|
44597
|
-
const parentDir = path13.basename(path13.dirname(f5));
|
|
44598
|
-
const dirId = extractIdFromFilename(parentDir);
|
|
44599
|
-
if (dirId) return dirId;
|
|
44600
|
-
const parentPath = normalizeFilesystemPath(path13.dirname(f5));
|
|
44601
|
-
if (path13.basename(f5) === ".collection.json" && parentPath === normalizedContentRoot) {
|
|
44602
|
-
return ROOT_COLLECTION_ID;
|
|
44603
|
-
}
|
|
44604
|
-
return null;
|
|
44605
|
-
}).filter((id) => id !== null);
|
|
44606
|
-
const conflictIdsDeduped = [...new Set(conflictIds)];
|
|
44607
|
-
const hadConflicts = conflictIdsDeduped.length > 0;
|
|
44608
|
-
workerLog(
|
|
44609
|
-
hadConflicts ? ` Rebased ${branchDir} (kept branch version for ${conflictIdsDeduped.length} conflicting file(s))` : ` Rebased ${branchDir} successfully`
|
|
44610
|
-
);
|
|
44611
|
-
await meta.save({
|
|
44612
|
-
branch: {
|
|
44613
|
-
name: branchDir,
|
|
44614
|
-
conflictStatus: hadConflicts ? "conflicts-detected" : "clean",
|
|
44615
|
-
conflictFiles: conflictIdsDeduped,
|
|
44616
|
-
// PR-W2: the cycle completed successfully -- clear any prior
|
|
44617
|
-
// failure record regardless of conflict outcome.
|
|
44618
|
-
rebaseFailure: void 0
|
|
44619
|
-
}
|
|
44620
|
-
});
|
|
44621
|
-
rebased.push(branchDir);
|
|
44622
|
-
if (publishedSha !== null) {
|
|
44623
|
-
if (publishedSha === preRebaseHead) {
|
|
44624
|
-
await this.markHistoryRewritten(branchPath, branchDir, publishedSha);
|
|
44625
|
-
if (await this.forcePublishToLocalRemote(branchPath, branchRef, publishedSha)) {
|
|
44626
|
-
await this.enqueueGitHubPush(branchRef);
|
|
44627
|
-
}
|
|
44628
|
-
} else {
|
|
44629
|
-
const divergence = `rebased locally, but remote.git holds ${publishedSha} for ${branchRef}, which this clone never had (a direct push to the branch?). Left untouched -- reconcile it before submitting again.`;
|
|
44630
|
-
workerLogWarn(` ${branchDir}: ${divergence}`);
|
|
44631
|
-
await this.recordRebaseFailure(branchPath, branchDir, divergence);
|
|
44632
|
-
}
|
|
44633
|
-
}
|
|
44634
|
-
} finally {
|
|
44635
|
-
try {
|
|
44636
|
-
if (await isRebaseInProgress(branchPath)) {
|
|
44637
|
-
workerLogWarn(
|
|
44638
|
-
` ${branchDir}: rebase still in progress on exit -- aborting so the clone is not left wedged`
|
|
44639
|
-
);
|
|
44640
|
-
await branchGit.rebase(["--abort"]);
|
|
44641
|
-
}
|
|
44642
|
-
} catch (abortErr) {
|
|
44643
|
-
workerLogWarn(
|
|
44644
|
-
` Failed to abort in-progress rebase for ${branchDir}: ${getErrorMessage2(abortErr)}`
|
|
44645
|
-
);
|
|
44646
|
-
}
|
|
44647
|
-
await releaseContentLock?.().catch((releaseErr) => {
|
|
44648
|
-
workerLogWarn(
|
|
44649
|
-
` Failed to release content-write lock for ${branchDir}: ${getErrorMessage2(releaseErr)}`
|
|
44650
|
-
);
|
|
44651
|
-
});
|
|
44652
|
-
}
|
|
44653
|
-
} catch (err) {
|
|
44654
|
-
const message = err instanceof Error ? err.message : "Unknown error";
|
|
44655
|
-
workerLogWarn(` Failed to sync ${branchDir}: ${message}`);
|
|
44656
|
-
const redactedMessage = redactCredentials(message);
|
|
44657
|
-
failed.push({ branch: branchDir, error: redactedMessage });
|
|
44658
|
-
await this.recordRebaseFailure(branchPath, branchDir, redactedMessage);
|
|
44659
|
-
}
|
|
44660
|
-
}
|
|
44661
|
-
return { rebased, skippedDirty, skippedLocked, failed };
|
|
44554
|
+
return runRebaseCycle(this.ctx());
|
|
44555
|
+
}
|
|
44556
|
+
// --- Git-sync cluster (worker/git-sync.ts) -----------------------------
|
|
44557
|
+
//
|
|
44558
|
+
// `syncGit` is the public loop entry `scheduleLoop` drives. The three private
|
|
44559
|
+
// ones below it are TEST-ONLY entry points -- see the task-queue block above
|
|
44560
|
+
// -- each called directly by one test file (cms-worker-sync-reconcile for the
|
|
44561
|
+
// settings push, cms-worker-base-refresh for the base workspace,
|
|
44562
|
+
// cms-worker.test.ts for the trash sweep). `syncGit` reaches all three as
|
|
44563
|
+
// module-level calls, so a stub installed on one of these is a no-op.
|
|
44564
|
+
//
|
|
44565
|
+
// `reconcileTrackedBranches` deliberately has NO delegator: no test reaches
|
|
44566
|
+
// it through the instance. Comments elsewhere that point at it as living in
|
|
44567
|
+
// this file are stale by definition -- it is in git-sync.ts.
|
|
44568
|
+
async syncGit() {
|
|
44569
|
+
return syncGit(this.ctx());
|
|
44570
|
+
}
|
|
44571
|
+
async pushSettingsBranches(git, trackedNames) {
|
|
44572
|
+
return pushSettingsBranches(this.ctx(), git, trackedNames);
|
|
44573
|
+
}
|
|
44574
|
+
async refreshBaseBranchWorkspace() {
|
|
44575
|
+
return refreshBaseBranchWorkspace(this.ctx());
|
|
44576
|
+
}
|
|
44577
|
+
async cleanupTrashedBranchDirs() {
|
|
44578
|
+
return cleanupTrashedBranchDirs(this.ctx());
|
|
44579
|
+
}
|
|
44580
|
+
async pollMergeState(branchDir, branchPath, metaFile) {
|
|
44581
|
+
return pollMergeState(this.ctx(), branchDir, branchPath, metaFile);
|
|
44662
44582
|
}
|
|
44663
44583
|
async refreshAuthCache() {
|
|
44664
44584
|
if (!this.running || !this.config.refreshAuthCache) return;
|
|
@@ -47853,10 +47773,10 @@ var SessionAPI = class extends AbstractAPI {
|
|
|
47853
47773
|
*/
|
|
47854
47774
|
async getToken(sessionId, template, expiresInSeconds) {
|
|
47855
47775
|
this.requireId(sessionId);
|
|
47856
|
-
const
|
|
47776
|
+
const path20 = template ? joinPaths(basePath24, sessionId, "tokens", template) : joinPaths(basePath24, sessionId, "tokens");
|
|
47857
47777
|
const requestOptions = {
|
|
47858
47778
|
method: "POST",
|
|
47859
|
-
path:
|
|
47779
|
+
path: path20
|
|
47860
47780
|
};
|
|
47861
47781
|
if (expiresInSeconds !== void 0) {
|
|
47862
47782
|
requestOptions.bodyParams = { expires_in_seconds: expiresInSeconds };
|
|
@@ -49978,12 +49898,12 @@ function buildRequest(options) {
|
|
|
49978
49898
|
userAgent = USER_AGENT,
|
|
49979
49899
|
skipApiVersionInUrl = false
|
|
49980
49900
|
} = options;
|
|
49981
|
-
const { path:
|
|
49901
|
+
const { path: path20, method, queryParams, headerParams, bodyParams, formData, options: opts } = requestOptions;
|
|
49982
49902
|
const { deepSnakecaseBodyParamKeys = false } = opts || {};
|
|
49983
49903
|
if (requireSecretKey) {
|
|
49984
49904
|
assertValidSecretKey(secretKey);
|
|
49985
49905
|
}
|
|
49986
|
-
const url = skipApiVersionInUrl ? joinPaths(apiUrl,
|
|
49906
|
+
const url = skipApiVersionInUrl ? joinPaths(apiUrl, path20) : joinPaths(apiUrl, apiVersion, path20);
|
|
49987
49907
|
const finalUrl = new URL(url);
|
|
49988
49908
|
if (queryParams) {
|
|
49989
49909
|
const snakecasedQueryParams = snakecase_keys_default({ ...queryParams });
|
|
@@ -52045,38 +51965,38 @@ function createClerkClient(options) {
|
|
|
52045
51965
|
}
|
|
52046
51966
|
|
|
52047
51967
|
// ../canopycms/src/auth/file-based-auth-cache.ts
|
|
52048
|
-
import
|
|
52049
|
-
import
|
|
51968
|
+
import fs14 from "node:fs/promises";
|
|
51969
|
+
import path18 from "node:path";
|
|
52050
51970
|
var log5 = createDebugLogger({ prefix: "FileBasedAuthCache" });
|
|
52051
51971
|
async function writeAuthCacheSnapshot(cachePath, files) {
|
|
52052
|
-
await
|
|
51972
|
+
await fs14.mkdir(cachePath, { recursive: true });
|
|
52053
51973
|
const timestamp2 = Date.now();
|
|
52054
|
-
const snapshotDir =
|
|
52055
|
-
await
|
|
51974
|
+
const snapshotDir = path18.join(cachePath, `snapshot-${timestamp2}`);
|
|
51975
|
+
await fs14.mkdir(snapshotDir, { recursive: true });
|
|
52056
51976
|
for (const [fileName, data2] of Object.entries(files)) {
|
|
52057
|
-
const tmpPath =
|
|
52058
|
-
const finalPath =
|
|
52059
|
-
await
|
|
52060
|
-
await
|
|
52061
|
-
}
|
|
52062
|
-
const currentLink =
|
|
52063
|
-
const tmpLink =
|
|
52064
|
-
await
|
|
52065
|
-
await
|
|
51977
|
+
const tmpPath = path18.join(snapshotDir, `${fileName}.tmp`);
|
|
51978
|
+
const finalPath = path18.join(snapshotDir, fileName);
|
|
51979
|
+
await fs14.writeFile(tmpPath, JSON.stringify(data2, null, 2), "utf-8");
|
|
51980
|
+
await fs14.rename(tmpPath, finalPath);
|
|
51981
|
+
}
|
|
51982
|
+
const currentLink = path18.join(cachePath, "current");
|
|
51983
|
+
const tmpLink = path18.join(cachePath, `current-${timestamp2}`);
|
|
51984
|
+
await fs14.symlink(path18.basename(snapshotDir), tmpLink);
|
|
51985
|
+
await fs14.rename(tmpLink, currentLink);
|
|
52066
51986
|
await cleanupOldSnapshots(cachePath, 2);
|
|
52067
51987
|
return snapshotDir;
|
|
52068
51988
|
}
|
|
52069
51989
|
async function cleanupOldSnapshots(cachePath, keepCount) {
|
|
52070
51990
|
let entries;
|
|
52071
51991
|
try {
|
|
52072
|
-
entries = await
|
|
51992
|
+
entries = await fs14.readdir(cachePath);
|
|
52073
51993
|
} catch {
|
|
52074
51994
|
return;
|
|
52075
51995
|
}
|
|
52076
51996
|
const snapshots = entries.filter((e5) => e5.startsWith("snapshot-")).sort().reverse();
|
|
52077
51997
|
for (const snapshot of snapshots.slice(keepCount)) {
|
|
52078
51998
|
try {
|
|
52079
|
-
await
|
|
51999
|
+
await fs14.rm(path18.join(cachePath, snapshot), {
|
|
52080
52000
|
recursive: true,
|
|
52081
52001
|
force: true
|
|
52082
52002
|
});
|
|
@@ -52170,7 +52090,7 @@ async function refreshClerkCache(options) {
|
|
|
52170
52090
|
}
|
|
52171
52091
|
|
|
52172
52092
|
// worker/index.ts
|
|
52173
|
-
import
|
|
52093
|
+
import path19 from "node:path";
|
|
52174
52094
|
async function getSecret(secretArn, retries = 3) {
|
|
52175
52095
|
const { SecretsManagerClient, GetSecretValueCommand } = await Promise.resolve().then(() => __toESM(require_dist_cjs53(), 1));
|
|
52176
52096
|
const client = new SecretsManagerClient({});
|
|
@@ -52209,7 +52129,7 @@ async function main() {
|
|
|
52209
52129
|
if (!clerkSecretKey && process.env.CLERK_SECRET_KEY_SECRET_ARN) {
|
|
52210
52130
|
clerkSecretKey = await getSecret(process.env.CLERK_SECRET_KEY_SECRET_ARN);
|
|
52211
52131
|
}
|
|
52212
|
-
const cachePath =
|
|
52132
|
+
const cachePath = path19.join(workspacePath, ".cache");
|
|
52213
52133
|
const refreshAuthCache = clerkSecretKey ? async () => {
|
|
52214
52134
|
const result = await refreshClerkCache({
|
|
52215
52135
|
secretKey: clerkSecretKey,
|