Skip to content

refactor: replace hardcoded URLs etc. with dynamic placeholders for improved configurability#49

Open
sbglasius wants to merge 6 commits into7.0.xfrom
chore/project-agnostic-gh-docs
Open

refactor: replace hardcoded URLs etc. with dynamic placeholders for improved configurability#49
sbglasius wants to merge 6 commits into7.0.xfrom
chore/project-agnostic-gh-docs

Conversation

@sbglasius
Copy link
Copy Markdown
Member

This pull request introduces dynamic project and repository metadata handling, automates contributor fetching, and improves documentation and publishing workflows by centralizing configuration and using templates. The main themes are metadata centralization, automation, and improved maintainability.

Project and Repository Metadata Centralization:

  • Added new properties to gradle.properties for GitHub org, project, and project metadata, and updated all references in build scripts and documentation templates to use these properties instead of hardcoded values. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]

Automated Contributor Fetching and Publishing:

  • Added a new ContributorTask (build-logic/src/main/groovy/contributor/ContributorTask.groovy) and config.contributors.gradle to fetch contributors from GitHub, store them as YAML, and expose them for use in publishing. [1] [2]
  • Integrated the contributors task into the plugin build so that Maven POM files are automatically populated with up-to-date contributor information. [1] [2]

Documentation Improvements:

  • Updated the documentation index template (docs/src/docs/index.tmpl) to use tokens for project/repo metadata and versioning, and improved version sorting and selection in the documentation generation Gradle task. [1] [2] [3] [4] [5] [6] [7]
  • The documentation build now dynamically injects the latest version and other metadata into the docs. [1] [2]

Dependency Updates:

  • Added org.apache.groovy:groovy-yaml as a build dependency to support YAML processing for contributors.

These changes make the build and release process more maintainable and ensure that documentation and published artifacts always reflect the latest project state and contributor information.

sbglasius added 6 commits May 4, 2026 17:26
…ed configurability

- Updated documentation links and project metadata to use placeholders (e.g., `@GITHUB_REPO_URL@`, `@REPO_SLUG@`).
- Dynamically resolve repository details via Gradle project properties.
- Enhanced documentation build process to replace tokens with computed values.
* Metadata in gradle.properties:  index.tmlp now 100% generic, as it uses metadata from gradle.properties
* Same data is used in plugin/build.gradle (except from developers)
@sbglasius
Copy link
Copy Markdown
Member Author

@jdaugherty @matrei I am asking either of you to review this PR because I want to make something similar for grails-server-timing (the current blueprint for Grails 7 plugins)

I would like for all the build-logic (and release description) to be in gradle.properties and with the necessary information there, it is possible to get the developers list from GitHub when publishing the plugin.

Thoughts, rants and suggestions are welcomed!

Next plan coming up is standard publish to the Slack #plugin channel when a new version of a plugin is released

Copy link
Copy Markdown

@jdaugherty jdaugherty left a comment

Choose a reason for hiding this comment

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

I love the effort to standardize some of this, but I think we should consider:

  1. moving the contributor.yaml population to a github action (i'm willing to help write this if you want) that opens a PR for periodic merging
  2. making this lazy (avoid flatMap, etc) to adhere to our best practices
  3. moving the task + versions to their own gradle plugin that we publish (I'm willing to help fix this since I did make it private to not deal with it 😂)

@@ -0,0 +1,9 @@

def fetchContributors = tasks.register("fetchContributors", contributor.ContributorTask) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The original reason I didn't add a fetch in core, and the other areas is b/c it's not accurate without a malimap. I think for plugins this is probably ok though.

yamlOutput.convention(layout.buildDirectory.file("generated/contributors.yml"))
}

extensions.add("contributorsData", fetchContributors.flatMap { it.contributors })
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Flatmap is non-lazy so you're invoking the task here. We typically avoid this as it's going to happen as part of the configuration workflow. Some possible workarounds:

  1. have a task dependency on the output of the fetchContributors as an input to wherever you need it (or a provider even)
  2. have the fetch contributors write to a file so the task is cacheable and then read from that file, you can then tweak the task for when it fetches

import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction

abstract class ContributorTask extends DefaultTask {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

  1. the task should be cacheable
  2. this seems like something we should publish instead of having it in every project

def outputFile = yamlOutput.get().asFile
outputFile.parentFile.mkdirs()

if (!token) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You're assuming Github token access to have a reproducible build. We typically don't do this as part of the grails builds.

.reverse()
.findAll { it.type == 'dir' }
.collect { it.name as String }
.findAll { it ==~ /\d+\.\d+\.(\d+|x)(-.*)?/ }
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You're parsing semversioning when there are libraries to do this. There are actually multiple classes in grails-core with this, we really should centralize this logic and publish it

String latestVersion = ""
try {
def conn = URI.create('https://api.github.com/repos/gpc/grails-export/contents/?ref=gh-pages').toURL().openConnection()
def conn = URI.create("https://api.github.com/repos/${githubOrg}/${githubProject}/contents/?ref=gh-pages").toURL().openConnection()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

It would probably be a better design to have a github action that just generates a contributor.yml and merges it into a branch, then it can be updated either manually or externally via github

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

If we should externalize the contributor.yml and make it a part of the project, how about moving all the meta-data to a project.yml and have it contain stuff like

projectTitle
projectDescription
projectOrganization
githubOrg
githubProject
contributors
historicVersions

Then it does not have to be in two separate files (gradle.properties and contributors.yml) and it can be easily read and updated by a github action?

String optionsHtml = versions
? versions.collect { v -> "<option value=\"${v}\">${v}</option>" }.join('\n ')
: '<option value="" disabled>No previous versions available</option>'
? versions.collect { v -> "<option value=\"${v}\">${v}</option>" }.join('\n ')
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

We should just clean up the documentation tasks in grails-core and publish them. The main blocker here is we need to pull our themeing templates out from the project and host them somewhere (we talked about doing it on the grails website itself)

? versions.collect { v -> "<option value=\"${v}\">${v}</option>" }.join('\n ')
: '<option value="" disabled>No previous versions available</option>'

def tokens = [
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Gradle supports template expansion:

  1. it can do it as part of the processResources for templates being published
  2. you can do it as part of the copy task itself:

tasks.register('generateConfig', Copy) {
from 'templates/config.tpl'
into "$buildDir/generated"
rename { 'config.conf' }
expand([
version: project.version,
env: 'prod'
])
}

Comment thread plugin/build.gradle
extensions.configure(org.apache.grails.gradle.publish.GrailsPublishExtension) {
it.artifactId = project.name
it.githubSlug = 'gpc/grails-export'
it.githubSlug = "${githubOrg}/${githubProject}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I don't think you even need to define some of these if you define the properties by default. the publish plugin by default looks for properties

Comment thread plugin/build.gradle
}
}

pluginManager.withPlugin('maven-publish') {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants