Skip to content

Commit 7b43f30

Browse files
committed
docs: design 1.x release adoption loop
1 parent 1471c0d commit 7b43f30

1 file changed

Lines changed: 293 additions & 0 deletions

File tree

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
# LayoutX2C 1.x Release and Adoption Loop Design
2+
3+
## Goal
4+
5+
Turn the existing 1.0 repository readiness work into a repeatable release and
6+
consumer-adoption loop. A release candidate must be verifiable as the same
7+
artifacts that a clean Android project will consume, and project documentation
8+
must distinguish released, tested, and planned behavior.
9+
10+
This is the first of four improvement projects. Correctness matrix expansion,
11+
core generator refactoring, and data-driven feature growth remain separate
12+
follow-up projects so that the release baseline stays stable while this work is
13+
implemented.
14+
15+
## Current State
16+
17+
- Tag `1.0.0` exists and points to the first stable release commit.
18+
- `main` is 27 commits past `1.0.0`, including backward-compatible features,
19+
fixes, tests, and reporting improvements.
20+
- The root build and `LayoutX2CPlugin.VERSION` both still hard-code `1.0.0`.
21+
This allows the plugin artifact version and its injected runtime/processor
22+
dependency versions to drift.
23+
- `docs/ROADMAP.md` still describes 1.0 as waiting for external release and
24+
links to two documents that were deliberately removed after the release.
25+
- CI validates JVM tests and coverage on one toolchain. The release workflow
26+
assembles Android artifacts, but it does not prove that a standalone project
27+
can resolve and use the locally published plugin marker, runtime, and
28+
processor together.
29+
- Compatibility claims currently mix intended support with combinations that
30+
CI actually verifies. In particular, the repository uses minSdk 23 and
31+
compileSdk 36 while the roadmap still mentions API 21-35.
32+
33+
## Scope
34+
35+
### 1. Version Ownership
36+
37+
Use the root Gradle project version as the only artifact version source.
38+
39+
- The development default becomes `1.1.0-SNAPSHOT`. The minor version reflects
40+
the backward-compatible capabilities added since 1.0.0.
41+
- The Gradle property `layoutx2c.version` is the local candidate override, and
42+
`VERSION` remains the release workflow override. If both are present and
43+
differ, configuration fails. A release build for tag `1.1.0` therefore
44+
publishes `1.1.0`, while ordinary local builds remain snapshots.
45+
- Remove the literal version from `LayoutX2CPlugin`. Generate a small plugin
46+
resource from `project.version` during `processResources`, and load it when
47+
adding `runtime` and `ksp-processor` dependencies.
48+
- Missing or malformed generated version metadata is a plugin application
49+
error with an actionable message. The plugin must never silently fall back to
50+
an unrelated release.
51+
- Add a test proving that all publications and plugin-injected coordinates use
52+
the same requested version.
53+
54+
This design does not introduce a version catalog or an external release tool.
55+
The repository has one product version, so a provider-backed root Gradle value
56+
is sufficient.
57+
58+
### 2. Versioned Release Documentation
59+
60+
Replace disposable root-level release documents with stable versioned history:
61+
62+
- `docs/releases/1.0.0.md` archives the published 1.0.0 notes from the release
63+
tag, corrected only where they claimed features that landed later.
64+
- `docs/migrations/1.0.md` records the stable API boundary introduced by 1.0.
65+
- `docs/releases/1.1.0.md` describes the user-visible changes since 1.0.0 and
66+
serves as the GitHub Release notes source when 1.1.0 is published.
67+
- `CHANGELOG.md` is a compact index linking to versioned release notes rather
68+
than duplicating their full contents.
69+
- `docs/ROADMAP.md` records 1.0 as released, describes 1.1 as the current
70+
development line, and removes the claim that external 1.0 publication is the
71+
only remaining task.
72+
- `docs/RELEASE.md` references versioned notes and separates repository
73+
readiness, artifact publication, and post-publication verification.
74+
75+
README installation examples use the latest stable released version until a
76+
new release is actually published. Main-branch-only capabilities are labelled
77+
as upcoming 1.1 behavior instead of being presented as available in 1.0.0.
78+
79+
### 3. Release Readiness Gate
80+
81+
Add a root `releaseCheck` lifecycle task. It composes existing build tasks and
82+
new deterministic validations rather than reimplementing Gradle publication
83+
logic in shell scripts.
84+
85+
The gate must verify:
86+
87+
- the effective version is valid SemVer and is not a snapshot for a release;
88+
- the workflow tag or manual version equals the effective project version;
89+
- all publishable modules use the same group and version;
90+
- required POM fields and the Gradle plugin marker are present in a temporary
91+
Maven repository;
92+
- local Markdown links in README, ROADMAP, release, migration, and changelog
93+
documents resolve to tracked files;
94+
- JVM tests, Android assembly, report generation, and the standalone consumer
95+
smoke test pass.
96+
97+
Repository checks may run `releaseCheck` with an explicit candidate version,
98+
but they do not sign or upload artifacts. Signing, credentials, and remote
99+
availability remain release-environment concerns.
100+
101+
### 4. Standalone Consumer Smoke Project
102+
103+
Create `integration-tests/consumer-smoke` as a standalone Android Gradle build,
104+
not a subproject and not a composite build.
105+
106+
The smoke flow is:
107+
108+
1. Publish all four artifacts plus the Gradle plugin marker to an isolated
109+
repository under the root build directory.
110+
2. Start the standalone build with only Google, Maven Central, Gradle Plugin
111+
Portal where required, and that isolated repository.
112+
3. Apply `io.github.donglua.layoutx2c` by version.
113+
4. Compile a minimal annotated layout configuration.
114+
5. Assert that the generated factory/registry exists and that
115+
`layoutX2CReport` produces JSON and HTML output.
116+
6. Run the smoke build with Gradle dependency refresh so a developer cache
117+
cannot hide missing publication metadata or transitive dependencies.
118+
119+
The fixture must not use `includeBuild`, direct project dependencies, or the
120+
Gradle TestKit plugin classpath. Those mechanisms would bypass the exact
121+
coordinates external users resolve.
122+
123+
The initial smoke project uses the repository's current Android/Kotlin/KSP
124+
toolchain. Broader AGP, KSP, JDK, and Android API combinations belong to the
125+
next correctness and compatibility project.
126+
127+
### 5. CI and Release Flow
128+
129+
Split checks by purpose while keeping each result visible:
130+
131+
- The normal check workflow runs unit tests with coverage and the standalone
132+
consumer smoke test.
133+
- The release workflow runs `releaseCheck` before secret validation and remote
134+
publication. Repository failures therefore surface before credentials are
135+
touched.
136+
- A successful artifact publication is followed by explicit resolution checks
137+
for each Maven coordinate and the Gradle plugin marker.
138+
- GitHub Release creation happens only after remote verification succeeds and
139+
uses `docs/releases/<version>.md` as its notes source.
140+
- A failed post-publication check does not attempt destructive rollback. It
141+
fails with the exact unresolved coordinate and leaves recovery to the
142+
maintainer because Maven releases are immutable.
143+
144+
The workflow continues to allow manual dispatch, but the supplied version must
145+
match an existing commit tag before a stable release can be published. Numeric
146+
SemVer tags without a `v` prefix remain the canonical format for consistency
147+
with the existing release trigger.
148+
149+
### 6. Adoption-Facing README
150+
151+
Restructure the top of README around the first successful integration path:
152+
153+
- stable version and minimal plugin setup;
154+
- one minimal `@FastLayoutConfig` example;
155+
- command to generate and open the fallback report;
156+
- a short tested-toolchain table that distinguishes current CI coverage from
157+
intended compatibility;
158+
- stable versus experimental API policy;
159+
- links to release history, migration notes, benchmarks, detailed support
160+
coverage, and release procedure.
161+
162+
The complete View and attribute list remains available but moves below the
163+
integration and diagnostics path. The README should help a new adopter reach a
164+
working build before asking them to understand every supported XML construct.
165+
166+
## Component Boundaries
167+
168+
### Build Version Provider
169+
170+
Owns the effective development or release version and exposes it to all
171+
subprojects. The generated plugin resource is an output of this provider, not a
172+
second source of truth.
173+
174+
### Release Validation Tasks
175+
176+
Own local, deterministic repository validation. They may inspect generated POM,
177+
plugin marker, documentation links, and publication directories. They do not
178+
perform network access or mutate tags.
179+
180+
### Consumer Smoke Build
181+
182+
Owns external-consumer behavior. It depends only on repository coordinates and
183+
public plugin/API contracts. It must remain small enough that a failure points
184+
to publication wiring rather than demo application complexity.
185+
186+
### Release Workflow
187+
188+
Owns credentials, signing, remote publication, remote resolution checks, and
189+
GitHub Release creation. It invokes repository tasks instead of embedding
190+
product-specific validation rules in YAML.
191+
192+
### Documentation
193+
194+
Owns the public contract at three time scales: README for current adoption,
195+
versioned notes for released history, and ROADMAP for future intent.
196+
197+
## Failure Handling
198+
199+
- Version mismatch: fail before tests or publication and print the effective
200+
project version, requested release version, and tag.
201+
- Missing plugin version resource: fail plugin application and name the missing
202+
resource; do not inject `unspecified` or a previous stable version.
203+
- Broken local documentation link: fail `releaseCheck` with source file and
204+
target path.
205+
- Smoke resolution failure: retain Gradle diagnostics and identify whether the
206+
plugin marker, runtime, processor, or compiler-core coordinate is absent.
207+
- Missing release secrets: fail only after repository readiness succeeds, using
208+
the existing explicit secret names.
209+
- Remote propagation delay: retry bounded resolution checks before failing.
210+
Do not recreate or overwrite an immutable Maven release.
211+
212+
## Testing Strategy
213+
214+
### Unit and Functional Tests
215+
216+
- Test version parsing and release/tag mismatch diagnostics.
217+
- Extend Gradle plugin functional tests to assert injected dependency versions.
218+
- Test document-link validation with valid, missing, and external links.
219+
- Test publication metadata for all modules and the plugin marker.
220+
221+
### Integration Test
222+
223+
- Publish to an isolated local Maven repository.
224+
- Build `integration-tests/consumer-smoke` with refreshed dependencies.
225+
- Verify generated source/classes and report JSON/HTML outputs.
226+
- Run from both local `releaseCheck` and CI.
227+
228+
### Existing Gates
229+
230+
- Preserve `test`, Kover XML/HTML generation, demo debug/release assembly,
231+
runtime release assembly, and Android test APK assembly.
232+
- Connected generated-versus-inflated tests remain optional in this phase and
233+
become a required matrix concern in the next project.
234+
235+
## Delivery Sequence
236+
237+
1. Introduce the single version provider and eliminate plugin version drift.
238+
2. Add local publication metadata assertions and the standalone consumer smoke
239+
project.
240+
3. Add `releaseCheck` and wire normal/release CI around it.
241+
4. Repair and version release documentation, then restructure README.
242+
5. Prepare 1.1.0 release notes and verify the complete gate with a candidate
243+
version.
244+
245+
Each step must leave ordinary development tests green. Documentation is updated
246+
after task names and outputs stabilize, so commands shown to adopters are
247+
executable rather than aspirational.
248+
249+
## Acceptance Criteria
250+
251+
- No production source file contains a second literal artifact version used for
252+
dependency injection.
253+
- `./gradlew releaseCheck -Playoutx2c.version=1.1.0` validates the candidate
254+
without signing or network publication.
255+
- A standalone Android project resolves the locally published plugin and all
256+
transitive LayoutX2C artifacts, generates code, and produces both report
257+
formats.
258+
- README documents the latest actually published version and its integration
259+
path; unreleased behavior is visibly labelled.
260+
- ROADMAP, release instructions, changelog, migration notes, and release notes
261+
contain no broken local links or contradictory 1.0 status.
262+
- CI checks consumer resolution on every pull request and release readiness
263+
before using publishing secrets.
264+
- The release workflow can publish and then create a GitHub Release only after
265+
remote coordinates are resolvable.
266+
267+
## Non-Goals
268+
269+
- Triggering the real 1.1.0 tag or remote publication without a separate
270+
maintainer decision.
271+
- Expanding the supported View, attribute, DataBinding, style, or theme surface.
272+
- Refactoring `ViewRegistry`, `BindingFacadeGeneratorV2`, or processor
273+
architecture.
274+
- Claiming a broad compatibility matrix based on one smoke toolchain.
275+
- Adding an IDE plugin, documentation site, telemetry, or automatic updater.
276+
277+
## Risks and Mitigations
278+
279+
- **Nested Gradle builds can accidentally use cached artifacts.** Use an
280+
isolated repository, explicit version, and dependency refresh; forbid
281+
composite substitution.
282+
- **Snapshot metadata can make smoke tests nondeterministic.** Use a unique
283+
isolated repository per build and delete it through Gradle task outputs, not
284+
ad hoc broad cleanup commands.
285+
- **README may advertise an unpublished version during release preparation.**
286+
Keep installation snippets on the latest stable version until publication;
287+
describe 1.1 changes in versioned release notes.
288+
- **Remote repositories may be eventually consistent.** Use bounded retries and
289+
fail with recovery instructions after publication rather than hiding the
290+
state.
291+
- **Release automation can overreach.** Local tasks never create tags, publish
292+
remotely, or create GitHub releases. Those actions remain confined to an
293+
explicitly triggered workflow with maintainer credentials.

0 commit comments

Comments
 (0)