CI has only ever produced debug-signed Android artifacts, because
build.gradle.kts:63-70 falls back to signingConfigs "debug" when
key.properties is absent, which is exactly the CI condition (#111).
Debug-signed builds cannot be installed over a Play install or over a
properly signed GitHub release, so users hit "App not installed".
Adds a separate workflow that signs with the real keystore.
Security posture, given this repo is PUBLIC and the keystore is the one
credential that cannot be replaced if leaked:
- No pull_request trigger. A PR, including from a fork, must never run
a job that can read these secrets.
- Pinned to the release-signing Environment, which carries a required
reviewer and only accepts main or v* tags. The signing secrets are
scoped to that environment, NOT to the repo, so a workflow that
omits the environment key cannot read them at all.
- Keystore decoded to disk only for the build, removed in a step with
if: always() so a failed build leaves nothing behind.
- Secrets passed via env: and printf'd into a file, never placed on a
command line where they would appear in process listings, and never
echoed.
Includes an apksigner check that fails the job if the built APK carries
CN=Android Debug. A green build alone does not prove release signing,
because the gradle fallback is silent; that check is what makes this
verifiable rather than assumed.
The keystore is PKCS12 despite its .jks extension, and PKCS12 cannot
carry a key password distinct from the store password, so one secret
correctly populates both fields.
Part of epic #312, plan #321 (Phase 5).
Publishes the Flutter web client. offband.org stays the Hugo marketing
site in OffbandMesh/offband-site, a separate Pages project, untouched.
push to main -> --branch=main -> production -> offband.app
push to dev -> --branch=dev -> dev tier -> dev.offband.app
Direct upload only. Cloudflare's dashboard "Connect to Git" must not be
enabled for this project; it would create a second git-integrated
project racing this workflow. Matches how offband-site deploys.
Because direct-upload projects cannot configure production branch
controls in the Cloudflare dashboard, the "never auto-deploy arbitrary
branches to prod" requirement from #122 is enforced by the workflow's
trigger list instead. That is the safety mechanism, and it is better
placed here than in dashboard state: reviewable and version controlled.
Builds via build_pipe rather than a plain `flutter build web`, because
build_pipe appends ?v=<version> to the bootstrap and manifest files.
Without it, browsers serve stale bundles after a deploy.
Two fixes folded in:
- pubspec.yaml build_pipe build_command did not pass
--dart-define-from-file, so a deployed site would have shipped a
dead GIF picker even though #331 wired the key into build.yml. The
deploy path and the CI path used different build commands.
- Retired the inherited zjs81 Workers deploy: removed deploy.yml and
wrangler.toml (which targeted THEIR account's "meshcore" project)
and dropped the now-dead `deploy` script from package.json. A root
wrangler.toml carrying Workers config can also interfere with
`wrangler pages deploy`.
Verified locally by running the real build with the bench toolchain:
build_pipe accepts the new flag, cache busting is applied
(flutter_bootstrap.js?v=e2019703...), output is 46 files with a 10.2 MB
largest asset (Pages limits are 25 MiB per asset and 20000 files), and
the Giphy key is confirmed compiled into main.dart.js.
Not verifiable locally: whether the repo's CLOUDFLARE_API_TOKEN carries
Pages:Edit scope. The workstation token is DNS-only and 403s on the
Pages API. A 403 on first deploy would indicate wrong scope; it fails
safely.
Part of epic #312, plan #321 (Phase 4).
Owner added GIPHY_API_KEY as a repo secret, resolving the open decision
flagged on this issue.
The app reads the key at COMPILE time via
String.fromEnvironment('GIPHY_API_KEY') (lib/widgets/gif_picker.dart:25),
so CI builds produced without it ship a non-functional GIF picker that
displays a "needs a Giphy API key" hint. That was harmless while CI
output was discarded; it stopped being harmless in #322 when artifacts
became real deliverables.
Generates dart_defines.json from the secret and passes
--dart-define-from-file, matching how the project is built locally
rather than inventing a second mechanism.
Wired into the four jobs that produce artifacts users run: android,
linux, web, windows. NOT wired into ios and macos, which are compile
checks producing nothing installable, and where the key would have no
effect on whether compilation succeeds.
Uses jq on the Linux runners rather than hand-escaped printf, so the
value is JSON-escaped correctly whatever it contains. An earlier printf
attempt silently mangled an escape and broke the YAML, which is the
argument for using the tool instead of string juggling. jq is verified
preinstalled on the runner images (Ubuntu 22.04 has 1.6, 24.04 has
1.7.1). The windows runner uses PowerShell ConvertTo-Json, since jq is
not guaranteed there.
Note: the key is compiled into the binary and is therefore extractable
from any published APK. Putting it in CI does not widen exposure beyond
what shipping the app already does.
Part of epic #312, plan #321 (Phase 3).
Three different Flutter versions were in play and none was the bench:
build.yml channel: stable (floating, resolving to 3.44.6)
flutter_dart.yml channel: stable (floating)
deploy.yml pinned 3.41.2, with a comment claiming it matched
local development, which had not been true for months
bench 3.44.1 / Dart 3.12.1
A floating toolchain is tolerable for a compile check and not tolerable
once CI produces signed release artifacts (#330), so this lands first.
Pinned all 8 flutter-action usages to 3.44.1, matching the bench exactly,
so "works locally" and "works in CI" mean the same thing. Verified against
Flutter's release index that 3.44.1 is a published stable release
(2026-06-01, Dart 3.12.1) rather than assuming a locally-installed
version was publicly fetchable.
Corrected deploy.yml's misleading local-parity comment rather than
leaving it to mislead the next reader.
Note: CI's green history is on 3.44.6 or later, so pinning DOWN may
surface a break the newer toolchain masked. That is the intent. If it
breaks, fix the break or move the bench up deliberately; do not re-float
the pin.
GIPHY_API_KEY as a CI secret was the third item of Phase 3 in #321. It
is deliberately NOT included here, is not dropped, and is flagged on
#331 for the owner's decision, since it is a credential only they can
create.
Part of epic #312, plan #321 (Phase 3).
Gemini adversarial review caught a real traceability defect.
On a `pull_request` event, `github.sha` evaluates to the last merge
commit of `refs/pull/N/merge`, not the PR head commit. That merge
commit is ephemeral and is not present in branch history, so a PR
artifact named with it cannot be traced back to any real commit,
which defeats the acceptance criterion this change was written to
satisfy.
Verified against GitHub's events-that-trigger-workflows reference:
"Note that GITHUB_SHA for this event is the last merge commit of the
pull request merge branch."
Now uses `github.event.pull_request.head.sha || github.sha`, which
resolves to the PR head on pull_request events and falls back to the
push SHA on pushes to dev/main.
Part of epic #312, plan #321 (Phase 1).
Two diagnostics faults from #313, fixed together:
1. Dead push trigger. build.yml and flutter_dart.yml declared
`push: branches: [main]`, but no `main` branch exists in this
repo (default is `dev`), so the push half of every trigger had
never fired. Nothing built on merge. Added `dev` to both; `main`
is created separately after this merges.
2. No artifacts. All six platform jobs stopped at `flutter build`,
so every binary CI produced was discarded with the runner. Added
actions/upload-artifact to the four jobs that produce something
installable or servable:
android build/app/outputs/flutter-apk/app-release.apk
windows build/windows/x64/runner/Release/
linux build/linux/x64/release/bundle/
web build/web/
ios and macos are compile checks only and upload nothing: ios is
built --no-codesign so it is not installable, and macos is kept
as a compile check per the owner's decision on #321.
Artifacts carry run number + commit SHA so a build traces back to a
commit, and use if-no-files-found: error so a wrong path fails loudly
rather than silently publishing an empty artifact.
Android artifacts are debug-signed for now; real signing is Phase 5.
The web artifact is the plain `flutter build web` output, not the
build_pipe versioned build used for deploys; Phase 4 addresses that.
Part of epic #312, plan #321 (Phase 1, owner-approved).
CI never compiled Windows, the primary test bench (RAK4631 / COM12 /
USB serial). Add a windows job mirroring the existing platform jobs:
runs-on windows-latest, flutter-action stable, pub get, flutter build
windows --release --no-pub.
Advisory to start (not in branch-protection required checks); flip to
required after 5 green runs (#226). The first run validates the
windows-latest env against the local bench (C++ ATL, Developer Mode,
llamadart/vulkan, flserial); fixes applied per the #226 plan.
Part of #226.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New workflow .github/workflows/triage-user-issues.yml. On issues:opened, if
the author is external (author_association not OWNER/MEMBER/COLLABORATOR),
apply user-submitted + priority:P2 + board:backlog using PROJECT_PAT so the
existing sync-labels-to-board.yml fires and sets Status=Backlog /
Priority=Medium on board #2. Maintainer-opened issues are untouched. type:
is left for manual triage.
workflow_dispatch entrypoint added for manual re-triage and the post-merge
live test: both triggers require the workflow to be on the default branch
first (issues:-triggered workflows only run from default; a new
workflow_dispatch is only dispatchable once on default), so validation
happens after the merge to dev.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
deploy.yml (inherited from upstream zjs81) fired on every tag push and failed on a missing CLOUDFLARE_API_TOKEN — secrets do not fork. Switch to workflow_dispatch-only to stop the per-tag failures; preserve the job for the scoped Offband web-app launch (Feature #121 / Epic #122).
The OffbandMesh org board (project #2) had no label->board sync, so issues never populated it (root cause of the 0-item board). Add sync-labels-to-board.yml (adapted from the DifferentWire/Unfocused canonical) wired to board #2's project + status/priority field and option IDs; runs on ubuntu-latest, uses repo secret PROJECT_PAT (classic PAT, project+repo scope). REQUIRES PROJECT_PAT set as a repo secret to function.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This adds a CI workflow in Github Actions to verify that the flutter builds compile for all supported platforms.
I tried adding Windows, but it currently fails, so I excluded it from this initial set.