From 3e24ec62013ae7d90d9250252219f43bdf970e7a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 20:42:57 +0000 Subject: [PATCH 1/8] Initial plan From 90874d7a03928707106d707619c7bc6dff3f0779 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 20:54:18 +0000 Subject: [PATCH 2/8] fix: move publishRetryMessage to end of loop and skip invalid messages Per reviewer feedback, publishRetryMessage is now called at the end of the processing loop for each message that is NOT marked as invalid. This prevents duplicate retry messages for the same event, since invalid messages already go back to the SQS queue for retry. Key changes: - Track validMessagesForRetry separately from all messages - Only messages that pass job queued check are added to validMessagesForRetry - publishRetryMessage is called after runner creation, not before - Messages marked as invalid (e.g., max runners reached, creation failed) are excluded from retry message publishing Tests updated to reflect new behavior: - publishRetryMessage is called AFTER runner creation - Messages marked invalid do not get retry messages published - All test scenarios updated with proper mock runner creation Co-authored-by: npalm <11609620+npalm@users.noreply.github.com> --- .../src/scale-runners/scale-up.test.ts | 15 ++++++++++----- .../control-plane/src/scale-runners/scale-up.ts | 16 +++++++++++++++- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts b/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts index 9549e7cb2d..9261d18bf9 100644 --- a/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts +++ b/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts @@ -1698,6 +1698,7 @@ describe('Retry mechanism tests', () => { it('calls publishRetryMessage for each valid message when job is queued', async () => { const messages = createTestMessages(3); + mockCreateRunner.mockResolvedValue(['i-12345', 'i-67890', 'i-abcdef']); // Create all requested runners await scaleUpModule.scaleUp(messages); @@ -1749,15 +1750,16 @@ describe('Retry mechanism tests', () => { ); }); - it('calls publishRetryMessage even when maximum runners is reached', async () => { + it('does not call publishRetryMessage when maximum runners is reached and messages are marked invalid', async () => { process.env.RUNNERS_MAXIMUM_COUNT = '0'; // No runners can be created const messages = createTestMessages(2); await scaleUpModule.scaleUp(messages); - // publishRetryMessage should still be called even though no runners will be created - expect(mockPublishRetryMessage).toHaveBeenCalledTimes(2); + // publishRetryMessage should NOT be called because messages are marked as invalid + // Invalid messages go back to the SQS queue and will be retried there + expect(mockPublishRetryMessage).not.toHaveBeenCalled(); expect(createRunner).not.toHaveBeenCalled(); }); @@ -1781,6 +1783,7 @@ describe('Retry mechanism tests', () => { it('calls publishRetryMessage when ENABLE_JOB_QUEUED_CHECK is false', async () => { process.env.ENABLE_JOB_QUEUED_CHECK = 'false'; + mockCreateRunner.mockResolvedValue(['i-12345', 'i-67890']); // Create all requested runners const messages = createTestMessages(2); @@ -1792,6 +1795,7 @@ describe('Retry mechanism tests', () => { }); it('calls publishRetryMessage for each message in a multi-runner scenario', async () => { + mockCreateRunner.mockResolvedValue(['i-12345', 'i-67890', 'i-abcdef', 'i-11111', 'i-22222']); // Create all requested runners const messages = createTestMessages(5); await scaleUpModule.scaleUp(messages); @@ -1808,8 +1812,9 @@ describe('Retry mechanism tests', () => { }); }); - it('calls publishRetryMessage before runner creation', async () => { + it('calls publishRetryMessage after runner creation', async () => { const messages = createTestMessages(1); + mockCreateRunner.mockResolvedValue(['i-12345']); // Create the requested runner const callOrder: string[] = []; mockPublishRetryMessage.mockImplementation(() => { @@ -1823,7 +1828,7 @@ describe('Retry mechanism tests', () => { await scaleUpModule.scaleUp(messages); - expect(callOrder).toEqual(['publishRetryMessage', 'createRunner']); + expect(callOrder).toEqual(['createRunner', 'publishRetryMessage']); }); }); diff --git a/lambdas/functions/control-plane/src/scale-runners/scale-up.ts b/lambdas/functions/control-plane/src/scale-runners/scale-up.ts index d340b06010..56bfe0864f 100644 --- a/lambdas/functions/control-plane/src/scale-runners/scale-up.ts +++ b/lambdas/functions/control-plane/src/scale-runners/scale-up.ts @@ -339,6 +339,7 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise messageId)); } + + // Publish retry messages for all messages that are not marked as invalid + for (const message of validMessagesForRetry) { + if (!invalidMessages.includes(message.messageId)) { + await publishRetryMessage(message as ActionRequestMessageRetry); + } + } } return invalidMessages; From 11c1578af5e10512a478d5d782d54180b880295c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 20:57:56 +0000 Subject: [PATCH 3/8] refactor: improve performance and reduce code duplication Address code review feedback: - Use Set for invalidMessageIds to improve lookup performance from O(n) to O(1) - Extract duplicate retry message publishing logic into helper function - Maintain both invalidMessages array (for return value) and Set (for fast lookups) This improves performance when processing large message batches and makes the code more maintainable by eliminating duplication. Co-authored-by: npalm <11609620+npalm@users.noreply.github.com> --- .../src/scale-runners/scale-up.ts | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/lambdas/functions/control-plane/src/scale-runners/scale-up.ts b/lambdas/functions/control-plane/src/scale-runners/scale-up.ts index 56bfe0864f..13eae63ca1 100644 --- a/lambdas/functions/control-plane/src/scale-runners/scale-up.ts +++ b/lambdas/functions/control-plane/src/scale-runners/scale-up.ts @@ -90,6 +90,17 @@ function generateRunnerServiceConfig(githubRunnerConfig: CreateGitHubRunnerConfi return config; } +async function publishRetryMessagesForValid( + messages: ActionRequestMessageSQS[], + invalidMessageIds: Set, +): Promise { + for (const message of messages) { + if (!invalidMessageIds.has(message.messageId)) { + await publishRetryMessage(message as ActionRequestMessageRetry); + } + } +} + async function getGithubRunnerRegistrationToken(githubRunnerConfig: CreateGitHubRunnerConfig, ghClient: Octokit) { const registrationToken = githubRunnerConfig.runnerType === 'Org' @@ -276,6 +287,7 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise(); const invalidMessages: string[] = []; + const invalidMessageIds = new Set(); for (const payload of payloads) { const { eventType, messageId, repositoryName, repositoryOwner } = payload; if (ephemeralEnabled && eventType !== 'workflow_job') { @@ -394,17 +406,15 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise messageId)); + const removedMessages = messages.splice(0, missingInstanceCount); + invalidMessages.push(...removedMessages.map(({ messageId }) => messageId)); + removedMessages.forEach(({ messageId }) => invalidMessageIds.add(messageId)); } // No runners will be created, so skip calling the EC2 API. if (newRunners <= 0) { // Publish retry messages for all remaining messages that are not marked as invalid - for (const message of validMessagesForRetry) { - if (!invalidMessages.includes(message.messageId)) { - await publishRetryMessage(message as ActionRequestMessageRetry); - } - } + await publishRetryMessagesForValid(validMessagesForRetry, invalidMessageIds); continue; } } @@ -456,15 +466,13 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise messageId)); + const failedMessages = messages.slice(0, failedInstanceCount); + invalidMessages.push(...failedMessages.map(({ messageId }) => messageId)); + failedMessages.forEach(({ messageId }) => invalidMessageIds.add(messageId)); } // Publish retry messages for all messages that are not marked as invalid - for (const message of validMessagesForRetry) { - if (!invalidMessages.includes(message.messageId)) { - await publishRetryMessage(message as ActionRequestMessageRetry); - } - } + await publishRetryMessagesForValid(validMessagesForRetry, invalidMessageIds); } return invalidMessages; From b5399f6517997741c43f03ace7d51e7896e6dc1e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 21:32:51 +0000 Subject: [PATCH 4/8] refactor: simplify naming and reduce complexity Simplified the retry message logic based on reviewer feedback: - Renamed `validMessagesForRetry` to `queuedMessages` (clearer intent) - Renamed `invalidMessageIds` to `rejectedMessageIds` (more accurate) - Removed `invalidMessages` array duplication - now using only Set - Removed `publishRetryMessagesForValid` helper function (inline instead) - Convert Set to array only at return statement Result: clearer naming, less complexity, same functionality. Net reduction of 6 lines while maintaining all tests passing. Co-authored-by: npalm <11609620+npalm@users.noreply.github.com> --- .../src/scale-runners/scale-up.ts | 44 ++++++++----------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/lambdas/functions/control-plane/src/scale-runners/scale-up.ts b/lambdas/functions/control-plane/src/scale-runners/scale-up.ts index 13eae63ca1..6a3ab93bb7 100644 --- a/lambdas/functions/control-plane/src/scale-runners/scale-up.ts +++ b/lambdas/functions/control-plane/src/scale-runners/scale-up.ts @@ -90,17 +90,6 @@ function generateRunnerServiceConfig(githubRunnerConfig: CreateGitHubRunnerConfi return config; } -async function publishRetryMessagesForValid( - messages: ActionRequestMessageSQS[], - invalidMessageIds: Set, -): Promise { - for (const message of messages) { - if (!invalidMessageIds.has(message.messageId)) { - await publishRetryMessage(message as ActionRequestMessageRetry); - } - } -} - async function getGithubRunnerRegistrationToken(githubRunnerConfig: CreateGitHubRunnerConfig, ghClient: Octokit) { const registrationToken = githubRunnerConfig.runnerType === 'Org' @@ -286,8 +275,7 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise(); - const invalidMessages: string[] = []; - const invalidMessageIds = new Set(); + const rejectedMessageIds = new Set(); for (const payload of payloads) { const { eventType, messageId, repositoryName, repositoryOwner } = payload; if (ephemeralEnabled && eventType !== 'workflow_job') { @@ -296,7 +284,7 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise messageId)); - removedMessages.forEach(({ messageId }) => invalidMessageIds.add(messageId)); + removedMessages.forEach(({ messageId }) => rejectedMessageIds.add(messageId)); } // No runners will be created, so skip calling the EC2 API. if (newRunners <= 0) { - // Publish retry messages for all remaining messages that are not marked as invalid - await publishRetryMessagesForValid(validMessagesForRetry, invalidMessageIds); + // Publish retry messages for messages that are not rejected + for (const message of queuedMessages) { + if (!rejectedMessageIds.has(message.messageId)) { + await publishRetryMessage(message as ActionRequestMessageRetry); + } + } continue; } } @@ -467,15 +458,18 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise messageId)); - failedMessages.forEach(({ messageId }) => invalidMessageIds.add(messageId)); + failedMessages.forEach(({ messageId }) => rejectedMessageIds.add(messageId)); } - // Publish retry messages for all messages that are not marked as invalid - await publishRetryMessagesForValid(validMessagesForRetry, invalidMessageIds); + // Publish retry messages for messages that are not rejected + for (const message of queuedMessages) { + if (!rejectedMessageIds.has(message.messageId)) { + await publishRetryMessage(message as ActionRequestMessageRetry); + } + } } - return invalidMessages; + return Array.from(rejectedMessageIds); } export function getGitHubEnterpriseApiUrl() { From cba7bbf3ed3d662b64fb865236ae67b243d87979 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 23:10:10 +0000 Subject: [PATCH 5/8] ci: run lambda workflow on all PRs, not just PRs to main Remove branch restriction from lambda.yml workflow to support stacked PRs and feature branch workflows. The workflow will now run on any PR that modifies lambda code or the workflow itself. Co-authored-by: npalm <11609620+npalm@users.noreply.github.com> --- .github/workflows/lambda.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/lambda.yml b/.github/workflows/lambda.yml index bbd6d93a8e..6f1e806594 100644 --- a/.github/workflows/lambda.yml +++ b/.github/workflows/lambda.yml @@ -2,8 +2,6 @@ name: Build lambdas on: pull_request: - branches: - - main paths: - 'lambdas/**' - '.github/workflows/lambda.yml' From ef5c981ba833fac9284d2b8501f6190bd8451b00 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 13:27:20 +0000 Subject: [PATCH 6/8] fix: apply prettier formatting fixes Fix formatting issues identified by prettier: - Remove trailing whitespace in scale-up.test.ts - Format .prettierrc and eslint.config.js - Format various tsconfig.json and other config files Co-authored-by: stuartp44 <1926002+stuartp44@users.noreply.github.com> --- lambdas/.prettierrc | 5 +---- lambdas/eslint.config.js | 7 ++----- .../functions/ami-housekeeper/tsconfig.json | 10 +++------- .../src/scale-runners/scale-up.test.ts | 2 +- lambdas/functions/control-plane/tsconfig.json | 10 +++------- .../gh-agent-syncer/tsconfig.build.json | 6 +----- .../functions/gh-agent-syncer/tsconfig.json | 10 +++------- .../termination-watcher/tsconfig.json | 10 +++------- .../test/resources/github_check_run_event.json | 2 +- .../resources/github_workflowjob_event.json | 4 +--- .../resources/multi_runner_configurations.json | 14 ++------------ lambdas/functions/webhook/tsconfig.json | 10 +++------- lambdas/libs/aws-powertools-util/tsconfig.json | 6 ++---- lambdas/libs/aws-ssm-util/tsconfig.json | 10 +++------- lambdas/nx.json | 18 ++++-------------- lambdas/tsconfig.json | 5 +---- lambdas/vitest.base.config.ts | 6 +++--- 17 files changed, 37 insertions(+), 98 deletions(-) diff --git a/lambdas/.prettierrc b/lambdas/.prettierrc index 3c9fb0ebf3..f0f88967ad 100644 --- a/lambdas/.prettierrc +++ b/lambdas/.prettierrc @@ -5,8 +5,5 @@ "semi": true, "importOrderSeparation": true, "importOrderSortSpecifiers": true, - "importOrder": [ - "", - "^[./]" - ] + "importOrder": ["", "^[./]"] } diff --git a/lambdas/eslint.config.js b/lambdas/eslint.config.js index d23aafd438..e7387223a9 100644 --- a/lambdas/eslint.config.js +++ b/lambdas/eslint.config.js @@ -14,9 +14,7 @@ const compat = new FlatCompat({ // Create the ESLint 9.x flat config module.exports = [ js.configs.recommended, - ...compat.extends( - 'plugin:@typescript-eslint/recommended' - ), + ...compat.extends('plugin:@typescript-eslint/recommended'), { // Global linting settings languageOptions: { @@ -28,7 +26,7 @@ module.exports = [ }, plugins: { '@typescript-eslint': require('@typescript-eslint/eslint-plugin'), - 'prettier': require('eslint-plugin-prettier'), + prettier: require('eslint-plugin-prettier'), }, rules: { 'prettier/prettier': 'error', @@ -39,4 +37,3 @@ module.exports = [ ignores: ['**/node_modules/**', '**/dist/**', '**/.nx/**', '**/coverage/**'], }, ]; - diff --git a/lambdas/functions/ami-housekeeper/tsconfig.json b/lambdas/functions/ami-housekeeper/tsconfig.json index 30cbbee83e..9d616eca0f 100644 --- a/lambdas/functions/ami-housekeeper/tsconfig.json +++ b/lambdas/functions/ami-housekeeper/tsconfig.json @@ -1,9 +1,5 @@ { - "extends" : "../../tsconfig.json", - "include": [ - "src/**/*" - ], - "exclude": [ - "src/**/*.test.ts" - ] + "extends": "../../tsconfig.json", + "include": ["src/**/*"], + "exclude": ["src/**/*.test.ts"] } diff --git a/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts b/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts index d6ad9fcec0..52ffd3463e 100644 --- a/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts +++ b/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts @@ -1760,7 +1760,7 @@ describe('Retry mechanism tests', () => { // publishRetryMessage should NOT be called because messages are marked as invalid // Invalid messages go back to the SQS queue and will be retried there expect(mockPublishRetryMessage).not.toHaveBeenCalled(); - + // Verify listEC2Runners is called to check current runner count expect(listEC2Runners).toHaveBeenCalledWith({ environment: 'unit-test-environment', diff --git a/lambdas/functions/control-plane/tsconfig.json b/lambdas/functions/control-plane/tsconfig.json index 30cbbee83e..9d616eca0f 100644 --- a/lambdas/functions/control-plane/tsconfig.json +++ b/lambdas/functions/control-plane/tsconfig.json @@ -1,9 +1,5 @@ { - "extends" : "../../tsconfig.json", - "include": [ - "src/**/*" - ], - "exclude": [ - "src/**/*.test.ts" - ] + "extends": "../../tsconfig.json", + "include": ["src/**/*"], + "exclude": ["src/**/*.test.ts"] } diff --git a/lambdas/functions/gh-agent-syncer/tsconfig.build.json b/lambdas/functions/gh-agent-syncer/tsconfig.build.json index bbda53fb48..8d554ce9b1 100644 --- a/lambdas/functions/gh-agent-syncer/tsconfig.build.json +++ b/lambdas/functions/gh-agent-syncer/tsconfig.build.json @@ -1,8 +1,4 @@ { "extends": "./tsconfig.json", - "exclude": [ - "**/*.test.ts", - "**/__mocks__", - "**/__tests__" - ] + "exclude": ["**/*.test.ts", "**/__mocks__", "**/__tests__"] } diff --git a/lambdas/functions/gh-agent-syncer/tsconfig.json b/lambdas/functions/gh-agent-syncer/tsconfig.json index 30cbbee83e..9d616eca0f 100644 --- a/lambdas/functions/gh-agent-syncer/tsconfig.json +++ b/lambdas/functions/gh-agent-syncer/tsconfig.json @@ -1,9 +1,5 @@ { - "extends" : "../../tsconfig.json", - "include": [ - "src/**/*" - ], - "exclude": [ - "src/**/*.test.ts" - ] + "extends": "../../tsconfig.json", + "include": ["src/**/*"], + "exclude": ["src/**/*.test.ts"] } diff --git a/lambdas/functions/termination-watcher/tsconfig.json b/lambdas/functions/termination-watcher/tsconfig.json index 30cbbee83e..9d616eca0f 100644 --- a/lambdas/functions/termination-watcher/tsconfig.json +++ b/lambdas/functions/termination-watcher/tsconfig.json @@ -1,9 +1,5 @@ { - "extends" : "../../tsconfig.json", - "include": [ - "src/**/*" - ], - "exclude": [ - "src/**/*.test.ts" - ] + "extends": "../../tsconfig.json", + "include": ["src/**/*"], + "exclude": ["src/**/*.test.ts"] } diff --git a/lambdas/functions/webhook/test/resources/github_check_run_event.json b/lambdas/functions/webhook/test/resources/github_check_run_event.json index c66bdda053..dc3c8dea5e 100644 --- a/lambdas/functions/webhook/test/resources/github_check_run_event.json +++ b/lambdas/functions/webhook/test/resources/github_check_run_event.json @@ -306,4 +306,4 @@ "id": 12345, "node_id": "MDweasdludGVaswefasSW5zdGFsbGF0aW9asdfaY0NTI2OQ==" } -} \ No newline at end of file +} diff --git a/lambdas/functions/webhook/test/resources/github_workflowjob_event.json b/lambdas/functions/webhook/test/resources/github_workflowjob_event.json index afaf7f2f3f..18be5e4f1d 100644 --- a/lambdas/functions/webhook/test/resources/github_workflowjob_event.json +++ b/lambdas/functions/webhook/test/resources/github_workflowjob_event.json @@ -15,9 +15,7 @@ "name": "build", "steps": [], "check_run_url": "https://api.github.com/repos/github-aws-runners/terraform-aws-github-runner/check-runs/3169272040", - "labels": [ - "self-hosted" - ] + "labels": ["self-hosted"] }, "repository": { "id": 258465213, diff --git a/lambdas/functions/webhook/test/resources/multi_runner_configurations.json b/lambdas/functions/webhook/test/resources/multi_runner_configurations.json index b90378fb16..f3ce2679d9 100644 --- a/lambdas/functions/webhook/test/resources/multi_runner_configurations.json +++ b/lambdas/functions/webhook/test/resources/multi_runner_configurations.json @@ -4,12 +4,7 @@ "arn": "queueARN-ubuntu", "fifo": false, "matcherConfig": { - "labelMatchers": [[ - "self-hosted", - "linux", - "x64", - "ubuntu" - ]], + "labelMatchers": [["self-hosted", "linux", "x64", "ubuntu"]], "exactMatch": true } }, @@ -18,12 +13,7 @@ "arn": "queueARN-latest", "fifo": false, "matcherConfig": { - "labelMatchers": [[ - "self-hosted", - "linux", - "x64", - "latest" - ]], + "labelMatchers": [["self-hosted", "linux", "x64", "latest"]], "exactMatch": false } } diff --git a/lambdas/functions/webhook/tsconfig.json b/lambdas/functions/webhook/tsconfig.json index 30cbbee83e..9d616eca0f 100644 --- a/lambdas/functions/webhook/tsconfig.json +++ b/lambdas/functions/webhook/tsconfig.json @@ -1,9 +1,5 @@ { - "extends" : "../../tsconfig.json", - "include": [ - "src/**/*" - ], - "exclude": [ - "src/**/*.test.ts" - ] + "extends": "../../tsconfig.json", + "include": ["src/**/*"], + "exclude": ["src/**/*.test.ts"] } diff --git a/lambdas/libs/aws-powertools-util/tsconfig.json b/lambdas/libs/aws-powertools-util/tsconfig.json index f34dbbda1e..52d43eaaa9 100644 --- a/lambdas/libs/aws-powertools-util/tsconfig.json +++ b/lambdas/libs/aws-powertools-util/tsconfig.json @@ -1,6 +1,4 @@ { - "extends" : "../../tsconfig.json", - "include": [ - "src/**/*" - ] + "extends": "../../tsconfig.json", + "include": ["src/**/*"] } diff --git a/lambdas/libs/aws-ssm-util/tsconfig.json b/lambdas/libs/aws-ssm-util/tsconfig.json index 30cbbee83e..9d616eca0f 100644 --- a/lambdas/libs/aws-ssm-util/tsconfig.json +++ b/lambdas/libs/aws-ssm-util/tsconfig.json @@ -1,9 +1,5 @@ { - "extends" : "../../tsconfig.json", - "include": [ - "src/**/*" - ], - "exclude": [ - "src/**/*.test.ts" - ] + "extends": "../../tsconfig.json", + "include": ["src/**/*"], + "exclude": ["src/**/*.test.ts"] } diff --git a/lambdas/nx.json b/lambdas/nx.json index 09252bcd62..bf735dd976 100644 --- a/lambdas/nx.json +++ b/lambdas/nx.json @@ -23,24 +23,14 @@ ], "targetDefaults": { "build": { - "inputs": [ - "{projectRoot}/src/index.ts" - ], - "dependsOn": [ - "default", - "^default", - "^build" - ], + "inputs": ["{projectRoot}/src/index.ts"], + "dependsOn": ["default", "^default", "^build"], "executor": "@nx/workspace:run-commands", "cache": true }, "dist": { - "outputs": [ - "{projectRoot}/dist/**/*" - ], - "dependsOn": [ - "build" - ], + "outputs": ["{projectRoot}/dist/**/*"], + "dependsOn": ["build"], "executor": "@nx/workspace:run-commands", "cache": true } diff --git a/lambdas/tsconfig.json b/lambdas/tsconfig.json index 8dee3cd66a..6dcdf8b274 100644 --- a/lambdas/tsconfig.json +++ b/lambdas/tsconfig.json @@ -3,9 +3,7 @@ "target": "ES2022", "module": "ES2022", "outDir": "dist", - "lib": [ - "es2022" - ], + "lib": ["es2022"], "downlevelIteration": true, "strict": true, "moduleResolution": "bundler", @@ -17,4 +15,3 @@ "types": ["vitest/globals"] } } - diff --git a/lambdas/vitest.base.config.ts b/lambdas/vitest.base.config.ts index f8082eda28..bdfd28d540 100644 --- a/lambdas/vitest.base.config.ts +++ b/lambdas/vitest.base.config.ts @@ -9,11 +9,11 @@ const defaultConfig = defineConfig({ include: ['**/src/**/*.ts'], exclude: ['**/*local*.ts', '**/*.d.ts', '**/*.test.ts', '**/node_modules/**'], all: true, - reportsDirectory: './coverage' + reportsDirectory: './coverage', }, globals: true, - watch: false - } + watch: false, + }, }); export default defaultConfig; From 02434589b5f4543d44e97a2b5482889ff7071dca Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 13:40:11 +0000 Subject: [PATCH 7/8] fix: resolve merge conflicts from stu/fix_job_retry branch The merge from stu/fix_job_retry incorrectly reverted the core change where publishRetryMessage was moved to the end of the loop. This fix restores the correct behavior: - Restore queuedMessages.push(message) instead of await publishRetryMessage(message) - Remove conflicting test assertions from merge (lines expecting both publishRetryMessage NOT to be called AND to be called 2 times) - Reorder test assertions to check listEC2Runners before publishRetryMessage All tests now pass with the correct behavior where publishRetryMessage is called AFTER runner creation and only for non-rejected messages. Co-authored-by: stuartp44 <1926002+stuartp44@users.noreply.github.com> --- .../control-plane/src/scale-runners/scale-up.test.ts | 9 +++------ .../control-plane/src/scale-runners/scale-up.ts | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts b/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts index 52ffd3463e..39fe66cb80 100644 --- a/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts +++ b/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts @@ -1757,10 +1757,6 @@ describe('Retry mechanism tests', () => { await scaleUpModule.scaleUp(messages); - // publishRetryMessage should NOT be called because messages are marked as invalid - // Invalid messages go back to the SQS queue and will be retried there - expect(mockPublishRetryMessage).not.toHaveBeenCalled(); - // Verify listEC2Runners is called to check current runner count expect(listEC2Runners).toHaveBeenCalledWith({ environment: 'unit-test-environment', @@ -1768,8 +1764,9 @@ describe('Retry mechanism tests', () => { runnerOwner: TEST_DATA_SINGLE.repositoryOwner, }); - // publishRetryMessage should still be called even though no runners will be created - expect(mockPublishRetryMessage).toHaveBeenCalledTimes(2); + // publishRetryMessage should NOT be called because messages are marked as invalid + // Invalid messages go back to the SQS queue and will be retried there + expect(mockPublishRetryMessage).not.toHaveBeenCalled(); expect(createRunner).not.toHaveBeenCalled(); }); diff --git a/lambdas/functions/control-plane/src/scale-runners/scale-up.ts b/lambdas/functions/control-plane/src/scale-runners/scale-up.ts index 0f12b2c346..6a3ab93bb7 100644 --- a/lambdas/functions/control-plane/src/scale-runners/scale-up.ts +++ b/lambdas/functions/control-plane/src/scale-runners/scale-up.ts @@ -358,7 +358,7 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise Date: Thu, 8 Jan 2026 09:59:18 +0000 Subject: [PATCH 8/8] revert: remove unnecessary formatting and workflow changes Per reviewer feedback to keep changes minimal and focused on the original objective. Reverting: - Formatting changes to config files (.prettierrc, eslint.config.js, tsconfig.json files, nx.json, vitest.base.config.ts) - Formatting changes to test resource JSON files - Workflow change to lambda.yml (restoring branches: main restriction) Only keeping the essential changes: - lambdas/functions/control-plane/src/scale-runners/scale-up.ts - lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts These are the only files needed to fix the original issue of moving publishRetryMessage to the end of the processing loop to avoid duplicate retries. Co-authored-by: Brend-Smits <15904543+Brend-Smits@users.noreply.github.com> --- .github/workflows/lambda.yml | 2 ++ lambdas/.prettierrc | 5 ++++- lambdas/eslint.config.js | 7 +++++-- .../functions/ami-housekeeper/tsconfig.json | 10 +++++++--- lambdas/functions/control-plane/tsconfig.json | 10 +++++++--- .../gh-agent-syncer/tsconfig.build.json | 6 +++++- .../functions/gh-agent-syncer/tsconfig.json | 10 +++++++--- .../termination-watcher/tsconfig.json | 10 +++++++--- .../test/resources/github_check_run_event.json | 2 +- .../resources/github_workflowjob_event.json | 4 +++- .../resources/multi_runner_configurations.json | 14 ++++++++++++-- lambdas/functions/webhook/tsconfig.json | 10 +++++++--- lambdas/libs/aws-powertools-util/tsconfig.json | 6 ++++-- lambdas/libs/aws-ssm-util/tsconfig.json | 10 +++++++--- lambdas/nx.json | 18 ++++++++++++++---- lambdas/tsconfig.json | 5 ++++- lambdas/vitest.base.config.ts | 6 +++--- 17 files changed, 99 insertions(+), 36 deletions(-) diff --git a/.github/workflows/lambda.yml b/.github/workflows/lambda.yml index 3520725023..4b9103fcd0 100644 --- a/.github/workflows/lambda.yml +++ b/.github/workflows/lambda.yml @@ -2,6 +2,8 @@ name: Build lambdas on: pull_request: + branches: + - main paths: - 'lambdas/**' - '.github/workflows/lambda.yml' diff --git a/lambdas/.prettierrc b/lambdas/.prettierrc index f0f88967ad..3c9fb0ebf3 100644 --- a/lambdas/.prettierrc +++ b/lambdas/.prettierrc @@ -5,5 +5,8 @@ "semi": true, "importOrderSeparation": true, "importOrderSortSpecifiers": true, - "importOrder": ["", "^[./]"] + "importOrder": [ + "", + "^[./]" + ] } diff --git a/lambdas/eslint.config.js b/lambdas/eslint.config.js index e7387223a9..d23aafd438 100644 --- a/lambdas/eslint.config.js +++ b/lambdas/eslint.config.js @@ -14,7 +14,9 @@ const compat = new FlatCompat({ // Create the ESLint 9.x flat config module.exports = [ js.configs.recommended, - ...compat.extends('plugin:@typescript-eslint/recommended'), + ...compat.extends( + 'plugin:@typescript-eslint/recommended' + ), { // Global linting settings languageOptions: { @@ -26,7 +28,7 @@ module.exports = [ }, plugins: { '@typescript-eslint': require('@typescript-eslint/eslint-plugin'), - prettier: require('eslint-plugin-prettier'), + 'prettier': require('eslint-plugin-prettier'), }, rules: { 'prettier/prettier': 'error', @@ -37,3 +39,4 @@ module.exports = [ ignores: ['**/node_modules/**', '**/dist/**', '**/.nx/**', '**/coverage/**'], }, ]; + diff --git a/lambdas/functions/ami-housekeeper/tsconfig.json b/lambdas/functions/ami-housekeeper/tsconfig.json index 9d616eca0f..30cbbee83e 100644 --- a/lambdas/functions/ami-housekeeper/tsconfig.json +++ b/lambdas/functions/ami-housekeeper/tsconfig.json @@ -1,5 +1,9 @@ { - "extends": "../../tsconfig.json", - "include": ["src/**/*"], - "exclude": ["src/**/*.test.ts"] + "extends" : "../../tsconfig.json", + "include": [ + "src/**/*" + ], + "exclude": [ + "src/**/*.test.ts" + ] } diff --git a/lambdas/functions/control-plane/tsconfig.json b/lambdas/functions/control-plane/tsconfig.json index 9d616eca0f..30cbbee83e 100644 --- a/lambdas/functions/control-plane/tsconfig.json +++ b/lambdas/functions/control-plane/tsconfig.json @@ -1,5 +1,9 @@ { - "extends": "../../tsconfig.json", - "include": ["src/**/*"], - "exclude": ["src/**/*.test.ts"] + "extends" : "../../tsconfig.json", + "include": [ + "src/**/*" + ], + "exclude": [ + "src/**/*.test.ts" + ] } diff --git a/lambdas/functions/gh-agent-syncer/tsconfig.build.json b/lambdas/functions/gh-agent-syncer/tsconfig.build.json index 8d554ce9b1..bbda53fb48 100644 --- a/lambdas/functions/gh-agent-syncer/tsconfig.build.json +++ b/lambdas/functions/gh-agent-syncer/tsconfig.build.json @@ -1,4 +1,8 @@ { "extends": "./tsconfig.json", - "exclude": ["**/*.test.ts", "**/__mocks__", "**/__tests__"] + "exclude": [ + "**/*.test.ts", + "**/__mocks__", + "**/__tests__" + ] } diff --git a/lambdas/functions/gh-agent-syncer/tsconfig.json b/lambdas/functions/gh-agent-syncer/tsconfig.json index 9d616eca0f..30cbbee83e 100644 --- a/lambdas/functions/gh-agent-syncer/tsconfig.json +++ b/lambdas/functions/gh-agent-syncer/tsconfig.json @@ -1,5 +1,9 @@ { - "extends": "../../tsconfig.json", - "include": ["src/**/*"], - "exclude": ["src/**/*.test.ts"] + "extends" : "../../tsconfig.json", + "include": [ + "src/**/*" + ], + "exclude": [ + "src/**/*.test.ts" + ] } diff --git a/lambdas/functions/termination-watcher/tsconfig.json b/lambdas/functions/termination-watcher/tsconfig.json index 9d616eca0f..30cbbee83e 100644 --- a/lambdas/functions/termination-watcher/tsconfig.json +++ b/lambdas/functions/termination-watcher/tsconfig.json @@ -1,5 +1,9 @@ { - "extends": "../../tsconfig.json", - "include": ["src/**/*"], - "exclude": ["src/**/*.test.ts"] + "extends" : "../../tsconfig.json", + "include": [ + "src/**/*" + ], + "exclude": [ + "src/**/*.test.ts" + ] } diff --git a/lambdas/functions/webhook/test/resources/github_check_run_event.json b/lambdas/functions/webhook/test/resources/github_check_run_event.json index dc3c8dea5e..c66bdda053 100644 --- a/lambdas/functions/webhook/test/resources/github_check_run_event.json +++ b/lambdas/functions/webhook/test/resources/github_check_run_event.json @@ -306,4 +306,4 @@ "id": 12345, "node_id": "MDweasdludGVaswefasSW5zdGFsbGF0aW9asdfaY0NTI2OQ==" } -} +} \ No newline at end of file diff --git a/lambdas/functions/webhook/test/resources/github_workflowjob_event.json b/lambdas/functions/webhook/test/resources/github_workflowjob_event.json index 18be5e4f1d..afaf7f2f3f 100644 --- a/lambdas/functions/webhook/test/resources/github_workflowjob_event.json +++ b/lambdas/functions/webhook/test/resources/github_workflowjob_event.json @@ -15,7 +15,9 @@ "name": "build", "steps": [], "check_run_url": "https://api.github.com/repos/github-aws-runners/terraform-aws-github-runner/check-runs/3169272040", - "labels": ["self-hosted"] + "labels": [ + "self-hosted" + ] }, "repository": { "id": 258465213, diff --git a/lambdas/functions/webhook/test/resources/multi_runner_configurations.json b/lambdas/functions/webhook/test/resources/multi_runner_configurations.json index f3ce2679d9..b90378fb16 100644 --- a/lambdas/functions/webhook/test/resources/multi_runner_configurations.json +++ b/lambdas/functions/webhook/test/resources/multi_runner_configurations.json @@ -4,7 +4,12 @@ "arn": "queueARN-ubuntu", "fifo": false, "matcherConfig": { - "labelMatchers": [["self-hosted", "linux", "x64", "ubuntu"]], + "labelMatchers": [[ + "self-hosted", + "linux", + "x64", + "ubuntu" + ]], "exactMatch": true } }, @@ -13,7 +18,12 @@ "arn": "queueARN-latest", "fifo": false, "matcherConfig": { - "labelMatchers": [["self-hosted", "linux", "x64", "latest"]], + "labelMatchers": [[ + "self-hosted", + "linux", + "x64", + "latest" + ]], "exactMatch": false } } diff --git a/lambdas/functions/webhook/tsconfig.json b/lambdas/functions/webhook/tsconfig.json index 9d616eca0f..30cbbee83e 100644 --- a/lambdas/functions/webhook/tsconfig.json +++ b/lambdas/functions/webhook/tsconfig.json @@ -1,5 +1,9 @@ { - "extends": "../../tsconfig.json", - "include": ["src/**/*"], - "exclude": ["src/**/*.test.ts"] + "extends" : "../../tsconfig.json", + "include": [ + "src/**/*" + ], + "exclude": [ + "src/**/*.test.ts" + ] } diff --git a/lambdas/libs/aws-powertools-util/tsconfig.json b/lambdas/libs/aws-powertools-util/tsconfig.json index 52d43eaaa9..f34dbbda1e 100644 --- a/lambdas/libs/aws-powertools-util/tsconfig.json +++ b/lambdas/libs/aws-powertools-util/tsconfig.json @@ -1,4 +1,6 @@ { - "extends": "../../tsconfig.json", - "include": ["src/**/*"] + "extends" : "../../tsconfig.json", + "include": [ + "src/**/*" + ] } diff --git a/lambdas/libs/aws-ssm-util/tsconfig.json b/lambdas/libs/aws-ssm-util/tsconfig.json index 9d616eca0f..30cbbee83e 100644 --- a/lambdas/libs/aws-ssm-util/tsconfig.json +++ b/lambdas/libs/aws-ssm-util/tsconfig.json @@ -1,5 +1,9 @@ { - "extends": "../../tsconfig.json", - "include": ["src/**/*"], - "exclude": ["src/**/*.test.ts"] + "extends" : "../../tsconfig.json", + "include": [ + "src/**/*" + ], + "exclude": [ + "src/**/*.test.ts" + ] } diff --git a/lambdas/nx.json b/lambdas/nx.json index bf735dd976..09252bcd62 100644 --- a/lambdas/nx.json +++ b/lambdas/nx.json @@ -23,14 +23,24 @@ ], "targetDefaults": { "build": { - "inputs": ["{projectRoot}/src/index.ts"], - "dependsOn": ["default", "^default", "^build"], + "inputs": [ + "{projectRoot}/src/index.ts" + ], + "dependsOn": [ + "default", + "^default", + "^build" + ], "executor": "@nx/workspace:run-commands", "cache": true }, "dist": { - "outputs": ["{projectRoot}/dist/**/*"], - "dependsOn": ["build"], + "outputs": [ + "{projectRoot}/dist/**/*" + ], + "dependsOn": [ + "build" + ], "executor": "@nx/workspace:run-commands", "cache": true } diff --git a/lambdas/tsconfig.json b/lambdas/tsconfig.json index 6dcdf8b274..8dee3cd66a 100644 --- a/lambdas/tsconfig.json +++ b/lambdas/tsconfig.json @@ -3,7 +3,9 @@ "target": "ES2022", "module": "ES2022", "outDir": "dist", - "lib": ["es2022"], + "lib": [ + "es2022" + ], "downlevelIteration": true, "strict": true, "moduleResolution": "bundler", @@ -15,3 +17,4 @@ "types": ["vitest/globals"] } } + diff --git a/lambdas/vitest.base.config.ts b/lambdas/vitest.base.config.ts index bdfd28d540..f8082eda28 100644 --- a/lambdas/vitest.base.config.ts +++ b/lambdas/vitest.base.config.ts @@ -9,11 +9,11 @@ const defaultConfig = defineConfig({ include: ['**/src/**/*.ts'], exclude: ['**/*local*.ts', '**/*.d.ts', '**/*.test.ts', '**/node_modules/**'], all: true, - reportsDirectory: './coverage', + reportsDirectory: './coverage' }, globals: true, - watch: false, - }, + watch: false + } }); export default defaultConfig;