Skip to content

Commit ecc0647

Browse files
committed
Merge main into upstream-prs-1707-1786
Conflicts with #43 and #44 in ImportOrderer, RemoveUnusedImports and their tests. ImportOrderer: an import now carries both the comments on the lines before it, from #44, and its declaration rendered from its toks, from this branch (palantir#1707). This branch's loop over same-line block comments already covers #44's single block comment after the `;`, so #44's copy of that step goes. Two interactions are decided here: - A javadoc comment right after an import's `;` is no longer an error. This branch rejected it because the formatter moves it onto a line of its own, which used to separate the imports. Since #44 a comment between imports goes with the import after it, and so does this one. - Two copies of an import that differ only in the comment on the lines before them no longer collapse into one, so that comment stays. This branch already kept copies that differ in a comment inside the declaration. RemoveUnusedImports: this branch's loop over JCTree with Trees.getEndPosition, and #43's coalesced ranges and blank-line cleanup. Tests: every case from both sides. This branch's case for a same-line block comment repeated #44's and is folded into it. Its javadoc case now expects the comment to move with the next import. A new case covers the duplicate. Checked: all 1493 formatter tests pass. On the JDK 21 sources (15,747 files, Temurin 21) and the JDK 25 sources (15,368 files, Corretto 25), the merge formats every file exactly as main does, with no errors.
2 parents aed6a78 + 43f5d3d commit ecc0647

38 files changed

Lines changed: 1039 additions & 134 deletions

‎.github/workflows/release.yml‎

Lines changed: 125 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
name: Release
22

3-
# Tags only, and the tag is the version: the build reads it from GITHUB_REF_NAME and jreleaserDeploy
4-
# refuses anything that is not a clean X.Y.Z or X.Y.Z.N.
3+
# Tags only, and the tag is the version: the build reads it from GITHUB_REF_NAME, and jreleaserDeploy and
4+
# publishPlugins refuse anything that is not a clean X.Y.Z or X.Y.Z.N.
55
#
66
# Two deployments per tag, because a published groupId:artifactId:version can never gain files
7-
# afterwards: the jars go up as one, and every platform's native binary as another. Both are uploaded
8-
# and validated only — the Portal holds them until someone presses publish.
7+
# afterwards: the jars go up as one, and every platform's native binary as another. Each is uploaded and
8+
# validated first, and the publish job publishes both only once both are in. A version whose native build
9+
# fails publishes nothing, and its deployments can be dropped in the Portal.
910
#
10-
# After both, a draft GitHub release on the tag collects the runnable jar, the IDE plugins and the native
11-
# binaries.
11+
# Once Maven Central serves both, the Gradle plugins go to the Gradle Plugin Portal, signed with the same
12+
# release key. After publishing, a draft GitHub release on the tag collects the runnable jar, the Gradle
13+
# and IDE plugins and the native binaries.
1214
#
13-
# Needs four repository secrets: JRELEASER_MAVENCENTRAL_USERNAME and JRELEASER_MAVENCENTRAL_PASSWORD
14-
# (the Central Portal user token) plus JRELEASER_GPG_SECRET_KEY and JRELEASER_GPG_PASSPHRASE. The draft
15-
# release uses the workflow's own GITHUB_TOKEN.
15+
# Needs six repository secrets: JRELEASER_MAVENCENTRAL_USERNAME and JRELEASER_MAVENCENTRAL_PASSWORD (the
16+
# Central Portal user token), JRELEASER_GPG_SECRET_KEY and JRELEASER_GPG_PASSPHRASE, and
17+
# GRADLE_PUBLISH_KEY and GRADLE_PUBLISH_SECRET (the Gradle Plugin Portal key). The draft release uses the
18+
# workflow's own GITHUB_TOKEN.
1619
on:
1720
push:
1821
tags:
@@ -29,6 +32,8 @@ jobs:
2932
jars:
3033
name: jars
3134
runs-on: ubuntu-latest
35+
outputs:
36+
deployment-id: ${{ steps.deployment.outputs.id }}
3237
steps:
3338
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
3439
with:
@@ -48,6 +53,16 @@ jobs:
4853
JRELEASER_GPG_SECRET_KEY: ${{ secrets.JRELEASER_GPG_SECRET_KEY }}
4954
JRELEASER_GPG_PASSPHRASE: ${{ secrets.JRELEASER_GPG_PASSPHRASE }}
5055

56+
- name: Hand the deployment to the publish job
57+
id: deployment
58+
run: |
59+
id="$(sed -n 's/^deployMavenCentralSonatypeDeploymentId=//p' build/jreleaser/output.properties)"
60+
if [ -z "$id" ]; then
61+
echo "No deployment ID in build/jreleaser/output.properties"
62+
exit 1
63+
fi
64+
echo "id=$id" >> "$GITHUB_OUTPUT"
65+
5166
- name: Keep JReleaser's log
5267
if: ${{ failure() }}
5368
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
@@ -121,6 +136,8 @@ jobs:
121136
name: native deploy
122137
needs: native-images
123138
runs-on: ubuntu-latest
139+
outputs:
140+
deployment-id: ${{ steps.deployment.outputs.id }}
124141
steps:
125142
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
126143
with:
@@ -144,7 +161,7 @@ jobs:
144161
- name: Show what was collected
145162
run: ls -l native-images
146163

147-
# No GraalVM here: nothing is compiled, the binaries are published exactly as they arrived.
164+
# No GraalVM here: nothing is compiled, the binaries are uploaded exactly as they arrived.
148165
- name: Stage, sign and upload the native images
149166
run: ./gradlew -PreleaseTarget=native -PnativeImages=native-images jreleaserDeploy
150167
env:
@@ -153,6 +170,16 @@ jobs:
153170
JRELEASER_GPG_SECRET_KEY: ${{ secrets.JRELEASER_GPG_SECRET_KEY }}
154171
JRELEASER_GPG_PASSPHRASE: ${{ secrets.JRELEASER_GPG_PASSPHRASE }}
155172

173+
- name: Hand the deployment to the publish job
174+
id: deployment
175+
run: |
176+
id="$(sed -n 's/^deployMavenCentralSonatypeDeploymentId=//p' build/jreleaser/output.properties)"
177+
if [ -z "$id" ]; then
178+
echo "No deployment ID in build/jreleaser/output.properties"
179+
exit 1
180+
fi
181+
echo "id=$id" >> "$GITHUB_OUTPUT"
182+
156183
- name: Keep JReleaser's log
157184
if: ${{ failure() }}
158185
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
@@ -162,13 +189,95 @@ jobs:
162189
if-no-files-found: ignore
163190
retention-days: 7
164191

