M1-02: Woodpecker CI — lint and simulator build #2
Labels
No labels
area/agent
area/capture
area/editor
area/geo
area/security
area/sync
area/transcription
area/ui
area/vault
area/voice-memos
blocked
milestone/M1
milestone/M2
milestone/M3
milestone/M4
needs-decision
needs-hardware
p0
p1
p2
type/bug
type/enhancement
type/feature
type/idea
type/infrastructure
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Stash/stash-ios#2
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
CI on every branch: SwiftLint, then
xcodebuildagainst the iOS simulator.Scope
.woodpecker.ymlrunning SwiftLint and a simulator build.main.in
stash-relayat M3..xcodeprojis not committed.Why it's this early
The repos were split precisely so CI stays simple — a monorepo would have meant path filters
to stop an iOS change from redeploying
stash.relay.wes.today. Keep this config boringenough that it never becomes a thing to reason about.
Done when
A pushed branch goes green, and a deliberately-broken lint rule goes red.
Milestone 1 of the Stash v3 rebuild, scoped 2026-08-06. M1 is a functioning iOS app on Wes's
phone with no sync at all — the entire goal is recording voice memos reliably and getting
them off the device by hand. Sync arrives in M3.
Working agreement for every issue in this repo: feature branch, tested before the PR opens
(on-device where the label says so), then Wes reviews the PR and we walk the code together.
Docs update in the same commit. A PR that takes more than 20 minutes to review is too big —
say so and split it.
Branch
m1-02-woodpecker-ci, one commit. Stacked onm1-01-repo-scaffold— CI needs an app to build, so it branches off #1 rather than offmain, and will show that commit until #1 merges.What's in it
.woodpecker.yml, three steps, on every branch and on PRs targetingmain:swiftlint lint --strict— first, because it is seconds where a build is minutes.xcodegen generate— the.xcodeprojisn't committed, so CI generates it the way a fresh clone does. Separate step so "project.yml is wrong" reads differently from "the code doesn't compile".xcodebuildagainstgeneric/platform=iOS SimulatorwithCODE_SIGNING_ALLOWED=NO.No deploy step. The generic destination is deliberate over a named device — pinning to
iPhone 17 Pro Maxmeans CI breaks the day Xcode retires that simulator, which has nothing to do with the change under test.What I validated
Locally, by parsing the YAML and executing each step's commands in order against a clean tree:
lintstep exits 2 withForce Unwrapping Violation, stopping the pipeline before the build. So a deliberately-broken lint rule does go red.What I did not validate — and this is the blocker
This pipeline has never run in Woodpecker, and cannot yet.
xcodebuildis macOS-only, and Woodpecker has exactly one agent: thedockerbackend container sitting next to the server on the Linux core-infra node (roles/woodpecker/templates/compose.yml.j2inhomeprod). There is no macOS runner, so there is nowhere for step 3 to execute.I've labelled the workflow
platform: darwin/arm64so it routes to a macOS agent the moment one exists. Until then the pipeline sits pending, not failing. Pending means nothing ran, which is worth knowing, because a pending pipeline next to a PR reads like a passing one at a glance.Closing the gap is a
homeprodchange, not astash-iosone, and it is bigger than this issue: register a macOS Woodpecker agent on thelocalbackend with Xcode,xcodegenandswiftlinton PATH, which also means exposing the server's gRPC port beyond the compose network it is currently confined to, with the firewall rule that implies. That is your call on infrastructure, so I stopped here rather than reaching intohomeprodfor it.The "done when" on this issue — a pushed branch goes green — is therefore not met. The config is written and its commands are proven; the runner is not there. Leaving this open.
Updated on Wes's call: split into two workflows so the half that doesn't need a Mac actually runs.
.woodpecker.ymlis gone, replaced by:.woodpecker/lint.yml—swiftlint lint --strictin the official Linux container. Labelledbackend: docker, so it lands on the agent that already exists. This runs today. Labelled by backend rather than by platform on purpose: the SwiftLint image is published for amd64 and arm64 both, so this workflow has no reason to hold an opinion about the agent's CPU..woodpecker/build.yml—xcodegen generatethen the simulatorxcodebuild, labelledplatform: darwin/arm64. Unchanged otherwise, and still pending until a macOS agent exists.Still no deploy step in either.
What I validated on the split
Ran SwiftLint in the actual
ghcr.io/realm/swiftlintLinux container, with the entrypoint overridden by a shell — which is how Woodpecker invokes a step, and it matters here: the image's own entrypoint isswiftlint, so a naivedocker run ... swiftlint lint --strictpasses "swiftlint" and "lint" as paths and silently lints the wrong thing. Under a shell entrypoint it behaves correctly:error: Force Unwrapping Violation, exit 2.Still not validated
Neither workflow has run in Woodpecker.
lint.ymlnow can, but I have no Woodpecker token and didn't go poking at the server, so I haven't confirmedstash-iosis activated in Woodpecker at all — if the repo was never enabled there, nothing fires regardless of what's in these files. Worth a glance at ci.wes.today when you review.build.ymlstill cannot run, for the reason in my previous comment. Building by hand locally in the meantime, which the tested-before-PR rule already required;AGENTS.mdandREADME.mdboth say so now.Leaving this open.
This has been resolved.