Skip to content

ci: publish Maven artifacts and p2 update site#1311

Closed
joaodinissf wants to merge 2 commits into
dsldevkit:masterfrom
joaodinissf:ci-publish-artifacts
Closed

ci: publish Maven artifacts and p2 update site#1311
joaodinissf wants to merge 2 commits into
dsldevkit:masterfrom
joaodinissf:ci-publish-artifacts

Conversation

@joaodinissf
Copy link
Copy Markdown
Collaborator

@joaodinissf joaodinissf commented Apr 15, 2026

Summary

  • Snapshot builds: every push to master deploys Maven artifacts and publishes the p2 update site to gh-pages under p2/snapshots/<sha>/ (last 20 retained)
  • Releases: triggered via workflow_dispatch (one-click button) or by pushing a v* tag — strips SNAPSHOT, builds, deploys, publishes to p2/releases/<version>/, creates a GitHub Release with the zipped update site
  • p2/releases/latest/: always points at the highest semver release, regardless of publish order
  • Automatic version bumping: after each release, a PR is opened with the next version. Encodes the project's 0-4 minor cycle: after X.4.0, the bump is (X+1).0.0
  • Patch releases: push a v* tag from a release/X.Y.x branch — the bump PR targets the release branch, not master
  • Deploy topology unchanged: only ddk-repository is pushed to Maven packages

Release types

Type Trigger Example Auto-bump
Minor dispatch or tag from master 17.3.0 17.4.0-SNAPSHOT → PR against master
Major (auto) dispatch or tag from master 17.4.0 18.0.0-SNAPSHOT → PR against master
Patch tag from release/X.Y.x 17.3.1 17.3.2-SNAPSHOT → PR against release/17.3.x

Required one-time setting (admin action)

After merging, Pages must be enabled:

Settings → Pages → Source → "Deploy from a branch" → gh-pages / (root)

The first workflow run on master creates the gh-pages branch. Flip the setting after that.

p2 Repository URLs

Channel URL
Latest snapshot https://dsldevkit.github.io/dsl-devkit/p2/snapshots/latest/
Pinned snapshot https://dsldevkit.github.io/dsl-devkit/p2/snapshots/<sha>/
Latest release https://dsldevkit.github.io/dsl-devkit/p2/releases/latest/
Pinned release https://dsldevkit.github.io/dsl-devkit/p2/releases/{version}/

Fork verification (joaodinissf/dsl-devkit)

All scenarios tested end-to-end:

# Scenario Result
1 Snapshot push ✅ p2/snapshots/<sha>/ created
2 Snapshot accumulation (multiple pushes) ✅ Multiple SHA dirs + latest
3 Minor release via dispatch ✅ Tag created, p2/releases/, GitHub Release, bump PR
4 Minor release via tag push ✅ Same result as dispatch
5 Auto-major (dispatch at X.4.0) ✅ Bump PR says (X+1).0.0-SNAPSHOT
6 Patch release from release branch ✅ Bump PR targets release/X.Y.x
7 latest after patch (semver sort) ✅ Stays at highest version, not most recent

🤖 Generated with Claude Code

@joaodinissf joaodinissf marked this pull request as draft April 15, 2026 20:26
@joaodinissf joaodinissf force-pushed the ci-publish-artifacts branch 10 times, most recently from a116cf3 to 1c3237f Compare April 17, 2026 22:06
@joaodinissf
Copy link
Copy Markdown
Collaborator Author