165-
# What Maven Central does not carry — the runnable formatter jar, the IntelliJ plugin zip, the Eclipse
166-
# plugin jar, and every platform's native binary as a plain download — goes into a draft GitHub release
167-
# on the tag, each file signed with the release key. Only once both deployments are in; publishing the
168-
# draft is a click on GitHub, and a re-run fails while a release for the tag exists.
192+
# Both deployments are uploaded and validated by now. Publishing one is a JReleaser run given its ID,
193+
# which builds nothing. One leg per deployment, so that a re-run repeats only the leg that failed.
194+
publish:
195+
name: publish (${{ matrix.deployment }})
196+
needs: [jars, native-deploy]
197+
runs-on: ubuntu-latest
198+
strategy:
199+
matrix:
200+
include:
201+
- deployment: jars
202+
id: ${{ needs.jars.outputs.deployment-id }}
203+
- deployment: native
204+
id: ${{ needs.native-deploy.outputs.deployment-id }}
205+
steps:
206+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
207+
with:
208+
fetch-depth: 0
209+
210+
- name: Install JDK 21
211+
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
212+
with:
213+
distribution: temurin
214+
java-version: '21'
215+
216+
- name: Publish the deployment
217+
run: ./gradlew jreleaserDeploy
218+
env:
219+
JRELEASER_MAVENCENTRAL_DEPLOYMENT_ID: ${{ matrix.id }}
220+
JRELEASER_DEPLOY_MAVEN_MAVENCENTRAL_SONATYPE_USERNAME: ${{ secrets.JRELEASER_MAVENCENTRAL_USERNAME }}
221+
JRELEASER_DEPLOY_MAVEN_MAVENCENTRAL_SONATYPE_PASSWORD: ${{ secrets.JRELEASER_MAVENCENTRAL_PASSWORD }}
222+
JRELEASER_GPG_SECRET_KEY: ${{ secrets.JRELEASER_GPG_SECRET_KEY }}
223+
JRELEASER_GPG_PASSPHRASE: ${{ secrets.JRELEASER_GPG_PASSPHRASE }}
224+
225+
- name: Keep JReleaser's log
226+
if: ${{ failure() }}
227+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
228+
with:
229+
name: jreleaser-log-publish-${{ matrix.deployment }}
230+
path: build/jreleaser/trace.log
231+
if-no-files-found: ignore
232+
retention-days: 7
233+
234+
# The plugins depend on the version's jars and native images, so they go up only once Maven Central
235+
# serves both, which takes some minutes after the publish job. The Gradle Plugin Portal never takes a
236+
# version back.
237+
gradle-plugins:
238+
name: Gradle plugins
239+
needs: publish
240+
runs-on: ubuntu-latest
241+
timeout-minutes: 90
242+
steps:
243+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
244+
with:
245+
fetch-depth: 0
246+
247+
- name: Install JDK 21
248+
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
249+
with:
250+
distribution: temurin
251+
java-version: '21'
252+
253+
- name: Wait for Maven Central to serve the version
254+
env:
255+
VERSION: ${{ github.ref_name }}
256+
run: |
257+
for artifact in open-java-format open-java-format-native; do
258+
url="https://repo1.maven.org/maven2/dev/openjavaformat/${artifact}/${VERSION}/${artifact}-${VERSION}.pom"
259+
until curl --silent --fail --head --output /dev/null "$url"; do
260+
echo "Waiting for ${url}"
261+
sleep 30
262+
done
263+
done
264+
265+
- name: Sign and publish the Gradle plugins
266+
run: ./gradlew :gradle-open-java-format:publishPlugins
267+
env:
268+
GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }}
269+
GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }}
270+
JRELEASER_GPG_SECRET_KEY: ${{ secrets.JRELEASER_GPG_SECRET_KEY }}
271+
JRELEASER_GPG_PASSPHRASE: ${{ secrets.JRELEASER_GPG_PASSPHRASE }}
272+
273+
# What Maven Central does not carry — the runnable formatter jar, the Gradle plugins' jar, the IntelliJ
274+
# plugin zip, the Eclipse plugin jar, and every platform's native binary as a plain download — goes into
275+
# a draft GitHub release on the tag, each file signed with the release key. Only once both deployments
276+
# are published; publishing the draft is a click on GitHub, and a re-run fails while a release for the
277+
# tag exists.
169278
github-release:
170279
name: draft GitHub release
171-
needs: [jars, native-deploy]
280+
needs: publish
172281
runs-on: ubuntu-latest
173282
permissions:
174283
contents: write
@@ -190,7 +299,7 @@ jobs:
190299
pattern: native-image-*
191300
merge-multiple: true
192301

