Skip to content

build(android): publish core + compose from CI on release tag (tag-derived version)#1089

Merged
zachshallbetter merged 3 commits into
mainfrom
build/android-maven-publish
Jul 22, 2026
Merged

build(android): publish core + compose from CI on release tag (tag-derived version)#1089
zachshallbetter merged 3 commits into
mainfrom
build/android-maven-publish

Conversation

@zachshallbetter

@zachshallbetter zachshallbetter commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Completes Android publishing so one release tag ships every platform in lockstep.

Started as a laptop-published, hardcoded-version stopgap. This PR makes it a real release path and fixes the two flaws in the original config before it lands.

Fixed

Version drift → tag-derived. Was version = "0.9.5" hardcoded in android/build.gradle.kts — the next publisher who forgot to bump it would overwrite 0.9.5 with different code. Now (findProperty("releaseVersion") ?: "0.0.0-SNAPSHOT"); CI passes -PreleaseVersion=<tag without v>. Verified with publishToMavenLocal: the artifact lands at the derived version, not 0.9.5.

Laptop publish → CI. android.yml gains a publish job (if: refs/tags/v*, permissions: packages: write, Actions GITHUB_TOKEN — no personal token) running :fundamental-core:publish :fundamental-compose:publish.

The lockstep result

git tag vX.Y.Z
  → release.yml   npm      @fundamental-engine/*@X.Y.Z
  → SPM tag       swift    resolves vX.Y.Z
  → android.yml   maven    com.fundamental:{fundamental-core,fundamental-compose}:X.Y.Z  ← this PR

Deliberately deferred

Maven Central migration — GitHub Packages requires a read token even for public packages (why Ascent needs gpr.key); npm and SPM don't. Moving to Maven Central drops that consumer friction, but it's a credential/namespace/signing task for before any external adopter, filed separately.

…ental:*:0.9.5)

The Kotlin port had no Maven publishing, so downstream apps could not consume it
without vendoring the source (which forks the engine). Adds maven-publish to the
two modules a Compose consumer needs:

  com.fundamental:fundamental-core:0.9.5     (pure kotlin/jvm)
  com.fundamental:fundamental-compose:0.9.5  (Android AAR; FieldView + Modifier.fieldBody)

compose's POM carries the transitive core dependency, so a consumer depends only
on fundamental-compose. Target is GitHub Packages via a shared
gradle/github-packages.gradle.kts; credentials come from GITHUB_ACTOR/GITHUB_TOKEN
env, nothing secret committed. local.properties (SDK path) stays gitignored.

Verified: both POMs resolve 200 from maven.pkg.github.com.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
fundamental-engine Ready Ready Preview Jul 22, 2026 3:36am

Request Review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ef407e38b4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

publishing {
publications {
create<MavenPublication>("release") {
from(components["release"]) // AGP's release AAR + POM (transitive core dep included)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Export Compose API dependencies from the AAR

Publishing components["release"] makes Gradle publish the release variant with this module's api/implementation separation. In the inspected Compose API, FieldView/Modifier.fieldBody expose @Composable, Modifier, Color, and LayoutCoordinates (FieldView.kt:78, 83-89, 262-263), but the corresponding Compose UI/runtime BOM/deps remain implementation, so a downstream app that follows the new coordinates and declares only com.fundamental:fundamental-compose will not get those classes on its compile classpath. Move the public Compose dependencies (and BOM constraint) to api or the published artifact is not self-contained.

Useful? React with 👍 / 👎.

Completes the Android publishing setup so a single release tag ships every platform in
lockstep. Two flaws in the initial publishing config are fixed here before it merges.

Version was hardcoded 'version = "0.9.5"'. The next publisher who forgot to bump it would
overwrite 0.9.5 with different code. It is now derived — CI passes -PreleaseVersion=<tag
without v>, and a local build with no property is a 0.0.0-SNAPSHOT that GitHub Packages will
not mistake for a release. Verified with publishToMavenLocal: the artifact lands at the
derived version, not 0.9.5.

Publishing was laptop-only with a personal token. android.yml now has a tag-triggered
publish job with packages: write and the Actions GITHUB_TOKEN, so releases come from CI with
an audit trail. On 'git tag vX.Y.Z': release.yml publishes npm, SPM resolves the Swift tag,
and this publishes com.fundamental:{fundamental-core,fundamental-compose}:X.Y.Z — one
version across three planes.

Maven Central migration (to drop the read-token requirement GitHub Packages imposes on
consumers) is deliberately left as a separate, credential-gated task for before any external
adopter.
@zachshallbetter zachshallbetter changed the title build(android): publish core + compose to GitHub Packages build(android): publish core + compose from CI on release tag (tag-derived version) Jul 22, 2026
@zachshallbetter
zachshallbetter merged commit 74bcff0 into main Jul 22, 2026
16 checks passed
@zachshallbetter
zachshallbetter deleted the build/android-maven-publish branch July 22, 2026 03:40
zachshallbetter added a commit that referenced this pull request Jul 22, 2026
Ships FieldHandle.guarantees and completes cross-plane publishing.

Breaking (pre-1.0): FieldHandle gains a required 'guarantees' member. Additive for every
consumer — reading field.guarantees breaks no calling code — and breaking only for
third-party code that implements FieldHandle itself, which must now provide the member.
Migration: return the runtime envelope or delegate to a wrapped field; the in-repo
implementers FieldLayer and FieldField both delegate and are the reference pattern. A break
to a protected symbol is the 0.MINOR position pre-1.0, hence 0.10.0 not a patch.

All seven packages bumped to 0.10.0 together; FIELD_VERSION advanced in lockstep across the
JS, Swift and Kotlin planes (drift test green). CHANGELOG restructured into a dated [0.10.0]
section with the breaking change and its migration called out first, so release.yml renders
a correct GitHub release body. Revived the per-version release note at
docs/release-notes/0.10.0.md. Corrected two stale version strings the drift had left behind
— PUBLISHING.md said 0.9.2, the README citation said 0.9.3.

One git tag v0.10.0 now releases npm, Swift (SPM tag) and Kotlin (Maven, via the CI publish
job added in #1089) at a single version.

typecheck, build, check:api (20 protected, no removals), check:docs, check:links and the
full suite all green.
zachshallbetter added a commit that referenced this pull request Jul 22, 2026
Ships **`FieldHandle.guarantees`** and completes cross-plane publishing.
The major we deferred `guarantees` for.

## Breaking (pre-1.0)

`FieldHandle` gains a **required** `guarantees` member.

- **Additive for every consumer** — reading `field.guarantees` breaks no
calling code.
- **Breaking only for third-party code that implements `FieldHandle`** —
it must now provide the member.
- **Migration:** return the runtime envelope, or delegate to a wrapped
field. `FieldLayer` (`@fundamental-engine/three`) and `FieldField`
(`@fundamental-engine/vanilla`) both delegate and are the reference
pattern.

A break to a protected symbol is the `0.MINOR` position pre-1.0 →
`0.10.0`, not a patch.

## Versions

- **7 published packages** → `0.10.0` together. The three **private
apps** (site, starter, observatory) stay at their independent versions —
the `@fundamental-engine/*` bump initially swept them in; reverted.
- **`FIELD_VERSION` → `0.10.0` in lockstep** across JS, Swift, Kotlin
(drift test green).

## Cross-plane release

One `git tag v0.10.0` now publishes every plane at one version:

```
release.yml   npm      @fundamental-engine/*@0.10.0
SPM tag       swift    resolves v0.10.0
android.yml   maven    com.fundamental:*:0.10.0   ← via the CI publish job from #1089
```

## Docs

- CHANGELOG restructured into a dated `[0.10.0]` with the breaking
change + migration first, so `release.yml` renders a correct GitHub
release body.
- Revived `docs/release-notes/0.10.0.md`.
- Fixed two stale version strings the drift left behind: `PUBLISHING.md`
(0.9.2) and the README citation (0.9.3).

## Gates

typecheck · build · `check:api` (20 protected, **no removals** —
`guarantees` is additive at the symbol level) · `check:docs` ·
`check:links` · full suite · FIELD_VERSION lockstep — all green. Publish
config verified against local Maven before the Android infra merged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant