@@ -751,15 +751,16 @@ let signCount = 0;
751
751
/**
752
752
* @typedef {{
753
753
* SignFileRecordList: {
754
- * SignFileList: { SrcPath: string; DstPath: string | null; }[];
754
+ * SignFileList: { SrcPath: string; DstPath: string | null }[];
755
755
* Certs: Cert;
756
+ * MacAppName: string | undefined
756
757
* }[]
757
758
* }} DDSignFileList
758
759
*
759
760
* @param {DDSignFileList } filelist
760
761
*/
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 ) ;
763
764
console . log ( "filelist:" , data ) ;
764
765
765
766
if ( ! process . env . MBSIGN_APPFOLDER ) {
@@ -802,6 +803,71 @@ async function sign(filelist) {
802
803
return ;
803
804
}
804
805
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
+
805
871
const tmp = await getSignTempDir ( ) ;
806
872
const filelistPath = path . resolve ( tmp , `signing-filelist-${ signCount ++ } .json` ) ;
807
873
await fs . promises . writeFile ( filelistPath , data ) ;
@@ -814,6 +880,61 @@ async function sign(filelist) {
814
880
finally {
815
881
await fs . promises . unlink ( filelistPath ) ;
816
882
}
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
+ }
817
938
}
818
939
819
940
/**
@@ -1065,12 +1186,14 @@ export const signNativePreviewPackages = task({
1065
1186
filelist . SignFileRecordList . push ( {
1066
1187
SignFileList : filelistPaths . map ( p => ( { SrcPath : p . path , DstPath : null } ) ) ,
1067
1188
Certs : cert ,
1189
+ MacAppName : undefined ,
1068
1190
} ) ;
1069
1191
break ;
1070
1192
case "LinuxSign" :
1071
1193
filelist . SignFileRecordList . push ( {
1072
1194
SignFileList : filelistPaths . map ( p => ( { SrcPath : p . path , DstPath : p . path + ".sig" } ) ) ,
1073
1195
Certs : cert ,
1196
+ MacAppName : undefined ,
1074
1197
} ) ;
1075
1198
break ;
1076
1199
case "MacDeveloperHarden" :
@@ -1095,6 +1218,7 @@ export const signNativePreviewPackages = task({
1095
1218
filelist . SignFileRecordList . push ( {
1096
1219
SignFileList : macZips . map ( p => ( { SrcPath : p . unsignedZipPath , DstPath : p . signedZipPath } ) ) ,
1097
1220
Certs : cert ,
1221
+ MacAppName : undefined , // MacAppName is only for notarization
1098
1222
} ) ;
1099
1223
break ;
1100
1224
default :
@@ -1115,11 +1239,14 @@ export const signNativePreviewPackages = task({
1115
1239
{
1116
1240
SignFileList : macZips . map ( p => ( { SrcPath : p . signedZipPath , DstPath : p . notarizedZipPath } ) ) ,
1117
1241
Certs : "8020" , // "MacNotarize" (friendly name not supported by the tooling)
1242
+ MacAppName : "MicrosoftTypeScript" ,
1118
1243
} ,
1119
1244
] ,
1120
1245
} ;
1121
1246
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 ) ;
1123
1250
1124
1251
// Finally, unzip the notarized files and move them back to their original locations.
1125
1252
@@ -1234,6 +1361,7 @@ export const signNativePreviewExtensions = task({
1234
1361
{
1235
1362
SignFileList : extensions . map ( ( { vsixSignaturePath } ) => ( { SrcPath : vsixSignaturePath , DstPath : null } ) ) ,
1236
1363
Certs : "VSCodePublisher" ,
1364
+ MacAppName : undefined ,
1237
1365
} ,
1238
1366
] ,
1239
1367
} ) ;
0 commit comments