193-
- name: Build the IDE plugins and open the draft release
302+
- name: Build the plugins and open the draft release
194303
run: ./gradlew -PreleaseTarget=github -PnativeImages=native-images jreleaserRelease
195304
env:
196305
JRELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ and published from this repository.
2323
| [Maven Central](https://central.sonatype.com/namespace/dev.openjavaformat) | `dev.openjavaformat:open-java-format`, with `-spi`, `-native` and `-jdk-bootstrap` |
2424
| [Gradle Plugin Portal](https://plugins.gradle.org/plugin/dev.openjavaformat.java-format) | `dev.openjavaformat.java-format` |
2525
| [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/34359-open-java-format) | the IntelliJ IDEA plugin |
26-
| [GitHub Releases](https://github.com/openjavaformat/open-java-format/releases/latest) | native binaries, a runnable jar, the IntelliJ and Eclipse plugins, every file signed |
26+
| [GitHub Releases](https://github.com/openjavaformat/open-java-format/releases/latest) | native binaries, a runnable jar, the Gradle, IntelliJ and Eclipse plugins, every file signed |
2727

2828
Why the project exists is in the [manifesto](https://openjavaformat.dev/manifesto/).
2929

‎buildSrc/build.gradle‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ plugins {
22
id 'groovy-gradle-plugin'
33
}
44

5+
// JReleaser's plugin marker and jreleaser-gradle-plugin live on the Gradle Plugin Portal only; everything they
6+
// depend on is on Maven Central, which the Portal merely proxies. That proxy once answered 404 for
7+
// jreleaser-engine and failed a CI build, so Central comes first and is asked directly, as in the root buildscript.
58
repositories {
9+
mavenCentral()
610
gradlePluginPortal()
711
}
812

‎buildSrc/src/main/groovy/open-java-format.release-conventions.gradle‎

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Applied to the root project: signs the staged publications and uploads them to the Maven Central
2-
// Portal. `mise run release` runs it; `mise run release:dry-run` rehearses without uploading.
2+
// Portal. The release workflow, .github/workflows/release.yml, runs it on a tag; --dryrun rehearses
3+
// without uploading.
34
plugins {
45
id 'base'
56
id 'org.jreleaser'
@@ -11,13 +12,21 @@ plugins {
1112
// collected, the default deploys the jars; on a tag both read the same version from it.
1213
//
1314
// `-PreleaseTarget=github` deploys nothing. It is for `jreleaserRelease`, which opens a draft GitHub
14-
// release on the tag with what Maven Central does not carry — the runnable formatter jar, the IntelliJ
15-
// plugin zip, the Eclipse plugin jar and every platform's native binary as a plain download — each file
16-
// signed, plus a checksum file.
15+
// release on the tag with what Maven Central does not carry — the runnable formatter jar, the Gradle
16+
// plugins' jar, the IntelliJ plugin zip, the Eclipse plugin jar and every platform's native binary as a
17+
// plain download — each file signed, plus a checksum file.
1718
//
18-
// gradle-open-java-format is not released here: its plugins go to the Gradle Plugin Portal by hand, with
19-
// `mise run release:gradle-plugin`, once the jars they depend on are published.
19+
// Publishing is a run of its own: with JRELEASER_MAVENCENTRAL_DEPLOYMENT_ID set, JReleaser publishes that
20+
// deployment, which an earlier run uploaded and the Portal validated, and nothing is built. The ID has to
21+
// be an environment variable: JReleaser 1.26 drops a deploymentId set in the DSL. The release workflow
22+
// uploads both deployments first and publishes them only once both are in, so a version whose native
23+
// build fails publishes nothing.
24+
//
25+
// gradle-open-java-format is not deployed here: the release workflow publishes its plugins to the Gradle
26+
// Plugin Portal once Maven Central serves the jars and native images they depend on. The GitHub release
27+
// carries its jar too, built from the same tag.
2028
def releaseTarget = providers.gradleProperty('releaseTarget').getOrElse('jars')
29+
def deploymentToPublish = providers.environmentVariable('JRELEASER_MAVENCENTRAL_DEPLOYMENT_ID').getOrNull()
2130
def releasedProjects
2231
if (releaseTarget == 'jars') {
2332
releasedProjects = [
@@ -79,7 +88,7 @@ jreleaser {
7988
skipTag = true
8089
tagName = '{{projectVersion}}'
8190
releaseName = '{{projectVersion}}'
82-
// Published by hand after a look, like the deployments waiting in the Portal.
91+
// Published by hand after a look.
8392
draft = true
8493
changelog {
8594
formatted = 'ALWAYS'
@@ -100,6 +109,9 @@ jreleaser {
100109
artifact {
101110
path = 'open-java-format/build/libs/open-java-format-{{projectVersion}}-all.jar'
102111
}
112+
artifact {
113+
path = 'gradle-open-java-format/build/libs/gradle-open-java-format-{{projectVersion}}.jar'
114+
}
103115
artifact {
104116
path = 'open-java-format-idea-plugin/build/distributions/open-java-format-idea-plugin-{{projectVersion}}.zip'
105117
}
@@ -131,10 +143,12 @@ jreleaser {
131143
url = 'https://central.sonatype.com/api/v1/publisher'
132144
namespace = 'dev.openjavaformat'
133145
applyMavenCentralRules = true
134-
// Upload and validate, then stop. The deployment waits in the Portal until someone
135-
// presses publish, so a version stays reversible until every one of its deployments
136-
// is in and has been looked at.
137-
stage = 'UPLOAD'
146+
// Upload and wait for the Portal to validate the deployment, then stop; its ID goes to
147+
// build/jreleaser/output.properties as deployMavenCentralSonatypeDeploymentId. Given
148+
// an ID, publish that deployment instead, and end once publishing has started rather
149+
// than waiting, up to half an hour, for Maven Central to serve the files.
150+
stage = deploymentToPublish == null ? 'UPLOAD' : 'PUBLISH'
151+
skipPublicationCheck = true
138152
stagingRepositories.each { stagingRepository(it) }
139153
}
140154
}
@@ -143,15 +157,18 @@ jreleaser {
143157
}
144158

145159
tasks.named('jreleaserDeploy') {
146-
releasedProjects.each { path ->
147-
dependsOn "${path}:publishAllPublicationsToStagingRepository"
160+
// Publishing a deployment that is already uploaded needs nothing built.
161+
if (deploymentToPublish == null) {
162+
releasedProjects.each { path ->
163+
dependsOn "${path}:publishAllPublicationsToStagingRepository"
164+
}
148165
}
149166
}
150167

151168
tasks.named('jreleaserRelease') {
152169
if (releaseTarget == 'github') {
153-
dependsOn ':open-java-format:allJar', ':open-java-format-idea-plugin:buildPlugin',
154-
':open-java-format-eclipse-plugin:jar'
170+
dependsOn ':open-java-format:allJar', ':gradle-open-java-format:jar',
171+
':open-java-format-idea-plugin:buildPlugin', ':open-java-format-eclipse-plugin:jar'
155172
}
156173
}
157174

‎gradle-open-java-format/build.gradle‎

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
// For the Gradle Plugin Portal: publishPlugins, run by hand once the version's jars are published on Maven
2-
// Central — the plugin depends on them. It applies java-gradle-plugin and maven-publish itself.
1+
// For the Gradle Plugin Portal: publishPlugins, run by the release workflow once Maven Central serves the
2+
// version's jars and native images — the plugins depend on them. The key comes from GRADLE_PUBLISH_KEY and
3+
// GRADLE_PUBLISH_SECRET. It applies java-gradle-plugin and maven-publish itself, and once the signing
4+
// plugin is applied it signs what it uploads.
35
apply plugin: 'com.gradle.plugin-publish'
46
apply plugin: 'groovy'
7+
apply plugin: 'signing'
58
apply plugin: 'open-java-format.publishing-conventions'
69

710
description = 'Gradle plugins for open-java-format: formatting tasks, the Spotless step and IntelliJ configuration'
@@ -161,11 +164,27 @@ tasks.named("test").configure {
161164
}
162165
}
163166

164-
// The Portal never takes a version back, so only a release version goes up — X.Y.Z or X.Y.Z.N, as for Maven
165-
// Central in open-java-format.release-conventions. --validate-only checks the metadata of any version.
167+
// Signed with the release key, which also signs the Maven Central artifacts and the GitHub release. Only the
168+
// release job has it, in JRELEASER_GPG_SECRET_KEY and JRELEASER_GPG_PASSPHRASE; anywhere else nothing is
169+
// signed.
170+
def signingKey = providers.environmentVariable('JRELEASER_GPG_SECRET_KEY').getOrNull()
171+
def signed = signingKey != null
172+
signing {
173+
required = false
174+
if (signed) {
175+
useInMemoryPgpKeys(signingKey, providers.environmentVariable('JRELEASER_GPG_PASSPHRASE').get())
176+
}
177+
}
178+
179+
// The Portal never takes a version back, so only a signed release version goes up — X.Y.Z or X.Y.Z.N, as for
180+
// Maven Central in open-java-format.release-conventions. --validate-only checks the metadata of any version.
166181
def portalVersion = provider { project.version.toString() }
167182
tasks.named('publishPlugins') {
168183
doFirst {
184+
if (!validateOnly.getOrElse(false) && !signed) {
185+
throw new GradleException('Refusing to publish unsigned plugins to the Gradle Plugin Portal: set '
186+
+ 'JRELEASER_GPG_SECRET_KEY and JRELEASER_GPG_PASSPHRASE, or pass --validate-only.')
187+
}
169188
if (!validateOnly.getOrElse(false) && !(portalVersion.get() ==~ /\d+\.\d+\.\d+(\.\d+)?/)) {
170189
throw new GradleException("Refusing to publish ${portalVersion.get()} to the Gradle Plugin Portal: check "
171190
+ 'out a release tag on a clean working tree, or pass --validate-only.')

‎gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/spotless/NativePalantirJavaFormatStep.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ String format(ProcessRunner runner, String input) throws IOException, Interrupte
6262
logger.info("Using native-image at {}", execFile);
6363
execSignature = FileSignature.signAsSet(execFile);
6464
List<String> argumentsWithPathToExe =
65-
List.of(execSignature.getOnlyFile().getAbsolutePath(), "--ojf", "-");
65+
List.of(execSignature.getOnlyFile().getAbsolutePath(), "-");
6666
return runner.exec(input.getBytes(StandardCharsets.UTF_8), argumentsWithPathToExe)
6767
.assertExitZero(StandardCharsets.UTF_8);
6868
}

0 commit comments

Comments
 (0)