Skip to content

Inherit publishing destinations when a module leaves them unset - #765

Merged
alexander-yevsyukov merged 4 commits into
masterfrom
claude/beautiful-lichterman-e23928
Sep 24, 2026
Merged

alexander-yevsyukov merged 4 commits into
masterfrom
claude/beautiful-lichterman-e23928

Conversation

@alexander-yevsyukov

Copy link
Copy Markdown
Contributor

What

SpinePublishing.publishTo() now returns the destinations of the extension it found on the project (ext.destinations), not those of the extension running configured().

  • SpinePublishing.kt: the fix, one token.
  • SpinePublishingTest: a new case. A subproject has its own extension with destinations unset, and the root extension has them set. publishTo() called through the subproject's extension must return the root's set.
  • uber-jar-module.gradle.kts: removes destinations = rootProject.the<SpinePublishing>().destinations, and the import only that line used.
  • PublicationChecksumsReconfigurationIgTest: this fixture copies the uber-jar-module pattern, so its module now leaves destinations unset too. That makes it a real-build check of the inheritance. A formatter also removed a stray double blank line there.

Why

publishTo() is a Project extension declared inside SpinePublishing, so it has two implicit receivers. Project has no destinations, so the bare name resolved to this@SpinePublishing, the extension running configured(), instead of ext. That caused two problems:

  1. A module that opens spinePublishing { customPublishing = true } without destinations walked up to the root and found the root's set. It then read its own uninitialized one, and the build failed with UninitializedPropertyAccessException during configuration. That's why uber-jar-module.gradle.kts, and three modules in logging, copied the root's destinations by hand.
  2. When the extension running configured() had destinations set and the project's own extension had a different set, the wrong set was used, with no error.

The existing test called publishTo() only through the root extension. There both receivers are the same object, so the test couldn't catch either problem.

The function has to stay a member: ext::destinations.isInitialized compiles only inside the class that declares the property. I checked this; a top-level version fails with "Backing field … is not accessible at this point".

Reviewer notes

  • Both new checks fail on master with UninitializedPropertyAccessException and pass with the fix.
  • tool-base applies uber-jar-module in five modules. They're all direct children of a root that sets destinations and lists them in modulesWithCustomPublishing, so they get the same set as before.
  • One behaviour change: if a root has no SpinePublishing or no destinations, the removed line used to fail loudly. Now the module gets an empty set and isn't published to any remote repository, with only an info log. Every other module already behaves this way.
  • Follow-up in logging: after its next ./config/pull, logging, logging-testlib and otel-backend can drop their hand-copied destinations.
  • This branch is one commit behind master (Publish an SPDX SBOM with each Maven publication #764). There's no conflict, and I checked the merged result locally (origin/master plus this diff): :buildSrc:build detekt passes, 111 tests, 0 failures. master requires branches to be up to date, so this needs Update branch before it can merge.

Verification

./gradlew :buildSrc:build detekt on JDK 17: 99 tests, 0 failures, detekt clean. CI in this repository runs only detekt, not the buildSrc tests.

🤖 Generated with Claude Code

alexander-yevsyukov and others added 2 commits September 24, 2026 17:30
`SpinePublishing.publishTo()` is a member extension of `Project`, so the
unqualified `destinations` it returned bound to the extension running
`configured()`, not to the `ext` it had just found on the project.

A module opening its own `spinePublishing { customPublishing = true }`
without `destinations` walked up to the root, found the root's set, and
then read its own uninitialized one, failing the build with
`UninitializedPropertyAccessException` instead of inheriting.

The existing test called `publishTo()` only through the root extension,
where both receivers are the same object, so it could not catch this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With `publishTo()` inheriting correctly, the module no longer needs
`destinations = rootProject.the<SpinePublishing>().destinations`: the walk
from the module reaches the root and returns the same set.

The reconfiguration TestKit fixture models this script, so its module now
leaves `destinations` unset too. That makes it a real-build check of the
inheritance: without the previous commit, it fails with
`UninitializedPropertyAccessException`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-24T16:59:38.180572Z 39d4e0d New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@alexander-yevsyukov alexander-yevsyukov self-assigned this Sep 24, 2026
@alexander-yevsyukov alexander-yevsyukov moved this to 🏗 In progress in v2.0 Sep 24, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

Add coverage for initialized extensions with different destination sets.

Review effort: Lite
Findings: None

What changed in this PR

Fixes publishing destination inheritance for module-local SpinePublishing extensions.

Changes:

  • Corrects publishTo() destination lookup.
  • Removes redundant destination copying.
  • Adds unit and integration coverage.
File Summary
buildSrc/​src/​test/​kotlin/​io/​spine/​gradle/​publish/​SpinePublishingTest.kt Tests destination inheritance.
buildSrc/​src/​test/​kotlin/​io/​spine/​gradle/​publish/​PublicationChecksumsReconfigurationIgTest.kt Verifies the integration scenario.
buildSrc/​src/​main/​kotlin/​uber-jar-module.gradle.kts Removes redundant destination configuration.
buildSrc/​src/​main/​kotlin/​io/​spine/​gradle/​publish/​SpinePublishing.kt Fixes destination resolution.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

The review asked for coverage of two initialized extensions with different
destination sets. Before the fix, when the root extension configured a module
that had opened its own `spinePublishing` with other `destinations`, the
unqualified `destinations` returned the root's set, without any error.

The new test calls `publishTo()` for such a subproject through the root
extension and expects the subproject's set. Without the fix it fails, getting
`[root-repo]` instead of `sub-repo`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alexander-yevsyukov
alexander-yevsyukov merged commit 8283357 into master Sep 24, 2026
2 checks passed
@alexander-yevsyukov
alexander-yevsyukov deleted the claude/beautiful-lichterman-e23928 branch September 24, 2026 17:13
@github-project-automation github-project-automation Bot moved this from In Review to ✅ Done in v2.0 Sep 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants