Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
1e5a60f
Video playback in snap
jjoelj Jun 2, 2026
8d82f27
Merge branch 'development' into joel/2.0/desktop/2.0.0.0
jjoelj Jun 2, 2026
1092751
Potentially fix network connectivity listener for some users
jjoelj Jun 2, 2026
18b3120
Bump versions
jjoelj Jun 2, 2026
388b272
More version
jjoelj Jun 2, 2026
9b7ec7a
Fix originalUrl Priority
jjoelj Jun 2, 2026
6090692
invis ink wasn't actually invis (at least on desktop
jjoelj Jun 2, 2026
3ab5c52
async/await
jjoelj Jun 11, 2026
0caf5cb
Shared prefs work across isolates on windows/linux now. And corruptio…
jjoelj Jun 11, 2026
5bcbe9c
Merge branch 'development' into joel/2.0/desktop/2.0.0.0
jjoelj Jun 11, 2026
99871c4
Re-add lastOpenedChat functionality now that prefs works properly
jjoelj Jun 11, 2026
eecccff
Remove Tenor gif picker
jjoelj Jun 11, 2026
1e83b42
Switch back to old drop impl
jjoelj Jun 11, 2026
8f03dea
Add windows build script. Improve installer cleanup
jjoelj Jun 11, 2026
7c69485
Fix json format
jjoelj Jun 11, 2026
706a950
Update build scripts to set flutter version
jjoelj Jun 11, 2026
74fe9cc
Don't need this anymore
jjoelj Jun 11, 2026
f710e99
github workflow
jjoelj Jun 11, 2026
8000a77
Fix linux fvm
jjoelj Jun 11, 2026
8294f93
Fix workflow versions
jjoelj Jun 11, 2026
8100a64
Bump versions
jjoelj Jun 12, 2026
ffa3e37
Remove testing
jjoelj Jun 12, 2026
1ee6ba9
Change windows installer name. Don't attach msix to release
jjoelj Jun 12, 2026
8ef6eb0
Split windows artifacts so msix download is easier
jjoelj Jun 12, 2026
fcabcc3
enforce pub lockfile for CI
jjoelj Jun 12, 2026
36641df
support vs2026
jjoelj Jun 12, 2026
e0b3f5b
Test virustotal integration
jjoelj Jun 12, 2026
08af168
Use vs2026
jjoelj Jun 12, 2026
0f86a32
Test splitting debug info
jjoelj Jun 12, 2026
b0a7002
Lmao why did that work. Remove testing
jjoelj Jun 12, 2026
18a4853
Merge branch 'development' into joel/2.0/desktop/2.0.0.0
jjoelj Jun 19, 2026
f698838
Test signpath signing
jjoelj Jun 19, 2026
379a8bd
Rename unsigned builds
jjoelj Jun 19, 2026
078ac79
Change msix detection to support sideloading
jjoelj Jun 19, 2026
23b8582
Add splash screen on desktop so some UI shows up while stuff is loading
jjoelj Jun 20, 2026
c47d764
Merge branch 'refs/heads/development' into joel/2.0/desktop/2.0.0.0
jjoelj Jun 20, 2026
7d67a52
Break build into phases so that the insides can be signed before the …
jjoelj Jun 20, 2026
31e288d
Remove debug
jjoelj Jun 20, 2026
6bfd0ad
Don't double zip
jjoelj Jun 20, 2026
a9cd5f8
different signing config for test and release msix
jjoelj Jun 20, 2026
b0c5281
Force messages to show up as they happen
jjoelj Jun 21, 2026
eb9a0e1
flutter analyze on PR. Remove temp branch-specific stuff
jjoelj Jun 21, 2026
060706f
ensure no pub get without lockfile enforcement
jjoelj Jun 21, 2026
0312a33
Merge branch 'development' into joel/2.0/desktop/2.0.0.0
jjoelj Jun 21, 2026
64529e1
Fix #3054
jjoelj Jun 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
324 changes: 324 additions & 0 deletions .github/workflows/desktop-builds.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
name: Desktop Builds

