Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ jobs:
# release-pipeline debuggability (runs once per tag, CC savings are zero).
run: ./gradlew --no-daemon publishToMavenCentral --no-configuration-cache

- name: Publish plugin to Gradle Plugin Portal
# Tag-only: the Portal rejects SNAPSHOT versions; branch pushes produce SNAPSHOTs.
# publishPlugins uploads directly — no manual promotion step unlike Maven Central.
if: startsWith(github.ref, 'refs/tags/')
env:
GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }}
GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }}
ORG_GRADLE_PROJECT_VERSION_NAME: ${{ steps.version.outputs.VERSION_NAME }}
run: ./gradlew --no-daemon :featured-gradle-plugin:publishPlugins --no-configuration-cache
Comment on lines +59 to +63
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Portal creds not passed 🐞 Bug ☼ Reliability

The new Plugin Portal publish step sets GRADLE_PUBLISH_KEY/SECRET as environment variables but does
not pass them into Gradle as project/system properties and there is no repo build logic that maps
these env vars into Gradle properties. As a result, the Gradle build won’t reliably receive publish
credentials and the portal publish can fail due to missing auth.
Agent Prompt
## Issue description
The workflow exports `GRADLE_PUBLISH_KEY` / `GRADLE_PUBLISH_SECRET` but does not pass them to Gradle as properties (the way other publishing creds are provided via `ORG_GRADLE_PROJECT_*`). There is also no composite action/build script that maps these env vars to Gradle properties, so `:featured-gradle-plugin:publishPlugins` can run without credentials.

## Issue Context
- Maven Central step uses `ORG_GRADLE_PROJECT_*` env vars, which Gradle automatically maps to project properties.
- Plugin Portal step currently only sets plain env vars.

## Fix Focus Areas
- .github/workflows/publish.yml[55-63]

## Suggested change
Update the step to pass the secrets explicitly to Gradle, e.g.

```yaml
- name: Publish plugin to Gradle Plugin Portal
  if: startsWith(github.ref, 'refs/tags/')
  env:
    ORG_GRADLE_PROJECT_VERSION_NAME: ${{ steps.version.outputs.VERSION_NAME }}
  run: |
    ./gradlew --no-daemon :featured-gradle-plugin:publishPlugins --no-configuration-cache \
      -Pgradle.publish.key='${{ secrets.GRADLE_PUBLISH_KEY }}' \
      -Pgradle.publish.secret='${{ secrets.GRADLE_PUBLISH_SECRET }}'
```

