app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit cdf1ddf3ca4dd716fc58b46cfe3efb769e96bc6b
parent 1d031c8c9e5116d459f4b93c4ac925471ff69934
Author: triesap <tyson@radroots.org>
Date:   Mon, 10 Aug 2026 20:28:54 +0000

build: add the explicit HarvestCircle build logic

- add independent contracts and convention-plugin included-build projects
- declare five inert plugin IDs for incremental migration
- prove every plugin applies in isolated TestKit fixtures
- expose a standalone build-logic verification target

Diffstat:
MMakefile | 7+++++--
Abuild-logic/contracts/build.gradle.kts | 15+++++++++++++++
Abuild-logic/contracts/src/main/kotlin/org/harvestcircle/buildlogic/contracts/BuildLogicContract.kt | 4++++
Abuild-logic/contracts/src/test/kotlin/org/harvestcircle/buildlogic/contracts/BuildLogicContractTest.kt | 11+++++++++++
Abuild-logic/plugins/build.gradle.kts | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abuild-logic/plugins/src/functionalTest/kotlin/org/harvestcircle/buildlogic/plugins/ConventionPluginSmokeTest.kt | 38++++++++++++++++++++++++++++++++++++++
Abuild-logic/plugins/src/main/kotlin/org/harvestcircle/buildlogic/plugins/HarvestCircleConventionPlugins.kt | 24++++++++++++++++++++++++
Abuild-logic/settings.gradle.kts | 24++++++++++++++++++++++++
Msettings.gradle.kts | 2++
9 files changed, 194 insertions(+), 2 deletions(-)

