Huakun Shen e4d1441d73
Feature: support npm extension publish (#62)
* feat: npm package registry API

* refactor: move package registry files

* refactor: move jsr and npm api to a new package

* ci: add verify-package-export

* test: implement tests for npm package validation as kunkun extension

* chore: add missing dep for package-registry pkg

* feat: make provenance an optional input for npm validation function

* ci: add verify-package-export as dev dep to 2 packages that uses it

* feat: add rekor log API, and return commit from jsr & npm package in validation function

* feat: return github repo info from validation function of jsr and npm

* feat: extend ExtPublishMetadata to include optional GitHub repository details

* fix: eslint for ui package

* refactor: format desktop

* fix: eslint errors in desktop

* format: all code

* ci: add lint to CI

* feat: add more info to validation function returned from package-registry npm jsr

* pnpm lock

* feat: add 2 more variables to supabase extension metadata model

* format

* feat: add provenance card

* feat: add workflow path to ExtPublishMetadata and jsr/npm validation

* update provenance

* feat: make store extension and provenance more responsive

* chore: add globals to ui package

* fix: remove unnecessary any to fix eslint

* fix: svg sanitize

* chore: add @typescript-eslint/eslint-plugin to ui package to fix eslint

* fix: update eslint dep to fix error

* fix: try fixing eslint

* fix: update eslint configuration for improved compatibility

* chore: add globals package and update README for Discord invite

* fix: update eslint rules and upgrade typescript-eslint dependency

- Disabled additional eslint rules to resolve errors:
  - @typescript-eslint/no-unused-expressions
  - svelte/no-inner-declarations
- Upgraded typescript-eslint from version 8.19.1 to 8.20.0 for improved compatibility.

* update pnpm lock

---------

Co-authored-by: Huakun Shen <huaukun.shen@huakunshen.com>
2025-01-16 06:00:07 -05:00

68 lines
2.3 KiB
TypeScript

import * as v from "valibot"
import { SigstoreAttestation, type RawRekorLog, type RawRekorLogEntry } from "./models"
export function getRekorLogId(logIndex: string): Promise<RawRekorLog> {
return fetch(`https://rekor.sigstore.dev/api/v1/log/entries?logIndex=${logIndex}`).then((res) =>
res.json()
)
}
/**
* For our use case (JSR), we expect only one entry in the rekor log, so we can just return the first one
* If there are multiple entries, we throw an error
* @param rekorLog
* @returns
*/
export function parseTheOnlyRecord(rekorLog: RawRekorLog): RawRekorLogEntry {
const entryUUIDs = Object.keys(rekorLog)
if (entryUUIDs.length !== 1) {
throw new Error("Expected exactly one entry in the rekor log")
}
return rekorLog[entryUUIDs[0]]
}
/**
* Attestation data is base64 encoded, so we need to decode it and parse it with valibot
* @param rekorLog
* @returns
*/
export function parseAttestation(rekorLog: RawRekorLogEntry): SigstoreAttestation {
const attestationData = rekorLog.attestation.data
const decoded = atob(attestationData)
const decodedJson = JSON.parse(decoded)
const parsed = v.safeParse(SigstoreAttestation, decodedJson)
if (!parsed.success) {
console.error(v.flatten(parsed.issues))
throw new Error("Failed to parse rekor log attestation")
}
return parsed.output
}
/**
* We expect only one entry in the rekor log, and there should be only one commit in the attestation
* @param logIndex
* @returns
*/
export async function getInfoFromRekorLog(logIndex: string): Promise<{
commit: string
githubActionInvocationId: string
workflowPath: string
workflowRepository: string
}> {
const rawLog = await getRekorLogId(logIndex)
const record = parseTheOnlyRecord(rawLog)
const attestation = parseAttestation(record)
if (attestation.predicate.buildDefinition.resolvedDependencies.length !== 1) {
throw new Error(
`Expected exactly one commit in the attestation, got: ${attestation.predicate.buildDefinition.resolvedDependencies.length}`
)
}
return {
commit: attestation.predicate.buildDefinition.resolvedDependencies[0].digest.gitCommit,
githubActionInvocationId: attestation.predicate.runDetails.metadata.invocationId,
workflowPath: attestation.predicate.buildDefinition.externalParameters.workflow.path,
workflowRepository: attestation.predicate.buildDefinition.externalParameters.workflow.repository
}
}