Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(config): fail builds if failed to fetch extensions #6008

Merged
merged 10 commits into from
Jan 15, 2025
Merged
33 changes: 28 additions & 5 deletions packages/config/src/api/site_info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ export const getSiteInfo = async function ({
offline = false,
testOpts = {},
siteFeatureFlagPrefix,
featureFlags = {},
}: GetSiteInfoOpts) {
const { env: testEnv = false } = testOpts
const errorOnExtensionFetchFail = featureFlags.error_builds_on_extension_fetch_fail

if (api === undefined || mode === 'buildbot' || testEnv) {
const siteInfo: { id?: string; account_id?: string } = {}
Expand All @@ -46,7 +48,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, errorOnExtensionFetchFail })
: []

return { siteInfo, accounts: [], addons: [], integrations }
}
Expand All @@ -55,7 +59,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, errorOnExtensionFetchFail }),
]

const [siteInfo, accounts, addons, integrations] = await Promise.all(promises)
Expand Down Expand Up @@ -109,13 +113,15 @@ type GetIntegrationsOpts = {
accountId?: string
testOpts: TestOptions
offline: boolean
errorOnExtensionFetchFail?: boolean
}

const getIntegrations = async function ({
siteId,
accountId,
testOpts,
offline,
errorOnExtensionFetchFail,
}: GetIntegrationsOpts): Promise<IntegrationResponse[]> {
if (!siteId || offline) {
return []
Expand All @@ -130,14 +136,31 @@ const getIntegrations = async function ({
? `${baseUrl}team/${accountId}/integrations/installations/meta/${siteId}`
: `${baseUrl}site/${siteId}/integrations/safe`

if (errorOnExtensionFetchFail) {
try {
const response = await fetch(url)
if (!response.ok) {
throw new Error(`Unexpected status code ${response.status} from fetching extensions`)
}
const bodyText = await response.text()
if (bodyText === '') {
return []
}

const integrations = await JSON.parse(bodyText)
return Array.isArray(integrations) ? integrations : []
} catch (error) {
return throwUserError(
`Failed retrieving extensions for site ${siteId}: ${error.message}. ${ERROR_CALL_TO_ACTION}`,
)
}
}

try {
const response = await fetch(url)

const integrations = await response.json()
return Array.isArray(integrations) ? integrations : []
} catch (error) {
// TODO: We should consider blocking the build as integrations are a critical part of the build process
// https://linear.app/netlify/issue/CT-1214/implement-strategy-in-builds-to-deal-with-integrations-that-we-fail-to
return []
}
}
56 changes: 56 additions & 0 deletions packages/config/tests/api/snapshots/tests.js.md
Original file line number Diff line number Diff line change
Expand Up @@ -2250,6 +2250,56 @@ Generated by [AVA](https://avajs.dev).
"token": "test"␊
}`

## Integrations are not returned if failed to fetch integrations and if flag is true

> Snapshot 1

'Failed retrieving extensions for site test: Unexpected status code 500 from fetching extensions. Double-check your login status with \'netlify status\' or contact support with details of your error.'

## Empty array of integrations are returned if failed to fetch integrations and if flag is false

> Snapshot 1

`{␊
"accounts": [],␊
"addons": [],␊
"branch": "branch",␊
"buildDir": "packages/config/tests/api/fixtures/base",␊
"config": {␊
"build": {␊
"environment": {},␊
"processing": {␊
"css": {},␊
"html": {},␊
"images": {},␊
"js": {}␊
},␊
"publish": "packages/config/tests/api/fixtures/base",␊
"publishOrigin": "default",␊
"services": {}␊
},␊
"functions": {␊
"*": {}␊
},␊
"headers": [],␊
"plugins": [],␊
"redirects": []␊
},␊
"configPath": "packages/config/tests/api/fixtures/base/netlify.toml",␊
"context": "production",␊
"env": {},␊
"hasApi": true,␊
"headersPath": "packages/config/tests/api/fixtures/base/_headers",␊
"integrations": [],␊
"redirectsPath": "packages/config/tests/api/fixtures/base/_redirects",␊
"repositoryRoot": "packages/config/tests/api/fixtures/base",␊
"siteInfo": {␊
"account_id": "account1",␊
"id": "test"␊
},␊
"token": "test"␊
}`

## baseRelDir is true if build.base is overridden

> Snapshot 1
Expand Down Expand Up @@ -2413,3 +2463,9 @@ Generated by [AVA](https://avajs.dev).
},␊
"token": "test"␊
}`

## Integrations are not returned if failed to fetch integrations

> Snapshot 1

'Failed retrieving extensions for site test: Unexpected status code 500 from fetching extensions. Double-check your login status with \'netlify status\' or contact support with details of your error.'
Binary file modified packages/config/tests/api/snapshots/tests.js.snap
Binary file not shown.
46 changes: 46 additions & 0 deletions packages/config/tests/api/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ const TEAM_INSTALLATIONS_META_RESPONSE = {
],
}

const TEAM_INSTALLATIONS_META_RESPONSE_INTERNAL_SERVER_ERROR = {
path: '/team/account1/integrations/installations/meta/test',
response: { error: 'Internal Server Error' },
status: 500,
}

const SITE_INTEGRATIONS_EMPTY_RESPONSE = {
path: '/site/test/integrations/safe',
response: [],
Expand Down Expand Up @@ -413,6 +419,46 @@ test('Integrations are returned if accountId is present and mode is dev', async
t.assert(config.integrations[0].has_build === true)
})

test('Integrations are not returned if failed to fetch integrations and if flag is true', async (t) => {
const { output } = await new Fixture('./fixtures/base')
.withFlags({
siteId: 'test',
mode: 'buildbot',
accountId: 'account1',
token: 'test',
featureFlags: {
error_builds_on_extension_fetch_fail: true,
},
})
.runConfigServer([
SITE_INFO_DATA,
TEAM_INSTALLATIONS_META_RESPONSE_INTERNAL_SERVER_ERROR,
FETCH_INTEGRATIONS_EMPTY_RESPONSE,
])

t.snapshot(normalizeOutput(output))
})

test('Empty array of integrations are returned if failed to fetch integrations and if flag is false', async (t) => {
const { output } = await new Fixture('./fixtures/base')
.withFlags({
siteId: 'test',
mode: 'buildbot',
accountId: 'account1',
token: 'test',
featureFlags: {
error_builds_on_extension_fetch_fail: false,
},
})
.runConfigServer([
SITE_INFO_DATA,
TEAM_INSTALLATIONS_META_RESPONSE_INTERNAL_SERVER_ERROR,
FETCH_INTEGRATIONS_EMPTY_RESPONSE,
])

t.snapshot(normalizeOutput(output))
})

test('baseRelDir is true if build.base is overridden', async (t) => {
const fixturesDir = normalize(`${fileURLToPath(test.meta.file)}/../fixtures`)

Expand Down
Loading