Skip to content

Commit 9955705

Browse files
authored
Fix signing for new release build pipeline (#1629)
1 parent f89a64d commit 9955705

File tree

1 file changed

+132
-4
lines changed

1 file changed

+132
-4
lines changed

Herebyfile.mjs

Lines changed: 132 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -751,15 +751,16 @@ let signCount = 0;
751751
/**
752752
* @typedef {{
753753
* SignFileRecordList: {
754-
* SignFileList: { SrcPath: string; DstPath: string | null; }[];
754+
* SignFileList: { SrcPath: string; DstPath: string | null }[];
755755
* Certs: Cert;
756+
* MacAppName: string | undefined
756757
* }[]
757758
* }} DDSignFileList
758759
*
759760
* @param {DDSignFileList} filelist
760761
*/
761-
async function sign(filelist) {
762-
const data = JSON.stringify(filelist, undefined, 4);
762+
async function sign(filelist, unchangedOutputOkay = false) {
763+
let data = JSON.stringify(filelist, undefined, 4);
763764
console.log("filelist:", data);
764765

765766
if (!process.env.MBSIGN_APPFOLDER) {
@@ -802,6 +803,71 @@ async function sign(filelist) {
802803
return;
803804
}
804805

806+
const signingWorkaround = true;
807+
808+
/** @type {{ source: string; target: string }[]} */
809+
const signingWorkaroundFiles = [];
810+
811+
if (signingWorkaround) {
812+
// DstPath is currently broken in the signing tool.
813+
// Copy all of the files to a new tempdir and then leave DstPath unset
814+
// so that it's overwritten, then move the file to the destination.
815+
console.log("Working around DstPath bug");
816+
817+
/** @type {DDSignFileList} */
818+
const newFileList = {
819+
SignFileRecordList: filelist.SignFileRecordList.map(list => {
820+
return {
821+
Certs: list.Certs,
822+
SignFileList: list.SignFileList.map(file => {
823+
const dstPath = file.DstPath;
824+
if (dstPath === null) {
825+
return file;
826+
}
827+
828+
const src = file.SrcPath;
829+
// File extensions must be preserved; use a prefix.
830+
const dstPathTemp = `${path.dirname(src)}/signing-temp-${path.basename(src)}`;
831+
832+
console.log(`Copying: ${src} -> ${dstPathTemp}`);
833+
fs.cpSync(src, dstPathTemp);
834+
835+
signingWorkaroundFiles.push({ source: dstPathTemp, target: dstPath });
836+
837+
return {
838+
SrcPath: dstPathTemp,
839+
DstPath: null,
840+
};
841+
}),
842+
MacAppName: list.MacAppName,
843+
};
844+
}),
845+
};
846+
847+
data = JSON.stringify(newFileList, undefined, 4);
848+
console.log("new filelist:", data);
849+
}
850+
851+
/** @type {Map<string, string>} */
852+
const srcHashes = new Map();
853+
854+
for (const record of filelist.SignFileRecordList) {
855+
for (const file of record.SignFileList) {
856+
const src = file.SrcPath;
857+
const dst = file.DstPath ?? src;
858+
859+
if (!fs.existsSync(src)) {
860+
throw new Error(`Source file does not exist: ${src}`);
861+
}
862+
863+
const hash = crypto.createHash("sha256").update(fs.readFileSync(src)).digest("hex");
864+
srcHashes.set(src, hash);
865+
866+
console.log(`Will sign ${src} -> ${dst}`);
867+
console.log(` sha256: ${hash}`);
868+
}
869+
}
870+
805871
const tmp = await getSignTempDir();
806872
const filelistPath = path.resolve(tmp, `signing-filelist-${signCount++}.json`);
807873
await fs.promises.writeFile(filelistPath, data);
@@ -814,6 +880,61 @@ async function sign(filelist) {
814880
finally {
815881
await fs.promises.unlink(filelistPath);
816882
}
883+
884+
if (signingWorkaround) {
885+
// Now, copy the files back.
886+
for (const { source, target } of signingWorkaroundFiles) {
887+
console.log(`Moving signed file: ${source} -> ${target}`);
888+
await fs.promises.rename(source, target);
889+
}
890+
}
891+
892+
/** @type {string[]} */
893+
let failures = [];
894+
895+
for (const record of filelist.SignFileRecordList) {
896+
for (const file of record.SignFileList) {
897+
const src = file.SrcPath;
898+
const dst = file.DstPath ?? src;
899+
900+
if (!fs.existsSync(dst)) {
901+
failures.push(`Signed file does not exist: ${dst}`);
902+
const newSrcHash = crypto.createHash("sha256").update(fs.readFileSync(src)).digest("hex");
903+
const oldSrcHash = srcHashes.get(src);
904+
assert(oldSrcHash);
905+
if (oldSrcHash !== newSrcHash) {
906+
failures.push(` Source file changed during signing: ${src}\n before: ${oldSrcHash}\n after: ${newSrcHash}`);
907+
}
908+
continue;
909+
}
910+
911+
const srcHash = srcHashes.get(src);
912+
assert(srcHash);
913+
const dstHash = crypto.createHash("sha256").update(fs.readFileSync(dst)).digest("hex");
914+
if (srcHash === dstHash) {
915+
const message = `Signed file is identical to source file (not signed?): ${src} -> ${dst}\n sha256: ${dstHash}`;
916+
if (unchangedOutputOkay) {
917+
console.log(message);
918+
}
919+
else {
920+
failures.push(message);
921+
continue;
922+
}
923+
}
924+
925+
if (src === dst) {
926+
console.log(`Signed ${src}`);
927+
}
928+
else {
929+
console.log(`Signed ${src} -> ${dst}`);
930+
}
931+
console.log(` sha256: ${dstHash}`);
932+
}
933+
}
934+
935+
if (failures.length) {
936+
throw new Error("Some files failed to sign:\n" + failures.map(f => " - " + f).join("\n"));
937+
}
817938
}
818939

819940
/**
@@ -1065,12 +1186,14 @@ export const signNativePreviewPackages = task({
10651186
filelist.SignFileRecordList.push({
10661187
SignFileList: filelistPaths.map(p => ({ SrcPath: p.path, DstPath: null })),
10671188
Certs: cert,
1189+
MacAppName: undefined,
10681190
});
10691191
break;
10701192
case "LinuxSign":
10711193
filelist.SignFileRecordList.push({
10721194
SignFileList: filelistPaths.map(p => ({ SrcPath: p.path, DstPath: p.path + ".sig" })),
10731195
Certs: cert,
1196+
MacAppName: undefined,
10741197
});
10751198
break;
10761199
case "MacDeveloperHarden":
@@ -1095,6 +1218,7 @@ export const signNativePreviewPackages = task({
10951218
filelist.SignFileRecordList.push({
10961219
SignFileList: macZips.map(p => ({ SrcPath: p.unsignedZipPath, DstPath: p.signedZipPath })),
10971220
Certs: cert,
1221+
MacAppName: undefined, // MacAppName is only for notarization
10981222
});
10991223
break;
11001224
default:
@@ -1115,11 +1239,14 @@ export const signNativePreviewPackages = task({
11151239
{
11161240
SignFileList: macZips.map(p => ({ SrcPath: p.signedZipPath, DstPath: p.notarizedZipPath })),
11171241
Certs: "8020", // "MacNotarize" (friendly name not supported by the tooling)
1242+
MacAppName: "MicrosoftTypeScript",
11181243
},
11191244
],
11201245
};
11211246

1122-
await sign(notarizeFilelist);
1247+
// Notarizing does not change the file, it just sends it to Apple, so ignore the case
1248+
// where the input files are the same as the output files.
1249+
await sign(notarizeFilelist, /*unchangedOutputOkay*/ true);
11231250

11241251
// Finally, unzip the notarized files and move them back to their original locations.
11251252

@@ -1234,6 +1361,7 @@ export const signNativePreviewExtensions = task({
12341361
{
12351362
SignFileList: extensions.map(({ vsixSignaturePath }) => ({ SrcPath: vsixSignaturePath, DstPath: null })),
12361363
Certs: "VSCodePublisher",
1364+
MacAppName: undefined,
12371365
},
12381366
],
12391367
});

0 commit comments

Comments
 (0)