@cloudsnorkel/cdk-github-runners 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/.gitattributes +6 -1
  2. package/.jsii +1663 -596
  3. package/API.md +1105 -107
  4. package/README.md +63 -48
  5. package/SETUP_GITHUB.md +56 -19
  6. package/demo-thumbnail.jpg +0 -0
  7. package/lib/index.d.ts +3 -2
  8. package/lib/index.js +7 -1
  9. package/lib/lambdas/build-image/index.js +121 -0
  10. package/lib/lambdas/delete-runner/index.js +29 -15
  11. package/lib/lambdas/setup/index.js +9103 -0
  12. package/lib/lambdas/status/index.js +33 -14
  13. package/lib/lambdas/token-retriever/index.js +20 -10
  14. package/lib/lambdas/update-lambda/index.js +55 -0
  15. package/lib/lambdas/webhook-handler/index.js +21 -8
  16. package/lib/providers/codebuild.d.ts +32 -3
  17. package/lib/providers/codebuild.js +58 -13
  18. package/lib/providers/common.d.ts +87 -7
  19. package/lib/providers/common.js +64 -4
  20. package/lib/providers/docker-images/codebuild/linux-arm64/Dockerfile +59 -0
  21. package/lib/providers/docker-images/codebuild/{Dockerfile → linux-x64/Dockerfile} +10 -5
  22. package/lib/providers/docker-images/fargate/linux-arm64/Dockerfile +41 -0
  23. package/lib/providers/docker-images/fargate/{runner.sh → linux-arm64/runner.sh} +0 -0
  24. package/lib/providers/docker-images/fargate/{Dockerfile → linux-x64/Dockerfile} +10 -5
  25. package/lib/providers/docker-images/fargate/linux-x64/runner.sh +5 -0
  26. package/lib/providers/docker-images/lambda/linux-arm64/Dockerfile +32 -0
  27. package/lib/providers/docker-images/lambda/{runner.js → linux-arm64/runner.js} +0 -0
  28. package/lib/providers/docker-images/lambda/{runner.sh → linux-arm64/runner.sh} +0 -0
  29. package/lib/providers/docker-images/lambda/linux-x64/Dockerfile +31 -0
  30. package/lib/providers/docker-images/lambda/linux-x64/runner.js +29 -0
  31. package/lib/providers/docker-images/lambda/linux-x64/runner.sh +12 -0
  32. package/lib/providers/fargate.d.ts +46 -2
  33. package/lib/providers/fargate.js +65 -10
  34. package/lib/providers/image-builders/codebuild.d.ts +170 -0
  35. package/lib/providers/image-builders/codebuild.js +340 -0
  36. package/lib/providers/image-builders/static.d.ts +29 -0
  37. package/lib/providers/image-builders/static.js +58 -0
  38. package/lib/providers/lambda.d.ts +27 -2
  39. package/lib/providers/lambda.js +88 -9
  40. package/lib/runner.d.ts +5 -16
  41. package/lib/runner.js +38 -26
  42. package/lib/secrets.d.ts +4 -1
  43. package/lib/secrets.js +12 -2
  44. package/lib/utils.d.ts +2 -2
  45. package/lib/utils.js +14 -3
  46. package/lib/webhook.d.ts +0 -1
  47. package/lib/webhook.js +2 -1
  48. package/package.json +12 -10
  49. package/changelog.md +0 -11
  50. package/lib/index.d.ts.map +0 -1
  51. package/lib/providers/codebuild.d.ts.map +0 -1
  52. package/lib/providers/common.d.ts.map +0 -1
  53. package/lib/providers/docker-images/lambda/Dockerfile +0 -27
  54. package/lib/providers/fargate.d.ts.map +0 -1
  55. package/lib/providers/lambda.d.ts.map +0 -1
  56. package/lib/runner.d.ts.map +0 -1
  57. package/lib/secrets.d.ts.map +0 -1
  58. package/lib/utils.d.ts.map +0 -1
  59. package/lib/webhook.d.ts.map +0 -1
  60. package/releasetag.txt +0 -1
  61. package/version.txt +0 -1
package/README.md CHANGED
@@ -10,32 +10,37 @@
10
10
 
