From 3a30fefae4b12ff6742d10e8e157853b4aa56f65 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Fri, 30 Oct 2026 18:24:59 +0000 Subject: [PATCH 01/13] Fix broken links in the docs Lychee found these links broken once the documentation site stopped inlining jQuery: - `running-builds.md` and `documentation-guidelines.md` no longer live in `.agents/`. Link `running-builds.md` and `documentation.md` in the shared `agents` repository instead. - The plugin is not published to the Gradle Plugin Portal: the build applies `java-gradle-plugin` but not `com.gradle.plugin-publish`, and the plugin marker sits in GitHub Packages and the Artifact Registry. Drop the Portal link and describe the actual destinations. - `EMBEDDING.md` became `showcase/embedding/README.md` in `embed-code-go`. - The user guide linked `../06-developers-guide/` and `../../developer/`. Neither is a page, so the theme resolved them to the site root. Link the first page of the Developer's guide. - Restore `typical_custom_option.jpg`, which was deleted rather than moved when the guides moved into `user/` and `developer/`. Co-Authored-By: Claude Opus 5.5 --- .../validation/developer/build-and-release.md | 26 ++++++++---------- .../documentation/embedded-examples.md | 2 +- .../developer/documentation/procedures.md | 6 ++-- .../validation/developer/testing-strategy.md | 2 +- .../validation/user/04-validators/_index.md | 2 +- .../04-validators/implement-a-validator.md | 2 +- .../05-custom-validation/pass-to-compiler.md | 2 +- .../user/05-custom-validation/summary.md | 2 +- .../typical_custom_option.jpg | Bin 0 -> 190656 bytes 9 files changed, 21 insertions(+), 23 deletions(-) create mode 100644 docs/content/docs/validation/user/05-custom-validation/typical_custom_option.jpg diff --git a/docs/content/docs/validation/developer/build-and-release.md b/docs/content/docs/validation/developer/build-and-release.md index 2c655e3a3c..f62fc6e4ef 100644 --- a/docs/content/docs/validation/developer/build-and-release.md +++ b/docs/content/docs/validation/developer/build-and-release.md @@ -14,7 +14,7 @@ where that page shows how a *consumer* applies the plugin, this page shows how the plugin and its dependencies are *produced*. For day-to-day commands (`./gradlew build`, `./gradlew dokka`, …) see -[`running-builds.md`][running-builds] in the `.agents/` directory; this page +[`running-builds.md`][running-builds] in the shared agent guidelines; this page focuses on the structure rather than the commands. ## The multi-project layout @@ -199,7 +199,6 @@ flowchart LR bundle[":java-bundle
shadow JAR"] runtime[":jvm-runtime
runtime library"] plugin[":gradle-plugin
io.spine.validation"] - portal["Gradle Plugin Portal"] maven["Maven repositories
(GitHub, GCAR)"] consumer["Consumer project"] @@ -207,31 +206,31 @@ flowchart LR java --> bundle bundle --> maven runtime --> maven - plugin --> portal + plugin --> maven plugin -. records coords of .-> bundle plugin -. records coords of .-> runtime - portal --> consumer maven --> consumer ``` For consumers, the important release contract is this: they apply the plugin -from the Gradle Plugin Portal, and that plugin version selects the recorded -bundle and runtime coordinates resolved from Maven repositories. +from Spine's Maven repositories, and that plugin version selects the recorded +bundle and runtime coordinates resolved from the same repositories. ## Publication destinations The publication targets are configured in the `spinePublishing { destinations }` -block above. The library artifacts go to: +block above. The library artifacts and the Gradle plugin go to: - The repository's [GitHub Packages registry][github-packages-validation] (`gitHub("validation")` resolves to `maven.pkg.github.com/SpineEventEngine/validation`). - Spine's Google Cloud Artifact Registry (`cloudArtifactRegistry`). -The Gradle plugin has an additional destination — the [Gradle Plugin -Portal][gradle-plugin-portal] — configured by the `java-gradle-plugin` and -`com.gradle.plugin-publish` plugins applied via the publishing convention. -That is the destination consumers reach when they write -`id("io.spine.validation")` in their `plugins { … }` block. +The `java-gradle-plugin` plugin publishes the Gradle plugin together with its +plugin marker artifact, `io.spine.validation:io.spine.validation.gradle.plugin`. +Gradle looks up this marker when a consumer writes `id("io.spine.validation")` +in their `plugins { … }` block. The plugin is not published to the Gradle Plugin +Portal, so consumers add Spine's repositories to `pluginManagement`, as shown in +“[Adding Validation to your build](../user/01-getting-started/adding-to-build/)”. Publication is automated by the [`publish.yml`][publish-workflow] GitHub Actions workflow, which runs on every push to `master` after PR checks have already @@ -305,6 +304,5 @@ release across every downstream tool that resolves it by name. [publish-workflow]: https://github.com/SpineEventEngine/validation/blob/master/.github/workflows/publish.yml [shadow]: https://gradleup.com/shadow/ [github-packages-validation]: https://github.com/SpineEventEngine/validation/packages -[gradle-plugin-portal]: https://plugins.gradle.org/plugin/io.spine.validation [core-jvm-compiler]: https://github.com/SpineEventEngine/core-jvm-compiler -[running-builds]: https://github.com/SpineEventEngine/validation/blob/master/.agents/running-builds.md +[running-builds]: https://github.com/SpineEventEngine/agents/blob/master/guidelines/running-builds.md diff --git a/docs/content/docs/validation/developer/documentation/embedded-examples.md b/docs/content/docs/validation/developer/documentation/embedded-examples.md index 6488317091..d2a87c0078 100644 --- a/docs/content/docs/validation/developer/documentation/embedded-examples.md +++ b/docs/content/docs/validation/developer/documentation/embedded-examples.md @@ -194,7 +194,7 @@ binaries are kept up to date. [hello-validation]: https://github.com/spine-examples/hello-validation [spine-time]: https://github.com/SpineEventEngine/time [embed-code-yml]: https://github.com/SpineEventEngine/validation/blob/master/docs/_settings/embed-code.yml -[embed-code-go-embedding]: https://github.com/SpineEventEngine/embed-code-go/blob/master/EMBEDDING.md +[embed-code-go-embedding]: https://github.com/SpineEventEngine/embed-code-go/blob/master/showcase/embedding/README.md [runtime-library]: https://github.com/SpineEventEngine/validation/blob/master/docs/content/docs/validation/developer/runtime-library.md [build-and-release]: https://github.com/SpineEventEngine/validation/blob/master/docs/content/docs/validation/developer/build-and-release.md [check-code-embedding]: https://github.com/SpineEventEngine/validation/blob/master/.github/workflows/check-code-embedding.yml diff --git a/docs/content/docs/validation/developer/documentation/procedures.md b/docs/content/docs/validation/developer/documentation/procedures.md index cb7a6f765e..ec33f37dc0 100644 --- a/docs/content/docs/validation/developer/documentation/procedures.md +++ b/docs/content/docs/validation/developer/documentation/procedures.md @@ -199,8 +199,8 @@ For the underlying mechanics, see "[Embedded examples](embedded-examples.md)". 3. Preview locally (see "Previewing the documentation locally") and click through every internal link. -4. Follow the project documentation guidelines in - [`documentation-guidelines.md`][doc-guidelines] — formatting of file +4. Follow the shared documentation guidelines in + [`documentation.md`][doc-guidelines] — formatting of file and directory names as code, reference-style external links, no widows/runts/orphans/rivers. @@ -322,7 +322,7 @@ for any build, test, or release questions. [version-gradle]: https://github.com/SpineEventEngine/validation/blob/master/version.gradle.kts [core-jvm-compiler-dep]: https://github.com/SpineEventEngine/validation/blob/master/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt [embed-code-yml]: https://github.com/SpineEventEngine/validation/blob/master/docs/_settings/embed-code.yml -[doc-guidelines]: https://github.com/SpineEventEngine/validation/blob/master/.agents/documentation-guidelines.md +[doc-guidelines]: https://github.com/SpineEventEngine/agents/blob/master/guidelines/documentation.md [theme-updates]: https://github.com/SpineEventEngine/site-commons#theme-updates [embed-code-go]: https://github.com/SpineEventEngine/embed-code-go [embed-code-releases]: https://github.com/SpineEventEngine/embed-code-go/releases diff --git a/docs/content/docs/validation/developer/testing-strategy.md b/docs/content/docs/validation/developer/testing-strategy.md index 60924e68cc..99e9b2639b 100644 --- a/docs/content/docs/validation/developer/testing-strategy.md +++ b/docs/content/docs/validation/developer/testing-strategy.md @@ -241,7 +241,7 @@ A handful of conventions keep specs across the suites consistent: behaviour. - **Inline comments are welcome in tests.** The general rule against inline comments in production code does not apply to specs — see - `.agents/documentation-guidelines.md`. Use them to explain non-obvious setup + `.agents/guidelines/documentation.md`. Use them to explain non-obvious setup in fixtures or to flag the violation shape a spec is asserting. - **One concern per spec class.** A spec named after an option covers that option; cross-cutting behaviour goes in its own spec. The diff --git a/docs/content/docs/validation/user/04-validators/_index.md b/docs/content/docs/validation/user/04-validators/_index.md index 9663a7e1d1..8c94f9fe57 100644 --- a/docs/content/docs/validation/user/04-validators/_index.md +++ b/docs/content/docs/validation/user/04-validators/_index.md @@ -108,6 +108,6 @@ their violations together. - [Implement a validator](implement-a-validator.md) - [Using `ValidatorRegistry`](validator-registry.md) - [Custom validation](../05-custom-validation/) -- [Developer's guide](../../developer/) +- [Developer's guide](../../developer/overview-and-audience.md) [auto-service]: https://github.com/google/auto/tree/main/service diff --git a/docs/content/docs/validation/user/04-validators/implement-a-validator.md b/docs/content/docs/validation/user/04-validators/implement-a-validator.md index 868f347e11..9c9f79f4f0 100644 --- a/docs/content/docs/validation/user/04-validators/implement-a-validator.md +++ b/docs/content/docs/validation/user/04-validators/implement-a-validator.md @@ -137,7 +137,7 @@ to the nested field in error (for example, to `starts_at.seconds`). - [Using `ValidatorRegistry`](validator-registry.md) - [Custom validation](../05-custom-validation/) -- [Developer's guide](../06-developers-guide/) +- [Developer's guide](../../developer/overview-and-audience.md) [TimestampValidator]: https://github.com/SpineEventEngine/validation/blob/master/jvm-runtime/src/main/kotlin/io/spine/validation/TimestampValidator.kt diff --git a/docs/content/docs/validation/user/05-custom-validation/pass-to-compiler.md b/docs/content/docs/validation/user/05-custom-validation/pass-to-compiler.md index 2d51ab1499..af9d21f680 100644 --- a/docs/content/docs/validation/user/05-custom-validation/pass-to-compiler.md +++ b/docs/content/docs/validation/user/05-custom-validation/pass-to-compiler.md @@ -72,7 +72,7 @@ in the Spine Time repository. ## What's next - [Summary](summary.md) -- [Developer's guide](../06-developers-guide/) +- [Developer's guide](../../developer/overview-and-audience.md) [time-gradle-plugin-kt]: https://github.com/SpineEventEngine/time/blob/master/gradle-plugin/src/main/kotlin/io/spine/tools/time/gradle/TimeGradlePlugin.kt [time-validation-kt]: https://github.com/SpineEventEngine/time/blob/master/gradle-plugin/src/main/kotlin/io/spine/tools/time/gradle/TimeValidation.kt diff --git a/docs/content/docs/validation/user/05-custom-validation/summary.md b/docs/content/docs/validation/user/05-custom-validation/summary.md index 2fbafdb252..010df292be 100644 --- a/docs/content/docs/validation/user/05-custom-validation/summary.md +++ b/docs/content/docs/validation/user/05-custom-validation/summary.md @@ -41,7 +41,7 @@ Spine Compiler. ## What's next -- Explore library internals: [Developer's guide](../../developer/) +- Explore library internals: [Developer's guide](../../developer/overview-and-audience.md) - See the modules that back this pipeline: [Key modules](../../developer/key-modules.md) diff --git a/docs/content/docs/validation/user/05-custom-validation/typical_custom_option.jpg b/docs/content/docs/validation/user/05-custom-validation/typical_custom_option.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5880f4e21679274b6a1779f002fa0298c5c96d15 GIT binary patch literal 190656 zcmeFYcU)6J_cs`thzdv-5Kur-T2z|Sq9Ot!BA|4Lg7gxR79b=l(wl&Qf&xkxA=0Hr zx`>F<2_*$ZIte9QO<}Lk^DEE0Z`pVE^V#1Y``Jk*Ik|T-ch1b2GiSc%%--Z43G&$x z>E#ZAn3+KoArQ!Z$UZhf2nR^9fiDP~1cd7!Gz4PJCix$-@_Zs(I zEa3QennR~(-@ldl$3y;p?2*|fO$-d+H!aPtnp`)8u>EcSr@wbN&p;r4{y`5cjV>Rv zwX;9QP5dW!fA8IO4ZQ#N`+t(aI%wm6djbL(QTd;g`Cnb;x#t$>3KpP%S^fd|aIj6n zAT8|iPx{nf+U1|L?q51I=zb7b=H_4e!7WPzkiG}fr#$|JcKH|D_5Opu<&(fNx_-XF zf1mX?{cZ6fH~-sK;P-woAB6-$EFnga%YXR?e}f6}1_C+11A%Zn{m*MIFCdV{Xb9v8 z{y(qD7eFBVPau$nq5r)0pM7%w?t{DksP6B$v%7<%zF7)^@Y+EjLf;^e0}lVV2Y&od z+I9@QbrftD0(^Nud?9X-V-ORFKg1QH3R34FXCP-GntRibC*W}YP3+*CgPon7gOdYH zoLroL6W2bjf24i?mbm|s_Ww;h|B?RjZ+3Q0@XfQ2Yu|rd|KBF|W_q{6Awis~Ag zTIVll8(cBGYGiD3&Fa=|Ya4JlT<^KLdw6^#Ky%Zq-Q+K%*uZL zBIiwEQE^FWS^3+V+PeCN#t%)+on75My?vkhKM#+Lj*U-DPT@)9`5!+Qel0F7Q#ZG^ zcWAr7??2!S{3ko$=RZ067k&gle%LuVIXJoh^25d+3T6%gPOjr;_X%FI;JzCmBzx}B ze&Ne$ud6$Fb*|J zjLjfiXb<9(`jEf_g;nbsRrUqG&CgVyyd&WW z%;2Y^fp_BdSV;iUxd<@7=9ZX*^sg;A8W-$A2pSuzLN8%+K31hGO1S2ugQ#KM)SVO| z7S{#S?{<;vpXl8+DW$@XzHMtzvKi+HYeJFn^@tR0O2*MdQ>7zTlzk@|r(|jneUdJ` zseMr4q#(gA*Hc4G;Aq5c%pIaAFu4aIs_j937&5C9_aFsck<>ki+WOC9k*C!@MA&q4 z6Z_U`NgXX+GNP+H7xP2(;8)|K9?e$HN>mt9#c1mkHo`oN-E;0tUFwEu0YTF1Gu>g9 zw}`%P53+JF`qmy~wp#e-Pc)pVVt9~w=RQkA+%O!z2Qg{d9Zo{kttPqokl}||vlwo- zQCG@F_hK+V+j7!nWKzo3sQ$`p;(T|JOW5M#|2fO- zegFT+%qo)StE+=p7z+P0o6WSHesDGiVt=~jnN=C&O~R@F%oQd9oRZcyw`M}BjK%e% z2>5sZBP}%C5&n&%>|d9Hw_vaeiBB4|1R$915#YNWewD|E6jG+RXn2T~z!w z!TMpfguPD_U!4?Zn3L%R`U3?^ zu7gd!yBXrPR?N~qDK6wh0!y7?#Vl+!38l2qZm0RG`&>9AflZRXN$Kv2ecXZh`L2)^ z&Yv0i!TXO65wl97Ob|75m7N9ay>=wK+UZs;`hGuWUfKP8w$;xPh?dUWdH?zSha-wF zvT7o%?85_LHLY3gv4f`Pu!;Uhq-5(J_?Xj!gBhv$BjA*hqhE&N+hM>%!1Q8v8fl2{ zv!y$HeHPB#jI>sUwneH?8foEx9>w{~H0r`Pzo3OXy8UBX=BIbk-M^$n4%pNsxgNB1 zX=Q{8k}C`*dp50Dwu|lS?NzI5o%ha%ThfvPOQw=JbKIiSXH7Mm?p(b2Qo zF*pCXNuAP{6!5UruFUz4{Q$taaP_fqXo(fkc5Y@>9SpeQzrdVrZ}exOlaccrKcDfA zuRh@GzsND({-eIFp|#O8QOi#D{()vY@Jf}lM3(dz6n}RX%jb>+r(aXZOqHu)S!$MS`3%H8_!yel#=*XE~4$f zn3Zs8=$iePZ~wY>?G67IYaHwlcG`n=fQtm1tmww^>v9DW%|%cKRE2K(3KMqeJ424^ z+3tgZ5`X%0oEW+I7o7K-^#VdSfjDE zAS6~pezXcbq&)62ZPk=qSZz*l{GBW=Fn=fTDFk-y6gE*^2EVT7L;FpYS=Ch+5&|MV z;^9ftHd>JQeT8LCwu$iCI2IhbTKpyN?$=enBZHA_l!$r$E^KN(-}XkL43CEV7z6h9 zMRf5G@6YMXJdehwW%oE3s^4?6-GnD=I5*iV9VX}+xrwU9wTNxMGNjp&GJ?kTS}4 zLPX;I-OMF?Zri2lKbV~|c5kIm8Zyaykb^<_$>HX!OObD7k$v{xvB7jbb-C=k7{N9A$k=fRXEF9cy?BC)o^W>QIEK5p= zQu$c=>*pZ2IK=6P^LuAKJaFpGf0tqTP(W27we>hjCIMcv8>pM)7qQ&G0R9np~!#-4dy|Dw`bVnQPnShqe5U5YuG`CbtAAv zhoXW23}v7jCLEL>!#XooZx(u;GFD$aMz)_>%(gyzt}i0Mhljbn)OD?u-RKxcH2o^- z3J_2C!E{`}dG0}?_8@#14}uKXa_QGZl^cE6tEsBF#dhu3&`7VqL)J9R)Wq&%6M@Xo zcuk1BS<%nO5AQ7FC=M7QfV(sA5J08I(n6?>G3^2j!{&w&#sxAAcB2*dp&6Hmep$QN zu%$8E?0rroVH7v?AyxbQB}b+O1;#ocK-KAysdrAM%dv*x`+_mi+8c=|ok%jAcV=ce z+`qyv3{hg@C-U>%psP}t*!#%0nw}G1;;KSEBZm^vpj`-IsRCOxd1fw48c^%f%Esej zbVDhP7Gu6HNnu&m;d;zxjhkPJNS65X&-;_+kMu++tg%s2$sTc_O<5sRo81fNU(D7* zi%@XUtr4yj-WFw=w7_M|cPuDCH( zbp!`0ESrdpSJCHSTp15~C3clM*0Ye84x|03c37I&TqIkC@$$d% z-V<{mf8?A2rW+A4?L&(mVTmA39jUO?R!IuI%f35H%_mVbBT{(Nn|)5n)Zy{5noQjn z5@i3Hs_&04iu@ia!gQG9h;Ih=Ae@Th7~hsXNUZOiax96wqNxv+$jkg;7VscFUc+#t zVL`n{Qo+F^=>_^09_`H(qJ(}-O&G>>SYSMd;zPAh3}RzYcPZ1I;U_aHu6?L$YOC{e zSQC&-+S>mi4tLZaJA{cM9N2?+naA%zD3|la!QoL2hDJ-DqTqUDxEat7l;(zNu2$`< z73j19PNkA%9-W@^xp&+Y+{c0mjp}SWeSP_OkDlH9n!YY`Db^&6PuGxShiI2WikXgN z7*f>*hQb2N&Hb}>M+Qot>i4R;$&v^6AmgWWT}4(1f*4{6T(A=73zcAXVQL#YRD8%X zNv&d~OZN4H(>|oj30`^nd043Br(p;@18lX<9wZslkiUAD zl6{fl(Fv7A`Z!U{dtk9@%XoO4I#B76GHu*m-!!U9-u#t$vcAFa&dBKwx8s+v?=kN1 zD44J=6ci^rW@)JTB~R0;nOg(N3;fL;m)FT#<99eRe-iFv1#0@JEW1vOFGm0SXx0-*1Feg(J!gh z-EXJZn@-_~Z}%W#3~p)^$*2Q{N6tMp$62z@dsq$=LCi|c;xEL58Evo z7&N7vI;s3A9_i~Ib6WlY=kpo%EeAsV#+oZtftJcF!MR4B+aeAnp{15Y^Tm{_un3eB z*Q*R(<3;<<#DlhXoAd0ty?*IVuvzoT?O(TDg)oPhH8}57c2;+OtxOWk8y+7rVUwc} z^)7mpM!T!~+wO<4OY)(lw36TQx1n;%p8D>`FKNtUYJaWi(d|)JsPg0Rqx$T#4A?iq z)kv2S2lK;A_HBg%?~9Yly~#n%_xz?(E~q@Tik~FPAedDwm#vn6_8?sjdyvFG zzO)LjND}Y4gE|$JmS2}|JtoUH;&SHjH`8>CsjZ1khocA9tbisu8$%38C1UWb^#R%q zN^Ez2452pqSJck_9T*?-cjr>b+_7}BKxrz!-K=>a#_Q;P=4*dB1F0Nrmh6pF9NRjb zp!{2G8~}1zJU$8x5k8qiPF-($h`Bh4J;+`0c`awdmx4F^7{qpGnk|Lz49`1QDuH5| z1-J%MYDw80*jS0aZft^#n0m}aQ0l>@3DJf`uaXYobB=S~644_@2n6Dw`Oz@RJfrhh zG=Nxy*4j^^IUuVFPHoQI5uE#m_%cRR9n0;Vw@-@_8Osu=$d%2w9Bk@jwtnk#&2;{Z z!*AJ;mv4oye2H2gO2F`f3m$GNl~%~i!y(`YQ0f!teVf+$Pu3H z*i3c!GG61$H)qT}hrweRH#c1qzXrx-bAL|iKX;BF4}XjkLkUso#NFuj3n=xMv&e{E ziQ9R#C0_>W+A3F-)Rk?jPj$)FqfNktLbsir;Rb*R4`(SA*C`VxE#5wivMBSG5%6dn z{*|QirTp#kpI1Hcw>eV@J?g$M_aKL{wHV@a8NOBoC5pw)<~}~jC&^ZU>Xv$$ z3ivsAU7acRM^`U*G~z~YaQKALETbo)RJjsI!f zon`NGAz9er>)N53{*&EjzSusJw0VOb0u|d$*ioR1mX=F1VwSawc*A*GgF{~rk@dJv z;OfSIIoepDWHcd}!GGWnARK_8IY!ljDzIRFC~wX;%?7JAS*cWLaQ| z;M+XXI{Lgrv1`GEJ11Z6+1=W3Mrj$j{m?fIv1m?c0z=F~fHHnHwsSYlvl>wS)eE1z ziX0qD^Aq}fXY1kSZsrZXvxAX?K9R8QJxH`yBu%nSU}p__zi!l3z{y^+3^O*MJfu^Nyl~cb8}aq>9`tCKmNdX4hxj z@2X!A9iO}Wbu06#?fl8x5M~8X4>smF)jbu2Qed^P)eTDLuRg-zRygqob;QoR!fuhK zscoR$mEGJlv-R2S1ZN1vr17VhZoQkvtT((7k)+G^26Y&oj60|S=L8bxp-B!MeFiO~ z%BrQqCml>z>ObW8h^wlUBp9!nToTT0`1AsQ$$|=9OQdWOF)1iLN*5`0?F3z$VMO&W zFF;y$`U&;vb2i=@4bn55KRn|_flm<}ysd3yny#X-Q&P4{xI^_@?G>O(I$-2M7eEin zh_@>v9lBwHt$`F*^0RiO(K^YNeS(*9 zuWO}Ze0UW;!W$I2@+VpIMmc0USvnfKa^Krj_MK*VfksZhWcSsDbIj_Hr&l#!PV~d- zxf#Mf^Si4Rr9drtA_eDB8Oag?1bT<*Vq-`s^>q2OTgR__+Mng>IJGRNvj9@Xb&RVF3TF#W@yiXQ$0>Y{u z=UI8=m#o!MLCv!d#jw|aica|ZU%k-7m=1wf8%k79jssvohDEp9kO|U&?K1KS-a7}8 zfZbUWB~AKI34fNGG19*kTMR*e0@YFj--{~rYy4*m4XazWF~+HeLWVlVTXm0QZBs6}8zT0D{Ud7&VCVV4-LD9^oEqt$_EISX zDro8Bdk~U>gNf6L2(tsj!Xw+JZb9<{n(vuqQ1=hMTO$)NPi&Nopso!#&77Gz?dpp; zTs^zdAa;V4#(Rh7ym_wdAFMuXnDfcxb6qFT<2x9hqc^#e0Ug$X9_p_?m=Ma4uH>0t zi-_m$9oiqaUXS1(sPKv}a#*qv$f_INgEVSAmzHc2?%_Nw-lTk_)i`doJ(q+9l&~EY z*hv|CW|OWL$^5XVIcd;;qtm9H-}ZIecxrrQFmyF~SS4%RC?>45_4HBIDmgsq<(rt^ z(vL$?1fd{)Kqh|HNk_O+P&|ITP%=M^lrLrF0_SYPUdY=_NGXDPe zXW9Ptx`>r0GC<;N>^BHEGq^JxeJ9JmCNLV!a zY0I{|gHy8ztKN`yTDfEH0^-CQ%FyvTb9D}>_VtOE{Z z3{hrjs}x-pB?s75p>9w^Q(Mn!#n-`CD{^&AFY&g0xql7e(SjVu?;pN&I``L*z#H|X zgc(BD2B4%6V@1OCB_n7V6uxT8JjhUT+?cKtX*_4zGTltyL94BjSdHf!A0vLqGLR~2(u@p5c$_}mQ=0T)}X|nnCEFH}YpPuz` zCWAhW@fvkZ3_(aA+)Z>`#Q5Ow9NjS9U~@6Ryc^9;xm}WZ*N+$69(5X=e8E>qTah$u zN;{r@u~Fc)o!#NAj$`*8@|R*jGzi~Es{-c9eteiX0$Ww9IB`R8X|R0CXN9bybjuX^ z#Yg_k-1M~mcAnoPy!HjzQEHfv3%Lhjh;q~3F-u@>GLM~kC-xxj`TYH5&MT}7l&a)V zRJP{L-=e$Cv(Nb5W|E>aV9)}jV4p5x`M{n-5N9zXiS2F}{Y0Gt|1_E?Q~ zg>~>BbViOiQeu-^kCSWPcwyP+=IlM~e-`h|gb+N_PvPOR3a6rb$(l(o4?q|!uzM3& z-!Ml|V)UEnZk%|lA0;2u15c_5y=Z8CY=^xWGnu*eWkOo2y{48Yc)`HE;&|r0(DW;| zE;C;*TEUA6wOVUiWNZSgZX8FKV12_K+L4LI38+W1%LwBg1|xmmF5yd3Pju8rBidK>80lK}CKW%c`guHcFkh4myrSE&CScWlYf>csSZ9TAt$HM=5YYb1DYaYlv|Dh= zC1@|K}n!6{nj-g znF)Oi$-&!KVN31xKHb<@8e=|(yIUzIw68*)q`1zwp@LcJ0RA>Uu4|8;m zAq#{CtL9QBu0HF)`e^>*FaO-;TPbs7v*So+pT*k4+0-?Kd8jbMp4u%_qtNLP17c^# zg@9bkMDdZjMq+Pi-7bV=w0k4`XzKe`4kGjr;~XV3_7O_bw;?P!o zqMeiNLtF!y-%=DqXv5BA)@{c42BV^cnVQ{gGQXm$8&oVs2xV0C2h-zG0>soqC|ycV zc2ujlhjf0)WUuVwBQYYk(4~4Nx*7dd-)(*tf8<_ZoIc;N(5_!>cjHFjSib3vmv=?> zE1js3f0Lki@W1mZ#D6zlD1P%q6sChdgOaA=J|e^&(cf{kD5&D#vFz?Vi9~kZ%L2Q< z8gH=Ap8A#A_B#fIoJi(9;KunL#V|XymFIz~FH}gbws(Hx$_hg@HpXgX$@%$8uTHql z!mn;8&0X4gph=+@#Q!?`NE-?Mf-s?}ZheP?*}vsRU2mSL&dXp+Y1;0aCV9WZXODrl znzYgk=1cY@8uNJ%vNkvV``T!plf`%`L!o(Xp|vHy_}Cqnta3Z2X}G;nsQZe%Wm(X@ z3zkLq1X3oKY<$RWk~(V3Z$@k`846n-`8Vf3+uq23t%s3?f)TO1@T1Bw1bi}mp5|Zk zZ3ilmDg<=VHK)6?Z8WtVLFlTSL8x=Io1VS#(4e*C6Z{+(CzpKE25<_5^KkxcN-Um6 zvWgF_yq1D}vO}H6v0W<7XY>B^YR1nM7QbCqDl`=4byv1!1Nm82`0Pueei6C=)rRH( z+@)sEdvrll(P!zN6YYYt-ZstrQ@=JV8MD@3CLJT+9OKW?jXA#W>TuwaHHb+~Wsv&T z5R?=U(hx%t6Lon?hnnX-CmY}G+g$gPPeA5=pHH(5(fFN4KW&F+b;BsWgBxqV z=@$$lN>LU#z(?_wA;P~jVa^1bccQ7WI>$u0Ky#Wgwpjpz$Y_fMR*5pq4I874STlLY znrK)FnNyi)J?lY|toOQ9>!;bf?DOGb6hbg8ThOiXLFo~q%^-{oM$97A4z2Ez%taZ; zC=0L2ydI>`#2|W`GDCkHo3+~&9$EETX&_iCxuRP>{>0qPxnqRg2m6$7TTkC%Fg6Q>w@kPm+CETAr{Fx{At&UJuh(^h1S>)Eo-bDh+oC=|; z*3w=)iKmU#G`yF-91T~6og6>jsPgqjSPujPt~288e2avdp+4+Ulr^Y=i;-eIEENj6 zWuB+r57conDAV%F#@M=+idW;J+IiVtVSbuvKKI&h*QMhPHChMg14*bcraH>E*@~jp z)0h*~in$AKeQ-4*GX3(qceqEKHXrA7zO^>*84AGzawlY@OzosdVGkorG&$*==U`U;fj-N(?wU3W1aDoKg^4!=9s z>vH^jC&_%E?`Xh9nf|L8q#<$EH$qJ;Ll9I>{?+qvLC>eiO%Aa*Xi%i}axaVZT~{G6 zA`qc6bK_*sP{z;JM^7p~wECxW1?-k~VxCCwul~hqgTPI{8ePZPM{|yYit8AJckj-L zwT(f8m4wIDrR;n02U{#6l!rJo43JCblOKP&TrF8+zKDAay7pe>*X}_c*2z4aJNhl9 zoQ9Yhyl1{YUNtjCe&ock`80o)F2mtE(bZ!xYR!4N9Naqd1)JkzjhR(d?VwS`;~!7T zCGg^CQvqc@~U!pw}tu z&co-mEgPT%5;PO!^!G7Tf#?8o{^L9?Ac~0LBY&d-*L%VY8ukLUiHu3*pm&2@n>BVNMOCJz^Gv-Te|)V-ur$n|jbL(Rmc(2?cC+ zot1{G7HUTk+pko&?1%AT3XevDc11NOy|lx+uM2Be5vmyAE{Ss!xJ&uPI*>t>nUW-V zB%r(sP$#Lo4}-CS$l6W{kZ+U4>%7&~h>vW0k-i-90&$FQV$N-7s`SZB-JKivY|`K~ zrN6uR2dse!*g+OQ;|PV75m*3t5Z#+ffT+#}ZNHK3_p?g@ria)`xe+tR?OuMCcNsoA z^Ti>DmK;j+213a)cn>h}C#7J*wMQTPdoiivSo*1Yx@e_dDmY9Ug&!>a~4> zGq1Bw*sz}?oI29KgGkQb4+Xtp{P-c70w6@ji6T=?C^1cx?C#Jx`I_$Q`U*6#@0u+tGBr zhF8q2$Dc%GCv!1vjmL0ev$(q2>dQCNe%5a7MrnIrA7Pp)*C$2}wR zwI+Jg(YC9;!|^b3;UP?(#pfkM`Iz5;SyQ9v&?57#vpd4IIwK{?q8TJQ^YY5A$9;BL zk={J4KH8ES^36-T&g+WgGd4eOi~O#pn$z^YWwZ9Pm4Q-ev<5oN$=iAxTB9~zsBs7tdp4c2d~=M&mFOz(*Js&^7{er7}MSX{RMugZV0c|onP;f85}@1 z7XsY-I{oz0>1~KaM3}0pj!+3qH2>!A5vr7HbGzs%}*JB9W3YH(v-J6rEEQLW5Dbv%9q19zW{anD}qm!zYh3Trk3m4P?R$S0#}~yubb9O^wN7d4r@DGohD8(+BGE3O2GM$=GQ6A zF7Akf#wr8Q%Pz?`)7E;>a5$#pSo?mjIeDtoM#qK>!w~Q)KsM8US&Zvyk)VR+rCvf# z&Vmzamk!&c_m{Z4XRM|hWL+Qx|tKQbRlSUaonMOu_f=rV3U=hd(^ zK#N<`XxlfY+-gyb_Y3)WPEzMdJj8OVDJktKj-7F91m{hN$**l(O{Oa_tVVR<^HM(Z ztV*(sB+BF)L$Au<%`(1zyTK2%0q;zE2NI?#E9(nqPA=YUu^gx6*{7@JFYZBT?cnSS zWDUX__q`(eLFZ>PcShm-^}?g-BR)aj)C&}gvKswjv_vzWnCpxEzK7Qes#>9aU?!q@ z(D-)tkyajRf+XEeU1@R0Nyz%!hWqeNq%7;isYXq)@h>$UCwuSd+&GY4Bmk;hTrIRS zAKyicV;1|FgKp0wjAMXg1xl+4_iY<41myHNr}uTI86cq!X6Zv#qaJmQIkT^2p=afc zlAe#97onOb!ra=RBqN3-on#K2ZjYUZiaVh~Ky~|wp)sEb7j;Ia-p)ek9yE!`qY=DB z2TPxuPvsFlcgNHFPP*KK4Nr7pCndWG{H?HgDWP7tKx-s*pSk4XP{G^`GT&Y+&oGzg zR^oZV^Oco9WZh>Kd)g>1@dqH^sc?qSFCu>u?oJto_)`1hD30XNMH+w}RO}0Gpwsm` zP)1XuLRhQ!tZFS@7 zE5>#ba|@a=^6``VWtI3ARBtxTIy=!l7>8qNVa&pIRn~Am zQb?_CJQlBeuyEQIKW*ZtAL|tiimAYj^clT{m$lhfJwD(?aeiQg0vu3C?Ht0x$nYmD zEkiO~7&zD`%{x-1?jUm*X>Lj9nqLa_6B=?m@A)&v)`@H*S@`+<%oB+%nG|qkOpS%r zi58Pv_~Y`0SSMjos0Uy08&8gb4w}UYQ`z41&ev9JnzRvRWX{|ilAIB-4u4g~u?_7C z&yLxF$7I^EX~=YxQ1t5-zxE?%JHP!MqS%I%^ciI)VO* zKEX_dxmB9d6mirWxg<`2`erF(5&&%)8`e~9bIbxwhpA?%owVsJBYWS~C#)#_%-h$O zF4q4UoW}tQ`LPT$4`Qwi8_FDrC2~>pqx?t}0i?FTRjRWWt>yB;fv~$da}G%-^@nBc zo(YOPlPCx0!iit2wZ#0Wb}m4UR2KX^2*-x}3X{Rl43rNAVj{8cMjTB;%bjM#Pqe5F z=+t$5m3(+c7{d13l@diWr=+em{sYgg216+g?5X>}nmD~#f{nwvXAzalArii!jV!6g z^o331ck6fg=(_dM{OjcV!*73AwxY1bC~shmhy&#IASvkmNS!`=R^B)Cx6nPvzEzaj z$=4cR>q|hpyQjQVH1TGW*GVA*FExWhUwYn1hRl1<|3Ly~bVZa3aF*Id#;kD=UnAR( z0gb^Wb1QQwmRnQqwOWURuAB}=&vc*MoL2}C$EN6Of#_59Mh9#%mAuipAq7CYopmYv zDZy)|J#)vu*G-m;1zY3Bp_a?!`5Q5lCpzovKZ>1+Q&Tq7GHQA#%=UYYZpi8aVe`9K z{1(k<6y}Z<0$L1*L0jm&mp$!K7}?3@*88!faM40tn+$_8$Dsz#96_1Slj$!)uT`x` zLbu1@pBlU1kMsBdIz0sa)joA~UaXgJ)PbQqQ8K~j6h(Z$jC=kQk#tAQrs`WD~3+g@*T-!~Li#t>Zov~d{XzuracLKjCd6y`I2N9>g zCri9Hq|pO;`-V55Fp}L8KC&;a_0sbRvxZTPW1C&YN*<_eFW-uQ^AP2;{i5^P><{(3 z@Or0xWK}$jv#3Fzs$Tm<9eH|1%JiAuky96@Hn@ z{d0aon62W`l1Y8IONCLWkIMVz1VhibPxr6Rg#RizeJ5{@d?IgZ{7OKk+p))bar44# z9vs6-hg@nmcJy?v)kUmXBG(F1H%yc`4gb{MIS>>cos{Lmx{y|&^uhRu@AOJbiQb=^ zTx*5HSv6L+-{(}AzrfgWVHOkHTF3Cz=BV=6%749cx|5rnUs z>n1N7md?$ZdHD8PJt4?9Y(6@o(@;f*C_Uv%oJVv{q18}|Ah{j6NJoeu!@Dre`&c7AHR5VH{DiAZeM0S2m2u&p1I zHeABVkvs2)*Zp5dgjEfX7`-1M=aF8AB;WfW_>6NEf5T@D6Ac$=mj_0A^Dcl(RticS z8oj;2>%(*R4WVtPZE}1SKP;ZS+`~WQE$WfZuLytnJK~QfcNN|zTxDklADKC$W{&8t z?vt(UV{JxdGAOvglx{@rPkPzLz=wi&FR||*!4Y91p z#=u|Gw*hyiQTaeou~- zvp z#hYhkK?1K=Y zs{Pa+v#i|noAiyfAE zyi{?zc#VegqX@mv`~42KeSC6urhvC^7TCsM0HJKdd*b!dC2^Xui>sDHm2i2k^a+b;VPqA z_kZ~VQuiEFrYsF1-b7d7N4G-WMMz;Tjkf+AK^)6pE%j}`700rZ%J(+!P(FU837McnRdtxM3z zG7+($Maw_rK{Rtu)I%IUZWn^xUAa>!yI`6a;Zt@mLao8g#?s~d*P|Sp)Rlyg+=?ap z9_+Mw?(?eG{qS>QHjXKUipJ!J>J2KY+{LI^x<7iPf_{?KPY}X+`tAEV?J$OxM!Mdl z3ND>9|0&*+#Cys}QGDKLx)I%#dTnVH zH;e>`8n_orQi2|Dpz&SSv2c3_%uhKlJ%h#<{7v~HTakySXl(ru?Z$dXD?GB3O$ z1N4e^1G>K#b`$R9%;sEh6os9JV>M)s0!mlMn1Kz>$4OxX?n~XaHzLuAK`92)I$tA-B*mUJ_Qrce;^SP&bO1-qr z-dS5)KUJM9Xne#Oa5ZttLKvdQ6;#AA-ZaSFgBGQpf5Ffyw_j$TZ7}vLNwJU&*X#_* z)0X%hOPgT6ts9zx(cnNXb-*NLzPLDai1irHAu)3Fd+pJARO!O z_-{P(lg?zz62AQ9;>~lB5U{U{OMN76RCD9iBc4~e9QuFGV4Iyz3{yWN|DlesOsAv(djH@RN&)zxS3!xlTv znUvsXdgN)qmWz$$QeN(}qge9koS6DTFIlznSp&a?r)j^d%9~Ozw#4<*gxRo$H(PMk zBYw`CtXm)oZ};O)LgCCyHVb=iyxUIP=sSPeoXM&y)ipJDf^}k#2(uN}dySyjTHO*eCcQVPx^QT%vXAVxLQEBH{h=tuFpMwW~GW}o?NsDGd0=DhsJI#*tF zcjD~DUFgrw8tvCkZfDM``vwSBXKTj%6&<(C!q%5Wb5xsM#DCq{Am5Ti` zvGpd~KVD|tRvg(M`fRc3qk?z=f5ZQ175~@sIR8q=$LQF^vA)TOZRbCN9|(rWg(rj! z+Z)vk&qS^ymh0Qzy{XgkR4+QhOYVZk@11$$3|b8H{WdPvj}F3~XJhm{Zr&(YEe}ZZ z5Llx>+jp5CWrORoJrG>cG{HJ5rrsuIo?GKwnw&R(;#xv9EWzvqGfZwA;|7h=#&r_d z&8vWQcmuMl2Si-FeX_Y(=%c9j*f$q(vMEt7@7&?92&~(>ep4i1$y_Q7tEK)$(WPQQ zz$$cCwO_U04k{n=6F7Buu{T*SO-0zk(dZ874PXPdsJXOvz)w(>0xH}cn8}SxV{)+{ z^`R84xZpO+?&|!tgkXOQ-;$Xq)_lJBbeC$^FSO$ z&pNA7psSU1Wdi79Il?+RX}`#lqw*G4rMh|*S2-ht7RGksUARU+ty&(Qlut@8g7A-T z>}O5q*Knt>IJ{-*xgYDx0};1@(89ttF#g1MgOtc->tQ%EHfAepjqt6_Yb_rb8_nl|gRVGe$IJcwWT+ou;c$)k_ootMR>699Rwu3VXT zFi3-cdV}TzWK(D|F)V3n$O}@`J0!bOMZ}8Axk&5IbtSCI+GTuJQ~Y@7*<-ys2CR41 zVymv8Ild2!t$K-}7(ww`M6q5;CK$d3o-I)^wWNgPW#7p28sZ_vzr&?zgphUP`_gJ%^sgpegWFI3c9c&+D56SI;ft~%VC(cLUd z>Nflk&ABZCOcNu;XfW_^moXiJXmJ!9vz>7Q)}2U*n-GnUBniKl82c5+aH{BMmeUMl z?g$#C+%q&$P~RJSpEna5H zXl|;}&bk%VrOR7J^L-k=>nw8OLBT`*bwArPym`thr|ibf^k-7<46g+|bG%ol&&kZFUxSn2i5j>m z?mjKQfI!myMtk0FT=>e>XRO7Dwpw@1KHm_L-Da&~lnCA1GZf8j&W|3;69bQadIIcg z&O0|zBTHI`|k!$701kYoQI0br8 zBx(lD3Xr5u_pvwtELH647Q=!vaLdo3%f7yD81HO5ByYw^efRC0+O*@99#xg1SY?}`FAH~SAmWJdpJKRxjD(soIoGU8Pd-$+ynXrVpjm5oHubVktX z=wa9a6i+egFg0|wuN*)DLtQLEs(xV=(x96qHCoU(sra_6!A<_|Fq zeQT$S(39b*t5)>;NLW|%J}flKjoKSU=0D)ACD!b;p6?ctl@r}cb-u(&+Z9b!=ahMM z;_A$Sv^tbrC8om@<9!6@0fr$E-$)+?LWwKbhYeeaJtIX_p`Lj!hI->=?(y_b)66vZ z=fTgmx{t3WLYn(GZ*p(f8-gw^G}sC0=n+j^;}gt#vDGljz;$X$TO~?wq&2v7C1?D# zYHr~YZC9)@WTc^7-opHRs_)^-x{z=E%ITjCo+OL+!ohh;e2r<~jzepE!)suxRMJLl zC>Yu9Lt@JAl0_@Xeh)Tod_Oi`@8gjtC1-3~S={u&MQ%$z%R(mYQ1w|O_x%@PcU#>+ zzM>~WFuE49_I#4~&DR4Z%nd*#OZo5t*$EtB`?8gGn0yXU%OZO(HwJPvr|oC<#i zgXsEDtXKz+(*+Z;DJ=f~gSR)2hWd~HNA+ou(2y;}R7hp3Y?Wn-Y)R8l3YUE%~nqmZmBMP-rc- zD?@zQf!-!+Ik{?jp6L$WyLV2-X$V<25P-oNjC?Y^FQnTvnkDba{P;+TXmR(?Oo;X%<~PnWFw8mnzrv3Y|=5}Q;RWn zJK%?sXjt{|<75_tcN-@KRj>hO266{)hKe$yuUAjzBm?6!9NIE4h&kCZrc=E}!%P%IKJknbxf$7=|O{))VE>H?3a zpAw=|%)fp%s@ub0e0Akn>%=O{YH8Gf7o}{U*nP-OG(T8Nq)J!bXnF|rp&QouvVGiQ z2+;LGd5<^6zyITq5o7A^#ett;!_z}}th305C8g4`q^gjCRILN7s#%}dg+*yQlStj1 z3-w6}=hO;eOWYLx9}Y4dsOJh4p^aXZT@1a%9sHBo&RODLpL4bpV+vi+h}Q4bJp)Ts zK?Z+Cv`<#GmgnjQ>B=lXNL#SklZ8_rdTa$i-#(3$gbvQk!#bjsx?CFCM}Fm35VtQY zSy!oaX?(sV?iDOK+w6K4-@DXM>OA@3Qt;jw{2Fc96`$>kZLphfymR=m#?_=FClobB zf@8E!QyvT==P7+HL(uN+X@D2B##V^KYe!%&vPoU9>%ZyPTpIJqzv6Da+@rMSj@C(h z7?P%Ld^Eny@zEij`u_+Q+RgD)RQ+lZ4^_5e!jptFyZ$UDg`gzeB8q#Cuc!P{2H1&h z1LbMePo8g4eJpx)H4yihCeoW(;-zgbB7$)BC3Su!&?^x#2Cyj}HygUcx%lQdK8};Y zy_WJdBC+e-vQA{Fd*=1LwD|F-^ppdGrL6jtfdz{!>y8H`ktfkWS*`>2oCsv|9CuXk z4B<<;>mfI6E{(I|Y)>pwb~o{8YT;O?DkSeqm~m)sORm~mbN}~fVRNk5%scokyA~1r z&*AfZoy?NL|5Ens!HfoAISL*=3zPh+OzAbtwk6DANaj zg#AC#Dnj zj~JfXpa6e~kO4Ho=`!vvu&1(x{R)U$a)J(jm${mssy5IWj=U%HFUgBBW(H4gFTYaL z@-(n23uS&`Bv3EscN)GKLwOaxGRj0uW4hoq&tDXu1#pQ{o)X$fJnB~M2~!gT{UyrM zUgRqh!iYKEUZs#awuD@UMsqT-00+(x>}$W=P_cpm5(DC0M*ci-Q`H zoNQ4NHW8$Kc>l)XOHRz(4p9FPSpX{Ed~pOpFE!VNoGSR{VE-ewz+sRdQhtdzowr)| za(RbAmWSDcbe~4@Xh3Pf9NgS6vtT)(=o0R)NC4jwdJ6u7!>DO!1)8=E55pY?_}=FC z$;)4U{&W%_bmsELZWV#g6Ft=q{vd!N6`*S{bDCF5z$!2mnT^Y9#AlD=b7+? zb}8sQ2vqoZhRMVO^zlY=c!iyJWN5hmE=wFWy48`nzr;!EG5@{ygN!sjk}iO813!Y} ztQb+F_r_u!E`~E8|yt1xMTJ z&1U5RC(>TQGs;yO0PfcLI~KV~kP2@%6nwrJNaMjjdn|4?K37qVaUEaCH9geU^XpT# z`|K5c{}i3?J6_7kY!fMjbyDu3YDHJbJOrU*8%`X2dU}wV!F^oBN1Q$bp@2q7rwx9} zTdk?S5`ACX&(qdjw-ly|NW^2L8(914Ot)^?x*poQ!#G;9Q5YK zTTwc!51C4k6K=*2qS&C=1}oU4TQ=HiRTi-@d$qk$-N8$)FY%|dZXi=D!HFDer*Yxf zX6s|53xWm!OiUw$VzcavbQ;T@xBY9QPy)(+9pXsg~h-vi~pE>4~B94deU5 zZj4QQ7gO~)k`u73aiOv>g9b{JK15w#B8A~=-L9GR?tW2l+aZ+6@& zYwi}O5{cPyYWU#Y-M5a|ksh~i0}tOG(04=KflHa1aA2Ls9b3xn=#6@eG~+8A`I<`f z@r{xA3ylw-(`FsezQcWwn7+&{>Ka}LThECuipC*4ho*jRh_ zi*#oO$>zQs87k>3cSlQPFT<|7t&F$}o~p$jnA2B3pLqG|u9Q!LxK=TSJq6mg$a3Nx zj7TzEjKRT}pc)rSjcDu6F4~)Ik;|mlT=-1R^sn8?yuvSkSzL;rg+#g6Aba+}JTZgf zJG9kx){;p|jd^+c&dm zT`-Za6v z+}*e-5UR|tUA*z_bohMQ@h`X|d$ye>8eJbV7}@e=^EGVCJ>tE+P(UOwpO6OH4f^a? zL%ZrP9L*k-IQow!byxKBZyl<8l1(kETMVL-ldr!TXNAk2Nh|ywPW&c5|5s$ij%Oqs zrR``}0s6uj#+QjuTKw&7zqwsc2DR-2ZVk$OVWvIQJjXu8&~QBWwNm7&t6h@_sx*ES+q{y(T>~esjWU$UByd{7iXrb7>y%phy7%K;8*3*7C%{f~Jq&Fp-~BlzbBOty`I@7fSq8;9zWUfO zeJyQ|my=18>_P$T8HrI_qdZDuu*oy0)lPlfaRViBnVB|3M;eh+o%{S zErEYJz!Xh?T@-8dYQm#`?y`53-HrO3164|*DYnX9dlWDGr~D=`HCK3SAMP5s^0#~o z$e4G{mn$@4XLPP5nk$dtbQl4s^*<bV#7%H>=nIO_^U!a$E||E2&AMSDL(;f`?R==D)9wEm`O zFDsYfm3@?m;EgunbqC999He}PmS`~u`a?pECP?RFQ8EZ00uWncU}AjK-5zEOilw!5 zeN1s>qSqyliJl8Bh{)A{4}YI`VWdS-@zD$f>V{22f&z16DBF(XSW^)()3u&wosBLc z@zqohO@{0&tEr)VwhG$*G;-4gxgR?fPe+zqRd^#Jg5L*Jq#59RL*irwX#{U#%yF=2 zKsE&tpC)D4Cf;K%lly+OG~Ips(D|nPuH5A;{jD>XVtTaC_g{N>?qE(*%AXA3HV58O z9EiZNpycf^!&x$jQ(cyIkbW`n3d)G9N67i9D#54M8oclj2;T@<{D6}23g$(`J`J{UVqo!DNK?`4=9E7}=hGdZ_#Ild+uvsY zL%%g6d@=IFhn`2PhA-HCLa`YTKa3z75OMD`KXF}5H#M1Wx~nuxNedmc?@kN;ugEG9 zUlwFyvs2<`<^#pw?dt{`K-R`{#&;8IDSVlh`LdO^`HU%4XPC#viB>1h_Mtb_-pu02 z`yHSfTy~A7%{&`>2K(Z!>^#pq6a1n2?LhpVCrY;XfkveRY(^Q2?4|R+B8NR0y zMU##oc5;7EBW|zv6zm!2+lJWcdP$$j1Dz8mEuRJJXU4u3uaWl~=d>nFEqNR9{lWu+ z+IsBFq6hnJ6I9G#Y4Vi1E@P7UO;xd<_GY3fE4|+KujgIv;eG8CHwG|c5Cwz`#D-8p zGLd~z$A+snEZiD_oe%FK#uCI^wM64VM&uqHqM~*tom0(P94o5hNv=5MYUmU=VOf$nu)-_7b z+<${*NcCr9J8O1fl)lSTiiy-*q1cyykdL|MaQhy z`S{)D#;2i@U+RCLWC%<=9iN+&iv#97MJWPsm#-74 z>s)I=Dc_qzV-P!#68x?Ij4XJUGtRSfxbG$W2er>7|eyB~SdMA66Gv|l$ zN{ttCkJ(PJy;KMcrnb-i{=rt5ik4E*nu2z|4FAeCW;GC)=Vvh!Rhh!EfNS1XIj7~W%+9XrxcR^8k z7PZqEgO`~!HLfP#4gIaBt81zTPi}aN(^nbfgyk@4R_Ppn&j)sD4l?SN@ACa?;xjKN zep85V)hmV&oz3BK21onRe=e68IeuNal63W+bxhs$M!6H}-=jW?{(dc-$;J;5*yj;< zQ1v!^S#fTVAkQB#vbO^=(xxV0WdT!kx2#gJ2yJ|*Y|rKgwL0gBH%$keNqt=^n(WmX zt;X{^3FN*lC&11*0|hhnk{V7{@GaOsru|&U8^#*{q}trVnd&_beCGXFqp88A2rWm( zvln;YCzLJ`=(8wDy{hjXpkQfH5`F+fA+cTLTp4Hcnio&asmnj#dMx)(vdrB}I~A{V zsPA$8L|zSZ8vO(K=5bt&F*kbDm55tx_%u}-OSe?5Ai$^gUi6>Qm_EdF{VVd!vZwFS z)+?%IyWt;(Rx)`9`Mbd-`Vcw>DC~YhP0!QG1pa0w;;#NTr`yL`o0{zc`HS1GXD!&N zKTvbqk~(2WTwMDwHt`#;71leL9JwwsPy1 zCZ(5>gC+0XSAQWl=A?g`+kRzfw-l@4oar+yfe4o1iJT^#6V&S<=!FW$4f>c*)9>O8 zx0mbbp0ae%n0aRui>!m^S)06%s-xXO!Jr>PNiMkDb9_6912Pk#9&}yTkS1kk*28M{ zGv=VHf^Vt+&~@xQHZi@AruDwE{J#&q-v6QUVn7e1qkShJ1RC%+_6fv^ydWK%8&CJ& z?gnB)KxX<#-$V{YdZm6qQq2XeYcOD>x@%NDXI1>j+-Z-~b=o)RdUf4we zTMAv|necTW9dK6cf)*L92RV5e-O=8mf-su`7ptB>t|zanUkcNb`mV!t$c#tj9(`x9 zf&Uq|86vZbJ85h?q&WUP1&%vV#rMtVA#FN5J$s}}^H=~>z*hn;RF z7%dphVC0am*WriRVZ9Z+9XK-%Y~?p7A+yIpW<%Hv1Ym0`ze^$}={?+rC?+G_`@J#%gC0CT>5|go} zo@Gd%mnc`i+xXXna5GlVubkpd??SO!_QA$~UWZ9XT4cvEiqJu;5-qka}mh=dB68$*{%hy>1yQA%kHOcG?58>Y#XL^Xg55O2pZ5m z>};V}4`+yB+=ZPP;6{AR@69#e?V#C7Q#Cc&-!7b;tRA6T?R&lW=$Dp*g}^d*NA&aQ zQk-_%;XjFB;eSQi+D1SiPN%6fm>oguR0o8}<|ukz%h;7U`{wP}f!NB*4V!LlWwu>p zp!hp1xu+l}4xqcr#6grE<94YQ1?pyUs;OHwhO2aWr2AtDH^=qiXYP_gP@VRz72P}O z6LKHG8nNw)I9sR(oXF+SFnfTpB|egg-Ld@Vpk>+8?daFdO|KtZ>6}rEKdJXd{hq4m znRB9F)>*~8o%nv#D;v;!je$3;uf$-J^8S0`2Ihdz3%pm+nRBFOD0YE+wj+B z?}zWhuKlX2FZ3MqvD0BTTNf}*Ft{4zoOuWNr!hT)zeAI&4V~nOb`llp&D5?mx3O;} zw;n557VaMZWu|=M@w4v(XxGcEvBn%LsRv0`N=X;LB<;QDxYk32)X6KJoMPrGg_GKQ zJMgv2Q0m+PoP8;}eb4w$mW+FZm+gk}kP7?q%C(Iasj?LY{d1%-x79IR)vsKa?Qo+GA>QxBGSK{O0U;<%F=D$ z*QCui{ak|y-~Sa6Mfg*u^qJTd5^pR2P@{m@>cb$apq<)HA#ByPNr(3Ky7ZA%hke2T zUHc@rv*VTW>iVv$*7wqe1z$)LB!CWg=pwuWoal+M6d#%dIhntw5NGE0=;szTB{?)e zesw~NOw&f7YhS0>d44eJJ8=Dk?0yjm9T0K*xHco^+x*}g4aBk1=*H&i`gXPUi2$5^ zWe-!U>ayA)jZc5*EyCf46Y$ynk^M<;99m8Q;J?BPP-XR2?(j5)6JdWvfK2SS=n92r z18FfT^A^fHOLR&H<9nV2&GnUiQ}Q0K4p(_hKmQVAu09;-aV)gx<(`peO3CAU!ZRl@u{#_p6Av~B zcH=)F*vO3O3a)2%cV0$pWvD~A6DPjXQE7t2Z1Vu<^6fO%gKYyUCAr5-)^~_||Jaoz zTX~{I<+YawoIN1i`ir*<|PS2=>fZE`9o>fh6mTC zx)1Ez*74={K`_=~XCW+3D1v|YuO%j{&;2TRX&TDIK(}Ek%iv^f#h@85ahK)54+Zey zXCvC!z4>R;6!8OQ?|+yM2CHglS0w)4Vm2w1TxTz2)7Pf3-Ksl2h93BpuLACThK|lJ z@yB@!QRRh`tHpL-p1zCpxO|SKw!`^Jb~ehH4|BYgc@aBd7dC>h~oKl(#m9! zUP7z3q4Mlmo#O|t=j_}^c{Fq=aq!0Bp4O+C2e!t2IVZc}1l)f3e>pw>e}CWbA%b{i zv{jD*to8gV)fz_WSLD3mK!(Uy=;7ZrTdrknJ)ns zRbt=B*CVilc%H4`acCRNRs-8=qfwoVC0Fx)kH7_+h^5gN=|eD=2>Z@FpZNM7>6xX8 z8aM0W+U`3d0wy{>pM6>r(|sQuAW|75F9 zdjGUKK5y`fV>q9A9g7*;;h#}xtR4xg;jB|tRcP^j(4gAFW|yzH{^1Rmu5;Y2ZkZdi zZSLaLBTOzqQS*W$Gp-*S2JGquHptfG8;#BNukZC3$ef~A^zA+D=(tJhCWr0~Cz(`<&#C%&jPkW!#2(Pv=WKq~F zG-y9^Wt?}3?+Q(V;QJG5VW^0La#krcsX>4`#R+xe!579 z$YLP!+tA84Pz!-v#wb6!MED-Doo`0pZ)oJy>JCH3zWsDW;=to)=SBkH;Xy}PC=jp# zkSJp#6gv-$`D6v9LT&sAN=B$Tz(Ke}ub+RV%qYcw$(o+EuW)V~%MD&h`h9-pSd%lW z@X904G^si-Lk0>UW>WRSQ3U%!l?nIVNUL{1QG|?hewVTEDEpbK{Q`z8lxqU+76l;> zJ$IL7%7}Sh+o9i(u!?xr4CE06;kHt+!bJ{}#iJ7vn{zvfiWwrHqGTAStng8cr;bq< zBX5v%im|!Dfg~AIpM;$^j}#uMmB08~z_SB^ZZ81YXyk=NM(Wy3)OtwqttC zRYv$OjDa=rZ|6a8RXI}UInw#_g?k7!6dd2Fx|@>9uv+ib33K?9(ZzE7b|U*`SEQ7A zfV1GL_hP%&>XD5hq&uxrCN`GFr;1|&wXaa$-!yq}py1c9NbR;p zQJK5ZJMw;|XZ#h>z^`WY}_c@@I7(lf7!^x|BnCv z%L2hjq-*^SglO!tbApGsAb?dMl(k#Y45ApCCPOmU!D&Ic^tA2YdbFPV9jBh9RzIvn z=Y*{Xzd(}=H!HK+hcp`cH{~5GRwleSexv7u@5bNRf?QDf(c)K$7v7STK}ZydYc;6oCFwju#e9}3?@R}PG7wj>jxqr zNK85m@n%9QT&Z0GD1r$_ndoO|9GtWB%7DaP>?S{jKlmcTj>OHSxBN{@l12C45EWTP zecie@B}n8~A#8D7C~gln-vl5@VEl;UhKT7L`>1^`i)^QK@lP5rvftbu1NN~sHZ7Z< zL?;+twt!>W-^*lj%UCF$0mzH8d6$F(C~3U%(#HV?E#5dhlVSAT(lb(NpP#lf zD&Wz@)?p|ON0uBX=KgDite!#U6TZ(gZuvPLZxejFFCN6}Q0fR$oqXdlTk(U&Ee3z@ zhl)S}hweDu6+)MB1cFNh4s31?2i<1%t{iv8xNavscN3S->1hC3AbXRRkhnps93z|5 z-NuBaLwnO>V^eOYhyYflGUSD27t5UwTf~ynZ%Vz(5g;AZE0A8tZ;5j7|hQ#EVXh2Fg8$FHyW`D z2R)ZB4H}`?h3P2E#~_{%(ur&& zic0P!)U2_}*twllxu-)(=K6el)Q*U=RCkN5&zeVbXeU!cMsWjy!Jde>^I@_J&U1&x@Dey2RKWx2N~b*oxy9of z2O`oZ8kmxYd-%Cx61P1nUEJ$i*6QkSJz@nu+3ezIA#e4D#FGUtC$f*Kjw-=sG2~KIbTfg>ghOMmU6g+78w~w&0gbq^* z29*M@6=mhmuO+$aK1Ku5UE!-76-1YjQYBSU-RlT7a( z*d2%nzyo4S?P4z~0}u_nhVH^8>fe$K*I*F&&FzL2nCYR)ux9xRpHN(<&DDEeDGJn= zx4J!pQ58J-++EjV%u#FklZ2;5!81(A*G=+kY_2-;0-0>&dWc``s5crsd}}o zA*XKxoo6)O1%DW)E52g@K4vhR`h@xP|1}Zie;sZA`}>7m^C==0CrE%66W_rm3+1OE ziO_Fw=nSQnLbgs_Ak7f!aBFn}(mUQ9@+q%PU>t->rm!y~X$|kyKi@&JNeR?Hfv*4gf67LXT2yb{Z-gpE6=-Y)bFJgTzez)uBX;J{NZH^`Rrp* zxW%)LyNGcYAPYiLGwsc@Jb*klXt^w#+ZLk3vKk=?PH*y@} zZAE|9Spn+~ap5*JC!FrosYmsg=dO$xq>^NW@6TI?M~5;F>$K$n+vDfYi##r2z1zuS+?~ z*ruKn&U{qp{?X-wIXb=o;xK7*_q)Yj?IQO|i^J2eV-ST0wb;ts zf34+5d}q{*aSyzP0?ctYGEkl6?J7&Ip{uW%sN`H7%^5=-2M=e1HpJQVAIPIAvllS= z&!0AmotM`QcT#2$ARSaUF^Txf=dSrd{$X}lTs02OA$7Y5Wzeo>&&$-?mGZmg$ujEX zZ{;N(Ug&EdwzOZ6iQ3~lp1dbX^rw>*{vh8`@PZHsM6ZaIRE{OECvG#u{s;Zh`R_vq zPT!L*Fh2K@8sN&EUpepU;Sd#$OWj1)cia2Ey#2!OjNNZ`{-)5K_;NP$i}4gRNL0iS zzO*FNmUi_elp^W+Ff)$v^asEI6G92Kc99ZJ|GCYO$kVoaF@%P#tVi~*en&*V+#pan zj~dc^gP1Afeiu|zeE%IZ481g<*55n-rd_XPI24{xpf=iYjDIQ5D*lI?pc#|&OZ;X= zv>y>&e|Xw-gU2EFUI#z0kXDL6DG32M3V^ziOSid)-^o`WHY~zY-yw7;{>5Vi7oS?n@|$J=#g0+ss)9l|;h& zg}w~wZ|xw=XzzXMm9c(1;8knQkB#pZf(_s9FTy)&W(>Azf-T9({>MPQ!6yjH8=CkU zOq3#is9il^%eV9F_gPnYoGfz$BB=}Zfy|k8b-w+K#vkb zgAN+6RIsxh0HFbG0vB=RAIt4NE;8AwQ|jiYzl>L9&sBtS-6|^5Jj;tebXERRbv=5vb#6ooZiy-_0Q^ zlz_Xo*C%#@Iy*CvD#2>FRrne2OX#>1fCwaPBy^~nn|cVTN^5a^w@f`%?GmU@V9le~O z)OsG+7M_Nr0hVV2S+YrIp_4&d9!4R$zvUR?cTZ^4r+l~7E=)aFW0d2#bkJh`>^_~t zS8MLTKWq?4JLU7wLUmyF5IMf@o=^)DhKG+hj8`Q!x81jre%aOI0<>e*gGi1y zM4T824?)z-hEjPa@O?B9&~(;{aI*oiyN+jxaS|$wto({9Yx?*zRUobF=F*xMb%7Lx z#&B(X<>f7JZu1=5l@1&sJw16|z!_3(GJ-}{NN!cEcDLl{Gd@iy2<1mf22l|!likfO zxvU_X`)tfx`u(N7)gKC#M1+|gZwGbo7lnhg#>F1gi~=(}1+Rr4I_@4nv?8E?{iSlG z&nYA_RBtCH=ZL{YmEDyUa_#v6+E`JdcHrbi!b`CoqS;>v&^{XL1us}Qn+pKuA<+!s z4nn7~Mweb{yQ<4la~@RDg-}~WHZ^CT`zw;cLx-u)g%M=~5~;;s3Sl&+#b^a*O(@<0 zh$kosV37n@FpXw!ywV5^2lJnOpvKQv_01?CGjT6+&*sfJr!_FlW0^DZ$(5^oES~5XVr~_4Zb>hWRZEM><*1JWlYPS#Te5l}7%q-3NGh&&M z5=Ztt@K2L=+ZThl=}YPvil;PWusV2vnpvpvl?((s{+3RQ-rqQM>F!0XYP*C@;1_C*W zLKjU8KSgZxF6)|Ei*MgOZh(PFMG(4jsPXMJj7O#?gEyC!_AfgbA8}ho+{lZ_`m(vI zoo;V^!o6hUA6O-CV8+2{#Qxjw3tRVCXW6OmIWN7%<#D>mxh;048&Iim09yJHq5V$X zL(qOiHx}?#>{H+TQT?a-6o7Rce`>{Su^YjMiVV~}L-Tnb=o53Id|C#3 zjwmghq%%HHO=b-~x z)r^C>N}+ex9Gw7e8q&U1NJG^qu=Ye6<+Ej&eetI5hHal5CGs3AFt;4WmeeyX^5Ki& zw%*q-z0*>b?#$zf>`D=a8zck&hatR-AOjD_-TsJIf~wcMiBgpzPxmg`u)p-g`rf|e zA=lQ_5M|iZ*5vzZ|D34htrBa`2hZMA{ctLTE^=Ey1kV+Y&>FG5L({q3h*48%25}o` zcY4R)6CRwTx3*?2_pjO4wkn0Tr8T_wCK(TyW_g&D96sflcJ<-(tOz3FE9p%tiaH?N zMg)@i5lX%0o3-h{NycQN0Z_kdG>)$PBJOZqHm>zc4JvV^={}igBp=%9em(Q?8m`mG zG^ncmGg|q}tg&z#DE~KKf`CY zH9UScUX)`JwPB&_XzktUPg$82?=;tptTzbG>=1SBod~Vc>Y^Ibl0sjPjP&*@>gw*h z&$4AE+ndy^ye!O$-W*)JOkGPhhf4-`Y?+g^5WYcraeL6a?Mjmj%x`s{&pXtO-PUjW z)rxS8$aYWxt*8z2Yty3hn(26RXtB$DFFSMGeBbmSR+U(ucHJ+B`fx>?J@?6PM~CW@ z>MU{0;eh6?rse#JDLWBidOI96tvV2LrjjKf@&>$^!QkPOl?=)#Y2}kWHWYL74l_^F zL&?}e(fOv$X%^mZt{ap@-WEf5!>b&@EB9E!Uj-Tv_7>!O3nGo z*I4=gx#voz?yXN*V)XR8DjQp&W*@txGydrD0>QhUF%72=QCsW;sRSS3q0C1>LFIb! z#hkFVd3e&g2%4L-9$c@4CwgBc=NxgLyYl;@Eo*U8topl6%_$;+@n;krVBSq3<0*hW ze~eR_*uAmP9k_=RlFQfyH7}ski!3~R{Cdiq@}p46BbMsbuaFYo9zC6XcK-Ek9<|C50^hc zc3!g2O0&6l%UTTb8!XG^o<|xDkob z_Me$0FT6{w`{GHoPmrKA=n#1fm!F&&s3srAH(nhY_#uK9uAb*tehL2gv}8@){}V&A`<=(=UJ^Lh~K4pFkz5^Uz(92>rT_`FT5so6YH z^dti*>D#`wVDWU@NQ3=DVMj~rxVu`7_zNq!#&S+c=K`lV=NNMO%^gPx!~r@2+Mkfm zJHp=xSPX|CeTL&5`8tMPx>Esc#2`b`tvUy1RRyqw>2t0g%cJ^-FfUfIxvcOL*ZWj0 zkDiD#yjczU%nQQ`eFMX33{T5Vy6m>o{d`Yka40x{1z(4*}S- zvC8>X=EvqS<-417UkzyQUbFptxXIdkL&_@^N!&%S-^T@&_}#*3R4t3ry_VXnk(k)( zS%S%1jQ_jV|DHBA3$e}#F%dr)TlHNl89UQTMu14#|d#d+m!`3Aht2=gk{FM=5a@ya89@F{KrcVYkYhr55^9v;Ls zO?PBA@Ab~;-)>$?+710;V>{vXdL0OP+*Z!7=1*S>4vpv;L`_Xy--+Gh>y~kK8h-ls z#jj_}80#6WIvmS4xAnS7n`U(u(wO4P(WiSE<|~bphbwE2{uPPb=`=u-pkQG!|4d5! z^^)Kf|2C<}*JAknQbuR}@^7O5L==!h99^M(ZLTgTYqI=5aGp-kK5|tO( zxy-&4#I=kh0-2b48q};Z1T@fOEYBTE!E`Iwd-<`{l}xWoCb;=Nh%BcZINSu+JF!8a z5t;lAl@Ii|8PNXAiF_F@{O%DC$O9=u=$wS&N}z3rRX#TGWb?|i0dMM`a z*sd&`B*G|O--fnDJWd z#^zka+oPm=z}sVp3;49L_rZ0ZGv6=lq19&eh_!D4jLi+$=QB|DmC=wEw^<*TiO~H8 zm&oKCw!OrmQ=%s44o!sz?s)zNHcnC&&c2EOx%_!L5z@@yt2vlJeI3n-sn)N#_bWtR z5#RERr(0{UP(%rj4v(TqAD?yHp5**s%XASMkzFY0m^g#hL7Rd1aB0%jt!C^x4=gOA zR^^z%K$^Yn>n2AlO6#qj*hp#h68ABe`{)$zDC^G@vt4$Ibu4 z9ZEc#wtJ+WrEjb7!{f)opi%M5WYGtQ+Fh+v$7>^8MFfPo!yZBGMdnZ!DmBlFTgUFY zQa9=UbI|&S?dyX6%Q;6$^}o+3IIax5$r8I%lFHZK%0dm1`7$u>1HmfTPp@zV#re<`MyV_BjI=vUIE?s`uq_=*ATqW7AF9*NZN{+gfDq zDk4nyM{}XBgO*!;Uq<_WXrmRIn8Y{M15hF*QCBx~M#cyG!Zx{ubv$?n?weP}!?%jc z&XScSg1hck`0a!a&_oTurkd{qn1#O|tzIu_VFIVmM)i>=@(hmHU=^ke@=erkO?(}; zQ4H$nxL+5dNJ^!65hMv6*D3T0nus#@2XKO1`YYli(CZxt3Z|pBK;agn4dQOfN55q1 zJRhsplP+!f5f*#m@ajp5^xF4g5n)xW$e{lF)51u~HIQ@@SvH&&?shQ#)|hunry!L> z{9ukq2t%>)O(CcQM~J!Kj)yD7Gu@U!r8j zKv28-;0EHn$GpaixAvo zod9IOmsa3Xpx?Y3_-=8)SEA;wT;N->6O#=|TQRw=A^6K}vmerKdGaiJMK1eTv~XX( zxao3!!&b^G*$q25gE%!#4}qa7sKdjca$NDbROjK6H@r?;`4G{moj*_!S9e(9RCuoC zsq;1+%5Og?nqJ%sJ7e~skn@vNK_?5R7{i^<{)+V9GLK3w7WS7?&+*kWS}&HG7iTDM zwnH5pD*9)r{BL6N#=jH){~~<1QpD=Dco^56JI2qRmCW9=6++y@U`m^^V}A zl`<8M=RDkTOy!bM=Z3Xd(c6N0Yzr!p_Fr{?P>s-WyWJ$>sgH~edySEJ*HmAr`gD)c zi%3HGC*9x%l-PE*hz72ez2yYr=GXth_v(MTl>hth_!S^nr3nWq^%>o3NtBHQG-BEd zIcx}y0GvEWM_3!86DD*&NV@=x@+zJlaUS7nVJQ2Y`SfqiJIZ#dJ0^CKVu8Gl83Nil>i?OjC!=q)eipK4wR3JsR4-q2b<{8ywV zv*>m9wO47RuqWV6R9ff+mDFAq8HKSQ!GmnmG}p!^`a|oWwi7W+)BshkLjvnBPSB zWUR3>E%;H@)G4Ew!FvyPWsKzQ-^livr>&TQp;ZS2&8-1EPvHO@)(*!xf5dB{Z~J>_ zu!rtdpM4naQ;C)qm|G=edM!f1mnx5|}N6&G!=7xM7POG^oo10mT)S)G+GT#pbYLj%W9@eMp`D;X!J=NKeJSBVam6D+4SqwrR zq^=)`jTF8tk!uM(7NM6zSAr?taCTRO0YL6mZy`%0AeH?;~6 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�cMjr^q8OT5${~cB9Y^yzw)o7VU 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 I-alUd3B6trOaK4? literal 0 HcmV?d00001 From 6a6b1e23a24377adde1e790dfff08b6c3cfe4b50 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Fri, 30 Oct 2026 18:25:23 +0000 Subject: [PATCH 02/13] Bump version -> `2.0.0-SNAPSHOT.470` Co-Authored-By: Claude Opus 5.5 --- version.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle.kts b/version.gradle.kts index 02fd8c67df..9cfbe87cf7 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -15,4 +15,4 @@ /** * The version of the Validation library to publish. */ -extra.set("validationVersion", "2.0.0-SNAPSHOT.469") +extra.set("validationVersion", "2.0.0-SNAPSHOT.470") From b9e7a165ec89a4420c69c32d87b6848ae70b0b21 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Fri, 30 Oct 2026 18:25:23 +0000 Subject: [PATCH 03/13] Embed the bumped `version.gradle.kts` in the release docs Co-Authored-By: Claude Opus 5.5 --- docs/content/docs/validation/developer/build-and-release.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/docs/validation/developer/build-and-release.md b/docs/content/docs/validation/developer/build-and-release.md index f62fc6e4ef..15e8ff39aa 100644 --- a/docs/content/docs/validation/developer/build-and-release.md +++ b/docs/content/docs/validation/developer/build-and-release.md @@ -47,7 +47,7 @@ through Gradle's `extra` properties: end="validationVersion"> ```kotlin -extra.set("validationVersion", "2.0.0-SNAPSHOT.469") +extra.set("validationVersion", "2.0.0-SNAPSHOT.470") ``` The root build script applies this file under `allprojects { … }` and assigns From 1641b397ade208264a0ca30ba3a5c744240a12d9 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Fri, 30 Oct 2026 18:26:18 +0000 Subject: [PATCH 04/13] Avoid repeating "plugin" in the publication text Co-Authored-By: Claude Opus 5.5 --- docs/content/docs/validation/developer/build-and-release.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/docs/validation/developer/build-and-release.md b/docs/content/docs/validation/developer/build-and-release.md index 15e8ff39aa..70a39be423 100644 --- a/docs/content/docs/validation/developer/build-and-release.md +++ b/docs/content/docs/validation/developer/build-and-release.md @@ -225,8 +225,8 @@ block above. The library artifacts and the Gradle plugin go to: (`gitHub("validation")` resolves to `maven.pkg.github.com/SpineEventEngine/validation`). - Spine's Google Cloud Artifact Registry (`cloudArtifactRegistry`). -The `java-gradle-plugin` plugin publishes the Gradle plugin together with its -plugin marker artifact, `io.spine.validation:io.spine.validation.gradle.plugin`. +`java-gradle-plugin` publishes the Gradle plugin together with its plugin +marker artifact, `io.spine.validation:io.spine.validation.gradle.plugin`. Gradle looks up this marker when a consumer writes `id("io.spine.validation")` in their `plugins { … }` block. The plugin is not published to the Gradle Plugin Portal, so consumers add Spine's repositories to `pluginManagement`, as shown in From 4f9577c0631580ab9d33609a2ca9bc0f2d04b00c Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Fri, 30 Oct 2026 18:28:26 +0000 Subject: [PATCH 05/13] Update dependency reports Co-Authored-By: Claude Opus 5.5 --- docs/dependencies/dependencies.md | 68 +++++++++++++++---------------- docs/dependencies/pom.xml | 2 +- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/docs/dependencies/dependencies.md b/docs/dependencies/dependencies.md index 6628a2bc41..2edc2256d2 100644 --- a/docs/dependencies/dependencies.md +++ b/docs/dependencies/dependencies.md @@ -1,6 +1,6 @@ -# Dependencies of `io.spine.tools:validation-context:2.0.0-SNAPSHOT.469` +# Dependencies of `io.spine.tools:validation-context:2.0.0-SNAPSHOT.470` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -1785,14 +1785,14 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:18:20 WEST 2026** using +This report was generated on **Fri Oct 30 18:27:25 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-context-tests:2.0.0-SNAPSHOT.469` +# Dependencies of `io.spine.tools:validation-context-tests:2.0.0-SNAPSHOT.470` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -3217,14 +3217,14 @@ This report was generated on **Thu Oct 22 15:18:20 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using +This report was generated on **Fri Oct 30 18:27:24 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-docs:2.0.0-SNAPSHOT.469` +# Dependencies of `io.spine.tools:validation-docs:2.0.0-SNAPSHOT.470` ## Runtime ## Compile, tests, and tooling @@ -3234,14 +3234,14 @@ This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:18:18 WEST 2026** using +This report was generated on **Fri Oct 30 18:27:23 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-gradle-plugin:2.0.0-SNAPSHOT.469` +# Dependencies of `io.spine.tools:validation-gradle-plugin:2.0.0-SNAPSHOT.470` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -4934,14 +4934,14 @@ This report was generated on **Thu Oct 22 15:18:18 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:18:20 WEST 2026** using +This report was generated on **Fri Oct 30 18:27:25 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-java:2.0.0-SNAPSHOT.469` +# Dependencies of `io.spine.tools:validation-java:2.0.0-SNAPSHOT.470` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -6726,14 +6726,14 @@ This report was generated on **Thu Oct 22 15:18:20 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:18:20 WEST 2026** using +This report was generated on **Fri Oct 30 18:27:25 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-java-bundle:2.0.0-SNAPSHOT.469` +# Dependencies of `io.spine.tools:validation-java-bundle:2.0.0-SNAPSHOT.470` ## Runtime 1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 13.0. @@ -6785,14 +6785,14 @@ This report was generated on **Thu Oct 22 15:18:20 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:18:18 WEST 2026** using +This report was generated on **Fri Oct 30 18:27:23 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-java-settings:2.0.0-SNAPSHOT.469` +# Dependencies of `io.spine.tools:validation-java-settings:2.0.0-SNAPSHOT.470` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -7577,14 +7577,14 @@ This report was generated on **Thu Oct 22 15:18:18 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using +This report was generated on **Fri Oct 30 18:27:24 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine:spine-validation-jvm-runtime:2.0.0-SNAPSHOT.469` +# Dependencies of `io.spine:spine-validation-jvm-runtime:2.0.0-SNAPSHOT.470` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -8389,14 +8389,14 @@ This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using +This report was generated on **Fri Oct 30 18:27:24 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-consumer:2.0.0-SNAPSHOT.469` +# Dependencies of `io.spine.tools:validation-consumer:2.0.0-SNAPSHOT.470` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -9747,14 +9747,14 @@ This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using +This report was generated on **Fri Oct 30 18:27:24 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-consumer-dependency:2.0.0-SNAPSHOT.469` +# Dependencies of `io.spine.tools:validation-consumer-dependency:2.0.0-SNAPSHOT.470` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -10674,14 +10674,14 @@ This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using +This report was generated on **Fri Oct 30 18:27:24 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-extensions:2.0.0-SNAPSHOT.469` +# Dependencies of `io.spine.tools:validation-extensions:2.0.0-SNAPSHOT.470` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -12116,14 +12116,14 @@ This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using +This report was generated on **Fri Oct 30 18:27:24 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-runtime:2.0.0-SNAPSHOT.469` +# Dependencies of `io.spine.tools:validation-runtime:2.0.0-SNAPSHOT.470` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -13096,14 +13096,14 @@ This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using +This report was generated on **Fri Oct 30 18:27:24 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-time:2.0.0-SNAPSHOT.469` +# Dependencies of `io.spine.tools:validation-time:2.0.0-SNAPSHOT.470` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -13858,14 +13858,14 @@ This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using +This report was generated on **Fri Oct 30 18:27:24 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-validating:2.0.0-SNAPSHOT.469` +# Dependencies of `io.spine.tools:validation-validating:2.0.0-SNAPSHOT.470` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -14988,14 +14988,14 @@ This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using +This report was generated on **Fri Oct 30 18:27:24 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-validator:2.0.0-SNAPSHOT.469` +# Dependencies of `io.spine.tools:validation-validator:2.0.0-SNAPSHOT.470` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -16449,14 +16449,14 @@ This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using +This report was generated on **Fri Oct 30 18:27:24 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-validator-dependency:2.0.0-SNAPSHOT.469` +# Dependencies of `io.spine.tools:validation-validator-dependency:2.0.0-SNAPSHOT.470` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -16731,14 +16731,14 @@ This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using +This report was generated on **Fri Oct 30 18:27:24 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-vanilla:2.0.0-SNAPSHOT.469` +# Dependencies of `io.spine.tools:validation-vanilla:2.0.0-SNAPSHOT.470` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -17433,6 +17433,6 @@ This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:18:19 WEST 2026** using +This report was generated on **Fri Oct 30 18:27:24 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file diff --git a/docs/dependencies/pom.xml b/docs/dependencies/pom.xml index 69168c1a9f..d49d82014b 100644 --- a/docs/dependencies/pom.xml +++ b/docs/dependencies/pom.xml @@ -10,7 +10,7 @@ all modules and does not describe the project structure per-subproject. --> io.spine.tools validation -2.0.0-SNAPSHOT.469 +2.0.0-SNAPSHOT.470 2015 From c92ba5bdf6b327cea43b0e9acb2541f119543b2b Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 2 Nov 2026 16:02:49 +0000 Subject: [PATCH 06/13] Embed Validation `2.0.0-SNAPSHOT.470` in "Adding to build" Co-Authored-By: Claude Opus 5.5 --- .../docs/validation/user/01-getting-started/adding-to-build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/docs/validation/user/01-getting-started/adding-to-build.md b/docs/content/docs/validation/user/01-getting-started/adding-to-build.md index 1ec494cedd..540873139b 100644 --- a/docs/content/docs/validation/user/01-getting-started/adding-to-build.md +++ b/docs/content/docs/validation/user/01-getting-started/adding-to-build.md @@ -90,7 +90,7 @@ Add the Validation plugin to the build. ```kotlin plugins { module - id("io.spine.validation") version "2.0.0-SNAPSHOT.469" + id("io.spine.validation") version "2.0.0-SNAPSHOT.470" } ``` From d9805c09e7bf36d55906a7f7520df28f8b9dc108 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 2 Nov 2026 16:05:26 +0000 Subject: [PATCH 07/13] Update `site-commons` Co-Authored-By: Claude Opus 5.5 --- docs/_preview/go.mod | 4 ++-- docs/_preview/go.sum | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/_preview/go.mod b/docs/_preview/go.mod index f7adfdf8ee..5948063bf4 100644 --- a/docs/_preview/go.mod +++ b/docs/_preview/go.mod @@ -1,8 +1,8 @@ module spine.io/validation/docs/preview -go 1.25.6 +go 1.27.3 require ( - github.com/SpineEventEngine/site-commons v0.0.0-20260522171914-2a606d89558f // indirect + github.com/SpineEventEngine/site-commons v0.0.0-20261102155919-1c7c5ef33424 // indirect github.com/gohugoio/hugo-mod-bootstrap-scss/v5 v5.20300.20800 // indirect ) diff --git a/docs/_preview/go.sum b/docs/_preview/go.sum index bcfc772e1c..6ed2b3b14d 100644 --- a/docs/_preview/go.sum +++ b/docs/_preview/go.sum @@ -2,6 +2,8 @@ github.com/SpineEventEngine/site-commons v0.0.0-20260507130158-84db050dfe11 h1:N github.com/SpineEventEngine/site-commons v0.0.0-20260507130158-84db050dfe11/go.mod h1:tkAl4StIREKmz9r5PiJtuDhvwMMkFXKWcaTyxhIikho= github.com/SpineEventEngine/site-commons v0.0.0-20260522171914-2a606d89558f h1:yF43XTDNj+w1S0fLA/XhrWMrhAoDpqPB7gmc4mUF/9Q= github.com/SpineEventEngine/site-commons v0.0.0-20260522171914-2a606d89558f/go.mod h1:tkAl4StIREKmz9r5PiJtuDhvwMMkFXKWcaTyxhIikho= +github.com/SpineEventEngine/site-commons v0.0.0-20261102155919-1c7c5ef33424 h1:tI8D25LWectjJRDxCrKbAiYFDbJi/3NGSNjAO2xJAnA= +github.com/SpineEventEngine/site-commons v0.0.0-20261102155919-1c7c5ef33424/go.mod h1:TfFFABXgPop1uc4eBdfWZF0Es06TGmNHfReiOzkZqrk= github.com/gohugoio/hugo-mod-bootstrap-scss/v5 v5.20300.20800 h1:j5myhhzYwIHTr5ctK96Elfgp5uRROvrlTzYwwe1nF8o= github.com/gohugoio/hugo-mod-bootstrap-scss/v5 v5.20300.20800/go.mod h1:P5GGyhdxi00C5zW7vkRo/IS532gZY/YS2TS395Xaxho= github.com/gohugoio/hugo-mod-jslibs-dist/popperjs/v2 v2.21100.20000/go.mod h1:mFberT6ZtcchrsDtfvJM7aAH2bDKLdOnruUHl0hlapI= From 8290f71f488a65d1d63a350196f1c9b7700c0362 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 2 Nov 2026 16:07:36 +0000 Subject: [PATCH 08/13] Link to `adding-to-build.md` so the links work on GitHub Co-Authored-By: Claude Opus 5.5 --- docs/content/docs/validation/developer/build-and-release.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/content/docs/validation/developer/build-and-release.md b/docs/content/docs/validation/developer/build-and-release.md index 70a39be423..414dee15ce 100644 --- a/docs/content/docs/validation/developer/build-and-release.md +++ b/docs/content/docs/validation/developer/build-and-release.md @@ -9,7 +9,7 @@ headline: Documentation This page describes how the Gradle multi-project build is wired, what each publishable module produces, and how the resulting artifacts reach downstream consumers. It is the contributor-side counterpart to “[Adding Validation to your -build](../user/01-getting-started/adding-to-build/)” in the User's Guide: +build](../user/01-getting-started/adding-to-build.md)” in the User's Guide: where that page shows how a *consumer* applies the plugin, this page shows how the plugin and its dependencies are *produced*. @@ -230,7 +230,7 @@ marker artifact, `io.spine.validation:io.spine.validation.gradle.plugin`. Gradle looks up this marker when a consumer writes `id("io.spine.validation")` in their `plugins { … }` block. The plugin is not published to the Gradle Plugin Portal, so consumers add Spine's repositories to `pluginManagement`, as shown in -“[Adding Validation to your build](../user/01-getting-started/adding-to-build/)”. +“[Adding Validation to your build](../user/01-getting-started/adding-to-build.md)”. Publication is automated by the [`publish.yml`][publish-workflow] GitHub Actions workflow, which runs on every push to `master` after PR checks have already @@ -266,7 +266,7 @@ Two kinds of downstream consumer pick up Validation artifacts: `plugins { … }` block. They never reference `:java-bundle` or `:jvm-runtime` directly — the Gradle plugin adds both, and `version.gradle.kts` in *this* repository determines which versions they get. The consumer-side flow is - documented in “[Adding Validation to your build](../user/01-getting-started/adding-to-build/)”. + documented in “[Adding Validation to your build](../user/01-getting-started/adding-to-build.md)”. - **Tooling projects** that bundle the Validation Compiler into their own Spine Compiler distribution. The most prominent example is the From 741ed5a0f371984f6f05dc80cc849ea6f8dbb75a Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 2 Nov 2026 16:53:46 +0000 Subject: [PATCH 09/13] Update shared agent tooling and config (./config/pull) Co-Authored-By: Claude Opus 5.5 --- .github/workflows/check-links.yml | 87 ++++- .github/workflows/increment-guard.yml | 20 +- .github/workflows/publish-api-docs.yml | 100 +++++ .github/workflows/publish.yml | 2 +- .github/workflows/revalidate-versions.yml | 6 +- buildSrc/src/main/kotlin/BuildExtensions.kt | 24 +- buildSrc/src/main/kotlin/DokkaExts.kt | 25 +- .../src/main/kotlin/dokka-setup.gradle.kts | 7 +- .../kotlin/io/spine/dependency/local/Base.kt | 2 +- .../io/spine/dependency/local/BaseTypes.kt | 2 +- .../io/spine/dependency/local/Change.kt | 2 +- .../io/spine/dependency/local/Compiler.kt | 2 +- .../io/spine/dependency/local/CoreJvm.kt | 2 +- .../spine/dependency/local/CoreJvmCompiler.kt | 4 +- .../io/spine/dependency/local/DokkaTools.kt | 2 +- .../io/spine/dependency/local/Logging.kt | 15 + .../kotlin/io/spine/dependency/local/Time.kt | 2 +- .../io/spine/dependency/local/ToolBase.kt | 23 +- .../io/spine/dependency/local/Validation.kt | 2 +- .../src/main/kotlin/io/spine/gradle/Cli.kt | 41 +- .../io/spine/gradle/ProjectExtensions.kt | 4 + .../main/kotlin/io/spine/gradle/TaskName.kt | 20 + .../gradle/checkstyle/CheckStyleConfig.kt | 2 +- .../kotlin/io/spine/gradle/git/Repository.kt | 130 ++++++- .../spine/gradle/github/pages/AuthorEmail.kt | 3 +- .../gradle/github/pages/DocsPublication.kt | 254 +++++++++++++ .../gradle/github/pages/PublishAllApiDocs.kt | 76 ++++ ...UpdateGitHubPages.kt => PublishApiDocs.kt} | 82 ++-- ...xtension.kt => PublishApiDocsExtension.kt} | 32 +- .../github/pages/RepositoryExtensions.kt | 26 +- .../io/spine/gradle/github/pages/TaskName.kt | 16 +- .../io/spine/gradle/github/pages/Update.kt | 148 -------- .../gradle/javadoc/ExcludeInternalDoclet.kt | 2 +- .../io/spine/gradle/javadoc/JavadocConfig.kt | 36 +- .../io/spine/gradle/publish/ProtoExts.kt | 16 +- .../gradle/publish/PublicationChecksums.kt | 44 +-- .../spine/gradle/publish/PublicationSbom.kt | 2 +- .../spine/gradle/publish/SpinePublishing.kt | 4 + .../publish/StandardJavaPublicationHandler.kt | 68 ++++ .../gradle/report/license/LicenseReporter.kt | 13 +- .../spine/gradle/report/pom/PomGenerator.kt | 6 +- .../src/main/kotlin/jvm-module.gradle.kts | 20 +- buildSrc/src/main/kotlin/module.gradle.kts | 8 +- .../src/test/kotlin/BuildExtensionsSpec.kt | 73 ++++ .../test/kotlin/io/spine/gradle/CliSpec.kt | 51 +++ .../io/spine/gradle/docs/DokkaSetupIgTest.kt | 8 +- .../io/spine/gradle/git/RepositorySpec.kt | 313 ++++++++++++++++ .../gradle/github/pages/NewestAboveSpec.kt | 55 +++ .../github/pages/PublishApiDocsIgTest.kt | 349 ++++++++++++++++++ .../spine/gradle/javadoc/JavadocConfigSpec.kt | 45 +++ .../io/spine/gradle/publish/ProtoExtsSpec.kt | 55 +++ .../gradle/publish/PublicationSbomIgTest.kt | 71 ++++ .../license/DependencyReportOutputTest.kt | 23 ++ config | 2 +- 54 files changed, 2053 insertions(+), 374 deletions(-) create mode 100644 .github/workflows/publish-api-docs.yml create mode 100644 buildSrc/src/main/kotlin/io/spine/gradle/github/pages/DocsPublication.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishAllApiDocs.kt rename buildSrc/src/main/kotlin/io/spine/gradle/github/pages/{UpdateGitHubPages.kt => PublishApiDocs.kt} (65%) rename buildSrc/src/main/kotlin/io/spine/gradle/github/pages/{UpdateGitHubPagesExtension.kt => PublishApiDocsExtension.kt} (70%) delete mode 100644 buildSrc/src/main/kotlin/io/spine/gradle/github/pages/Update.kt create mode 100644 buildSrc/src/test/kotlin/BuildExtensionsSpec.kt create mode 100644 buildSrc/src/test/kotlin/io/spine/gradle/CliSpec.kt create mode 100644 buildSrc/src/test/kotlin/io/spine/gradle/git/RepositorySpec.kt create mode 100644 buildSrc/src/test/kotlin/io/spine/gradle/github/pages/NewestAboveSpec.kt create mode 100644 buildSrc/src/test/kotlin/io/spine/gradle/github/pages/PublishApiDocsIgTest.kt create mode 100644 buildSrc/src/test/kotlin/io/spine/gradle/javadoc/JavadocConfigSpec.kt create mode 100644 buildSrc/src/test/kotlin/io/spine/gradle/publish/ProtoExtsSpec.kt diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml index 6755ff910e..cf02c5d64f 100644 --- a/.github/workflows/check-links.yml +++ b/.github/workflows/check-links.yml @@ -8,16 +8,23 @@ on: - 'docs/**' - 'site/**' - 'lychee.toml' + - '.lycheeignore' - '.github/workflows/check-links.yml' workflow_dispatch: env: - HUGO_VERSION: 0.161.1 + HUGO_VERSION: 0.169.0 LYCHEE_RELEASE: "lychee-x86_64-unknown-linux-gnu.tar.gz" LYCHEE_VERSION_TAG: "lychee-v0.24.2" # SHA256 of the above tarball, pinned at download time. Update alongside # LYCHEE_VERSION_TAG whenever the binary is upgraded. LYCHEE_SHA256: "1f4e0ef7f6554a6ed33dd7ac144fb2e1bbed98598e7af973042fc5cd43951c9a" + # Pinned to the release the Spine sites were checked with. Newer releases + # add Sass deprecations, which nobody has checked against those sites yet. + DART_SASS_VERSION: 1.99.0 + # SHA256 of `dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz`, from the GitHub release. + # Update alongside DART_SASS_VERSION. + DART_SASS_SHA256: "f0a32ab99bc0a1eb62ea7140a056a1bca82d534390b7c3dba4f2016eb0077753" # Force Hugo to write its module cache where the cache step actually # restores from. Hugo's default on Linux is `~/.cache/hugo_cache` # (or `$TMPDIR/hugo_cache_$USER`), neither of which matches the @@ -38,6 +45,11 @@ jobs: # Detect the Hugo site root (`docs/` or `site/`) by looking for a Hugo # config file. Hugo config may live directly in the site root or in a # `config/` or `config/_default/` subdirectory (both layouts are valid). + # A repo may also keep its only Hugo config in the `_preview/` sub-tree + # (e.g. `docs/_preview/hugo.toml`), so the same paths count there too. + # Hugo finds such a config only when it runs in `$dir/_preview`, so it + # needs `$dir/_preview/package-lock.json`: the `$dir` fallback below + # is for site-root configs only. # Outputs `present=true|false` and `work_dir` (the directory where # `npm ci` / `hugo` commands should run — either `$dir/_preview` for # repos that use a separate preview sub-tree, or `$dir` for repos whose @@ -51,19 +63,22 @@ jobs: for dir in docs site; do for cfg in hugo.toml hugo.yaml \ config/hugo.toml config/hugo.yaml \ - config/_default/hugo.toml config/_default/hugo.yaml; do + config/_default/hugo.toml config/_default/hugo.yaml \ + _preview/hugo.toml _preview/hugo.yaml \ + _preview/config/hugo.toml _preview/config/hugo.yaml \ + _preview/config/_default/hugo.toml _preview/config/_default/hugo.yaml; do if [ -f "$dir/$cfg" ]; then if [ -f "$dir/_preview/package-lock.json" ]; then echo "work_dir=$dir/_preview" >> "$GITHUB_OUTPUT" echo "present=true" >> "$GITHUB_OUTPUT" echo "::notice::Hugo site found under $dir/ (work_dir: $dir/_preview)" - elif [ -f "$dir/package-lock.json" ]; then + elif [ -f "$dir/package-lock.json" ] && [[ $cfg != _preview/* ]]; then echo "work_dir=$dir" >> "$GITHUB_OUTPUT" echo "present=true" >> "$GITHUB_OUTPUT" echo "::notice::Hugo site found under $dir/ (work_dir: $dir)" else echo "present=false" >> "$GITHUB_OUTPUT" - echo "::notice::Hugo config found in $dir/ but no package-lock.json found — skipping link check." + echo "::notice::Found $dir/$cfg but no package-lock.json to build it with — skipping link check." fi exit 0 fi @@ -79,13 +94,38 @@ jobs: hugo-version: ${{ env.HUGO_VERSION }} extended: true - # `actions/setup-node@v4` ships with built-in npm caching that hashes + # The Spine Hugo theme compiles its SCSS with Dart Sass, because Hugo + # deprecated LibSass in v0.153.0 and will remove it. Hugo does not + # bundle Dart Sass: it runs the `sass` binary it finds on `PATH`, and + # a site on that theme fails to build without one. The release + # tarball is standalone, so no Node or Dart setup is needed. The + # checksum guards the download, as it does for Lychee below. Sites + # still on LibSass ignore the binary. + - name: Install Dart Sass + if: steps.docs.outputs.present == 'true' + run: | + archive="dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz" + curl -fsSLO "https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/${archive}" + echo "${DART_SASS_SHA256} ${archive}" | sha256sum --check --strict + tar -xzf "${archive}" -C "${RUNNER_TEMP}" + rm "${archive}" + echo "${RUNNER_TEMP}/dart-sass" >> "${GITHUB_PATH}" + + # Hugo finds Dart Sass on `PATH`. `hugo env` lists it when it does. + # `GITHUB_PATH` only takes effect from the next step on, so this check + # cannot live in the install step above. It fails here, rather than + # mid-build, if the install did not put `sass` where Hugo looks. + - name: Check Dart Sass + if: steps.docs.outputs.present == 'true' + run: hugo env | grep 'github.com/sass/dart-sass' + + # `actions/setup-node` ships with built-in npm caching that hashes # the lockfile and restores `~/.npm`. We use that instead of a - # standalone `actions/cache@v4` block so there is only one source of + # standalone `actions/cache` block so there is only one source of # truth for the cache key (no drift between two layers). - name: Setup Node if: steps.docs.outputs.present == 'true' - uses: actions/setup-node@v4 + uses: actions/setup-node@v7 with: node-version: '26' cache: 'npm' @@ -97,7 +137,7 @@ jobs: # invalidates the cache deterministically regardless of site root. - name: Cache Hugo Modules if: steps.docs.outputs.present == 'true' - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: /tmp/hugo_cache key: ${{ runner.os }}-hugomod-${{ hashFiles('docs/**/go.sum', 'site/**/go.sum') }} @@ -121,7 +161,7 @@ jobs: # `max_cache_age` expires. - name: Cache Lychee results if: steps.docs.outputs.present == 'true' - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: .lycheecache key: cache-lychee-${{ runner.os }}-${{ hashFiles('lychee.toml') }} @@ -136,7 +176,7 @@ jobs: - name: Cache Lychee executable if: steps.docs.outputs.present == 'true' id: cache-lychee - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: lychee key: ${{ runner.os }}-${{ env.LYCHEE_VERSION_TAG }}-${{ env.LYCHEE_RELEASE }} @@ -156,7 +196,7 @@ jobs: # string when the file is absent, so this guard runs the download iff # neither the exact key nor any restore-key restored the binary. - name: Download Lychee executable - uses: robinraju/release-downloader@v1.7 + uses: robinraju/release-downloader@v1.13 if: steps.docs.outputs.present == 'true' && hashFiles('lychee/lychee') == '' with: repository: "lycheeverse/lychee" @@ -208,9 +248,32 @@ jobs: exit 1 fi + # Lychee prints broken links to this step's log only; the run page + # shows just "Process completed with exit code 2". `tee` keeps the + # report in the log and saves a copy, which the next step adds to the + # run summary. (`--output` would not do: it moves the report from the + # log to the file.) `pipefail` keeps Lychee's exit code. Without it, + # the step would take `tee`'s 0 and pass despite broken links. - name: Check links if: steps.docs.outputs.present == 'true' run: | + set -o pipefail ./lychee/lychee --config lychee.toml --timeout 60 \ --base-url http://localhost:1313/ \ - '${{ steps.docs.outputs.work_dir }}/public/**/*.html' + '${{ steps.docs.outputs.work_dir }}/public/**/*.html' \ + | tee lychee-report.txt + + # Runs after a failed `Check links` too, which is when the report + # matters most. `hashFiles` skips this step when Lychee never ran. + # `!cancelled()` needs the `${{ }}` wrapper: a bare leading `!` is + # a YAML tag. + - name: Publish link report + if: ${{ !cancelled() && hashFiles('lychee-report.txt') != '' }} + run: | + { + echo '## Link check' + echo + echo '```text' + cat lychee-report.txt + echo '```' + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/increment-guard.yml b/.github/workflows/increment-guard.yml index 9a6333f0ff..56f3008854 100644 --- a/.github/workflows/increment-guard.yml +++ b/.github/workflows/increment-guard.yml @@ -1,9 +1,13 @@ # Guards the project version by executing the Gradle `checkVersionIncrement` task, # which verifies that the version is both (a) strictly greater than the base branch # version in `version.gradle.kts` and (b) not already published. The result is -# published as the `Version Guard` commit status — the context required by branch -# protection and re-published by `revalidate-versions.yml` on the heads of other open -# PRs when the base branch advances (so a stale duplicate bump turns red before merge). +# published as the `Version bump` commit status, which `revalidate-versions.yml` also +# re-publishes on the heads of other open PRs when the base branch advances (so a stale +# duplicate bump turns red before merge). +# +# Branch protection must require the `Version bump` status, not the job's own +# `Check version increment` check run: only the status carries the revalidation verdict, +# and a job skipped by its `if` reports a passing check run. # # The check runs only for pull requests targeting a default (`master`/`main`) or # a release-line (e.g. `2.x-jdk8-master`) branch. It is the responsibility of a branch @@ -21,7 +25,7 @@ on: pull_request: # Beyond the default activity types (`opened`, `synchronize`, `reopened`), two more are # needed because they change what the guard must compare against without a new head SHA, - # which would otherwise leave a stale-green `Version Guard` status mergeable: + # which would otherwise leave a stale-green `Version bump` status mergeable: # * `ready_for_review` — a draft that went stale while in draft becomes ready; and # * `edited` — the base branch is retargeted (e.g. a release line -> `master`), so the # strict comparison must be recomputed against the new base. @@ -39,7 +43,7 @@ jobs: (endsWith(github.base_ref, 'master') || endsWith(github.base_ref, 'main')) && (github.event.action != 'edited' || github.event.changes.base.ref.from != '') - # `statuses: write` lets the job publish the `Version Guard` commit status that + # `statuses: write` lets the job publish the `Version bump` commit status that # branch protection requires. permissions: contents: read @@ -76,7 +80,7 @@ jobs: VERSION_GUARD: "true" run: ./gradlew checkVersionIncrement --stacktrace - # Publish the verdict as the `Version Guard` commit status on the PR head. Posting it + # Publish the verdict as the `Version bump` commit status on the PR head. Posting it # on every run (success or failure) is what lets a later re-bump clear a failure that # `revalidate-versions.yml` set when the base branch advanced. # @@ -84,7 +88,7 @@ jobs: # posted (and a fork head SHA is not in this repo). The Spine agent workflow pushes PR # branches to the same repository; fork contributions are handled by a maintainer, who # owns the version bump. - - name: Report the Version Guard status + - name: Report the Version bump status if: always() && github.event.pull_request.head.repo.fork == false shell: bash env: @@ -96,5 +100,5 @@ jobs: fi gh api -X POST "repos/${{ github.repository }}/statuses/${{ github.event.pull_request.head.sha }}" \ -f state="${state}" \ - -f context="Version Guard" \ + -f context="Version bump" \ -f description="Version increment check" diff --git a/.github/workflows/publish-api-docs.yml b/.github/workflows/publish-api-docs.yml new file mode 100644 index 0000000000..1f6f17a18f --- /dev/null +++ b/.github/workflows/publish-api-docs.yml @@ -0,0 +1,100 @@ +name: Publish API docs + +# Publishes the API documentation of the published modules to the `gh-pages` +# branch, which GitHub Pages serves under `spine.io`. +# +# The job runs apart from `publish.yml`, and neither waits for the other: a +# failure here leaves the Maven publication intact, and the other way round. +# `:publishAllApiDocs` is registered in the root project by `spinePublishing { }`, +# so it exists in every repository that publishes, and does nothing where no +# module documents itself. It runs the `publishApiDocs` task of each published +# module. +# +# Only release versions are published. For a snapshot version, which most merges +# produce, the task of each module only says that it skips the documentation, +# and the run ends green without generating or pushing any. +# +# A failed run can be re-run as it is: each module commits its documentation +# even when nothing changed, and retries a failed push after rebasing onto +# `gh-pages`. +# +# Each module clones only the tip of `gh-pages` and checks out only its own most +# recent documentation and the folder of its version, so that a run does not +# fetch the documentation of every version of every module. +# +# This job does not ask whether `publish.yml` could publish the version, as that +# would couple the two again. When `publish.yml` stops because the version is +# already taken, which the version checks of pull requests normally prevent, +# this job still copies the documentation of the commit over that of the version. +# +# Unlike `publish.yml`, this file is not a caller of a reusable workflow hosted +# by `config`. That split exists to make `config` the signer of the attestation +# of the published artifacts, and the documentation is not attested. + +on: + push: + branches: [master] + +jobs: + publish-api-docs: + name: Publish API docs + runs-on: ubuntu-latest + + # One run at a time per repository, since every run pushes to `gh-pages`. + # `queue: max` keeps up to 100 runs waiting, instead of letting a newer run + # cancel the waiting one: each run also adds the documentation of its own + # version, which no later run would publish. The waiting runs may start in + # another order than their commits, so a run replaces the most recent + # documentation of a module only if no newer version is published already. + concurrency: + group: api-docs-publishing-${{ github.repository }} + queue: max + + # The push goes over SSH with the deploy key, not with `GITHUB_TOKEN`, which + # only reads the dependencies published to GitHub Packages. + permissions: + contents: read + packages: read + + steps: + - uses: actions/checkout@v6 + with: + submodules: 'true' + + - uses: actions/setup-java@v5 + with: + java-version: 17 + distribution: zulu + + # The cache stays on, unlike in `publishing.yml`, which turns it off to keep + # its attestation trustworthy: nothing here is attested. A run on `master` + # reads only the caches written by the workflows that run on `master`. + - uses: gradle/actions/setup-gradle@v6 + + # The same step as in `build-on-ubuntu.yml`. For a release version, the build + # runs Dokka for every published module, in parallel, and the Linux runner + # ships with effectively no swap, so a memory peak would be an instant OOM + # kill. It goes before the deploy key is decrypted, so that the action never + # runs with the key on disk. + - name: Add swap space + uses: pierotofy/set-swap-space@v1.0 + with: + swap-size-gb: 8 + + - name: Decrypt Git SSH credentials + run: ./config/scripts/decrypt.sh "$GIT_CREDENTIALS_KEY" ./.github/keys/deploy_key_rsa.gpg ./deploy_key_rsa + env: + GIT_CREDENTIALS_KEY: ${{ secrets.GIT_CREDENTIALS_KEY }} + + # The SSH agent does not accept a key that others can read. The build runs + # `register-ssh-key.sh` to hand the key to the agent. + - name: Set file system permissions + run: chmod 400 ./deploy_key_rsa && chmod +x ./config/scripts/register-ssh-key.sh + + - name: Publish API docs + run: ./gradlew :publishAllApiDocs --stacktrace + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + FORMAL_GIT_HUB_PAGES_AUTHOR: developers@spine.io + # https://docs.github.com/en/actions/reference/environment-variables + REPO_SLUG: ${{ github.repository }} # e.g. SpineEventEngine/core-jvm diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8dc7c913bb..aacfe8e243 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -30,7 +30,7 @@ on: jobs: publish: name: Publish to Maven repositories - uses: SpineEventEngine/config/.github/workflows/publishing.yml@ad3dff0890c7716cbba3fe2ce17bf4fe5905a318 + uses: SpineEventEngine/config/.github/workflows/publishing.yml@3a0d13a6bc72f1d3b873a6c5f300aa9bf26ffe0a permissions: contents: read packages: write diff --git a/.github/workflows/revalidate-versions.yml b/.github/workflows/revalidate-versions.yml index 2d2ecb9c6d..59f81803cc 100644 --- a/.github/workflows/revalidate-versions.yml +++ b/.github/workflows/revalidate-versions.yml @@ -5,7 +5,7 @@ # now stale: its publish would collide. GitHub does not re-run a PR's checks when its base # advances, so this workflow does it actively — for each other open PR whose # `version.gradle.kts` version is `<=` the new base version, it posts a failing -# `Version Guard` commit status on the PR head, blocking the merge until the author +# `Version bump` commit status on the PR head, blocking the merge until the author # re-bumps. The status self-clears: the re-bump push runs `increment-guard.yml`, which # posts a fresh `success` on the new head. # @@ -40,14 +40,14 @@ concurrency: jobs: revalidate: - name: Revalidate open PRs + name: Mark stale version bumps runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: submodules: 'true' - - name: Revalidate open PRs against the new base version + - name: Mark open PRs whose version is not above the new base shell: bash env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/buildSrc/src/main/kotlin/BuildExtensions.kt b/buildSrc/src/main/kotlin/BuildExtensions.kt index a69dd1a044..cedacd2588 100644 --- a/buildSrc/src/main/kotlin/BuildExtensions.kt +++ b/buildSrc/src/main/kotlin/BuildExtensions.kt @@ -27,7 +27,6 @@ import io.spine.dependency.test.Kotest import io.spine.dependency.test.Kover import io.spine.gradle.repo.standardToSpineSdk import org.gradle.api.Project -import org.gradle.api.Task import org.gradle.api.artifacts.ModuleDependency import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.tasks.JavaExec @@ -168,7 +167,8 @@ fun Project.configureTaskDependencies() { * Creates a dependency between the Gradle task of *this* name * onto the task with `taskName`. * - * If either of the tasks does not exist in the enclosing `Project`, + * Neither task is created by this method. A task registered later is matched too. + * If either of the tasks never exists in the enclosing `Project`, * this method does nothing. * * This extension is kept local to the `configureTaskDependencies` extension @@ -176,9 +176,8 @@ fun Project.configureTaskDependencies() { */ fun String.dependOn(taskName: String) { val whoDepends = this - val dependOntoTask: Task? = tasks.findByName(taskName) - dependOntoTask?.let { - tasks.findByName(whoDepends)?.dependsOn(it) + tasks.named { it == whoDepends }.configureEach { + dependsOn(tasks.named { it == taskName }) } } @@ -267,19 +266,20 @@ fun JavaExec.remoteDebug(enabled: Boolean = true) { * * The port number is [5566][BuildSettings.REMOTE_DEBUG_PORT]. * + * The task is configured when Gradle creates it, so calling this function + * does not create the task. + * * @param enabled If `true` the task will be suspended. - * @throws IllegalStateException if the task with the given name is not found, or, - * if the task is not of [JavaExec] type. + * @throws IllegalStateException if the task with the given name is not found. + * @throws org.gradle.api.InvalidUserDataException if the task is not of [JavaExec] type. */ fun Project.setRemoteDebug(taskName: String, enabled: Boolean = true) { - val task = tasks.findByName(taskName) - check(task != null) { + check(taskName in tasks.names) { "Could not find a task named `$taskName` in the project `$name`." } - check(task is JavaExec) { - "The task `$taskName` is not of type `JavaExec`." + tasks.named(taskName, JavaExec::class.java).configure { + remoteDebug(enabled) } - task.remoteDebug(enabled) } /** diff --git a/buildSrc/src/main/kotlin/DokkaExts.kt b/buildSrc/src/main/kotlin/DokkaExts.kt index 5aeba8d5f0..849fea6cb2 100644 --- a/buildSrc/src/main/kotlin/DokkaExts.kt +++ b/buildSrc/src/main/kotlin/DokkaExts.kt @@ -25,6 +25,7 @@ import org.gradle.api.artifacts.dsl.DependencyHandler import org.gradle.api.execution.TaskExecutionGraph import org.gradle.api.publish.maven.tasks.PublishToMavenLocal import org.gradle.api.publish.maven.tasks.PublishToMavenRepository +import org.gradle.api.tasks.TaskCollection import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.bundling.Jar @@ -183,14 +184,22 @@ fun DokkaExtension.configureForJava(project: Project, sourceLinkRemoteUrl: Strin } /** - * Finds the `dokkaGenerateHtml` Gradle task. + * Finds the `dokkaGeneratePublicationHtml` Gradle task without creating it. + * + * The returned collection is live: it holds the task once Dokka registers it, + * and stays empty in a project without Dokka. */ -fun TaskContainer.dokkaHtmlTask(): Task? = this.findByName("dokkaGeneratePublicationHtml") +fun TaskContainer.dokkaHtmlTask(): TaskCollection = + named { it == "dokkaGeneratePublicationHtml" } /** - * Finds the `dokkaGeneratePublicationJavadoc` Gradle task. + * Finds the `dokkaGeneratePublicationJavadoc` Gradle task without creating it. + * + * The returned collection is live: it holds the task once Dokka registers it, + * and stays empty in a project without Dokka. */ -fun TaskContainer.dokkaJavadocTask(): Task? = this.findByName("dokkaGeneratePublicationJavadoc") +fun TaskContainer.dokkaJavadocTask(): TaskCollection = + named { it == "dokkaGeneratePublicationJavadoc" } /** * Locates or creates the `htmlDocsJar` task in this [Project]. @@ -205,9 +214,7 @@ fun Project.htmlDocsJar(): TaskProvider = tasks.getOrCreate("htmlDocsJar") archiveClassifier.set("html-docs") from(files(dokkaHtmlOutput())) - tasks.dokkaHtmlTask()?.let { dokkaTask -> - this@getOrCreate.dependsOn(dokkaTask) - } + this@getOrCreate.dependsOn(tasks.dokkaHtmlTask()) } /** @@ -238,7 +245,7 @@ fun Task.isInPublishingGraph(): Boolean = * Such a build usually feeds integration tests, which need the published code, * but not its documentation. So the Dokka tasks are skipped when the execution graph * contains a [PublishToMavenLocal] task and no task that ships the documentation - * elsewhere: `publish`, a [PublishToMavenRepository] task, or `updateGitHubPages`. + * elsewhere: `publish`, a [PublishToMavenRepository] task, or `publishApiDocs`. * They still run if a task named on the command line selects a documentation task of * the graph: one that has `dokka` in its name, ignoring case, or a documentation JAR, * `javadocJar` or `htmlDocsJar`. The name may be the full one, e.g., `dokkaGenerate`, @@ -276,7 +283,7 @@ fun Project.skipDokkaWhenPublishingToMavenLocal() { /** * The names of the lifecycle tasks that ship the documentation beyond Maven Local. */ -private val docsShippingTaskNames = setOf("publish", TaskName.updateGitHubPages) +private val docsShippingTaskNames = setOf("publish", TaskName.publishApiDocs) /** * Tells if this graph publishes to Maven Local and does not contain a task that ships diff --git a/buildSrc/src/main/kotlin/dokka-setup.gradle.kts b/buildSrc/src/main/kotlin/dokka-setup.gradle.kts index c66e9f2f4d..43eb56fa75 100644 --- a/buildSrc/src/main/kotlin/dokka-setup.gradle.kts +++ b/buildSrc/src/main/kotlin/dokka-setup.gradle.kts @@ -41,10 +41,7 @@ afterEvaluate { DocumentationSettings.SourceLink.url(project) ) } - val kspKotlin = tasks.findByName("kspKotlin") - kspKotlin?.let { - tasks.withType().configureEach { - dependsOn(kspKotlin) - } + tasks.withType().configureEach { + dependsOn(tasks.named { it == "kspKotlin" }) } } diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt index baaf5c7053..e58ab1415e 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt @@ -25,7 +25,7 @@ package io.spine.dependency.local ) object Base { const val version = "2.0.0-SNAPSHOT.462" - const val versionForBuildScript = "2.0.0-SNAPSHOT.461" + const val versionForBuildScript = "2.0.0-SNAPSHOT.462" const val group = Spine.group private const val prefix = "spine" const val libModule = "$prefix-base" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/BaseTypes.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/BaseTypes.kt index d92259dc5a..5f08d0bb3e 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/BaseTypes.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/BaseTypes.kt @@ -23,7 +23,7 @@ package io.spine.dependency.local "ConstPropertyName" /* We use a custom convention for artifact properties. */, ) object BaseTypes { - const val version = "2.0.0-SNAPSHOT.226" + const val version = "2.0.0-SNAPSHOT.227" const val group = Spine.group const val artifact = "spine-base-types" const val lib = "$group:$artifact:$version" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Change.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Change.kt index 1f6ebe1c62..91864be355 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Change.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Change.kt @@ -23,7 +23,7 @@ package io.spine.dependency.local "ConstPropertyName" /* We use a custom convention for artifact properties. */, ) object Change { - const val version = "2.0.0-SNAPSHOT.208" + const val version = "2.0.0-SNAPSHOT.209" const val group = Spine.group const val artifact = "spine-change" const val lib = "$group:$artifact:$version" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt index 66a3e8c889..b75c0f1ef9 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt @@ -69,7 +69,7 @@ object Compiler : Dependency() { * transitive dependencies, this is the version used to build the project itself. */ val dogfoodingVersion: String - private const val fallbackDfVersion = "2.0.0-SNAPSHOT.069" + private const val fallbackDfVersion = "2.0.0-SNAPSHOT.081" /** * The artifact for the Compiler Gradle plugin. diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt index 35246a7793..94f3d9d682 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt @@ -30,7 +30,7 @@ typealias CoreJava = CoreJvm ) object CoreJvm { const val group = Spine.group - const val version = "2.0.0-SNAPSHOT.571" + const val version = "2.0.0-SNAPSHOT.577" const val coreArtifact = "spine-core" const val clientArtifact = "spine-client" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt index 3465c08c02..8fc131eaa8 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt @@ -34,12 +34,12 @@ object CoreJvmCompiler { /** * The version used in the build classpath. */ - const val dogfoodingVersion = "2.0.0-SNAPSHOT.098" + const val dogfoodingVersion = "2.0.0-SNAPSHOT.099" /** * The version to be used for integration tests. */ - const val version = "2.0.0-SNAPSHOT.098" + const val version = "2.0.0-SNAPSHOT.099" /** * The ID of the Gradle plugin. diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/DokkaTools.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/DokkaTools.kt index 37bfae4ed7..4591fac501 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/DokkaTools.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/DokkaTools.kt @@ -25,7 +25,7 @@ package io.spine.dependency.local ) object DokkaTools { const val group = Spine.toolsGroup - const val version = "2.0.0-SNAPSHOT.11" + const val version = "2.0.0-SNAPSHOT.12" /** * The artifact dropped its `spine-` prefix in `2.0.0-SNAPSHOT.9`, to diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Logging.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Logging.kt index 96fbbb284c..b84652a194 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Logging.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Logging.kt @@ -42,7 +42,22 @@ object Logging { // Transitive dependencies. // Make `public` and use them to force a version in a particular repository, if needed. internal const val julBackend = "$group:spine-logging-jul-backend:$version" + + /** + * The former `middleware` module of Spine Logging. + * + * In `2.0.0-SNAPSHOT.270`, its code moved into the `logging` module, which is + * published as [lib]. The last published version of this artifact is + * `2.0.0-SNAPSHOT.263`, so these coordinates do not resolve at [version]. + * + * @deprecated Use [lib] instead. + */ + @Deprecated( + "The module was merged into `spine-logging` and is no longer published.", + ReplaceWith("lib"), + ) const val middleware = "$group:spine-logging-middleware:$version" + internal const val platformGenerator = "$group:spine-logging-platform-generator:$version" internal const val jvmDefaultPlatform = "$group:spine-logging-jvm-default-platform:$version" } diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt index 3e8e39938d..d9221c3d7b 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt @@ -28,7 +28,7 @@ import io.spine.dependency.Dependency ) object Time : Dependency() { override val group = Spine.group - override val version = "2.0.0-SNAPSHOT.251" + override val version = "2.0.0-SNAPSHOT.253" private const val infix = "spine-time" fun lib(version: String): String = "$group:$infix:$version" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt index bb675bca2d..7bd22c472a 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt @@ -33,7 +33,7 @@ package io.spine.dependency.local object ToolBase { const val group = Spine.toolsGroup const val version = "2.0.0-SNAPSHOT.428" - const val dogfoodingVersion = "2.0.0-SNAPSHOT.426" + const val dogfoodingVersion = "2.0.0-SNAPSHOT.428" const val archive = "$group:archive:$version" const val code = "$group:code:$version" @@ -56,11 +56,28 @@ object ToolBase { const val rootGradlePlugins = "$group:root-gradle-plugins:$version" const val gradlePluginApi = "$group:gradle-plugin-api:$version" + + /** + * Stub Gradle plugins for the tests of the `gradle-plugin-api` module. + * + * The `gradle-plugin-api-test-fixtures` module that provides them is internal + * to `tool-base`, which never publishes it to a remote repository. Only + * `publishToMavenLocal` installs it, so these coordinates resolve only from + * a local `~/.m2` copy. + * + * @deprecated There is no replacement. Remove the usage. + */ + @Deprecated( + "The artifact is internal to `tool-base` and is not published remotely. Remove the usage." + ) const val gradlePluginApiTestFixtures = "$group:gradle-plugin-api-test-fixtures:$version" const val jvmTools = "$group:jvm-tools:$version" - const val jvmToolPluginDogfooding = "$group:jvm-tool-plugins-all:$dogfoodingVersion" - const val jvmToolPlugins = "$group:jvm-tool-plugins-all:$version" + + // The plain plugin JAR. `jvm-tool-plugins-all` is no longer published + // as of `2.0.0-SNAPSHOT.440`. See https://github.com/SpineEventEngine/tool-base/pull/201 + const val jvmToolPluginDogfooding = "$group:jvm-tool-plugins:$dogfoodingVersion" + const val jvmToolPlugins = "$group:jvm-tool-plugins:$version" const val protobufSetupPlugins = "$group:protobuf-setup-plugins:$version" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt index 155cbbc602..1be9aff47a 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt @@ -27,7 +27,7 @@ object Validation { /** * The version of the Validation library artifacts. */ - const val version = "2.0.0-SNAPSHOT.464" + const val version = "2.0.0-SNAPSHOT.469" const val group = Spine.toolsGroup private const val prefix = "validation" diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/Cli.kt b/buildSrc/src/main/kotlin/io/spine/gradle/Cli.kt index 0e43f3f71f..36ba14ebe3 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/Cli.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/Cli.kt @@ -20,7 +20,6 @@ import java.io.File import java.io.InputStream import java.io.StringWriter import java.lang.ProcessBuilder.Redirect.PIPE -import java.util.* /** * Executes a process from Gradle code. @@ -32,20 +31,34 @@ class Cli(private val workingFolder: File) { /** * Executes the given terminal command and retrieves the command output. * - * [Executes][Runtime.exec] the given `String` array as a CLI command. + * Runs the given command, the program followed by its arguments, in + * the [workingFolder]. * * If the execution is successful, returns the command output. * Throws an [IllegalStateException] otherwise. * * @param command the command to execute. - * @return the command line output. + * @return the standard output as the command wrote it, line breaks included, + * decoded as UTF-8. * @throws IllegalStateException if the execution fails. */ - fun execute(vararg command: String): String { + fun execute(vararg command: String): String = execute(command.asList()) + + /** + * Executes the given terminal command and retrieves the command output. + * + * Works like the `vararg` overload, for a command assembled as a list. + * + * @param command the command to execute, the program followed by its arguments. + * @return the standard output as the command wrote it, line breaks included, + * decoded as UTF-8. + * @throws IllegalStateException if the execution fails. + */ + fun execute(command: List): String { val outWriter = StringWriter() val errWriter = StringWriter() - val process = ProcessBuilder(*command).apply { + val process = ProcessBuilder(command).apply { directory(workingFolder) redirectOutput(PIPE) redirectError(PIPE) @@ -66,22 +79,22 @@ class Cli(private val workingFolder: File) { val commandLine = command.joinToString(" ") val nl = System.lineSeparator() val errorMsg = "Command `$commandLine` finished with exit code $exitCode:" + - "$nl$errWriter" + - "$nl$outWriter." + "$nl${errWriter.toString().trimEnd()}" + + "$nl${outWriter.toString().trimEnd()}." throw IllegalStateException(errorMsg) } } } /** - * Starts a background thread that reads all lines from this [InputStream] and - * appends them to [dest], returning the thread so the caller can [join][Thread.join] - * it once the process has exited, ensuring the buffer holds the complete output. + * Starts a background thread that reads all of this [InputStream] and appends it + * to [dest], returning the thread so the caller can [join][Thread.join] it once + * the process has exited, ensuring the buffer holds the complete output. + * + * The text is appended as the process wrote it, line breaks included, so that + * a multi-line output can be split into lines. */ private fun InputStream.pourTo(dest: StringWriter): Thread = Thread { - val sc = Scanner(this) - while (sc.hasNextLine()) { - dest.append(sc.nextLine()) - } + bufferedReader().use { dest.append(it.readText()) } }.also { it.start() } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/ProjectExtensions.kt b/buildSrc/src/main/kotlin/io/spine/gradle/ProjectExtensions.kt index 41cac88758..5c16bd5661 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/ProjectExtensions.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/ProjectExtensions.kt @@ -59,7 +59,11 @@ fun Project.applyPlugin(cls: Class>) { * * The task must be present. Also, a caller is responsible for using the proper value of * the generic parameter `T`. + * + * Gradle creates the task if it is only registered so far, even in a build that never + * runs it. Prefer `tasks.named(name)`, which finds the task without creating it. */ +@Deprecated(message = "Please use `tasks.named(name)`, which does not create the task.") @Suppress("UNCHECKED_CAST") /* See the method docs. */ fun Project.getTask(name: String): T { val task = this.tasks.findByName(name) diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/TaskName.kt b/buildSrc/src/main/kotlin/io/spine/gradle/TaskName.kt index 0a595b3b40..763b9832b2 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/TaskName.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/TaskName.kt @@ -17,6 +17,7 @@ package io.spine.gradle import kotlin.reflect.KClass import org.gradle.api.Task import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider import org.gradle.kotlin.dsl.named import org.gradle.kotlin.dsl.register @@ -45,3 +46,22 @@ internal fun TaskContainer.named(name: TaskName) = named(name.valu */ internal fun TaskContainer.register(name: TaskName, init: T.() -> Unit) = register(name.value, name.clazz, init) + +/** + * Returns the task named [name] in this container, registering it with [init] + * if it is not there yet. + * + * Unlike [register][TaskContainer.register], this does not fail when the task is + * already registered, so it suits code that may reach the same task more than once. + * [init] applies only when this call registers the task; for an existing task it is + * ignored, so a setup needed in both cases goes to [TaskProvider.configure]. + */ +internal fun TaskContainer.getOrRegister( + name: String, + init: Task.() -> Unit = {} +): TaskProvider = + if (name in names) { + named(name) + } else { + register(name, init) + } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/checkstyle/CheckStyleConfig.kt b/buildSrc/src/main/kotlin/io/spine/gradle/checkstyle/CheckStyleConfig.kt index b8232683fa..79351fbf16 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/checkstyle/CheckStyleConfig.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/checkstyle/CheckStyleConfig.kt @@ -64,7 +64,7 @@ object CheckStyleConfig { "checkstyleTest", "checkstyleTestFixtures" ).forEach { - task -> tasks.findByName(task)?.enabled = false + task -> tasks.named { it == task }.configureEach { enabled = false } } } } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/git/Repository.kt b/buildSrc/src/main/kotlin/io/spine/gradle/git/Repository.kt index 1b7e09abf9..6630f985f2 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/git/Repository.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/git/Repository.kt @@ -61,14 +61,33 @@ class Repository private constructor( repoExecute("git", "clone", sshUrl, ".") } + /** + * Clones the tip of the [branch], or of the default branch if it is `null`, + * into the [temporary folder][location]. + * + * The blobs are fetched only when the working tree needs them, and the working + * tree holds only the top-level files until [checkOutOnly] replaces them with + * the selected paths. + */ + private fun cloneSparsely(branch: String?) { + val options = listOf("--depth", "1", "--filter=blob:none", "--sparse", "--single-branch") + val branchOption = branch?.let { listOf("--branch", it) } ?: emptyList() + repoExecute(listOf("git", "clone") + options + branchOption + listOf("--", sshUrl, ".")) + } + /** * Executes a command in the [location]. */ - private fun repoExecute(vararg command: String): String { - val cmd = command.toList().joinToString(" ") + private fun repoExecute(vararg command: String): String = repoExecute(command.asList()) + + /** + * Executes a command, assembled as a list, in the [location]. + */ + private fun repoExecute(command: List): String { + val cmd = command.joinToString(" ") val msg = "[Repo (${project.path})] Executing command: `$cmd`." System.err.println(msg) - return Cli(location.toFile()).execute(*command) + return Cli(location.toFile()).execute(command) } /** @@ -125,12 +144,59 @@ class Repository private constructor( * match a namespaced branch such as `feature/$name`. Relies on `git ls-remote` * returning an empty output with a zero exit code when the branch is absent, * so the check does not raise an exception. + * + * @param name the name of the branch. + * @param remote the name of the remote or its URL; `origin` by default. */ - private fun remoteHasBranch(name: String): Boolean { - val output = repoExecute("git", "ls-remote", "--heads", "origin", "refs/heads/$name") + private fun remoteHasBranch(name: String, remote: String = "origin"): Boolean { + val output = repoExecute("git", "ls-remote", "--heads", remote, "refs/heads/$name") return output.isNotBlank() } + /** + * Checks out only the paths of the current branch that match the given [patterns], + * leaving the rest of the branch out of the working tree. + * + * The call replaces the paths checked out before, including the top-level files + * of a [sparse clone][Factory.cloneSparsely], so it has to be given every path + * to keep at once. + * + * The patterns follow the `.gitignore` syntax, so a folder can be checked out + * without some of its subfolders. The paths left out stay in the branch: committing + * [all changes][commitAllChanges] does not delete them. + * + * Leaving out some subfolders of a folder takes the non-cone mode of + * `git sparse-checkout`, which Git documents as deprecated, though Git 2.54 still + * supports it without a warning. The mode needs Git 2.35 or newer. + */ + fun checkOutOnly(patterns: List) { + repoExecute(listOf("git", "sparse-checkout", "set", "--no-cone") + patterns) + } + + /** + * Obtains the names of the folders in the given [folder] of the current commit, + * whether they are checked out or not. + * + * Reads the tree of the current commit, so the changes in the working tree that + * are not committed yet are not reflected. The files in the [folder] are skipped. + * + * @param folder the path of the folder relative to the repository root. + * @return the names of the folders in [folder], or an empty list if there is + * no such folder. + */ + fun listFolders(folder: String): List { + require(folder.isNotBlank()) { "The folder to list cannot be blank." } + // Each entry is ` \t`, the entries are separated + // by `NUL`, and the paths are not quoted. + return repoExecute("git", "ls-tree", "-z", "HEAD", "${folder.trimEnd('/')}/") + .split('\u0000') + .filter { it.isNotBlank() } + .mapNotNull { entry -> + val (info, path) = entry.split('\t', limit = 2) + path.substringAfterLast('/').takeIf { info.split(' ')[1] == "tree" } + } + } + /** * Creates the [branch] as an orphan branch seeded with [initialFiles] and * pushes it to the remote. @@ -173,7 +239,6 @@ class Repository private constructor( // If the branch now exists on the remote, another module won the // creation race and we adopt its branch; otherwise the failure is // genuine and is rethrown. - repoExecute("git", "fetch", "origin") if (!remoteHasBranch(branch)) { throw e } @@ -184,8 +249,14 @@ class Repository private constructor( /** * Discards the local orphan branch in favour of the same-named branch that * already exists on the remote, keeping the local branch in sync with it. + * + * The branch is added to the branches fetched from the remote first: a single-branch + * clone, which a shallow one is too, fetches no other branch, and Git does not + * accept a branch it does not fetch as the upstream of a local one. */ private fun adoptRemoteBranch(branch: String) { + repoExecute("git", "remote", "set-branches", "--add", "origin", branch) + repoExecute("git", "fetch", "origin", "+refs/heads/$branch:refs/remotes/origin/$branch") repoExecute("git", "reset", "--hard", "origin/$branch") repoExecute("git", "branch", "--set-upstream-to=origin/$branch", branch) } @@ -274,6 +345,53 @@ class Repository private constructor( return repo } + + /** + * Clones the tip of the [branch] of the repository with the provided SSH URL + * in a temporary folder, with only its top-level files checked out. + * + * Suits a branch with a large tip of which a change touches only a small part, + * such as the branch with the documentation of every published version. + * Further blobs are fetched only for the paths that [checkOutOnly] selects. + * The commits are made and [pushed][push] as in a full clone. + * + * A [branch] that does not exist yet is created and seeded with [initialFiles], + * which are top-level files. + * + * Configures the username and the email of the Git user. + * See [configureUser] documentation for more information. + * + * @throws IllegalArgumentException if SSH URL is a blank string. + */ + fun cloneSparsely( + project: Project, + sshUrl: String, + user: UserInfo, + branch: String, + initialFiles: Map = emptyMap(), + ): Repository { + require(sshUrl.isNotBlank()) { "SSH URL cannot be an empty string." } + + val repo = Repository(project, sshUrl = sshUrl, user = user, currentBranch = branch) + var cloned = false + try { + // Ask the remote first: an existing branch is cloned alone, while + // a missing one needs the default branch to start from. + val branchExists = repo.remoteHasBranch(branch, remote = sshUrl) + repo.cloneSparsely(branch.takeIf { branchExists }) + repo.configureUser(user) + if (!branchExists) { + repo.createOrphanBranch(branch, initialFiles) + } + cloned = true + return repo + } finally { + // The caller cannot close a repository it never gets. + if (!cloned) { + repo.close() + } + } + } } } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/AuthorEmail.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/AuthorEmail.kt index 39d16ac8fd..cec12afdc9 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/AuthorEmail.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/AuthorEmail.kt @@ -15,7 +15,8 @@ package io.spine.gradle.github.pages /** - * An author of updates to GitHub pages. + * The email of the author of the commits that publish the API documentation + * to GitHub Pages. */ class AuthorEmail(val value: String) { diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/DocsPublication.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/DocsPublication.kt new file mode 100644 index 0000000000..11f37da3b0 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/DocsPublication.kt @@ -0,0 +1,254 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.gradle.github.pages + +import io.spine.gradle.VersionComparator +import io.spine.gradle.git.Repository +import java.io.File +import java.nio.file.Path +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.FileCollection +import org.gradle.api.logging.Logger + +/** + * Publishes the API documentation of the given [project] to GitHub Pages. + * + * The documentation in each format is copied into a clone of the `gh-pages` branch + * and committed there, and the commits are pushed. + * + * The clone has only the most recent documentation of the [project] and the folder of + * its version checked out, without the folders of the versions published earlier. + * The branch holds the documentation of every version of every module, which is too + * much to check out for a change to a single module. + */ +internal fun Task.publishToGitHubPages(project: Project) { + val plugin = project.plugins.getPlugin(PublishApiDocs::class.java) + + SshKey(plugin.rootFolder, logger).register() + + val repository = Repository.forPublishingDocumentation(project) + + val javadoc = JavadocPublication(project, plugin.javadocOutputFolder, repository, logger) + val html = HtmlPublication(project, plugin.htmlOutputFolder, repository, logger) + + repository.use { + repository.checkOutOnly(javadoc.checkoutPatterns() + html.checkoutPatterns()) + javadoc.publish() + html.publish() + repository.push() + } +} + +/** + * Publishes the documentation of a [project] in one format to the clone of + * the [repository] serving GitHub Pages. + * + * The documentation goes to the folder of its version, and also replaces the most + * recent documentation of the project, unless a newer version is already published. + */ +private abstract class DocsPublication( + private val project: Project, + private val docsSourceFolder: Path, + private val repository: Repository, + private val logger: Logger +) { + + /** + * The folder under the repository's root(`/`) for storing documentation. + * + * The value should not contain any leading or trailing file separators. + * + * The absolute path to the project's documentation is made by appending its + * name to the end, making `/docsDestinationFolder/project.name`. + */ + protected abstract val docsDestinationFolder: String + + /** + * The name of the format of the documentation to publish. + * + * This name will appear in logs as part of a message. + */ + protected abstract val formatName: String + + /** + * The path of the folder with the documentation of the [project] in this format, + * relative to the repository root. + */ + private val folder: String + get() = "$docsDestinationFolder/${project.name}" + + private val mostRecentFolder by lazy { + File("${repository.location}/$folder") + } + + /** + * The folder holding the documentation of each published version in a subfolder + * named after the version. + */ + private val versionsFolder by lazy { + File(mostRecentFolder, VERSIONS_FOLDER) + } + + private fun log(message: () -> String) { + if (logger.isDebugEnabled) { + logger.debug(message()) + } + } + + /** + * Obtains the patterns of the paths this publication writes, for + * [checking out][Repository.checkOutOnly] just those paths. + */ + fun checkoutPatterns(): List = + docsCheckoutPatterns(folder, project.version.toString()) + + /** + * Copies the documentation into the repository and commits it. + */ + fun publish() { + val module = project.name + log { "Publishing the `$formatName` documentation of the module `$module`." } + + val documentation = replaceMostRecentDocs() + copyIntoVersionDir(documentation) + + val version = project.version + val commitMessage = + "Publish the `$formatName` documentation of the module" + + " `$module` for the version `$version`." + repository.commitAllChanges(commitMessage) + + log { "Published the `$formatName` documentation of the module `$module`." } + } + + /** + * Replaces the most recent documentation with the one of this version, unless + * the documentation of a newer version is already published. + * + * The runs of `publish-api-docs.yml` are serialized, but not necessarily in the + * order of their commits, and a release of an older line may come after a newer + * one. Either way, the most recent documentation stays that of the newest version. + */ + private fun replaceMostRecentDocs(): ConfigurableFileCollection { + val generatedDocs = project.files(docsSourceFolder) + + val version = project.version.toString() + val newer = publishedVersions().newestAbove(version) + if (newer != null) { + logger.lifecycle( + "The most recent `$formatName` documentation of `${project.name}` stays" + + " that of `$newer`, which is newer than `$version`." + ) + return generatedDocs + } + + log { "Replacing the most recent `$formatName` documentation in `$mostRecentFolder`." } + copyDocs(generatedDocs, mostRecentFolder) + + return generatedDocs + } + + /** + * Obtains the versions whose documentation is published, as named by the folders + * under `v/`. + * + * The names are read from the tree of the current commit, as the folders of + * the other versions are not checked out. + */ + private fun publishedVersions(): List = + repository.listFolders("$folder/$VERSIONS_FOLDER") + + private fun copyDocs(source: FileCollection, destination: File) { + destination.mkdirs() + project.copy { + from(source) + into(destination) + } + } + + private fun copyIntoVersionDir(generatedDocs: ConfigurableFileCollection) { + val versionedDocDir = File(versionsFolder, "${project.version}") + + log { "Storing the new version of `$formatName` documentation in `${versionedDocDir}`." } + copyDocs(generatedDocs, versionedDocDir) + } +} + +private class JavadocPublication( + project: Project, + docsSourceFolder: Path, + repository: Repository, + logger: Logger +) : DocsPublication(project, docsSourceFolder, repository, logger) { + + override val docsDestinationFolder: String + get() = "javadoc" + override val formatName: String + get() = "javadoc" +} + +private class HtmlPublication( + project: Project, + docsSourceFolder: Path, + repository: Repository, + logger: Logger +) : DocsPublication(project, docsSourceFolder, repository, logger) { + + override val docsDestinationFolder: String + get() = "reference" + override val formatName: String + get() = "html" +} + +/** + * Returns the newest of these versions if it is newer than the given [version], + * or `null` otherwise. + * + * The versions are compared by [VersionComparator], so that `2.10.0` is newer than + * `2.9.0`, and a release is newer than the snapshots leading to it. + */ +internal fun Iterable.newestAbove(version: String): String? = + maxWithOrNull(VersionComparator)?.takeIf { VersionComparator.compare(it, version) > 0 } + +/** + * The name of the folder that holds the documentation of each published version + * in a subfolder named after the version. + */ +private const val VERSIONS_FOLDER = "v" + +/** + * Returns the patterns, in the `.gitignore` syntax, of the paths that a publication + * of the documentation in the given [folder] writes: the most recent documentation, + * and the folder of the given [version] under `v/`, but not the folders of the other + * versions. + * + * The folders under `v/` are excluded one by one, and the folder of the [version] + * is included again after them, as the last matching pattern wins. This is how + * `git sparse-checkout` lays out its patterns in cone mode. + * + * For example, for the `javadoc/core` folder and the version `1.2.0`, the patterns + * select everything in `javadoc/core`, exclude each folder in `javadoc/core/v`, and + * select `javadoc/core/v/1.2.0` again. + * + * @param folder the path of the documentation folder, relative to the repository root. + * @param version the version whose folder is included. + */ +internal fun docsCheckoutPatterns(folder: String, version: String): List = listOf( + "/$folder/*", + "!/$folder/$VERSIONS_FOLDER/*/", + "/$folder/$VERSIONS_FOLDER/$version/", +) diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishAllApiDocs.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishAllApiDocs.kt new file mode 100644 index 0000000000..e610c5861e --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishAllApiDocs.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.gradle.github.pages + +import io.spine.gradle.SpineTaskGroup +import io.spine.gradle.getOrRegister +import io.spine.gradle.github.pages.TaskName.publishAllApiDocs +import io.spine.gradle.github.pages.TaskName.publishApiDocs +import org.gradle.api.Project + +/** + * Registers the [TaskName.publishAllApiDocs] task in the root project, which publishes + * the API documentation of all the published projects of the build. + * + * Usage: + * ``` + * PublishAllApiDocs.registerIn(project.rootProject, projectsToPublish) + * ``` + * + * `spinePublishing { }` makes this call, as it knows which projects are published. + * The task is not a part of the publication to Maven repositories: the + * `publish-api-docs.yml` workflow runs it apart from `publish`. + * + * The task is a lifecycle one: it has no action of its own, and publishes + * the documentation through the [TaskName.publishApiDocs] tasks it depends on, + * which the [PublishApiDocs] plugin registers in each documented project. + */ +internal object PublishAllApiDocs { + + /** + * Registers the [TaskName.publishAllApiDocs] task in the [root] project. + * The task publishes the API documentation of the [published] projects. + * + * The task depends on the [TaskName.publishApiDocs] task of each of + * the [published] projects that has one, that is, applies the [PublishApiDocs] + * plugin. This includes [root] when it is published itself, as in a single-project + * build. A project that applies the plugin, but is not published, such as a module + * of integration tests, is left out. The task of a project with a snapshot version + * publishes nothing. + * + * The task is registered even when none of the projects has the documentation + * to publish, and then does nothing. This lets the `publish-api-docs.yml` + * workflow run it in any repository that publishes: Gradle fails on a task + * that is missing from the build. + * + * The plugin registers the task of a project once that project is evaluated, + * so the tasks to depend on are looked up only when Gradle resolves + * the dependencies of this task. Repeated calls add their projects to the same + * task, as `spinePublishing { }` may run more than once in a build. + */ + fun registerIn(root: Project, published: Set) { + val task = root.tasks.getOrRegister(publishAllApiDocs) { + group = SpineTaskGroup.name + description = "Publishes the API documentation of all the published modules" + } + task.configure { + dependsOn(root.provider { + published + .filter { publishApiDocs in it.tasks.names } + .map { it.tasks.named(publishApiDocs) } + }) + } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPages.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishApiDocs.kt similarity index 65% rename from buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPages.kt rename to buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishApiDocs.kt index e4960aaf71..f4ca18bc31 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPages.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishApiDocs.kt @@ -15,12 +15,12 @@ package io.spine.gradle.github.pages import dokkaHtmlTask -import dokkaJavadocTask import io.spine.gradle.SpineTaskGroup import io.spine.gradle.fs.LazyTempPath import io.spine.gradle.github.pages.TaskName.copyHtmlDocs import io.spine.gradle.github.pages.TaskName.copyJavadocDocs -import io.spine.gradle.github.pages.TaskName.updateGitHubPages +import io.spine.gradle.github.pages.TaskName.publishApiDocs +import io.spine.gradle.isSnapshot import java.io.File import org.gradle.api.Plugin import org.gradle.api.Project @@ -30,12 +30,18 @@ import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider /** - * Registers the `updateGitHubPages` task that performs the update of - * the GitHub Pages with the documentation generated in Javadoc and HTML format - * for a particular Gradle project. + * Registers the `publishApiDocs` task that publishes the API documentation + * of a particular Gradle project, generated in Javadoc and HTML format. * * The generated documentation is appended to the `spine.io` site - * via GitHub pages by pushing commits to the `gh-pages` branch. + * via GitHub Pages by pushing commits to the `gh-pages` branch. + * The documentation of a snapshot version is not published to the site. Its Javadoc and + * HTML archives are still built by Dokka and published to Maven repositories by `publish`. + * + * The task is not a part of the Maven publication. It is run by the `publish-api-docs.yml` + * workflow, apart from `publish.yml`, through the `publishAllApiDocs` task of the root + * project, which [publishes the documentation of the published modules that apply + * this plugin][PublishAllApiDocs]. * * In order to work, the script needs a `deploy_key_rsa` private RSA key file in the * repository root. It is recommended to encrypt it in the repository and then decrypt @@ -47,18 +53,18 @@ import org.gradle.api.tasks.TaskProvider * the SSH private (encrypted `deploy_key_rsa`) and the public * ("GitHub Pages publisher" on GitHub) keys. * - * Another requirement is an environment variable `REPO_SLUG`, which is set by the CI - * environment, such as `Publish` GitHub Actions workflow. It points to the repository - * for which the update is executed. E.g.: + * Another requirement is an environment variable `REPO_SLUG`, which a CI workflow sets, + * such as the `Publish API docs` GitHub Actions workflow. It points to + * the repository for which the documentation is published. E.g.: * * ``` * REPO_SLUG: SpineEventEngine/base * ``` * - * @see UpdateGitHubPagesExtension for the extension that is used to configure + * @see PublishApiDocsExtension for the extension that is used to configure * this plugin */ -class UpdateGitHubPages : Plugin { +class PublishApiDocs : Plugin { /** * Root folder of the repository, to which this `Project` belongs. @@ -74,13 +80,13 @@ class UpdateGitHubPages : Plugin { /** * Path to the temp folder used to gather the Javadoc format output (generated by Dokka) - * before submitting it to the GitHub Pages update. + * before publishing it. */ internal val javadocOutputFolder = LazyTempPath("javadoc") /** * Path to the temp folder used to gather the HTML documentation - * generated by Dokka before submitting it to the GitHub Pages update. + * generated by Dokka before publishing it. */ internal val htmlOutputFolder = LazyTempPath("html") @@ -94,41 +100,45 @@ class UpdateGitHubPages : Plugin { * when the project version changes to non-snapshot. */ override fun apply(project: Project) { - val extension = UpdateGitHubPagesExtension.createIn(project) + val extension = PublishApiDocsExtension.createIn(project) project.afterEvaluate { - registerTasks(extension) + if (version.toString().isSnapshot()) { + registerNoOpTask() + } else { + registerTasks(extension) + } } } /** - * Registers the `updateGitHubPages` task that performs no actual update, but prints - * the message telling the update is skipped, since the project is in - * its `SNAPSHOT` version. + * Registers the [TaskName.publishApiDocs] task that publishes nothing, but tells + * that the documentation is not published, since the project is in its `SNAPSHOT` + * version. */ - @Suppress("unused") private fun Project.registerNoOpTask() { - tasks.register(updateGitHubPages) { + val module = name + val snapshotVersion = version.toString() + tasks.register(publishApiDocs) { group = SpineTaskGroup.name - description = "Skips the GitHub Pages update for snapshot project versions" + description = "Skips publishing the API documentation of snapshot versions" doLast { - val project = this@registerNoOpTask - println( - "GitHub Pages update will be skipped since this project is a snapshot: " + - "`${project.name}-${project.version}`." + logger.lifecycle( + "The API documentation of `$module` is not published, since" + + " `$snapshotVersion` is a snapshot version." ) } } } - private fun Project.registerTasks(extension: UpdateGitHubPagesExtension) { + private fun Project.registerTasks(extension: PublishApiDocsExtension) { rootFolder = extension.rootFolder() includedInputs = extension.includedInputs() tasks.registerCopyJavadoc() tasks.registerCopyDokka() - val updatePagesTask = tasks.registerUpdateTask() - updatePagesTask.configure { + val publishTask = tasks.registerPublishTask() + publishTask.configure { dependsOn(copyJavadocDocs) dependsOn(copyHtmlDocs) } @@ -147,7 +157,8 @@ class UpdateGitHubPages : Plugin { private fun TaskContainer.composeJavadocInputs(): List { val inputs = mutableListOf() - inputs.add(dokkaJavadocTask()!!) + // Unlike the HTML docs, the Javadoc is required, so a missing task fails the build. + inputs.add(named("dokkaGeneratePublicationJavadoc")) inputs.addAll(includedInputs) return inputs } @@ -165,22 +176,19 @@ class UpdateGitHubPages : Plugin { private fun TaskContainer.composeDokkaInputs(): List { val inputs = mutableListOf() - - dokkaHtmlTask()?.let { - inputs.add(it) - } + inputs.add(dokkaHtmlTask()) inputs.addAll(includedInputs) return inputs } - private fun TaskContainer.registerUpdateTask(): TaskProvider { - return register(updateGitHubPages) { + private fun TaskContainer.registerPublishTask(): TaskProvider { + return register(publishApiDocs) { group = SpineTaskGroup.name - description = "Publishes the generated documentation to the `gh-pages` branch" + description = "Publishes the API documentation to the `gh-pages` branch" doLast { try { - updateGhPages(project) + publishToGitHubPages(project) } finally { cleanup() } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPagesExtension.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishApiDocsExtension.kt similarity index 70% rename from buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPagesExtension.kt rename to buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishApiDocsExtension.kt index 5a7b1a88f0..fbf0d9bc96 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPagesExtension.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishApiDocsExtension.kt @@ -23,44 +23,44 @@ import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.property /** - * Configures the `updateGitHubPages` extension. + * Configures the `publishApiDocs` extension. */ @Suppress("unused") -fun Project.updateGitHubPages( - action: UpdateGitHubPagesExtension.() -> Unit +fun Project.publishApiDocs( + action: PublishApiDocsExtension.() -> Unit ) { - apply() + apply() - val extension = extensions.getByType(UpdateGitHubPagesExtension::class) + val extension = extensions.getByType(PublishApiDocsExtension::class) extension.action() } /** - * The extension for configuring the [UpdateGitHubPages] plugin. + * The extension for configuring the [PublishApiDocs] plugin. * - * @property rootFolder The root folder of the repository to which the updated `Project` belongs. + * @property rootFolder The root folder of the repository to which the documented `Project` belongs. * @property includeInputs The external inputs, which output should be included - * into the GitHub Pages update. The values are interpreted according to + * into the published documentation. The values are interpreted according to * [Copy.from][org.gradle.api.tasks.Copy.from] specification. * This property is optional. */ -class UpdateGitHubPagesExtension private constructor( - var rootFolder: Property, - var includeInputs: SetProperty +class PublishApiDocsExtension private constructor( + val rootFolder: Property, + val includeInputs: SetProperty ) { internal companion object { /** * The name of the extension. */ - const val name = "updateGitHubPages" + const val name = "publishApiDocs" /** * Creates a new extension and adds it to the passed project. */ - fun createIn(project: Project): UpdateGitHubPagesExtension { + fun createIn(project: Project): PublishApiDocsExtension { val factory = project.objects - val result = UpdateGitHubPagesExtension( + val result = PublishApiDocsExtension( rootFolder = factory.property(File::class), includeInputs = factory.setProperty(Any::class.java) ) @@ -76,8 +76,8 @@ class UpdateGitHubPagesExtension private constructor( fun rootFolder(): File = rootFolder.get() /** - * Returns the external inputs, which results should be included into the - * GitHub Pages update. + * Returns the external inputs, whose results should be included into the + * published documentation. */ fun includedInputs(): Set = includeInputs.get() } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/RepositoryExtensions.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/RepositoryExtensions.kt index 0be661d2e6..8a704cb949 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/RepositoryExtensions.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/RepositoryExtensions.kt @@ -21,17 +21,19 @@ import io.spine.gradle.repo.RepoSlug import org.gradle.api.Project /** - * Clones the current project repository with the branch dedicated to publishing - * documentation to GitHub Pages checked out. + * Clones the branch of the current project repository dedicated to publishing + * documentation to GitHub Pages. * * The repository's GitHub SSH URL is derived from the `REPO_SLUG` environment * variable. The [branch][Branch.documentation] dedicated to publishing documentation - * is automatically checked out in this repository, and created if it does not exist - * yet. A freshly created branch is seeded with a `CNAME` file so that GitHub Pages - * serves the documentation under the `spine.io` custom domain. Also, the username - * and the email of the git user are automatically configured. + * is cloned [sparsely][Repository.Factory.cloneSparsely]: its tip holds the documentation + * of every published version of every module, while a publication touches the folders + * of one module only; the caller [checks them out][Repository.checkOutOnly]. The branch + * is created if it does not exist yet. A freshly created branch is seeded with a `CNAME` + * file so that GitHub Pages serves the documentation under the `spine.io` custom domain. + * Also, the username and the email of the Git user are automatically configured. * - * The username is set to `"UpdateGitHubPages Plugin"`, and the email is derived from + * The username is set to `"PublishApiDocs Plugin"`, and the email is derived from * the `FORMAL_GIT_HUB_PAGES_AUTHOR` environment variable. * * @throws org.gradle.api.GradleException if any of the environment variables described above @@ -40,7 +42,7 @@ import org.gradle.api.Project internal fun Repository.Factory.forPublishingDocumentation(project: Project): Repository { val host = RepoSlug.fromVar().gitHost() - val username = "UpdateGitHubPages Plugin" + val username = "PublishApiDocs Plugin" val userEmail = AuthorEmail.fromVar().toString() val user = UserInfo(username, userEmail) @@ -51,5 +53,11 @@ internal fun Repository.Factory.forPublishingDocumentation(project: Project): Re // custom domain. val initialFiles = mapOf("CNAME" to "spine.io\n") - return clone(project, host, user, branch, initialFiles) + return cloneSparsely( + project, + sshUrl = host, + user = user, + branch = branch, + initialFiles = initialFiles + ) } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/TaskName.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/TaskName.kt index cc453f0bf8..2d7d20de25 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/TaskName.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/TaskName.kt @@ -17,19 +17,25 @@ package io.spine.gradle.github.pages object TaskName { /** - * The name of the task that updates the GitHub Pages. + * The name of the task that publishes the API documentation of a project. */ - const val updateGitHubPages = "updateGitHubPages" + const val publishApiDocs = "publishApiDocs" /** - * The name of the helper task to gather the generated Javadoc format - * documentation generated by Dokka before updating GitHub Pages. + * The name of the task of the root project that publishes the API documentation + * of all the published projects of the build. + */ + const val publishAllApiDocs = "publishAllApiDocs" + + /** + * The name of the helper task to gather the Javadoc format documentation + * generated by Dokka before publishing it to GitHub Pages. */ const val copyJavadocDocs = "copyJavadocDocs" /** * The name of the helper task to gather HTML documentation - * generated by Dokka before updating GitHub Pages. + * generated by Dokka before publishing it to GitHub Pages. */ const val copyHtmlDocs = "copyHtmlDocs" } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/Update.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/Update.kt deleted file mode 100644 index 29896d159f..0000000000 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/Update.kt +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2026 CodeMatters, Lda. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under - * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - * either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ - -package io.spine.gradle.github.pages - -import io.spine.gradle.git.Repository -import java.io.File -import java.nio.file.Path -import org.gradle.api.Project -import org.gradle.api.Task -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.FileCollection -import org.gradle.api.logging.Logger - -/** - * Performs the update of GitHub pages. - */ -fun Task.updateGhPages(project: Project) { - val plugin = project.plugins.getPlugin(UpdateGitHubPages::class.java) - - SshKey(plugin.rootFolder, logger).register() - - val repository = Repository.forPublishingDocumentation(project) - - val updateJavadocFormat = - UpdateJavadocFormat(project, plugin.javadocOutputFolder, repository, logger) - - val updateHtmlFormat = - UpdateHtmlFormat(project, plugin.htmlOutputFolder, repository, logger) - - repository.use { - updateJavadocFormat.run() - updateHtmlFormat.run() - repository.push() - } -} - -private abstract class UpdateDocumentation( - private val project: Project, - private val docsSourceFolder: Path, - private val repository: Repository, - private val logger: Logger -) { - - /** - * The folder under the repository's root(`/`) for storing documentation. - * - * The value should not contain any leading or trailing file separators. - * - * The absolute path to the project's documentation is made by appending its - * name to the end, making `/docsDestinationFolder/project.name`. - */ - protected abstract val docsDestinationFolder: String - - /** - * The name of the format of the documentation to update. - * - * This name will appear in logs as part of a message. - */ - protected abstract val formatName: String - - private val mostRecentFolder by lazy { - File("${repository.location}/${docsDestinationFolder}/${project.name}") - } - - private fun log(message: () -> String) { - if (logger.isDebugEnabled) { - logger.debug(message()) - } - } - - fun run() { - val module = project.name - log { "Update of the `$formatName` documentation for the module `$module` started." } - - val documentation = replaceMostRecentDocs() - copyIntoVersionDir(documentation) - - val version = project.version - val updateMessage = - "Update `$formatName` documentation for the module" + - " `$module` with the version `$version`." - repository.commitAllChanges(updateMessage) - - log { "Update of the `$formatName` documentation for `$module` successfully finished." } - } - - private fun replaceMostRecentDocs(): ConfigurableFileCollection { - val generatedDocs = project.files(docsSourceFolder) - - log { "Replacing the most recent `$formatName` documentation in `$mostRecentFolder`." } - copyDocs(generatedDocs, mostRecentFolder) - - return generatedDocs - } - - private fun copyDocs(source: FileCollection, destination: File) { - destination.mkdir() - project.copy { - from(source) - into(destination) - } - } - - private fun copyIntoVersionDir(generatedDocs: ConfigurableFileCollection) { - val versionedDocDir = File("$mostRecentFolder/v/${project.version}") - - log { "Storing the new version of `$formatName` documentation in `${versionedDocDir}`." } - copyDocs(generatedDocs, versionedDocDir) - } -} - -private class UpdateJavadocFormat( - project: Project, - docsSourceFolder: Path, - repository: Repository, - logger: Logger -) : UpdateDocumentation(project, docsSourceFolder, repository, logger) { - - override val docsDestinationFolder: String - get() = "javadoc" - override val formatName: String - get() = "javadoc" -} - -private class UpdateHtmlFormat( - project: Project, - docsSourceFolder: Path, - repository: Repository, - logger: Logger -) : UpdateDocumentation(project, docsSourceFolder, repository, logger) { - - override val docsDestinationFolder: String - get() = "reference" - override val formatName: String - get() = "html" -} diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/ExcludeInternalDoclet.kt b/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/ExcludeInternalDoclet.kt index 825d3254fc..d346328537 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/ExcludeInternalDoclet.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/ExcludeInternalDoclet.kt @@ -89,7 +89,7 @@ private fun Project.appendCustomJavadocTask(excludeInternalDoclet: Configuration !it.absolutePath.contains("generated") }.asFileTree - classpath = javadocTask.classpath + classpath = javadocTask.get().classpath options { encoding = JavadocConfig.encoding.name diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/JavadocConfig.kt b/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/JavadocConfig.kt index cb6998de2f..35ff1fbc3a 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/JavadocConfig.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/JavadocConfig.kt @@ -19,19 +19,25 @@ import java.io.File import org.gradle.api.JavaVersion import org.gradle.api.Project import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.javadoc.Javadoc import org.gradle.external.javadoc.StandardJavadocDocletOptions import productionModules /** - * Finds a [Javadoc] Gradle task by the passed name. + * Finds a [Javadoc] Gradle task by the passed name without creating it. + * + * The task must be registered. */ -fun TaskContainer.javadocTask(named: String) = this.getByName(named) as Javadoc +fun TaskContainer.javadocTask(named: String): TaskProvider = + this.named(named, Javadoc::class.java) /** - * Finds a default [Javadoc] Gradle task. + * Finds a default [Javadoc] Gradle task without creating it. + * + * The task must be registered. */ -fun TaskContainer.javadocTask() = this.getByName("javadoc") as Javadoc +fun TaskContainer.javadocTask(): TaskProvider = javadocTask("javadoc") /** * Javadoc processing settings. @@ -57,15 +63,23 @@ object JavadocConfig { val encoding = Encoding("UTF-8") + /** + * Applies the Javadoc settings to the `javadoc` task of the given [project], + * or disables the task if the project is not a production module. + * + * The task must be registered. It is configured when Gradle creates it, + * so calling this function does not create the task. + */ fun applyTo(project: Project) { - val javadocTask = project.tasks.javadocTask() - if (!isProductionModule(project)) { - javadocTask.enabled = false - return + project.tasks.javadocTask().configure { + if (!isProductionModule(project)) { + enabled = false + return@configure + } + discardJavaModulesInLinks(this) + val docletOptions = options as StandardJavadocDocletOptions + configureDoclet(docletOptions) } - discardJavaModulesInLinks(javadocTask) - val docletOptions = javadocTask.options as StandardJavadocDocletOptions - configureDoclet(docletOptions) } private fun isProductionModule(project: Project) = project.run { diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/ProtoExts.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/ProtoExts.kt index fd5dba83fc..0ade0727b7 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/ProtoExts.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/ProtoExts.kt @@ -20,6 +20,7 @@ import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.file.FileTreeElement import org.gradle.api.file.SourceDirectorySet +import org.gradle.api.tasks.TaskCollection import org.gradle.api.tasks.bundling.Jar /** @@ -67,15 +68,18 @@ internal fun FileTreeElement.isGoogleProtoSource(): Boolean { } /** - * The reference to the `generateProto` task of a `main` source set. + * The `generateProto` task of a `main` source set, found without creating it. + * + * The returned collection is live: it holds the task once it is registered, + * and stays empty in a project without Protobuf. */ -internal fun Project.generateProto(): Task? = tasks.findByName("generateProto") +internal fun Project.generateProto(): TaskCollection = + tasks.named { it == "generateProto" } /** - * Makes this [Jar] task depend on the [generateProto] task, if it exists in the same project. + * Makes this [Jar] task depend on the [generateProto] task, once that task is registered + * in the same project. */ internal fun Jar.dependOnGenerateProto() { - project.generateProto()?.let { - this.dependsOn(it) - } + this.dependsOn(project.generateProto()) } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationChecksums.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationChecksums.kt index 7d6cf7e998..d083018b97 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationChecksums.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationChecksums.kt @@ -15,6 +15,7 @@ package io.spine.gradle.publish import io.spine.gradle.SpineTaskGroup +import io.spine.gradle.getOrRegister import java.io.File import java.security.MessageDigest import java.util.Locale @@ -98,6 +99,13 @@ internal object PublicationChecksums { * the root project of a multi-module build, which is where `publish.yml` * expects the manifest. A module configuring the extension for itself also * gets an aggregator of its own, describing only that module. + * + * The registration is idempotent. `spinePublishing { }` configures the extension + * anew on every call, and the same project can be reached by two such calls: + * the root extension lists a module in [SpinePublishing.modulesWithCustomPublishing], + * while the module itself opens the extension with `customPublishing = true` — + * which is what `uber-jar-module.gradle.kts` does. Plain registration would fail + * on the second call, during configuration. */ fun registerTasks(host: Project, published: Set) { val collectors = published.map { registerCollectorIn(it) } @@ -177,10 +185,10 @@ internal object PublicationChecksums { // build directory afterwards. // // The list belongs to the project rather than to this call, because the - // task is registered once while `registerTasks` may run several times — - // see `getOrRegister`. Were the action to close over one call's projects, - // a later call adding a module would register its collector and still - // leave it out of the manifest. + // task is registered once while `registerTasks` may run several times, as + // the documentation of `registerTasks` explains. Were the action to close + // over one call's projects, a later call adding a module would register its + // collector and still leave it out of the manifest. val sources = host.attestationSources() sources += published.map { collectorOutput(it) } val target = host.layout.buildDirectory.file(aggregatePath) @@ -256,27 +264,6 @@ internal object PublicationChecksums { } } -/** - * Returns the task named [name] in this container, registering it with [init] - * if it is not there yet. - * - * `spinePublishing { }` configures the extension anew on every call, and the - * same project can be reached by two such calls: the root extension lists a - * module in [SpinePublishing.modulesWithCustomPublishing], while the module - * itself opens the extension with `customPublishing = true` — which is what - * `uber-jar-module.gradle.kts` does. Plain registration fails on the second - * call, during configuration, so registration has to be idempotent. - */ -private fun TaskContainer.getOrRegister( - name: String, - init: Task.() -> Unit -): TaskProvider = - if (names.contains(name)) { - named(name) - } else { - register(name, init) - } - /** * Returns the `clean` task of this container, if it has one. * @@ -345,7 +332,7 @@ private fun Project.publishedArtifacts(): Map { */ private fun Project.pomFileOf(publication: MavenPublication): File? = tasks.withType(GenerateMavenPom::class.java) - .findByName("generatePomFileFor${publication.taskSuffix()}") + .findByName(publication.pomTaskName()) ?.destination ?.takeIf { it.exists() } @@ -390,6 +377,11 @@ private fun MavenPublication.rejectMavenSnapshot() { private fun MavenPublication.taskSuffix(): String = "${name.replaceFirstChar { it.titlecase(Locale.ROOT) }}Publication" +/** + * Returns the name of the task that writes the POM of this publication. + */ +internal fun MavenPublication.pomTaskName(): String = "generatePomFileFor${taskSuffix()}" + /** * Returns the SHA-256 digest of this file as a lowercase hexadecimal string. * diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationSbom.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationSbom.kt index e32c622fdf..64ea35ec76 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationSbom.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationSbom.kt @@ -154,7 +154,7 @@ internal object PublicationSbom { * Whether a project is a JVM or a multiplatform module is known only once it is * evaluated, so the registration waits until then. It is idempotent, because * `spinePublishing { }` may reach one project twice, as the documentation of - * `getOrRegister` in `PublicationChecksums.kt` explains. + * [PublicationChecksums.registerTasks] explains. */ fun registerTasks(host: Project, published: Set) { published.forEach { project -> diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/SpinePublishing.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/SpinePublishing.kt index 4af48a5633..31a7a9fcd4 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/SpinePublishing.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/SpinePublishing.kt @@ -17,6 +17,7 @@ package io.spine.gradle.publish import io.spine.dependency.local.Spine +import io.spine.gradle.github.pages.PublishAllApiDocs import io.spine.gradle.repo.Repository import java.util.Locale import org.gradle.api.Project @@ -332,6 +333,9 @@ open class SpinePublishing(private val project: Project) { // After `PublicationSbom`: its `projectsEvaluated` callback adds the SBOM artifacts // that the one of `PublicationState` collects. PublicationState.registerOn(project.rootProject) + // `publishAllApiDocs` is registered here, as this is where the published modules + // are known. It is not tied to `publish`: neither task depends on the other. + PublishAllApiDocs.registerIn(project.rootProject, projectsToPublish) } /** diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/StandardJavaPublicationHandler.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/StandardJavaPublicationHandler.kt index f17170a071..030d03470c 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/StandardJavaPublicationHandler.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/StandardJavaPublicationHandler.kt @@ -17,9 +17,11 @@ package io.spine.gradle.publish import io.spine.gradle.repo.Repository import org.gradle.api.Project import org.gradle.api.publish.maven.MavenPublication +import org.gradle.api.publish.maven.tasks.GenerateMavenPom import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.bundling.Jar import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.withType /** * A publication for a typical Java project. @@ -58,6 +60,12 @@ class StandardJavaPublicationHandler private constructor( */ const val PUBLICATION_NAME = "mavenJava" + /** + * The packaging of a POM whose main artifact is a JAR, + * and the extension of that artifact. + */ + private const val JAR = "jar" + override fun create( project: Project, destinations: Set, @@ -108,6 +116,7 @@ class StandardJavaPublicationHandler private constructor( val javaComponent = project.components.findByName("java") javaComponent?.let { from(it) + declareJarPackaging() } /* @@ -118,4 +127,63 @@ class StandardJavaPublicationHandler private constructor( artifact(it) } } + + /** + * Sets the `jar` packaging of the POM, which Gradle would calculate from + * the JAR of the `java` component. + * + * Until the packaging is set, reading it makes Gradle calculate it from the artifacts + * of the publication, which creates the tasks producing them. [PublicationSbom] reads + * the packaging in every build, so that the SBOM it adds does not change it. + * + * Writing the POM fails if the publication turns out to have a main artifact other + * than one JAR, so that a misleading POM is not published. The artifacts are checked + * when the POM is written, so that the check neither creates the tasks producing them + * nor takes the artifacts of the component into the publication before it is final. + * The result is an input of the task, so an up-to-date task does not hide a mismatch. + */ + private fun MavenPublication.declareJarPackaging() { + pom.packaging = JAR + val publication = this + val pomTask = pomTaskName() + project.tasks.withType() + .named { it == pomTask } + .configureEach { + val mismatch = project.provider { publication.jarPackagingMismatch() } + inputs.property("jarPackagingMismatch", mismatch.orElse("")) + doFirst { + mismatch.orNull?.let { throw IllegalStateException(it) } + } + } + } + + /** + * Describes how this publication contradicts the `jar` packaging, or returns `null` + * if it does not. + * + * The publication contradicts it if it keeps the `jar` packaging, but does not have + * exactly one main artifact, a JAR, the SBOM not counted. Such is the case if + * the build gives the `jar` task a classifier, or adds an artifact without one. + */ + private fun MavenPublication.jarPackagingMismatch(): String? { + if (pom.packaging != JAR) { + // The build has set a packaging of its own. + return null + } + val mainArtifacts = artifacts.filter { + it.classifier.isNullOrEmpty() && it.extension != PublicationSbom.sbomExtension + } + return if (mainArtifacts.singleOrNull()?.extension == JAR) { + null + } else { + val found = if (mainArtifacts.isEmpty()) { + "no main artifact" + } else { + "the main artifacts ${mainArtifacts.map { it.file.name }}" + } + "The POM of the `$name` publication of `${project.path}` declares the `jar`" + + " packaging, but the publication has $found. Please keep the `jar`" + + " task without a classifier, or publish the module with custom publishing." + } + } } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt index 65d8d69a14..c2af416980 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt @@ -20,7 +20,6 @@ import com.github.jk1.license.LicenseReportPlugin import io.spine.dependency.local.Spine import io.spine.gradle.SpineTaskGroup import io.spine.gradle.applyPlugin -import io.spine.gradle.getTask import io.spine.gradle.report.license.Paths.outputFilename import io.spine.gradle.report.license.Paths.relativePath import java.io.File @@ -130,10 +129,13 @@ object LicenseReporter { group = SpineTaskGroup.name description = "Merges per-project license reports into a single repository-wide report" val consolidationTask = this - val assembleTask = project.getTask("assemble") + val assembleTask = project.tasks.named("assemble") val sourceProjects: Iterable = sourceProjects(rootProject) sourceProjects.forEach { - val perProjectTask = it.getTask(projectTaskName) + // Obtained rather than configured lazily: in a single-project build, this + // action belongs to the container that holds the task, and Gradle forbids + // configuring a task of a container from such an action. + val perProjectTask = it.tasks.named(projectTaskName).get() consolidationTask.dependsOn(perProjectTask) perProjectTask.dependsOn(assembleTask) } @@ -142,8 +144,9 @@ object LicenseReporter { } dependsOn(assembleTask) } - project.getTask("build") - .finalizedBy(mergeTask) + project.tasks.named("build").configure { + finalizedBy(mergeTask) + } } /** diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.kt index 479e4f5f20..234a3af44f 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.kt @@ -100,11 +100,9 @@ object PomGenerator { writer.writeTo(pomFile) } - val assembleTask = project.tasks.findByName("assemble")!! - dependsOn(assembleTask) + dependsOn(project.tasks.named("assemble")) } - val buildTask = project.tasks.findByName("build")!! - buildTask.finalizedBy(task) + project.tasks.named("build").configure { finalizedBy(task) } } } diff --git a/buildSrc/src/main/kotlin/jvm-module.gradle.kts b/buildSrc/src/main/kotlin/jvm-module.gradle.kts index 830335fbd8..11777117af 100644 --- a/buildSrc/src/main/kotlin/jvm-module.gradle.kts +++ b/buildSrc/src/main/kotlin/jvm-module.gradle.kts @@ -26,7 +26,7 @@ import io.spine.dependency.local.Reflect import io.spine.dependency.test.Jacoco import io.spine.gradle.SpineTaskGroup import io.spine.gradle.checkstyle.CheckStyleConfig -import io.spine.gradle.github.pages.updateGitHubPages +import io.spine.gradle.github.pages.publishApiDocs import io.spine.gradle.javac.configureErrorProne import io.spine.gradle.javac.configureJavac import io.spine.gradle.javadoc.JavadocConfig @@ -58,7 +58,7 @@ project.run { val generatedDir = "$projectDir/generated" setTaskDependencies(generatedDir) - configureGitHubPages() + configureApiDocs() } typealias Module = Project @@ -150,19 +150,21 @@ fun Module.setTaskDependencies(generatedDir: String) { clean.configure { dependsOn(cleanGenerated) } - - project.afterEvaluate { - val publish = tasks.findByName("publish") - publish?.dependsOn("${project.path}:updateGitHubPages") - } } afterEvaluate { configureTaskDependencies() } } -fun Module.configureGitHubPages() { - updateGitHubPages { +/** + * Applies the plugin that registers the `publishApiDocs` task of this module. + * + * The `publish-api-docs.yml` workflow runs the task through `publishAllApiDocs` of + * the root project, which depends on it for the published modules only. It is not + * a part of `publish`. + */ +fun Module.configureApiDocs() { + publishApiDocs { rootFolder.set(rootDir) } } diff --git a/buildSrc/src/main/kotlin/module.gradle.kts b/buildSrc/src/main/kotlin/module.gradle.kts index b0e9c8bc9f..48fc38c9fe 100644 --- a/buildSrc/src/main/kotlin/module.gradle.kts +++ b/buildSrc/src/main/kotlin/module.gradle.kts @@ -47,7 +47,7 @@ import io.spine.dependency.local.Time import io.spine.dependency.local.ToolBase import io.spine.dependency.local.Validation import io.spine.dependency.test.JUnit -import io.spine.gradle.github.pages.updateGitHubPages +import io.spine.gradle.github.pages.publishApiDocs import io.spine.gradle.javac.configureErrorProne import io.spine.gradle.javac.configureJavac import io.spine.gradle.javadoc.JavadocConfig @@ -237,11 +237,7 @@ fun Module.configureProtoc() { * Configures documentation publishing for this subproject. */ fun Module.setupDocPublishing() { - updateGitHubPages { + publishApiDocs { rootFolder.set(rootDir) } - - tasks.named("publish") { - dependsOn("${project.path}:updateGitHubPages") - } } diff --git a/buildSrc/src/test/kotlin/BuildExtensionsSpec.kt b/buildSrc/src/test/kotlin/BuildExtensionsSpec.kt new file mode 100644 index 0000000000..4ac7a55670 --- /dev/null +++ b/buildSrc/src/test/kotlin/BuildExtensionsSpec.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import org.gradle.api.DefaultTask +import org.gradle.api.InvalidUserDataException +import org.gradle.api.Project +import org.gradle.api.tasks.JavaExec +import org.gradle.testfixtures.ProjectBuilder +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +@DisplayName("Build extensions should") +internal class BuildExtensionsSpec { + + private val project: Project = ProjectBuilder.builder().build() + + @Nested inner class + `set remote debug options` { + + @Test + fun `on a JavaExec task once it is created`() { + project.tasks.register("launch", JavaExec::class.java) + + project.setRemoteDebug("launch") + + val debug = project.tasks.named("launch", JavaExec::class.java).get().debugOptions + debug.enabled.get() shouldBe true + debug.port.get() shouldBe BuildSettings.REMOTE_DEBUG_PORT + } + + @Test + fun `without creating the task`() { + var created = false + project.tasks.register("launch", JavaExec::class.java) { created = true } + + project.setRemoteDebug("launch") + + created shouldBe false + } + + @Test + fun `failing at once for a missing task`() { + shouldThrow { + project.setRemoteDebug("missing") + } + } + + @Test + fun `failing at once for a task other than JavaExec`() { + var created = false + project.tasks.register("plain", DefaultTask::class.java) { created = true } + + shouldThrow { + project.setRemoteDebug("plain") + } + created shouldBe false + } + } +} diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/CliSpec.kt b/buildSrc/src/test/kotlin/io/spine/gradle/CliSpec.kt new file mode 100644 index 0000000000..69f03ba85b --- /dev/null +++ b/buildSrc/src/test/kotlin/io/spine/gradle/CliSpec.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.gradle + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import java.io.File +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir + +@DisplayName("`Cli` should") +internal class CliSpec { + + @TempDir + lateinit var workingFolder: File + + @Test + fun `return the output as the command wrote it, line breaks included`() { + // `printf` turns the `\n` sequences of its format into line breaks. + Cli(workingFolder).execute("printf", "one\\ntwo\\n") shouldBe "one\ntwo\n" + } + + @Test + fun `run a command assembled as a list`() { + Cli(workingFolder).execute(listOf("printf", "%s", "one")) shouldBe "one" + } + + @Test + fun `fail with the exit code and the error output of a failed command`() { + val error = shouldThrow { + Cli(workingFolder).execute("sh", "-c", "echo 'went wrong' >&2; exit 3") + } + + error.message shouldContain "exit code 3" + error.message shouldContain "went wrong" + } +} diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/docs/DokkaSetupIgTest.kt b/buildSrc/src/test/kotlin/io/spine/gradle/docs/DokkaSetupIgTest.kt index 6ec5effe99..150fe348f3 100644 --- a/buildSrc/src/test/kotlin/io/spine/gradle/docs/DokkaSetupIgTest.kt +++ b/buildSrc/src/test/kotlin/io/spine/gradle/docs/DokkaSetupIgTest.kt @@ -52,7 +52,7 @@ private const val PROBE_PATH = ":dokka-sample:dokkaGeneratePublicationProbe" * The build publishes a `javadocJar` that depends on the probe, just as the `javadocJar` * that `spinePublishing` creates depends on `dokkaGeneratePublicationJavadoc`. The build * publishes to a file-based repository and to its own Maven Local repository, so it never - * touches `~/.m2`. The `updateGitHubPages` task of the build stands in for the one that + * touches `~/.m2`. The `publishApiDocs` task of the build stands in for the one that * copies the Dokka output to GitHub Pages. * * The project with the probe is named `dokka-sample`, like the modules of `dokka-tools`, @@ -105,10 +105,10 @@ internal class DokkaSetupIgTest { } @Test - fun `run a Dokka task when the build updates GitHub Pages and publishes to Maven Local`() { + fun `run a Dokka task when the build publishes the API docs and publishes to Maven Local`() { runStoringThenReusing( ":dokka-sample:publishToMavenLocal", - ":dokka-sample:updateGitHubPages" + ":dokka-sample:publishApiDocs" ) { it.probeOutcome shouldBe SUCCESS } @@ -227,7 +227,7 @@ internal class DokkaSetupIgTest { } } - tasks.register("updateGitHubPages") { + tasks.register("publishApiDocs") { dependsOn(docsProbe) } """.trimIndent() diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/git/RepositorySpec.kt b/buildSrc/src/test/kotlin/io/spine/gradle/git/RepositorySpec.kt new file mode 100644 index 0000000000..e84919f555 --- /dev/null +++ b/buildSrc/src/test/kotlin/io/spine/gradle/git/RepositorySpec.kt @@ -0,0 +1,313 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.gradle.git + +import io.kotest.matchers.collections.shouldBeEmpty +import io.kotest.matchers.collections.shouldContain +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder +import io.kotest.matchers.file.shouldExist +import io.kotest.matchers.file.shouldNotExist +import io.kotest.matchers.shouldBe +import io.spine.gradle.Cli +import io.spine.gradle.github.pages.docsCheckoutPatterns +import java.io.File +import org.gradle.api.Project +import org.gradle.testfixtures.ProjectBuilder +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir + +/** + * Verifies a [sparse clone][Repository.Factory.cloneSparsely] against a local bare + * repository, reached by a `file:` URL the way Git reaches a remote one: with a plain + * path, Git would ignore `--depth` and `--filter`. + * + * The `gh-pages` branch of the repository holds a top-level `CNAME`, the documentation + * of the module `m`, with the folders of its versions `1.0.0` and `1.1.0` and a file + * next to them, and the documentation of the module `other`. The tests check out + * the paths that [docsCheckoutPatterns] selects for `m`. + */ +@DisplayName("A sparse clone of a `Repository` should") +internal class RepositorySpec { + + @TempDir + lateinit var tempDir: File + + private lateinit var remote: File + + /** A full clone of the [remote], standing in for the publications of other modules. */ + private lateinit var seed: File + + private lateinit var project: Project + + private val user = UserInfo("Docs Publisher", "docs@example.com") + + @BeforeEach + fun createRemote() { + remote = tempDir.resolve("remote.git") + git(tempDir, "init", "--bare", "--initial-branch=master", remote.path) + // Allow partial clones, with the blobs fetched on demand, as GitHub does. + git(remote, "config", "uploadpack.allowFilter", "true") + git(remote, "config", "uploadpack.allowAnySHA1InWant", "true") + + seed = tempDir.resolve("seed") + git(tempDir, "clone", remote.path, seed.path) + git(seed, "config", "user.name", "Seed") + git(seed, "config", "user.email", "seed@example.com") + seed.write("README.md", "The sources.") + seed.commit("Add the sources.") + git(seed, "push", "origin", "HEAD:refs/heads/master") + + git(seed, "switch", "--orphan", "gh-pages") + seed.write("CNAME", "spine.io\n") + seed.write("javadoc/m/index.html", "m, 1.1.0") + seed.write("javadoc/m/v/1.0.0/index.html", "m, 1.0.0") + seed.write("javadoc/m/v/1.1.0/index.html", "m, 1.1.0") + seed.write("javadoc/m/v/notes.txt", "Not a version.") + seed.write("javadoc/other/index.html", "other, 1.1.0") + seed.commit("Publish the documentation.") + git(seed, "push", "origin", "gh-pages") + + project = ProjectBuilder.builder() + .withProjectDir(tempDir.resolve("project")) + .build() + } + + @Test + fun `check out only the top-level files`() { + cloneDocs().use { repo -> + val root = repo.location.toFile() + + root.resolve("CNAME").shouldExist() + root.resolve("javadoc").shouldNotExist() + } + } + + @Test + fun `check out only the paths of a publication`() { + cloneDocs().use { repo -> + repo.checkOutOnly(docsCheckoutPatterns("javadoc/m", "2.0.0")) + val root = repo.location.toFile() + + root.resolve("javadoc/m/index.html").shouldExist() + root.resolve("javadoc/m/v/1.0.0").shouldNotExist() + root.resolve("javadoc/other").shouldNotExist() + } + } + + /** + * Publishing a version again, as a re-run of a failed publication does, writes + * into the folder of that version, which is in the branch already. + */ + @Test + fun `check out and update the folder of a version published before`() { + cloneDocs().use { repo -> + repo.checkOutOnly(docsCheckoutPatterns("javadoc/m", "1.1.0")) + val root = repo.location.toFile() + + root.resolve("javadoc/m/v/1.1.0/index.html").shouldExist() + root.resolve("javadoc/m/v/1.0.0").shouldNotExist() + + root.write("javadoc/m/v/1.1.0/index.html", "m, 1.1.0, again") + repo.commitAllChanges("Publish `m` 1.1.0 again.") + + changedPaths(root) shouldContainExactly listOf("M\tjavadoc/m/v/1.1.0/index.html") + } + } + + @Test + fun `list the folders that are not checked out, without the files`() { + cloneDocs().use { repo -> + repo.listFolders("javadoc/m/v") shouldContainExactly listOf("1.0.0", "1.1.0") + repo.listFolders("javadoc/absent").shouldBeEmpty() + } + } + + @Test + fun `commit only the changed paths, keeping those not checked out`() { + cloneDocs().use { repo -> + repo.checkOutOnly(docsCheckoutPatterns("javadoc/m", "2.0.0")) + val root = repo.location.toFile() + root.write("javadoc/m/index.html", "m, 2.0.0") + root.write("javadoc/m/v/2.0.0/index.html", "m, 2.0.0") + + repo.commitAllChanges("Publish `m` 2.0.0.") + + changedPaths(root) shouldContainExactlyInAnyOrder listOf( + "M\tjavadoc/m/index.html", + "A\tjavadoc/m/v/2.0.0/index.html" + ) + } + } + + @Test + fun `push onto a commit that another publication pushed meanwhile`() { + cloneDocs().use { repo -> + repo.checkOutOnly(docsCheckoutPatterns("javadoc/m", "2.0.0")) + repo.location.toFile().write("javadoc/m/v/2.0.0/index.html", "m, 2.0.0") + repo.commitAllChanges("Publish `m` 2.0.0.") + + seed.write("javadoc/other/index.html", "other, 2.0.0") + seed.commit("Publish `other` 2.0.0.") + git(seed, "push", "origin", "gh-pages") + + repo.push() + } + + val log = git(remote, "log", "--format=%s", "gh-pages").lines() + log shouldContain "Publish `m` 2.0.0." + log shouldContain "Publish `other` 2.0.0." + git(remote, "show", "gh-pages:javadoc/m/v/2.0.0/index.html") shouldBe "m, 2.0.0" + git(remote, "show", "gh-pages:javadoc/other/index.html") shouldBe "other, 2.0.0" + git(remote, "show", "gh-pages:javadoc/m/v/1.0.0/index.html") shouldBe "m, 1.0.0" + } + + @Test + fun `create a branch that does not exist yet, and push to it`() { + val cname = "spine.io\n" + + cloneSite(initialFiles = mapOf("CNAME" to cname)).use { repo -> + git(remote, "ls-tree", "--name-only", "site").trim() shouldBe "CNAME" + git(remote, "show", "site:CNAME") shouldBe cname + + repo.checkOutOnly(docsCheckoutPatterns("javadoc/m", "1.0.0")) + repo.location.toFile().write("javadoc/m/index.html", "m, 1.0.0") + repo.commitAllChanges("Publish `m` 1.0.0.") + repo.push() + } + + git(remote, "show", "site:javadoc/m/index.html") shouldBe "m, 1.0.0" + } + + @Test + fun `adopt a branch that another publication created first, and push to it`() { + val preparedCommit = raceForSiteBranch() + + cloneSite(initialFiles = mapOf("CNAME" to "spine.io\n")).use { repo -> + val root = repo.location.toFile() + git(root, "rev-parse", "HEAD").trim() shouldBe preparedCommit + git(root, "rev-parse", "--abbrev-ref", "site@{upstream}").trim() shouldBe + "origin/site" + + raceHook.delete() + repo.checkOutOnly(docsCheckoutPatterns("javadoc/m", "1.0.0")) + root.write("javadoc/m/index.html", "m, 1.0.0") + repo.commitAllChanges("Publish `m` 1.0.0.") + repo.push() + } + + git(remote, "show", "site:javadoc/m/index.html") shouldBe "m, 1.0.0" + } + + /** + * The full [clone][Repository.Factory.clone] shares the adoption of a branch with + * the sparse one. + */ + @Test + fun `adopt a branch that another publication created first, in a full clone`() { + val preparedCommit = raceForSiteBranch() + + Repository.clone( + project, + sshUrl = url, + user = user, + branch = "site", + initialFiles = mapOf("CNAME" to "spine.io\n") + ).use { repo -> + val root = repo.location.toFile() + git(root, "rev-parse", "HEAD").trim() shouldBe preparedCommit + git(root, "rev-parse", "--abbrev-ref", "site@{upstream}").trim() shouldBe + "origin/site" + } + } + + /** + * Makes another publication create the `site` branch after this one has found it + * missing, and push first. + * + * The `pre-receive` hook of the [remote] stands in for that publication: it creates + * the branch from a prepared commit, and rejects the push of this publication. + * + * @return the prepared commit. + */ + private fun raceForSiteBranch(): String { + git(seed, "switch", "--orphan", "prepared") + seed.write("CNAME", "spine.io\n") + seed.commit("Initialize the `site` branch.") + git(seed, "push", "origin", "prepared:refs/heads/prepared-site") + // Git keeps the objects a push brings in a quarantine until the hook accepts + // them, and refuses to update a reference from there, so the hook leaves it. + raceHook.apply { + writeText( + """ + #!/bin/sh + env -u GIT_QUARANTINE_PATH -u GIT_OBJECT_DIRECTORY \ + -u GIT_ALTERNATE_OBJECT_DIRECTORIES \ + git update-ref refs/heads/site refs/heads/prepared-site + exit 1 + """.trimIndent() + ) + setExecutable(true) + } + return git(seed, "rev-parse", "HEAD").trim() + } + + private val raceHook: File + get() = remote.resolve("hooks/pre-receive") + + private val url: String + get() = remote.toPath().toUri().toString() + + /** + * Obtains the paths the last commit in the [root] of a clone changed, each + * with the letter of its change. + */ + private fun changedPaths(root: File): List = + git(root, "show", "--name-status", "--format=", "HEAD") + .lines() + .filter { it.isNotBlank() } + + private fun cloneDocs(): Repository = + Repository.cloneSparsely(project, sshUrl = url, user = user, branch = "gh-pages") + + /** + * Clones the `site` branch, which the [remote] does not have at first. + */ + private fun cloneSite(initialFiles: Map): Repository = + Repository.cloneSparsely( + project, + sshUrl = url, + user = user, + branch = "site", + initialFiles = initialFiles + ) +} + +private fun git(directory: File, vararg arguments: String): String = + Cli(directory).execute("git", *arguments) + +private fun File.write(path: String, content: String) { + resolve(path).apply { + parentFile.mkdirs() + writeText(content) + } +} + +private fun File.commit(message: String) { + git(this, "add", "--all") + git(this, "commit", "--message=$message") +} diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/github/pages/NewestAboveSpec.kt b/buildSrc/src/test/kotlin/io/spine/gradle/github/pages/NewestAboveSpec.kt new file mode 100644 index 0000000000..ad5f631703 --- /dev/null +++ b/buildSrc/src/test/kotlin/io/spine/gradle/github/pages/NewestAboveSpec.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.gradle.github.pages + +import io.kotest.matchers.nulls.shouldBeNull +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +/** + * Verifies how the publication of the API documentation decides whether the most + * recent documentation of a module may be replaced by that of the published version. + */ +@DisplayName("`newestAbove` should") +internal class NewestAboveSpec { + + @Test + fun `find no newer version when nothing is published`() { + emptyList().newestAbove("2.0.0").shouldBeNull() + } + + @Test + fun `find no newer version for the newest one`() { + listOf("1.9.0", "2.0.0-SNAPSHOT.575", "2.0.0-SNAPSHOT.99") + .newestAbove("2.0.0").shouldBeNull() + } + + @Test + fun `find no newer version for a version published again`() { + listOf("1.9.0", "2.0.0").newestAbove("2.0.0").shouldBeNull() + } + + @Test + fun `find the newest version above an older one`() { + listOf("1.9.0", "2.1.0", "2.0.0").newestAbove("1.9.5") shouldBe "2.1.0" + } + + @Test + fun `compare versions by their meaning rather than as text`() { + listOf("2.9.0").newestAbove("2.10.0").shouldBeNull() + listOf("2.10.0").newestAbove("2.9.0") shouldBe "2.10.0" + } +} diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/github/pages/PublishApiDocsIgTest.kt b/buildSrc/src/test/kotlin/io/spine/gradle/github/pages/PublishApiDocsIgTest.kt new file mode 100644 index 0000000000..9fc3a345f1 --- /dev/null +++ b/buildSrc/src/test/kotlin/io/spine/gradle/github/pages/PublishApiDocsIgTest.kt @@ -0,0 +1,349 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.gradle.github.pages + +import io.kotest.matchers.collections.shouldBeEmpty +import io.kotest.matchers.collections.shouldContain +import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder +import io.kotest.matchers.collections.shouldNotContain +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import java.io.File +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir + +/** + * Verifies which tasks the `publishAllApiDocs` task of the root project, registered by + * `spinePublishing { }`, brings into a build: the `publishApiDocs` tasks of the projects. + * + * The builds that would publish documentation run with `--dry-run`, so that nothing + * is pushed to GitHub Pages. The builds of a snapshot version run for real, as they + * publish nothing. + * + * In the multi-module build, `api` and `backend` are published and document + * themselves, `testing` documents itself, but is not published, as a module of + * integration tests, and `bare` is published, but does not document itself. + * + * No build applies Dokka. A task named like the Dokka one stands in for it, as + * the plugin and the `javadocJar` of `spinePublishing` look the task up by name. + */ +@DisplayName("`publishAllApiDocs` task should") +internal class PublishApiDocsIgTest { + + @TempDir + lateinit var projectDir: File + + @Test + fun `publish the docs of each published module that documents itself`() { + writeMultiModuleBuild(documented = setOf("api", "backend", "testing")) + + val planned = dryRun(":$aggregate") + + planned shouldContain ":$aggregate" + planned.docsTasks() shouldContainExactlyInAnyOrder listOf( + ":api:$taskName", + ":backend:$taskName" + ) + // `testing` has the task, so the aggregate left it out only for not being published. + dryRun(":testing:$taskName").docsTasks() shouldBe listOf(":testing:$taskName") + } + + /** + * `lib` opens `spinePublishing { }` on its own, so the second call reaches + * the task of the root project while `lib` is evaluated, with projects of its own. + */ + @Test + fun `publish the docs of a module that configures its own publishing`() { + file("settings.gradle.kts").writeText( + """ + rootProject.name = "own-publishing-sample" + include("api", "lib") + """.trimIndent() + ) + file("build.gradle.kts").writeText( + """ + ${buildscript()} + + import io.spine.gradle.github.pages.publishApiDocs + import io.spine.gradle.publish.spinePublishing + + allprojects { + group = "io.spine.sample" + version = "1.0.0" + } + + subprojects { + apply(plugin = "java-library") + $generatorStub + } + + project("api").publishApiDocs { + rootFolder.set(rootDir) + } + + spinePublishing { + modules = setOf("api") + destinations = emptySet() + } + """.trimIndent() + ) + file("lib/build.gradle.kts").apply { + parentFile.mkdirs() + writeText( + """ + import io.spine.gradle.github.pages.publishApiDocs + import io.spine.gradle.publish.spinePublishing + + publishApiDocs { + rootFolder.set(rootDir) + } + + spinePublishing { + destinations = emptySet() + } + """.trimIndent() + ) + } + file("api").mkdirs() + + dryRun(":$aggregate").docsTasks() shouldContainExactlyInAnyOrder listOf( + ":api:$taskName", + ":lib:$taskName" + ) + } + + @Test + fun `not be a part of the publication to Maven repositories`() { + writeMultiModuleBuild(documented = setOf("api", "backend", "testing")) + + val planned = dryRun("publish") + + planned shouldContain ":api:publish" + planned shouldNotContain ":$aggregate" + planned.docsTasks().shouldBeEmpty() + } + + @Test + fun `do nothing in a build that documents no module`() { + writeMultiModuleBuild(documented = emptySet()) + + val result = runner(":$aggregate").build() + + result.tasks.map { it.path } shouldBe listOf(":$aggregate") + result.task(":$aggregate")?.outcome shouldBe TaskOutcome.UP_TO_DATE + } + + @Test + fun `publish the docs of the project in a single-project build`() { + writeSingleProjectBuild(version = "1.0.0") + + val planned = dryRun(":$aggregate") + + planned.docsTasks() shouldBe listOf(":$taskName") + planned shouldContain ":${TaskName.copyJavadocDocs}" + } + + @Test + fun `publish no docs of a snapshot version`() { + writeMultiModuleBuild(documented = setOf("api", "backend", "testing"), version = snapshot) + + val result = runner(":$aggregate").build() + + val paths = result.tasks.map { it.path } + paths.docsTasks() shouldContainExactlyInAnyOrder listOf( + ":api:$taskName", + ":backend:$taskName" + ) + paths shouldNotContain ":api:${TaskName.copyJavadocDocs}" + result.output shouldContain "`$snapshot` is a snapshot version" + } + + /** + * Only the publication to GitHub Pages skips a snapshot version. The archives of + * its documentation, built by Dokka, are still published to Maven repositories. + */ + @Test + fun `leave the docs archives of a snapshot version to the Maven publication`() { + writeMultiModuleBuild(documented = setOf("api"), version = snapshot) + file("build.gradle.kts").appendText( + "\n" + """ + project("api") { + apply(plugin = "maven-publish") + the().repositories.maven { + name = "remote" + url = uri(rootDir.resolve("remote-repo")) + } + } + """.trimIndent() + ) + + val planned = dryRun(":api:publish") + + planned shouldContain ":api:javadocJar" + planned shouldContain ":api:dokkaGeneratePublicationJavadoc" + planned.docsTasks().shouldBeEmpty() + } + + @Test + fun `publish no docs of a snapshot version in a single-project build`() { + writeSingleProjectBuild(version = snapshot) + + val result = runner(":$aggregate").build() + + result.tasks.map { it.path } shouldContainExactlyInAnyOrder listOf( + ":$taskName", + ":$aggregate" + ) + result.output shouldContain "`$snapshot` is a snapshot version" + } + + private fun writeSingleProjectBuild(version: String) { + file("settings.gradle.kts").writeText("""rootProject.name = "single-sample"""") + file("build.gradle.kts").writeText( + """ + ${buildscript()} + + import io.spine.gradle.github.pages.publishApiDocs + import io.spine.gradle.publish.spinePublishing + + group = "io.spine.sample" + version = "$version" + + apply(plugin = "java-library") + $generatorStub + + publishApiDocs { + rootFolder.set(rootDir) + } + + spinePublishing { + destinations = emptySet() + } + """.trimIndent() + ) + } + + private fun writeMultiModuleBuild(documented: Set, version: String = "1.0.0") { + val modules = listOf("api", "backend", "bare", "testing") + modules.forEach { file(it).mkdirs() } + file("settings.gradle.kts").writeText( + """ + rootProject.name = "docs-sample" + include(${modules.joinToString { "\"$it\"" }}) + """.trimIndent() + ) + file("build.gradle.kts").writeText( + """ + ${buildscript()} + + import io.spine.gradle.github.pages.publishApiDocs + import io.spine.gradle.publish.spinePublishing + + allprojects { + group = "io.spine.sample" + version = "$version" + } + + subprojects { + apply(plugin = "java-library") + $generatorStub + } + + listOf(${documented.joinToString { "\"$it\"" }}).forEach { + project(it).publishApiDocs { + rootFolder.set(rootDir) + } + } + + spinePublishing { + modules = setOf("api", "backend", "bare") + destinations = emptySet() + } + """.trimIndent() + ) + } + + /** + * Runs the build with `--dry-run` and returns the paths of the tasks + * it would run. + * + * TestKit reports no tasks for a dry run, so they are read from the output, + * which lists each of them as ` SKIPPED`. + */ + private fun dryRun(task: String): List = + runner(task, "--dry-run").build().output.lines() + .filter { it.startsWith(":") && it.endsWith(" SKIPPED") } + .map { it.substringBefore(' ') } + + private fun runner(vararg args: String): GradleRunner = + GradleRunner.create() + .withProjectDir(projectDir) + .withArguments(*args, "--stacktrace") + + /** + * Selects the paths of the tasks that publish documentation. + */ + private fun List.docsTasks(): List = + filter { it.substringAfterLast(':') == taskName } + + private fun file(relativePath: String): File = projectDir.resolve(relativePath) + + private fun buildscript(): String = + """ + buildscript { + dependencies { + classpath(files( + ${buildSrcClasspath()} + )) + } + } + """.trimIndent() + + /** + * Renders the classpath with the production classes of `buildSrc` as + * arguments of `files(...)`, for injection into the build script classpath + * of the generated build. + * + * The classpath comes from the `test` task in `buildSrc/build.gradle.kts`. + */ + private fun buildSrcClasspath(): String { + val classpath = requireNotNull(System.getProperty("buildSrc.classpath")) { + "The `buildSrc.classpath` system property is not set." + + " It is supplied by the `test` task in `buildSrc/build.gradle.kts`." + } + return classpath.split(File.pathSeparator).joinToString(",\n") { + " \"${File(it).invariantSeparatorsPath}\"" + } + } + + private companion object { + + const val taskName = TaskName.publishApiDocs + + const val aggregate = TaskName.publishAllApiDocs + + /** A version in the format the Spine SDK publishes from every merge. */ + const val snapshot = "1.0.0-SNAPSHOT.1" + + /** + * A task standing in for the Dokka one whose output the plugin copies, and on + * which the `javadocJar` of `spinePublishing` depends. + */ + const val generatorStub = """tasks.register("dokkaGeneratePublicationJavadoc")""" + } +} diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/javadoc/JavadocConfigSpec.kt b/buildSrc/src/test/kotlin/io/spine/gradle/javadoc/JavadocConfigSpec.kt new file mode 100644 index 0000000000..19b9e97316 --- /dev/null +++ b/buildSrc/src/test/kotlin/io/spine/gradle/javadoc/JavadocConfigSpec.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.gradle.javadoc + +import io.kotest.matchers.shouldBe +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPlugin +import org.gradle.testfixtures.ProjectBuilder +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +@DisplayName("`JavadocConfig` should") +internal class JavadocConfigSpec { + + private val project: Project = ProjectBuilder.builder().build().also { + it.pluginManager.apply(JavaPlugin::class.java) + } + + /** + * The project of a build without subprojects is not a production module, + * so its `javadoc` task is disabled. + */ + @Test + fun `configure the javadoc task once Gradle creates it`() { + var created = false + project.tasks.named { it == "javadoc" }.configureEach { created = true } + + JavadocConfig.applyTo(project) + + created shouldBe false + project.tasks.javadocTask().get().enabled shouldBe false + } +} diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/publish/ProtoExtsSpec.kt b/buildSrc/src/test/kotlin/io/spine/gradle/publish/ProtoExtsSpec.kt new file mode 100644 index 0000000000..3859d28ab3 --- /dev/null +++ b/buildSrc/src/test/kotlin/io/spine/gradle/publish/ProtoExtsSpec.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.gradle.publish + +import io.kotest.matchers.collections.shouldBeEmpty +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.shouldBe +import org.gradle.api.Project +import org.gradle.api.plugins.BasePlugin +import org.gradle.api.tasks.bundling.Jar +import org.gradle.testfixtures.ProjectBuilder +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +@DisplayName("Proto extensions should") +internal class ProtoExtsSpec { + + private val project: Project = ProjectBuilder.builder().build().also { + it.pluginManager.apply(BasePlugin::class.java) + } + + private val jar: Jar = project.tasks.register("protoJar", Jar::class.java).get() + + @Test + fun `make a JAR depend on generateProto registered later, without creating it`() { + jar.dependOnGenerateProto() + var created = false + project.tasks.register("generateProto") { created = true } + + created shouldBe false + jar.dependencyNames() shouldContainExactly listOf("generateProto") + } + + @Test + fun `add no dependency to a JAR in a project without generateProto`() { + jar.dependOnGenerateProto() + + jar.dependencyNames().shouldBeEmpty() + } + + private fun Jar.dependencyNames(): List = + taskDependencies.getDependencies(this).map { it.name } +} diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/publish/PublicationSbomIgTest.kt b/buildSrc/src/test/kotlin/io/spine/gradle/publish/PublicationSbomIgTest.kt index d64306ebb6..60e0ff7445 100644 --- a/buildSrc/src/test/kotlin/io/spine/gradle/publish/PublicationSbomIgTest.kt +++ b/buildSrc/src/test/kotlin/io/spine/gradle/publish/PublicationSbomIgTest.kt @@ -30,6 +30,7 @@ import io.kotest.matchers.file.shouldNotExist import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain import io.kotest.matchers.string.shouldNotContain import java.io.File import java.util.jar.JarOutputStream @@ -199,6 +200,73 @@ internal class PublicationSbomIgTest { packagingOf("osgi", osgiBundle) shouldBe "bundle" } + /** + * Until the packaging of a POM is set, reading it creates the tasks that produce + * the artifacts of the publication. A standard publication sets its packaging + * as it is created, so a build that does not publish creates none of them. + * + * The init script reports the tasks created before the task graph is ready, which + * excludes those that the fixture creates later, to report the SBOMs. + */ + @Test + fun `create no archive task of a standard publication in a build that does not publish`() { + val initScript = file("report-created-tasks.gradle") + initScript.writeText( + """ + def graphReady = false + gradle.taskGraph.whenReady { graphReady = true } + allprojects { + tasks.configureEach { task -> + if (!graphReady) println("$createdPrefix${'$'}{task.path}") + } + } + """.trimIndent() + ) + val result = GradleRunner.create() + .withProjectDir(projectDir) + .withArguments("help", "--init-script", initScript.path, "--offline", "--stacktrace") + .build() + + val created = result.output.lines() + .filter { it.startsWith(createdPrefix) } + .map { it.removePrefix(createdPrefix) } + created shouldContain ":help" + created shouldNotContainAnyOf listOf("api", "impl").flatMap { module -> + listOf("jar", "sourcesJar", "javadocJar", "htmlDocsJar").map { ":$module:$it" } + } + } + + /** + * A standard publication declares the `jar` packaging before its artifacts are known, + * so writing its POM fails if the build gives the `jar` task a classifier, + * rather than publish a POM that names no main artifact. + * + * The case checks `StandardJavaPublicationHandler`, but shares the fixture of this + * class, whose `api` module is a standard publication with an SBOM. + * + * The build of the fixture has written the POM of `api` already. So the case also shows + * that the result of the check is an input of the task: without it, the task would be + * up to date, and skip the check. + */ + @Test + fun `fail to write the POM of a standard publication whose JAR is classified`() { + val script = file("api/build.gradle.kts") + val original = script.readText() + script.appendText("\ntasks.jar { archiveClassifier.set(\"plain\") }\n") + try { + val result = GradleRunner.create() + .withProjectDir(projectDir) + .withArguments(":api:${pomTaskOf("mavenJava")}", "--offline", "--stacktrace") + .buildAndFail() + + result.output shouldContain + "The POM of the `mavenJava` publication of `:api` declares the `jar`" + + " packaging, but the publication has no main artifact." + } finally { + script.writeText(original) + } + } + @Test fun `describe runtime dependencies only`() { val names = implSbom().packageNames() @@ -908,6 +976,9 @@ internal class PublicationSbomIgTest { /** Marks the lines in which the fixture reports the SBOMs of each publication. */ const val reportPrefix = "SBOM-REPORT " + /** Marks the lines in which an init script reports the tasks created. */ + const val createdPrefix = "CREATED-TASK " + /** The publication of a thin JAR with a hand-written POM. */ const val pluginJar = "pluginJar" diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/report/license/DependencyReportOutputTest.kt b/buildSrc/src/test/kotlin/io/spine/gradle/report/license/DependencyReportOutputTest.kt index a6213ab912..96f756d75d 100644 --- a/buildSrc/src/test/kotlin/io/spine/gradle/report/license/DependencyReportOutputTest.kt +++ b/buildSrc/src/test/kotlin/io/spine/gradle/report/license/DependencyReportOutputTest.kt @@ -74,6 +74,29 @@ class DependencyReportOutputTest { reportFile.readText() shouldBe "license report" } + /** + * In a build of one project, the report to merge is that of the root project, whose + * task is in the same task container as the merging task. + */ + @Test + fun `merge the license report of a single-project build`() { + project.pluginManager.apply(BasePlugin::class.java) + LicenseReporter.generateReportIn(project) + val sourceReport = project.layout.buildDirectory.asFile.get() + .resolve(Paths.relativePath) + .resolve(Paths.outputFilename) + sourceReport.parentFile.mkdirs() + sourceReport.writeText("license report") + + LicenseReporter.mergeAllReports(project) + + project.tasks.named("mergeAllLicenseReports").get() + .executeActions() + + val reportFile = projectDir.resolve("docs/dependencies/dependencies.md") + reportFile.readText() shouldBe "license report" + } + private fun subproject(name: String): Project { val subprojectDir = projectDir.resolve(name) subprojectDir.mkdirs() diff --git a/config b/config index ad3dff0890..3a0d13a6bc 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit ad3dff0890c7716cbba3fe2ce17bf4fe5905a318 +Subproject commit 3a0d13a6bc72f1d3b873a6c5f300aa9bf26ffe0a From e967e687112b9492523384d00b52618459ac1c5b Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 2 Nov 2026 16:53:48 +0000 Subject: [PATCH 10/13] Make KSP in `jvm-runtime` wait for the generated Protobuf sources `configureTaskDependencies()` now wires the tasks lazily, so it no longer realizes them in `afterEvaluate`. That eager realization used to order `generateProto` before `kspKotlin` by chance. Without it, Gradle reports that `kspKotlin` reads `generated/main/java` (and `kspTestKotlin` reads `generated/test/java`) without depending on the task that writes it. Co-Authored-By: Claude Opus 5.5 --- jvm-runtime/build.gradle.kts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/jvm-runtime/build.gradle.kts b/jvm-runtime/build.gradle.kts index 0f9ffbf7a7..5d38be8fed 100644 --- a/jvm-runtime/build.gradle.kts +++ b/jvm-runtime/build.gradle.kts @@ -75,3 +75,13 @@ dependencies { } forceSpineBase() + +// KSP reads the Java sources that the Protobuf tasks write to `generated//java`. +mapOf( + "kspKotlin" to "generateProto", + "kspTestKotlin" to "generateTestProto", +).forEach { (ksp, generateProto) -> + tasks.named { it == ksp }.configureEach { + dependsOn(generateProto) + } +} From 9d9fd495e04ad3b12f31ea4c56c29cfcb1c477c7 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 2 Nov 2026 16:53:48 +0000 Subject: [PATCH 11/13] Update dependency reports Co-Authored-By: Claude Opus 5.5 --- docs/dependencies/dependencies.md | 454 ++++-------------------------- docs/dependencies/pom.xml | 16 +- 2 files changed, 57 insertions(+), 413 deletions(-) diff --git a/docs/dependencies/dependencies.md b/docs/dependencies/dependencies.md index 2edc2256d2..afceed41c5 100644 --- a/docs/dependencies/dependencies.md +++ b/docs/dependencies/dependencies.md @@ -710,7 +710,7 @@ * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -799,7 +799,7 @@ * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protoc. **Version** : 4.36.0. +1. **Group** : com.google.protobuf. **Name** : protoc. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -844,18 +844,10 @@ * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.97.0. - * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - 1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.97.0. - * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - 1. **Group** : com.sksamuel.aedile. **Name** : aedile-core. **Version** : 3.0.4. * **Project URL:** [https://www.github.com/sksamuel/aedile](https://www.github.com/sksamuel/aedile) * **License:** [The Apache 2.0 License](https://opensource.org/licenses/Apache-2.0) @@ -1083,10 +1075,6 @@ * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -1785,7 +1773,7 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 30 18:27:25 WET 2026** using +This report was generated on **Mon Nov 02 16:51:30 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -3217,7 +3205,7 @@ This report was generated on **Fri Oct 30 18:27:25 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 30 18:27:24 WET 2026** using +This report was generated on **Mon Nov 02 16:51:30 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -3234,7 +3222,7 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 30 18:27:23 WET 2026** using +This report was generated on **Mon Nov 02 16:51:28 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -4934,7 +4922,7 @@ This report was generated on **Fri Oct 30 18:27:23 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 30 18:27:25 WET 2026** using +This report was generated on **Mon Nov 02 16:51:30 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -5651,7 +5639,7 @@ This report was generated on **Fri Oct 30 18:27:25 WET 2026** using * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5740,7 +5728,7 @@ This report was generated on **Fri Oct 30 18:27:25 WET 2026** using * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protoc. **Version** : 4.36.0. +1. **Group** : com.google.protobuf. **Name** : protoc. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5785,18 +5773,10 @@ This report was generated on **Fri Oct 30 18:27:25 WET 2026** using * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.97.0. - * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - 1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.97.0. - * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - 1. **Group** : com.sksamuel.aedile. **Name** : aedile-core. **Version** : 3.0.4. * **Project URL:** [https://www.github.com/sksamuel/aedile](https://www.github.com/sksamuel/aedile) * **License:** [The Apache 2.0 License](https://opensource.org/licenses/Apache-2.0) @@ -6024,10 +6004,6 @@ This report was generated on **Fri Oct 30 18:27:25 WET 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -6726,7 +6702,7 @@ This report was generated on **Fri Oct 30 18:27:25 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 30 18:27:25 WET 2026** using +This report was generated on **Mon Nov 02 16:51:30 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -6785,7 +6761,7 @@ This report was generated on **Fri Oct 30 18:27:25 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 30 18:27:23 WET 2026** using +This report was generated on **Mon Nov 02 16:51:28 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -7577,7 +7553,7 @@ This report was generated on **Fri Oct 30 18:27:23 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 30 18:27:24 WET 2026** using +This report was generated on **Mon Nov 02 16:51:30 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -8389,7 +8365,7 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 30 18:27:24 WET 2026** using +This report was generated on **Mon Nov 02 16:52:38 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -9747,7 +9723,7 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 30 18:27:24 WET 2026** using +This report was generated on **Mon Nov 02 16:51:30 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -9849,11 +9825,6 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -9864,60 +9835,30 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-dataformats-text](https://github.com/FasterXML/jackson-dataformats-text) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-dataformats-text](https://github.com/FasterXML/jackson-dataformats-text) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) - * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -9936,12 +9877,6 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) - * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -9974,10 +9909,6 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.code.gson. **Name** : gson. **Version** : 2.13.2. - * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.google.code.gson. **Name** : gson. **Version** : 2.14.0. * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -9986,11 +9917,7 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **Project URL:** [https://github.com/google/gson/gson](https://github.com/google/gson/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.11. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -9998,11 +9925,11 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-common-deps. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-common-deps. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-gradle-plugin. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-gradle-plugin. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -10043,21 +9970,11 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) @@ -10096,18 +10013,10 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.97.0. - * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - 1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.97.0. - * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - 1. **Group** : com.sksamuel.aedile. **Name** : aedile-core. **Version** : 3.0.4. * **Project URL:** [https://www.github.com/sksamuel/aedile](https://www.github.com/sksamuel/aedile) * **License:** [The Apache 2.0 License](https://opensource.org/licenses/Apache-2.0) @@ -10206,23 +10115,23 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** Indiana University Extreme! Lab Software License * **License:** [Indiana University Extreme! Lab Software License](https://raw.githubusercontent.com/x-stream/mxparser/master/LICENSE.txt) -1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.81.0. +1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.81.0. +1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-context. **Version** : 1.81.0. +1. **Group** : io.grpc. **Name** : grpc-context. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.81.0. +1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-inprocess. **Version** : 1.81.0. +1. **Group** : io.grpc. **Name** : grpc-inprocess. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -10230,15 +10139,15 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **Project URL:** [https://github.com/grpc/grpc-kotlin](https://github.com/grpc/grpc-kotlin) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.81.0. +1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf-lite. **Version** : 1.81.0. +1. **Group** : io.grpc. **Name** : grpc-protobuf-lite. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.81.0. +1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -10674,7 +10583,7 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 30 18:27:24 WET 2026** using +This report was generated on **Mon Nov 02 16:51:30 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -11275,11 +11184,6 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -11290,60 +11194,30 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-dataformats-text](https://github.com/FasterXML/jackson-dataformats-text) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-dataformats-text](https://github.com/FasterXML/jackson-dataformats-text) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) - * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -11362,12 +11236,6 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) - * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -11400,19 +11268,11 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.code.gson. **Name** : gson. **Version** : 2.13.2. - * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.google.code.gson. **Name** : gson. **Version** : 2.14.0. * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.11. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -11420,11 +11280,11 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-common-deps. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-common-deps. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-gradle-plugin. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-gradle-plugin. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -11465,21 +11325,11 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) @@ -11518,18 +11368,10 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.97.0. - * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - 1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.97.0. - * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - 1. **Group** : com.sksamuel.aedile. **Name** : aedile-core. **Version** : 3.0.4. * **Project URL:** [https://www.github.com/sksamuel/aedile](https://www.github.com/sksamuel/aedile) * **License:** [The Apache 2.0 License](https://opensource.org/licenses/Apache-2.0) @@ -11628,42 +11470,22 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** Indiana University Extreme! Lab Software License * **License:** [Indiana University Extreme! Lab Software License](https://raw.githubusercontent.com/x-stream/mxparser/master/LICENSE.txt) -1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-context. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-context. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-inprocess. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-inprocess. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -11672,26 +11494,14 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **Project URL:** [https://github.com/grpc/grpc-kotlin](https://github.com/grpc/grpc-kotlin) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf-lite. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-protobuf-lite. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -12116,7 +11926,7 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 30 18:27:24 WET 2026** using +This report was generated on **Mon Nov 02 16:51:30 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -12218,11 +12028,6 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -12233,60 +12038,30 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-dataformats-text](https://github.com/FasterXML/jackson-dataformats-text) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-dataformats-text](https://github.com/FasterXML/jackson-dataformats-text) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) - * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -12305,12 +12080,6 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) - * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -12347,10 +12116,6 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.code.gson. **Name** : gson. **Version** : 2.13.2. - * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.google.code.gson. **Name** : gson. **Version** : 2.14.0. * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -12359,11 +12124,7 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **Project URL:** [https://github.com/google/gson/gson](https://github.com/google/gson/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.11. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -12371,11 +12132,11 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-common-deps. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-common-deps. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-gradle-plugin. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-gradle-plugin. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -12427,21 +12188,11 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) @@ -12492,18 +12243,10 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.97.0. - * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - 1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.97.0. - * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - 1. **Group** : com.sksamuel.aedile. **Name** : aedile-core. **Version** : 3.0.4. * **Project URL:** [https://www.github.com/sksamuel/aedile](https://www.github.com/sksamuel/aedile) * **License:** [The Apache 2.0 License](https://opensource.org/licenses/Apache-2.0) @@ -12606,23 +12349,23 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** Indiana University Extreme! Lab Software License * **License:** [Indiana University Extreme! Lab Software License](https://raw.githubusercontent.com/x-stream/mxparser/master/LICENSE.txt) -1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.81.0. +1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.81.0. +1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-context. **Version** : 1.81.0. +1. **Group** : io.grpc. **Name** : grpc-context. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.81.0. +1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-inprocess. **Version** : 1.81.0. +1. **Group** : io.grpc. **Name** : grpc-inprocess. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -12630,15 +12373,15 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **Project URL:** [https://github.com/grpc/grpc-kotlin](https://github.com/grpc/grpc-kotlin) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.81.0. +1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf-lite. **Version** : 1.81.0. +1. **Group** : io.grpc. **Name** : grpc-protobuf-lite. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.81.0. +1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -13096,7 +12839,7 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 30 18:27:24 WET 2026** using +This report was generated on **Mon Nov 02 16:51:30 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -13858,7 +13601,7 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 30 18:27:24 WET 2026** using +This report was generated on **Mon Nov 02 16:51:29 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -13960,11 +13703,6 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -13981,12 +13719,6 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -13999,48 +13731,24 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-dataformats-text](https://github.com/FasterXML/jackson-dataformats-text) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-dataformats-text](https://github.com/FasterXML/jackson-dataformats-text) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) - * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -14071,12 +13779,6 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) - * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -14121,10 +13823,6 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.code.gson. **Name** : gson. **Version** : 2.13.2. - * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.google.code.gson. **Name** : gson. **Version** : 2.14.0. * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -14133,11 +13831,7 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **Project URL:** [https://github.com/google/gson/gson](https://github.com/google/gson/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.11. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -14145,11 +13839,11 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-common-deps. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-common-deps. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-gradle-plugin. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-gradle-plugin. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -14218,21 +13912,11 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) @@ -14283,18 +13967,10 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.97.0. - * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - 1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.97.0. - * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - 1. **Group** : com.sksamuel.aedile. **Name** : aedile-core. **Version** : 3.0.4. * **Project URL:** [https://www.github.com/sksamuel/aedile](https://www.github.com/sksamuel/aedile) * **License:** [The Apache 2.0 License](https://opensource.org/licenses/Apache-2.0) @@ -14397,42 +14073,22 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **License:** Indiana University Extreme! Lab Software License * **License:** [Indiana University Extreme! Lab Software License](https://raw.githubusercontent.com/x-stream/mxparser/master/LICENSE.txt) -1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-context. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-context. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-inprocess. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-inprocess. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -14441,26 +14097,14 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using * **Project URL:** [https://github.com/grpc/grpc-kotlin](https://github.com/grpc/grpc-kotlin) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf-lite. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-protobuf-lite. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.81.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.83.1. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -14988,7 +14632,7 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 30 18:27:24 WET 2026** using +This report was generated on **Mon Nov 02 16:51:30 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -16449,7 +16093,7 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 30 18:27:24 WET 2026** using +This report was generated on **Mon Nov 02 16:51:30 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -16731,7 +16375,7 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 30 18:27:24 WET 2026** using +This report was generated on **Mon Nov 02 16:51:29 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -17433,6 +17077,6 @@ This report was generated on **Fri Oct 30 18:27:24 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 30 18:27:24 WET 2026** using +This report was generated on **Mon Nov 02 16:51:29 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file diff --git a/docs/dependencies/pom.xml b/docs/dependencies/pom.xml index d49d82014b..b7bba31fb3 100644 --- a/docs/dependencies/pom.xml +++ b/docs/dependencies/pom.xml @@ -62,19 +62,19 @@ all modules and does not describe the project structure per-subproject. io.spine spine-time - 2.0.0-SNAPSHOT.251 + 2.0.0-SNAPSHOT.253 compile io.spine spine-time-java - 2.0.0-SNAPSHOT.251 + 2.0.0-SNAPSHOT.253 compile io.spine spine-validation-jvm-runtime - 2.0.0-SNAPSHOT.464 + 2.0.0-SNAPSHOT.469 compile @@ -277,17 +277,17 @@ all modules and does not describe the project structure per-subproject. io.spine.tools core-jvm-gradle-plugin - 2.0.0-SNAPSHOT.098 + 2.0.0-SNAPSHOT.099 io.spine.tools core-jvm-plugins - 2.0.0-SNAPSHOT.098 + 2.0.0-SNAPSHOT.099 io.spine.tools dokka-extensions - 2.0.0-SNAPSHOT.11 + 2.0.0-SNAPSHOT.12 io.spine.tools @@ -297,12 +297,12 @@ all modules and does not describe the project structure per-subproject. io.spine.tools time-validation - 2.0.0-SNAPSHOT.251 + 2.0.0-SNAPSHOT.253 io.spine.tools validation-java-bundle - 2.0.0-SNAPSHOT.464 + 2.0.0-SNAPSHOT.469 net.sourceforge.pmd From 0d60f1e679f0f46969009cf3d13530140b51599a Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 2 Nov 2026 17:01:13 +0000 Subject: [PATCH 12/13] Embed CoreJvm Compiler `2.0.0-SNAPSHOT.099` in "Adding to build" The `./config/pull` raised `CoreJvmCompiler.version`, which `updateCoreJvmPluginVersion` writes into the examples. Co-Authored-By: Claude Opus 5.5 --- .../docs/validation/user/01-getting-started/adding-to-build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/docs/validation/user/01-getting-started/adding-to-build.md b/docs/content/docs/validation/user/01-getting-started/adding-to-build.md index 540873139b..c37f6ce082 100644 --- a/docs/content/docs/validation/user/01-getting-started/adding-to-build.md +++ b/docs/content/docs/validation/user/01-getting-started/adding-to-build.md @@ -120,7 +120,7 @@ adding Validation directly. CoreJvm brings in the Validation Gradle plugin for y ```kotlin plugins { module - id("io.spine.core-jvm") version "2.0.0-SNAPSHOT.098" + id("io.spine.core-jvm") version "2.0.0-SNAPSHOT.099" } ``` From 9e8ba807d926cb76789b240bfb8ba1e384da63af Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 2 Nov 2026 17:14:40 +0000 Subject: [PATCH 13/13] Drop unused imports and the leftover classpath in `jvm-runtime` The script no longer uses `SpinePublishing` or `StandardJavaPublicationHandler`. No plugin from `CoreJvmCompiler.gradlePlugin` is applied here. The classpath entry came in as `mcJava.pluginLib` with `apply(plugin = "io.spine.mc-java")` (fb62d71094). It stayed after 53ada48c25 removed the `apply`, and later commits only renamed it. Co-Authored-By: Claude Opus 5.5 --- jvm-runtime/build.gradle.kts | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/jvm-runtime/build.gradle.kts b/jvm-runtime/build.gradle.kts index 5d38be8fed..31f4b835a6 100644 --- a/jvm-runtime/build.gradle.kts +++ b/jvm-runtime/build.gradle.kts @@ -1,27 +1,15 @@ /* - * Copyright 2024, TeamDev. All rights reserved. + * Copyright 2026 CodeMatters, Lda. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. */ @file:Suppress("RemoveRedundantQualifierName") @@ -31,8 +19,6 @@ import io.spine.dependency.lib.AutoServiceKsp import io.spine.dependency.local.Base import io.spine.dependency.local.Spine import io.spine.dependency.local.TestLib -import io.spine.gradle.publish.SpinePublishing -import io.spine.gradle.publish.StandardJavaPublicationHandler buildscript { standardSpineSdkRepositories() @@ -46,9 +32,6 @@ buildscript { } } } - dependencies { - classpath(io.spine.dependency.local.CoreJvmCompiler.gradlePlugin) - } } plugins {