11name : Deployment
2- run-name : ${{ inputs.tag_name }} / ${{ inputs.environment }}
2+ run-name : ${{ inputs.tag_name }} / ${{ inputs.environment }}${{ inputs.dry_run == true && ' (dry run)' || '' }}
33
44concurrency :
55 group : ${{ github.workflow }}-${{ github.ref_name }}
1616 tag_name :
1717 required : true
1818 type : string
19+ description : " The tag name for the release (e.g. v2.100.0)."
1920 environment :
2021 default : production
2122 type : environment
23+ description : " The deployment environment."
2224 platforms :
2325 default : " linux,macos,windows"
2426 type : string
27+ description : " Comma-separated list of platforms to build."
2528 release :
26- description : " Whether to create a GitHub Release "
29+ description : " Whether to run the final release job. the dry_run flag still blocks final submissions. "
2730 type : boolean
2831 default : true
29-
32+ dry_run :
33+ description : " Perform a dry run without publishing artifacts or creating a release"
34+ type : boolean
35+ default : true
36+
3037jobs :
3138 validate-tag-name :
3239 runs-on : ubuntu-latest
4451 runs-on : ubuntu-latest
4552 environment : ${{ inputs.environment }}
4653 if : contains(inputs.platforms, 'linux')
54+ timeout-minutes : 20
4755 steps :
4856 - name : Checkout
4957 uses : actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
@@ -87,31 +95,55 @@ jobs:
8795 runs-on : macos-latest
8896 environment : ${{ inputs.environment }}
8997 if : contains(inputs.platforms, 'macos')
98+ timeout-minutes : 20
9099 steps :
91100 - name : Checkout
92101 uses : actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
93102 - name : Set up Go
94103 uses : actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
95104 with :
96105 go-version-file : ' go.mod'
97- - name : Configure macOS signing
106+
107+ - name : Install code signing certificate
98108 if : inputs.environment == 'production'
109+ shell : bash
99110 env :
100- APPLE_DEVELOPER_ID : ${{ vars.APPLE_DEVELOPER_ID }}
101- APPLE_APPLICATION_CERT : ${{ secrets.APPLE_APPLICATION_CERT }}
102- APPLE_APPLICATION_CERT_PASSWORD : ${{ secrets.APPLE_APPLICATION_CERT_PASSWORD }}
111+ DEVELOPER_ID_CERT : ${{ secrets.GATEWATCHER_DEVELOPER_ID_CERT }}
112+ DEVELOPER_ID_CERT_PASSWORD : ${{ secrets.GATEWATCHER_DEVELOPER_ID_PASSWORD }}
103113 run : |
104- keychain="$RUNNER_TEMP/buildagent.keychain"
105- keychain_password="password1"
114+ # create a keychain for the certificate
115+ PW=pwd.${{ github.run_number }}
116+ security create-keychain -p $PW "$RUNNER_TEMP/build.keychain"
117+ security set-keychain-settings -lut 21600 "$RUNNER_TEMP/build.keychain"
118+ security default-keychain -s "$RUNNER_TEMP/build.keychain"
119+ security unlock-keychain -p $PW "$RUNNER_TEMP/build.keychain"
106120
107- security create-keychain -p "$keychain_password" "$keychain"
108- security default-keychain -s "$keychain"
109- security unlock-keychain -p "$keychain_password" "$keychain"
110-
111- base64 -D <<<"$APPLE_APPLICATION_CERT" > "$RUNNER_TEMP/cert.p12"
112- security import "$RUNNER_TEMP/cert.p12" -k "$keychain" -P "$APPLE_APPLICATION_CERT_PASSWORD" -T /usr/bin/codesign
113- security set-key-partition-list -S "apple-tool:,apple:,codesign:" -s -k "$keychain_password" "$keychain"
121+ # import the certificate
122+ base64 -d <<< "$DEVELOPER_ID_CERT" > "$RUNNER_TEMP/cert.p12"
123+ security import "$RUNNER_TEMP/cert.p12" -k "$RUNNER_TEMP/build.keychain" -P "$DEVELOPER_ID_CERT_PASSWORD" -T /usr/bin/codesign
124+ security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $PW "$RUNNER_TEMP/build.keychain"
114125 rm "$RUNNER_TEMP/cert.p12"
126+
127+ - name : Add App Store Connect API key to keychain
128+ if : inputs.environment == 'production'
129+ uses : nodeselector/setup-apple-codesign@ab275d0f6fb63ef9e20b12b42ea0d567f935723c
130+ id : setup-apple-codesign
131+ with :
132+ asset-type : " app-store-connect-api-key"
133+ app-store-connect-api-key-key-id : ${{ secrets.GATEWATCHER_APP_STORE_CONNECT_API_KEY_ID }}
134+ app-store-connect-api-key-issuer-id : ${{ secrets.GATEWATCHER_APP_STORE_CONNECT_API_ISSUER_ID }}
135+ app-store-connect-api-key-base64-private-key : ${{ secrets.GATEWATCHER_APP_STORE_CONNECT_API_BASE64_PRIVATE_KEY }}
136+
137+ - name : Configure notarization credentials
138+ if : inputs.environment == 'production'
139+ shell : bash
140+ run : |
141+ xcrun notarytool store-credentials "notarytool-password" \
142+ --key "${{ steps.setup-apple-codesign.outputs.app-store-connect-api-key-key-path }}" \
143+ --key-id "${{ steps.setup-apple-codesign.outputs.app-store-connect-api-key-key-id }}" \
144+ --issuer "${{ steps.setup-apple-codesign.outputs.app-store-connect-api-key-issuer-id }}" \
145+ --keychain "$RUNNER_TEMP/build.keychain"
146+
115147 - name : Install GoReleaser
116148 uses : goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7.2.3
117149 with :
@@ -128,14 +160,16 @@ jobs:
128160 - name : Build release binaries
129161 env :
130162 TAG_NAME : ${{ inputs.tag_name }}
131- APPLE_DEVELOPER_ID : ${{ vars.APPLE_DEVELOPER_ID }}
163+ KEYCHAIN : ${{ runner.temp }}/build.keychain
164+ DEVELOPER_ID_CERT_IDENTIFIER : ${{ vars.MAC_APP_SIGNING_IDENTITY }}
165+ DO_SIGN_ARTIFACTS : ${{ inputs.environment == 'production' }}
132166 run : script/release --local "$TAG_NAME" --platform macos
133167 - name : Notarize macOS archives
134168 if : inputs.environment == 'production'
135169 env :
136- APPLE_ID : ${{ vars.APPLE_ID }}
137- APPLE_ID_PASSWORD : ${{ secrets.APPLE_ID_PASSWORD }}
138- APPLE_DEVELOPER_ID : ${{ vars.APPLE_DEVELOPER_ID }}
170+ DEVELOPER_ID_CERT_IDENTIFIER : ${{ vars.MAC_APP_SIGNING_IDENTITY }}
171+ KEYCHAIN : ${{ runner.temp }}/build.keychain
172+ DO_SIGN_ARTIFACTS : ${{ inputs.environment == 'production' }} # Technically redundant given the step guard above, but kept for consistency with the build step.
139173 run : |
140174 shopt -s failglob
141175 script/sign dist/gh_*_macOS_*.zip
@@ -167,6 +201,7 @@ jobs:
167201 runs-on : windows-2022
168202 environment : ${{ inputs.environment }}
169203 if : contains(inputs.platforms, 'windows')
204+ timeout-minutes : 20
170205 steps :
171206 - name : Checkout
172207 uses : actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
@@ -225,6 +260,7 @@ jobs:
225260 DLIB_PATH : ${{ runner.temp }}\acs\bin\x64\Azure.CodeSigning.Dlib.dll
226261 METADATA_PATH : ${{ runner.temp }}\acs\metadata.json
227262 TAG_NAME : ${{ inputs.tag_name }}
263+ DO_SIGN_ARTIFACTS : ${{ inputs.environment == 'production' }}
228264 run : script/release --local "$TAG_NAME" --platform windows
229265 - name : Set up MSBuild
230266 id : setupmsbuild
@@ -265,6 +301,7 @@ jobs:
265301 AZURE_TENANT_ID : ${{ secrets.SPN_GITHUB_CLI_SIGNING_TENANT_ID }}
266302 DLIB_PATH : ${{ runner.temp }}\acs\bin\x64\Azure.CodeSigning.Dlib.dll
267303 METADATA_PATH : ${{ runner.temp }}\acs\metadata.json
304+ DO_SIGN_ARTIFACTS : ${{ inputs.environment == 'production' }} # Technically this could just be true since we don't run this step if the environment is not production, but we keep it the same as the build step for consistency.
268305 run : |
269306 Get-ChildItem -Path .\dist -Filter *.msi | ForEach-Object {
270307 .\script\sign.ps1 $_.FullName
@@ -288,14 +325,25 @@ jobs:
288325 uses : actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
289326 - name : Merge built artifacts
290327 uses : actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
328+ - name : Generate site deploy token
329+ id : site-deploy-token
330+ if : inputs.environment == 'production'
331+ uses : actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
332+ with :
333+ client-id : ${{ secrets.SITE_DEPLOY_APP_CLIENT_ID }}
334+ private-key : ${{ secrets.SITE_DEPLOY_APP_PRIVATE_KEY }}
335+ owner : github
336+ repositories : cli.github.com
291337 - name : Checkout documentation site
338+ if : ${{ inputs.environment == 'production' }}
292339 uses : actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
293340 with :
294341 repository : github/cli.github.com
295342 path : site
296343 fetch-depth : 0
297- token : ${{ secrets.SITE_DEPLOY_PAT }}
344+ token : ${{ steps.site-deploy-token.outputs.token }}
298345 - name : Update site man pages
346+ if : ${{ inputs.environment == 'production' }}
299347 env :
300348 GIT_COMMITTER_NAME : cli automation
301349 GIT_AUTHOR_NAME : cli automation
@@ -339,32 +387,30 @@ jobs:
339387 cp script/rpmmacros ~/.rpmmacros
340388 rpmsign --addsign dist/*.rpm
341389 - name : Attest release artifacts
342- if : inputs.environment == 'production'
390+ if : inputs.environment == 'production' && !inputs.dry_run
343391 uses : actions/attest@a1948c3f048ba23858d222213b7c278aabede763 # v4.1.1
344392 with :
345393 subject-path : " dist/gh_*"
346394 create-storage-record : false # (default: true)
347395 - name : Run createrepo
348- env :
349- GPG_SIGN : ${{ inputs.environment == 'production' }}
396+ if : ${{ inputs.environment == 'production' }}
350397 run : |
351398 mkdir -p site/packages/rpm
352399 cp dist/*.rpm site/packages/rpm/
353400 ./script/createrepo.sh
354401 cp -r dist/repodata site/packages/rpm/
355402 pushd site/packages/rpm
356- [ "$GPG_SIGN" = "false" ] || gpg --yes --detach-sign --armor repodata/repomd.xml
403+ gpg --yes --detach-sign --armor repodata/repomd.xml
357404 popd
358405 - name : Run reprepro
406+ if : ${{ inputs.environment == 'production' }}
359407 env :
360- GPG_SIGN : ${{ inputs.environment == 'production' }}
361408 # We are no longer adding to the distribution list.
362409 # All apt distributions should use "stable" according to our install documentation.
363410 # In the future we will remove legacy distributions listed here.
364411 RELEASES : " cosmic eoan disco groovy focal stable oldstable testing sid unstable buster bullseye stretch jessie bionic trusty precise xenial hirsute impish kali-rolling"
365412 run : |
366413 mkdir -p upload
367- [ "$GPG_SIGN" = "true" ] || sed -i.bak '/^SignWith:/d' script/distributions
368414 for release in $RELEASES; do
369415 for file in dist/*.deb; do
370416 reprepro --confdir="+b/script" includedeb "$release" "$file"
@@ -376,7 +422,7 @@ jobs:
376422 - name : Create the release
377423 env :
378424 # In non-production environments, the assets will not have been signed
379- DO_PUBLISH : ${{ inputs.environment == 'production' }}
425+ DO_PUBLISH : ${{ inputs.environment == 'production' && !inputs.dry_run }}
380426 TAG_NAME : ${{ inputs.tag_name }}
381427 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
382428 run : |
@@ -398,8 +444,9 @@ jobs:
398444 [ "$DO_PUBLISH" = "false" ] || guard=""
399445 script/label-assets dist/gh_* | xargs $guard gh release create "${release_args[@]}" --
400446 - name : Publish site
447+ if : ${{ inputs.environment == 'production' }}
401448 env :
402- DO_PUBLISH : ${{ inputs.environment == 'production' && !contains(inputs.tag_name, '-') }}
449+ DO_PUBLISH : ${{ inputs.environment == 'production' && !contains(inputs.tag_name, '-') && !inputs.dry_run }}
403450 TAG_NAME : ${{ inputs.tag_name }}
404451 GIT_COMMITTER_NAME : cli automation
405452 GIT_AUTHOR_NAME : cli automation
@@ -414,4 +461,4 @@ jobs:
414461 else
415462 git log --oneline @{upstream}..
416463 git diff --name-status @{upstream}..
417- fi
464+ fi
0 commit comments