(This comment was drafted by Claude on João's behalf.)

@rubenporras — flagging a one-time setting change this PR needs from an admin (João only has write on this repo). After merge, Pages needs to be enabled with source gh-pages / (root):

Settings → Pages → Source → "Deploy from a branch" → gh-pages / (root)

Recommended sequence:

  1. Merge PR
  2. Wait for the first workflow run on master to finish — it bootstraps the gh-pages branch with initial snapshot content
  3. Flip the Pages source as above

Without step 3 the workflow keeps pushing to gh-pages fine, but nothing serves at the documented URLs until Pages is enabled.

@joaodinissf joaodinissf marked this pull request as ready for review April 17, 2026 22:12
@joaodinissf joaodinissf force-pushed the ci-publish-artifacts branch 4 times, most recently from 4b14ce9 to 594a6de Compare April 20, 2026 08:21
@joaodinissf joaodinissf enabled auto-merge (rebase) April 20, 2026 08:36
@joaodinissf joaodinissf requested review from rubenporras and removed request for rubenporras April 20, 2026 11:48
@joaodinissf joaodinissf marked this pull request as draft April 20, 2026 11:49
auto-merge was automatically disabled April 20, 2026 11:49

Pull request was converted to draft

@joaodinissf joaodinissf force-pushed the ci-publish-artifacts branch from ed6f668 to 7f8e643 Compare April 21, 2026 13:50
rubenporras
rubenporras previously approved these changes Apr 21, 2026
@joaodinissf joaodinissf marked this pull request as ready for review April 21, 2026 14:17
rubenporras
rubenporras previously approved these changes Apr 21, 2026
rubenporras
rubenporras previously approved these changes Apr 21, 2026
@joaodinissf joaodinissf force-pushed the ci-publish-artifacts branch from 156d6bf to d3d8e18 Compare April 21, 2026 18:48
@joaodinissf joaodinissf force-pushed the ci-publish-artifacts branch 8 times, most recently from 391517a to 23f78dd Compare April 21, 2026 20:38
@joaodinissf
Copy link
Copy Markdown
Collaborator Author

Versioning strategy: lockstep vs. per-module — analysis and recommendation

Left by Opus at João's request.

This PR releases all DDK modules under a single version number. This note explains why, with references to how other Eclipse/OSGi projects approach the same question.

The two approaches

Aspect Lockstep (all modules share one version) Per-module (each module has its own version)
Release scope Every tag releases the full product Each module releases independently
Version coordination Trivial — one number to manage Complex — must track N version lifecycles and their compatibility matrix
Consumer experience Point at one p2 URL, everything resolves Must compose compatible versions from multiple repos/channels
OSGi Require-Bundle ranges All bundles at the same version; ranges are trivially satisfied Each module's version moves independently; ranges must be wide enough to tolerate drift without being so wide they admit broken combinations
Build verification Full integration test on every release Must test each module against its declared dependency ranges — combinatorial explosion
Release friction Higher per-release (full build even for one-line fix) Lower per-module, but coordination overhead when changes span modules
CI cost One pipeline, predictable N pipelines, or one pipeline with conditional module detection
Target platform management One URL per channel (snapshot, latest release, pinned release) Composite p2 repositories or manual version pinning

Why lockstep is the right choice for DDK

  1. OSGi structural coupling. Every DDK bundle declares Require-Bundle with version ranges in its MANIFEST.MF. These ranges create hard dependency links — releasing xtext at a version outside check.core's declared range breaks p2 resolution. Per-module versioning would require carefully maintaining version ranges across ~60 modules, with no tooling to verify range correctness short of a full product build — which is what lockstep already does.

  2. p2 repositories are coherent artifact sets. Eclipse's p2 provisioning system is designed around the idea that an update site contains a self-consistent set of installable units. Publishing check.core 17.2.0 alongside xtext 17.5.0 requires someone to have tested that specific combination. Lockstep guarantees this by construction. (See Eclipse Version Numbering policy and OSGi Semantic Versioning for the underlying principles.)

  3. Precedent from comparable projects.

    • Eclipse Platform (~200 bundles): lockstep quarterly release trains (2024-03, 2024-06, etc.). Individual bundles have their own micro-version increments between trains, but releases are coordinated.
    • Eclipse LSP4E (6 modules): strict lockstep, single parent POM version, tag-based releases with semantic versioning.
    • Eclipse Kura (IoT gateway, ~50 bundles): lockstep with explicit versioning policy — "all Kura bundles are released together with the same version."
    • No major Tycho-based project uses fully independent per-module versioning.
  4. DDK's module count (~60) is in the sweet spot for lockstep. Per this analysis of lockstep vs. independent versioning: lockstep is ideal for tightly coupled modules where the API surface between them is hard to define and breaking changes are hard to detect in isolation. DDK's DSL runtimes, generators, and UI plugins are exactly this — deeply intertwined via Xtext infrastructure, Guice bindings, and shared extension points.

  5. The practical alternative to per-module isn't zero friction — it's different friction. Moving to per-module would require: per-module CI pipelines or conditional build detection, per-module tagging conventions, composite p2 repository management, and a compatibility testing strategy. The overhead of managing that for 60 tightly-coupled OSGi modules would exceed the overhead of the occasional "release everything for a one-line fix" that lockstep requires.

What this workflow provides instead of per-module decoupling

Rather than splitting the release granularity (high cost, low payoff for OSGi), this PR reduces release friction within lockstep:

  • Tag-based releases — release when ready, not on a fixed cadence
  • Automated version bumping — a PR is opened after each release to start the next cycle
  • Snapshot + release p2 coexistence — consumers choose: always-latest (/p2/snapshots/) or pinned (/p2/releases/<version>/)
  • /p2/releases/latest/ — a stable URL for target platforms and future baseline version validation
  • GitHub Release with attached p2 zip — download a self-contained update site per release

@joaodinissf
Copy link
Copy Markdown
Collaborator Author

Proposed rework: dispatch-based releases, smart versioning, snapshot retention

Left by Opus at João's request.

This PR is being reworked to add three capabilities. Posting the design here for visibility before implementing.

Release mechanism

  • Primary: workflow_dispatch — one-click "Release" button in the GitHub UI, no manual git tag needed. The release version is derived from the current pom SNAPSHOT version. Tags are created by the workflow using GITHUB_TOKEN (which doesn't re-trigger the workflow, avoiding double runs).
  • Secondary: tag push — for patch releases from release branches, or as a manual fallback. The workflow validates the tag matches the pom version at the tagged commit.

Smart version bumping

  • The project follows a fixed 0–4 minor cycle: X.0 → X.1 → X.2 → X.3 → X.4 → (X+1).0 → ...
  • The bump logic encodes this: after releasing X.4.0, the next SNAPSHOT is automatically (X+1).0.0. No human decision needed.
  • Patch releases (from release branches): version Z increments, bump PR targets the release branch, not master.
Type Where Trigger Example Auto-bump
Minor master dispatch or tag 17.3.0 17.4.0-SNAPSHOT → PR against master
Major (auto) master dispatch or tag 17.4.0 18.0.0-SNAPSHOT → PR against master
Patch release/X.Y.x tag only 17.3.1 17.3.2-SNAPSHOT → PR against release/17.3.x

p2 snapshot retention

  • Currently: snapshots overwrite a single p2/snapshots/ directory on each push
  • Proposed: accumulate under p2/snapshots/<short-sha>/ with a p2/snapshots/latest/ alias, pruned to the last 20
  • Consumers can pin to a specific snapshot by SHA, or use latest/ for always-current
  • Releases continue to accumulate permanently under p2/releases/<version>/ with a p2/releases/latest/ alias

What stays the same

  • Lockstep versioning (see analysis in previous comment)
  • gh-pages branch-based p2 publication with concurrency serialization
  • Maven deploy topology (only ddk-repository deploys)
  • jgit-based build qualifiers
  • GitHub Release creation with p2 zip
  • Maven SNAPSHOT package retention (last 20)
  • Baseline version validation plugin (skip=true until first release)

Patch release workflow (new)

Release branches are created on demand:

  1. Branch release/17.3.x from tag v17.3.0
  2. Set version to 17.3.1-SNAPSHOT, commit the fix
  3. Push tag v17.3.1 — the workflow validates, builds, publishes, and opens a bump PR against the release branch

Edge cases handled

  • Same-SHA branch+tag push → concurrency group serializes, disjoint paths
  • Build failure mid-release → no partial gh-pages/release state; Maven deploy conflict on re-tag requires manual package cleanup or new patch version
  • First-ever run → gh-pages bootstrap via orphan branch
  • Delayed bump PR merge → documented, prompt merge recommended
  • workflow_dispatch tag creation → GITHUB_TOKEN anti-loop prevents double run

@joaodinissf joaodinissf force-pushed the ci-publish-artifacts branch 7 times, most recently from 5337a7c to 5a40e6a Compare April 21, 2026 23:24
On every push to master, build and deploy SNAPSHOT artifacts, publish
the p2 update site to gh-pages (last 20 snapshots retained by SHA).
Releases are triggered via workflow_dispatch (one-click) or by pushing
a v* tag. The workflow creates GitHub Releases, maintains a
p2/releases/latest/ alias (highest semver, not most recent), and opens
a version-bump PR with smart logic encoding the project's 0-4 minor
cycle. Patch releases from release branches are supported via tag push.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@joaodinissf joaodinissf force-pushed the ci-publish-artifacts branch from 5a40e6a to 6119f38 Compare April 21, 2026 23:27
@joaodinissf
Copy link
Copy Markdown
Collaborator Author

Superseded by #1357

@joaodinissf joaodinissf deleted the ci-publish-artifacts branch May 21, 2026 14:38
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.

2 participants