Skip to content

build: add sk.ainet.multiplatform and sk.ainet.npm-pins convention plugins - #900

Open
michalharakal wants to merge 2 commits into
developfrom
feature/804-multiplatform-convention-plugin
Open

build: add sk.ainet.multiplatform and sk.ainet.npm-pins convention plugins#900
michalharakal wants to merge 2 commits into
developfrom
feature/804-multiplatform-convention-plugin

Conversation

@michalharakal

@michalharakal michalharakal commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

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

…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
michalharakal force-pushed the feature/804-multiplatform-convention-plugin branch from 545df19 to 3db891d Compare July 29, 2026 13:29
@github-actions

Copy link
Copy Markdown

📖 Documentation Preview

The documentation has been built successfully for this PR.

Generated Files:

  • Operator documentation: docs/modules/operators/_generated_/
  • JSON schema output: operators.json

Artifacts:

  • Download the documentation-preview-900 artifact to view the complete documentation locally.

This comment will be updated automatically when the PR is updated.

1 similar comment
@github-actions

Copy link
Copy Markdown

📖 Documentation Preview

The documentation has been built successfully for this PR.

Generated Files:

  • Operator documentation: docs/modules/operators/_generated_/
  • JSON schema output: operators.json

Artifacts:

  • Download the documentation-preview-900 artifact to view the complete documentation locally.

This comment will be updated automatically when the PR is updated.

@michalharakal
michalharakal requested a review from MacOS July 29, 2026 17:21
@michalharakal michalharakal changed the title build: add sk.ainet.multiplatform and sk.ainet.npm-pins convention pl… build: add sk.ainet.multiplatform and sk.ainet.npm-pins convention plugins Jul 29, 2026
@michalharakal
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
@github-actions

Copy link
Copy Markdown

📖 Documentation Preview

The documentation has been built successfully for this PR.

Generated Files:

  • Operator documentation: docs/modules/operators/_generated_/
  • JSON schema output: operators.json

Artifacts:

  • Download the documentation-preview-900 artifact to view the complete documentation locally.

This comment will be updated automatically when the PR is updated.

@michalharakal
michalharakal marked this pull request as ready for review July 30, 2026 10:24
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.

[Feature]: Pin all NPM packages with their commit hash

1 participant