diff --git a/workspaces/libnpmpublish/lib/publish.js b/workspaces/libnpmpublish/lib/publish.js index 89ca01662cdb5..d36b7fe6c374f 100644 --- a/workspaces/libnpmpublish/lib/publish.js +++ b/workspaces/libnpmpublish/lib/publish.js @@ -42,15 +42,25 @@ Remove the 'private' field from the package.json to publish it.`), ) } - const metadata = await buildMetadata(reg, pubManifest, tarballData, spec, opts) + const { metadata, transparencyLogUrl } = await buildMetadata( + reg, + pubManifest, + tarballData, + spec, + opts + ) try { - return await npmFetch(spec.escapedName, { + const res = await npmFetch(spec.escapedName, { ...opts, method: 'PUT', body: metadata, ignoreBody: true, }) + if (transparencyLogUrl) { + res.transparencyLogUrl = transparencyLogUrl + } + return res } catch (err) { if (err.code !== 'E409') { throw err @@ -64,12 +74,17 @@ Remove the 'private' field from the package.json to publish it.`), query: { write: true }, }) const newMetadata = patchMetadata(current, metadata) - return npmFetch(spec.escapedName, { + const res = await npmFetch(spec.escapedName, { ...opts, method: 'PUT', body: newMetadata, ignoreBody: true, }) + /* istanbul ignore next */ + if (transparencyLogUrl) { + res.transparencyLogUrl = transparencyLogUrl + } + return res } } @@ -138,6 +153,7 @@ const buildMetadata = async (registry, manifest, tarballData, spec, opts) => { } // Handle case where --provenance flag was set to true + let transparencyLogUrl if (provenance === true) { const subject = { name: npa.toPurl(spec), @@ -178,8 +194,11 @@ const buildMetadata = async (registry, manifest, tarballData, spec, opts) => { const tlogEntry = provenanceBundle?.verificationMaterial?.tlogEntries[0] /* istanbul ignore else */ if (tlogEntry) { - const logUrl = `${TLOG_BASE_URL}?logIndex=${tlogEntry.logIndex}` - log.notice('publish', `Provenance statement published to transparency log: ${logUrl}`) + transparencyLogUrl = `${TLOG_BASE_URL}?logIndex=${tlogEntry.logIndex}` + log.notice( + 'publish', + `Provenance statement published to transparency log: ${transparencyLogUrl}` + ) } const serializedBundle = JSON.stringify(provenanceBundle) @@ -190,7 +209,10 @@ const buildMetadata = async (registry, manifest, tarballData, spec, opts) => { } } - return root + return { + metadata: root, + transparencyLogUrl, + } } const patchMetadata = (current, newData) => { diff --git a/workspaces/libnpmpublish/test/publish.js b/workspaces/libnpmpublish/test/publish.js index 065765807b889..fa68f4d98052d 100644 --- a/workspaces/libnpmpublish/test/publish.js +++ b/workspaces/libnpmpublish/test/publish.js @@ -511,6 +511,7 @@ t.test('publish includes access', async t => { }) t.ok(ret, 'publish succeeded') + t.notOk(ret.transparencyLogUrl, 'no transparencyLogUrl for non-provenance publish') }) t.test('refuse if package is unscoped plus `restricted` access', async t => { @@ -804,6 +805,11 @@ t.test('publish existing package with provenance in gha', async t => { rekorURL: rekorURL, }) t.ok(ret, 'publish succeeded') + t.equal( + ret.transparencyLogUrl, + 'https://search.sigstore.dev/?logIndex=2513258', + 'has appropriate transparencyLogUrl property' + ) t.match(log, [ ['notice', 'publish', 'Signed provenance statement with source and build information from GitHub Actions'],