-
Notifications
You must be signed in to change notification settings - Fork 74
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
Add Artifacts Manifest Extension #1649
Add Artifacts Manifest Extension #1649
Conversation
Generate changelog in
|
@@ -0,0 +1,5 @@ | |||
type: improvement | |||
improvement: | |||
description: add artifacts manifest extension |
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.
nice - could we include a mention (and maybe example) in the README so that if people search for this they'll find something?
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.
Looks neat to me! Just need a real +1 from @CRogers / other BE infra maintainer
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.
Thanks for putting this together.
A few comments below.
Can you also add a test to CreateManifestTaskIntegrationSpec
, so we can be sure that it works when invoked via the buildscript? It will also ensure that we don't inadvertently break this feature in the future.
@@ -154,6 +163,7 @@ final void createManifest() throws IOException { | |||
.productVersion(getProjectVersion()) | |||
.putAllExtensions(getManifestExtensions().get()) | |||
.putExtensions("product-dependencies", productDependencies) | |||
.putExtensions("artifacts", artifacts) |
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.
as a safety mechanism, I think we should check to see if there is any artifacts
extension currently, and then throw telling users to use the new thing instead of silently failing. As whatever they put before will be clobbered.
|
||
```gradle | ||
distribution { | ||
artifact { |
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.
is it artifacts
or artifact
? going from the above statement:
You can specify container images that your product requires using the
artifacts
declaration on thedistribution
@@ -75,6 +77,7 @@ public BaseDistributionExtension(Project project) { | |||
productDependencies = project.getObjects().listProperty(ProductDependency.class); | |||
optionalProductDependencies = project.getObjects().setProperty(ProductId.class); | |||
ignoredProductDependencies = project.getObjects().setProperty(ProductId.class); | |||
artifacts = project.getObjects().setProperty(ArtifactLocator.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.
let's make this a DomainObjectSet
. You can get an instance of one via project.getObjects().domainObjectSet()
. It allows you to have listeners on things and some convenient methods which I will discuss below.
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.
you would also set up your configureEach
/whenObjectAdded
here.
uriIsValid(uri); | ||
} | ||
|
||
public static void uriIsValid(String uri) { |
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.
nit: private
} | ||
|
||
public final void artifact(ArtifactLocator artifactLocator) { | ||
artifactLocator.isValid(); |
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 believe you want to do this here, because there's nothing stopping people from using the getArtifacts()
method and adding to the set directly.
You can either:
- remove direct access to
getArtifacts()
by making it private/package-private - although this might not be foolproof considering that gradle buildscripts can access things - or, use a
configureEach
orwhenObjectAdded
on the named domain object set. Then it doesn't matter howArtifactLocator
ends up in the set, it will then be validated before anyone can do anything with it.
import java.util.Objects; | ||
|
||
@JsonIgnoreProperties(ignoreUnknown = true) | ||
public final class ArtifactLocator implements Serializable { |
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.
You should make this a managed type. This will allow you to have lazily set properties and not have to get/set in an afterEvaluate
in your consumers. Yes, it is different from all the others, however, it's a bit of a pain to migrate all the others.
This will crop up in your PR when you try to use this new thing.
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.
You will then have to have a separate type for json serialization, but you won't have to expose that to consumers.
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.
Huh, looks like the docs aren't there for Gradle 8.8. For myself, I'm linking them for 7.6.2: https://docs.gradle.org/7.6.2/userguide/custom_gradle_types.html#managed_types
|
||
/** Lazily configures and adds a {@link ArtifactLocator}. */ | ||
public final void artifact(@DelegatesTo(ArtifactLocator.class) Closure<ArtifactLocator> closure) { | ||
artifacts.add(providerFactory.provider(() -> { |
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.
this simplifies to just:
create the ArtifactLocator
You will need to do something like project.getObjects().newInstance(ArtifactLocator.class)
to make Gradle instantiate and "managed" your managed type for you.
configure it
can just do your project.configure(artifactLocator, closure)
, and you don't have to worry about wrapping that in a provider since the underlying type is lazy
just add it
add it to the domain object set, and the validation in the configureEach
or whenObjectAdded
will take over.
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.
you should also add a artifact(Action<ArtifactLocator> action) { ... }
method. That gives you a type safe way of adding it to the set when using Java. Buildscript will likely pick the Closure
form.
Instead of project.configure(...)
you end up just doing action.execute(locator)
.
return artifacts; | ||
} | ||
|
||
public final void artifact(ArtifactLocator artifactLocator) { |
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.
With the whole managed type thing, it becomes a lot more cumbersome for consumers to make this as it needs to be instantiated via gradle. So you should get rid of this method and make them use the closure/action methods below.
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.
if we go down the whole 'managed type' route, is it still convenient to interact with these APIs from other java codebases??
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.
yes, it's how all new(er) plugins are written and also preferred way from Gradle. You have all the lazy safe mutators via map
and flatMap
etc.
As argued in some other comment, not having Property
means that you can never be sure when the value of things inside ArtifactLocator
is finally set. Depending on when your plugin is applied and when someone or something might change the value that ArtifactLocator
depends on, means that you often end up having to write afterEvaluate
which mostly sucks.
```gradle | ||
distribution { | ||
artifact { | ||
type = 'oci' |
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.
is there any point of having the user specify oci
? is it always oci
?
Regardless of whether I think we include it or not. We should document what other options there are and what effect it has.
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 added some notes about this in the readme, but the short answer is that I wanted to match what we do in the SLS spec. I'd prefer being a little verbose over baking in an assumption that "oci" will be the only type we ever support
readme.md
Outdated
} | ||
``` | ||
|
||
The result will be embedded in the `deployment/manifest.yml` file. |
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.
For manifest consumers, it might be worth giving a snippet of what this does to the manifest.yml
file. Doesn't have to be the whole file, just the basic required ones and what it adds to the extensions
object or whatever it's supposed to be.
def 'write artifacts to manifest'() { | ||
buildFile << """ | ||
distribution { | ||
${ARTIFACT} |
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 inline
private void validateEmptyArtifactsExtension() { | ||
Preconditions.checkArgument( | ||
!getManifestExtensions().get().containsKey("artifacts"), | ||
"Artifacts " | ||
+ "specified directly the using the manifest-extensions block in the 'distributions' " | ||
+ "extension will be overwritten! Please use the 'artifact' closure in the 'distributions' " | ||
+ "extension to add artifacts instead."); | ||
} | ||
|
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.
talked offline, please rephrase
import java.util.Objects; | ||
|
||
@JsonIgnoreProperties(ignoreUnknown = true) | ||
public final class JsonArtifactLocator implements Serializable { |
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.
offline: get rid of serializable and don't use this in the gradle type
Invalidated by push of b1aacdf
👍 |
Before this PR
Automatic discovery of the "main container image" has been deprecated for a while now.
After this PR
==COMMIT_MSG==
Explicitly list main container image in artifacts extension
==COMMIT_MSG==
Possible downsides?