UNPKG

@supernovaio/cli

Version:

Supernova.io Command Line Interface

188 lines (186 loc) 9.48 kB
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="234261e7-3ea7-524f-8429-b89d3e65efb0")}catch(e){}}(); import AdmZip from "adm-zip"; import axios, { isAxiosError } from "axios"; import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; import { createApiClient } from "../../http-client.js"; import { watchAnalyzeStatus } from "../../analyze-status.js"; import { deriveRepoPathFromSnapshotRoot, detectSnapshotScannerType, hasUploadableSnapshotContent, listSnapshotRootsUnderPath, } from "../helpers.js"; import { emphasis, muted, success, warning } from "../ui.js"; export function createScanService(input) { return { persistAnalyzePackages(componentPackages) { const config = input.configService.get(); input.configService.update({ analyze: { ...config?.analyze, packages: componentPackages, }, }); }, async runScanAgainstSourcePath(runInput) { const previousCwd = process.cwd(); const before = new Set(listSnapshotRootsUnderPath(runInput.scanSourcePath)); try { process.chdir(runInput.scanSourcePath); const args = ["--dryRun", "--suppressDryRunNotice", "--designSystemId", runInput.designSystemId]; for (const componentPackage of runInput.componentPackages) { args.push("--package", componentPackage); } await input.runAnalyzeCommand(runInput.scanSourcePath, args); } finally { process.chdir(previousCwd); } const after = listSnapshotRootsUnderPath(runInput.scanSourcePath); const createdSnapshotRoots = after.filter(snapshotRoot => !before.has(snapshotRoot)); const uploadableSnapshotRoots = createdSnapshotRoots.filter(snapshotRoot => hasUploadableSnapshotContent(snapshotRoot)); const emptySnapshotRoots = createdSnapshotRoots.filter(snapshotRoot => !hasUploadableSnapshotContent(snapshotRoot)); for (const snapshotRoot of emptySnapshotRoots) { fs.rmSync(snapshotRoot, { force: true, recursive: true }); } if (uploadableSnapshotRoots.length === 0) { input.error("No analyzable scan results were produced. Run setup from the repository that contains your design system packages and component usage."); } return uploadableSnapshotRoots; }, async uploadAnalysisSnapshots(uploadInput) { const { designSystemId, scanOutputPaths } = uploadInput; if (scanOutputPaths.length === 0) return { snapshotIds: [] }; input.log(""); input.log(emphasis("Uploading analysis snapshots to Supernova...")); const apiClient = await createApiClient(input.env); const analyzeConfig = input.configService.get()?.analyze; const repoId = analyzeConfig?.repoId ?? crypto.randomUUID(); const uploadedSnapshotIds = []; const failedUploads = []; for (const [index, snapshotRoot] of scanOutputPaths.entries()) { const repoPath = deriveRepoPathFromSnapshotRoot(snapshotRoot); const localSnapshotId = path.basename(snapshotRoot); const archive = createArchiveFromSnapshotRoot(snapshotRoot); const uploadId = `A${String(index + 1).padStart(2, "0")}`; const scannerType = detectSnapshotScannerType(snapshotRoot); let initResponse; try { const response = await apiClient.post("/code-snapshots/upload", { archiveChecksum: archive.checksum, archiveName: archive.archiveName, archiveSize: archive.sizeBytes, designSystemId, repoId, repoName: analyzeConfig?.repoName ?? readRepoNameFromRepo(repoPath), repoPackageName: readPackageNameFromRepo(repoPath), scannerType, }); initResponse = response.data.result; } catch (error) { logHttpError(input.log, "snapshot upload init", error); failedUploads.push({ localSnapshotId, scannerType, stage: "init", uploadId }); input.log(warning(`Skipping ${uploadId} ${localSnapshotId} (${scannerType}) after init failure.`)); continue; } try { await axios.put(initResponse.uploadUrl, archive.archive, { headers: { "Content-Length": archive.archive.length, "Content-Type": "application/zip", }, }); } catch (error) { logHttpError(input.log, "snapshot upload PUT", error); failedUploads.push({ localSnapshotId, scannerType, stage: "put", uploadId }); input.log(warning(`Skipping ${uploadId} ${localSnapshotId} (${scannerType}) after archive upload failure.`)); continue; } try { await apiClient.post(`/code-snapshots/${initResponse.snapshotId}/finalize`, {}); } catch (error) { logHttpError(input.log, "snapshot finalize", error); failedUploads.push({ localSnapshotId, scannerType, stage: "finalize", uploadId }); input.log(warning(`Skipping ${uploadId} ${localSnapshotId} (${scannerType}) after finalize failure.`)); continue; } uploadedSnapshotIds.push(initResponse.snapshotId); input.log(`${success("Uploaded")} ${localSnapshotId} ${muted(`(${scannerType})`)}`); } if (failedUploads.length > 0) { input.log(warning(`Failed uploads: ${failedUploads.length}`)); for (const failure of failedUploads) { input.log(muted(`- ${failure.uploadId} ${failure.localSnapshotId} (${failure.scannerType}) stage=${failure.stage}`)); } } if (uploadedSnapshotIds.length === 0) { input.error("No analysis snapshots were uploaded successfully."); } try { const response = await apiClient.post("/code-snapshots/process-run", { designSystemId }); const { result } = response.data; input.log(`${success("Started snapshot processing")} ${muted(`run: ${result.processingRunId} snapshots: ${result.snapshotIds.length}`)}`); return result; } catch (error) { logHttpError(input.log, "process-run", error); input.error("Analysis snapshots uploaded, but starting processing run failed."); } }, async waitForAnalysisProcessing(waitInput) { input.log("Checking design system analysis processing status. If analysis is still running, setup will continue automatically when it finishes."); await watchAnalyzeStatus({ apiClient: await createApiClient(input.env), designSystemId: waitInput.designSystemId, error: input.error, log: input.log, processingRunId: waitInput.processingRunId, }); }, }; } function createArchiveFromSnapshotRoot(snapshotRoot) { const zip = new AdmZip(); zip.addLocalFolder(snapshotRoot); const archive = zip.toBuffer(); const checksum = crypto.createHash("sha256").update(archive).digest("hex"); return { archive, archiveName: `${path.basename(snapshotRoot)}.zip`, checksum, sizeBytes: archive.byteLength, }; } function readPackageNameFromRepo(repoPath) { const packageJsonPath = path.join(repoPath, "package.json"); if (!fs.existsSync(packageJsonPath)) { return path.basename(repoPath); } try { const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); return packageJson.name ?? path.basename(repoPath); } catch { return path.basename(repoPath); } } function readRepoNameFromRepo(repoPath) { return path.basename(repoPath); } function logHttpError(log, step, error) { if (isAxiosError(error)) { const status = error.response?.status; const responseData = error.response?.data; const message = typeof responseData === "string" ? responseData : typeof responseData?.message === "string" ? responseData.message : error.message; log(`${warning(step)}${status ? ` (${status})` : ""}: ${message}`); return; } log(`${warning(step)}: ${error instanceof Error ? error.message : String(error)}`); } //# sourceMappingURL=scan-service.js.map //# debugId=234261e7-3ea7-524f-8429-b89d3e65efb0