From ed853b7e5d62d7d328dcc5f9783a72e28618d5c2 Mon Sep 17 00:00:00 2001 From: khen <30577427+khendrikse@users.noreply.github.com> Date: Thu, 20 Feb 2025 16:17:08 +0100 Subject: [PATCH 1/2] feat: allow jigsaw to be called with the build bot token --- packages/build/src/plugins/child/run.js | 3 ++- packages/build/src/steps/plugin.js | 1 + .../build/src/types/netlify_plugin_options.ts | 4 +++ packages/config/src/api/site_info.ts | 25 +++++++++++++++--- packages/config/src/main.ts | 1 + packages/config/tests/api/tests.js | 26 +++++++++++++++++++ 6 files changed, 56 insertions(+), 4 deletions(-) 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..b9ec5762f6 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,8 +38,11 @@ export const getSiteInfo = async function ({ offline = false, testOpts = {}, siteFeatureFlagPrefix, + token, + featureFlags = {}, }: GetSiteInfoOpts) { const { env: testEnv = false } = testOpts + const sendBuildBotTokenToJigsaw = featureFlags.send_build_bot_token_to_jigsaw if (api === undefined || mode === 'buildbot' || testEnv) { const siteInfo: { id?: string; account_id?: string } = {} @@ -46,7 +51,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, sendBuildBotTokenToJigsaw }) + : [] return { siteInfo, accounts: [], addons: [], integrations } } @@ -55,7 +62,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, sendBuildBotTokenToJigsaw }), ] const [siteInfo, accounts, addons, integrations] = await Promise.all(promises) @@ -109,6 +116,8 @@ type GetIntegrationsOpts = { accountId?: string testOpts: TestOptions offline: boolean + token?: string + sendBuildBotTokenToJigsaw?: boolean } const getIntegrations = async function ({ @@ -116,6 +125,8 @@ const getIntegrations = async function ({ accountId, testOpts, offline, + token, + sendBuildBotTokenToJigsaw, }: GetIntegrationsOpts): Promise { if (!siteId || offline) { return [] @@ -131,7 +142,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({ From b216dd5c746882541a56b0aa720dc38ce5ad4ca1 Mon Sep 17 00:00:00 2001 From: khen <30577427+khendrikse@users.noreply.github.com> Date: Fri, 21 Feb 2025 12:24:58 +0100 Subject: [PATCH 2/2] chore: pass ffs to getIntegrations --- packages/config/src/api/site_info.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/config/src/api/site_info.ts b/packages/config/src/api/site_info.ts index b9ec5762f6..ec9586999b 100644 --- a/packages/config/src/api/site_info.ts +++ b/packages/config/src/api/site_info.ts @@ -42,7 +42,6 @@ export const getSiteInfo = async function ({ featureFlags = {}, }: GetSiteInfoOpts) { const { env: testEnv = false } = testOpts - const sendBuildBotTokenToJigsaw = featureFlags.send_build_bot_token_to_jigsaw if (api === undefined || mode === 'buildbot' || testEnv) { const siteInfo: { id?: string; account_id?: string } = {} @@ -52,7 +51,7 @@ export const getSiteInfo = async function ({ const integrations = mode === 'buildbot' && !offline - ? await getIntegrations({ siteId, testOpts, offline, accountId, token, sendBuildBotTokenToJigsaw }) + ? await getIntegrations({ siteId, testOpts, offline, accountId, token, featureFlags }) : [] return { siteInfo, accounts: [], addons: [], integrations } @@ -62,7 +61,7 @@ export const getSiteInfo = async function ({ getSite(api, siteId, siteFeatureFlagPrefix), getAccounts(api), getAddons(api, siteId), - getIntegrations({ siteId, testOpts, offline, accountId, token, sendBuildBotTokenToJigsaw }), + getIntegrations({ siteId, testOpts, offline, accountId, token, featureFlags }), ] const [siteInfo, accounts, addons, integrations] = await Promise.all(promises) @@ -117,7 +116,7 @@ type GetIntegrationsOpts = { testOpts: TestOptions offline: boolean token?: string - sendBuildBotTokenToJigsaw?: boolean + featureFlags?: Record } const getIntegrations = async function ({ @@ -126,12 +125,12 @@ const getIntegrations = async function ({ testOpts, offline, token, - sendBuildBotTokenToJigsaw, + 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`)