chore: add parse-github-url dependency and update GitHub parsing logic

- Added `parse-github-url` package as a dependency in `package.json`.
- Updated `parseGitHubRepoFromUri` function to utilize `parse-github-url` for improved URI parsing.
- Introduced `getGitHubRepoMetadata` function to fetch repository metadata using Octokit.
- Updated validation data structure to include optional `repoId`.
- Enhanced tests to cover new functionality and error handling for invalid URIs.
This commit is contained in:
Huakun Shen 2025-03-24 01:43:59 -04:00
parent 140efa938e
commit 7eca29a600
No known key found for this signature in database
7 changed files with 78 additions and 14 deletions

View File

@ -13,6 +13,7 @@
},
"devDependencies": {
"@types/bun": "latest",
"@types/parse-github-url": "^1.0.3",
"verify-package-export": "^0.0.3"
},
"peerDependencies": {
@ -21,6 +22,7 @@
"dependencies": {
"@huakunshen/jsr-client": "^0.1.5",
"@kksh/api": "workspace:*",
"@octokit/rest": "^21.1.0"
"@octokit/rest": "^21.1.0",
"parse-github-url": "^1.0.3"
}
}

View File

@ -1,9 +1,15 @@
import { expect, test } from "bun:test"
import { parseGitHubRepoFromUri } from "../github"
import { getGitHubRepoNodeId, parseGitHubRepoFromUri } from "../github"
test("parse github repo from uri", () => {
expect(parseGitHubRepoFromUri("https://github.com/huakunshen/kunkun-ext-ossinsight")).toEqual({
owner: "huakunshen",
expect(parseGitHubRepoFromUri("https://github.com/kunkunsh/kunkun-ext-ossinsight")).toEqual({
owner: "kunkunsh",
repo: "kunkun-ext-ossinsight"
})
expect(() => parseGitHubRepoFromUri("invalid-uri")).toThrow("Invalid GitHub repository URI")
})
test("get github repo id", async () => {
const id = await getGitHubRepoNodeId("kunkunsh", "kunkun")
expect(id).toBeDefined()
})

View File

@ -2,6 +2,7 @@
* TODO: move this module to another folder
*/
import { Octokit } from "@octokit/rest"
import gh from "parse-github-url"
/**
* Check if a user is a public member of a GitHub organization
@ -33,16 +34,34 @@ export function authenticatedUserIsMemberOfGitHubOrg(
})
}
/**
* Parse a GitHub repository URI into owner and repo
* If not a valid GitHub repository URI, throw an error
* @param uri
* @returns owner and repo
*/
export function parseGitHubRepoFromUri(uri: string): {
owner: string
repo: string
} {
// check regex
const regex = /https?:\/\/github\.com\/([^\/]+)\/([^\/]+)/
const match = uri.match(regex)
if (!match) {
const ghUrl = gh(uri)
if (!ghUrl) {
throw new Error("Invalid GitHub repository URI")
}
const [, owner, repo] = match
return { owner, repo }
if (!ghUrl.owner || !ghUrl.name) {
throw new Error("Invalid GitHub repository URI")
}
return { owner: ghUrl.owner, repo: ghUrl.name }
}
/**
* Get GitHub repository metadata
* @param owner
* @param repo
* @param githubToken - Optional GitHub token to prevent rate limiting
* @returns repository metadata
*/
export function getGitHubRepoMetadata(owner: string, repo: string, githubToken?: string) {
const octokit = new Octokit({ auth: githubToken })
return octokit.rest.repos.get({ owner, repo }).then((res) => res.data)
}

View File

@ -7,7 +7,11 @@ import {
} from "@huakunshen/jsr-client/hey-api-client"
import { ExtPackageJson, License } from "@kksh/api/models"
import * as v from "valibot"
import { authenticatedUserIsMemberOfGitHubOrg, userIsPublicMemberOfGitHubOrg } from "../github"
import {
authenticatedUserIsMemberOfGitHubOrg,
getGitHubRepoMetadata,
userIsPublicMemberOfGitHubOrg
} from "../github"
import type { ExtensionPublishValidationData } from "../models"
import type { NpmPkgMetadata } from "../npm/models"
import { getInfoFromRekorLog } from "../sigstore"
@ -368,6 +372,16 @@ export async function validateJsrPackageAsKunkunExtension(payload: {
}
}
const rekorInfo = await getInfoFromRekorLog(rekorLogId)
/* -------------------------------------------------------------------------- */
/* Get GitHub Node ID */
/* -------------------------------------------------------------------------- */
const githubRepoMetadata = await getGitHubRepoMetadata(
githubRepo.owner,
githubRepo.name,
payload.githubToken
)
return {
data: {
pkgJson: parseResult.output,
@ -383,7 +397,8 @@ export async function validateJsrPackageAsKunkunExtension(payload: {
commit: rekorInfo.commit,
repo: githubRepo.name,
owner: githubRepo.owner,
workflowPath: rekorInfo.workflowPath
workflowPath: rekorInfo.workflowPath,
repoId: githubRepoMetadata.node_id
}
}
}

View File

@ -74,7 +74,8 @@ export const ExtensionPublishValidationData = v.object({
commit: v.string(),
repo: v.string(),
owner: v.string(),
workflowPath: v.string()
workflowPath: v.string(),
repoId: v.optional(v.string())
})
})
export type ExtensionPublishValidationData = v.InferOutput<typeof ExtensionPublishValidationData>

View File

@ -20,7 +20,8 @@ export const ExtPublishMetadata = v.object({
repo: v.string("GitHub repo of the extension"),
owner: v.string("GitHub owner of the extension"),
commit: v.string("Commit hash of the extension"),
workflowPath: v.string("Workflow path of the extension")
workflowPath: v.string("Workflow path of the extension"),
repoId: v.optional(v.string("GitHub repo node ID of the extension"))
})
)
})

