@@ -25,10 +25,15 @@ import _ "embed"
25
25
//go:embed certificates/quick-lint-js.cer
26
26
var AppleCodesignCertificate []byte
27
27
28
+ //go:embed certificates/quick-lint-js.gpg.key
29
+ var QLJSGPGKey []byte
30
+
28
31
type SigningStuff struct {
29
32
AppleCodesignIdentity string // Common Name from the macOS Keychain.
30
33
Certificate []byte
31
34
CertificateSHA1Hash [20 ]byte
35
+ GPGIdentity string // Fingerprint or email or name.
36
+ GPGKey []byte
32
37
PrivateKeyPKCS12Path string
33
38
}
34
39
@@ -39,8 +44,10 @@ func main() {
39
44
40
45
var signingStuff SigningStuff
41
46
signingStuff .Certificate = AppleCodesignCertificate
47
+ signingStuff .GPGKey = QLJSGPGKey
42
48
43
49
flag .StringVar (& signingStuff .AppleCodesignIdentity , "AppleCodesignIdentity" , "" , "" )
50
+ flag .StringVar (& signingStuff .GPGIdentity , "GPGIdentity" , "" , "" )
44
51
flag .StringVar (& signingStuff .PrivateKeyPKCS12Path , "PrivateKeyPKCS12" , "" , "" )
45
52
flag .Parse ()
46
53
if flag .NArg () != 2 {
@@ -92,10 +99,20 @@ type FileTransformType int
92
99
const (
93
100
NoTransform FileTransformType = iota
94
101
AppleCodesign
102
+ GPGSign
95
103
MicrosoftOsslsigncode
96
104
)
97
105
98
106
var filesToTransform map [string ]map [string ]FileTransformType = map [string ]map [string ]FileTransformType {
107
+ "manual/linux-aarch64.tar.gz" : map [string ]FileTransformType {
108
+ "quick-lint-js/bin/quick-lint-js" : GPGSign ,
109
+ },
110
+ "manual/linux-armhf.tar.gz" : map [string ]FileTransformType {
111
+ "quick-lint-js/bin/quick-lint-js" : GPGSign ,
112
+ },
113
+ "manual/linux.tar.gz" : map [string ]FileTransformType {
114
+ "quick-lint-js/bin/quick-lint-js" : GPGSign ,
115
+ },
99
116
"manual/macos-aarch64.tar.gz" : map [string ]FileTransformType {
100
117
"quick-lint-js/bin/quick-lint-js" : AppleCodesign ,
101
118
},
@@ -114,12 +131,18 @@ var filesToTransform map[string]map[string]FileTransformType = map[string]map[st
114
131
"npm/quick-lint-js-0.5.0.tgz" : map [string ]FileTransformType {
115
132
"package/darwin-aarch64/bin/quick-lint-js" : AppleCodesign ,
116
133
"package/darwin-x64/bin/quick-lint-js" : AppleCodesign ,
134
+ "package/linux-arm/bin/quick-lint-js" : GPGSign ,
135
+ "package/linux-arm64/bin/quick-lint-js" : GPGSign ,
136
+ "package/linux-x64/bin/quick-lint-js" : GPGSign ,
117
137
"package/win32-arm64/bin/quick-lint-js" : MicrosoftOsslsigncode ,
118
138
"package/win32-x64/bin/quick-lint-js" : MicrosoftOsslsigncode ,
119
139
},
120
140
"vscode/quick-lint-js-0.5.0.vsix" : map [string ]FileTransformType {
121
141
"extension/dist/quick-lint-js-vscode-node_darwin-arm64.node" : AppleCodesign ,
122
142
"extension/dist/quick-lint-js-vscode-node_darwin-x64.node" : AppleCodesign ,
143
+ "extension/dist/quick-lint-js-vscode-node_linux-arm.node" : GPGSign ,
144
+ "extension/dist/quick-lint-js-vscode-node_linux-arm64.node" : GPGSign ,
145
+ "extension/dist/quick-lint-js-vscode-node_linux-x64.node" : GPGSign ,
123
146
"extension/dist/quick-lint-js-vscode-node_win32-arm.node" : MicrosoftOsslsigncode ,
124
147
"extension/dist/quick-lint-js-vscode-node_win32-arm64.node" : MicrosoftOsslsigncode ,
125
148
"extension/dist/quick-lint-js-vscode-node_win32-ia32.node" : MicrosoftOsslsigncode ,
@@ -195,6 +218,14 @@ type FileTransformResult struct {
195
218
// If newFile is not nil, Close will delete the file as if by
196
219
// os.Remove(newFile.Name())
197
220
newFile * os.File
221
+
222
+ // If siblingFile is not nil, then a new file is created named
223
+ // siblingFileName.
224
+ //
225
+ // If siblingFile is not nil, Close will delete the file as if by
226
+ // os.Remove(siblingFile.Name()).
227
+ siblingFile * os.File
228
+ siblingFileName string
198
229
}
199
230
200
231
func (self * FileTransformResult ) UpdateTarHeader (header * tar.Header ) error {
@@ -232,6 +263,12 @@ func (self *FileTransformResult) Close() {
232
263
os .Remove (self .newFile .Name ())
233
264
self .newFile = nil
234
265
}
266
+
267
+ if self .siblingFile != nil {
268
+ self .siblingFile .Close ()
269
+ os .Remove (self .siblingFile .Name ())
270
+ self .siblingFile = nil
271
+ }
235
272
}
236
273
237
274
func TransformTarGz (
@@ -252,6 +289,14 @@ func TransformTarGz(
252
289
}
253
290
return transform , nil
254
291
292
+ case GPGSign :
293
+ log .Printf ("signing with GPG: %s:%s\n " , sourceFilePath , path )
294
+ transform , err := GPGSignTransform (path , file , signingStuff )
295
+ if err != nil {
296
+ return FileTransformResult {}, err
297
+ }
298
+ return transform , nil
299
+
255
300
case MicrosoftOsslsigncode :
256
301
log .Printf ("signing with osslsigncode: %s:%s\n " , sourceFilePath , path )
257
302
transform , err := MicrosoftOsslsigncodeTransform (file , signingStuff )
@@ -289,7 +334,16 @@ func TransformTarGzGeneric(
289
334
return err
290
335
}
291
336
292
- transformResult , err := transform (header .Name , tarReader )
337
+ var fileContent bytes.Buffer
338
+ bytesWritten , err := fileContent .ReadFrom (tarReader )
339
+ if err != nil {
340
+ return err
341
+ }
342
+ if bytesWritten != header .Size {
343
+ return fmt .Errorf ("failed to read entire file" )
344
+ }
345
+
346
+ transformResult , err := transform (header .Name , bytes .NewReader (fileContent .Bytes ()))
293
347
if err != nil {
294
348
return err
295
349
}
@@ -298,13 +352,35 @@ func TransformTarGzGeneric(
298
352
if err := transformResult .UpdateTarHeader (header ); err != nil {
299
353
return err
300
354
}
301
- var file io.Reader = tarReader
355
+ var file io.Reader = bytes . NewReader ( fileContent . Bytes ())
302
356
if transformResult .newFile != nil {
303
357
file = transformResult .newFile
304
358
}
305
359
if err := WriteTarEntry (header , file , tarWriter ); err != nil {
306
360
return err
307
361
}
362
+
363
+ if transformResult .siblingFile != nil {
364
+ siblingFileStat , err := transformResult .siblingFile .Stat ()
365
+ if err != nil {
366
+ return err
367
+ }
368
+ siblingHeader := tar.Header {
369
+ Typeflag : tar .TypeReg ,
370
+ Name : filepath .Join (filepath .Dir (header .Name ), transformResult .siblingFileName ),
371
+ Size : siblingFileStat .Size (),
372
+ Mode : header .Mode &^ 0111 ,
373
+ Uid : header .Uid ,
374
+ Gid : header .Gid ,
375
+ Uname : header .Uname ,
376
+ Gname : header .Gname ,
377
+ ModTime : siblingFileStat .ModTime (),
378
+ Format : tar .FormatUSTAR ,
379
+ }
380
+ if err := WriteTarEntry (& siblingHeader , transformResult .siblingFile , tarWriter ); err != nil {
381
+ return err
382
+ }
383
+ }
308
384
}
309
385
return nil
310
386
}
@@ -326,6 +402,14 @@ func TransformZip(
326
402
}
327
403
return transform , nil
328
404
405
+ case GPGSign :
406
+ log .Printf ("signing with GPG: %s:%s\n " , sourceFile .Name (), path )
407
+ transform , err := GPGSignTransform (path , file , signingStuff )
408
+ if err != nil {
409
+ return FileTransformResult {}, err
410
+ }
411
+ return transform , nil
412
+
329
413
case MicrosoftOsslsigncode :
330
414
log .Printf ("signing with osslsigncode: %s:%s\n " , sourceFile .Name (), path )
331
415
transform , err := MicrosoftOsslsigncodeTransform (file , signingStuff )
@@ -397,6 +481,17 @@ func TransformZipGeneric(
397
481
return err
398
482
}
399
483
}
484
+
485
+ if transformResult .siblingFile != nil {
486
+ siblingZIPEntryFile , err := destinationZipFile .Create (filepath .Join (filepath .Dir (zipEntry .Name ), transformResult .siblingFileName ))
487
+ if err != nil {
488
+ return err
489
+ }
490
+ _ , err = io .Copy (siblingZIPEntryFile , transformResult .siblingFile )
491
+ if err != nil {
492
+ return err
493
+ }
494
+ }
400
495
}
401
496
return nil
402
497
}
@@ -479,6 +574,109 @@ func AppleCodesignVerifyFile(filePath string, signingStuff SigningStuff) error {
479
574
return nil
480
575
}
481
576
577
+ // originalPath need not be a path to a real file.
578
+ func GPGSignTransform (originalPath string , exe io.Reader , signingStuff SigningStuff ) (FileTransformResult , error ) {
579
+ tempDir , err := ioutil .TempDir ("" , "quick-lint-js-sign-release" )
580
+ if err != nil {
581
+ return FileTransformResult {}, err
582
+ }
583
+ TempDirs = append (TempDirs , tempDir )
584
+
585
+ tempFile , err := os .Create (filepath .Join (tempDir , "data" ))
586
+ if err != nil {
587
+ return FileTransformResult {}, err
588
+ }
589
+ defer os .Remove (tempFile .Name ())
590
+ _ , err = io .Copy (tempFile , exe )
591
+ tempFile .Close ()
592
+ if err != nil {
593
+ return FileTransformResult {}, err
594
+ }
595
+
596
+ signatureFilePath , err := GPGSignFile (tempFile .Name (), signingStuff )
597
+ if err != nil {
598
+ return FileTransformResult {}, err
599
+ }
600
+ if err := GPGVerifySignature (tempFile .Name (), signatureFilePath , signingStuff ); err != nil {
601
+ return FileTransformResult {}, err
602
+ }
603
+
604
+ signatureFile , err := os .Open (signatureFilePath )
605
+ if err != nil {
606
+ return FileTransformResult {}, err
607
+ }
608
+
609
+ return FileTransformResult {
610
+ siblingFile : signatureFile ,
611
+ siblingFileName : filepath .Base (originalPath ) + ".asc" ,
612
+ }, nil
613
+ }
614
+
615
+ func GPGSignFile (filePath string , signingStuff SigningStuff ) (string , error ) {
616
+ process := exec .Command (
617
+ "gpg" ,
618
+ "--local-user" , signingStuff .GPGIdentity ,
619
+ "--armor" ,
620
+ "--detach-sign" ,
621
+ "--" ,
622
+ filePath ,
623
+ )
624
+ process .Stdout = os .Stdout
625
+ process .Stderr = os .Stderr
626
+ if err := process .Start (); err != nil {
627
+ return "" , err
628
+ }
629
+ if err := process .Wait (); err != nil {
630
+ return "" , err
631
+ }
632
+ return filePath + ".asc" , nil
633
+ }
634
+
635
+ func GPGVerifySignature (filePath string , signatureFilePath string , signingStuff SigningStuff ) error {
636
+ // HACK(strager): Use /tmp instead of the default temp dir. macOS'
637
+ // default temp dir is so long that it breaks gpg-agent.
638
+ tempGPGHome , err := ioutil .TempDir ("/tmp" , "quick-lint-js-sign-release" )
639
+ if err != nil {
640
+ return err
641
+ }
642
+ TempDirs = append (TempDirs , tempGPGHome )
643
+
644
+ var env []string
645
+ env = append ([]string {}, os .Environ ()... )
646
+ env = append (env , "GNUPGHOME=" + tempGPGHome )
647
+
648
+ process := exec .Command ("gpg" , "--import" )
649
+ keyReader := bytes .NewReader (signingStuff .GPGKey )
650
+ process .Stdin = keyReader
651
+ process .Stdout = os .Stdout
652
+ process .Stderr = os .Stderr
653
+ process .Env = env
654
+ if err := process .Start (); err != nil {
655
+ return err
656
+ }
657
+ if err := process .Wait (); err != nil {
658
+ return err
659
+ }
660
+
661
+ process = exec .Command (
662
+ "gpg" , "--verify" ,
663
+ "--" ,
664
+ signatureFilePath ,
665
+ filePath ,
666
+ )
667
+ process .Stdout = os .Stdout
668
+ process .Stderr = os .Stderr
669
+ process .Env = env
670
+ if err := process .Start (); err != nil {
671
+ return err
672
+ }
673
+ if err := process .Wait (); err != nil {
674
+ return err
675
+ }
676
+
677
+ return nil
678
+ }
679
+
482
680
func MicrosoftOsslsigncodeTransform (exe io.Reader , signingStuff SigningStuff ) (FileTransformResult , error ) {
483
681
tempDir , err := ioutil .TempDir ("" , "quick-lint-js-sign-release" )
484
682
if err != nil {
0 commit comments