mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-07-20 10:12:45 -04:00
ci(desktop): sign Windows installers and publish production releases to winget (#6217)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
12
.github/workflows/create-or-promote-release.yml
vendored
12
.github/workflows/create-or-promote-release.yml
vendored
@@ -132,6 +132,18 @@ jobs:
|
||||
call-promote-workflow:
|
||||
if: ${{ !inputs.dry_run && inputs.channel != 'internal' }}
|
||||
needs: determine-tags
|
||||
# promote.yml's token is capped by this job's grants, and a called
|
||||
# workflow requesting more than its caller grants fails at startup —
|
||||
# so this must cover promote.yml's declared workflow-level set, plus
|
||||
# actions: write for its publish-workflow dispatch. Scoped to this job
|
||||
# so call-release-workflow doesn't carry it.
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
statuses: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
actions: write
|
||||
uses: ./.github/workflows/promote.yml
|
||||
with:
|
||||
tag_name: ${{ needs.determine-tags.outputs.tag_to_process }}
|
||||
|
||||
22
.github/workflows/promote.yml
vendored
22
.github/workflows/promote.yml
vendored
@@ -143,6 +143,15 @@ jobs:
|
||||
update-github-release:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
needs: [ prepare-build-info, promote-release ]
|
||||
# actions: write is scoped here — only this job's publish-workflow
|
||||
# dispatch needs it, and the other jobs must not get it. Job-level
|
||||
# permissions replace the workflow-level block, so the full set this
|
||||
# job uses is listed.
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
statuses: write
|
||||
actions: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7.0.0
|
||||
@@ -167,6 +176,19 @@ jobs:
|
||||
--draft=false \
|
||||
--prerelease=${{ inputs.channel != 'production' }}
|
||||
|
||||
# The edit above is made with GITHUB_TOKEN, and GITHUB_TOKEN-caused
|
||||
# events never start workflow runs — winget-publish.yml's `released`
|
||||
# trigger will not fire from it. workflow_dispatch is exempt from that
|
||||
# suppression, so dispatch it explicitly. Soft-fail: a winget hiccup
|
||||
# must not block the changelog stamp or Discord notification.
|
||||
- name: Dispatch winget publish
|
||||
if: ${{ inputs.channel == 'production' }}
|
||||
continue-on-error: true
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ inputs.final_tag }}
|
||||
run: gh workflow run winget-publish.yml --ref main -f "tag=$TAG"
|
||||
|
||||
- name: Stamp CHANGELOG.md for release
|
||||
if: ${{ inputs.channel == 'production' }}
|
||||
env:
|
||||
|
||||
49
.github/workflows/release.yml
vendored
49
.github/workflows/release.yml
vendored
@@ -62,6 +62,20 @@ on:
|
||||
required: false
|
||||
APPLE_TEAM_ID:
|
||||
required: false
|
||||
# Windows Authenticode signing via Azure Trusted Signing (now "Artifact Signing").
|
||||
# All optional: the signing step skips cleanly when absent, like macOS above.
|
||||
AZURE_TENANT_ID:
|
||||
required: false
|
||||
AZURE_CLIENT_ID:
|
||||
required: false
|
||||
AZURE_CLIENT_SECRET:
|
||||
required: false
|
||||
AZURE_TRUSTED_SIGNING_ENDPOINT:
|
||||
required: false
|
||||
AZURE_TRUSTED_SIGNING_ACCOUNT:
|
||||
required: false
|
||||
AZURE_TRUSTED_SIGNING_CERT_PROFILE:
|
||||
required: false
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ inputs.tag_name }}
|
||||
@@ -269,6 +283,8 @@ jobs:
|
||||
GRADLE_CACHE_URL: ${{ secrets.GRADLE_CACHE_URL }}
|
||||
GRADLE_CACHE_USERNAME: ${{ secrets.GRADLE_CACHE_USERNAME }}
|
||||
GRADLE_CACHE_PASSWORD: ${{ secrets.GRADLE_CACHE_PASSWORD }}
|
||||
# Secrets aren't readable in step `if:` expressions, so surface presence here.
|
||||
SIGN_WINDOWS: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT != '' && 'true' || 'false' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7.0.0
|
||||
@@ -308,6 +324,39 @@ jobs:
|
||||
${{ contains(runner.os, 'macOS') && ':desktopApp:packageReleaseUberJarForCurrentOS' || '' }}
|
||||
'-PaboutLibraries.release=true' '-Pdesktop.release=true' --no-daemon
|
||||
|
||||
# Authenticode-signs the MSI/EXE installers via Azure Trusted Signing
|
||||
# (rebranded "Artifact Signing"). Skips cleanly when the secrets are
|
||||
# absent, mirroring SIGN_MACOS. Required repository secrets:
|
||||
# AZURE_TENANT_ID – Entra tenant ID
|
||||
# AZURE_CLIENT_ID – app registration (service principal) client ID
|
||||
# AZURE_CLIENT_SECRET – client secret for that app registration
|
||||
# AZURE_TRUSTED_SIGNING_ENDPOINT – e.g. https://eus.codesigning.azure.net
|
||||
# AZURE_TRUSTED_SIGNING_ACCOUNT – Trusted Signing account name
|
||||
# AZURE_TRUSTED_SIGNING_CERT_PROFILE – public-trust certificate profile name
|
||||
# The service principal needs the "Trusted Signing Certificate Profile
|
||||
# Signer" role on the account. Presence is gated on the ENDPOINT secret;
|
||||
# a half-configured set fails here loudly rather than shipping unsigned.
|
||||
# Note: only the installers are signed, not the launcher .exe packaged
|
||||
# inside them — SmartScreen reputation attaches to the downloaded file.
|
||||
# Sign the inner .exe between createReleaseDistributable and packaging
|
||||
# if that ever becomes necessary.
|
||||
- name: Sign Windows installers (Azure Trusted Signing)
|
||||
if: runner.os == 'Windows' && env.SIGN_WINDOWS == 'true'
|
||||
uses: azure/artifact-signing-action@v2.0.0
|
||||
with:
|
||||
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||
endpoint: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }}
|
||||
signing-account-name: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT }}
|
||||
certificate-profile-name: ${{ secrets.AZURE_TRUSTED_SIGNING_CERT_PROFILE }}
|
||||
files-folder: desktopApp/build/compose/binaries/main-release
|
||||
files-folder-filter: msi,exe
|
||||
files-folder-recurse: true
|
||||
file-digest: SHA256
|
||||
timestamp-rfc3161: http://timestamp.acs.microsoft.com
|
||||
timestamp-digest: SHA256
|
||||
|
||||
# jpackage computes the .deb Depends: line from the build host's package names.
|
||||
# Ubuntu 24.04 records time_t64 transition names (libasound2t64, libpng16-16t64)
|
||||
# that don't exist on Debian 12 Bookworm / Raspberry Pi OS, making the .deb
|
||||
|
||||
56
.github/workflows/winget-publish.yml
vendored
Normal file
56
.github/workflows/winget-publish.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
name: Publish to winget
|
||||
|
||||
# The `released` type fires when a release is published as a full release OR
|
||||
# when an existing pre-release is flipped to a full release — the latter is
|
||||
# what promote.yml's production promotion does (`gh release edit
|
||||
# --prerelease=false` on the already-published release object). It never fires
|
||||
# for drafts or pre-releases, so internal/closed/open promotions are ignored.
|
||||
#
|
||||
# CAVEAT: promote.yml performs that edit with the workflow's own GITHUB_TOKEN,
|
||||
# and events caused by GITHUB_TOKEN never start workflow runs — so promote.yml
|
||||
# also dispatches this workflow explicitly (workflow_dispatch is exempt from
|
||||
# that suppression). The release trigger stays for manually-published releases;
|
||||
# workflow_dispatch doubles as the manual retry/seed path.
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Production release tag to publish (e.g., v2.8.0)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
# The WINGET_TOKEN PAT does all the work against microsoft/winget-pkgs;
|
||||
# nothing in this repo is written.
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
winget:
|
||||
# Belt and braces for release events — `released` should already exclude
|
||||
# these. workflow_dispatch has no release payload and passes through.
|
||||
if: ${{ !github.event.release.prerelease && !github.event.release.draft }}
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# Secrets aren't readable in step `if:` expressions; skip cleanly until
|
||||
# the token is configured.
|
||||
HAS_WINGET_TOKEN: ${{ secrets.WINGET_TOKEN != '' && 'true' || 'false' }}
|
||||
steps:
|
||||
# Prerequisites (both manual, one-time):
|
||||
# 1. Meshtastic.MeshtasticDesktop must already exist in
|
||||
# microsoft/winget-pkgs — winget-releaser only updates existing
|
||||
# packages. Submit the first version by hand with `wingetcreate new
|
||||
# <MSI release asset URL>`.
|
||||
# 2. WINGET_TOKEN secret: a *classic* PAT with the public_repo scope
|
||||
# (fine-grained PATs are not supported), owned by an account that
|
||||
# has (or can create) a fork of microsoft/winget-pkgs.
|
||||
# The version is derived from the release tag with the leading `v`
|
||||
# stripped (v2.8.0 -> 2.8.0), matching the MSI's ProductVersion.
|
||||
- name: Submit MSI manifest to microsoft/winget-pkgs
|
||||
if: env.HAS_WINGET_TOKEN == 'true'
|
||||
uses: vedantmgoyal9/winget-releaser@v2
|
||||
with:
|
||||
identifier: Meshtastic.MeshtasticDesktop
|
||||
installers-regex: '\.msi$'
|
||||
release-tag: ${{ inputs.tag || github.event.release.tag_name }}
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
||||
Reference in New Issue
Block a user