Skip to content

Commit 6fe994e

Browse files
y3rshsfoster1
andauthored
build: Sign windows builds via remote key storage (#15718) (#15735)
## `cherry-pick` 246efcb Must update `chore_release-7.4.0` so that windows builds work and we may test an update. To understand the changes see #15718 Co-authored-by: Seth Foster <[email protected]>
1 parent 9b8eafc commit 6fe994e

File tree

3 files changed

+134
-11
lines changed

3 files changed

+134
-11
lines changed

.github/workflows/app-test-build-deploy.yaml

+66-11
Original file line numberDiff line numberDiff line change
@@ -184,24 +184,45 @@ jobs:
184184
echo "both develop builds for edge"
185185
echo 'variants=["release", "internal-release"]' >> $GITHUB_OUTPUT
186186
echo 'type=develop' >> $GITHUB_OUTPUT
187-
elif [ "${{ format('{0}', endsWith(github.ref, 'app-build-internal')) }}" = "true" ] ; then
188-
echo "internal-release builds for app-build-internal suffixes"
187+
elif [ "${{ format('{0}', contains(github.ref, 'app-build-internal')) }}" = "true" ] ; then
188+
189189
echo 'variants=["internal-release"]' >> $GITHUB_OUTPUT
190-
echo 'type=develop' >> $GITHUB_OUTPUT
191-
elif [ "${{ format('{0}', endsWith(github.ref, 'app-build')) }}" = "true" ] ; then
192-
echo "release develop builds for app-build suffixes"
190+
if [ "${{ format('{0}', contains(github.ref, 'as-release')) }}" = "true" ] ; then
191+
echo "internal-release as-release builds for app-build-internal + as-release suffixes"
192+
echo 'type=as-release' >> $GITHUB_OUTPUT
193+
else
194+
echo "internal-release develop builds for app-build-internal suffixes"
195+
echo 'type=develop' >> $GITHUB_OUTPUT
196+
fi
197+
elif [ "${{ format('{0}', contains(github.ref, 'app-build')) }}" = "true" ] ; then
193198
echo 'variants=["release"]' >> $GITHUB_OUTPUT
194-
echo 'type=develop' >> $GITHUB_OUTPUT
195-
elif [ "${{ format('{0}', endsWith(github.ref, 'app-build-both')) }}" = "true" ] ; then
196-
echo "Both develop builds for app-build-both suffixes"
199+
if [ "${{ format('{0}', contains(github.ref, 'as-release')) }}" = "true" ] ; then
200+
echo "release as-release builds for app-build + as-release suffixes"
201+
echo 'type=as-release' >> $GITHUB_OUTPUT
202+
else
203+
echo "release develop builds for app-build suffixes"
204+
echo 'type=develop' >> $GITHUB_OUTPUT
205+
fi
206+
elif [ "${{ format('{0}', contains(github.ref, 'app-build-both')) }}" = "true" ] ; then
207+
197208
echo 'variants=["release", "internal-release"]' >> $GITHUB_OUTPUT
198-
echo 'type=develop' >> $GITHUB_OUTPUT
209+
if [ "${{ format('{0}', contains(github.ref, 'as-release')) }}" = "true" ] ; then
210+
echo "Both as-release builds for app-build-both + as-release suffixes"
211+
echo 'type=as-release' >> $GITHUB_OUTPUT
212+
else
213+
echo "Both develop builds for app-build-both + as-release suffixes"
214+
echo 'type=develop' >> $GITHUB_OUTPUT
215+
fi
199216
else
200217
echo "No build for ref ${{github.ref}} and event ${{github.event_type}}"
201218
echo 'variants=[]' >> $GITHUB_OUTPUT
202219
echo 'type=develop' >> $GITHUB_OUTPUT
203220
fi
204221
222+
- name: set summary
223+
run: |
224+
echo 'Type: ${{steps.determine-build-type.outputs.type}} Variants: ${{steps.determine-build-type.outputs.variants}}' >> $GITHUB_STEP_SUMMARY
225+
205226
build-app:
206227
needs: [determine-build-type]
207228
if: needs.determine-build-type.outputs.variants != '[]'
@@ -276,15 +297,49 @@ jobs:
276297
npm config set cache ${{ github.workspace }}/.npm-cache
277298
yarn config set cache-folder ${{ github.workspace }}/.yarn-cache
278299
make setup-js
300+
301+
- name: 'Configure Windows code signing environment'
302+
if: startsWith(matrix.os, 'windows') && contains(needs.determine-build-type.outputs.type, 'release')
303+
shell: bash
304+
run: |
305+
echo "${{ secrets.SM_CLIENT_CERT_FILE_B64 }}" | base64 --decode > /d/Certificate_pkcs12.p12
306+
echo "${{ secrets.WINDOWS_CSC_B64}}" | base64 --decode > /d/opentrons_labworks_inc.crt
307+
echo "C:\Program Files (x86)\Windows Kits\10\App Certification Kit" >> $GITHUB_PATH
308+
echo "C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools" >> $GITHUB_PATH
309+
echo "C:\Program Files\DigiCert\DigiCert Keylocker Tools" >> $GITHUB_PATH
310+
311+
- name: 'Setup Windows code signing helpers'
312+
if: startsWith(matrix.os, 'windows') && contains(needs.determine-build-type.outputs.type, 'release')
313+
shell: cmd
314+
env:
315+
SM_HOST: ${{ secrets.SM_HOST }}
316+
SM_CLIENT_CERT_FILE: "D:\\Certificate_pkcs12.p12"
317+
SM_CLIENT_CERT_PASSWORD: ${{secrets.SM_CLIENT_CERT_PASSWORD}}
318+
SM_API_KEY: ${{secrets.SM_API_KEY}}
319+
run: |
320+
curl -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/Keylockertools-windows-x64.msi/download -H "x-api-key:${{secrets.SM_API_KEY}}" -o Keylockertools-windows-x64.msi
321+
msiexec /i Keylockertools-windows-x64.msi /quiet /qn
322+
smksp_registrar.exe list
323+
smctl.exe keypair ls
324+
C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user
325+
smksp_cert_sync.exe
326+
smctl.exe healthcheck --all
327+
279328
# build the desktop app and deploy it
280329
- name: 'build ${{matrix.variant}} app for ${{ matrix.os }}'
281330
if: matrix.target == 'desktop'
282331
timeout-minutes: 60
283332
env:
284333
OT_APP_MIXPANEL_ID: ${{ secrets.OT_APP_MIXPANEL_ID }}
285334
OT_APP_INTERCOM_ID: ${{ secrets.OT_APP_INTERCOM_ID }}
286-
WIN_CSC_LINK: ${{ secrets.OT_APP_CSC_WINDOWS }}
287-
WIN_CSC_KEY_PASSWORD: ${{ secrets.OT_APP_CSC_KEY_WINDOWS }}
335+
WINDOWS_SIGN: ${{ format('{0}', contains(needs.determine-build-type.outputs.type, 'release')) }}
336+
SM_HOST: ${{secrets.SM_HOST}}
337+
SM_CLIENT_CERT_FILE: "D:\\Certificate_pkcs12.p12"
338+
SM_CLIENT_CERT_PASSWORD: ${{secrets.SM_CLIENT_CERT_PASSWORD}}
339+
SM_API_KEY: ${{secrets.SM_API_KEY}}
340+
SM_CODE_SIGNING_CERT_SHA1_HASH: ${{secrets.SM_CODE_SIGNING_CERT_SHA1_HASH}}
341+
SM_KEYPAIR_ALIAS: ${{secrets.SM_KEYPAIR_ALIAS}}
342+
WINDOWS_CSC_FILEPATH: "D:\\opentrons_labworks_inc.crt"
288343
CSC_LINK: ${{ secrets.OT_APP_CSC_MACOS }}
289344
CSC_KEY_PASSWORD: ${{ secrets.OT_APP_CSC_KEY_MACOS }}
290345
APPLE_ID: ${{ secrets.OT_APP_APPLE_ID }}

app-shell/electron-builder.config.js

+6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const {
88
} = process.env
99
const DEV_MODE = process.env.NODE_ENV !== 'production'
1010
const USE_PYTHON = process.env.NO_PYTHON !== 'true'
11+
const WINDOWS_SIGN = process.env.WINDOWS_SIGN === 'true'
1112
const project = process.env.OPENTRONS_PROJECT ?? 'robot-stack'
1213

1314
// this will generate either
@@ -72,6 +73,11 @@ module.exports = async () => ({
7273
target: ['nsis'],
7374
publisherName: 'Opentrons Labworks Inc.',
7475
icon: project === 'robot-stack' ? 'build/icon.ico' : 'build/three.ico',
76+
forceCodeSigning: WINDOWS_SIGN,
77+
rfc3161TimeStampServer: 'http://timestamp.digicert.com',
78+
sign: 'scripts/windows-custom-sign.js',
79+
signDlls: true,
80+
signingHashAlgorithms: ['sha256'],
7581
},
7682
nsis: {
7783
oneClick: false,
+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// from https://github.com/electron-userland/electron-builder/issues/7605
2+
3+
'use strict'
4+
5+
const { execSync } = require('node:child_process')
6+
7+
exports.default = async configuration => {
8+
const signCmd = `smctl sign --keypair-alias="${String(
9+
process.env.SM_KEYPAIR_ALIAS
10+
)}" --input "${String(configuration.path)}" --certificate="${String(
11+
process.env.WINDOWS_CSC_FILEPATH
12+
)}" --exit-non-zero-on-fail --failfast --verbose`
13+
console.log(signCmd)
14+
try {
15+
const signProcess = execSync(signCmd, {
16+
stdio: 'pipe',
17+
})
18+
console.log(`Sign success!`)
19+
console.log(
20+
`Sign stdout: ${signProcess?.stdout?.toString() ?? '<no output>'}`
21+
)
22+
console.log(
23+
`Sign stderr: ${signProcess?.stderr?.toString() ?? '<no output>'}`
24+
)
25+
console.log(`Sign code: ${signProcess.code}`)
26+
} catch (err) {
27+
console.error(`Exception running sign: ${err.status}!
28+
Process stdout:
29+
${err?.stdout?.toString() ?? '<no output>'}
30+
-------------
31+
Process stderr:
32+
${err?.stdout?.toString() ?? '<no output>'}
33+
-------------
34+
`)
35+
throw err
36+
}
37+
const verifyCmd = `smctl sign verify --fingerprint="${String(
38+
process.env.SM_CODE_SIGNING_CERT_SHA1_HASH
39+
)}" --input="${String(configuration.path)}" --verbose`
40+
console.log(verifyCmd)
41+
try {
42+
const verifyProcess = execSync(verifyCmd, { stdio: 'pipe' })
43+
console.log(`Verify success!`)
44+
console.log(
45+
`Verify stdout: ${verifyProcess?.stdout?.toString() ?? '<no output>'}`
46+
)
47+
console.log(
48+
`Verify stderr: ${verifyProcess?.stderr?.toString() ?? '<no output>'}`
49+
)
50+
} catch (err) {
51+
console.error(`
52+
Exception running verification: ${err.status}!
53+
Process stdout:
54+
${err?.stdout?.toString() ?? '<no output>'}
55+
--------------
56+
Process stderr:
57+
${err?.stderr?.toString() ?? '<no output>'}
58+
--------------
59+
`)
60+
throw err
61+
}
62+
}

0 commit comments

Comments
 (0)