Skip to content
Open
Show file tree
Hide file tree
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
9 changes: 9 additions & 0 deletions scripts/deploy/publish-npm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ runMain(() => {
},
})

printLog('Building')
// Usually we don't need to build packages before publishing them, because yarn will call each
// `prepack` script to build packages during "yarn npm publish".
//
// But when things go wrong and some packages fail to be published, and we want to retry the job,
// yarn will skip already published packages (--tolerate-republish) so if any unpublished package
// depends on an already published one for their build, the build will fail.
command`yarn build`.withEnvironment({ BUILD_MODE: 'release' }).run()

printLog(dryRun ? 'Publishing (dry run)' : 'Publishing')
command`yarn workspaces foreach --verbose --all --topological --no-private npm publish --tolerate-republish --access public ${dryRun ? ['--dry-run'] : []}`
.withEnvironment({
Expand Down
67 changes: 65 additions & 2 deletions scripts/release/renew-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import fs from 'node:fs'
import path from 'node:path'
import os from 'node:os'
import { Writable } from 'node:stream'
import { printError, printLog, runMain } from '../lib/executionUtils.ts'
import { printError, printLog, printWarning, runMain } from '../lib/executionUtils.ts'
import { findPackageJsonFiles } from '../lib/filesUtils.ts'
import { command } from '../lib/command.ts'

Expand All @@ -21,6 +21,27 @@ runMain(async () => {
process.exit(1)
}

const publishablePackages = findPublisheablePackages()
const missingPackages = await findMissingPackages(publishablePackages)

if (missingPackages.length > 0) {
printWarning("The following packages don't exist yet on the npm registry:")
for (const packageName of missingPackages) {
printWarning(` - ${packageName}`)
}
printWarning(
'Renewing the granular token with non-existing packages will fail. Placeholder packages must be published first.'
)

const answer = await prompt({ question: 'Publish placeholder packages now? [y/N] ' })
if (answer.toLowerCase() !== 'y') {
printError('Aborting. Either publish the missing packages or mark them as private in their package.json.')
process.exit(1)
}

publishPlaceholderPackages(missingPackages)
}

const password = await prompt({ question: 'npmjs.com password: ', showOutput: false })
const otp = await prompt({ question: 'npmjs.com OTP: ' })

Expand Down Expand Up @@ -53,7 +74,7 @@ runMain(async () => {
token_description: 'Token used to publish Browser SDK packages (@datadog/browser-*) from the CI.',
expires: 90,
bypass_2fa: true,
packages: findPublisheablePackages(),
packages: publishablePackages,
packages_and_scopes_permission: 'read-write',
},
otp,
Expand Down Expand Up @@ -123,6 +144,48 @@ async function callNpmApi<T>({
return responseBody as T
}

async function findMissingPackages(packageNames: string[]): Promise<string[]> {
const results = await Promise.all(
packageNames.map(async (packageName) => {
const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}`)
return response.status === 404 ? [packageName] : []
})
)
return results.flat()
}

function publishPlaceholderPackages(packageNames: string[]): void {
for (const packageName of packageNames) {
publishPlaceholderPackage(packageName)
}
}

function publishPlaceholderPackage(packageName: string): void {
printLog(`Publishing placeholder for ${packageName}...`)
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'browser-sdk-placeholder-'))
try {
fs.writeFileSync(
path.join(tmpDir, 'package.json'),
JSON.stringify(
{
name: packageName,
version: '0.0.0',
publishConfig: { access: 'public' },
},
null,
2
)
)

fs.writeFileSync(path.join(tmpDir, 'README.md'), `Placeholder for package ${packageName}\n`)

command`npm publish`.withCurrentWorkingDirectory(tmpDir).withLogs().run()
printLog(`Published placeholder for ${packageName}`)
} finally {
fs.rmSync(tmpDir, { recursive: true, force: true })
}
}

function findPublisheablePackages() {
const files = findPackageJsonFiles()
return files
Expand Down
Loading