11
11
  Use this CDK construct to create ephemeral [self-hosted GitHub runners][1] on-demand inside your AWS account.
12
12
 
13
- * Easy to configure GitHub integration
13
+ * Easy to configure GitHub integration with a web-based interface
14
14
  * Customizable runners with decent defaults
15
- * Supports multiple runner configurations controlled by labels
15
+ * Multiple runner configurations controlled by labels
16
16
  * Everything fully hosted in your account
17
17
 
18
18
  Self-hosted runners in AWS are useful when:
19
19
 
20
20
  * You need easy access to internal resources in your actions
21
21
  * You want to pre-install some software for your actions
22
- * You want to provide some basic AWS API access ([aws-actions/configure-aws-credentials][2] has more security controls)
22
+ * You want to provide some basic AWS API access (but [aws-actions/configure-aws-credentials][2] has more security controls)
23
23
 
24
- Ephemeral runners are the [recommended way by GitHub][14] for auto-scaling, and they make sure all jobs run with a clean image. Runners are started on-demand. You don't pay unless a job is running.
24
+ Ephemeral (or on-demand) runners are the [recommended way by GitHub][14] for auto-scaling, and they make sure all jobs run with a clean image. Runners are started on-demand. You don't pay unless a job is running.
25
25
 
26
26
  ## API
27
27
 
28
- Documentation of available constructs and their interface is available on [Constructs Hub][13] in all supported programming languages.
28
+ The best way to browse API documentation is on [Constructs Hub][13]. It is available in all supported programming languages.
29
29
 
30
30
  ## Providers
31
31
 
32
32
  A runner provider creates compute resources on-demand and uses [actions/runner][5] to start a runner.
33
33
 
34
- | Provider | Time limit | vCPUs | RAM | Storage | sudo | Docker |
35
- |-----------|--------------------------|--------------------------|-----------------------------------|------------------------------|------|--------|
36
- | CodeBuild | 8 hours (default 1 hour) | 2 (default), 4, 8, or 72 | 3gb (default), 7gb, 15gb or 145gb | 50gb to 824gb (default 64gb) | ✔ | ✔ |
37
- | Fargate | Unlimited | 0.25 to 4 (default 1) | 512mb to 30gb (default 2gb) | 20gb to 200gb (default 25gb) | ✔ | ❌ |
38
- | Lambda | 15 minutes | 1 to 6 (default 2) | 128mb to 10gb (default 2gb) | Up to 10gb (default 10gb) | ❌ | ❌ |
34
+ | | CodeBuild | Fargate | Lambda |
35
+ |------------------|--------------------------|----------------|----------------|
36
+ | **Time limit** | 8 hours | Unlimited | 15 minutes |
37
+ | **vCPUs** | 2, 4, 8, or 72 | 0.25 to 4 | 1 to 6 |
38
+ | **RAM** | 3gb, 7gb, 15gb, or 145gb | 512mb to 30gb | 128mb to 10gb |
39
+ | **Storage** | 50gb to 824gb | 20gb to 200gb | Up to 10gb |
40
+ | **Architecture** | x86_64, ARM64 | x86_64, ARM64 | x86_64, ARM64 |
41
+ | **sudo** | ✔ | ✔ | ❌ |
42
+ | **Docker** | ✔ | ❌ | ❌ |
43
+ | **Spot pricing** | ❌ | ✔ | ❌ |
39
44
 
40
45
  The best provider to use mostly depends on your current infrastructure. When in doubt, CodeBuild is always a good choice. Execution history and logs are easy to view, and it has no restrictive limits unless you need to run for more than 8 hours.
41
46
 
@@ -72,63 +77,73 @@ You can also create your own provider by implementing `IRunnerProvider`.
72
77
  4. Deploy your stack
73
78
  5. Look for the status command output similar to `aws --region us-east-1 lambda invoke --function-name status-XYZ123 status.json`
74
79
  6. Execute the status command (you may need to specify `--profile` too) and open the resulting `status.json` file
75
- 7. [Setup GitHub](SETUP_GITHUB.md) integration as an app or with personal access token
80
+ 7. Open the URL in `github.setup.url` from `status.json` or [manually setup GitHub](SETUP_GITHUB.md) integration as an app or with personal access token
76
81
  8. Run status command again to confirm `github.auth.status` and `github.webhook.status` are OK