diff --git a/Makefile b/Makefile @@ -5,10 +5,10 @@ CARGO ?= cargo CARGO_MANIFEST := core/Cargo.toml EXTBUILD ?= $(if $(shell cargo extbuild --version 2>/dev/null),cargo extbuild run --) -.PHONY: help doctor lock metadata format format-fix lint test check build bindings dev run audit licenses foundation-check package source-check package-check signing-check notarization-check release-check clean +.PHONY: help doctor lock metadata build-logic-check format format-fix lint test check build bindings dev run audit licenses foundation-check package source-check package-check signing-check notarization-check release-check clean help: - @printf '%s\n' doctor lock metadata format format-fix lint test check build bindings dev run audit licenses foundation-check package source-check package-check signing-check notarization-check release-check clean + @printf '%s\n' doctor lock metadata build-logic-check format format-fix lint test check build bindings dev run audit licenses foundation-check package source-check package-check signing-check notarization-check release-check clean doctor: $(if $(strip $(EXTBUILD)),cargo extbuild doctor,@:) @@ -22,6 +22,9 @@ lock: doctor metadata: doctor $(EXTBUILD) $(CARGO) metadata --manifest-path $(CARGO_MANIFEST) --locked --format-version 1 --no-deps +build-logic-check: doctor + $(EXTBUILD) $(GRADLE) --no-daemon -p build-logic :contracts:check :plugins:check :plugins:functionalTest + format: doctor $(EXTBUILD) $(CARGO) fmt --manifest-path $(CARGO_MANIFEST) --all -- --check $(EXTBUILD) $(GRADLE) --no-daemon :app:shared:ktlintCheck :app:desktop:ktlintCheck diff --git a/build-logic/contracts/build.gradle.kts b/build-logic/contracts/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + alias(libs.plugins.kotlin.jvm) +} + +kotlin { + jvmToolchain(21) +} + +dependencies { + testImplementation(kotlin("test")) +} + +tasks.test { + useJUnitPlatform() +} diff --git a/build-logic/contracts/src/main/kotlin/org/harvestcircle/buildlogic/contracts/BuildLogicContract.kt b/build-logic/contracts/src/main/kotlin/org/harvestcircle/buildlogic/contracts/BuildLogicContract.kt @@ -0,0 +1,4 @@ +package org.harvestcircle.buildlogic.contracts + +/** Marker for the Gradle-independent HarvestCircle build-contract module. */ +public object BuildLogicContract diff --git a/build-logic/contracts/src/test/kotlin/org/harvestcircle/buildlogic/contracts/BuildLogicContractTest.kt b/build-logic/contracts/src/test/kotlin/org/harvestcircle/buildlogic/contracts/BuildLogicContractTest.kt @@ -0,0 +1,11 @@ +package org.harvestcircle.buildlogic.contracts + +import kotlin.test.Test +import kotlin.test.assertNotNull + +class BuildLogicContractTest { + @Test + fun contractModuleLoadsWithoutGradleApi() { + assertNotNull(BuildLogicContract) + } +} diff --git a/build-logic/plugins/build.gradle.kts b/build-logic/plugins/build.gradle.kts @@ -0,0 +1,71 @@ +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.testing.Test + +plugins { + `java-gradle-plugin` + alias(libs.plugins.kotlin.jvm) +} + +kotlin { + jvmToolchain(21) +} + +dependencies { + implementation(project(":contracts")) + testImplementation(kotlin("test")) +} + +val sourceSets = the<SourceSetContainer>() +val functionalTestSourceSet = sourceSets.create("functionalTest") + +configurations[functionalTestSourceSet.implementationConfigurationName] + .extendsFrom(configurations.testImplementation.get()) +configurations[functionalTestSourceSet.runtimeOnlyConfigurationName] + .extendsFrom(configurations.testRuntimeOnly.get()) + +dependencies { + add(functionalTestSourceSet.implementationConfigurationName, gradleTestKit()) + add(functionalTestSourceSet.implementationConfigurationName, kotlin("test")) +} + +gradlePlugin { + plugins { + create("harvestCircleRoot") { + id = "org.harvestcircle.build.root" + implementationClass = "org.harvestcircle.buildlogic.plugins.HarvestCircleRootPlugin" + } + create("harvestCircleKmpShared") { + id = "org.harvestcircle.build.kmp-shared" + implementationClass = "org.harvestcircle.buildlogic.plugins.HarvestCircleKmpSharedPlugin" + } + create("harvestCircleDesktopApp") { + id = "org.harvestcircle.build.desktop-app" + implementationClass = "org.harvestcircle.buildlogic.plugins.HarvestCircleDesktopAppPlugin" + } + create("harvestCircleRustFfi") { + id = "org.harvestcircle.build.rust-ffi" + implementationClass = "org.harvestcircle.buildlogic.plugins.HarvestCircleRustFfiPlugin" + } + create("harvestCirclePackaging") { + id = "org.harvestcircle.build.packaging" + implementationClass = "org.harvestcircle.buildlogic.plugins.HarvestCirclePackagingPlugin" + } + } + testSourceSets(functionalTestSourceSet) +} + +tasks.withType<Test>().configureEach { + useJUnitPlatform() +} + +val functionalTest by tasks.registering(Test::class) { + description = "Runs convention-plugin tests against isolated Gradle builds." + group = "verification" + testClassesDirs = functionalTestSourceSet.output.classesDirs + classpath = functionalTestSourceSet.runtimeClasspath + shouldRunAfter(tasks.test) +} + +tasks.check { + dependsOn(functionalTest) +} diff --git a/build-logic/plugins/src/functionalTest/kotlin/org/harvestcircle/buildlogic/plugins/ConventionPluginSmokeTest.kt b/build-logic/plugins/src/functionalTest/kotlin/org/harvestcircle/buildlogic/plugins/ConventionPluginSmokeTest.kt @@ -0,0 +1,38 @@ +package org.harvestcircle.buildlogic.plugins + +import org.gradle.testkit.runner.GradleRunner +import kotlin.io.path.createTempDirectory +import kotlin.io.path.createDirectories +import kotlin.io.path.writeText +import kotlin.test.Test +import kotlin.test.assertTrue + +class ConventionPluginSmokeTest { + @Test + fun everyConventionPluginAppliesToAnIsolatedBuild() { + val temporaryDirectory = createTempDirectory("harvestcircle-build-logic-") + val pluginIds = + listOf( + "org.harvestcircle.build.root", + "org.harvestcircle.build.kmp-shared", + "org.harvestcircle.build.desktop-app", + "org.harvestcircle.build.rust-ffi", + "org.harvestcircle.build.packaging", + ) + + pluginIds.forEach { pluginId -> + val fixture = temporaryDirectory.resolve(pluginId.substringAfterLast('.')).createDirectories() + fixture.resolve("settings.gradle.kts").writeText("rootProject.name = \"fixture\"\n") + fixture.resolve("build.gradle.kts").writeText("plugins { id(\"$pluginId\") }\n") + + val result = + GradleRunner.create() + .withProjectDir(fixture.toFile()) + .withPluginClasspath() + .withArguments("tasks", "--stacktrace") + .build() + + assertTrue(result.output.contains("BUILD SUCCESSFUL"), pluginId) + } + } +} diff --git a/build-logic/plugins/src/main/kotlin/org/harvestcircle/buildlogic/plugins/HarvestCircleConventionPlugins.kt b/build-logic/plugins/src/main/kotlin/org/harvestcircle/buildlogic/plugins/HarvestCircleConventionPlugins.kt @@ -0,0 +1,24 @@ +package org.harvestcircle.buildlogic.plugins + +import org.gradle.api.Plugin +import org.gradle.api.Project + +public class HarvestCircleRootPlugin : Plugin<Project> { + override fun apply(target: Project) = Unit +} + +public class HarvestCircleKmpSharedPlugin : Plugin<Project> { + override fun apply(target: Project) = Unit +} + +public class HarvestCircleDesktopAppPlugin : Plugin<Project> { + override fun apply(target: Project) = Unit +} + +public class HarvestCircleRustFfiPlugin : Plugin<Project> { + override fun apply(target: Project) = Unit +} + +public class HarvestCirclePackagingPlugin : Plugin<Project> { + override fun apply(target: Project) = Unit +} diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts @@ -0,0 +1,24 @@ +import org.gradle.api.initialization.resolve.RepositoriesMode + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + } +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + mavenCentral() + } + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} + +rootProject.name = "harvestcircle-build-logic" + +include(":contracts", ":plugins") diff --git a/settings.gradle.kts b/settings.gradle.kts @@ -1,6 +1,8 @@ import org.gradle.api.initialization.resolve.RepositoriesMode pluginManagement { + includeBuild("build-logic") + repositories { google { mavenContent {