diff --git a/packages/build/src/plugins/child/run.js b/packages/build/src/plugins/child/run.js index 4433f57c38..1fbc42c160 100644 --- a/packages/build/src/plugins/child/run.js +++ b/packages/build/src/plugins/child/run.js @@ -10,7 +10,7 @@ import { getUtils } from './utils.js' /** Run a specific plugin event handler */ export const run = async function ( - { event, error, constants, envChanges, featureFlags, netlifyConfig, otelCarrier }, + { event, error, constants, envChanges, featureFlags, netlifyConfig, otelCarrier, extensionMetadata }, { methods, inputs, packageJson, verbose }, ) { setGlobalContext(propagation.extract(context.active(), otelCarrier)) @@ -31,6 +31,7 @@ export const run = async function ( error, featureFlags, systemLog, + extensionMetadata, } const envBefore = setEnvChanges(envChanges) diff --git a/packages/build/src/steps/plugin.js b/packages/build/src/steps/plugin.js index a18eafba71..655dbb359e 100644 --- a/packages/build/src/steps/plugin.js +++ b/packages/build/src/steps/plugin.js @@ -66,6 +66,7 @@ export const firePluginStep = async function ({ netlifyConfig, constants, otelCarrier, + extensionMetadata, }, logs: logsA, verbose, diff --git a/packages/build/src/types/netlify_plugin_options.ts b/packages/build/src/types/netlify_plugin_options.ts index a5f1da7cbb..3be67b322a 100644 --- a/packages/build/src/types/netlify_plugin_options.ts +++ b/packages/build/src/types/netlify_plugin_options.ts @@ -23,4 +23,8 @@ export interface NetlifyPluginOptions systemLog?(message: string): void + /** + * When an extension's event handler executes, we pass extension-specific metadata to the exension + */ + extensionMetadata: { extension_token?: string } } diff --git a/packages/config/src/api/site_info.ts b/packages/config/src/api/site_info.ts index e0612918d1..ec9586999b 100644 --- a/packages/config/src/api/site_info.ts +++ b/packages/config/src/api/site_info.ts @@ -1,5 +1,6 @@ import { NetlifyAPI } from 'netlify' import fetch from 'node-fetch' +import type { RequestInit } from 'node-fetch' import { getEnvelope } from '../env/envelope.js' import { throwUserError } from '../error.js' @@ -17,6 +18,7 @@ type GetSiteInfoOpts = { featureFlags?: Record testOpts?: TestOptions siteFeatureFlagPrefix: string + token: string } /** * Retrieve Netlify Site information, if available. @@ -36,6 +38,8 @@ export const getSiteInfo = async function ({ offline = false, testOpts = {}, siteFeatureFlagPrefix, + token, + featureFlags = {}, }: GetSiteInfoOpts) { const { env: testEnv = false } = testOpts @@ -46,7 +50,9 @@ export const getSiteInfo = async function ({ if (accountId !== undefined) siteInfo.account_id = accountId const integrations = - mode === 'buildbot' && !offline ? await getIntegrations({ siteId, testOpts, offline, accountId }) : [] + mode === 'buildbot' && !offline + ? await getIntegrations({ siteId, testOpts, offline, accountId, token, featureFlags }) + : [] return { siteInfo, accounts: [], addons: [], integrations } } @@ -55,7 +61,7 @@ export const getSiteInfo = async function ({ getSite(api, siteId, siteFeatureFlagPrefix), getAccounts(api), getAddons(api, siteId), - getIntegrations({ siteId, testOpts, offline, accountId }), + getIntegrations({ siteId, testOpts, offline, accountId, token, featureFlags }), ] const [siteInfo, accounts, addons, integrations] = await Promise.all(promises) @@ -109,6 +115,8 @@ type GetIntegrationsOpts = { accountId?: string testOpts: TestOptions offline: boolean + token?: string + featureFlags?: Record } const getIntegrations = async function ({ @@ -116,11 +124,13 @@ const getIntegrations = async function ({ accountId, testOpts, offline, + token, + featureFlags, }: GetIntegrationsOpts): Promise { if (!siteId || offline) { return [] } - + const sendBuildBotTokenToJigsaw = featureFlags?.send_build_bot_token_to_jigsaw const { host } = testOpts const baseUrl = new URL(host ? `http://${host}` : `https://api.netlifysdk.com`) @@ -131,7 +141,15 @@ const getIntegrations = async function ({ : `${baseUrl}site/${siteId}/integrations/safe` try { - const response = await fetch(url) + const requestOptions = {} as RequestInit + + if (sendBuildBotTokenToJigsaw && token) { + requestOptions.headers = { + 'netlify-sdk-build-bot-token': token, + } + } + + const response = await fetch(url, requestOptions) if (!response.ok) { throw new Error(`Unexpected status code ${response.status} from fetching extensions`) } diff --git a/packages/config/src/main.ts b/packages/config/src/main.ts index a42dd9e7a8..a9e20759e7 100644 --- a/packages/config/src/main.ts +++ b/packages/config/src/main.ts @@ -80,6 +80,7 @@ export const resolveConfig = async function (opts) { offline, featureFlags, testOpts, + token, }) const { defaultConfig: defaultConfigA, baseRelDir: baseRelDirA } = parseDefaultConfig({ diff --git a/packages/config/tests/api/tests.js b/packages/config/tests/api/tests.js index e235c0bbeb..f01d136981 100644 --- a/packages/config/tests/api/tests.js +++ b/packages/config/tests/api/tests.js @@ -419,6 +419,32 @@ test('Integrations are returned if accountId is present and mode is dev', async t.assert(config.integrations[0].has_build === true) }) +test('Integrations are returned and called with a netlify-sdk-build-bot-token', async (t) => { + const { output, requests } = await new Fixture('./fixtures/base') + .withFlags({ + siteId: 'test', + mode: 'dev', + token: 'test', + accountId: 'account1', + featureFlags: { + send_build_bot_token_to_jigsaw: true, + }, + }) + .runConfigServer([SITE_INFO_DATA, TEAM_INSTALLATIONS_META_RESPONSE, FETCH_INTEGRATIONS_EMPTY_RESPONSE]) + + const config = JSON.parse(output) + const installationsHeaders = requests.find( + (request) => request.url === TEAM_INSTALLATIONS_META_RESPONSE.path, + )?.headers + + t.assert(installationsHeaders.includes('netlify-sdk-build-bot-token')) + t.assert(config.integrations) + t.assert(config.integrations.length === 1) + t.assert(config.integrations[0].slug === 'test') + t.assert(config.integrations[0].version === 'so-cool-v2') + t.assert(config.integrations[0].has_build === true) +}) + test('Integrations are not returned if failed to fetch integrations', async (t) => { const { output } = await new Fixture('./fixtures/base') .withFlags({