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

chore: instrument plugin version pinning #5582

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 71 additions & 41 deletions packages/build/src/plugins/pinned_version.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
import { trace, context } from '@opentelemetry/api'
import { wrapTracer } from '@opentelemetry/api/experimental'

import { handleBuildError } from '../error/handle.js'
import { addBuildErrorToActiveSpan } from '../tracing/main.js'
import { getMajorVersion, isPrerelease } from '../utils/semver.js'

const tracer = wrapTracer(trace.getTracer('plugins'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a problem, just FYI:
If you are not using new methods like with(Active)Span from wrapped / sugared tracer - this is no-op as it will just inherit regular tracer start(Active)Span without modifying their behavior

Ref: https://github.com/open-telemetry/opentelemetry-js/blob/c0468673cb0cfe61d12edf69745fabfdf940d7b0/api/src/experimental/trace/SugaredTracer.ts#L30-L44

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah thanks for the callout, I just assumed this would just keep the same interface -.- and it bums me out that it doesnt 😭


// Retrieve plugin's pinned major versions by fetching the latest `PluginRun`
// Only applies to `netlify.toml`-only installed plugins.
export const addPinnedVersions = async function ({ pluginsOptions, api, siteInfo: { id: siteId }, sendStatus }) {
if (!sendStatus || api === undefined || !siteId) {
return pluginsOptions
}

const packages = pluginsOptions.filter(shouldFetchPinVersion).map(getPackageName)
if (packages.length === 0) {
return pluginsOptions
}
return await tracer.startActiveSpan('add-pinned-version', async (span) => {
const packages = pluginsOptions.filter(shouldFetchPinVersion).map(getPackageName)
if (packages.length === 0) {
return pluginsOptions
}
span.addAttributes({
'build.plugins.pinned_versions.packages': packages.join(','),
})

const pluginRuns = await api.getLatestPluginRuns({ site_id: siteId, packages, state: 'success' })
const pluginsOptionsA = pluginsOptions.map((pluginOption) => addPinnedVersion(pluginOption, pluginRuns))
span.end()

const pluginRuns = await api.getLatestPluginRuns({ site_id: siteId, packages, state: 'success' })
const pluginsOptionsA = pluginsOptions.map((pluginOption) => addPinnedVersion(pluginOption, pluginRuns))
return pluginsOptionsA
return pluginsOptionsA
})
}

const shouldFetchPinVersion = function ({ pinnedVersion, loadedFrom, origin }) {
Expand All @@ -27,12 +40,18 @@ const getPackageName = function ({ packageName }) {
}

const addPinnedVersion = function (pluginOptions, pluginRuns) {
const foundPluginRun = pluginRuns.find((pluginRun) => pluginRun.package === pluginOptions.packageName)
const { packageName } = pluginOptions
const foundPluginRun = pluginRuns.find((pluginRun) => pluginRun.package === packageName)
if (foundPluginRun === undefined) {
return pluginOptions
}

const pinnedVersion = getMajorVersion(foundPluginRun.version)
const span = trace.getActiveSpan()
span.addAttributes({
[`build.plugins.pinned_versions.${packageName}`]: pinnedVersion,
})

return pinnedVersion === undefined ? pluginOptions : { ...pluginOptions, pinnedVersion }
}

Expand All @@ -56,23 +75,26 @@ export const pinPlugins = async function ({
return
}

const pluginsOptionsA = pluginsOptions.filter((pluginOptions) => shouldPinVersion({ pluginOptions, failedPlugins }))
await Promise.all(
pluginsOptionsA.map((pluginOptions) =>
pinPlugin({
pluginOptions,
api,
childEnv,
mode,
netlifyConfig,
errorMonitor,
logs,
debug,
testOpts,
siteId,
}),
),
)
return await tracer.startActiveSpan('pin-plugins', async (span) => {
const pluginsOptionsA = pluginsOptions.filter((pluginOptions) => shouldPinVersion({ pluginOptions, failedPlugins }))
await Promise.all(
pluginsOptionsA.map((pluginOptions) =>
pinPlugin({
pluginOptions,
api,
childEnv,
mode,
netlifyConfig,
errorMonitor,
logs,
debug,
testOpts,
siteId,
}),
),
)
span.end()
})
}

// Only pin version if:
Expand Down Expand Up @@ -113,23 +135,31 @@ const pinPlugin = async function ({
testOpts,
siteId,
}) {
const pinnedVersion = getMajorVersion(version)
try {
await api.updatePlugin({
package: encodeURIComponent(packageName),
site_id: siteId,
body: { pinned_version: pinnedVersion },
})
// Bitballoon API randomly fails with 502.
// Builds should be successful when this API call fails, but we still want
// to report the error both in logs and in error monitoring.
} catch (error) {
if (shouldIgnoreError(error)) {
return
}
return await tracer.startActiveSpan(`pin-plugin-${packageName}`, async (span) => {
const pinnedVersion = getMajorVersion(version)
try {
span.addAttributes({
[`build.plugins.pin_version.${packageName}`]: pinnedVersion,
})
await api.updatePlugin({
package: encodeURIComponent(packageName),
site_id: siteId,
body: { pinned_version: pinnedVersion },
})
// Bitballoon API randomly fails with 502.
// Builds should be successful when this API call fails, but we still want
// to report the error both in logs and in error monitoring.
} catch (error) {
addBuildErrorToActiveSpan(error)
if (shouldIgnoreError(error)) {
return
}

await handleBuildError(error, { errorMonitor, netlifyConfig, childEnv, mode, logs, debug, testOpts })
}
await handleBuildError(error, { errorMonitor, netlifyConfig, childEnv, mode, logs, debug, testOpts })
} finally {
span.end()
}
})
}

// Status is 404 if the plugin is uninstalled while the build is ongoing.
Expand Down
Loading