20
pnpm-lock.yaml generated
View File

@ -746,6 +746,12 @@ importers:
'@types/bun':
specifier: latest
version: 1.2.5
'@types/parse-github-url':
specifier: ^1.0.3
version: 1.0.3
parse-github-url:
specifier: ^1.0.3
version: 1.0.3
verify-package-export:
specifier: ^0.0.3
version: 0.0.3(typescript@5.7.3)
@ -5558,6 +5564,9 @@ packages:
'@types/node@22.8.7':
resolution: {integrity: sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q==}
'@types/parse-github-url@1.0.3':
resolution: {integrity: sha512-7sTbCVmSVzK/iAsHGIxoqiyAnqix9opZm68lOvaU6DBx9EQ9kHMSp0y7Criu2OCsZ9wDllEyCRU+LU4hPRxXUA==}
'@types/parse-json@4.0.2':
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
@ -9786,6 +9795,11 @@ packages:
resolution: {integrity: sha512-wXoQGL1D+2COYWCD35/xbiKma1Z15xvZL8cI25wvxzled58V51SJM04Urt/uznS900iQor7QO04SgdfT/XlbuA==}
engines: {node: '>=8'}
parse-github-url@1.0.3:
resolution: {integrity: sha512-tfalY5/4SqGaV/GIGzWyHnFjlpTPTNpENR9Ea2lLldSJ8EWXMsvacWucqY3m3I4YPtas15IxTLQVQ5NSYXPrww==}
engines: {node: '>= 0.10'}
hasBin: true
parse-json@5.2.0:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
@ -17727,6 +17741,10 @@ snapshots:
dependencies:
undici-types: 6.19.8
'@types/parse-github-url@1.0.3':
dependencies:
'@types/node': 22.13.1
'@types/parse-json@4.0.2': {}
'@types/pbf@3.0.5': {}
@ -23194,6 +23212,8 @@ snapshots:
git-config-path: 2.0.0
ini: 1.3.8
parse-github-url@1.0.3: {}
parse-json@5.2.0:
dependencies:
'@babel/code-frame': 7.26.2