From 3115fea0eea623423469cb393b4e467b721accf2 Mon Sep 17 00:00:00 2001 From: Mohammad Bagher Abiyat Date: Tue, 26 Mar 2024 09:57:20 +0330 Subject: [PATCH] no sha route fix --- e2e/publish.test.mts | 28 +++------- .../[repo]/[ref]/[package].get.ts | 25 +++++---- .../backend/server/routes/publish.post.ts | 51 +++++++++++++++---- packages/backend/server/types.ts | 5 ++ packages/backend/server/utils/bucket.ts | 22 +++++--- packages/backend/tsconfig.json | 1 + packages/cli/index.ts | 21 +++++++- packages/cli/tsconfig.json | 1 + 8 files changed, 101 insertions(+), 53 deletions(-) diff --git a/e2e/publish.test.mts b/e2e/publish.test.mts index 920f3e40..c34a8af4 100644 --- a/e2e/publish.test.mts +++ b/e2e/publish.test.mts @@ -65,7 +65,8 @@ await wp({ port: PORT }); GITHUB_REPOSITORY: workflowJobQueuedFixture.payload.repository.full_name, GITHUB_RUN_ID: workflowJobQueuedFixture.payload.workflow_job.run_id, GITHUB_RUN_ATTEMPT: workflowJobQueuedFixture.payload.workflow_job.run_attempt, - GITHUB_ACTOR_ID:workflowJobQueuedFixture.payload.sender.id , + GITHUB_ACTOR_ID:workflowJobQueuedFixture.payload.sender.id, + GITHUB_SHA:workflowJobQueuedFixture.payload.workflow_job.head_sha, }).map(([k, v]) => `${k}=${v}`).join(' ') await ezSpawn.async(`${env} pnpm --filter=playground run publish`, [], { stdio: 'inherit', @@ -82,17 +83,15 @@ await wp({ port: PORT }); }) const playgroundShaBlob =await playgroundShaData.blob() - console.log(playgroundShaBlob) assert.ok(!!playgroundShaBlob.size, "playground size should not be zero") assert.equal(playgroundShaData.status, 200, "playground response should be 200") - // const playgroundWithoutShaUrl = new URL('/stackblitz-labs/stackblitz-ci/main/playground', serverUrl) - // const playgroundWithoutShaData = await fetch(playgroundWithoutShaUrl, { - // method: 'GET', - // }) - // const playgroundWithoutShaBlob = await playgroundWithoutShaData.blob() - // console.log('sha url and non-sha url', playgroundShaBlob.arrayBuffer, playgroundWithoutShaBlob.arrayBuffer) - // assert.deepEqual(await playgroundShaBlob.arrayBuffer(), await playgroundWithoutShaBlob.arrayBuffer(), "sha urls and non-sha urls should not give different results") + const playgroundWithoutShaUrl = new URL('/stackblitz-labs/stackblitz-ci/main/playground', serverUrl) + const playgroundWithoutShaData = await fetch(playgroundWithoutShaUrl, { + method: 'GET', + }) + const playgroundWithoutShaBlob = await playgroundWithoutShaData.blob() + assert.deepEqual(await playgroundShaBlob.arrayBuffer(), await playgroundWithoutShaBlob.arrayBuffer(), "sha urls and non-sha urls should not give different results") } { playgroundShaUrl.searchParams.set('id', Date.now().toString()) @@ -104,14 +103,3 @@ await wp({ port: PORT }); } } - - - - - -// // c.abort(); -// // await p - -// process.on('beforeExit', () => { -// c.abort() -// }) diff --git a/packages/backend/server/routes/[orgOrAuthor]/[repo]/[ref]/[package].get.ts b/packages/backend/server/routes/[orgOrAuthor]/[repo]/[ref]/[package].get.ts index c5caa606..56ac7c57 100644 --- a/packages/backend/server/routes/[orgOrAuthor]/[repo]/[ref]/[package].get.ts +++ b/packages/backend/server/routes/[orgOrAuthor]/[repo]/[ref]/[package].get.ts @@ -7,21 +7,20 @@ type Params = Omit & { export default eventHandler(async (event) => { const params = getRouterParams(event) as Params; - const packagesBucket = usePackagesBucket(); const { package: packageName, ...hashPrefixMetadata } = params; const metadataHash = sha256(objectHash(hashPrefixMetadata)); - const keys = await packagesBucket.getKeys(metadataHash) - console.log(keys) - // const packageKey = `${metadataHash}:${sha}:${packageName.split('.tgz')[0]}`; - // if (!(await packagesBucket.hasItem(packageKey))) { - // throw createError({ - // status: 404, - // }); - // } - // const buffer = await packagesBucket.getItemRaw(packageKey); - // setResponseHeader(event, "content-type", "application/tar+gzip"); - // // add caching - // return new Response(buffer); + const cursorBucket = useCursorBucket(); + if (!(await cursorBucket.hasItem(metadataHash))) { + throw createError({ + status: 404, + }); + } + const currentCursor = (await cursorBucket.getItem(metadataHash))!; + + sendRedirect( + event, + `/${params.orgOrAuthor}/${params.repo}/${params.ref}/${currentCursor.sha}/${params.package}` + ); }); diff --git a/packages/backend/server/routes/publish.post.ts b/packages/backend/server/routes/publish.post.ts index ba4d8682..45880e01 100644 --- a/packages/backend/server/routes/publish.post.ts +++ b/packages/backend/server/routes/publish.post.ts @@ -1,5 +1,5 @@ import { objectHash, sha256 } from "ohash"; -import {generateCommitPublishMessage} from '../utils/markdown' +import { generateCommitPublishMessage } from "../utils/markdown"; export default eventHandler(async (event) => { const contentLength = Number(getHeader(event, "content-length")); @@ -8,29 +8,58 @@ export default eventHandler(async (event) => { // Payload too large return new Response("Max size limit is 5mb", { status: 413 }); } - const key = getRequestHeader(event, "sb-key"); + const { + "sb-package-name": packageName, + "sb-commit-timestamp": commitTimestampStr, + "sb-key": key + } = getHeaders(event); + if (!key || !packageName || !commitTimestampStr) { + throw createError({ + statusCode: 400, + message: "sb-package-name, sb-commit-timestamp, sb-key headers are required" + }) + } + const workflowsBucket = useWorkflowsBucket(); const packagesBucket = usePackagesBucket(); - + const cursorBucket = useCursorBucket(); if (!(await workflowsBucket.hasItem(key))) { - return new Response("", { status: 401 }); + throw createError({ + statusCode: 401, + message: "Try publishing from a github workflow" + }) } const binary = await readRawBody(event, false); - const { "sb-package-name": packageName, "sb-package-version": _ } = - getHeaders(event); + + if (!packageName || !commitTimestampStr) { + throw createError({ + statusCode: 400, + message: "sb-key header is missing" + }) + + } + const commitTimestamp = Number(commitTimestampStr); - const workflowData = await workflowsBucket.getItem(key); + const workflowData = (await workflowsBucket.getItem(key))!; const { sha, ...hashPrefixMetadata } = workflowData; const metadataHash = sha256(objectHash(hashPrefixMetadata)); const packageKey = `${metadataHash}:${sha}:${packageName}`; + const currentCursor = await cursorBucket.getItem(metadataHash); + await packagesBucket.setItemRaw(packageKey, binary); + if (!currentCursor || currentCursor.timestamp < commitTimestamp) { + await cursorBucket.setItem(metadataHash, { + sha, + timestamp: commitTimestamp, + }); + } await workflowsBucket.removeItem(key); const app = useOctokitApp(event); - const origin = getRequestURL(event).origin + const origin = getRequestURL(event).origin; app.octokit.request("POST /repos/{owner}/{repo}/check-runs", { name: "Stackblitz CR (Publish)", @@ -38,9 +67,9 @@ export default eventHandler(async (event) => { repo: workflowData.repo, head_sha: sha, output: { - title: 'Stackblitz CR', - summary: 'Published successfully.', - text: generateCommitPublishMessage(origin, packageName, workflowData) + title: "Stackblitz CR", + summary: "Published successfully.", + text: generateCommitPublishMessage(origin, packageName, workflowData), }, conclusion: "success", }); diff --git a/packages/backend/server/types.ts b/packages/backend/server/types.ts index e9b1ba66..69d8e8f9 100644 --- a/packages/backend/server/types.ts +++ b/packages/backend/server/types.ts @@ -6,3 +6,8 @@ export type WorkflowData = { sha: string, ref: string // branch } + +export type Cursor = { + timestamp: number + sha: string +} diff --git a/packages/backend/server/utils/bucket.ts b/packages/backend/server/utils/bucket.ts index c87307b8..fa5ae6f8 100644 --- a/packages/backend/server/utils/bucket.ts +++ b/packages/backend/server/utils/bucket.ts @@ -1,6 +1,6 @@ -import { R2Bucket } from "@cloudflare/workers-types"; -import {prefixStorage} from 'unstorage' -import { WorkflowData } from "../types"; +import type { R2Bucket } from "@cloudflare/workers-types"; +import { prefixStorage } from "unstorage"; +import { WorkflowData, Cursor } from "../types"; type Binary = Parameters[1]; @@ -9,11 +9,19 @@ export function useBucket() { } export function useWorkflowsBucket() { - const storage = useBucket() - return prefixStorage(storage, 'workflows') + const storage = useBucket(); + return prefixStorage( + storage, + "workflows" + ); } export function usePackagesBucket() { - const storage = useBucket() - return prefixStorage(storage, 'packages') + const storage = useBucket(); + return prefixStorage(storage, "packages"); +} + +export function useCursorBucket() { + const storage = useBucket(); + return prefixStorage(storage, "cursor"); } diff --git a/packages/backend/tsconfig.json b/packages/backend/tsconfig.json index ae047b7a..fcda35de 100644 --- a/packages/backend/tsconfig.json +++ b/packages/backend/tsconfig.json @@ -1,6 +1,7 @@ // https://nitro.unjs.io/guide/typescript { "compilerOptions": { + "strict": true, "skipLibCheck": true }, "extends": "./.nitro/types/tsconfig.json" diff --git a/packages/cli/index.ts b/packages/cli/index.ts index d81e6406..08c2394f 100644 --- a/packages/cli/index.ts +++ b/packages/cli/index.ts @@ -1,5 +1,5 @@ import { defineCommand, runMain } from "citty"; -import assert from 'node:assert' +import assert from "node:assert"; import path from "path"; import ezSpawn from "@jsdevtools/ez-spawn"; // import { createRequire } from "module"; @@ -61,6 +61,18 @@ const main = defineCommand({ meta: {}, run: async () => { await ezSpawn.async("npm pack", { stdio: "inherit" }); + const p = await ezSpawn.async( + `git show -s --format=\%ct ${GITHUB_SHA}`, + { stdio: "overlapped" } + ); + console.log(p.stdout, p.stderr) + const commitTimestamp = Number(p.stdout); + console.log("commit timestamp", commitTimestamp); + assert.ok( + !Number.isNaN(commitTimestamp), + "failed at getting commit timestamp" + ); + const file = await fs.readFile(`${name}-${version}.tgz`); const data = await fetch(publishUrl, { @@ -69,10 +81,15 @@ const main = defineCommand({ "sb-key": key, "sb-package-name": name, "sb-package-version": version, + "sb-commit-timestamp": commitTimestamp, }, body: file, }); - assert.equal(data.status, 200, `publishing failed: ${await data.text()}`) + assert.equal( + data.status, + 200, + `publishing failed: ${await data.text()}` + ); const url = new URL( `/${GITHUB_REPOSITORY}/${GITHUB_REF_NAME}/${GITHUB_SHA}/${name}`, diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index 56c85e4c..53269bee 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -1,6 +1,7 @@ // https://nitro.unjs.io/guide/typescript { "compilerOptions": { + "strict": true, "skipLibCheck": true }, "extends": "../backend/.nitro/types/tsconfig.json"