(Alternatively, write them to `~/.gradle/gradle.properties` in the job, but passing `-P` is simplest/explicit.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +62 to +63
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Version_name override ignored 🐞 Bug ≡ Correctness

The workflow computes a tag/SNAPSHOT VERSION_NAME and exports it as
ORG_GRADLE_PROJECT_VERSION_NAME, but the featured-gradle-plugin included build force-sets its
version from ../gradle.properties, overriding CI-provided VERSION_NAME. This can publish the
plugin with the gradle.properties version instead of the computed tag/SNAPSHOT version, creating
wrong-version releases and inconsistent artifacts.
Agent Prompt
## Issue description
`featured-gradle-plugin/settings.gradle.kts` currently sets `project.version` from `../gradle.properties` unconditionally in `gradle.beforeProject { ... }`, which overrides `VERSION_NAME` provided via CI (`ORG_GRADLE_PROJECT_VERSION_NAME`). This defeats the workflow’s computed version for the included build.

## Issue Context
- CI computes `VERSION_NAME` from the tag name (or appends `-SNAPSHOT` on branch pushes).
- The included build hard-pins the version from `../gradle.properties` (`VERSION_NAME=1.0.0`), so CI overrides are ignored.

## Fix Focus Areas
- featured-gradle-plugin/settings.gradle.kts[38-58]
- gradle.properties[38-40]
- .github/workflows/publish.yml[30-63]

## Suggested change
In `featured-gradle-plugin/settings.gradle.kts`, resolve the version by preferring a Gradle property override, falling back to `../gradle.properties`:

```kotlin
gradle.beforeProject {
  val parentProps = Properties().apply {
    parentPropertiesText.orNull?.reader()?.use { load(it) }
  }

  // Prefer CI / command-line overrides
  val overriddenVersion = providers.gradleProperty("VERSION_NAME").orNull
  val fileVersion = parentProps.getProperty("VERSION_NAME")
  val resolvedVersion = overriddenVersion ?: fileVersion ?: "unspecified"

  // Don’t clobber existing overrides
  parentProps.forEach { key, value ->
    if (!extensions.extraProperties.has(key.toString())) {
      extensions.extraProperties[key.toString()] = value.toString()
    }
  }
  extensions.extraProperties["VERSION_NAME"] = resolvedVersion
  version = resolvedVersion
}
```

This keeps `gradle.properties` as the default source of truth while allowing CI/tag/SNAPSHOT overrides to actually take effect for the included build.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


publish-xcframework:
name: Publish XCFramework to GitHub Release
# Only runs for version tags — not for SNAPSHOT pushes to main.
Expand Down
32 changes: 32 additions & 0 deletions featured-gradle-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import org.gradle.api.publish.maven.tasks.PublishToMavenRepository

plugins {
alias(libs.plugins.kotlinJvm)
alias(libs.plugins.kotlinSerialization)
`java-gradle-plugin`
alias(libs.plugins.pluginPublish)
alias(libs.plugins.mavenPublish)
}

Expand All @@ -13,14 +16,22 @@ kotlin {
}

gradlePlugin {
website.set("https://github.com/AndroidBroadcast/Featured")
vcsUrl.set("https://github.com/AndroidBroadcast/Featured")
plugins {
create("featured") {
id = "dev.androidbroadcast.featured"
implementationClass = "dev.androidbroadcast.featured.gradle.FeaturedPlugin"
displayName = "Featured Gradle Plugin"
description = "Gradle plugin for Featured – generates type-safe configuration flag declarations"
tags.set(listOf("featured", "feature-flags", "configuration", "codegen", "kotlin-multiplatform"))
}
create("featuredApplication") {
id = "dev.androidbroadcast.featured.application"
implementationClass = "dev.androidbroadcast.featured.gradle.FeaturedApplicationPlugin"
displayName = "Featured Application Gradle Plugin"
description = "Featured aggregator plugin – merges per-module flag manifests into a single generated registry"
tags.set(listOf("featured", "feature-flags", "configuration", "codegen", "kotlin-multiplatform"))
}
}
}
Expand Down Expand Up @@ -59,6 +70,27 @@ mavenPublishing {
}
}

// The java-gradle-plugin marker artifacts (one per plugin id, the second under its own
// groupId) are resolved via the Gradle Plugin Portal, where `publishPlugins` uploads them.
// Vanniktech binds every MavenPublication — markers included — to the Maven Central
// repository, which would pollute the Central listing with two stub marker artifacts.
// Disable only the marker -> Central tasks so Central carries just the clean
// `featured-gradle-plugin` impl jar (+ sources/javadoc/pom); the Portal still receives the
// markers via `publishPlugins`, which uploads publications directly over HTTP independently
// of these maven-publish repository tasks.
//
// Match on the task name (which Gradle fixes at registration time) rather than the task's
// `repository`/`publication` properties: maven-publish wires those properties later, in an
// afterEvaluate, so a `configureEach` action that reads them runs too early and sees them
// unset. The name `publish<Pub>PublicationTo<Repo>Repository` is always correct here.
// `publishPluginMavenPublication...` (the impl) ends with `PluginMavenPublication...`, not
// `PluginMarkerMavenPublication...`, so it is intentionally left enabled.
tasks.withType<PublishToMavenRepository>().configureEach {
if (name.endsWith("PluginMarkerMavenPublicationToMavenCentralRepository")) {
enabled = false
}
}

// A separate configuration whose resolved jars are appended to the pluginUnderTestMetadata
// classpath. This makes GradleRunner.withPluginClasspath() inject them into the TestKit
// subprocess, which is necessary for compileOnly dependencies (like AGP) that the plugin
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ spotless = "8.4.0"
ktlint = "1.8.0"
turbine = "1.2.1"
mavenPublish = "0.36.0"
pluginPublish = "2.1.1"
dokka = "2.2.0"
detekt = "1.23.8"
configcat = "5.1.0"
Expand Down Expand Up @@ -93,5 +94,6 @@ kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
skie = { id = "co.touchlab.skie", version.ref = "skie" }
spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
mavenPublish = { id = "com.vanniktech.maven.publish", version.ref = "mavenPublish" }
pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" }
dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
androidKmpLibrary = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" }
Loading