diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0464c9c7..47c77fb3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -129,7 +129,7 @@ jobs: - name: Lint and build JVM/Android modules timeout-minutes: 20 - run: ./gradlew ktlintCheck :ampere-core:assemble :ampere-cli:assemble :ampere-compose:assemble :ampere-eval:assemble + run: ./gradlew ktlintCheck verifyPublishWorkflowCoverage :ampere-core:assemble :ampere-cli:assemble :ampere-compose:assemble :ampere-eval:assemble jvm-test: name: JVM + Android Tests (${{ matrix.module }}, JDK 21) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6e69c48b..13667544 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -56,7 +56,7 @@ jobs: - name: Run ktlint before publish timeout-minutes: 10 - run: ./gradlew ktlintCheck + run: ./gradlew ktlintCheck verifyPublishWorkflowCoverage - name: Run tests before publish timeout-minutes: 20 @@ -76,13 +76,13 @@ jobs: ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }} run: | export ORG_GRADLE_PROJECT_signingInMemoryKey="$(cat /tmp/signing-key.asc)" - ./gradlew :ampere-core:publishAllPublicationsToMavenCentralRepository :ampere-core-test-fixtures:publishAllPublicationsToMavenCentralRepository :ampere-phosphor:publishAllPublicationsToMavenCentralRepository + ./gradlew :ampere-core:publishAllPublicationsToMavenCentralRepository :ampere-core-test-fixtures:publishAllPublicationsToMavenCentralRepository :ampere-phosphor:publishAllPublicationsToMavenCentralRepository :ampere-bindings-android:publishAllPublicationsToMavenCentralRepository :ampere-bindings-apple:publishAllPublicationsToMavenCentralRepository :ampere-cli:publishAllPublicationsToMavenCentralRepository :ampere-eval:publishAllPublicationsToMavenCentralRepository - name: Dry run publish if: ${{ github.event.inputs.dry_run == 'true' }} run: | echo "Dry run mode - skipping actual publish" - ./gradlew :ampere-core:publishAllPublicationsToMavenCentralRepository :ampere-core-test-fixtures:publishAllPublicationsToMavenCentralRepository :ampere-phosphor:publishAllPublicationsToMavenCentralRepository --dry-run + ./gradlew :ampere-core:publishAllPublicationsToMavenCentralRepository :ampere-core-test-fixtures:publishAllPublicationsToMavenCentralRepository :ampere-phosphor:publishAllPublicationsToMavenCentralRepository :ampere-bindings-android:publishAllPublicationsToMavenCentralRepository :ampere-bindings-apple:publishAllPublicationsToMavenCentralRepository :ampere-cli:publishAllPublicationsToMavenCentralRepository :ampere-eval:publishAllPublicationsToMavenCentralRepository --dry-run - name: Create GitHub Release if: startsWith(github.ref, 'refs/tags/v') && github.event.inputs.dry_run != 'true' diff --git a/build.gradle.kts b/build.gradle.kts index 4980370d..813ab9be 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,3 +15,47 @@ allprojects { maxParallelForks = Runtime.getRuntime().availableProcessors().coerceIn(2, 8) } } + +// AMPR-271: ampere-core-test-fixtures was configured with `mavenPublishing { ... }` — so it +// looked published — but the publish workflow's actual `./gradlew ...` command line never +// mentioned it, so it silently never reached Maven Central across two releases (0.12.0, +// 0.13.0). "Declared for publishing" and "listed in the CI publish step" are two different +// facts that can drift apart with no build failure to catch it. This task closes that gap: +// it fails the build if any subproject with a `mavenPublishing {` block is missing from the +// publish/dry-run steps in .github/workflows/publish.yml, so the next module that opts into +// publishing can't fall out of releases the same way. +val verifyPublishWorkflowCoverage = tasks.register("verifyPublishWorkflowCoverage") { + group = "verification" + description = "Fails if a module configured for Maven Central publishing is missing from " + + "the publish step in .github/workflows/publish.yml (AMPR-271)." + + val workflowFile = layout.projectDirectory.file(".github/workflows/publish.yml") + val subprojectBuildFiles = subprojects + .map { it.path to it.projectDir.resolve("build.gradle.kts") } + .filter { (_, file) -> file.exists() } + + inputs.file(workflowFile) + inputs.files(subprojectBuildFiles.map { it.second }) + + doLast { + val workflowText = workflowFile.asFile.readText() + val publishedModules = subprojectBuildFiles + .filter { (_, file) -> file.readText().contains("mavenPublishing {") } + .map { (path, _) -> path } + + val missing = publishedModules.filter { path -> + !workflowText.contains("$path:publishAllPublicationsToMavenCentralRepository") + } + + if (missing.isNotEmpty()) { + throw GradleException( + "The following modules configure Maven Central publishing " + + "(mavenPublishing { ... } in their build.gradle.kts) but are missing from " + + "the publish step(s) in .github/workflows/publish.yml:\n" + + missing.joinToString("\n") { " - $it" } + + "\n\nAdd them to the 'Publish to Maven Central' and 'Dry run publish' run " + + "commands, or the artifact will silently never reach Central (AMPR-271).", + ) + } + } +}