on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
sign:
description: "Run SignPath signing on this manual build (test the signing pipeline without a release tag)"
type: boolean
default: false

permissions:
contents: write # attach artifacts to releases on tag builds

env:
FLUTTER_VERSION: "3.44.2"

jobs:
windows:
runs-on: windows-latest
timeout-minutes: 60
env:
SIGNPATH_API_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN }}
# Must match the signing cert subject: release cert on tags, test cert on manual builds.
SIGNED_MSIX_PUBLISHER: "${{ startsWith(github.ref, 'refs/tags/') && 'CN=SignPath Foundation,O=SignPath Foundation,C=US,S=DE,L=Lewes' || 'CN=Test certificate for ''BlueBubbles [OSS]''' }}"
SIGNED_MSIX_IDENTITY: BlueBubbles.BlueBubbles
steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Install fvm
run: choco install fvm -y

# Keyed on the Flutter version so the SDK is only re-downloaded when the
# version actually changes — not on every build-script edit.
- name: Cache Flutter SDK (fvm)
uses: actions/cache@v5
with:
path: ~/fvm
key: fvm-${{ runner.os }}-${{ runner.arch }}-${{ env.FLUTTER_VERSION }}

- name: Cache pub packages
uses: actions/cache@v5
with:
path: ~/AppData/Local/Pub/Cache
key: pub-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pubspec.lock') }}

# Inno Setup 6 and Rust (for super_native_extensions) are preinstalled
# on the windows-latest runner image.
- name: Build app (phase 1)
run: .\windows\build.ps1 -Phase Build
shell: pwsh

