-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[#4][#6] Close and release tasks #9
Conversation
connectTimeout.set(extension.connectTimeout) | ||
this.onlyIf { extension.useStaging.getOrElse(false) } | ||
} | ||
private val stagingRepositoryId: Property<String> = repository.stagingRepositoryId |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about the best way to bind stagingRepositoryId in InitializeNexusStagingRepository
and Repository.stagingRepositoryId
, to be able to set value in task to set it in Extension/Repository. I initially just set stagingRepositoryId: Property<String> = repository.stagingRepositoryId
, but I don't like sharing a reference, so maybe you have any better idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
= objects.property().convention(repository.stagingRepositoryId)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, Unless I don't understand convention()
properly, I will not be able to propagate value back to repository once it is set in the task. I need the other way round connection: task -> repository.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, now I understand your intention.
Can you please clarify how do you mean the task should be used?
I think the user does not know stagingRepositoryId
before the task is executed, so I don't see how the user would configure the property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
User could override the property in extension.repository to for example close or releate a requested staging repository with id known from the previous (failed) closing operation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How problematic would it be to do the clean up in the second call? E.g.
gw release* -PyourID* && gw dropAllRepositories*
(assuming there will be that task one day)
While technically it is possible to call close/release/drop commands in Nexus with multiple repository IDs it's not a case with simple init/publish/close/release use case. On the other hand I can't find any sensible obstacles to use ListProperty<String>
in the extension and have init to set just one element (there could be also some helper method to set just one).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How problematic would it be to do the clean up in the second call?
Users are not always comfortable with storing passwords/keys, so "release / drop" might require the user to type the password (or provide it with environment/property).
So your example with &&
would be much more complicated, as there are multiple credentials involved. In Apache case the credentials are for dist.apache.org
(SVN), repository.apache.org
(Nexus), gitbox.apache.org
(Git) all of which participate in the release candidate/release publish.
Having a single command helps there.
Then there's "error recovery". If the actions are split into multiple commands, it makes it more complicated to "resume after failure". It looks like the user would have to analyze which command failed in order to retry or rollback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this property in this class at all? Wouldn't it suffice to set it in NexusRepository
? In addition, I think we should write it to a file and read it from there in NexusRepository
by default. That way, one could initialize and publish in the first Gradle invocation, do some manual testing, and close and release without passing the stagingRepositoryId
manually. Thought?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently NexusRepository
is only passed to the constructor to collect some required values. The reference is not kept. However, if you are fine with that I will refactor the code.
Regarding keeping it in the file I propose to postpone it to Milestone 2 - it's not MVP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed.
@Disabled("Broken - must run only after publishMavenJavaPublicationToSonatypeRepository not publishToSonatypeRepository") | ||
internal fun `close task should run after related publish`() { | ||
initSingleProjectWithDefaultConfiguration() | ||
assertGivenTaskMustRunAfterAnother("closeSonatypeStagingRepository", "publishToSonatypeRepository") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
publishToNexusTask (and close/release tasks) depends/mustRunAfter publishMavenJavaPublicationToSonatypeRepository. It would be probably better to use publishToSonatypeRepository to also cover PDF documentation update, CDC stubs, etc. @marcphilipp How would be tweak it to cover that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm afraid I don't understand your question. Could you please elaborate a bit more?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. How the easiest would be to change the plugin implementation to make closeSonatypeStagingRepository
depends on publishToSonatypeRepository
instead of all the related publications separately (e.g. publishMavenJavaPublicationToSonatypeRepository
, publishPdfDocumentationPublicationToSonatypeRepository
)?
It's not a big deal though as a publication for PDF documents should be also covered by:
val publishTasks = project.tasks
.withType<PublishToMavenRepository>()
.matching { it.repository == nexusRepository }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just add closeTask.mustRunAfter(publishToNexusTask)
and the same for releaseTask
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would be best to get a reference/provider to the umbrella publish task in the production plugin code?
Hmm, is it possible to switch to "draft PR" once a PR is created? |
Initial version.
27dc786
to
48db8ad
Compare
src/main/kotlin/io/github/gradlenexus/publishplugin/InitializeNexusStagingRepository.kt
Outdated
Show resolved
Hide resolved
src/main/kotlin/io/github/gradlenexus/publishplugin/NexusRepository.kt
Outdated
Show resolved
Hide resolved
src/main/kotlin/io/github/gradlenexus/publishplugin/ReleaseNexusStagingRepository.kt
Outdated
Show resolved
Hide resolved
@@ -97,6 +97,28 @@ class NexusClient(private val baseUrl: URI, username: String?, password: String? | |||
} | |||
} | |||
|
|||
fun closeStagingRepository(stagingRepositoryId: String) { | |||
try { | |||
val response = api.closeStagingRepo(Dto(StagingRepositoryToTransit(listOf(stagingRepositoryId), "Closed by io.github.gradle-nexus.publish-plugin Gradle plugin"))).execute() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wish there was a more-or-less meaningful (configurable?) comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please raise an issue for that. Could be implemented in the future - not an MVP.
try { | ||
val response = api.closeStagingRepo(Dto(StagingRepositoryToTransit(listOf(stagingRepositoryId), "Closed by io.github.gradle-nexus.publish-plugin Gradle plugin"))).execute() | ||
if (!response.isSuccessful) { | ||
throw failure("close staging repository", response) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should failure message include the repository id / group id?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I plan to make error handling for general for all the calls.
src/main/kotlin/io/github/gradlenexus/publishplugin/internal/NexusClient.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't have time for a full review, yet, but added some comments. I'll continue tomorrow. 🙂
src/compatTest/kotlin/io/github/gradlenexus/publishplugin/NexusPublishPluginTests.kt
Show resolved
Hide resolved
src/main/kotlin/io/github/gradlenexus/publishplugin/BaseOperationOnNexusStagingRepository.kt
Outdated
Show resolved
Hide resolved
protected val clientTimeout: Property<Duration> = objects.property() | ||
|
||
@get:Internal | ||
protected val connectTimeout: Property<Duration> = objects.property() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ Maybe we should extract these properties into a @Nested
NexusConfiguration
object instead of using a base class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One could want to have a separated timeout per repository. However, there still should be an easy way to set it globally (as in most of the cases it is enough - in fact most of the users (except @vlsi ;) ) will only use one repository per build).
src/main/kotlin/io/github/gradlenexus/publishplugin/CloseNexusStagingRepository.kt
Outdated
Show resolved
Hide resolved
@Disabled("Broken - must run only after publishMavenJavaPublicationToSonatypeRepository not publishToSonatypeRepository") | ||
internal fun `close task should run after related publish`() { | ||
initSingleProjectWithDefaultConfiguration() | ||
assertGivenTaskMustRunAfterAnother("closeSonatypeStagingRepository", "publishToSonatypeRepository") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm afraid I don't understand your question. Could you please elaborate a bit more?
connectTimeout.set(extension.connectTimeout) | ||
this.onlyIf { extension.useStaging.getOrElse(false) } | ||
} | ||
private val stagingRepositoryId: Property<String> = repository.stagingRepositoryId |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this property in this class at all? Wouldn't it suffice to set it in NexusRepository
? In addition, I think we should write it to a file and read it from there in NexusRepository
by default. That way, one could initialize and publish in the first Gradle invocation, do some manual testing, and close and release without passing the stagingRepositoryId
manually. Thought?
By keeping a reference to NexusRepository.
See: diffplug/spotless#476 As a side effect import-ordering has been disabled to do not break the build. It changed to fail after upgrade. I don't want to mix that PR with global import reordering.
@get:Internal | ||
protected val connectTimeout: Property<Duration> = objects.property() | ||
|
||
private val repository: Property<NexusRepository> = objects.property() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@marcphilipp Have you thought about something like to that to keep NexusRepository
in a task? Our previous conversation disappeared :-/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, we could use @Nested
and remove all the fields that just copy its properties. Doing both looks redundant to me.
Using a command line switch.
val client = NexusClient(repository.get().nexusUrl.get(), repository.get().username.orNull, repository.get().password.orNull, clientTimeout.orNull, connectTimeout.orNull) | ||
val stagingProfileId = determineAndCacheStagingProfileId(client) | ||
logger.info("Closing staging repository with id '{}' for stagingProfileId '{}'", stagingRepositoryConfig.get().repositoryId, stagingProfileId) | ||
client.closeStagingRepository(stagingRepositoryConfig.get().repositoryId!!) //should be checked by @Input |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't feel comfortable with using !!
and mutable structures (with nullable fields) at all. However, those 2 properties can be set independently and in addition they can be null
(in some time periods - set by the earlier task execution or overridden with @Option
). Nevertheless, I would like to hear any constructive proposal how to deal with it in some "better way".
- Remove stagingProfileId from close/release tasks as it is not needed - Wire stagingRepositoryId for close/release task in plugin - Remove mutable state from NexusRepository - Rename CLI option to --staging-repository-id to make it more idiomatic
Applied @marcphilipp changes from #11. |
Merging conflicts due to the recent changes (spotless upgrade and import reordering) in master :-/. Will fix. |
Sorry! 😬 |
@@ -40,7 +39,6 @@ constructor( | |||
private val stagingRepositoryId: (String) -> Unit | |||
) : AbstractNexusStagingRepositoryTask(objects, extension, repository) { | |||
|
|||
@Optional |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's only required if stagingProfileId
is not set. Thus, I think @Optional
was correct, wasn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, they both can be not set and then there will be val packageGroup = packageGroup.get()
called. However, it would be problematic to add two-field present validation, so probably packageGroup.get()
is good enough, especially as it reports the name of the not provided property. I will restore @Optional
.
3dc3036
to
5b2c8ce
Compare
The initial implementation of close and release tasks. It's effectively broken with the real Nexus instance (due to missing retrier to handle "in transition" periods - #7), but I wanted to cover it in another PR as I had some issue with Kotlin and I'm not sure about some Gradle related bindings here. Therefore, I would like to get faster feedback for the following code.
In the next PR I plan to work on: