@cloudsnorkel/cdk-github-runners 0.14.15 → 0.14.16
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.
- package/.jsii +800 -274
- package/API.md +480 -6
- package/README.md +149 -0
- package/SETUP_GITHUB.md +99 -3
- package/assets/delete-failed-runner.lambda/index.js +40 -31
- package/assets/idle-runner-repear.lambda/index.js +40 -31
- package/assets/setup.lambda/index.html +12 -7
- package/assets/setup.lambda/index.js +26 -26
- package/assets/status.lambda/index.js +40 -31
- package/assets/token-retriever.lambda/index.js +40 -31
- package/assets/webhook-handler.lambda/index.js +110 -43
- package/assets/webhook-redelivery.lambda/index.js +40 -31
- package/lib/access.js +1 -1
- package/lib/image-builders/api.js +1 -1
- package/lib/image-builders/aws-image-builder/ami.d.ts +1 -2
- package/lib/image-builders/aws-image-builder/ami.js +6 -13
- package/lib/image-builders/aws-image-builder/builder.d.ts +4 -2
- package/lib/image-builders/aws-image-builder/builder.js +36 -34
- package/lib/image-builders/aws-image-builder/container.d.ts +2 -2
- package/lib/image-builders/aws-image-builder/container.js +7 -12
- package/lib/image-builders/aws-image-builder/deprecated/ami.js +1 -1
- package/lib/image-builders/aws-image-builder/deprecated/container.js +1 -1
- package/lib/image-builders/aws-image-builder/deprecated/linux-components.js +1 -1
- package/lib/image-builders/aws-image-builder/deprecated/windows-components.js +1 -1
- package/lib/image-builders/aws-image-builder/index.d.ts +0 -1
- package/lib/image-builders/aws-image-builder/index.js +1 -2
- package/lib/image-builders/aws-image-builder/workflow.d.ts +4 -4
- package/lib/image-builders/aws-image-builder/workflow.js +7 -10
- package/lib/image-builders/codebuild-deprecated.js +1 -1
- package/lib/image-builders/components.js +1 -1
- package/lib/image-builders/static.js +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +2 -1
- package/lib/providers/codebuild.js +16 -10
- package/lib/providers/common.d.ts +53 -0
- package/lib/providers/common.js +11 -4
- package/lib/providers/composite.d.ts +61 -0
- package/lib/providers/composite.js +229 -0
- package/lib/providers/ec2.js +9 -8
- package/lib/providers/ecs.js +11 -6
- package/lib/providers/fargate.js +14 -9
- package/lib/providers/index.d.ts +1 -0
- package/lib/providers/index.js +2 -1
- package/lib/providers/lambda.js +6 -5
- package/lib/runner.d.ts +29 -5
- package/lib/runner.js +57 -24
- package/lib/secrets.js +1 -1
- package/lib/webhook-handler.lambda.d.ts +11 -0
- package/lib/webhook-handler.lambda.js +81 -14
- package/lib/webhook.d.ts +52 -7
- package/lib/webhook.js +4 -2
- package/package.json +15 -19
- package/assets/image-builders/aws-image-builder/versioner.lambda/index.js +0 -2115
- package/lib/image-builders/aws-image-builder/common.d.ts +0 -10
- package/lib/image-builders/aws-image-builder/common.js +0 -48
- package/lib/image-builders/aws-image-builder/versioner-function.d.ts +0 -13
- package/lib/image-builders/aws-image-builder/versioner-function.js +0 -23
- package/lib/image-builders/aws-image-builder/versioner.lambda.d.ts +0 -7
- package/lib/image-builders/aws-image-builder/versioner.lambda.js +0 -115
package/README.md
CHANGED
|
@@ -296,6 +296,155 @@ new GitHubRunners(this, 'runners', {
|
|
|
296
296
|
});
|
|
297
297
|
```
|
|
298
298
|
|
|
299
|
+
### Composite Providers
|
|
300
|
+
|
|
301
|
+
Composite providers allow you to combine multiple runner providers with different strategies. There are two types:
|
|
302
|
+
|
|
303
|
+
**Fallback Strategy**: Try providers in order until one succeeds. Useful for trying spot instances first, then falling back to on-demand if spot capacity is unavailable.
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
// Try spot instances first, fall back to on-demand if spot is unavailable
|
|
307
|
+
const ecsFallback = CompositeProvider.fallback(this, 'ECS Fallback', [
|
|
308
|
+
new EcsRunnerProvider(this, 'ECS Spot', {
|
|
309
|
+
labels: ['ecs', 'linux', 'x64'],
|
|
310
|
+
spot: true,
|
|
311
|
+
// ... other config
|
|
312
|
+
}),
|
|
313
|
+
new EcsRunnerProvider(this, 'ECS On-Demand', {
|
|
314
|
+
labels: ['ecs', 'linux', 'x64'],
|
|
315
|
+
spot: false,
|
|
316
|
+
// ... other config
|
|
317
|
+
}),
|
|
318
|
+
]);
|
|
319
|
+
|
|
320
|
+
new GitHubRunners(this, 'runners', {
|
|
321
|
+
providers: [ecsFallback],
|
|
322
|
+
});
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Weighted Distribution Strategy**: Randomly select a provider based on weights. Useful for distributing load across multiple availability zones or instance types.
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
// Distribute 60% of traffic to AZ-1, 40% to AZ-2
|
|
329
|
+
const distributedProvider = CompositeProvider.distribute(this, 'Fargate Distribution', [
|
|
330
|
+
{
|
|
331
|
+
weight: 3, // 3/(3+2) = 60%
|
|
332
|
+
provider: new FargateRunnerProvider(this, 'Fargate AZ-1', {
|
|
333
|
+
labels: ['fargate', 'linux', 'x64'],
|
|
334
|
+
subnetSelection: vpc.selectSubnets({
|
|
335
|
+
availabilityZones: [vpc.availabilityZones[0]],
|
|
336
|
+
}),
|
|
337
|
+
// ... other config
|
|
338
|
+
}),
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
weight: 2, // 2/(3+2) = 40%
|
|
342
|
+
provider: new FargateRunnerProvider(this, 'Fargate AZ-2', {
|
|
343
|
+
labels: ['fargate', 'linux', 'x64'],
|
|
344
|
+
subnetSelection: vpc.selectSubnets({
|
|
345
|
+
availabilityZones: [vpc.availabilityZones[1]],
|
|
346
|
+
}),
|
|
347
|
+
// ... other config
|
|
348
|
+
}),
|
|
349
|
+
},
|
|
350
|
+
]);
|
|
351
|
+
|
|
352
|
+
new GitHubRunners(this, 'runners', {
|
|
353
|
+
providers: [distributedProvider],
|
|
354
|
+
});
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
**Important**: All providers in a composite must have the exact same labels. This ensures any provisioned runner can match the labels requested by the GitHub workflow job.
|
|
358
|
+
|
|
359
|
+
### Custom Provider Selection
|
|
360
|
+
|
|
361
|
+
By default, providers are selected based on label matching: the first provider that has all the labels requested by the job is selected. You can customize this behavior using a provider selector Lambda function to:
|
|
362
|
+
|
|
363
|
+
* Filter out certain jobs (prevent runner provisioning)
|
|
364
|
+
* Dynamically select a provider based on job characteristics (repository, branch, time of day, etc.)
|
|
365
|
+
* Customize labels for the runner (add, remove, or modify labels dynamically)
|
|
366
|
+
|
|
367
|
+
The selector function receives the full GitHub webhook payload, a map of all available providers and their labels, and the default provider/labels that would have been selected. It returns the provider to use (or `undefined` to skip runner creation) and the labels to assign to the runner.
|
|
368
|
+
|
|
369
|
+
**Example: Route jobs to different providers based on repository**
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
import { ComputeType } from 'aws-cdk-lib/aws-codebuild';
|
|
373
|
+
import { Function, Code, Runtime } from 'aws-cdk-lib/aws-lambda';
|
|
374
|
+
import { GitHubRunners, CodeBuildRunnerProvider } from '@cloudsnorkel/cdk-github-runners';
|
|
375
|
+
|
|
376
|
+
const defaultProvider = new CodeBuildRunnerProvider(this, 'default', {
|
|
377
|
+
labels: ['custom-runner', 'default'],
|
|
378
|
+
});
|
|
379
|
+
const productionProvider = new CodeBuildRunnerProvider(this, 'production', {
|
|
380
|
+
labels: ['custom-runner', 'production'],
|
|
381
|
+
computeType: ComputeType.LARGE,
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
const providerSelector = new Function(this, 'provider-selector', {
|
|
385
|
+
runtime: Runtime.NODEJS_LATEST,
|
|
386
|
+
handler: 'index.handler',
|
|
387
|
+
code: Code.fromInline(`
|
|
388
|
+
exports.handler = async (event) => {
|
|
389
|
+
const { payload, providers, defaultProvider, defaultLabels } = event;
|
|
390
|
+
|
|
391
|
+
// Route production repos to dedicated provider
|
|
392
|
+
if (payload.repository.name.includes('prod')) {
|
|
393
|
+
return {
|
|
394
|
+
provider: '${productionProvider.node.path}',
|
|
395
|
+
labels: ['custom-runner', 'production', 'modified-via-selector'],
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Filter out draft PRs
|
|
400
|
+
if (payload.workflow_job.head_branch?.startsWith('draft/')) {
|
|
401
|
+
return { provider: undefined }; // Skip runner provisioning
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Use default for everything else
|
|
405
|
+
return {
|
|
406
|
+
provider: defaultProvider,
|
|
407
|
+
labels: defaultLabels,
|
|
408
|
+
};
|
|
409
|
+
};
|
|
410
|
+
`),
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
new GitHubRunners(this, 'runners', {
|
|
414
|
+
providers: [defaultProvider, productionProvider],
|
|
415
|
+
providerSelector: providerSelector,
|
|
416
|
+
});
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
**Example: Add dynamic labels based on job metadata**
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
const providerSelector = new Function(this, 'provider-selector', {
|
|
423
|
+
runtime: Runtime.NODEJS_LATEST,
|
|
424
|
+
handler: 'index.handler',
|
|
425
|
+
code: Code.fromInline(`
|
|
426
|
+
exports.handler = async (event) => {
|
|
427
|
+
const { payload, defaultProvider, defaultLabels } = event;
|
|
428
|
+
|
|
429
|
+
// Add branch name as a label
|
|
430
|
+
const branch = payload.workflow_job.head_branch || 'unknown';
|
|
431
|
+
const labels = [...(defaultLabels || []), 'branch:' + branch];
|
|
432
|
+
|
|
433
|
+
return {
|
|
434
|
+
provider: defaultProvider,
|
|
435
|
+
labels: labels,
|
|
436
|
+
};
|
|
437
|
+
};
|
|
438
|
+
`),
|
|
439
|
+
});
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**Important considerations:**
|
|
443
|
+
|
|
444
|
+
* ⚠️ **Label matching responsibility**: You are responsible for ensuring the selected provider's labels match what the job requires. If labels don't match, the runner will be provisioned but GitHub Actions won't assign the job to it.
|
|
445
|
+
* ⚠️ **No guarantee of assignment**: Provider selection only determines which provider will provision a runner. GitHub Actions may still route the job to any available runner with matching labels. For reliable provider assignment, consider repo-level runner registration (the default).
|
|
446
|
+
* ⚡ **Performance**: The selector runs synchronously during webhook processing. Keep it fast and efficient—the webhook has a 30-second timeout total.
|
|
447
|
+
|
|
299
448
|
## Examples
|
|
300
449
|
|
|
301
450
|
Beyond the code snippets above, the fullest example available is the [integration test](test/default.integ.ts).
|
package/SETUP_GITHUB.md
CHANGED
|
@@ -1,6 +1,102 @@
|
|
|
1
1
|
# Setup GitHub
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Overview of Options
|
|
4
|
+
|
|
5
|
+
You will need to make several decisions during setup. Here's a quick guide to help you choose:
|
|
6
|
+
|
|
7
|
+
### 1. Authentication Method
|
|
8
|
+
|
|
9
|
+
**Choose between:**
|
|
10
|
+
- **GitHub App** (recommended) - More fine-grained permissions, easier setup with wizard, better security
|
|
11
|
+
- **Personal Access Token (PAT)** - Simpler but less flexible, requires manual webhook setup
|
|
12
|
+
|
|
13
|
+
**When to use App:** Almost always. Use PAT only if you have specific constraints that prevent using an app.
|
|
14
|
+
|
|
15
|
+
**When to use PAT:** If you need a quick setup and don't need fine-grained permissions, or if your organization has policies preventing app creation.
|
|
16
|
+
|
|
17
|
+
> **⚠️ IT/DevOps Help May Be Required:** Installing a GitHub app on repositories or organizations may require repository administrator or organization owner permissions. You may need to coordinate with your IT/devops team to install the app on the desired repositories.
|
|
18
|
+
|
|
19
|
+
### 2. GitHub Instance
|
|
20
|
+
|
|
21
|
+
**Choose between:**
|
|
22
|
+
- **GitHub.com** - Public GitHub
|
|
23
|
+
- **GitHub Enterprise Server** - Self-hosted GitHub instance
|
|
24
|
+
|
|
25
|
+
**When to use Enterprise Server:** If your organization uses a self-hosted GitHub Enterprise Server instance.
|
|
26
|
+
|
|
27
|
+
> **⚠️ IT/DevOps Help Required:** Setting up with GitHub Enterprise Server requires coordination with your IT/devops team to obtain the correct domain and ensure network connectivity.
|
|
28
|
+
|
|
29
|
+
### 3. App Scope (if using App)
|
|
30
|
+
|
|
31
|
+
**Choose between:**
|
|
32
|
+
- **User App** - For personal repositories
|
|
33
|
+
- **Organization App** - For organization repositories
|
|
34
|
+
|
|
35
|
+
**When to use User App:** If you only need to run workflows for your personal repositories.
|
|
36
|
+
|
|
37
|
+
**When to use Organization App:** If you need to run workflows for repositories in an organization.
|
|
38
|
+
|
|
39
|
+
> **⚠️ IT/DevOps Approval Required:** Creating an organization app may require organization owner or administrator permissions. You may need to request approval from your IT/devops team or organization administrators.
|
|
40
|
+
|
|
41
|
+
### 4. Runner Registration Level
|
|
42
|
+
|
|
43
|
+
**Choose between:**
|
|
44
|
+
- **Repository-level** (recommended) - Runners registered to specific repositories
|
|
45
|
+
- **Organization-level** - Runners registered to the entire organization
|
|
46
|
+
|
|
47
|
+
> **Note:** This determines where on-demand runners will be registered dynamically each time they are provisioned. This is independent of where the GitHub app is installed. Organization-level registration makes runners available to **all repositories in the organization**, regardless of app installation.
|
|
48
|
+
|
|
49
|
+
**When to use Repository-level:**
|
|
50
|
+
- You want better isolation between repositories
|
|
51
|
+
- You want to reduce the risk of jobs being accidentally assigned to the wrong runners
|
|
52
|
+
- You're okay with requiring the `administration` permission
|
|
53
|
+
|
|
54
|
+
**When to use Organization-level:**
|
|
55
|
+
- You need to minimize permissions (only requires `organization_self_hosted_runners`)
|
|
56
|
+
- You fully trust all repositories in your organization
|
|
57
|
+
- You want all repositories to share the same pool of runners
|
|
58
|
+
|
|
59
|
+
> **⚠️ IT/DevOps Approval May Be Required:** Organization-level registration affects all repositories in the organization. Your organization may have policies requiring approval before enabling organization-wide runner registration.
|
|
60
|
+
|
|
61
|
+
#### Registration Level Recommendation
|
|
62
|
+
|
|
63
|
+
This determines where on-demand runners will be registered dynamically each time they are provisioned. This is independent of where the GitHub app is installed.
|
|
64
|
+
|
|
65
|
+
Repository-level registration is recommended over organization-level registration for better control and security:
|
|
66
|
+
|
|
67
|
+
* **Reduced risk of accidental job assignment**: Runners are dynamically registered to the repository matching the job that triggered them. This ensures jobs are only assigned to runners intended for that specific repository, reducing the risk of accidental assignment when using multiple runner configurations.
|
|
68
|
+
* **Better isolation**: Each repository only sees runners that are explicitly registered to it, providing better isolation between different projects or teams.
|
|
69
|
+
* **Trade-off**: Repository-level registration requires the `administration` permission, which is broader than the `organization_self_hosted_runners` permission required for organization-level registration. If you need to minimize permissions and fully trust all repositories in your organization, organization-level registration may be acceptable.
|
|
70
|
+
|
|
71
|
+
Organization-level registration registers runners to the entire organization, making them available to **all repositories in the organization**, regardless of whether the app is installed on those repositories. This can lead to jobs being routed to runners that weren't intended for them. Runners may be assigned jobs from repositories where this app isn't even installed.
|
|
72
|
+
|
|
73
|
+
### 5. Setup Method
|
|
74
|
+
|
|
75
|
+
**Choose between:**
|
|
76
|
+
- **Setup Wizard** (recommended) - Interactive web interface, guides you through the process
|
|
77
|
+
- **Manual Setup** - Step-by-step instructions, more control over each step
|
|
78
|
+
|
|
79
|
+
**When to use Setup Wizard:** If you prefer a guided, interactive experience.
|
|
80
|
+
|
|
81
|
+
**When to use Manual Setup:** If you need more control, want to understand each step in detail, or the wizard isn't available.
|
|
82
|
+
|
|
83
|
+
### 6. Webhook Scope (if using PAT)
|
|
84
|
+
|
|
85
|
+
**Choose between:**
|
|
86
|
+
- **Organization/Enterprise webhook** - Single webhook for all repositories
|
|
87
|
+
- **Repository-level webhooks** - One webhook per repository
|
|
88
|
+
|
|
89
|
+
**When to use Organization/Enterprise webhook:** If you want to manage a single webhook for multiple repositories.
|
|
90
|
+
|
|
91
|
+
**When to use Repository-level webhooks:** If you only need runners for a few specific repositories.
|
|
92
|
+
|
|
93
|
+
> **⚠️ IT/DevOps Approval Required:** Creating organization or enterprise-level webhooks requires organization owner or enterprise administrator permissions. Repository-level webhooks only require repository admin permissions.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Setup GitHub
|
|
98
|
+
|
|
99
|
+
Integration with GitHub can be done using an [app](#app-authentication) or [personal access token](#personal-access-token-authentication). Using an app allows more fine-grained access control. Using an app is easier with the setup wizard.
|
|
4
100
|
|
|
5
101
|
## App Authentication
|
|
6
102
|
|
|
@@ -49,7 +145,7 @@ Integration with GitHub can be done using an [app](#app-authentication) or [pers
|
|
|
49
145
|
1. Open the downloaded private key with any text editor
|
|
50
146
|
2. Copy the text from the private key as-is into the secret
|
|
51
147
|
|
|
52
|
-
## Personal Access Token
|
|
148
|
+
## Personal Access Token Authentication
|
|
53
149
|
|
|
54
150
|
### Create Token
|
|
55
151
|
|
|
@@ -79,7 +175,7 @@ Integration with GitHub can be done using an [app](#app-authentication) or [pers
|
|
|
79
175
|
|
|
80
176
|
1. For organizations go to https://github.com/organizations/MY_ORG/settings/hooks after replacing `MY_ORG` with your GitHub organization name
|
|
81
177
|
2. For enterprise go to https://github.com/enterprises/MY_ENTERPRISE/settings/hooks after replacing `MY_ENTERPRISE` with your GitHub enterprise name
|
|
82
|
-
3. Otherwise, you can create one per repository in your repository settings under Webhooks
|
|
178
|
+
3. Otherwise, you can create one webhook per repository in your repository settings under Webhooks
|
|
83
179
|
4. Configure the webhook:
|
|
84
180
|
1. For Webhook URL use the value of `github.webhook.url` from `status.json`
|
|
85
181
|
2. Open the URL in `github.webhook.secretUrl` from `status.json`, retrieve the secret value, and use it for webhook secret
|