Build one canary lane per repository in CI

This commit is contained in:
John Preston
2026-08-20 11:11:51 +04:00
parent 77b0dab198
commit 536a05295d

View File

@@ -1,31 +1,29 @@
# Canary channels: single Release+LTO build per platform (Windows x64, # Canary channel: single Release+LTO build per platform (Windows x64,
# universal macOS, Linux x64 through docker), for both the public and the # universal macOS, Linux x64 through docker), signed, packed as v2
# private canary channel, signed, packed as v2 updates and published via # updates and published via a local Bot API server.
# a local Bot API server. #
# Each repository builds exactly ONE lane. The lane follows repository
# visibility (public repo -> canary-public, private repo ->
# canary-private) and can be overridden with the repository variable
# CANARY_CHANNEL ("public" or "private"); the private lane refuses to
# run from a public repository no matter what. A repository holds only
# its own lane's secrets.
# #
# Infrastructure this workflow needs before its first real run, all under # Infrastructure this workflow needs before its first real run, all under
# the 'canary' environment unless noted. Placeholders, do not invent values: # the 'canary' environment unless noted. Placeholders, do not invent values:
# #
# Both repositories:
# vars.CANARY_CHANNEL
# Optional lane override, see above. Normally left unset.
# secrets.CANARY_API_ID / secrets.CANARY_API_HASH # secrets.CANARY_API_ID / secrets.CANARY_API_HASH
# Production Telegram API credentials for the canary app builds. # Production Telegram API credentials for the canary app builds.
# secrets.CANARY_BOT_TOKEN # secrets.CANARY_BOT_TOKEN
# Bot admin in both canary channels (posts files, edits metadata). # Bot admin in this lane's channel (posts files, edits metadata).
# secrets.CANARY_PUBLIC_CHANNEL_ID
# Numeric -100... public channel id for the Bot API calls.
# secrets.CANARY_PRIVATE_CHANNEL_ID
# Bare numeric id of the private channel (no -100 prefix), also
# compiled into canary-private builds for discovery.
# vars.CANARY_PUBLIC_CHANNEL_USERNAME
# Public channel username compiled into canary-public builds.
# vars.CANARY_METADATA_MSG_ID / vars.CANARY_PRIVATE_METADATA_MSG_ID
# Fixed ids of the pinned metadata messages in the two channels.
# secrets.AZURE_CLIENT_ID / secrets.AZURE_TENANT_ID # secrets.AZURE_CLIENT_ID / secrets.AZURE_TENANT_ID
# Entra federated credential for OIDC az login (id-token: write). # This lane's Entra federated credential for OIDC az login
# (id-token: write).
# secrets.AZURE_KEYVAULT_NAME # secrets.AZURE_KEYVAULT_NAME
# Key Vault holding the canary ES256 keys. # Key Vault holding this lane's canary ES256 key.
# vars.CANARY_SIGNING_KEY_ID / vars.CANARY_PRIVATE_SIGNING_KEY_ID
# Key Vault key names, must match the manifest ids
# ("cp-2026a" / "cx-2026a").
# secrets.SM_API_KEY / secrets.SM_CLIENT_CERT_FILE_B64 / # secrets.SM_API_KEY / secrets.SM_CLIENT_CERT_FILE_B64 /
# secrets.SM_CLIENT_CERT_PASSWORD / secrets.SM_HOST / # secrets.SM_CLIENT_CERT_PASSWORD / secrets.SM_HOST /
# secrets.SM_KEYPAIR_ALIAS # secrets.SM_KEYPAIR_ALIAS
@@ -39,6 +37,25 @@
# secrets.R2_SECRET_ACCESS_KEY / vars.R2_SYMBOLS_BUCKET # secrets.R2_SECRET_ACCESS_KEY / vars.R2_SYMBOLS_BUCKET
# Cloudflare R2 bucket for breakpad symbols (upload placeholder). # Cloudflare R2 bucket for breakpad symbols (upload placeholder).
# #
# Public repository only:
# secrets.CANARY_PUBLIC_CHANNEL_ID
# Numeric -100... public channel id for the Bot API calls.
# vars.CANARY_PUBLIC_CHANNEL_USERNAME
# Public channel username compiled into canary-public builds.
# vars.CANARY_METADATA_MSG_ID
# Fixed id of the pinned metadata message in the public channel.
# vars.CANARY_SIGNING_KEY_ID
# Key Vault key name, must match the manifest id ("cp-2026a").
#
# Private repository only:
# secrets.CANARY_PRIVATE_CHANNEL_ID
# Bare numeric id of the private channel (no -100 prefix), also
# compiled into canary-private builds for discovery.
# vars.CANARY_PRIVATE_METADATA_MSG_ID
# Fixed id of the pinned metadata message in the private channel.
# vars.CANARY_PRIVATE_SIGNING_KEY_ID
# Key Vault key name, must match the manifest id ("cx-2026a").
#
# Publishing no-ops cleanly while CANARY_BOT_TOKEN is absent, so the # Publishing no-ops cleanly while CANARY_BOT_TOKEN is absent, so the
# workflow can run before the bots/channels/KeyLocker exist. # workflow can run before the bots/channels/KeyLocker exist.
@@ -65,12 +82,11 @@ jobs:
environment: canary environment: canary
outputs: outputs:
channel: ${{ steps.compute.outputs.channel }}
base: ${{ steps.compute.outputs.base }} base: ${{ steps.compute.outputs.base }}
commit: ${{ steps.compute.outputs.commit }} commit: ${{ steps.compute.outputs.commit }}
counter_public: ${{ steps.compute.outputs.counter_public }} counter: ${{ steps.compute.outputs.counter }}
counter_private: ${{ steps.compute.outputs.counter_private }} previous: ${{ steps.compute.outputs.previous }}
previous_public: ${{ steps.compute.outputs.previous_public }}
previous_private: ${{ steps.compute.outputs.previous_private }}
publish: ${{ steps.compute.outputs.publish }} publish: ${{ steps.compute.outputs.publish }}
steps: steps:
@@ -81,13 +97,41 @@ jobs:
# must survive force-pushes and rebases of the canary branch. # must survive force-pushes and rebases of the canary branch.
fetch-depth: 0 fetch-depth: 0
- name: Compute canary versions. - name: Compute canary version.
id: compute id: compute
env: env:
BOT_TOKEN: ${{ secrets.CANARY_BOT_TOKEN }} BOT_TOKEN: ${{ secrets.CANARY_BOT_TOKEN }}
PUBLIC_CHANNEL: ${{ secrets.CANARY_PUBLIC_CHANNEL_ID }} PUBLIC_CHANNEL: ${{ secrets.CANARY_PUBLIC_CHANNEL_ID }}
PRIVATE_CHANNEL: ${{ secrets.CANARY_PRIVATE_CHANNEL_ID }} PRIVATE_CHANNEL: ${{ secrets.CANARY_PRIVATE_CHANNEL_ID }}
CHANNEL_OVERRIDE: ${{ vars.CANARY_CHANNEL }}
REPO_IS_PRIVATE: ${{ github.event.repository.private }}
run: | run: |
CHANNEL="$CHANNEL_OVERRIDE"
if [ -z "$CHANNEL" ]; then
if [ "$REPO_IS_PRIVATE" = "true" ]; then
CHANNEL=private
else
CHANNEL=public
fi
fi
case "$CHANNEL" in
public|private) ;;
*)
echo "::error::Bad CANARY_CHANNEL value '$CHANNEL'."
exit 1
;;
esac
if [ "$CHANNEL" = "private" ] && [ "$REPO_IS_PRIVATE" != "true" ]; then
echo "::error::The private canary lane only builds from the private repository."
exit 1
fi
echo "channel=$CHANNEL" >> $GITHUB_OUTPUT
if [ "$CHANNEL" = "public" ]; then
CHAT_ID="$PUBLIC_CHANNEL"
else
CHAT_ID="-100$PRIVATE_CHANNEL"
fi
while IFS=' ' read -r name value; do while IFS=' ' read -r name value; do
[ "$name" = "AppVersion" ] && BASE="$value" [ "$name" = "AppVersion" ] && BASE="$value"
done < Telegram/build/version done < Telegram/build/version
@@ -100,40 +144,33 @@ jobs:
fi fi
echo "publish=$PUBLISH" >> $GITHUB_OUTPUT echo "publish=$PUBLISH" >> $GITHUB_OUTPUT
compute_counter() { # $1 = chat id, $2 = channel name, $3 = suffix COUNTER=1
local COUNTER=1 PREVIOUS=""
local PREVIOUS="" if [ "$PUBLISH" = "true" ] && [ -n "$CHAT_ID" ] && [ "$CHAT_ID" != "-100" ]; then
if [ "$PUBLISH" = "true" ] && [ -n "$1" ]; then PINNED=$(curl -sf "https://api.telegram.org/bot$BOT_TOKEN/getChat?chat_id=$CHAT_ID" \
local PINNED=$(curl -sf "https://api.telegram.org/bot$BOT_TOKEN/getChat?chat_id=$1" \ | jq -r '.result.pinned_message.text // empty')
| jq -r '.result.pinned_message.text // empty') if [ -n "$PINNED" ]; then
if [ -n "$PINNED" ]; then OLD_BASE=$(echo "$PINNED" | jq -r ".channels.\"canary-$CHANNEL\".base // 0")
local OLD_BASE=$(echo "$PINNED" | jq -r ".channels.\"$2\".base // 0") OLD_COUNTER=$(echo "$PINNED" | jq -r ".channels.\"canary-$CHANNEL\".counter // 0")
local OLD_COUNTER=$(echo "$PINNED" | jq -r ".channels.\"$2\".counter // 0") PREVIOUS=$(echo "$PINNED" | jq -r ".channels.\"canary-$CHANNEL\".commit // empty")
PREVIOUS=$(echo "$PINNED" | jq -r ".channels.\"$2\".commit // empty") if [ "$OLD_BASE" = "$BASE" ]; then
if [ "$OLD_BASE" = "$BASE" ]; then COUNTER=$((OLD_COUNTER + 1))
COUNTER=$((OLD_COUNTER + 1))
fi
fi fi
fi fi
echo "counter_$3=$COUNTER" >> $GITHUB_OUTPUT fi
echo "previous_$3=$PREVIOUS" >> $GITHUB_OUTPUT echo "counter=$COUNTER" >> $GITHUB_OUTPUT
echo "canary-$3: $BASE #$COUNTER" echo "previous=$PREVIOUS" >> $GITHUB_OUTPUT
} echo "Canary lane: canary-$CHANNEL, version $BASE #$COUNTER (publish: $PUBLISH)"
compute_counter "$PUBLIC_CHANNEL" "canary-public" public
compute_counter "-100$PRIVATE_CHANNEL" "canary-private" private
windows: windows:
name: Windows x64 (${{ matrix.channel }}) name: Windows x64 (${{ needs.version.outputs.channel }})
runs-on: depot-windows-latest-16 runs-on: depot-windows-latest-16
needs: version needs: version
environment: canary environment: canary
strategy: # win-arm64 is phase 2: add an arch matrix here together with the
matrix: # windows-11-arm runner and the VS ARM64 build tools steps from
channel: [public, private] # win.yml when the canary channels grow an arm feed.
# win-arm64 is phase 2: add an arch dimension here together with
# the windows-11-arm runner and the VS ARM64 build tools steps
# from win.yml when the canary channels grow an arm feed.
env: env:
PREPARE_PATH: "Telegram/build/prepare/prepare.py" PREPARE_PATH: "Telegram/build/prepare/prepare.py"
@@ -163,14 +200,13 @@ jobs:
- name: Read canary configuration. - name: Read canary configuration.
shell: bash shell: bash
run: | run: |
if [ "${{ matrix.channel }}" = "public" ]; then echo "CANARY_COUNTER=${{ needs.version.outputs.counter }}" >> $GITHUB_ENV
if [ "${{ needs.version.outputs.channel }}" = "public" ]; then
echo "CANARY_TAG=canarypub" >> $GITHUB_ENV echo "CANARY_TAG=canarypub" >> $GITHUB_ENV
echo "CANARY_COUNTER=${{ needs.version.outputs.counter_public }}" >> $GITHUB_ENV
echo "CANARY_KEY_ID=${{ vars.CANARY_SIGNING_KEY_ID }}" >> $GITHUB_ENV echo "CANARY_KEY_ID=${{ vars.CANARY_SIGNING_KEY_ID }}" >> $GITHUB_ENV
echo "CANARY_DEFINES=-D TDESKTOP_UPDATE_CHANNEL=canary-public -D TDESKTOP_CANARY_PUBLIC_CHANNEL=${{ vars.CANARY_PUBLIC_CHANNEL_USERNAME }} -D TDESKTOP_CANARY_METADATA_MSG_ID=${{ vars.CANARY_METADATA_MSG_ID }}" >> $GITHUB_ENV echo "CANARY_DEFINES=-D TDESKTOP_UPDATE_CHANNEL=canary-public -D TDESKTOP_CANARY_PUBLIC_CHANNEL=${{ vars.CANARY_PUBLIC_CHANNEL_USERNAME }} -D TDESKTOP_CANARY_METADATA_MSG_ID=${{ vars.CANARY_METADATA_MSG_ID }}" >> $GITHUB_ENV
else else
echo "CANARY_TAG=canarypriv" >> $GITHUB_ENV echo "CANARY_TAG=canarypriv" >> $GITHUB_ENV
echo "CANARY_COUNTER=${{ needs.version.outputs.counter_private }}" >> $GITHUB_ENV
echo "CANARY_KEY_ID=${{ vars.CANARY_PRIVATE_SIGNING_KEY_ID }}" >> $GITHUB_ENV echo "CANARY_KEY_ID=${{ vars.CANARY_PRIVATE_SIGNING_KEY_ID }}" >> $GITHUB_ENV
echo "CANARY_DEFINES=-D TDESKTOP_UPDATE_CHANNEL=canary-private -D TDESKTOP_CANARY_PRIVATE_CHANNEL_ID=${{ secrets.CANARY_PRIVATE_CHANNEL_ID }} -D TDESKTOP_CANARY_METADATA_MSG_ID=${{ vars.CANARY_PRIVATE_METADATA_MSG_ID }}" >> $GITHUB_ENV echo "CANARY_DEFINES=-D TDESKTOP_UPDATE_CHANNEL=canary-private -D TDESKTOP_CANARY_PRIVATE_CHANNEL_ID=${{ secrets.CANARY_PRIVATE_CHANNEL_ID }} -D TDESKTOP_CANARY_METADATA_MSG_ID=${{ vars.CANARY_PRIVATE_METADATA_MSG_ID }}" >> $GITHUB_ENV
fi fi
@@ -330,7 +366,7 @@ jobs:
./Packer.exe -path Telegram.exe -path Updater.exe \ ./Packer.exe -path Telegram.exe -path Updater.exe \
-target win64 \ -target win64 \
-version ${{ needs.version.outputs.base }} \ -version ${{ needs.version.outputs.base }} \
-channel canary-${{ matrix.channel }} \ -channel canary-${{ needs.version.outputs.channel }} \
-counter $CANARY_COUNTER \ -counter $CANARY_COUNTER \
-keys-loc ../../Telegram/Resources/update \ -keys-loc ../../Telegram/Resources/update \
-emit-signing-input signing-input.bin -emit-signing-input signing-input.bin
@@ -340,7 +376,7 @@ jobs:
--output canary.sig \ --output canary.sig \
--az-vault "${{ secrets.AZURE_KEYVAULT_NAME }}" \ --az-vault "${{ secrets.AZURE_KEYVAULT_NAME }}" \
--az-key "$CANARY_KEY_ID" --az-key "$CANARY_KEY_ID"
./Packer.exe -channel canary-${{ matrix.channel }} \ ./Packer.exe -channel canary-${{ needs.version.outputs.channel }} \
-keys-loc ../../Telegram/Resources/update \ -keys-loc ../../Telegram/Resources/update \
-unsigned tv2-win64-$CANARY_TAG-${{ needs.version.outputs.base }}-$CANARY_COUNTER.unsigned \ -unsigned tv2-win64-$CANARY_TAG-${{ needs.version.outputs.base }}-$CANARY_COUNTER.unsigned \
-embed-signatures $CANARY_KEY_ID:canary.sig -embed-signatures $CANARY_KEY_ID:canary.sig
@@ -354,19 +390,15 @@ jobs:
- uses: actions/upload-artifact@v7 - uses: actions/upload-artifact@v7
name: Upload artifact. name: Upload artifact.
with: with:
name: canary-${{ matrix.channel }}-win64 name: canary-win64
path: ${{ env.TBUILD }}\${{ env.REPO_NAME }}\out\Release\artifact\ path: ${{ env.TBUILD }}\${{ env.REPO_NAME }}\out\Release\artifact\
macos: macos:
name: macOS universal (${{ matrix.channel }}) name: macOS universal (${{ needs.version.outputs.channel }})
runs-on: depot-macos-latest runs-on: depot-macos-latest
needs: version needs: version
environment: canary environment: canary
strategy:
matrix:
channel: [public, private]
env: env:
PREPARE_PATH: "Telegram/build/prepare/prepare.py" PREPARE_PATH: "Telegram/build/prepare/prepare.py"
@@ -382,14 +414,13 @@ jobs:
- name: Read canary configuration. - name: Read canary configuration.
run: | run: |
if [ "${{ matrix.channel }}" = "public" ]; then echo "CANARY_COUNTER=${{ needs.version.outputs.counter }}" >> $GITHUB_ENV
if [ "${{ needs.version.outputs.channel }}" = "public" ]; then
echo "CANARY_TAG=canarypub" >> $GITHUB_ENV echo "CANARY_TAG=canarypub" >> $GITHUB_ENV
echo "CANARY_COUNTER=${{ needs.version.outputs.counter_public }}" >> $GITHUB_ENV
echo "CANARY_KEY_ID=${{ vars.CANARY_SIGNING_KEY_ID }}" >> $GITHUB_ENV echo "CANARY_KEY_ID=${{ vars.CANARY_SIGNING_KEY_ID }}" >> $GITHUB_ENV
echo "CANARY_DEFINES=-D TDESKTOP_UPDATE_CHANNEL=canary-public -D TDESKTOP_CANARY_PUBLIC_CHANNEL=${{ vars.CANARY_PUBLIC_CHANNEL_USERNAME }} -D TDESKTOP_CANARY_METADATA_MSG_ID=${{ vars.CANARY_METADATA_MSG_ID }}" >> $GITHUB_ENV echo "CANARY_DEFINES=-D TDESKTOP_UPDATE_CHANNEL=canary-public -D TDESKTOP_CANARY_PUBLIC_CHANNEL=${{ vars.CANARY_PUBLIC_CHANNEL_USERNAME }} -D TDESKTOP_CANARY_METADATA_MSG_ID=${{ vars.CANARY_METADATA_MSG_ID }}" >> $GITHUB_ENV
else else
echo "CANARY_TAG=canarypriv" >> $GITHUB_ENV echo "CANARY_TAG=canarypriv" >> $GITHUB_ENV
echo "CANARY_COUNTER=${{ needs.version.outputs.counter_private }}" >> $GITHUB_ENV
echo "CANARY_KEY_ID=${{ vars.CANARY_PRIVATE_SIGNING_KEY_ID }}" >> $GITHUB_ENV echo "CANARY_KEY_ID=${{ vars.CANARY_PRIVATE_SIGNING_KEY_ID }}" >> $GITHUB_ENV
echo "CANARY_DEFINES=-D TDESKTOP_UPDATE_CHANNEL=canary-private -D TDESKTOP_CANARY_PRIVATE_CHANNEL_ID=${{ secrets.CANARY_PRIVATE_CHANNEL_ID }} -D TDESKTOP_CANARY_METADATA_MSG_ID=${{ vars.CANARY_PRIVATE_METADATA_MSG_ID }}" >> $GITHUB_ENV echo "CANARY_DEFINES=-D TDESKTOP_UPDATE_CHANNEL=canary-private -D TDESKTOP_CANARY_PRIVATE_CHANNEL_ID=${{ secrets.CANARY_PRIVATE_CHANNEL_ID }} -D TDESKTOP_CANARY_METADATA_MSG_ID=${{ vars.CANARY_PRIVATE_METADATA_MSG_ID }}" >> $GITHUB_ENV
fi fi
@@ -523,7 +554,7 @@ jobs:
cd $REPO_NAME/out/Release cd $REPO_NAME/out/Release
./Packer -path Telegram.app \ ./Packer -path Telegram.app \
-version ${{ needs.version.outputs.base }} \ -version ${{ needs.version.outputs.base }} \
-channel canary-${{ matrix.channel }} \ -channel canary-${{ needs.version.outputs.channel }} \
-counter $CANARY_COUNTER \ -counter $CANARY_COUNTER \
-keys-loc ../../Telegram/Resources/update \ -keys-loc ../../Telegram/Resources/update \
-emit-signing-input signing-input.bin -emit-signing-input signing-input.bin
@@ -533,7 +564,7 @@ jobs:
--output canary.sig \ --output canary.sig \
--az-vault "${{ secrets.AZURE_KEYVAULT_NAME }}" \ --az-vault "${{ secrets.AZURE_KEYVAULT_NAME }}" \
--az-key "$CANARY_KEY_ID" --az-key "$CANARY_KEY_ID"
./Packer -channel canary-${{ matrix.channel }} \ ./Packer -channel canary-${{ needs.version.outputs.channel }} \
-keys-loc ../../Telegram/Resources/update \ -keys-loc ../../Telegram/Resources/update \
-unsigned tv2-mac-$CANARY_TAG-${{ needs.version.outputs.base }}-$CANARY_COUNTER.unsigned \ -unsigned tv2-mac-$CANARY_TAG-${{ needs.version.outputs.base }}-$CANARY_COUNTER.unsigned \
-embed-signatures $CANARY_KEY_ID:canary.sig -embed-signatures $CANARY_KEY_ID:canary.sig
@@ -547,19 +578,15 @@ jobs:
- uses: actions/upload-artifact@v7 - uses: actions/upload-artifact@v7
name: Upload artifact. name: Upload artifact.
with: with:
name: canary-${{ matrix.channel }}-mac name: canary-mac
path: ${{ env.REPO_NAME }}/out/Release/artifact/ path: ${{ env.REPO_NAME }}/out/Release/artifact/
linux: linux:
name: Linux x64 (${{ matrix.channel }}) name: Linux x64 (${{ needs.version.outputs.channel }})
runs-on: depot-ubuntu-latest-16 runs-on: depot-ubuntu-latest-16
needs: version needs: version
environment: canary environment: canary
strategy:
matrix:
channel: [public, private]
env: env:
IMAGE_TAG: tdesktop:centos_env IMAGE_TAG: tdesktop:centos_env
@@ -571,14 +598,13 @@ jobs:
- name: Read canary configuration. - name: Read canary configuration.
run: | run: |
if [ "${{ matrix.channel }}" = "public" ]; then echo "CANARY_COUNTER=${{ needs.version.outputs.counter }}" >> $GITHUB_ENV
if [ "${{ needs.version.outputs.channel }}" = "public" ]; then
echo "CANARY_TAG=canarypub" >> $GITHUB_ENV echo "CANARY_TAG=canarypub" >> $GITHUB_ENV
echo "CANARY_COUNTER=${{ needs.version.outputs.counter_public }}" >> $GITHUB_ENV
echo "CANARY_KEY_ID=${{ vars.CANARY_SIGNING_KEY_ID }}" >> $GITHUB_ENV echo "CANARY_KEY_ID=${{ vars.CANARY_SIGNING_KEY_ID }}" >> $GITHUB_ENV
echo "CANARY_DEFINES=-D TDESKTOP_UPDATE_CHANNEL=canary-public -D TDESKTOP_CANARY_PUBLIC_CHANNEL=${{ vars.CANARY_PUBLIC_CHANNEL_USERNAME }} -D TDESKTOP_CANARY_METADATA_MSG_ID=${{ vars.CANARY_METADATA_MSG_ID }}" >> $GITHUB_ENV echo "CANARY_DEFINES=-D TDESKTOP_UPDATE_CHANNEL=canary-public -D TDESKTOP_CANARY_PUBLIC_CHANNEL=${{ vars.CANARY_PUBLIC_CHANNEL_USERNAME }} -D TDESKTOP_CANARY_METADATA_MSG_ID=${{ vars.CANARY_METADATA_MSG_ID }}" >> $GITHUB_ENV
else else
echo "CANARY_TAG=canarypriv" >> $GITHUB_ENV echo "CANARY_TAG=canarypriv" >> $GITHUB_ENV
echo "CANARY_COUNTER=${{ needs.version.outputs.counter_private }}" >> $GITHUB_ENV
echo "CANARY_KEY_ID=${{ vars.CANARY_PRIVATE_SIGNING_KEY_ID }}" >> $GITHUB_ENV echo "CANARY_KEY_ID=${{ vars.CANARY_PRIVATE_SIGNING_KEY_ID }}" >> $GITHUB_ENV
echo "CANARY_DEFINES=-D TDESKTOP_UPDATE_CHANNEL=canary-private -D TDESKTOP_CANARY_PRIVATE_CHANNEL_ID=${{ secrets.CANARY_PRIVATE_CHANNEL_ID }} -D TDESKTOP_CANARY_METADATA_MSG_ID=${{ vars.CANARY_PRIVATE_METADATA_MSG_ID }}" >> $GITHUB_ENV echo "CANARY_DEFINES=-D TDESKTOP_UPDATE_CHANNEL=canary-private -D TDESKTOP_CANARY_PRIVATE_CHANNEL_ID=${{ secrets.CANARY_PRIVATE_CHANNEL_ID }} -D TDESKTOP_CANARY_METADATA_MSG_ID=${{ vars.CANARY_PRIVATE_METADATA_MSG_ID }}" >> $GITHUB_ENV
fi fi
@@ -692,7 +718,7 @@ jobs:
cd out/Release cd out/Release
./Packer -path Telegram -path Updater \ ./Packer -path Telegram -path Updater \
-version ${{ needs.version.outputs.base }} \ -version ${{ needs.version.outputs.base }} \
-channel canary-${{ matrix.channel }} \ -channel canary-${{ needs.version.outputs.channel }} \
-counter $CANARY_COUNTER \ -counter $CANARY_COUNTER \
-keys-loc ../../Telegram/Resources/update \ -keys-loc ../../Telegram/Resources/update \
-emit-signing-input signing-input.bin -emit-signing-input signing-input.bin
@@ -702,7 +728,7 @@ jobs:
--output canary.sig \ --output canary.sig \
--az-vault "${{ secrets.AZURE_KEYVAULT_NAME }}" \ --az-vault "${{ secrets.AZURE_KEYVAULT_NAME }}" \
--az-key "$CANARY_KEY_ID" --az-key "$CANARY_KEY_ID"
./Packer -channel canary-${{ matrix.channel }} \ ./Packer -channel canary-${{ needs.version.outputs.channel }} \
-keys-loc ../../Telegram/Resources/update \ -keys-loc ../../Telegram/Resources/update \
-unsigned tv2-linux-$CANARY_TAG-${{ needs.version.outputs.base }}-$CANARY_COUNTER.unsigned \ -unsigned tv2-linux-$CANARY_TAG-${{ needs.version.outputs.base }}-$CANARY_COUNTER.unsigned \
-embed-signatures $CANARY_KEY_ID:canary.sig -embed-signatures $CANARY_KEY_ID:canary.sig
@@ -716,11 +742,11 @@ jobs:
- uses: actions/upload-artifact@v7 - uses: actions/upload-artifact@v7
name: Upload artifact. name: Upload artifact.
with: with:
name: canary-${{ matrix.channel }}-linux name: canary-linux
path: out/Release/artifact/ path: out/Release/artifact/
publish: publish:
name: Publish name: Publish (${{ needs.version.outputs.channel }})
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [version, windows, macos, linux] needs: [version, windows, macos, linux]
if: needs.version.outputs.publish == 'true' if: needs.version.outputs.publish == 'true'
@@ -758,105 +784,102 @@ jobs:
sudo apt-get update && sudo apt-get install -y osslsigncode sudo apt-get update && sudo apt-get install -y osslsigncode
FAILED=0 FAILED=0
for CHANNEL in public private; do for EXE in Telegram.exe Updater.exe; do
for EXE in Telegram.exe Updater.exe; do if ! osslsigncode verify "artifacts/canary-win64/$EXE"; then
if ! osslsigncode verify "artifacts/canary-$CHANNEL-win64/$EXE"; then echo "::error::$EXE is not Authenticode-signed."
echo "::error::canary-$CHANNEL $EXE is not Authenticode-signed."
FAILED=1
fi
done
# TODO(canary-infra): pin an apple-codesign (rcodesign) release
# for full macOS signature+staple verification on Linux:
# rcodesign verify artifacts/canary-$CHANNEL-mac/Telegram.app
if [ ! -d "artifacts/canary-$CHANNEL-mac/Telegram.app/Contents/_CodeSignature" ]; then
echo "::error::canary-$CHANNEL Telegram.app has no code signature."
FAILED=1 FAILED=1
fi fi
done done
# TODO(canary-infra): pin an apple-codesign (rcodesign) release
# for full macOS signature+staple verification on Linux:
# rcodesign verify artifacts/canary-mac/Telegram.app
if [ ! -d "artifacts/canary-mac/Telegram.app/Contents/_CodeSignature" ]; then
echo "::error::Telegram.app has no code signature."
FAILED=1
fi
exit $FAILED exit $FAILED
- name: Publish both channels. - name: Publish the channel.
env: env:
CHANNEL: ${{ needs.version.outputs.channel }}
PUBLIC_CHANNEL: ${{ secrets.CANARY_PUBLIC_CHANNEL_ID }} PUBLIC_CHANNEL: ${{ secrets.CANARY_PUBLIC_CHANNEL_ID }}
PRIVATE_CHANNEL: ${{ secrets.CANARY_PRIVATE_CHANNEL_ID }} PRIVATE_CHANNEL: ${{ secrets.CANARY_PRIVATE_CHANNEL_ID }}
PUBLIC_MSG_ID: ${{ vars.CANARY_METADATA_MSG_ID }} PUBLIC_MSG_ID: ${{ vars.CANARY_METADATA_MSG_ID }}
PRIVATE_MSG_ID: ${{ vars.CANARY_PRIVATE_METADATA_MSG_ID }} PRIVATE_MSG_ID: ${{ vars.CANARY_PRIVATE_METADATA_MSG_ID }}
run: | run: |
if [ "$CHANNEL" = "public" ]; then
CHAT_ID="$PUBLIC_CHANNEL"
MSG_ID="$PUBLIC_MSG_ID"
else
CHAT_ID="-100$PRIVATE_CHANNEL"
MSG_ID="$PRIVATE_MSG_ID"
fi
COUNTER="${{ needs.version.outputs.counter }}"
PREVIOUS="${{ needs.version.outputs.previous }}"
CAPTION=$({
echo "Canary #$COUNTER · ${{ needs.version.outputs.commit }}"
echo ""
if [ -n "$PREVIOUS" ] && git cat-file -e "$PREVIOUS^{commit}" 2>/dev/null; then
git log --no-merges --pretty=format:'• %s' "$PREVIOUS..HEAD" | head -20
else
git log --no-merges --pretty=format:'• %s' -10
fi
} | head -c 1000)
declare -A POSTS
for PLATFORM in win64 mac linux; do
FILE=$(ls artifacts/canary-$PLATFORM/tv2-* | head -1)
if [[ "$FILE" == *.unsigned ]]; then
echo "::error::$PLATFORM update is unsigned, refusing to publish."
exit 1
fi
RESPONSE=$(curl -sf "$BOT_API/bot$BOT_TOKEN/sendDocument" \
-F chat_id="$CHAT_ID" \
-F document=@"$FILE" \
-F caption="$CAPTION")
POSTS[$PLATFORM]=$(echo "$RESPONSE" | jq -r '.result.message_id')
echo "$PLATFORM -> post ${POSTS[$PLATFORM]}"
done
MANIFEST_B64=$(base64 -w0 Telegram/Resources/update/manifest.min.json) MANIFEST_B64=$(base64 -w0 Telegram/Resources/update/manifest.min.json)
MANIFEST_SIG_B64=$(base64 -w0 Telegram/Resources/update/manifest.sig) MANIFEST_SIG_B64=$(base64 -w0 Telegram/Resources/update/manifest.sig)
make_changelog() { # $1 = previous commit, $2 = counter # The macOS build is universal, both runtime platform keys
{ # point at the same post.
echo "Canary #$2 · ${{ needs.version.outputs.commit }}" NEW=$(jq -n \
echo "" --arg manifest "$MANIFEST_B64" \
if [ -n "$1" ] && git cat-file -e "$1^{commit}" 2>/dev/null; then --arg manifest_sig "$MANIFEST_SIG_B64" \
git log --no-merges --pretty=format:'• %s' "$1..HEAD" | head -20 --arg commit "${{ needs.version.outputs.commit }}" \
else --argjson base "${{ needs.version.outputs.base }}" \
git log --no-merges --pretty=format:'• %s' -10 --argjson counter "$COUNTER" \
fi --argjson win64 "${POSTS[win64]}" \
} | head -c 1000 --argjson mac "${POSTS[mac]}" \
} --argjson linux "${POSTS[linux]}" \
"{
publish_channel() { # $1=channel $2=chat_id $3=msg_id $4=counter $5=previous format: 1,
local CAPTION=$(make_changelog "$5" "$4") manifest: \$manifest,
declare -A POSTS manifest_sig: \$manifest_sig,
for PLATFORM in win64 mac linux; do channels: {
local FILE=$(ls artifacts/canary-$1-$PLATFORM/tv2-* | head -1) \"canary-$CHANNEL\": {
if [[ "$FILE" == *.unsigned ]]; then base: \$base,
echo "::error::canary-$1 $PLATFORM update is unsigned, refusing to publish." counter: \$counter,
return 1 commit: \$commit,
fi posts: {
local RESPONSE=$(curl -sf "$BOT_API/bot$BOT_TOKEN/sendDocument" \ win64: \$win64,
-F chat_id="$2" \ mac: \$mac,
-F document=@"$FILE" \ armac: \$mac,
-F caption="$CAPTION") linux: \$linux
POSTS[$PLATFORM]=$(echo "$RESPONSE" | jq -r '.result.message_id')
echo "canary-$1 $PLATFORM -> post ${POSTS[$PLATFORM]}"
done
# The macOS build is universal, both runtime platform keys
# point at the same post.
local NEW=$(jq -n \
--arg manifest "$MANIFEST_B64" \
--arg manifest_sig "$MANIFEST_SIG_B64" \
--arg commit "${{ needs.version.outputs.commit }}" \
--argjson base "${{ needs.version.outputs.base }}" \
--argjson counter "$4" \
--argjson win64 "${POSTS[win64]}" \
--argjson mac "${POSTS[mac]}" \
--argjson linux "${POSTS[linux]}" \
"{
format: 1,
manifest: \$manifest,
manifest_sig: \$manifest_sig,
channels: {
\"canary-$1\": {
base: \$base,
counter: \$counter,
commit: \$commit,
posts: {
win64: \$win64,
mac: \$mac,
armac: \$mac,
linux: \$linux
}
} }
} }
}")
curl -sf "$BOT_API/bot$BOT_TOKEN/editMessageText" \
-F chat_id="$2" \
-F message_id="$3" \
--form-string text="$NEW" || {
echo "::error::Could not edit the canary-$1 metadata message $3."
return 1
} }
} }")
curl -sf "$BOT_API/bot$BOT_TOKEN/editMessageText" \
publish_channel public "$PUBLIC_CHANNEL" "$PUBLIC_MSG_ID" \ -F chat_id="$CHAT_ID" \
"${{ needs.version.outputs.counter_public }}" \ -F message_id="$MSG_ID" \
"${{ needs.version.outputs.previous_public }}" --form-string text="$NEW" || {
publish_channel private "-100$PRIVATE_CHANNEL" "$PRIVATE_MSG_ID" \ echo "::error::Could not edit the metadata message $MSG_ID."
"${{ needs.version.outputs.counter_private }}" \ exit 1
"${{ needs.version.outputs.previous_private }}" }