diff --git a/src/commands/deploy/deploy.ts b/src/commands/deploy/deploy.ts
index e3c96fcc55c..7828979231d 100644
--- a/src/commands/deploy/deploy.ts
+++ b/src/commands/deploy/deploy.ts
@@ -40,6 +40,7 @@ import {
} from '../../utils/command-helpers.js'
import { DEFAULT_CONCURRENT_HASH, DEFAULT_DEPLOY_TIMEOUT } from '../../utils/deploy/constants.js'
import { type DeployEvent, deploySite } from '../../utils/deploy/deploy-site.js'
+import { getDeploySourceFields } from '../../utils/deploy/deploy-source.js'
import { uploadSourceZip } from '../../utils/deploy/upload-source-zip.js'
import { getEnvelopeEnv } from '../../utils/env/index.js'
import { mergeDeployEnvVars } from '../../utils/env/deploy-env-vars.js'
@@ -600,7 +601,7 @@ const runDeploy = async ({
draft,
branch: alias,
include_upload_url: options.uploadSourceZip,
- deploy_source: process.env.NETLIFY_DEPLOY_SOURCE || 'cli',
+ ...getDeploySourceFields(),
}
const createDeployResponse = await api.createSiteDeploy({ siteId, title, body: createDeployBody })
@@ -1390,7 +1391,7 @@ export const deploy = async (options: DeployOptionValues, command: BaseCommand)
draft,
branch: alias,
include_upload_url: options.uploadSourceZip,
- deploy_source: process.env.NETLIFY_DEPLOY_SOURCE || 'cli',
+ ...getDeploySourceFields(),
}
// TODO: Type this properly in `@netlify/api`.
diff --git a/src/utils/deploy/deploy-source.ts b/src/utils/deploy/deploy-source.ts
new file mode 100644
index 00000000000..81c490eb207
--- /dev/null
+++ b/src/utils/deploy/deploy-source.ts
@@ -0,0 +1,16 @@
+export interface DeploySourceFields {
+ deploy_source: string
+ agent_runner_id?: string
+ agent_runner_session_id?: string
+}
+
+export const getDeploySourceFields = (): DeploySourceFields => {
+ const agentRunnerId = process.env.NETLIFY_AGENT_RUNNER_ID
+ const agentRunnerSessionId = process.env.NETLIFY_AGENT_RUNNER_SESSION_ID
+
+ return {
+ deploy_source: process.env.NETLIFY_DEPLOY_SOURCE || 'cli',
+ ...(agentRunnerId ? { agent_runner_id: agentRunnerId } : {}),
+ ...(agentRunnerSessionId ? { agent_runner_session_id: agentRunnerSessionId } : {}),
+ }
+}
diff --git a/tests/integration/commands/deploy/deploy-api-routes.ts b/tests/integration/commands/deploy/deploy-api-routes.ts
index 8d5696c4c24..8bde2f00c7d 100644
--- a/tests/integration/commands/deploy/deploy-api-routes.ts
+++ b/tests/integration/commands/deploy/deploy-api-routes.ts
@@ -53,6 +53,8 @@ interface CreateDeployBody {
branch?: string
environment?: DeployEnvironmentVariable[]
deploy_source?: string
+ agent_runner_id?: string
+ agent_runner_session_id?: string
}
export interface DeployRouteState {
diff --git a/tests/integration/commands/deploy/deploy.test.ts b/tests/integration/commands/deploy/deploy.test.ts
index efcf72613ab..1e137585460 100644
--- a/tests/integration/commands/deploy/deploy.test.ts
+++ b/tests/integration/commands/deploy/deploy.test.ts
@@ -1535,6 +1535,80 @@ describe.concurrent('deploy command', () => {
})
})
+ test('should forward agent runner ids from the environment in create deploy request', async (t) => {
+ await withMockDeploy(async (mockApi) => {
+ await withSiteBuilder(t, async (builder) => {
+ builder.withContentFile({
+ path: 'public/index.html',
+ content: '
test
',
+ })
+
+ await builder.build()
+
+ await callCli(
+ ['deploy', '--json', '--no-build', '--dir', 'public'],
+ getCLIOptions({
+ apiUrl: mockApi.apiUrl,
+ builder,
+ env: {
+ NETLIFY_DEPLOY_SOURCE: 'agent_runner',
+ NETLIFY_AGENT_RUNNER_ID: 'runner-123',
+ NETLIFY_AGENT_RUNNER_SESSION_ID: 'session-456',
+ },
+ }),
+ ).then(parseDeploy)
+
+ const createDeployRequest = mockApi.requests.find(
+ (req) => req.method === 'POST' && req.path === '/api/v1/sites/site_id/deploys',
+ )
+ expect(createDeployRequest).toBeDefined()
+ expect(createDeployRequest!.body as Record).toMatchObject({
+ deploy_source: 'agent_runner',
+ agent_runner_id: 'runner-123',
+ agent_runner_session_id: 'session-456',
+ })
+ })
+ })
+ })
+
+ test('should forward agent runner ids in create deploy request when building', async (t) => {
+ await withMockDeploy(async (mockApi) => {
+ await withSiteBuilder(t, async (builder) => {
+ builder
+ .withContentFile({
+ path: 'public/index.html',
+ content: 'test
',
+ })
+ .withNetlifyToml({ config: { build: { publish: 'public' } } })
+
+ await builder.build()
+
+ await callCli(
+ ['deploy', '--json'],
+ getCLIOptions({
+ apiUrl: mockApi.apiUrl,
+ builder,
+ env: {
+ NETLIFY_DEPLOY_SOURCE: 'agent_runner',
+ NETLIFY_AGENT_RUNNER_ID: 'runner-123',
+ NETLIFY_AGENT_RUNNER_SESSION_ID: 'session-456',
+ },
+ }),
+ ).then(parseDeploy)
+
+ const createDeployRequest = mockApi.requests.find(
+ (req) => req.method === 'POST' && req.path === '/api/v1/sites/site_id/deploys',
+ )
+ expect(createDeployRequest).toBeDefined()
+ expect(createDeployRequest!.body as Record).toMatchObject({
+ deploy_source: 'agent_runner',
+ agent_runner_id: 'runner-123',
+ agent_runner_session_id: 'session-456',
+ })
+ })
+ })
+ })
+
test('should include build_version in deploy body', async (t) => {
await withMockDeploy(async (mockApi, deployState) => {
await withSiteBuilder(t, async (builder) => {
diff --git a/tests/unit/utils/deploy/deploy-source.test.ts b/tests/unit/utils/deploy/deploy-source.test.ts
new file mode 100644
index 00000000000..d36d4e363fc
--- /dev/null
+++ b/tests/unit/utils/deploy/deploy-source.test.ts
@@ -0,0 +1,48 @@
+import { afterEach, beforeEach, expect, test, vi } from 'vitest'
+
+import { getDeploySourceFields } from '../../../../src/utils/deploy/deploy-source.js'
+
+beforeEach(() => {
+ vi.stubEnv('NETLIFY_DEPLOY_SOURCE', undefined)
+ vi.stubEnv('NETLIFY_AGENT_RUNNER_ID', undefined)
+ vi.stubEnv('NETLIFY_AGENT_RUNNER_SESSION_ID', undefined)
+})
+
+afterEach(() => {
+ vi.unstubAllEnvs()
+})
+
+test('defaults the deploy source to `cli`', () => {
+ expect(getDeploySourceFields()).toEqual({ deploy_source: 'cli' })
+})
+
+test('honors NETLIFY_DEPLOY_SOURCE', () => {
+ vi.stubEnv('NETLIFY_DEPLOY_SOURCE', 'agent_runner')
+
+ expect(getDeploySourceFields()).toEqual({ deploy_source: 'agent_runner' })
+})
+
+test('forwards the agent runner ids', () => {
+ vi.stubEnv('NETLIFY_DEPLOY_SOURCE', 'agent_runner')
+ vi.stubEnv('NETLIFY_AGENT_RUNNER_ID', 'runner-123')
+ vi.stubEnv('NETLIFY_AGENT_RUNNER_SESSION_ID', 'session-456')
+
+ expect(getDeploySourceFields()).toEqual({
+ deploy_source: 'agent_runner',
+ agent_runner_id: 'runner-123',
+ agent_runner_session_id: 'session-456',
+ })
+})
+
+test('omits the session id when only the runner id is set', () => {
+ vi.stubEnv('NETLIFY_AGENT_RUNNER_ID', 'runner-123')
+
+ expect(getDeploySourceFields()).toEqual({ deploy_source: 'cli', agent_runner_id: 'runner-123' })
+})
+
+test('omits empty agent runner ids', () => {
+ vi.stubEnv('NETLIFY_AGENT_RUNNER_ID', '')
+ vi.stubEnv('NETLIFY_AGENT_RUNNER_SESSION_ID', '')
+
+ expect(getDeploySourceFields()).toEqual({ deploy_source: 'cli' })
+})