77
82
  9. Trigger a GitHub action that has a `self-hosted` label with `runs-on: [self-hosted, linux, codebuild]` or similar
78
83
  10. If the action is not successful, see [troubleshooting](#Troubleshooting)
79
84
 
85
+ [![Demo](demo-thumbnail.jpg)](https://youtu.be/wlyv_3V8lIw)
86
+
80
87
  ## Customizing
81
88
 
82
- The default providers configured by [`GitHubRunners`](https://constructs.dev/packages/@cloudsnorkel/cdk-github-runners/v/0.0.11/api/GitHubRunners?lang=typescript) are useful for testing but probably not too much for actual production work. They run in the default VPC or no VPC and have no added IAM permissions. You would usually want to configure the providers yourself.
89
+ The default providers configured by `GitHubRunners` are useful for testing but probably not too much for actual production work. They run in the default VPC or no VPC and have no added IAM permissions. You would usually want to configure the providers yourself.
83
90
 
84
91
  For example:
85
92
 
86
93
  ```typescript
87
- import * as cdk from 'aws-cdk-lib';
88
- import { aws_ec2 as ec2, aws_s3 as s3 } from 'aws-cdk-lib';
89
- import { GitHubRunners, CodeBuildRunner } from '@cloudsnorkel/cdk-github-runners';
90
-
91
- const app = new cdk.App();
92
- const stack = new cdk.Stack(
93
- app,
94
- 'github-runners-test',
95
- {
96
- env: {
97
- account: process.env.CDK_DEFAULT_ACCOUNT,
98
- region: process.env.CDK_DEFAULT_REGION,
99
- },
100
- },
101
- );
102
-
103
- const vpc = ec2.Vpc.fromLookup(stack, 'vpc', { vpcId: 'vpc-1234567' });
104
- const runnerSg = new ec2.SecurityGroup(stack, 'runner security group', { vpc: vpc });
105
- const dbSg = ec2.SecurityGroup.fromSecurityGroupId(stack, 'database security group', 'sg-1234567');
106
- const bucket = new s3.Bucket(stack, 'runner bucket');
94
+ let vpc: ec2.Vpc;
95
+ let runnerSg: ec2.SecurityGroup;
96
+ let dbSg: ec2.SecurityGroup;
97
+ let bucket: s3.Bucket;
107
98
 
108
99
  // create a custom CodeBuild provider
109
- const myProvider = new CodeBuildRunner(
110
- stack, 'codebuild runner',
111
- {
112
- label: 'my-codebuild',
113
- vpc: vpc,
114
- securityGroup: runnerSg,
115
- },
116
- );
100
+ const myProvider = new CodeBuildRunner(this, 'codebuild runner', {
101
+ label: 'my-codebuild',
102
+ vpc: vpc,
103
+ securityGroup: runnerSg,
104
+ });
117
105
  // grant some permissions to the provider
118
106
  bucket.grantReadWrite(myProvider);
119
107
  dbSg.connections.allowFrom(runnerSg, ec2.Port.tcp(3306), 'allow runners to connect to MySQL database');
120
108
 
121
109
  // create the runner infrastructure
122
- new GitHubRunners(
123
- stack,
124
- 'runners',
125
- {
110
+ new GitHubRunners(this, 'runners', {
126
111
  providers: [myProvider],
127
- defaultProviderLabel: 'my-codebuild',
128
- }
129
- );
112
+ });
113
+ ```
114
+
115
+ Another way to customize runners is by modifying the image used to spin them up. The image contains the [runner][5], any required dependencies, and integration code with the provider. You may choose to customize this image by adding more packages, for example.
116
+
117
+ ```typescript
118
+ const myBuilder = new CodeBuildImageBuilder(this, 'image builder', {
119
+ dockerfilePath: FargateProvider.LINUX_X64_DOCKERFILE_PATH,
120
+ runnerVersion: RunnerVersion.specific('2.291.0'),
121
+ rebuildInterval: Duration.days(14),
122
+ });
123
+ myBuilder.setBuildArg('EXTRA_PACKAGES', 'nginx xz-utils');
124
+
125
+ const myProvider = new FargateProvider(this, 'fargate runner', {
126
+ label: 'my-codebuild',
127
+ vpc: vpc,
128
+ securityGroup: runnerSg,
129
+ });
130
+
131
+ // create the runner infrastructure
132
+ new GitHubRunners(stack, 'runners', {
133
+ providers: [myProvider],
134
+ });
135
+ ```
136
+
137
+ Your workflow will then look like:
130
138
 
131
- app.synth();
139
+ ```yaml
140
+ name: self-hosted example
141
+ on: push
142
+ jobs:
143
+ self-hosted:
144
+ runs-on: [self-hosted, my-codebuild]
145
+ steps:
146
+ - run: echo hello world
132
147
  ```
133
148
 
134
149
  ## Architecture
package/SETUP_GITHUB.md CHANGED
@@ -1,9 +1,23 @@
1
1
  # Setup GitHub
2
2
 
3
- Integration with GitHub can be done using an [app](#app-authentication) or [personal access token](#personal-access-token). Using an app allows more fine-grained access control. Personal access tokens are easier to set up but belong to a user instead of an organization.
3
+ Integration with GitHub can be done using an [app](#app-authentication) or [personal access token](#personal-access-token). Using an app allows more fine-grained access control. Using an app is easier with the setup wizard.
4
4
 
5
5
  ## App Authentication
6
6
 
7
+ ### Setup Wizard
8
+
9
+ 1. Open the URL in `github.setup.url` from `status.json`
10
+ 2. If you want to create an app for your personal repositories, click the Create button under New Personal App
11
+ 3. If you want to create an app for your organization:
12
+ 1. Find the New Organization App section
13
+ 2. Type in the organization name in organization slug (ORGANIZATION from https://github.com/ORGANIZATION/REPO)
14
+ 3. Click the Create button
15
+ 4. Follow the instructions on GitHub
16
+ 5. When brought back to the setup wizard, click the install link
17
+ 6. Install the new app on your desired repositories
18
+
19
+ ### Manually
20
+
7
21
  1. Decide if you want to create a personal app or an organization app
8
22
  1. For a personal app use https://github.com/settings/apps/new
9
23
  2. For an organization app use https://github.com/organizations/MY_ORG/settings/apps/new after replacing `MY_ORG` with your GitHub organization name
@@ -30,21 +44,44 @@ Integration with GitHub can be done using an [app](#app-authentication) or [pers
30
44
 
31
45
  ## Personal Access Token
32
46
 
33
- 1. Create a new token
34
- 1. Go to https://github.com/settings/tokens/new
35
- 2. Choose your expiration date (you will need to replace the token if it expires)
36
- 3. Under scopes select `repo`
37
- 4. Copy the generated token
38
- 2. Open the URL in `github.auth.secretUrl` from `status.json` and edit the secret value
39
- 1. If you're using a self-hosted GitHub instance, put its domain in `domain` (e.g. `github.mycompany.com`)
40
- 2. Put the generated token in `personalAuthToken`
41
- 3. Ignore all other values
42
- 3. Create a webhook
43
- 1. For organizations go to https://github.com/organizations/MY_ORG/settings/hooks after replacing `MY_ORG` with your GitHub organization name
44
- 2. For enterprise go to https://github.com/enterprises/MY_ENTERPRISE/settings/hooks after replacing `MY_ENTERPRISE` with your GitHub enterprise name
45
- 3. Otherwise, you can create one per repository in your repository settings under Webhooks
46
- 4. Configure the webhook:
47
- 1. For Webhook URL use the value of `github.webhook.url` from `status.json`
48
- 2. Open the URL in `github.webhook.secretUrl` from `status.json`, retrieve the secret value, and use it for webhook secret
49
- 3. Make sure content type is set to JSON
50
- 4. Select individual jobs and select only Workflow jobs
47
+ ### Create Token
48
+
49
+ 1. Go to https://github.com/settings/tokens/new
50
+ 2. Choose your expiration date (you will need to replace the token if it expires)
51
+ 3. Under scopes select `repo`
52
+ 4. Copy the generated token
53
+
54
+ ### Set Token
55
+
56
+ #### Setup Wizard
57
+
58
+ 1. Open the URL in `github.setup.url` from `status.json`
59
+ 2. Enter your personal access token under Using Personal Access Token
60
+ 3. Click the Set button
61
+
62
+ #### Manually
63
+
64
+ 1. Open the URL in `github.auth.secretUrl` from `status.json` and edit the secret value
65
+ 2. If you're using a self-hosted GitHub instance, put its domain in `domain` (e.g. `github.mycompany.com`)
66
+ 3. Put the generated token in `personalAuthToken`
67
+ 4. Ignore all other values
68
+
69
+ ### Setup Webhook
70
+
71
+ 1. For organizations go to https://github.com/organizations/MY_ORG/settings/hooks after replacing `MY_ORG` with your GitHub organization name
72
+ 2. For enterprise go to https://github.com/enterprises/MY_ENTERPRISE/settings/hooks after replacing `MY_ENTERPRISE` with your GitHub enterprise name
73
+ 3. Otherwise, you can create one per repository in your repository settings under Webhooks
74
+ 4. Configure the webhook:
75
+ 1. For Webhook URL use the value of `github.webhook.url` from `status.json`
76
+ 2. Open the URL in `github.webhook.secretUrl` from `status.json`, retrieve the secret value, and use it for webhook secret
77
+ 3. Make sure content type is set to JSON
78
+ 4. Select individual jobs and select only Workflow jobs
79
+
80
+ ## Resetting Setup Wizard
81
+
82
+ If the setup wizard tells you setup has already been completed or if `github.setup.status` is completed, or if `github.setup.url` is empty:
83
+
84
+ 1. Open the URL in `github.setup.secretUrl` from `status.json`
85
+ 2. Edit the secret
86
+ 3. Put a new random value in `token`
87
+ 4. Run status function again to get the new URL
Binary file
package/lib/index.d.ts CHANGED
@@ -3,5 +3,6 @@ export { GitHubRunners, GitHubRunnersProps } from './runner';
3
3
  export { CodeBuildRunner, CodeBuildRunnerProps } from './providers/codebuild';
4
4
  export { LambdaRunner, LambdaRunnerProps } from './providers/lambda';
5
5
  export { FargateRunner, FargateRunnerProps } from './providers/fargate';
6
- export { IRunnerProvider, RunnerProviderProps, RunnerVersion, RunnerRuntimeParameters } from './providers/common';
7
- //# sourceMappingURL=index.d.ts.map
6
+ export { IRunnerProvider, RunnerProviderProps, RunnerVersion, RunnerRuntimeParameters, RunnerImage, IImageBuilder, Architecture, Os } from './providers/common';
7
+ export { CodeBuildImageBuilder, CodeBuildImageBuilderProps } from './providers/image-builders/codebuild';
8
+ export { StaticRunnerImage } from './providers/image-builders/static';
package/lib/index.js CHANGED
@@ -12,4 +12,10 @@ var fargate_1 = require("./providers/fargate");
12
12
  Object.defineProperty(exports, "FargateRunner", { enumerable: true, get: function () { return fargate_1.FargateRunner; } });
13
13
  var common_1 = require("./providers/common");
14
14
  Object.defineProperty(exports, "RunnerVersion", { enumerable: true, get: function () { return common_1.RunnerVersion; } });
15
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxxQ0FBb0M7QUFBM0Isa0dBQUEsT0FBTyxPQUFBO0FBQ2hCLG1DQUE2RDtBQUFwRCx1R0FBQSxhQUFhLE9BQUE7QUFDdEIsbURBQThFO0FBQXJFLDRHQUFBLGVBQWUsT0FBQTtBQUN4Qiw2Q0FBcUU7QUFBNUQsc0dBQUEsWUFBWSxPQUFBO0FBQ3JCLCtDQUF3RTtBQUEvRCx3R0FBQSxhQUFhLE9BQUE7QUFDdEIsNkNBQWtIO0FBQW5FLHVHQUFBLGFBQWEsT0FBQSIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCB7IFNlY3JldHMgfSBmcm9tICcuL3NlY3JldHMnO1xuZXhwb3J0IHsgR2l0SHViUnVubmVycywgR2l0SHViUnVubmVyc1Byb3BzIH0gZnJvbSAnLi9ydW5uZXInO1xuZXhwb3J0IHsgQ29kZUJ1aWxkUnVubmVyLCBDb2RlQnVpbGRSdW5uZXJQcm9wcyB9IGZyb20gJy4vcHJvdmlkZXJzL2NvZGVidWlsZCc7XG5leHBvcnQgeyBMYW1iZGFSdW5uZXIsIExhbWJkYVJ1bm5lclByb3BzIH0gZnJvbSAnLi9wcm92aWRlcnMvbGFtYmRhJztcbmV4cG9ydCB7IEZhcmdhdGVSdW5uZXIsIEZhcmdhdGVSdW5uZXJQcm9wcyB9IGZyb20gJy4vcHJvdmlkZXJzL2ZhcmdhdGUnO1xuZXhwb3J0IHsgSVJ1bm5lclByb3ZpZGVyLCBSdW5uZXJQcm92aWRlclByb3BzLCBSdW5uZXJWZXJzaW9uLCBSdW5uZXJSdW50aW1lUGFyYW1ldGVycyB9IGZyb20gJy4vcHJvdmlkZXJzL2NvbW1vbic7XG4iXX0=
15
+ Object.defineProperty(exports, "Architecture", { enumerable: true, get: function () { return common_1.Architecture; } });
16
+ Object.defineProperty(exports, "Os", { enumerable: true, get: function () { return common_1.Os; } });
17
+ var codebuild_2 = require("./providers/image-builders/codebuild");
18
+ Object.defineProperty(exports, "CodeBuildImageBuilder", { enumerable: true, get: function () { return codebuild_2.CodeBuildImageBuilder; } });
19
+ var static_1 = require("./providers/image-builders/static");
20
+ Object.defineProperty(exports, "StaticRunnerImage", { enumerable: true, get: function () { return static_1.StaticRunnerImage; } });
21
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxxQ0FBb0M7QUFBM0Isa0dBQUEsT0FBTyxPQUFBO0FBQ2hCLG1DQUE2RDtBQUFwRCx1R0FBQSxhQUFhLE9BQUE7QUFDdEIsbURBQThFO0FBQXJFLDRHQUFBLGVBQWUsT0FBQTtBQUN4Qiw2Q0FBcUU7QUFBNUQsc0dBQUEsWUFBWSxPQUFBO0FBQ3JCLCtDQUF3RTtBQUEvRCx3R0FBQSxhQUFhLE9BQUE7QUFDdEIsNkNBQWdLO0FBQWpILHVHQUFBLGFBQWEsT0FBQTtBQUF1RCxzR0FBQSxZQUFZLE9BQUE7QUFBRSw0RkFBQSxFQUFFLE9BQUE7QUFDbkksa0VBQXlHO0FBQWhHLGtIQUFBLHFCQUFxQixPQUFBO0FBQzlCLDREQUFzRTtBQUE3RCwyR0FBQSxpQkFBaUIsT0FBQSIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCB7IFNlY3JldHMgfSBmcm9tICcuL3NlY3JldHMnO1xuZXhwb3J0IHsgR2l0SHViUnVubmVycywgR2l0SHViUnVubmVyc1Byb3BzIH0gZnJvbSAnLi9ydW5uZXInO1xuZXhwb3J0IHsgQ29kZUJ1aWxkUnVubmVyLCBDb2RlQnVpbGRSdW5uZXJQcm9wcyB9IGZyb20gJy4vcHJvdmlkZXJzL2NvZGVidWlsZCc7XG5leHBvcnQgeyBMYW1iZGFSdW5uZXIsIExhbWJkYVJ1bm5lclByb3BzIH0gZnJvbSAnLi9wcm92aWRlcnMvbGFtYmRhJztcbmV4cG9ydCB7IEZhcmdhdGVSdW5uZXIsIEZhcmdhdGVSdW5uZXJQcm9wcyB9IGZyb20gJy4vcHJvdmlkZXJzL2ZhcmdhdGUnO1xuZXhwb3J0IHsgSVJ1bm5lclByb3ZpZGVyLCBSdW5uZXJQcm92aWRlclByb3BzLCBSdW5uZXJWZXJzaW9uLCBSdW5uZXJSdW50aW1lUGFyYW1ldGVycywgUnVubmVySW1hZ2UsIElJbWFnZUJ1aWxkZXIsIEFyY2hpdGVjdHVyZSwgT3MgfSBmcm9tICcuL3Byb3ZpZGVycy9jb21tb24nO1xuZXhwb3J0IHsgQ29kZUJ1aWxkSW1hZ2VCdWlsZGVyLCBDb2RlQnVpbGRJbWFnZUJ1aWxkZXJQcm9wcyB9IGZyb20gJy4vcHJvdmlkZXJzL2ltYWdlLWJ1aWxkZXJzL2NvZGVidWlsZCc7XG5leHBvcnQgeyBTdGF0aWNSdW5uZXJJbWFnZSB9IGZyb20gJy4vcHJvdmlkZXJzL2ltYWdlLWJ1aWxkZXJzL3N0YXRpYyc7XG4iXX0=
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
21
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
+
23
+ // src/lambdas/build-image/index.ts
24
+ var build_image_exports = {};
25
+ __export(build_image_exports, {
26
+ handler: () => handler
27
+ });
28
+ module.exports = __toCommonJS(build_image_exports);
29
+ var AWS = __toESM(require("aws-sdk"));
30
+ var codebuild = new AWS.CodeBuild();
31
+ var ecr = new AWS.ECR();
32
+ async function handler(event, context) {
33
+ try {
34
+ console.log(JSON.stringify(event));
35
+ const repoName = event.ResourceProperties.RepoName;
36
+ const projectName = event.ResourceProperties.ProjectName;
37
+ switch (event.RequestType) {
38
+ case "Create":
39
+ case "Update":
40
+ console.log(`Starting CodeBuild project ${projectName}`);
41
+ await codebuild.startBuild({
42
+ projectName,
43
+ environmentVariablesOverride: [
44
+ {
45
+ type: "PLAINTEXT",
46
+ name: "STACK_ID",
47
+ value: event.StackId
48
+ },
49
+ {
50
+ type: "PLAINTEXT",
51
+ name: "REQUEST_ID",
52
+ value: event.RequestId
53
+ },
54
+ {
55
+ type: "PLAINTEXT",
56
+ name: "LOGICAL_RESOURCE_ID",
57
+ value: event.LogicalResourceId
58
+ },
59
+ {
60
+ type: "PLAINTEXT",
61
+ name: "RESPONSE_URL",
62
+ value: event.ResponseURL
63
+ }
64
+ ]
65
+ }).promise();
66
+ break;
67
+ case "Delete":
68
+ const images = await ecr.listImages({ repositoryName: repoName, maxResults: 100 }).promise();
69
+ if (images.imageIds && images.imageIds.length > 0) {
70
+ await ecr.batchDeleteImage({
71
+ imageIds: images.imageIds.map((i) => {
72
+ return { imageDigest: i.imageDigest };
73
+ }),
74
+ repositoryName: repoName
75
+ }).promise();
76
+ }
77
+ await respond("SUCCESS", "OK", event.PhysicalResourceId, {});
78
+ break;
79
+ }
80
+ } catch (e) {
81
+ console.log(e);
82
+ await respond("FAILED", e.message || "Internal Error", context.logStreamName, {});
83
+ }
84
+ function respond(responseStatus, reason, physicalResourceId, data) {
85
+ const responseBody = JSON.stringify({
86
+ Status: responseStatus,
87
+ Reason: reason,
88
+ PhysicalResourceId: physicalResourceId,
89
+ StackId: event.StackId,
90
+ RequestId: event.RequestId,
91
+ LogicalResourceId: event.LogicalResourceId,
92
+ NoEcho: false,
93
+ Data: data
94
+ });
95
+ console.log("Responding", responseBody);
96
+ const parsedUrl = require("url").parse(event.ResponseURL);
97
+ const requestOptions = {
98
+ hostname: parsedUrl.hostname,
99
+ path: parsedUrl.path,
100
+ method: "PUT",
101
+ headers: {
102
+ "content-type": "",
103
+ "content-length": responseBody.length
104
+ }
105
+ };
106
+ return new Promise((resolve, reject) => {
107
+ try {
108
+ const request = require("https").request(requestOptions, resolve);
109
+ request.on("error", reject);
110
+ request.write(responseBody);
111
+ request.end();
112
+ } catch (e) {
113
+ reject(e);
114
+ }
115
+ });
116
+ }
117
+ }
118
+ // Annotate the CommonJS export names for ESM import in node:
119
+ 0 && (module.exports = {
120
+ handler
121
+ });
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -12509,8 +12510,25 @@ var require_dist_node15 = __commonJS({
12509
12510
  // src/lambdas/github.ts
12510
12511
  var import_auth_app = __toESM(require_dist_node12());
12511
12512
  var import_core = __toESM(require_dist_node15());
12513
+
12514
+ // src/lambdas/helpers.ts
12512
12515
  var AWS = __toESM(require("aws-sdk"));
12513
12516
  var sm = new AWS.SecretsManager();
12517
+ async function getSecretValue(arn) {
12518
+ if (!arn) {
12519
+ throw new Error("Missing secret ARN");
12520
+ }
12521
+ const secret = await sm.getSecretValue({ SecretId: arn }).promise();
12522
+ if (!secret.SecretString) {
12523
+ throw new Error(`No SecretString in ${arn}`);
12524
+ }
12525
+ return secret.SecretString;
12526
+ }
12527
+ async function getSecretJsonValue(arn) {
12528
+ return JSON.parse(await getSecretValue(arn));
12529
+ }
12530
+
12531
+ // src/lambdas/github.ts
12514
12532
  function baseUrlFromDomain(domain) {
12515
12533
  if (domain == "github.com") {
12516
12534
  return "https://api.github.com";
@@ -12521,21 +12539,13 @@ async function getOctokit(installationId) {
12521
12539
  if (!process.env.GITHUB_SECRET_ARN || !process.env.GITHUB_PRIVATE_KEY_SECRET_ARN) {
12522
12540
  throw new Error("Missing environment variables");
12523
12541
  }
12524
- const secret = await sm.getSecretValue({
12525
- SecretId: process.env.GITHUB_SECRET_ARN
12526
- }).promise();
12527
- if (!secret.SecretString) {
12528
- throw new Error(`No secret string in ${process.env.GITHUB_SECRET_ARN}`);
12529
- }
12530
- const githubSecrets = JSON.parse(secret.SecretString);
12542
+ const githubSecrets = await getSecretJsonValue(process.env.GITHUB_SECRET_ARN);
12531
12543
  let baseUrl = baseUrlFromDomain(githubSecrets.domain);
12532
12544
  let token;
12533
12545
  if (githubSecrets.personalAuthToken) {
12534
12546
  token = githubSecrets.personalAuthToken;
12535
12547
  } else {
12536
- const privateKey = (await sm.getSecretValue({
12537
- SecretId: process.env.GITHUB_PRIVATE_KEY_SECRET_ARN
12538
- }).promise()).SecretString;
12548
+ const privateKey = await getSecretValue(process.env.GITHUB_PRIVATE_KEY_SECRET_ARN);
12539
12549
  const appOctokit = new import_core.Octokit({
12540
12550
  baseUrl,
12541
12551
  authStrategy: import_auth_app.createAppAuth,
@@ -12581,11 +12591,15 @@ async function getRunnerId(octokit, owner, repo, name) {
12581
12591
  }
12582
12592
  exports.handler = async function(event) {
12583
12593
  const { octokit } = await getOctokit(event.installationId);
12584
- await octokit.request("POST /repos/{owner}/{repo}/actions/runs/{runId}/cancel", {
12585
- owner: event.owner,
12586
- repo: event.repo,
12587
- runId: event.runId
12588
- });
12594
+ try {
12595
+ await octokit.request("POST /repos/{owner}/{repo}/actions/runs/{runId}/cancel", {
12596
+ owner: event.owner,
12597
+ repo: event.repo,
12598
+ runId: event.runId
12599
+ });
12600
+ } catch (e) {
12601
+ console.error(`Unable to cancel workflow: ${e}`);
12602
+ }
12589
12603
  const runnerId = await getRunnerId(octokit, event.owner, event.repo, event.runnerName);
12590
12604
  if (!runnerId) {
12591
12605
  console.error(`Unable to find runner id for ${event.owner}/${event.repo}:${event.runnerName}`);