# sign=true when: token present AND (release tag or manual sign=true).
- name: Determine signing
id: gate
shell: bash
run: |
if [ -n "$SIGNPATH_API_TOKEN" ] && { [[ "$GITHUB_REF" == refs/tags/* ]] || [ "${{ inputs.sign }}" = "true" ]; }; then
echo "sign=true" >> "$GITHUB_OUTPUT"
else
echo "sign=false" >> "$GITHUB_OUTPUT"
fi

# Sign the inner binaries before packaging so both the msix and installer ship them signed.
- name: Zip app payload
if: ${{ steps.gate.outputs.sign == 'true' }}
shell: pwsh
run: Compress-Archive -Path 'build\windows\x64\runner\Release\*' -DestinationPath 'windows\bluebubbles-payload-unsigned.zip' -Force

- name: Upload app payload artifact
id: upload-payload
if: ${{ steps.gate.outputs.sign == 'true' }}
uses: actions/upload-artifact@v7
with:
name: bluebubbles-payload-unsigned
path: windows/bluebubbles-payload-unsigned.zip
archive: false

- name: Sign app payload (SignPath)
id: sign-payload
if: ${{ steps.gate.outputs.sign == 'true' }}
uses: signpath/github-action-submit-signing-request@v2
with:
api-token: ${{ env.SIGNPATH_API_TOKEN }}
organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG }}
signing-policy-slug: ${{ startsWith(github.ref, 'refs/tags/') && 'release-signing' || 'test-signing' }}
artifact-configuration-slug: ${{ vars.SIGNPATH_ARTIFACT_CONFIG_APP }}
github-artifact-id: ${{ steps.upload-payload.outputs.artifact-id }}
wait-for-completion: true
output-artifact-directory: out/payload

- name: Replace Release binaries with signed payload
if: ${{ steps.gate.outputs.sign == 'true' }}
shell: pwsh
run: |
Remove-Item -Path 'build\windows\x64\runner\Release' -Recurse -Force
Move-Item -Path 'out\payload' -Destination 'build\windows\x64\runner\Release' -Force

- name: Package installer + msix (phase 2)
run: .\windows\build.ps1 -Phase Package
shell: pwsh

- name: Upload unsigned installer artifact
id: upload-installer
uses: actions/upload-artifact@v7
with:
name: bluebubbles-installer-unsigned
path: windows/bluebubbles_installer.exe

# Only built when SIGNED_MSIX_PUBLISHER is configured.
- name: Upload unsigned msix artifact
id: upload-msix
if: ${{ hashFiles('windows/bluebubbles.msix') != '' }}
uses: actions/upload-artifact@v7
with:
name: bluebubbles-msix-unsigned
path: windows/bluebubbles.msix

- name: Upload debug symbols
uses: actions/upload-artifact@v7
with:
name: bluebubbles-windows-symbols
path: build/windows/symbols/

# Store msix — uploaded for MS Store submission only, never released here.
- name: Upload store msix artifact
uses: actions/upload-artifact@v7
with:
name: bluebubbles-msix-store
path: windows/bluebubbles-store.msix

# Sign the outer wrappers (inner binaries already signed above; msix config is container-only).
- name: Sign installer (SignPath)
id: sign-installer
if: ${{ steps.gate.outputs.sign == 'true' }}
uses: signpath/github-action-submit-signing-request@v2
with:
api-token: ${{ env.SIGNPATH_API_TOKEN }}
organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG }}
signing-policy-slug: ${{ startsWith(github.ref, 'refs/tags/') && 'release-signing' || 'test-signing' }}
artifact-configuration-slug: ${{ vars.SIGNPATH_ARTIFACT_CONFIG_INSTALLER }}
github-artifact-id: ${{ steps.upload-installer.outputs.artifact-id }}
wait-for-completion: true
output-artifact-directory: out/installer

- name: Sign release msix (SignPath)
id: sign-msix
if: ${{ steps.gate.outputs.sign == 'true' && startsWith(github.ref, 'refs/tags/') && steps.upload-msix.outputs.artifact-id != '' }}
uses: signpath/github-action-submit-signing-request@v2
with:
api-token: ${{ env.SIGNPATH_API_TOKEN }}
organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG }}
signing-policy-slug: 'release-signing'
artifact-configuration-slug: ${{ vars.SIGNPATH_ARTIFACT_CONFIG_MSIX }}
github-artifact-id: ${{ steps.upload-msix.outputs.artifact-id }}
wait-for-completion: true
output-artifact-directory: out/msix

- name: Sign test msix (SignPath)
id: sign-msix-test
if: ${{ steps.gate.outputs.sign == 'true' && !startsWith(github.ref, 'refs/tags/') && steps.upload-msix.outputs.artifact-id != '' }}
uses: signpath/github-action-submit-signing-request@v2
with:
api-token: ${{ env.SIGNPATH_API_TOKEN }}
organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG }}
signing-policy-slug: 'test-signing'
artifact-configuration-slug: ${{ vars.SIGNPATH_ARTIFACT_CONFIG_MSIX_TEST }}
github-artifact-id: ${{ steps.upload-msix.outputs.artifact-id }}
wait-for-completion: true
output-artifact-directory: out/msix

# Output paths follow the SignPath artifact configuration — adjust if yours nests differently.
- name: Upload installer
if: ${{ steps.sign-installer.outcome == 'success' }}
uses: actions/upload-artifact@v7
with:
name: bluebubbles-installer
path: out/installer/bluebubbles_installer.exe

- name: Upload msix
if: ${{ steps.sign-msix.outcome == 'success' || steps.sign-msix-test.outcome == 'success' }}
uses: actions/upload-artifact@v7
with:
name: bluebubbles-msix
path: out/msix/bluebubbles.msix

- name: Attach installer to release
if: ${{ startsWith(github.ref, 'refs/tags/') && steps.sign-installer.outcome == 'success' }}
uses: softprops/action-gh-release@v3
with:
files: out/installer/bluebubbles_installer.exe

- name: Attach msix to release
if: ${{ startsWith(github.ref, 'refs/tags/') && (steps.sign-msix.outcome == 'success' || steps.sign-msix-test.outcome == 'success') }}
uses: softprops/action-gh-release@v3
with:
files: out/msix/bluebubbles.msix

linux:
strategy:
fail-fast: false
matrix:
include:
- arch: x86_64
runner: ubuntu-latest
- arch: aarch64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
timeout-minutes: 60
steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
clang \
cmake \
ninja-build \
pkg-config \
libgtk-3-dev \
libwebkit2gtk-4.1-dev \
libmpv-dev \
libayatana-appindicator3-dev \
libnotify-dev

- name: Install fvm
run: |
curl -fsSL https://fvm.app/install.sh | bash
echo "$HOME/fvm/bin" >> "$GITHUB_PATH"

# Keyed on the Flutter version so the SDK is only re-downloaded when the
# version actually changes — not on every build-script edit.
- name: Cache Flutter SDK (fvm)
uses: actions/cache@v5
with:
path: ~/fvm
key: fvm-${{ runner.os }}-${{ runner.arch }}-${{ env.FLUTTER_VERSION }}

- name: Cache pub packages
uses: actions/cache@v5
with:
path: ~/.pub-cache
key: pub-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pubspec.lock') }}

- name: Build
run: bash linux/build.sh

- name: Upload artifacts
uses: actions/upload-artifact@v7
with:
name: bluebubbles-linux-${{ matrix.arch }}
path: bluebubbles-linux-${{ matrix.arch }}.tar.gz

- name: Attach to release
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v3
with:
files: bluebubbles-linux-${{ matrix.arch }}.tar.gz

# Scans the released downloads with VirusTotal and writes the analysis links to the
# job summary. Prefers the released installer/msix when present; falls back to the
# unsigned installer otherwise. The store msix is not scanned — Microsoft vets it.
virustotal:
needs: [windows, linux]
runs-on: ubuntu-latest
timeout-minutes: 15
env:
VT_API_KEY: ${{ secrets.VT_API_KEY }}
steps:
- name: Download artifacts
uses: actions/download-artifact@v8
with:
path: artifacts

- name: Select files to scan
id: select
run: |
files=()
if [ -f artifacts/bluebubbles-installer/bluebubbles_installer.exe ]; then
files+=("artifacts/bluebubbles-installer/bluebubbles_installer.exe")
else
files+=("artifacts/bluebubbles-installer-unsigned/bluebubbles_installer.exe")
fi
[ -f artifacts/bluebubbles-msix/bluebubbles.msix ] && \
files+=("artifacts/bluebubbles-msix/bluebubbles.msix")
[ -f artifacts/bluebubbles-linux-x86_64/bluebubbles-linux-x86_64.tar.gz ] && \
files+=("artifacts/bluebubbles-linux-x86_64/bluebubbles-linux-x86_64.tar.gz")
[ -f artifacts/bluebubbles-linux-aarch64/bluebubbles-linux-aarch64.tar.gz ] && \
files+=("artifacts/bluebubbles-linux-aarch64/bluebubbles-linux-aarch64.tar.gz")
{
echo 'files<<EOF'
printf '%s\n' "${files[@]}"
echo 'EOF'
} >> "$GITHUB_OUTPUT"

- name: VirusTotal scan
id: virustotal
if: env.VT_API_KEY != ''
uses: crazy-max/ghaction-virustotal@v5
with:
vt_api_key: ${{ env.VT_API_KEY }}
files: ${{ steps.select.outputs.files }}

# analysis output is comma-separated <filename>=<analysisURL> pairs.
- name: Format scan links
if: steps.virustotal.outputs.analysis != ''
env:
ANALYSIS: ${{ steps.virustotal.outputs.analysis }}
run: |
{
echo '### VirusTotal scans'
tr ',' '\n' <<< "$ANALYSIS" | while IFS='=' read -r file url; do
echo "- [\`$(basename "$file")\`]($url)"
done
} >> "$GITHUB_STEP_SUMMARY"
Loading