Skip to content

Commit 54b6cc6

Browse files
authoredDec 14, 2021
feat(codecommit): allow initializing a Repository with contents (#17968)
This repo introduces new properties to the constructor signature of the codecommit L2 Repository. It allows users to upload code when creating a codecommit repository by leveraging the aws-s3-assets lib. The user is able to upload whole directories at the moment. The behaviour has a unit tests, and an integration test, which is passing (verified manually as well). Closes #17967, provides a possible fix to #16958 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 73e5fec commit 54b6cc6

12 files changed

+378
-3
lines changed
 

‎packages/@aws-cdk/aws-codecommit/README.md

+18-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ To add a CodeCommit Repository to your stack:
2121
```ts
2222
import * as codecommit from '@aws-cdk/aws-codecommit';
2323

24-
const repo = new codecommit.Repository(this, 'Repository' ,{
24+
const repo = new codecommit.Repository(this, 'Repository', {
2525
repositoryName: 'MyRepositoryName',
2626
description: 'Some description.', // optional property
2727
});
@@ -37,6 +37,23 @@ To add an Amazon SNS trigger to your repository:
3737
repo.notify('arn:aws:sns:*:123456789012:my_topic');
3838
```
3939

40+
## Add initial commit
41+
42+
It is possible to initialize the Repository via the `Code` class.
43+
It provides methods for loading code from a directory, `.zip` file and from a pre-created CDK Asset.
44+
45+
Example:
46+
47+
```ts
48+
import * as codecommit from '@aws-cdk/aws-codecommit';
49+
import * as path from 'path';
50+
51+
const repo = new codecommit.Repository(this, 'Repository', {
52+
repositoryName: 'MyRepositoryName',
53+
code: codecommit.Code.fromDirectory(path.join(__dirname, 'directory/'), 'develop'), // optional property, branch parameter can be omitted
54+
});
55+
```
56+
4057
## Events
4158

4259
CodeCommit repositories emit Amazon CloudWatch events for certain activities.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import * as fs from 'fs';
2+
import * as path from 'path';
3+
import * as assets from '@aws-cdk/aws-s3-assets';
4+
import { Construct } from 'constructs';
5+
import { CfnRepository } from './codecommit.generated';
6+
7+
/**
8+
* Represents the structure to pass into the underlying CfnRepository class.
9+
*/
10+
export interface CodeConfig {
11+
/**
12+
* represents the underlying code structure
13+
*/
14+
readonly code: CfnRepository.CodeProperty;
15+
}
16+
17+
/**
18+
* Represents the contents to initialize the repository with.
19+
*/
20+
export abstract class Code {
21+
/**
22+
* Code from directory.
23+
* @param directoryPath the path to the local directory containing the contents to initialize the repository with
24+
* @param branch the name of the branch to create in the repository. Default is "main"
25+
*/
26+
public static fromDirectory(directoryPath: string, branch?: string): Code {
27+
const resolvedPath = path.resolve(directoryPath);
28+
29+
const statResult = fs.statSync(resolvedPath);
30+
if (!statResult || !statResult.isDirectory()) {
31+
throw new Error(`'${directoryPath}' needs to be a path to a directory (resolved to: '${resolvedPath }')`);
32+
}
33+
34+
return new PathResolvedCode(resolvedPath, branch);
35+
}
36+
37+
/**
38+
* Code from preexisting ZIP file.
39+
* @param filePath the path to the local ZIP file containing the contents to initialize the repository with
40+
* @param branch the name of the branch to create in the repository. Default is "main"
41+
*/
42+
public static fromZipFile(filePath: string, branch?: string): Code {
43+
const resolvedPath = path.resolve(filePath);
44+
45+
const statResult = fs.statSync(resolvedPath);
46+
if (!statResult || !statResult.isFile()) {
47+
throw new Error(`'${filePath}' needs to be a path to a ZIP file (resolved to: '${resolvedPath }')`);
48+
}
49+
50+
return new PathResolvedCode(resolvedPath, branch);
51+
}
52+
53+
/**
54+
* Code from user-supplied asset.
55+
* @param asset pre-existing asset
56+
* @param branch the name of the branch to create in the repository. Default is "main"
57+
*/
58+
public static fromAsset(asset: assets.Asset, branch?: string): Code {
59+
return new AssetCode(asset, branch);
60+
}
61+
62+
/**
63+
* This method is called after a repository is passed this instance of Code in its 'code' property.
64+
*
65+
* @param scope the binding scope
66+
*/
67+
public abstract bind(scope: Construct): CodeConfig;
68+
}
69+
70+
class PathResolvedCode extends Code {
71+
constructor(private readonly resolvedPath: string, private readonly branch?: string) {
72+
super();
73+
}
74+
75+
public bind(scope: Construct): CodeConfig {
76+
const asset = new assets.Asset(scope, 'PathResolvedCodeAsset', {
77+
path: this.resolvedPath,
78+
});
79+
80+
return (new AssetCode(asset, this.branch)).bind(scope);
81+
}
82+
}
83+
84+
class AssetCode extends Code {
85+
constructor(private readonly asset: assets.Asset, private readonly branch?: string) {
86+
super();
87+
}
88+
89+
public bind(_scope: Construct): CodeConfig {
90+
if (!this.asset.isZipArchive) {
91+
throw new Error('Asset must be a .zip file or a directory (resolved to: ' + this.asset.assetPath + ' )');
92+
}
93+
94+
return {
95+
code: {
96+
branchName: this.branch,
97+
s3: {
98+
bucket: this.asset.s3BucketName,
99+
key: this.asset.s3ObjectKey,
100+
},
101+
},
102+
};
103+
}
104+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './events';
22
export * from './repository';
3+
export * from './code';
34

45
// AWS::CodeCommit CloudFormation Resources:
56
export * from './codecommit.generated';

‎packages/@aws-cdk/aws-codecommit/lib/repository.ts

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as events from '@aws-cdk/aws-events';
33
import * as iam from '@aws-cdk/aws-iam';
44
import { ArnFormat, IResource, Lazy, Resource, Stack } from '@aws-cdk/core';
55
import { Construct } from 'constructs';
6+
import { Code } from './code';
67
import { CfnRepository } from './codecommit.generated';
78

89
/**
@@ -488,6 +489,13 @@ export interface RepositoryProps {
488489
* @default - No description.
489490
*/
490491
readonly description?: string;
492+
493+
/**
494+
* The contents with which to initialize the repository after it has been created.
495+
*
496+
* @default - No initialization (create empty repo)
497+
*/
498+
readonly code?: Code;
491499
}
492500

493501
/**
@@ -552,6 +560,7 @@ export class Repository extends RepositoryBase {
552560
repositoryName: props.repositoryName,
553561
repositoryDescription: props.description,
554562
triggers: Lazy.any({ produce: () => this.triggers }, { omitEmptyArray: true }),
563+
code: (props.code?.bind(this))?.code,
555564
});
556565

557566
this.repositoryName = this.getResourceNameAttribute(repository.attrName);

‎packages/@aws-cdk/aws-codecommit/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
"@aws-cdk/cdk-build-tools": "0.0.0",
8383
"@aws-cdk/cdk-integ-tools": "0.0.0",
8484
"@aws-cdk/cfn2ts": "0.0.0",
85+
"@aws-cdk/cloud-assembly-schema": "0.0.0",
8586
"@aws-cdk/pkglint": "0.0.0",
8687
"@types/jest": "^27.0.3",
8788
"aws-sdk": "^2.848.0",
@@ -91,6 +92,7 @@
9192
"@aws-cdk/aws-codestarnotifications": "0.0.0",
9293
"@aws-cdk/aws-events": "0.0.0",
9394
"@aws-cdk/aws-iam": "0.0.0",
95+
"@aws-cdk/aws-s3-assets": "0.0.0",
9496
"@aws-cdk/core": "0.0.0",
9597
"constructs": "^3.3.69"
9698
},
@@ -99,6 +101,7 @@
99101
"@aws-cdk/aws-codestarnotifications": "0.0.0",
100102
"@aws-cdk/aws-events": "0.0.0",
101103
"@aws-cdk/aws-iam": "0.0.0",
104+
"@aws-cdk/aws-s3-assets": "0.0.0",
102105
"@aws-cdk/core": "0.0.0",
103106
"constructs": "^3.3.69"
104107
},
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Test

‎packages/@aws-cdk/aws-codecommit/test/codecommit.test.ts

+88-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import '@aws-cdk/assert-internal/jest';
2+
import { join, resolve } from 'path';
23
import { Role, ServicePrincipal } from '@aws-cdk/aws-iam';
3-
import { Stack } from '@aws-cdk/core';
4-
import { Repository, RepositoryProps } from '../lib';
4+
import { Asset } from '@aws-cdk/aws-s3-assets';
5+
import * as cxschema from '@aws-cdk/cloud-assembly-schema';
6+
import { App, Stack } from '@aws-cdk/core';
7+
import { Code, Repository, RepositoryProps } from '../lib';
58

69
describe('codecommit', () => {
710
describe('CodeCommit Repositories', () => {
@@ -66,6 +69,89 @@ describe('codecommit', () => {
6669

6770
});
6871

72+
test('Repository can be initialized with contents from a ZIP file', () => {
73+
// GIVEN
74+
const app = new App();
75+
const stack = new Stack(app, 'MyStack');
76+
77+
// WHEN
78+
new Repository(stack, 'Repository', {
79+
repositoryName: 'MyRepositoryName',
80+
code: Code.fromZipFile(join(__dirname, 'asset-test.zip')),
81+
});
82+
83+
// THEN
84+
const assetMetadata = app.synth().tryGetArtifact(stack.stackName)!.findMetadataByType(cxschema.ArtifactMetadataEntryType.ASSET);
85+
expect(assetMetadata).toHaveLength(1);
86+
});
87+
88+
test('Repository can be initialized with contents from a directory', () => {
89+
// GIVEN
90+
const app = new App();
91+
const stack = new Stack(app, 'MyStack');
92+
93+
// WHEN
94+
new Repository(stack, 'Repository', {
95+
repositoryName: 'MyRepositoryName',
96+
code: Code.fromDirectory(join(__dirname, 'asset-test')),
97+
});
98+
99+
// THEN
100+
const assetMetadata = app.synth().tryGetArtifact(stack.stackName)!.findMetadataByType(cxschema.ArtifactMetadataEntryType.ASSET);
101+
expect(assetMetadata).toHaveLength(1);
102+
});
103+
104+
test('Repository can be initialized with contents from an asset', () => {
105+
// GIVEN
106+
const app = new App();
107+
const stack = new Stack(app, 'MyStack');
108+
109+
const readmeAsset = new Asset(stack, 'ReadmeAsset', {
110+
path: join(__dirname, 'asset-test'),
111+
});
112+
113+
// WHEN
114+
new Repository(stack, 'Repository', {
115+
repositoryName: 'MyRepositoryName',
116+
code: Code.fromAsset(readmeAsset),
117+
});
118+
119+
// THEN
120+
const assetMetadata = app.synth().tryGetArtifact(stack.stackName)!.findMetadataByType(cxschema.ArtifactMetadataEntryType.ASSET);
121+
expect(assetMetadata).toHaveLength(1);
122+
});
123+
124+
test('Repository throws Error when initialized with file while expecting directory', () => {
125+
// GIVEN
126+
const app = new App();
127+
const stack = new Stack(app, 'MyStack');
128+
const filePath = join(__dirname, 'asset-test/test.md');
129+
130+
// THEN
131+
expect(() => {
132+
new Repository(stack, 'Repository', {
133+
repositoryName: 'MyRepositoryName',
134+
code: Code.fromDirectory(filePath),
135+
});
136+
}).toThrow(`'${filePath}' needs to be a path to a directory (resolved to: '${resolve(filePath)}')`);
137+
});
138+
139+
test('Repository throws Error when initialized with directory while expecting file', () => {
140+
// GIVEN
141+
const app = new App();
142+
const stack = new Stack(app, 'MyStack');
143+
144+
const dirPath = join(__dirname, 'asset-test/');
145+
146+
// THEN
147+
expect(() => {
148+
new Repository(stack, 'Repository', {
149+
repositoryName: 'MyRepositoryName',
150+
code: Code.fromZipFile(dirPath),
151+
});
152+
}).toThrow(`'${dirPath}' needs to be a path to a ZIP file (resolved to: '${resolve(dirPath)}')`);
153+
});
154+
69155
/**
70156
* Fix for https://github.com/aws/aws-cdk/issues/10630
71157
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"Resources": {
3+
"Repo02AC86CF": {
4+
"Type": "AWS::CodeCommit::Repository",
5+
"Properties": {
6+
"RepositoryName": "aws-cdk-codecommit-repo-contents-zip-file",
7+
"Code": {
8+
"S3": {
9+
"Bucket": {
10+
"Ref": "AssetParametersea7c70c09e0d23ef6105931ee931effc8b607184343aebf5e45e972807b3fc18S3Bucket361A4B4D"
11+
},
12+
"Key": {
13+
"Fn::Join": [
14+
"",
15+
[
16+
{
17+
"Fn::Select": [
18+
0,
19+
{
20+
"Fn::Split": [
21+
"||",
22+
{
23+
"Ref": "AssetParametersea7c70c09e0d23ef6105931ee931effc8b607184343aebf5e45e972807b3fc18S3VersionKeyFDE2007C"
24+
}
25+
]
26+
}
27+
]
28+
},
29+
{
30+
"Fn::Select": [
31+
1,
32+
{
33+
"Fn::Split": [
34+
"||",
35+
{
36+
"Ref": "AssetParametersea7c70c09e0d23ef6105931ee931effc8b607184343aebf5e45e972807b3fc18S3VersionKeyFDE2007C"
37+
}
38+
]
39+
}
40+
]
41+
}
42+
]
43+
]
44+
}
45+
}
46+
}
47+
}
48+
}
49+
},
50+
"Parameters": {
51+
"AssetParametersea7c70c09e0d23ef6105931ee931effc8b607184343aebf5e45e972807b3fc18S3Bucket361A4B4D": {
52+
"Type": "String",
53+
"Description": "S3 bucket for asset \"ea7c70c09e0d23ef6105931ee931effc8b607184343aebf5e45e972807b3fc18\""
54+
},
55+
"AssetParametersea7c70c09e0d23ef6105931ee931effc8b607184343aebf5e45e972807b3fc18S3VersionKeyFDE2007C": {
56+
"Type": "String",
57+
"Description": "S3 key for asset version \"ea7c70c09e0d23ef6105931ee931effc8b607184343aebf5e45e972807b3fc18\""
58+
},
59+
"AssetParametersea7c70c09e0d23ef6105931ee931effc8b607184343aebf5e45e972807b3fc18ArtifactHash21ADA702": {
60+
"Type": "String",
61+
"Description": "Artifact hash for asset \"ea7c70c09e0d23ef6105931ee931effc8b607184343aebf5e45e972807b3fc18\""
62+
}
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as cdk from '@aws-cdk/core';
2+
import * as codecommit from '../lib';
3+
import { Code } from '../lib';
4+
5+
const app = new cdk.App();
6+
const stack = new cdk.Stack(app, 'aws-cdk-codecommit-repo-contents-zip-file');
7+
8+
new codecommit.Repository(stack, 'Repo', {
9+
repositoryName: 'aws-cdk-codecommit-repo-contents-zip-file',
10+
code: Code.fromZipFile('./asset-test.zip'),
11+
});
12+
13+
app.synth();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"Resources": {
3+
"Repo02AC86CF": {
4+
"Type": "AWS::CodeCommit::Repository",
5+
"Properties": {
6+
"RepositoryName": "aws-cdk-codecommit-repo-contents-assets",
7+
"Code": {
8+
"S3": {
9+
"Bucket": {
10+
"Ref": "AssetParameters32b8e8a8b79a84deb31e4d456dbcf3e40937f201633ae38c9e90e15b82084ae3S3BucketD4E005C8"
11+
},
12+
"Key": {
13+
"Fn::Join": [
14+
"",
15+
[
16+
{
17+
"Fn::Select": [
18+
0,
19+
{
20+
"Fn::Split": [
21+
"||",
22+
{
23+
"Ref": "AssetParameters32b8e8a8b79a84deb31e4d456dbcf3e40937f201633ae38c9e90e15b82084ae3S3VersionKey52BCEABD"
24+
}
25+
]
26+
}
27+
]
28+
},
29+
{
30+
"Fn::Select": [
31+
1,
32+
{
33+
"Fn::Split": [
34+
"||",
35+
{
36+
"Ref": "AssetParameters32b8e8a8b79a84deb31e4d456dbcf3e40937f201633ae38c9e90e15b82084ae3S3VersionKey52BCEABD"
37+
}
38+
]
39+
}
40+
]
41+
}
42+
]
43+
]
44+
}
45+
}
46+
}
47+
}
48+
}
49+
},
50+
"Parameters": {
51+
"AssetParameters32b8e8a8b79a84deb31e4d456dbcf3e40937f201633ae38c9e90e15b82084ae3S3BucketD4E005C8": {
52+
"Type": "String",
53+
"Description": "S3 bucket for asset \"32b8e8a8b79a84deb31e4d456dbcf3e40937f201633ae38c9e90e15b82084ae3\""
54+
},
55+
"AssetParameters32b8e8a8b79a84deb31e4d456dbcf3e40937f201633ae38c9e90e15b82084ae3S3VersionKey52BCEABD": {
56+
"Type": "String",
57+
"Description": "S3 key for asset version \"32b8e8a8b79a84deb31e4d456dbcf3e40937f201633ae38c9e90e15b82084ae3\""
58+
},
59+
"AssetParameters32b8e8a8b79a84deb31e4d456dbcf3e40937f201633ae38c9e90e15b82084ae3ArtifactHash1A78403B": {
60+
"Type": "String",
61+
"Description": "Artifact hash for asset \"32b8e8a8b79a84deb31e4d456dbcf3e40937f201633ae38c9e90e15b82084ae3\""
62+
}
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as cdk from '@aws-cdk/core';
2+
import * as codecommit from '../lib';
3+
import { Code } from '../lib';
4+
5+
const app = new cdk.App();
6+
const stack = new cdk.Stack(app, 'aws-cdk-codecommit-repo-contents-assets');
7+
8+
new codecommit.Repository(stack, 'Repo', {
9+
repositoryName: 'aws-cdk-codecommit-repo-contents-assets',
10+
code: Code.fromDirectory('./asset-test'),
11+
});
12+
13+
app.synth();

0 commit comments

Comments
 (0)
Please sign in to comment.