build: add sk.ainet.multiplatform and sk.ainet.npm-pins convention plugins - #900
Open
michalharakal wants to merge 2 commits into
Open
build: add sk.ainet.multiplatform and sk.ainet.npm-pins convention plugins#900michalharakal wants to merge 2 commits into
michalharakal wants to merge 2 commits into
Conversation
…ugins First slice of #804. Two convention plugins land in build-logic/convention, and three representative modules migrate to prove the shapes. sk.ainet.multiplatform absorbs the Kotlin Multiplatform boilerplate that every library module copies today: the target list, the android { } body, explicitApi(), kotlin-test in commonTest, and the Karma hardening that was duplicated as 23 byte-identical karma.config.d files. The single shared copy now lives at gradle/karma.config.d/ and is wired in via KotlinKarma.useConfigDirectory. Which platforms a module builds comes from the skainet.targets Gradle property rather than the skainet { } DSL. Targets must exist before the module's own kotlin { } block runs -- the source-set convention accessors create source sets on access, and KGP then refuses to attach a compilation to one that already exists ("The compilation 'main' cannot be created after the source set 'jvmMain'"). A DSL block is evaluated too late for that; Gradle properties are readable while the plugin is applied. Everything non-structural stays in skainet { }. sk.ainet.npm-pins closes the Gradle/JS half of #847 and supersedes #894. PR #894 tried to bump ws 8.20.1 -> 8.21.1 (GHSA-96hv-2xvq-fx4p) by editing kotlin-js-store/wasm/yarn.lock, a generated file; kotlinWasmUpgradeYarnLock put it straight back, leaving a zero-line diff. Declaring npm("ws", ...) in a source set would not have worked either, since ws is transitive. The pin is now one npm-ws line in libs.versions.toml, turned into a Yarn resolutions entry in both the JS and Wasm root package.json files; verifyNpmPins reads the committed lockfiles back and fails on drift. Verified: ./gradlew :module:tasks --all is byte-identical before and after for all three pilots; apiCheck passes with no new dumps; publishToMavenLocal plus validate-published-poms.sh reports all 1131 POMs unchanged; both lockfiles regenerate onto ws 8.21.1; verifyNpmPins passes and fails as expected when a lockfile is tampered with; jsTest/wasmJsTest still receive the Karma timeouts; ./gradlew build is green and reuses the config cache. Progresses #804 Closes #847 Supersedes #894
michalharakal
force-pushed
the
feature/804-multiplatform-convention-plugin
branch
from
July 29, 2026 13:29
545df19 to
3db891d
Compare
|
📖 Documentation Preview The documentation has been built successfully for this PR. Generated Files:
Artifacts:
This comment will be updated automatically when the PR is updated. |
1 similar comment
|
📖 Documentation Preview The documentation has been built successfully for this PR. Generated Files:
Artifacts:
This comment will be updated automatically when the PR is updated. |
michalharakal
marked this pull request as draft
July 30, 2026 08:24
Follow-up to 3db891d. Pins were discovered by scanning `[versions]` for `npm-*` aliases and mangling the alias back into a package name, which meant the declaration was implicit, the package name was derived rather than written, and names a catalog alias cannot spell (socket.io, @types/node) needed a packageNameOverrides escape hatch. Pins are now declared: skainet { npmPins { pin("ws", libs.versions.npm.ws) } } The version still lives in libs.versions.toml, so dependency-update tooling keeps seeing a bumpable number; only the package-to-version mapping moves into the build script, where it is written out literally. readPins(), toPackageName(), normalizeSeparators() and packageNameOverrides are gone. The root `skainet { }` block is a new SkainetRootExtension, deliberately sharing its name with the module-level block from sk.ainet.multiplatform: one SKaiNET namespace to look for, different types behind it because the concerns differ. findOrCreate() lets further root conventions nest into the same block instead of adding top-level extensions. Two validations, both at configuration time, both previously impossible to express: a package pinned twice is an error (a Yarn resolution is global, so the second declaration would silently win), and a range such as ^8.21.1 is an error (verifyNpmPins compares the lockfile's resolved version for equality, so a range could never verify). SkainetMultiplatformPlugin no longer applies sk.ainet.npm-pins to the root project from a web-targeted module. That cross-project mutation is rejected under isolated projects, and with pins now declared in the root script body it could also apply the plugin too late for the block to exist. It is replaced by an assertion: a module that builds js/wasmJs fails at configuration time if the root does not apply the plugin, since without it no resolutions are written and verifyNpmPins does not exist to catch the drift -- a silent security regression rather than a build error. The check reads a build-scoped marker service (NpmPinsMarker) rather than rootProject.pluginManager, so it stays legal under isolated projects; the registration is lazy and the service is never instantiated. Verified: verifyNpmPins reports 1 pin against 2 lockfiles and the config cache entry is reused; rootPackageJson and wasmRootPackageJson both emit {"ws": "8.21.1"} into their resolutions; ./gradlew tasks --all configures every project; tampering kotlin-js-store/wasm/yarn.lock fails verifyNpmPins with the file, actual, expected and the regeneration command; a duplicate pin and a ^-range pin each fail with their own message; removing the root plugin fails :skainet-pipeline with the new assertion. Progresses #804
|
📖 Documentation Preview The documentation has been built successfully for this PR. Generated Files:
Artifacts:
This comment will be updated automatically when the PR is updated. |
michalharakal
marked this pull request as ready for review
July 30, 2026 10:24
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First slice of #804. Two convention plugins land in build-logic/convention, and three representative modules migrate to prove the shapes.
sk.ainet.multiplatform absorbs the Kotlin Multiplatform boilerplate that every library module copies today: the target list, the android { } body, explicitApi(), kotlin-test in commonTest, and the Karma hardening that was duplicated as 23 byte-identical karma.config.d files. The single shared copy now lives at gradle/karma.config.d/ and is wired in via KotlinKarma.useConfigDirectory.
Which platforms a module builds comes from the skainet.targets Gradle property rather than the skainet { } DSL. Targets must exist before the module's own kotlin { } block runs -- the source-set convention accessors create source sets on access, and KGP then refuses to attach a compilation to one that already exists ("The compilation 'main' cannot be created after the source set 'jvmMain'"). A DSL block is evaluated too late for that; Gradle properties are readable while the plugin is applied. Everything non-structural stays in skainet { }.
sk.ainet.npm-pins closes the Gradle/JS half of #847 and supersedes #894. PR #894 tried to bump ws 8.20.1 -> 8.21.1 (GHSA-96hv-2xvq-fx4p) by editing kotlin-js-store/wasm/yarn.lock, a generated file; kotlinWasmUpgradeYarnLock put it straight back, leaving a zero-line diff. Declaring npm("ws", ...) in a source set would not have worked either, since ws is transitive. The pin is now one npm-ws line in libs.versions.toml, turned into a Yarn resolutions entry in both the JS and Wasm root package.json files; verifyNpmPins reads the committed lockfiles back and fails on drift.
Verified: ./gradlew :module:tasks --all is byte-identical before and after for all three pilots; apiCheck passes with no new dumps; publishToMavenLocal plus validate-published-poms.sh reports all 1131 POMs unchanged; both lockfiles regenerate onto ws 8.21.1; verifyNpmPins passes and fails as expected when a lockfile is tampered with; jsTest/wasmJsTest still receive the Karma timeouts; ./gradlew build is green and reuses the config cache.
Progresses #804
Closes #847
Supersedes #894