-
Notifications
You must be signed in to change notification settings - Fork 4k
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
feat(aws-codepipeline): make input and output artifact names optional when creating Actions #845
Conversation
I'm in favor of changing to |
@@ -89,7 +89,7 @@ export abstract class ProjectRef extends cdk.Construct implements events.IEventR | |||
* @param props the properties of the new Action | |||
* @returns the newly created {@link PipelineBuildAction} build Action | |||
*/ | |||
public addBuildToPipeline(stage: codepipeline.IStage, name: string, props: CommonPipelineBuildActionProps): PipelineBuildAction { | |||
public addBuildToPipeline(stage: codepipeline.IStage, name: string, props?: CommonPipelineBuildActionProps): PipelineBuildAction { |
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.
Default props
to {}
.
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.
@@ -68,7 +68,7 @@ export abstract class RepositoryRef extends cdk.Construct { | |||
* @param props the properties of the new Action | |||
* @returns the newly created {@link PipelineSourceAction} | |||
*/ | |||
public addToPipeline(stage: actions.IStage, name: string, props: CommonPipelineSourceActionProps): PipelineSourceAction { | |||
public addToPipeline(stage: actions.IStage, name: string, props?: CommonPipelineSourceActionProps): PipelineSourceAction { |
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.
Default props
to {}
.
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.
@@ -192,13 +211,19 @@ export abstract class Action extends cdk.Construct { | |||
} | |||
} | |||
|
|||
protected addOutputArtifact(name: string): Artifact { | |||
const artifact = new Artifact(this, name); | |||
protected addOutputArtifact(name?: string): 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.
You can instead do:
protected addOutputArtifact(name: string = this.stage._generateOutputArtifactName(this)) {
const artifact = new Artifact(this, outputArtifactName);
retenu 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.
I don't think that will work. I want to call this method from the Action subclasses with props.outputArtifactName
, where outputArtifactName
is an optional string
.
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 think @RomainMuller is right, you should be able to do that. If you pass undefined
to name
(which will be the case if props.outputArtifactName
is not defined, it will apply the default.
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 stand corrected :) will change.
return artifact; | ||
} | ||
|
||
protected addInputArtifact(artifact: Artifact): Action { | ||
this._inputArtifacts.push(artifact); | ||
protected addInputArtifact(artifact?: Artifact): Action { |
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.
Similar sugaring tip as before applies there.
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.
Same reply as above (I'm actually using the optional parameter).
@@ -146,6 +146,14 @@ export class Stage extends cdk.Construct implements actions.IStage { | |||
} | |||
} | |||
|
|||
public _generateOutputArtifactName(action: actions.Action): string { | |||
return (this.pipeline as any)._generateOutputArtifactName(this, action); |
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.
Honestly I don't like having to do this. I'd rather actually make the methods public & mark them s internal-use-only, which is already semi-obvious from their _
-prepended names.
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.
Does it make much difference though?
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.
Main difference is the compiler stops trying to help the moment you've done as any
, so you might miss breaking changes if you rename stuff & there is no test coverage through there.
private _generateOutputArtifactName(stage: actions.IStage, action: actions.Action): string { | ||
// for now, just return a generic output artifact, | ||
// ignoring the names of both the Stage, and the Action | ||
return 'Artifact_' + (++this.artifactsCounter); |
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.
The drawback is that this makes artifact names dependent on generation order. This means a user refactoring code & trying to ensure they didn't inadvertently change their infrastructure cannot rely on a simple diff test.
I would like this to be order-independent unless we have a very good reason why it's better otherwise (I understand this is a simplicity vs. user-friendliness tradeoff).
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 see. What would you see as the artifact name then? Some combination of Stage & Action names? `Artifact_${stage.name}_${action.name}`
?
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.
We can use the uniqueId
of the construct.
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.
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 disadvantage of this: the artifact names are now super ugly (Artifact_awscdkcodepipelinecodecommitcodebuildMyBuildProjectbuild61B48DC4
).
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.
But does anyone care?
|
||
// ignore unused private method (it's actually used in Stage) | ||
// @ts-ignore | ||
private _findInputArtifactFor(stage: actions.IStage, action: actions.Action): actions.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.
The defaulting behavior that involves this particular element has a caveat that changing the runOrder
after the defaulting behavior has happened will cause incorrect values to be used... A solutions is to make the runOder
immutable & set at construction time?
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.
Yep. Not sure if it's a huge deal, as you can always name the artifacts directly, but I'm fine with making runOrder
immutable (the rest of the properties of Actions already are).
Actually, that's a great point. The issue is that |
* The name of the build's output artifact | ||
* The name of the build's output artifact. | ||
* | ||
* @default an auto-generated name will be used | ||
*/ | ||
artifactName?: string; |
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.
+1 on changing this to outputArtifactName
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.
Done (everywhere).
@@ -89,7 +89,7 @@ export abstract class ProjectRef extends cdk.Construct implements events.IEventR | |||
* @param props the properties of the new Action | |||
* @returns the newly created {@link PipelineBuildAction} build Action | |||
*/ | |||
public addBuildToPipeline(stage: codepipeline.IStage, name: string, props: CommonPipelineBuildActionProps): PipelineBuildAction { | |||
public addBuildToPipeline(stage: codepipeline.IStage, name: string, props?: CommonPipelineBuildActionProps): PipelineBuildAction { | |||
return new PipelineBuildAction(this.parent!, name, { |
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.
change this.parent!
to this
please
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.
@@ -68,7 +68,7 @@ export abstract class RepositoryRef extends cdk.Construct { | |||
* @param props the properties of the new Action | |||
* @returns the newly created {@link PipelineSourceAction} | |||
*/ | |||
public addToPipeline(stage: actions.IStage, name: string, props: CommonPipelineSourceActionProps): PipelineSourceAction { | |||
public addToPipeline(stage: actions.IStage, name: string, props?: CommonPipelineSourceActionProps): PipelineSourceAction { | |||
return new PipelineSourceAction(this.parent!, name, { |
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.parent!
=> this
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.
* | ||
* @param action the Action to generate the output artifact name for | ||
*/ | ||
_generateOutputArtifactName(action: Action): string; |
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 wonder if it would be more elegant to hide all these internal methods under some _internal
scope:
interface IStageInternal {
attachAction(...);
generateOutputArtifactName(...);
findInputArtifactFor(...);
};
interface IStage {
// public API
// internal API
_internal: IStageInternal;
}
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.
Done.
@@ -192,13 +211,19 @@ export abstract class Action extends cdk.Construct { | |||
} | |||
} | |||
|
|||
protected addOutputArtifact(name: string): Artifact { | |||
const artifact = new Artifact(this, name); | |||
protected addOutputArtifact(name?: string): 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.
I think @RomainMuller is right, you should be able to do that. If you pass undefined
to name
(which will be the case if props.outputArtifactName
is not defined, it will apply the default.
if (props.artifactName) { | ||
this.artifact = this.addOutputArtifact(props.artifactName); | ||
} | ||
this.artifact = this.addOutputArtifact(props.artifactName); |
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 is a change in behavior. This means that now BuildAction
will always have an output artifact defined for it. Could be totally fine, but what happens if the CodeBuild project doesn't specify any artifacts in buildspec.yaml
but you still define an output 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.
Good call out. I'll check what happens.
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.
Confirmed it doesn't cause any problems - there simply is no output artifact produced from the CodeBuild Project in this case, but the build Action itself succeeds without issues.
private _generateOutputArtifactName(stage: actions.IStage, action: actions.Action): string { | ||
// for now, just return a generic output artifact, | ||
// ignoring the names of both the Stage, and the Action | ||
return 'Artifact_' + (++this.artifactsCounter); |
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.
We can use the uniqueId
of the construct.
|
||
// ignore unused private method (it's actually used in Stage) | ||
// @ts-ignore | ||
private _findInputArtifactFor(stage: actions.IStage, action: actions.Action): actions.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.
add doc that describes what this method is doing
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.
Done.
* | ||
* @param action the Action to find the input artifact for | ||
*/ | ||
_findInputArtifactFor(action: Action): 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.
Rename to findInputArtifact
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.
Done.
* Finds an input artifact for the given Action | ||
* among the existing output artifacts currently in the Pipeline. | ||
* This is an internal operation - | ||
* you should never need to call it directly. |
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.
Provide some details on the algorithm
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.
Done (it's not an easy one to describe...).
b8e701c
to
5cc4f3c
Compare
Updated with review comments. There are 2 more things that could be nice to change while we're in this area:
protected addChild(child: cdk.Construct, name: string) {
super.addChild(child, name);
if (child instanceof Artifact) {
this._outputArtifacts.push(child);
}
}
protected addOutputArtifact(name: string = this.stage._internal._generateOutputArtifactName(this)): Artifact {
const artifact = new Artifact(this, name);
return artifact;
} It kind of abuses the Construct hierarchy, and I have no idea what these extra artifacts would accomplish - I think Action should only take artifacts as arguments when constructing them. I would like to remove this code. @RomainMuller @eladb thoughts on these 2 points? |
Regarding the Regarding the second point, I think I'm okay dropping those if we don't have a clear use-case for them. We can always re-introduce if it turns out to be needed for something that cannot be done another way (unlikely). |
@skinny85 I agree that output artifacts should be defined during initialization and we can get rid of |
Can you elaborate on this @RomainMuller ? When can a CodeBuild project produce multiple artifacts? |
@RomainMuller ping According to a table in the AWS docs, CodeBuild can have a maximum of 1 output. |
@skinny85 well that's interesting... What happens now when your build spec specifies And then, this still leaves me with the question of how do I access this "one" artifact... Right now the only way I found to work is |
I assume they're ignored by CodePipeline.
This is the Do the changes here now make sense? |
Thanks for the clarification, @skinny85. I'm cool with this (and sorry the conversation dragged for so long here). |
5cc4f3c
to
8280ca1
Compare
Rebased & made the two changes we discussed above. |
… when creating Actions. BREAKING CHANGE: this commit contains the following breaking changes: * Rename 'artifactName' in Action construction properties to 'outputArtifactName' * Rename the 'artifact' property of Actions to 'outputArtifact' * No longer allow adding output artifacts to Actions by instantiating the Artifact class * Rename Action#input/outputArtifacts properties to _input/_outputArtifacts Previously, we always required customers to explicitly name the output artifacts the Actions used in the Pipeline, and to explicitly "wire together" the outputs of one Action as inputs to another. With this change, the CodePipeline Construct generates artifact names, if the customer didn't provide one explicitly, and tries to find the first available output artifact to use as input to a newly created Action that needs it, thus turning both the input and output artifacts from required to optional properties.
8280ca1
to
fedc18e
Compare
Missed a usage of the old |
No worries, and thanks for the review! |
__IMPORTANT NOTE__: This release includes a [breaking change](#845) in the AWS CodePipeline construct library: * The `inputArtifacts` and `outputArtifacts` properties of `Action` were intended for internal usage only, and have consequently been renamed to `_inputArtifacts` and `_outputArtifacts` respectively. * The `artifact` property of `Action` classes was renamed to `outputArtifact`. * The `artifactName` property of `Action` classes was renamed to `outputArtifactName`. * It is no longer possible to add output artifacts to `Actions` by instantiating `Artifact`. This release also includes a [fix](#911) for a bug that would make the toolkit unusable for multi-stack applications. In order to benefit from this fix, a globally installed CDK toolkit must also be updated: ```shell $ npm i -g aws-cdk $ cdk --version 0.12.0 (build ...) ``` Like always, you will also need to update your project's library versions: |Language|Update?| |--------|-------| |JavaScript/TypeScript (npm)|[`npx npm-check-updates -u`](https://www.npmjs.com/package/npm-check-updates)| |Java (maven)|[`mvn versions:use-latest-versions`](https://www.mojohaus.org/versions-maven-plugin/use-latest-versions-mojo.html) |.NET (NuGet)|[`nuget update`](https://docs.microsoft.com/en-us/nuget/tools/cli-ref-update) * **aws-cdk:** multi-stack apps can be synthesized or deployed [#911](#911). * **@aws-cdk/aws-codebuild:** allow passing oauth token to GitHubEnterpriseSource [#908](#908) * **@aws-cdk/aws-codepipeline:** make input and output artifact names optional when creating Actions. [#845](#945) * **@aws-cdk/aws-cloudformation:** add permission management to CreateUpdate and Delete Stack CodePipeline Actions. [#880](#880)
* **aws-codebuild:** allow passing oauth token to GitHubEnterpriseSource ([#908](#908)) ([c23da91](c23da91)) * **toolkit:** multi-stack apps cannot be synthesized or deployed ([#911](#911)) ([5511076](5511076)), closes [#868](#868) [#294](#294) [#910](#910) * **aws-cloudformation:** add permission management to CreateUpdate and Delete Stack CodePipeline Actions. ([#880](#880)) ([8b3ae43](8b3ae43)) * **aws-codepipeline:** make input and output artifact names optional when creating Actions. ([#845](#845)) ([3d91c93](3d91c93)) * **aws-codepipeline:** this commit contains the following breaking changes: * Rename 'artifactName' in Action construction properties to 'outputArtifactName' * Rename the 'artifact' property of Actions to 'outputArtifact' * No longer allow adding output artifacts to Actions by instantiating the Artifact class * Rename Action#input/outputArtifacts properties to _input/_outputArtifacts Previously, we always required customers to explicitly name the output artifacts the Actions used in the Pipeline, and to explicitly "wire together" the outputs of one Action as inputs to another. With this change, the CodePipeline Construct generates artifact names, if the customer didn't provide one explicitly, and tries to find the first available output artifact to use as input to a newly created Action that needs it, thus turning both the input and output artifacts from required to optional properties.
* **aws-codebuild:** allow passing oauth token to GitHubEnterpriseSource ([#908](#908)) ([c23da91](c23da91)) * **toolkit:** multi-stack apps cannot be synthesized or deployed ([#911](#911)) ([5511076](5511076)), closes [#868](#868) [#294](#294) [#910](#910) * **aws-cloudformation:** add permission management to CreateUpdate and Delete Stack CodePipeline Actions. ([#880](#880)) ([8b3ae43](8b3ae43)) * **aws-codepipeline:** make input and output artifact names optional when creating Actions. ([#845](#845)) ([3d91c93](3d91c93)) * **aws-codepipeline:** this commit contains the following breaking changes: * Rename 'artifactName' in Action construction properties to 'outputArtifactName' * Rename the 'artifact' property of Actions to 'outputArtifact' * No longer allow adding output artifacts to Actions by instantiating the Artifact class * Rename Action#input/outputArtifacts properties to _input/_outputArtifacts Previously, we always required customers to explicitly name the output artifacts the Actions used in the Pipeline, and to explicitly "wire together" the outputs of one Action as inputs to another. With this change, the CodePipeline Construct generates artifact names, if the customer didn't provide one explicitly, and tries to find the first available output artifact to use as input to a newly created Action that needs it, thus turning both the input and output artifacts from required to optional properties.
Previously, we always required customers to explicitly name the output artifacts the Actions used in the Pipeline, and to explicitly "wire together" the outputs of one Action as inputs to another. With this change, the CodePipeline Construct generates artifact names, if the customer didn't provide one explicitly, and tries to find the first available output artifact to use as input to a newly created Action that needs it, thus turning both the input and output artifacts from required to optional properties.
@eladb @RomainMuller I also had one more suggestion. I've always hated the
artifactName
property name of the Actions. It's misleading, and inconsistent withinputArtifact
. How would you guys feel about a breaking change to rename that property tooutputArtifactName
?By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license.