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
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,61 @@ class DownloadPage {
}
}

/**
* Renders a "plugins-only" card for a Grails major version that has
* Apache-released companion plugins but no matching {@code coreReleases:}
* entry yet. The card mirrors the visual treatment of {@link #renderDownload}
* but omits the core source / binary / wrapper / release-notes block - only
* the companion plugins are listed, each with their Source / SHA512 / ASC
* verification links and a release-notes link.
*
* <p>Returns the empty string for an empty {@code companions} list so that
* callers can safely render unconditionally.
*
* @param major the Grails major version this card represents (e.g. 8)
* @param companions companion plugins published for this upcoming major
*/
@CompileDynamic
static String renderCompanionsOnlyCard(int major, List<CompanionArtifact> companions) {
if (!companions) {
return ''
}
renderHtml {
div(class: 'guide-group') {
div(class: 'guide-group-header') {
img(src: '[%url]/images/download.svg', alt: "Apache Grails ${major} plugins")
h2("Apache Grails ${major} Plugins")
}
ul {
companions.each { CompanionArtifact c ->
li {
a(
href: sourceUrl(c.version, c.artifactId, '', c.mirrorDirectory),
"${c.displayName} ${c.version} Source"
)
a(
href: sourceVerificationUrl(c.version, c.artifactId, '.sha512', c.mirrorDirectory),
'SHA512'
)
a(
href: sourceVerificationUrl(c.version, c.artifactId, '.asc', c.mirrorDirectory),
'ASC'
)
}
}
companions.each { CompanionArtifact c ->
li {
a(
href: "https://github.com/${c.releaseNotesRepo}/releases/tag/v${c.version}",
"${c.displayName} ${c.version} Release Notes"
)
}
}
}
}
}
}

/**
* @return {@code true} if this version line is distributed through the
* Apache mirrors ({@code major >= 7}). Older versions fell back to
Expand All @@ -187,14 +242,18 @@ class DownloadPage {
* grid (one card per active minor line, e.g. 7.0 and 7.1 today, plus
* 8.0 once that ships), a "Pre-release" grid below it for any
* Apache-released milestones / RCs that haven't been superseded by
* stable, the older-versions dropdown, and a "Get Started" two-column
* footer with Application Forge + SDKMAN install instructions.
* stable, an "Upcoming Plugins" grid for Apache-released companion
* plugins whose target Grails major hasn't shipped yet (e.g. a Grails 8
* plugin published before any Grails 8 core release), the older-versions
* dropdown, and a "Get Started" two-column footer with Application Forge
* + SDKMAN install instructions.
*/
@CompileDynamic
static String mainContent(File releases) {
List<String> currentLines = SiteMap.activeMinorLines(releases)
Map<String, ReleaseVersion> latestPerLine = SiteMap.latestStablePerMinorLine(releases)
Map<Integer, ReleaseVersion> preReleasesPerMajor = SiteMap.latestPreReleasePerMajor(releases)
List<Integer> orphanCompanionMajors = SiteMap.orphanCompanionMajors(releases)

renderHtml {
div(class: 'header-bar chalices-bg') {
Expand Down Expand Up @@ -255,6 +314,30 @@ class DownloadPage {
}
}

if (!orphanCompanionMajors.isEmpty()) {
h2(
class: 'release-section-header column-header',
'Plugins for upcoming Apache Grails releases'
)
p(
'Per Apache release policy, the plugins below are official Apache releases ' +
'published to Maven Central with full source and signature artifacts. They ' +
'target an upcoming Grails major version that has not yet had a release ' +
'recorded on this site; once that version ships these plugins will move under ' +
'its download card automatically.'
)
div(class: 'release-grid') {
orphanCompanionMajors.each { Integer major ->
mkp.yieldUnescaped(
DownloadPage.renderCompanionsOnlyCard(
major,
SiteMap.companionArtifactsFor(releases, major)
)
)
}
}
}

h2(class: 'release-section-header column-header', 'Older Versions')
p('You can download previous versions as far back as Grails 0.1.')
p(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,44 @@ class SiteMap {
}
}

/**
* Returns the Grails major versions that have entries under
* {@code companionArtifacts:} but no entry of any kind (stable or
* pre-release) under {@code coreReleases:}. These are companion plugins
* that have shipped ahead of their target Grails core release.
*
* <p>The downloads page renders a dedicated section for these "orphan"
* companions so they're discoverable until the matching core release
* lands. Once any release for that major is appended to
* {@code coreReleases:} (including a milestone or RC) the major drops out
* of this list automatically and the existing per-major card picks up its
* companions through {@link #companionArtifactsFor}.
*
* <p>Result is sorted descending so the highest upcoming major is rendered
* first. Companion entries with an empty list are skipped (they would
* produce an empty card).
*
* @param releases the {@code conf/releases.yml} file
* @return descending list of orphan major versions; never null
*/
static List<Integer> orphanCompanionMajors(File releases) {
assert releases.exists()
Set<Integer> coreMajors = versions(releases)*.major as Set<Integer>
def model = releases.newInputStream().withCloseable {
new Yaml().load(it) as Map
}
Map section = (model.companionArtifacts ?: [:]) as Map
List<Integer> orphans = []
section.each { key, value ->
Integer major = (key as String).toInteger()
List entries = value as List
if (entries && !coreMajors.contains(major)) {
orphans << major
}
}
orphans.toSorted().reverse()
}

/**
* @return the highest stable {@link ReleaseVersion} per major version,
* keyed by major. Used as the cross-major reference point for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,123 @@ companionArtifacts:
result[1].artifactId == 'grails-quartz'
}

void 'orphanCompanionMajors returns an empty list when no companionArtifacts section exists'() {

given:
File releases = releasesFile('''
coreReleases:
- version: 7.0.0
- version: 7.1.0
'''.stripIndent())

expect:
SiteMap.orphanCompanionMajors(releases) == []
}

void 'orphanCompanionMajors returns an empty list when every companion major has a stable core release'() {

given:
File releases = releasesFile('''
coreReleases:
- version: 7.0.0
- version: 7.1.0
companionArtifacts:
'7':
- artifactId: grails-redis
version: '5.0.1'
mirrorDirectory: redis
releaseNotesRepo: apache/grails-redis
displayName: Grails Redis Plugin
'''.stripIndent())

expect:
SiteMap.orphanCompanionMajors(releases) == []
}

void 'orphanCompanionMajors returns an empty list when the companion major has only a pre-release in coreReleases'() {

given: 'a companion major that is also represented by a milestone in coreReleases'
File releases = releasesFile('''
coreReleases:
- version: 7.1.0
- version: 8.0.0-M1
companionArtifacts:
'8':
- artifactId: grails-publish
version: '1.0.0-M1'
mirrorDirectory: grails-publish
releaseNotesRepo: apache/grails-gradle-publish
displayName: Grails Publish Gradle Plugin
'''.stripIndent())

expect: 'the companion is not orphan because the existing pre-release card will pick it up'
SiteMap.orphanCompanionMajors(releases) == []
}

void 'orphanCompanionMajors returns the major when companions exist but no core release of any kind exists for that major'() {

given:
File releases = releasesFile('''
coreReleases:
- version: 7.1.0
companionArtifacts:
'7':
- artifactId: grails-redis
version: '5.0.1'
mirrorDirectory: redis
releaseNotesRepo: apache/grails-redis
displayName: Grails Redis Plugin
'8':
- artifactId: grails-publish
version: '1.0.0-M1'
mirrorDirectory: grails-publish
releaseNotesRepo: apache/grails-gradle-publish
displayName: Grails Publish Gradle Plugin
'''.stripIndent())

expect:
SiteMap.orphanCompanionMajors(releases) == [8]
}

void 'orphanCompanionMajors returns multiple majors in descending order'() {

given:
File releases = releasesFile('''
coreReleases:
- version: 7.1.0
companionArtifacts:
'8':
- artifactId: grails-publish
version: '1.0.0-M1'
mirrorDirectory: grails-publish
releaseNotesRepo: apache/grails-gradle-publish
displayName: Grails Publish Gradle Plugin
'9':
- artifactId: grails-experimental
version: '0.1.0'
mirrorDirectory: experimental
releaseNotesRepo: apache/grails-experimental
displayName: Grails Experimental Plugin
'''.stripIndent())

expect:
SiteMap.orphanCompanionMajors(releases) == [9, 8]
}

void 'orphanCompanionMajors skips majors whose companion list is empty'() {

given:
File releases = releasesFile('''
coreReleases:
- version: 7.1.0
companionArtifacts:
'8': []
'''.stripIndent())

expect:
SiteMap.orphanCompanionMajors(releases) == []
}

void 'versions accepts the legacy releases: key during the migration window'() {

given:
Expand Down
Loading