octocms 0.4.9 → 0.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/AdminApp.d.ts +2 -2
- package/dist/admin/AdminApp.d.ts.map +1 -1
- package/dist/admin/AdminApp.js +1 -2
- package/dist/admin/AdminApp.js.map +1 -1
- package/dist/admin/actions/git.d.ts +17 -0
- package/dist/admin/actions/git.d.ts.map +1 -1
- package/dist/admin/actions/git.js +11 -0
- package/dist/admin/actions/git.js.map +1 -1
- package/dist/admin/github.d.ts +16 -0
- package/dist/admin/github.d.ts.map +1 -1
- package/dist/admin/github.js +27 -0
- package/dist/admin/github.js.map +1 -1
- package/dist/admin/query/hooks/useRecentCMSPullRequests.d.ts +7 -0
- package/dist/admin/query/hooks/useRecentCMSPullRequests.d.ts.map +1 -0
- package/dist/admin/query/hooks/useRecentCMSPullRequests.js +16 -0
- package/dist/admin/query/hooks/useRecentCMSPullRequests.js.map +1 -0
- package/dist/admin/query/keys.d.ts +1 -0
- package/dist/admin/query/keys.d.ts.map +1 -1
- package/dist/admin/query/keys.js +2 -1
- package/dist/admin/query/keys.js.map +1 -1
- package/dist/agent/index.cjs +28 -1
- package/dist/agent/index.cjs.map +1 -1
- package/dist/chunk-5MC45AYI.js +7 -0
- package/dist/chunk-5MC45AYI.js.map +1 -0
- package/dist/cli/index.js +3 -3
- package/dist/components/Dashboard/DashboardContent.d.ts.map +1 -1
- package/dist/components/Dashboard/DashboardContent.js +30 -9
- package/dist/components/Dashboard/DashboardContent.js.map +1 -1
- package/dist/components/Dashboard/RecentPullRequests.d.ts +7 -0
- package/dist/components/Dashboard/RecentPullRequests.d.ts.map +1 -0
- package/dist/components/Dashboard/RecentPullRequests.js +96 -0
- package/dist/components/Dashboard/RecentPullRequests.js.map +1 -0
- package/dist/components/Dashboard/skeletons/{DashboardPageChromeSkeleton.d.ts → ContentPageChromeSkeleton.d.ts} +3 -3
- package/dist/components/Dashboard/skeletons/ContentPageChromeSkeleton.d.ts.map +1 -0
- package/dist/components/Dashboard/skeletons/{DashboardPageChromeSkeleton.js → ContentPageChromeSkeleton.js} +3 -3
- package/dist/components/Dashboard/skeletons/ContentPageChromeSkeleton.js.map +1 -0
- package/dist/components/Layout/TopHeader.d.ts.map +1 -1
- package/dist/components/Layout/TopHeader.js +2 -3
- package/dist/components/Layout/TopHeader.js.map +1 -1
- package/dist/components/skeletons/MainSlotSkeleton.js +3 -3
- package/dist/components/skeletons/MainSlotSkeleton.js.map +1 -1
- package/dist/{embeddingsGen-NZQ2RXXP.js → embeddingsGen-7MXSZQ43.js} +2 -2
- package/dist/{github-RGFCTMMT.js → github-7HIP6RW3.js} +28 -1
- package/dist/github-7HIP6RW3.js.map +1 -0
- package/dist/globals.css +25 -3
- package/dist/index.cjs +47 -19
- package/dist/index.cjs.map +1 -1
- package/dist/{init-W645ZB34.js → init-LMBF6CMC.js} +2 -2
- package/dist/query.cjs +47 -19
- package/dist/query.cjs.map +1 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +47 -19
- package/dist/query.js.map +1 -1
- package/globals.css +25 -3
- package/package.json +1 -1
- package/dist/admin/pages/DashboardPage.d.ts +0 -2
- package/dist/admin/pages/DashboardPage.d.ts.map +0 -1
- package/dist/admin/pages/DashboardPage.js +0 -18
- package/dist/admin/pages/DashboardPage.js.map +0 -1
- package/dist/chunk-GL7FGEA4.js +0 -7
- package/dist/chunk-GL7FGEA4.js.map +0 -1
- package/dist/components/Dashboard/skeletons/DashboardPageChromeSkeleton.d.ts.map +0 -1
- package/dist/components/Dashboard/skeletons/DashboardPageChromeSkeleton.js.map +0 -1
- package/dist/github-RGFCTMMT.js.map +0 -1
- /package/dist/{embeddingsGen-NZQ2RXXP.js.map → embeddingsGen-7MXSZQ43.js.map} +0 -0
- /package/dist/{init-W645ZB34.js.map → init-LMBF6CMC.js.map} +0 -0
package/dist/admin/AdminApp.d.ts
CHANGED
|
@@ -8,8 +8,8 @@ type AdminAppProps = {
|
|
|
8
8
|
* file in the user app that re-exports this component as the default.
|
|
9
9
|
*
|
|
10
10
|
* Route segments map to admin pages:
|
|
11
|
-
* /cms →
|
|
12
|
-
* /cms/content → ContentPage (
|
|
11
|
+
* /cms → ContentPage (admin home — content list)
|
|
12
|
+
* /cms/content → ContentPage (alias for /cms — kept for legacy links)
|
|
13
13
|
* /cms/content/<type> → CollectionPage
|
|
14
14
|
* /cms/content/<type>/<id> → EntryPage
|
|
15
15
|
* /cms/chat → ChatPage (gated on `isAgentEnabled(agentConfig)`)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminApp.d.ts","sourceRoot":"","sources":["../../admin/AdminApp.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AdminApp.d.ts","sourceRoot":"","sources":["../../admin/AdminApp.tsx"],"names":[],"mappings":"AAWA,KAAK,aAAa,GAAG;IACnB,MAAM,EAAE,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CACtC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,QAAQ,CAAC,EAAE,MAAM,EAAE,EAAE,aAAa,oDAyCvD"}
|
package/dist/admin/AdminApp.js
CHANGED
|
@@ -6,7 +6,6 @@ import { CollectionPage } from "./pages/CollectionPage";
|
|
|
6
6
|
import { ContentModelPage } from "./pages/ContentModelPage";
|
|
7
7
|
import { ContentPage } from "./pages/ContentPage";
|
|
8
8
|
import { ContentTypePage } from "./pages/ContentTypePage";
|
|
9
|
-
import { DashboardPage } from "./pages/DashboardPage";
|
|
10
9
|
import { EntryPage } from "./pages/EntryPage";
|
|
11
10
|
import { MediaAssetPage } from "./pages/MediaAssetPage";
|
|
12
11
|
import { MediaPage } from "./pages/MediaPage";
|
|
@@ -14,7 +13,7 @@ async function AdminApp({ params }) {
|
|
|
14
13
|
const { path } = await params;
|
|
15
14
|
const segments = path != null ? path : [];
|
|
16
15
|
if (segments.length === 0) {
|
|
17
|
-
return /* @__PURE__ */ jsx(
|
|
16
|
+
return /* @__PURE__ */ jsx(ContentPage, {});
|
|
18
17
|
}
|
|
19
18
|
if (segments[0] === "chat") {
|
|
20
19
|
return /* @__PURE__ */ jsx(ChatPage, {});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../admin/AdminApp.tsx"],"sourcesContent":["import { DashboardContentSkeleton } from '../components/Dashboard/DashboardContent.skeleton';\n\nimport { ChatPage } from './pages/ChatPage';\nimport { CollectionPage } from './pages/CollectionPage';\nimport { ContentModelPage } from './pages/ContentModelPage';\nimport { ContentPage } from './pages/ContentPage';\nimport { ContentTypePage } from './pages/ContentTypePage';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../admin/AdminApp.tsx"],"sourcesContent":["import { DashboardContentSkeleton } from '../components/Dashboard/DashboardContent.skeleton';\n\nimport { ChatPage } from './pages/ChatPage';\nimport { CollectionPage } from './pages/CollectionPage';\nimport { ContentModelPage } from './pages/ContentModelPage';\nimport { ContentPage } from './pages/ContentPage';\nimport { ContentTypePage } from './pages/ContentTypePage';\nimport { EntryPage } from './pages/EntryPage';\nimport { MediaAssetPage } from './pages/MediaAssetPage';\nimport { MediaPage } from './pages/MediaPage';\n\ntype AdminAppProps = {\n params: Promise<{ path?: string[] }>;\n};\n\n/**\n * Catch-all admin router. Mounted via a single `src/app/cms/[[...path]]/page.tsx`\n * file in the user app that re-exports this component as the default.\n *\n * Route segments map to admin pages:\n * /cms → ContentPage (admin home — content list)\n * /cms/content → ContentPage (alias for /cms — kept for legacy links)\n * /cms/content/<type> → CollectionPage\n * /cms/content/<type>/<id> → EntryPage\n * /cms/chat → ChatPage (gated on `isAgentEnabled(agentConfig)`)\n * /cms/media → MediaPage (library — grid + folders)\n * /cms/media/<id> → MediaAssetPage (full-page asset editor)\n * /cms/model → ContentModelPage\n * /cms/model/<type> → ContentTypePage\n *\n * **No `Suspense` in the dispatcher** — `await params` runs in this async RSC; Next.js\n * keeps the previous segment visible during navigation. Thin server shells (`*Page`)\n * hand off to client components that load via TanStack Query and render their own\n * block-level skeletons (`LeftPanelSkeleton`, `ContentTableSkeleton`, etc.).\n */\nexport async function AdminApp({ params }: AdminAppProps) {\n const { path } = await params;\n const segments = path ?? [];\n\n if (segments.length === 0) {\n return <ContentPage />;\n }\n\n if (segments[0] === 'chat') {\n return <ChatPage />;\n }\n\n if (segments[0] === 'media') {\n if (segments.length === 1) {\n return <MediaPage />;\n }\n const id = segments[1];\n return <MediaAssetPage id={id} key={id} />;\n }\n\n if (segments[0] === 'model') {\n if (segments.length === 1) {\n return <ContentModelPage />;\n }\n const [, type] = segments;\n return <ContentTypePage type={type} key={type} />;\n }\n\n if (segments[0] === 'content') {\n if (segments.length === 1) {\n return <ContentPage />;\n }\n if (segments.length === 2) {\n const [, type] = segments;\n return <CollectionPage params={Promise.resolve({ type })} key={type} />;\n }\n const [, type, id] = segments;\n return <EntryPage params={Promise.resolve({ type, id })} key={`${type}/${id}`} />;\n }\n\n return <DashboardContentSkeleton />;\n}\n"],"mappings":";AAwCW;AAxCX,SAAS,gCAAgC;AAEzC,SAAS,gBAAgB;AACzB,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AACjC,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAChC,SAAS,iBAAiB;AAC1B,SAAS,sBAAsB;AAC/B,SAAS,iBAAiB;AA0B1B,eAAsB,SAAS,EAAE,OAAO,GAAkB;AACxD,QAAM,EAAE,KAAK,IAAI,MAAM;AACvB,QAAM,WAAW,sBAAQ,CAAC;AAE1B,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO,oBAAC,eAAY;AAAA,EACtB;AAEA,MAAI,SAAS,CAAC,MAAM,QAAQ;AAC1B,WAAO,oBAAC,YAAS;AAAA,EACnB;AAEA,MAAI,SAAS,CAAC,MAAM,SAAS;AAC3B,QAAI,SAAS,WAAW,GAAG;AACzB,aAAO,oBAAC,aAAU;AAAA,IACpB;AACA,UAAM,KAAK,SAAS,CAAC;AACrB,WAAO,oBAAC,kBAAe,MAAa,EAAI;AAAA,EAC1C;AAEA,MAAI,SAAS,CAAC,MAAM,SAAS;AAC3B,QAAI,SAAS,WAAW,GAAG;AACzB,aAAO,oBAAC,oBAAiB;AAAA,IAC3B;AACA,UAAM,CAAC,EAAE,IAAI,IAAI;AACjB,WAAO,oBAAC,mBAAgB,QAAiB,IAAM;AAAA,EACjD;AAEA,MAAI,SAAS,CAAC,MAAM,WAAW;AAC7B,QAAI,SAAS,WAAW,GAAG;AACzB,aAAO,oBAAC,eAAY;AAAA,IACtB;AACA,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,CAAC,EAAEA,KAAI,IAAI;AACjB,aAAO,oBAAC,kBAAe,QAAQ,QAAQ,QAAQ,EAAE,MAAAA,MAAK,CAAC,KAAQA,KAAM;AAAA,IACvE;AACA,UAAM,CAAC,EAAE,MAAM,EAAE,IAAI;AACrB,WAAO,oBAAC,aAAU,QAAQ,QAAQ,QAAQ,EAAE,MAAM,GAAG,CAAC,KAAQ,GAAG,IAAI,IAAI,EAAE,EAAI;AAAA,EACjF;AAEA,SAAO,oBAAC,4BAAyB;AACnC;","names":["type"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import './registerConfig';
|
|
2
|
+
import { type RecentCMSPullRequestState } from '../github';
|
|
2
3
|
import type { EntryCommitHistory } from '../../types';
|
|
3
4
|
import { type ActionResult, type CreateBranchInput, type CreateBranchResult, type CreatePRResult } from './utils';
|
|
4
5
|
export declare const pushToGit: () => Promise<ActionResult>;
|
|
@@ -56,4 +57,20 @@ export declare const publishBranch: (branchName: string) => Promise<ActionResult
|
|
|
56
57
|
* shape so the UI never throws.
|
|
57
58
|
*/
|
|
58
59
|
export declare const getEntryCommits: (filePath: string) => Promise<EntryCommitHistory>;
|
|
60
|
+
export type RecentCMSPullRequest = {
|
|
61
|
+
branch: string;
|
|
62
|
+
prUrl: string;
|
|
63
|
+
prNumber: number;
|
|
64
|
+
title: string;
|
|
65
|
+
state: RecentCMSPullRequestState;
|
|
66
|
+
updatedAt: string;
|
|
67
|
+
authorLogin: string | null;
|
|
68
|
+
authorAvatarUrl: string | null;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Most-recently-updated PRs tagged with the `cms-update` label, across all
|
|
72
|
+
* states (open / merged / closed). Powers the dashboard "Recent pull requests"
|
|
73
|
+
* card. Returns `[]` in dev or on error.
|
|
74
|
+
*/
|
|
75
|
+
export declare const getRecentCMSPullRequests: (limit?: number) => Promise<RecentCMSPullRequest[]>;
|
|
59
76
|
//# sourceMappingURL=git.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../../admin/actions/git.ts"],"names":[],"mappings":"AAEA,OAAO,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../../admin/actions/git.ts"],"names":[],"mappings":"AAEA,OAAO,kBAAkB,CAAC;AAQ1B,OAAO,EAcL,KAAK,yBAAyB,EAC/B,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAe,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAQnE,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACpB,MAAM,SAAS,CAAC;AAQjB,eAAO,MAAM,SAAS,QAAa,OAAO,CAAC,YAAY,CA2BtD,CAAC;AAEF,eAAO,MAAM,QAAQ,QAAa,OAAO,CAAC,cAAc,CAYvD,CAAC;AAEF,eAAO,MAAM,eAAe,wBAE3B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,SAAS,QAAa,OAAO,CAAC,MAAM,CAoBhD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,QAAa,OAAO,CAAC,OAAO,CAGvD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAU,OAAO,iBAAiB,KAAG,OAAO,CAAC,kBAAkB,CAoEvF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,GAAU,YAAY,MAAM,KAAG,OAAO,CAAC,IAAI,CAUtE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,QAAa,OAAO,CAAC,IAAI,CAGhD,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,QAAa,OAAO,CAAC,SAAS,EAAE,CAsC3D,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GAAU,YAAY,MAAM,KAAG,OAAO,CAAC,YAAY,CAyB5E,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAU,UAAU,MAAM,KAAG,OAAO,CAAC,kBAAkB,CAkDlF,CAAC;AAMF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,yBAAyB,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GAAU,cAAS,KAAG,OAAO,CAAC,oBAAoB,EAAE,CASxF,CAAC"}
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
getPublishedPointerRef,
|
|
18
18
|
isProductionMode,
|
|
19
19
|
listGitHubCMSPullRequests,
|
|
20
|
+
listGitHubRecentCMSPullRequests,
|
|
20
21
|
markPRReadyForReview,
|
|
21
22
|
resolveContentBranch,
|
|
22
23
|
saveGitHubFile
|
|
@@ -269,6 +270,15 @@ const getEntryCommits = async (filePath) => {
|
|
|
269
270
|
return empty;
|
|
270
271
|
}
|
|
271
272
|
};
|
|
273
|
+
const getRecentCMSPullRequests = async (limit = 5) => {
|
|
274
|
+
if (!isProductionMode()) return [];
|
|
275
|
+
try {
|
|
276
|
+
return await listGitHubRecentCMSPullRequests(limit);
|
|
277
|
+
} catch (e) {
|
|
278
|
+
logCmsServerError({ operation: "getRecentCMSPullRequests", message: getErrorMessage(e) });
|
|
279
|
+
return [];
|
|
280
|
+
}
|
|
281
|
+
};
|
|
272
282
|
export {
|
|
273
283
|
clearBranch,
|
|
274
284
|
createBranch,
|
|
@@ -276,6 +286,7 @@ export {
|
|
|
276
286
|
getBranch,
|
|
277
287
|
getEntryCommits,
|
|
278
288
|
getIsProduction,
|
|
289
|
+
getRecentCMSPullRequests,
|
|
279
290
|
hasActiveBranch,
|
|
280
291
|
listCMSBranches,
|
|
281
292
|
publishBranch,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../admin/actions/git.ts"],"sourcesContent":["'use server';\n\nimport './registerConfig';\n\nimport { execFile } from 'node:child_process';\n\nimport { cookies } from 'next/headers';\n\nimport { getConfig } from '../../lib/configStore';\n\nimport {\n assertGitHubConfig,\n createGitHubBranch,\n createGitHubCMSPullRequest,\n createGitHubPullRequest,\n getGitHubFile,\n getPublicOctokits,\n getPublishedPointerRef,\n isProductionMode,\n listGitHubCMSPullRequests,\n markPRReadyForReview,\n resolveContentBranch,\n saveGitHubFile,\n} from '../github';\nimport type { EntryCommit, EntryCommitHistory } from '../../types';\nimport {\n BRANCH_HISTORY_FILE_PATH,\n parseBranchHistoryFile,\n serializeBranchHistoryFile,\n upsertBranchWorkspace,\n} from '../../lib/branchHistory';\nimport { logCmsServerError } from '../../lib/cmsServerLog';\nimport {\n actionErr,\n actionOk,\n getErrorMessage,\n type ActionResult,\n type CreateBranchInput,\n type CreateBranchResult,\n type CreatePRResult,\n} from './utils';\nimport { getPointerFilePath, serializePointerPayload } from '../../lib/contentBranch';\nimport { warmBranch } from '../store/contentStore';\nimport { buildJsons } from './build';\nimport { waitForPublicReadConsistency } from './files';\n\nconst CMS_BRANCH_COOKIE = 'cms-active-branch';\n\nexport const pushToGit = async (): Promise<ActionResult> => {\n if (isProductionMode()) {\n // In production, commits are made via GitHub API on each save — no push needed\n return actionOk();\n }\n\n try {\n const cookieStore = await cookies();\n const activeBranch = cookieStore.get(CMS_BRANCH_COOKIE)?.value;\n const pushCmd = activeBranch\n ? `git commit -am CommitFromCMS && git push origin HEAD:${activeBranch}`\n : 'git commit -am CommitFromCMS && git push';\n\n await new Promise<void>((resolve, reject) => {\n execFile('sh', ['-c', pushCmd], (error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n\n return actionOk();\n } catch (e) {\n return actionErr(new Error(`Failed git push: ${getErrorMessage(e)}`));\n }\n};\n\nexport const createPR = async (): Promise<CreatePRResult> => {\n const config = getConfig();\n const baseBranch = config.git.baseBranch;\n const title = `CMS content update (${new Date().toISOString().split('T')[0]})`;\n const body = `Content changes from the CMS editor.\\n\\nSource branch: \\`${baseBranch}\\``;\n\n try {\n const { url, number } = await createGitHubPullRequest(title, body, baseBranch);\n return { success: true, url, number };\n } catch (e) {\n return actionErr(e);\n }\n};\n\nexport const getIsProduction = async () => {\n return isProductionMode();\n};\n\n/**\n * Get the currently active CMS branch.\n * In dev: falls back to the local git branch name.\n * In prod: falls back to `config.git.baseBranch`.\n */\nexport const getBranch = async (): Promise<string> => {\n const cookieStore = await cookies();\n const activeBranch = cookieStore.get(CMS_BRANCH_COOKIE)?.value;\n\n if (activeBranch) return activeBranch;\n\n if (!isProductionMode()) {\n try {\n return await new Promise<string>((resolve, reject) => {\n execFile('git', ['branch', '--show-current'], (error, stdout) => {\n if (error) reject(error);\n else resolve(stdout.trim());\n });\n });\n } catch (_e) {\n // fall through to config.git.baseBranch\n }\n }\n\n return getConfig().git.baseBranch;\n};\n\n/**\n * Returns true if a CMS feature branch is currently active (cookie is set).\n */\nexport const hasActiveBranch = async (): Promise<boolean> => {\n const cookieStore = await cookies();\n return !!cookieStore.get(CMS_BRANCH_COOKIE)?.value;\n};\n\n/**\n * Create a GitHub branch from `config.git.baseBranch`, commit `cms/branch-history.json` so the\n * branch has at least one commit, open a draft PR with the `cms-update` label (best effort),\n * and set the active CMS branch cookie.\n */\nexport const createBranch = async (input: CreateBranchInput): Promise<CreateBranchResult> => {\n const config = getConfig();\n const branchName = input.branchName.trim();\n const workspaceTitle = input.title.trim();\n const description = input.description?.trim();\n\n if (!branchName) {\n return { success: false, error: 'Branch name is required.' };\n }\n\n if (!workspaceTitle) {\n return { success: false, error: 'Workspace title is required.' };\n }\n\n const baseBranch = config.git.baseBranch;\n\n try {\n await createGitHubBranch(branchName);\n\n const historyRaw = await getGitHubFile(BRANCH_HISTORY_FILE_PATH, baseBranch);\n const historyDoc = parseBranchHistoryFile(historyRaw?.content ?? '');\n const merged = upsertBranchWorkspace(historyDoc, branchName, {\n title: workspaceTitle,\n ...(description ? { description } : {}),\n createdAt: new Date().toISOString(),\n });\n const historyContent = serializeBranchHistoryFile(merged);\n\n await saveGitHubFile(BRANCH_HISTORY_FILE_PATH, historyContent, 'CMS: add branch workspace metadata', branchName);\n } catch (e) {\n const msg = getErrorMessage(e);\n logCmsServerError({\n operation: 'createBranch.metadata',\n branch: branchName,\n message: msg,\n });\n return { success: false, error: msg };\n }\n\n const prBodyLines = [\n `Content changes from the CMS editor.`,\n '',\n `Branch: \\`${branchName}\\``,\n ...(description ? ['', description] : []),\n ];\n const prBody = prBodyLines.join('\\n');\n\n let prUrl = '';\n let prWarning: string | undefined;\n\n try {\n const pr = await createGitHubCMSPullRequest(branchName, workspaceTitle, prBody, baseBranch);\n prUrl = pr.url;\n } catch (e) {\n prWarning = getErrorMessage(e);\n }\n\n const cookieStore = await cookies();\n cookieStore.set(CMS_BRANCH_COOKIE, branchName, { path: '/' });\n\n // Pre-populate the content store for the new branch\n if (isProductionMode()) {\n warmBranch(branchName).catch(() => {\n /* best-effort */\n });\n }\n\n return prWarning ? { success: true, prUrl: '', prWarning } : { success: true, prUrl };\n};\n\n/**\n * Switch the active CMS branch to an existing branch (by PR).\n */\nexport const setActiveBranch = async (branchName: string): Promise<void> => {\n const cookieStore = await cookies();\n cookieStore.set(CMS_BRANCH_COOKIE, branchName, { path: '/' });\n\n // Pre-populate the content store so the first read after switching is instant\n if (isProductionMode()) {\n warmBranch(branchName).catch(() => {\n /* best-effort — first read will trigger a blocking fetch if this fails */\n });\n }\n};\n\n/**\n * Clear the active CMS branch, reverting listing/reads to `config.git.baseBranch`.\n */\nexport const clearBranch = async (): Promise<void> => {\n const cookieStore = await cookies();\n cookieStore.delete(CMS_BRANCH_COOKIE);\n};\n\nexport type CMSBranch = {\n branch: string;\n prUrl: string;\n prNumber: number;\n title: string;\n isPublished: boolean;\n};\n\n/**\n * List CMS branches for the header: base branch plus **open** PRs labelled `cms-update`\n * (see `listGitHubCMSPullRequests`). Remote `cms/*` refs without an open labelled PR are omitted.\n */\nexport const listCMSBranches = async (): Promise<CMSBranch[]> => {\n try {\n const config = getConfig();\n const baseBranch = config.git.baseBranch;\n const pointerBranch = config.git.publishedPointerBranch?.trim();\n\n const [publishedBranch, prList] = await Promise.all([resolveContentBranch(), listGitHubCMSPullRequests()]);\n\n const base: CMSBranch = {\n branch: baseBranch,\n prUrl: '',\n prNumber: 0,\n title: baseBranch,\n isPublished: publishedBranch === baseBranch,\n };\n\n const features: CMSBranch[] = [];\n\n for (const pr of prList) {\n const branch = pr.branch.trim();\n if (!branch || branch === baseBranch) continue;\n if (pointerBranch && branch === pointerBranch) continue;\n if (!branch.startsWith('cms/')) continue;\n features.push({\n branch,\n prUrl: pr.prUrl,\n prNumber: pr.prNumber,\n title: pr.title.trim() || branch,\n isPublished: branch === publishedBranch,\n });\n }\n\n features.sort((a, b) => b.prNumber - a.prNumber);\n\n return [base, ...features];\n } catch (_e) {\n return [];\n }\n};\n\n/**\n * Publish a branch by writing `{ \"branch\": \"<name>\", \"buildId\": \"<id>\" }` into the per-build\n * pointer file (see {@link getPointerFilePath}) on the pointer branch when\n * `config.git.publishedPointerBranch` is set, otherwise on the base branch, invalidating all\n * public caches, and marking the associated PR as ready for review.\n *\n * Public pages will serve content from the published branch on the next request.\n */\nexport const publishBranch = async (branchName: string): Promise<ActionResult> => {\n try {\n const config = getConfig();\n const baseBranch = config.git.baseBranch;\n const pointerRef = getPublishedPointerRef();\n const targetBranch = pointerRef ?? baseBranch;\n const pointerPath = getPointerFilePath();\n const content = serializePointerPayload(branchName);\n\n await saveGitHubFile(pointerPath, content, `Publish branch ${branchName} (${pointerPath})`, targetBranch);\n await waitForPublicReadConsistency(pointerPath, content, targetBranch);\n await buildJsons('');\n\n if (branchName !== baseBranch) {\n try {\n await markPRReadyForReview(branchName);\n } catch (_e) {\n // PR marking is best-effort — don't fail publish if it errors\n }\n }\n\n return actionOk();\n } catch (e) {\n return actionErr(new Error(`Failed to publish branch: ${getErrorMessage(e)}`));\n }\n};\n\n/**\n * Fetch the last 5 commits that touched the given entry file for display in the\n * editor History panel. Production-only (dev mode writes to the local FS so there\n * is no GitHub history to show). Best-effort: any failure resolves to an empty\n * shape so the UI never throws.\n */\nexport const getEntryCommits = async (filePath: string): Promise<EntryCommitHistory> => {\n const empty: EntryCommitHistory = { commits: [], seeAllUrl: '' };\n\n if (!isProductionMode()) {\n return empty;\n }\n if (!filePath) {\n return empty;\n }\n\n try {\n const { owner, repo } = assertGitHubConfig();\n const [octokit] = getPublicOctokits();\n const baseBranch = getConfig().git.baseBranch;\n\n const { data } = await octokit.rest.repos.listCommits({\n owner,\n repo,\n path: filePath,\n per_page: 5,\n });\n\n const commits: EntryCommit[] = data.slice(0, 5).map((c) => {\n const gitAuthor = c.commit.author;\n const committer = c.commit.committer;\n const ghAuthor = c.author;\n\n const message = (c.commit.message || '').split('\\n', 1)[0];\n const login = ghAuthor?.login ?? null;\n const name = gitAuthor?.name || ghAuthor?.login || 'unknown';\n const avatarUrl = ghAuthor?.avatar_url ?? null;\n const committedAt = gitAuthor?.date || committer?.date || '';\n\n return {\n sha: c.sha,\n shortSha: c.sha.slice(0, 7),\n message,\n author: { login, name, avatarUrl },\n committedAt,\n url: c.html_url,\n };\n });\n\n const seeAllUrl = `https://github.com/${owner}/${repo}/commits/${encodeURIComponent(baseBranch)}/${encodeURI(filePath)}`;\n\n return { commits, seeAllUrl };\n } catch (e) {\n logCmsServerError({ operation: 'getEntryCommits', message: getErrorMessage(e) });\n return empty;\n }\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../admin/actions/git.ts"],"sourcesContent":["'use server';\n\nimport './registerConfig';\n\nimport { execFile } from 'node:child_process';\n\nimport { cookies } from 'next/headers';\n\nimport { getConfig } from '../../lib/configStore';\n\nimport {\n assertGitHubConfig,\n createGitHubBranch,\n createGitHubCMSPullRequest,\n createGitHubPullRequest,\n getGitHubFile,\n getPublicOctokits,\n getPublishedPointerRef,\n isProductionMode,\n listGitHubCMSPullRequests,\n listGitHubRecentCMSPullRequests,\n markPRReadyForReview,\n resolveContentBranch,\n saveGitHubFile,\n type RecentCMSPullRequestState,\n} from '../github';\nimport type { EntryCommit, EntryCommitHistory } from '../../types';\nimport {\n BRANCH_HISTORY_FILE_PATH,\n parseBranchHistoryFile,\n serializeBranchHistoryFile,\n upsertBranchWorkspace,\n} from '../../lib/branchHistory';\nimport { logCmsServerError } from '../../lib/cmsServerLog';\nimport {\n actionErr,\n actionOk,\n getErrorMessage,\n type ActionResult,\n type CreateBranchInput,\n type CreateBranchResult,\n type CreatePRResult,\n} from './utils';\nimport { getPointerFilePath, serializePointerPayload } from '../../lib/contentBranch';\nimport { warmBranch } from '../store/contentStore';\nimport { buildJsons } from './build';\nimport { waitForPublicReadConsistency } from './files';\n\nconst CMS_BRANCH_COOKIE = 'cms-active-branch';\n\nexport const pushToGit = async (): Promise<ActionResult> => {\n if (isProductionMode()) {\n // In production, commits are made via GitHub API on each save — no push needed\n return actionOk();\n }\n\n try {\n const cookieStore = await cookies();\n const activeBranch = cookieStore.get(CMS_BRANCH_COOKIE)?.value;\n const pushCmd = activeBranch\n ? `git commit -am CommitFromCMS && git push origin HEAD:${activeBranch}`\n : 'git commit -am CommitFromCMS && git push';\n\n await new Promise<void>((resolve, reject) => {\n execFile('sh', ['-c', pushCmd], (error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n\n return actionOk();\n } catch (e) {\n return actionErr(new Error(`Failed git push: ${getErrorMessage(e)}`));\n }\n};\n\nexport const createPR = async (): Promise<CreatePRResult> => {\n const config = getConfig();\n const baseBranch = config.git.baseBranch;\n const title = `CMS content update (${new Date().toISOString().split('T')[0]})`;\n const body = `Content changes from the CMS editor.\\n\\nSource branch: \\`${baseBranch}\\``;\n\n try {\n const { url, number } = await createGitHubPullRequest(title, body, baseBranch);\n return { success: true, url, number };\n } catch (e) {\n return actionErr(e);\n }\n};\n\nexport const getIsProduction = async () => {\n return isProductionMode();\n};\n\n/**\n * Get the currently active CMS branch.\n * In dev: falls back to the local git branch name.\n * In prod: falls back to `config.git.baseBranch`.\n */\nexport const getBranch = async (): Promise<string> => {\n const cookieStore = await cookies();\n const activeBranch = cookieStore.get(CMS_BRANCH_COOKIE)?.value;\n\n if (activeBranch) return activeBranch;\n\n if (!isProductionMode()) {\n try {\n return await new Promise<string>((resolve, reject) => {\n execFile('git', ['branch', '--show-current'], (error, stdout) => {\n if (error) reject(error);\n else resolve(stdout.trim());\n });\n });\n } catch (_e) {\n // fall through to config.git.baseBranch\n }\n }\n\n return getConfig().git.baseBranch;\n};\n\n/**\n * Returns true if a CMS feature branch is currently active (cookie is set).\n */\nexport const hasActiveBranch = async (): Promise<boolean> => {\n const cookieStore = await cookies();\n return !!cookieStore.get(CMS_BRANCH_COOKIE)?.value;\n};\n\n/**\n * Create a GitHub branch from `config.git.baseBranch`, commit `cms/branch-history.json` so the\n * branch has at least one commit, open a draft PR with the `cms-update` label (best effort),\n * and set the active CMS branch cookie.\n */\nexport const createBranch = async (input: CreateBranchInput): Promise<CreateBranchResult> => {\n const config = getConfig();\n const branchName = input.branchName.trim();\n const workspaceTitle = input.title.trim();\n const description = input.description?.trim();\n\n if (!branchName) {\n return { success: false, error: 'Branch name is required.' };\n }\n\n if (!workspaceTitle) {\n return { success: false, error: 'Workspace title is required.' };\n }\n\n const baseBranch = config.git.baseBranch;\n\n try {\n await createGitHubBranch(branchName);\n\n const historyRaw = await getGitHubFile(BRANCH_HISTORY_FILE_PATH, baseBranch);\n const historyDoc = parseBranchHistoryFile(historyRaw?.content ?? '');\n const merged = upsertBranchWorkspace(historyDoc, branchName, {\n title: workspaceTitle,\n ...(description ? { description } : {}),\n createdAt: new Date().toISOString(),\n });\n const historyContent = serializeBranchHistoryFile(merged);\n\n await saveGitHubFile(BRANCH_HISTORY_FILE_PATH, historyContent, 'CMS: add branch workspace metadata', branchName);\n } catch (e) {\n const msg = getErrorMessage(e);\n logCmsServerError({\n operation: 'createBranch.metadata',\n branch: branchName,\n message: msg,\n });\n return { success: false, error: msg };\n }\n\n const prBodyLines = [\n `Content changes from the CMS editor.`,\n '',\n `Branch: \\`${branchName}\\``,\n ...(description ? ['', description] : []),\n ];\n const prBody = prBodyLines.join('\\n');\n\n let prUrl = '';\n let prWarning: string | undefined;\n\n try {\n const pr = await createGitHubCMSPullRequest(branchName, workspaceTitle, prBody, baseBranch);\n prUrl = pr.url;\n } catch (e) {\n prWarning = getErrorMessage(e);\n }\n\n const cookieStore = await cookies();\n cookieStore.set(CMS_BRANCH_COOKIE, branchName, { path: '/' });\n\n // Pre-populate the content store for the new branch\n if (isProductionMode()) {\n warmBranch(branchName).catch(() => {\n /* best-effort */\n });\n }\n\n return prWarning ? { success: true, prUrl: '', prWarning } : { success: true, prUrl };\n};\n\n/**\n * Switch the active CMS branch to an existing branch (by PR).\n */\nexport const setActiveBranch = async (branchName: string): Promise<void> => {\n const cookieStore = await cookies();\n cookieStore.set(CMS_BRANCH_COOKIE, branchName, { path: '/' });\n\n // Pre-populate the content store so the first read after switching is instant\n if (isProductionMode()) {\n warmBranch(branchName).catch(() => {\n /* best-effort — first read will trigger a blocking fetch if this fails */\n });\n }\n};\n\n/**\n * Clear the active CMS branch, reverting listing/reads to `config.git.baseBranch`.\n */\nexport const clearBranch = async (): Promise<void> => {\n const cookieStore = await cookies();\n cookieStore.delete(CMS_BRANCH_COOKIE);\n};\n\nexport type CMSBranch = {\n branch: string;\n prUrl: string;\n prNumber: number;\n title: string;\n isPublished: boolean;\n};\n\n/**\n * List CMS branches for the header: base branch plus **open** PRs labelled `cms-update`\n * (see `listGitHubCMSPullRequests`). Remote `cms/*` refs without an open labelled PR are omitted.\n */\nexport const listCMSBranches = async (): Promise<CMSBranch[]> => {\n try {\n const config = getConfig();\n const baseBranch = config.git.baseBranch;\n const pointerBranch = config.git.publishedPointerBranch?.trim();\n\n const [publishedBranch, prList] = await Promise.all([resolveContentBranch(), listGitHubCMSPullRequests()]);\n\n const base: CMSBranch = {\n branch: baseBranch,\n prUrl: '',\n prNumber: 0,\n title: baseBranch,\n isPublished: publishedBranch === baseBranch,\n };\n\n const features: CMSBranch[] = [];\n\n for (const pr of prList) {\n const branch = pr.branch.trim();\n if (!branch || branch === baseBranch) continue;\n if (pointerBranch && branch === pointerBranch) continue;\n if (!branch.startsWith('cms/')) continue;\n features.push({\n branch,\n prUrl: pr.prUrl,\n prNumber: pr.prNumber,\n title: pr.title.trim() || branch,\n isPublished: branch === publishedBranch,\n });\n }\n\n features.sort((a, b) => b.prNumber - a.prNumber);\n\n return [base, ...features];\n } catch (_e) {\n return [];\n }\n};\n\n/**\n * Publish a branch by writing `{ \"branch\": \"<name>\", \"buildId\": \"<id>\" }` into the per-build\n * pointer file (see {@link getPointerFilePath}) on the pointer branch when\n * `config.git.publishedPointerBranch` is set, otherwise on the base branch, invalidating all\n * public caches, and marking the associated PR as ready for review.\n *\n * Public pages will serve content from the published branch on the next request.\n */\nexport const publishBranch = async (branchName: string): Promise<ActionResult> => {\n try {\n const config = getConfig();\n const baseBranch = config.git.baseBranch;\n const pointerRef = getPublishedPointerRef();\n const targetBranch = pointerRef ?? baseBranch;\n const pointerPath = getPointerFilePath();\n const content = serializePointerPayload(branchName);\n\n await saveGitHubFile(pointerPath, content, `Publish branch ${branchName} (${pointerPath})`, targetBranch);\n await waitForPublicReadConsistency(pointerPath, content, targetBranch);\n await buildJsons('');\n\n if (branchName !== baseBranch) {\n try {\n await markPRReadyForReview(branchName);\n } catch (_e) {\n // PR marking is best-effort — don't fail publish if it errors\n }\n }\n\n return actionOk();\n } catch (e) {\n return actionErr(new Error(`Failed to publish branch: ${getErrorMessage(e)}`));\n }\n};\n\n/**\n * Fetch the last 5 commits that touched the given entry file for display in the\n * editor History panel. Production-only (dev mode writes to the local FS so there\n * is no GitHub history to show). Best-effort: any failure resolves to an empty\n * shape so the UI never throws.\n */\nexport const getEntryCommits = async (filePath: string): Promise<EntryCommitHistory> => {\n const empty: EntryCommitHistory = { commits: [], seeAllUrl: '' };\n\n if (!isProductionMode()) {\n return empty;\n }\n if (!filePath) {\n return empty;\n }\n\n try {\n const { owner, repo } = assertGitHubConfig();\n const [octokit] = getPublicOctokits();\n const baseBranch = getConfig().git.baseBranch;\n\n const { data } = await octokit.rest.repos.listCommits({\n owner,\n repo,\n path: filePath,\n per_page: 5,\n });\n\n const commits: EntryCommit[] = data.slice(0, 5).map((c) => {\n const gitAuthor = c.commit.author;\n const committer = c.commit.committer;\n const ghAuthor = c.author;\n\n const message = (c.commit.message || '').split('\\n', 1)[0];\n const login = ghAuthor?.login ?? null;\n const name = gitAuthor?.name || ghAuthor?.login || 'unknown';\n const avatarUrl = ghAuthor?.avatar_url ?? null;\n const committedAt = gitAuthor?.date || committer?.date || '';\n\n return {\n sha: c.sha,\n shortSha: c.sha.slice(0, 7),\n message,\n author: { login, name, avatarUrl },\n committedAt,\n url: c.html_url,\n };\n });\n\n const seeAllUrl = `https://github.com/${owner}/${repo}/commits/${encodeURIComponent(baseBranch)}/${encodeURI(filePath)}`;\n\n return { commits, seeAllUrl };\n } catch (e) {\n logCmsServerError({ operation: 'getEntryCommits', message: getErrorMessage(e) });\n return empty;\n }\n};\n\n// ---------------------------------------------------------------------------\n// Dashboard reads (production-only, best-effort)\n// ---------------------------------------------------------------------------\n\nexport type RecentCMSPullRequest = {\n branch: string;\n prUrl: string;\n prNumber: number;\n title: string;\n state: RecentCMSPullRequestState;\n updatedAt: string;\n authorLogin: string | null;\n authorAvatarUrl: string | null;\n};\n\n/**\n * Most-recently-updated PRs tagged with the `cms-update` label, across all\n * states (open / merged / closed). Powers the dashboard \"Recent pull requests\"\n * card. Returns `[]` in dev or on error.\n */\nexport const getRecentCMSPullRequests = async (limit = 5): Promise<RecentCMSPullRequest[]> => {\n if (!isProductionMode()) return [];\n\n try {\n return await listGitHubRecentCMSPullRequests(limit);\n } catch (e) {\n logCmsServerError({ operation: 'getRecentCMSPullRequests', message: getErrorMessage(e) });\n return [];\n }\n};\n"],"mappings":"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","names":[]}
|
package/dist/admin/github.d.ts
CHANGED
|
@@ -84,6 +84,22 @@ export declare const listGitHubCMSPullRequests: () => Promise<{
|
|
|
84
84
|
prNumber: number;
|
|
85
85
|
title: string;
|
|
86
86
|
}[]>;
|
|
87
|
+
export type RecentCMSPullRequestState = 'open' | 'merged' | 'closed';
|
|
88
|
+
/**
|
|
89
|
+
* List the most-recently-updated PRs tagged with the `cms-update` label,
|
|
90
|
+
* across all states (open / merged / closed). Used by the dashboard
|
|
91
|
+
* "Recent pull requests" card.
|
|
92
|
+
*/
|
|
93
|
+
export declare const listGitHubRecentCMSPullRequests: (limit?: number) => Promise<{
|
|
94
|
+
branch: string;
|
|
95
|
+
prUrl: string;
|
|
96
|
+
prNumber: number;
|
|
97
|
+
title: string;
|
|
98
|
+
state: RecentCMSPullRequestState;
|
|
99
|
+
updatedAt: string;
|
|
100
|
+
authorLogin: string | null;
|
|
101
|
+
authorAvatarUrl: string | null;
|
|
102
|
+
}[]>;
|
|
87
103
|
/**
|
|
88
104
|
* Create a pull request from the configured branch to the target branch.
|
|
89
105
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../admin/github.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../admin/github.ts"],"names":[],"mappings":"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"}
|
package/dist/admin/github.js
CHANGED
|
@@ -381,6 +381,32 @@ const listGitHubCMSPullRequests = async () => {
|
|
|
381
381
|
title: pr.title
|
|
382
382
|
}));
|
|
383
383
|
};
|
|
384
|
+
const listGitHubRecentCMSPullRequests = async (limit = 5) => {
|
|
385
|
+
const octokit = await getWriteOctokit();
|
|
386
|
+
const { owner, repo } = assertGitHubConfig();
|
|
387
|
+
const { data: prs } = await octokit.rest.pulls.list({
|
|
388
|
+
owner,
|
|
389
|
+
repo,
|
|
390
|
+
state: "all",
|
|
391
|
+
sort: "updated",
|
|
392
|
+
direction: "desc",
|
|
393
|
+
per_page: 50
|
|
394
|
+
});
|
|
395
|
+
return prs.filter((pr) => pr.labels.some((l) => l.name === "cms-update")).slice(0, Math.max(1, limit)).map((pr) => {
|
|
396
|
+
var _a, _b, _c, _d;
|
|
397
|
+
const state = pr.merged_at ? "merged" : pr.state === "open" ? "open" : "closed";
|
|
398
|
+
return {
|
|
399
|
+
branch: pr.head.ref,
|
|
400
|
+
prUrl: pr.html_url,
|
|
401
|
+
prNumber: pr.number,
|
|
402
|
+
title: pr.title,
|
|
403
|
+
state,
|
|
404
|
+
updatedAt: pr.updated_at,
|
|
405
|
+
authorLogin: (_b = (_a = pr.user) == null ? void 0 : _a.login) != null ? _b : null,
|
|
406
|
+
authorAvatarUrl: (_d = (_c = pr.user) == null ? void 0 : _c.avatar_url) != null ? _d : null
|
|
407
|
+
};
|
|
408
|
+
});
|
|
409
|
+
};
|
|
384
410
|
const createGitHubPullRequest = async (title, body, targetBranch = "main") => {
|
|
385
411
|
const octokit = await getWriteOctokit();
|
|
386
412
|
const { owner, repo, branch } = assertGitHubConfig();
|
|
@@ -443,6 +469,7 @@ export {
|
|
|
443
469
|
listGitHubCMSPullRequests,
|
|
444
470
|
listGitHubFiles,
|
|
445
471
|
listGitHubFilesRecursive,
|
|
472
|
+
listGitHubRecentCMSPullRequests,
|
|
446
473
|
markPRReadyForReview,
|
|
447
474
|
mergePullRequest,
|
|
448
475
|
readGitHubBinaryFilePublic,
|
package/dist/admin/github.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../admin/github.ts"],"sourcesContent":["import { Octokit } from 'octokit';\nimport { getServerSession } from 'next-auth';\n\nimport { logCmsServerError } from '../lib/cmsServerLog';\nimport { mapGitHubApiErrorToContentSource } from '../lib/contentSourceError';\n\nimport { authOptions } from './auth';\n\nimport {\n assertGitHubConfig,\n getPublicOctokits,\n getPublishedPointerRef,\n readGitHubFilePublic,\n listGitHubFiles,\n listGitHubFilesRecursive,\n listGitHubBranchRefsByPrefix,\n resolveContentBranch,\n isProductionMode,\n} from '../github-public';\n\nexport {\n assertGitHubConfig,\n getPublicOctokits,\n getPublishedPointerRef,\n readGitHubFilePublic,\n listGitHubFiles,\n listGitHubFilesRecursive,\n listGitHubBranchRefsByPrefix,\n resolveContentBranch,\n isProductionMode,\n};\n\nconst getErrorMessage = (error: unknown): string => {\n if (error instanceof Error) {\n return error.message;\n }\n\n return String(error);\n};\n\nconst formatGitHubApiError = (error: any): string => {\n const status = error?.status;\n const message = error?.response?.data?.message || error?.message || getErrorMessage(error);\n\n if (status) {\n return `${status} ${message}`;\n }\n\n return message;\n};\n\nconst getDefaultBranchRef = async (octokit: Octokit, owner: string, repo: string): Promise<string> => {\n const { data: repoData } = await octokit.rest.repos.get({ owner, repo });\n const defaultBranch = repoData.default_branch || 'main';\n const { data: defaultRef } = await octokit.rest.git.getRef({\n owner,\n repo,\n ref: `heads/${defaultBranch}`,\n });\n\n return defaultRef.object.sha;\n};\n\nconst ensureBranchExists = async (octokit: Octokit, owner: string, repo: string, branch: string) => {\n try {\n await octokit.rest.git.getRef({\n owner,\n repo,\n ref: `heads/${branch}`,\n });\n } catch (error: any) {\n if (error.status !== 404) {\n throw error;\n }\n\n const baseSha = await getDefaultBranchRef(octokit, owner, repo);\n\n await octokit.rest.git.createRef({\n owner,\n repo,\n ref: `refs/heads/${branch}`,\n sha: baseSha,\n });\n }\n};\n\nconst getOctokit = async () => {\n const session = await getServerSession(authOptions);\n const accessToken = (session as any)?.accessToken;\n\n if (!accessToken) {\n throw new Error('No GitHub access token found. Please sign in again.');\n }\n\n return new Octokit({ auth: accessToken });\n};\n\n/**\n * Get an Octokit client suitable for write operations (save, delete, create PR).\n * Prefers CMS_GITHUB_TOKEN so it works even when the GitHub App is not installed\n * on the target repo. Falls back to the user session token.\n */\nconst getWriteOctokit = async (): Promise<Octokit> => {\n const token = process.env.CMS_GITHUB_TOKEN?.trim();\n\n if (token) {\n return new Octokit({ auth: token });\n }\n\n return getOctokit();\n};\n\n/**\n * Read a binary file from GitHub using a static server token (no user session required).\n * If `branch` is provided, reads from that branch directly (used by the media route\n * so editors see uploads on their active feature branch before publishing).\n * Otherwise uses {@link resolveContentBranch} so assets match the same ref as\n * `query()` / public JSON (not only `git.baseBranch`).\n * Returns a Buffer, or null if the file does not exist.\n */\nexport const readGitHubBinaryFilePublic = async (filePath: string, branch?: string): Promise<Buffer | null> => {\n const { owner, repo } = assertGitHubConfig();\n const ref = branch ?? (await resolveContentBranch());\n const clients = getPublicOctokits();\n\n for (const octokit of clients) {\n try {\n const { data } = await octokit.rest.repos.getContent({\n owner,\n repo,\n path: filePath,\n ref,\n });\n\n if ('content' in data && data.type === 'file') {\n return Buffer.from(data.content.replace(/\\n/g, ''), 'base64');\n }\n\n return null;\n } catch (error: any) {\n if (error.status === 429) {\n throw mapGitHubApiErrorToContentSource(error, { owner, repo, ref });\n }\n\n if (error.status === 401 || error.status === 403 || error.status === 404) {\n continue;\n }\n\n throw mapGitHubApiErrorToContentSource(error, { owner, repo, ref });\n }\n }\n\n return null;\n};\n\n/**\n * Get a file's content and SHA from the GitHub repo.\n * Returns { content, sha } or null if the file doesn't exist.\n */\nexport const getGitHubFile = async (\n filePath: string,\n branch?: string,\n): Promise<{ content: string; sha: string } | null> => {\n const [octokit] = getPublicOctokits();\n const { owner, repo, branch: configBranch } = assertGitHubConfig();\n const ref = branch || configBranch;\n\n try {\n const { data } = await octokit.rest.repos.getContent({\n owner,\n repo,\n path: filePath,\n ref,\n });\n\n if ('content' in data && data.type === 'file') {\n const content = Buffer.from(data.content, 'base64').toString('utf-8');\n return { content, sha: data.sha };\n }\n\n return null;\n } catch (error: any) {\n if (error.status === 404) {\n return null;\n }\n throw error;\n }\n};\n\n/**\n * Create or update a file in the GitHub repo via a commit.\n *\n * @param existingSha Optional SHA of the existing file blob. When provided from\n * the content store, skips the pre-read API call (saves 1 request per write).\n */\nexport const saveGitHubFile = async (\n filePath: string,\n content: string,\n message: string,\n branch?: string,\n existingSha?: string,\n) => {\n const octokit = await getWriteOctokit();\n const { owner, repo, branch: configBranch } = assertGitHubConfig();\n const targetBranch = branch || configBranch;\n\n await ensureBranchExists(octokit, owner, repo, targetBranch);\n\n // Get existing file SHA if it exists (required for updates) — skip when caller provides it\n const existing = existingSha ? { sha: existingSha } : await getGitHubFile(filePath, targetBranch);\n\n try {\n await octokit.rest.repos.createOrUpdateFileContents({\n owner,\n repo,\n path: filePath,\n message,\n content: Buffer.from(content).toString('base64'),\n branch: targetBranch,\n ...(existing ? { sha: existing.sha } : {}),\n });\n } catch (error: any) {\n if (error.status === 403) {\n throw new Error(\n 'GitHub token is missing repository write permissions. Ensure the app is installed on the repo and has Contents: Read and write permission.',\n );\n }\n\n if (error.status === 409) {\n throw new Error(\n 'GitHub rejected the commit due to branch protection or write conflicts. Use a writable branch (for example cms/edits) or relax direct-push protection on main.',\n );\n }\n\n if (error.status === 422) {\n throw new Error(`GitHub validation failed while saving content: ${formatGitHubApiError(error)}`);\n }\n\n throw new Error(`GitHub save failed: ${formatGitHubApiError(error)}`);\n }\n};\n\n/**\n * Create or update a binary file in the GitHub repo via a commit.\n * Unlike saveGitHubFile, this accepts a Buffer directly to avoid encoding corruption.\n */\nexport const saveGitHubBinaryFile = async (filePath: string, buffer: Buffer, message: string, branch?: string) => {\n const octokit = await getWriteOctokit();\n const { owner, repo, branch: configBranch } = assertGitHubConfig();\n const targetBranch = branch || configBranch;\n\n await ensureBranchExists(octokit, owner, repo, targetBranch);\n\n const existing = await getGitHubFile(filePath, targetBranch);\n\n try {\n await octokit.rest.repos.createOrUpdateFileContents({\n owner,\n repo,\n path: filePath,\n message,\n content: buffer.toString('base64'),\n branch: targetBranch,\n ...(existing ? { sha: existing.sha } : {}),\n });\n } catch (error: any) {\n if (error.status === 403) {\n throw new Error(\n 'GitHub token is missing repository write permissions. Ensure the app is installed on the repo and has Contents: Read and write permission.',\n );\n }\n\n if (error.status === 409) {\n throw new Error(\n 'GitHub rejected the commit due to branch protection or write conflicts. Use a writable branch (for example cms/edits) or relax direct-push protection on main.',\n );\n }\n\n if (error.status === 422) {\n throw new Error(`GitHub validation failed while saving content: ${formatGitHubApiError(error)}`);\n }\n\n throw new Error(`GitHub save failed: ${formatGitHubApiError(error)}`);\n }\n};\n\n/**\n * A single file change in a multi-file commit. `delete` removes the path\n * from the tree; `upsert-text` and `upsert-binary` create or update it.\n */\nexport type GitHubBatchChange =\n | { kind: 'upsert-text'; path: string; content: string }\n | { kind: 'upsert-binary'; path: string; content: Buffer }\n | { kind: 'delete'; path: string };\n\n/**\n * Commit multiple file changes atomically as a single GitHub commit on\n * `branch`. Used by the visual schema editor to write `cms/schema.json`\n * plus regenerated artifacts plus migrated entry files in one commit.\n *\n * Implementation: builds a git tree off the branch's current HEAD, creates a\n * single commit, then fast-forwards the branch. If `branch` does not exist,\n * it is created from `git.baseBranch` first (mirrors `ensureBranchExists`).\n *\n * Throws if all `changes` are no-ops (e.g. all deletes target missing paths)\n * — GitHub rejects empty trees.\n */\nexport const commitMultipleFilesToGitHub = async (\n changes: readonly GitHubBatchChange[],\n message: string,\n branch?: string,\n): Promise<{ sha: string }> => {\n if (changes.length === 0) {\n throw new Error('commitMultipleFilesToGitHub: no changes provided');\n }\n\n const octokit = await getWriteOctokit();\n const { owner, repo, branch: configBranch } = assertGitHubConfig();\n const targetBranch = branch || configBranch;\n\n await ensureBranchExists(octokit, owner, repo, targetBranch);\n\n const { data: ref } = await octokit.rest.git.getRef({\n owner,\n repo,\n ref: `heads/${targetBranch}`,\n });\n const headSha = ref.object.sha;\n\n const { data: headCommit } = await octokit.rest.git.getCommit({\n owner,\n repo,\n commit_sha: headSha,\n });\n const baseTreeSha = headCommit.tree.sha;\n\n const tree: {\n path: string;\n mode: '100644';\n type: 'blob';\n sha?: string | null;\n content?: string;\n }[] = [];\n\n for (const change of changes) {\n if (change.kind === 'delete') {\n tree.push({ path: change.path, mode: '100644', type: 'blob', sha: null });\n continue;\n }\n if (change.kind === 'upsert-text') {\n // Push content directly. GitHub will create a blob server-side.\n tree.push({ path: change.path, mode: '100644', type: 'blob', content: change.content });\n continue;\n }\n // Binary: must upload as a blob first, then reference its SHA.\n const { data: blob } = await octokit.rest.git.createBlob({\n owner,\n repo,\n content: change.content.toString('base64'),\n encoding: 'base64',\n });\n tree.push({ path: change.path, mode: '100644', type: 'blob', sha: blob.sha });\n }\n\n let newTreeSha: string;\n try {\n const { data: newTree } = await octokit.rest.git.createTree({\n owner,\n repo,\n base_tree: baseTreeSha,\n tree,\n });\n newTreeSha = newTree.sha;\n } catch (error: any) {\n throw new Error(`GitHub createTree failed: ${formatGitHubApiError(error)}`);\n }\n\n if (newTreeSha === baseTreeSha) {\n throw new Error('commitMultipleFilesToGitHub: changes produced no diff (every file was already up to date)');\n }\n\n const { data: newCommit } = await octokit.rest.git.createCommit({\n owner,\n repo,\n message,\n tree: newTreeSha,\n parents: [headSha],\n });\n\n try {\n await octokit.rest.git.updateRef({\n owner,\n repo,\n ref: `heads/${targetBranch}`,\n sha: newCommit.sha,\n });\n } catch (error: any) {\n if (error.status === 422) {\n throw new Error(\n `Branch \"${targetBranch}\" moved while committing. Retry the schema save: ${formatGitHubApiError(error)}`,\n );\n }\n throw new Error(`GitHub updateRef failed: ${formatGitHubApiError(error)}`);\n }\n\n return { sha: newCommit.sha };\n};\n\n/**\n * Delete a file from the GitHub repo via a commit.\n */\nexport const deleteGitHubFile = async (filePath: string, message: string, branch?: string) => {\n const octokit = await getWriteOctokit();\n const { owner, repo, branch: configBranch } = assertGitHubConfig();\n const targetBranch = branch || configBranch;\n\n const existing = await getGitHubFile(filePath, targetBranch);\n\n if (!existing) {\n return; // File doesn't exist, nothing to delete\n }\n\n await octokit.rest.repos.deleteFile({\n owner,\n repo,\n path: filePath,\n message,\n sha: existing.sha,\n branch: targetBranch,\n });\n};\n\n/**\n * Create a new branch in the GitHub repo from `config.git.baseBranch`.\n */\nexport const createGitHubBranch = async (branchName: string): Promise<void> => {\n const octokit = await getWriteOctokit();\n const { owner, repo, branch: baseBranch } = assertGitHubConfig();\n\n const { data: baseRef } = await octokit.rest.git.getRef({\n owner,\n repo,\n ref: `heads/${baseBranch}`,\n });\n\n await octokit.rest.git.createRef({\n owner,\n repo,\n ref: `refs/heads/${branchName}`,\n sha: baseRef.object.sha,\n });\n};\n\nconst ensureLabelExists = async (octokit: Octokit, owner: string, repo: string, name: string, color: string) => {\n try {\n await octokit.rest.issues.getLabel({ owner, repo, name });\n } catch (error: any) {\n if (error.status === 404) {\n await octokit.rest.issues.createLabel({\n owner,\n repo,\n name,\n color,\n description: 'Content updates from the CMS editor',\n });\n } else {\n throw error;\n }\n }\n};\n\n/**\n * Create a draft pull request for a CMS branch, adding the 'cms-update' label.\n */\nexport const createGitHubCMSPullRequest = async (\n headBranch: string,\n title: string,\n body: string,\n targetBranch: string,\n): Promise<{ url: string; number: number }> => {\n const octokit = await getWriteOctokit();\n const { owner, repo } = assertGitHubConfig();\n\n let data: { html_url: string; number: number };\n\n try {\n const res = await octokit.rest.pulls.create({\n owner,\n repo,\n title,\n body,\n head: headBranch,\n base: targetBranch,\n draft: true,\n });\n data = res.data;\n } catch (error: any) {\n const detail = formatGitHubApiError(error);\n logCmsServerError({\n operation: 'pulls.create',\n branch: headBranch,\n status: error?.status,\n message: detail,\n });\n\n if (error?.status === 422) {\n const lower = detail.toLowerCase();\n if (lower.includes('no commits between')) {\n throw new Error(\n 'Could not open draft pull request: GitHub reports no new commits on this branch versus the base branch. Save branch metadata should have created a commit — please try again or open a pull request manually on GitHub.',\n );\n }\n\n throw new Error(`Could not open draft pull request: ${detail}`);\n }\n\n throw new Error(`Could not open draft pull request: ${detail}`);\n }\n\n try {\n await ensureLabelExists(octokit, owner, repo, 'cms-update', '0075ca');\n await octokit.rest.issues.addLabels({\n owner,\n repo,\n issue_number: data.number,\n labels: ['cms-update'],\n });\n } catch (_e) {\n // Label is cosmetic — don't fail branch creation if labelling errors\n }\n\n return { url: data.html_url, number: data.number };\n};\n\n/**\n * List all open PRs tagged with the 'cms-update' label.\n */\nexport const listGitHubCMSPullRequests = async (): Promise<\n { branch: string; prUrl: string; prNumber: number; title: string }[]\n> => {\n const octokit = await getWriteOctokit();\n const { owner, repo } = assertGitHubConfig();\n\n const { data: prs } = await octokit.rest.pulls.list({\n owner,\n repo,\n state: 'open',\n per_page: 100,\n });\n\n return prs\n .filter((pr) => pr.labels.some((l) => l.name === 'cms-update'))\n .map((pr) => ({\n branch: pr.head.ref,\n prUrl: pr.html_url,\n prNumber: pr.number,\n title: pr.title,\n }));\n};\n\n/**\n * Create a pull request from the configured branch to the target branch.\n */\nexport const createGitHubPullRequest = async (\n title: string,\n body: string,\n targetBranch: string = 'main',\n): Promise<{ url: string; number: number }> => {\n const octokit = await getWriteOctokit();\n const { owner, repo, branch } = assertGitHubConfig();\n\n if (branch === targetBranch) {\n throw new Error(`Cannot create PR: source branch \"${branch}\" is the same as target branch \"${targetBranch}\"`);\n }\n\n const { data } = await octokit.rest.pulls.create({\n owner,\n repo,\n title,\n body,\n head: branch,\n base: targetBranch,\n });\n\n return { url: data.html_url, number: data.number };\n};\n\n/**\n * Merge a pull request via squash.\n */\nexport const mergePullRequest = async (prNumber: number): Promise<void> => {\n const octokit = await getWriteOctokit();\n const { owner, repo } = assertGitHubConfig();\n\n await octokit.rest.pulls.merge({\n owner,\n repo,\n pull_number: prNumber,\n merge_method: 'squash',\n });\n};\n\n/**\n * Convert a draft pull request for a CMS branch to \"Ready for Review\".\n * No-op if no open PR exists for the branch or if it is already non-draft.\n */\nexport const markPRReadyForReview = async (branchName: string): Promise<void> => {\n const octokit = await getWriteOctokit();\n const { owner, repo } = assertGitHubConfig();\n\n const { data: prs } = await octokit.rest.pulls.list({\n owner,\n repo,\n head: `${owner}:${branchName}`,\n state: 'open',\n per_page: 1,\n });\n\n const pr = prs[0];\n if (!pr || !pr.draft) return;\n\n await octokit.graphql(\n `mutation MarkReadyForReview($pullRequestId: ID!) {\n markPullRequestReadyForReview(input: { pullRequestId: $pullRequestId }) {\n pullRequest { isDraft }\n }\n }`,\n { pullRequestId: pr.node_id },\n );\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../admin/github.ts"],"sourcesContent":["import { Octokit } from 'octokit';\nimport { getServerSession } from 'next-auth';\n\nimport { logCmsServerError } from '../lib/cmsServerLog';\nimport { mapGitHubApiErrorToContentSource } from '../lib/contentSourceError';\n\nimport { authOptions } from './auth';\n\nimport {\n assertGitHubConfig,\n getPublicOctokits,\n getPublishedPointerRef,\n readGitHubFilePublic,\n listGitHubFiles,\n listGitHubFilesRecursive,\n listGitHubBranchRefsByPrefix,\n resolveContentBranch,\n isProductionMode,\n} from '../github-public';\n\nexport {\n assertGitHubConfig,\n getPublicOctokits,\n getPublishedPointerRef,\n readGitHubFilePublic,\n listGitHubFiles,\n listGitHubFilesRecursive,\n listGitHubBranchRefsByPrefix,\n resolveContentBranch,\n isProductionMode,\n};\n\nconst getErrorMessage = (error: unknown): string => {\n if (error instanceof Error) {\n return error.message;\n }\n\n return String(error);\n};\n\nconst formatGitHubApiError = (error: any): string => {\n const status = error?.status;\n const message = error?.response?.data?.message || error?.message || getErrorMessage(error);\n\n if (status) {\n return `${status} ${message}`;\n }\n\n return message;\n};\n\nconst getDefaultBranchRef = async (octokit: Octokit, owner: string, repo: string): Promise<string> => {\n const { data: repoData } = await octokit.rest.repos.get({ owner, repo });\n const defaultBranch = repoData.default_branch || 'main';\n const { data: defaultRef } = await octokit.rest.git.getRef({\n owner,\n repo,\n ref: `heads/${defaultBranch}`,\n });\n\n return defaultRef.object.sha;\n};\n\nconst ensureBranchExists = async (octokit: Octokit, owner: string, repo: string, branch: string) => {\n try {\n await octokit.rest.git.getRef({\n owner,\n repo,\n ref: `heads/${branch}`,\n });\n } catch (error: any) {\n if (error.status !== 404) {\n throw error;\n }\n\n const baseSha = await getDefaultBranchRef(octokit, owner, repo);\n\n await octokit.rest.git.createRef({\n owner,\n repo,\n ref: `refs/heads/${branch}`,\n sha: baseSha,\n });\n }\n};\n\nconst getOctokit = async () => {\n const session = await getServerSession(authOptions);\n const accessToken = (session as any)?.accessToken;\n\n if (!accessToken) {\n throw new Error('No GitHub access token found. Please sign in again.');\n }\n\n return new Octokit({ auth: accessToken });\n};\n\n/**\n * Get an Octokit client suitable for write operations (save, delete, create PR).\n * Prefers CMS_GITHUB_TOKEN so it works even when the GitHub App is not installed\n * on the target repo. Falls back to the user session token.\n */\nconst getWriteOctokit = async (): Promise<Octokit> => {\n const token = process.env.CMS_GITHUB_TOKEN?.trim();\n\n if (token) {\n return new Octokit({ auth: token });\n }\n\n return getOctokit();\n};\n\n/**\n * Read a binary file from GitHub using a static server token (no user session required).\n * If `branch` is provided, reads from that branch directly (used by the media route\n * so editors see uploads on their active feature branch before publishing).\n * Otherwise uses {@link resolveContentBranch} so assets match the same ref as\n * `query()` / public JSON (not only `git.baseBranch`).\n * Returns a Buffer, or null if the file does not exist.\n */\nexport const readGitHubBinaryFilePublic = async (filePath: string, branch?: string): Promise<Buffer | null> => {\n const { owner, repo } = assertGitHubConfig();\n const ref = branch ?? (await resolveContentBranch());\n const clients = getPublicOctokits();\n\n for (const octokit of clients) {\n try {\n const { data } = await octokit.rest.repos.getContent({\n owner,\n repo,\n path: filePath,\n ref,\n });\n\n if ('content' in data && data.type === 'file') {\n return Buffer.from(data.content.replace(/\\n/g, ''), 'base64');\n }\n\n return null;\n } catch (error: any) {\n if (error.status === 429) {\n throw mapGitHubApiErrorToContentSource(error, { owner, repo, ref });\n }\n\n if (error.status === 401 || error.status === 403 || error.status === 404) {\n continue;\n }\n\n throw mapGitHubApiErrorToContentSource(error, { owner, repo, ref });\n }\n }\n\n return null;\n};\n\n/**\n * Get a file's content and SHA from the GitHub repo.\n * Returns { content, sha } or null if the file doesn't exist.\n */\nexport const getGitHubFile = async (\n filePath: string,\n branch?: string,\n): Promise<{ content: string; sha: string } | null> => {\n const [octokit] = getPublicOctokits();\n const { owner, repo, branch: configBranch } = assertGitHubConfig();\n const ref = branch || configBranch;\n\n try {\n const { data } = await octokit.rest.repos.getContent({\n owner,\n repo,\n path: filePath,\n ref,\n });\n\n if ('content' in data && data.type === 'file') {\n const content = Buffer.from(data.content, 'base64').toString('utf-8');\n return { content, sha: data.sha };\n }\n\n return null;\n } catch (error: any) {\n if (error.status === 404) {\n return null;\n }\n throw error;\n }\n};\n\n/**\n * Create or update a file in the GitHub repo via a commit.\n *\n * @param existingSha Optional SHA of the existing file blob. When provided from\n * the content store, skips the pre-read API call (saves 1 request per write).\n */\nexport const saveGitHubFile = async (\n filePath: string,\n content: string,\n message: string,\n branch?: string,\n existingSha?: string,\n) => {\n const octokit = await getWriteOctokit();\n const { owner, repo, branch: configBranch } = assertGitHubConfig();\n const targetBranch = branch || configBranch;\n\n await ensureBranchExists(octokit, owner, repo, targetBranch);\n\n // Get existing file SHA if it exists (required for updates) — skip when caller provides it\n const existing = existingSha ? { sha: existingSha } : await getGitHubFile(filePath, targetBranch);\n\n try {\n await octokit.rest.repos.createOrUpdateFileContents({\n owner,\n repo,\n path: filePath,\n message,\n content: Buffer.from(content).toString('base64'),\n branch: targetBranch,\n ...(existing ? { sha: existing.sha } : {}),\n });\n } catch (error: any) {\n if (error.status === 403) {\n throw new Error(\n 'GitHub token is missing repository write permissions. Ensure the app is installed on the repo and has Contents: Read and write permission.',\n );\n }\n\n if (error.status === 409) {\n throw new Error(\n 'GitHub rejected the commit due to branch protection or write conflicts. Use a writable branch (for example cms/edits) or relax direct-push protection on main.',\n );\n }\n\n if (error.status === 422) {\n throw new Error(`GitHub validation failed while saving content: ${formatGitHubApiError(error)}`);\n }\n\n throw new Error(`GitHub save failed: ${formatGitHubApiError(error)}`);\n }\n};\n\n/**\n * Create or update a binary file in the GitHub repo via a commit.\n * Unlike saveGitHubFile, this accepts a Buffer directly to avoid encoding corruption.\n */\nexport const saveGitHubBinaryFile = async (filePath: string, buffer: Buffer, message: string, branch?: string) => {\n const octokit = await getWriteOctokit();\n const { owner, repo, branch: configBranch } = assertGitHubConfig();\n const targetBranch = branch || configBranch;\n\n await ensureBranchExists(octokit, owner, repo, targetBranch);\n\n const existing = await getGitHubFile(filePath, targetBranch);\n\n try {\n await octokit.rest.repos.createOrUpdateFileContents({\n owner,\n repo,\n path: filePath,\n message,\n content: buffer.toString('base64'),\n branch: targetBranch,\n ...(existing ? { sha: existing.sha } : {}),\n });\n } catch (error: any) {\n if (error.status === 403) {\n throw new Error(\n 'GitHub token is missing repository write permissions. Ensure the app is installed on the repo and has Contents: Read and write permission.',\n );\n }\n\n if (error.status === 409) {\n throw new Error(\n 'GitHub rejected the commit due to branch protection or write conflicts. Use a writable branch (for example cms/edits) or relax direct-push protection on main.',\n );\n }\n\n if (error.status === 422) {\n throw new Error(`GitHub validation failed while saving content: ${formatGitHubApiError(error)}`);\n }\n\n throw new Error(`GitHub save failed: ${formatGitHubApiError(error)}`);\n }\n};\n\n/**\n * A single file change in a multi-file commit. `delete` removes the path\n * from the tree; `upsert-text` and `upsert-binary` create or update it.\n */\nexport type GitHubBatchChange =\n | { kind: 'upsert-text'; path: string; content: string }\n | { kind: 'upsert-binary'; path: string; content: Buffer }\n | { kind: 'delete'; path: string };\n\n/**\n * Commit multiple file changes atomically as a single GitHub commit on\n * `branch`. Used by the visual schema editor to write `cms/schema.json`\n * plus regenerated artifacts plus migrated entry files in one commit.\n *\n * Implementation: builds a git tree off the branch's current HEAD, creates a\n * single commit, then fast-forwards the branch. If `branch` does not exist,\n * it is created from `git.baseBranch` first (mirrors `ensureBranchExists`).\n *\n * Throws if all `changes` are no-ops (e.g. all deletes target missing paths)\n * — GitHub rejects empty trees.\n */\nexport const commitMultipleFilesToGitHub = async (\n changes: readonly GitHubBatchChange[],\n message: string,\n branch?: string,\n): Promise<{ sha: string }> => {\n if (changes.length === 0) {\n throw new Error('commitMultipleFilesToGitHub: no changes provided');\n }\n\n const octokit = await getWriteOctokit();\n const { owner, repo, branch: configBranch } = assertGitHubConfig();\n const targetBranch = branch || configBranch;\n\n await ensureBranchExists(octokit, owner, repo, targetBranch);\n\n const { data: ref } = await octokit.rest.git.getRef({\n owner,\n repo,\n ref: `heads/${targetBranch}`,\n });\n const headSha = ref.object.sha;\n\n const { data: headCommit } = await octokit.rest.git.getCommit({\n owner,\n repo,\n commit_sha: headSha,\n });\n const baseTreeSha = headCommit.tree.sha;\n\n const tree: {\n path: string;\n mode: '100644';\n type: 'blob';\n sha?: string | null;\n content?: string;\n }[] = [];\n\n for (const change of changes) {\n if (change.kind === 'delete') {\n tree.push({ path: change.path, mode: '100644', type: 'blob', sha: null });\n continue;\n }\n if (change.kind === 'upsert-text') {\n // Push content directly. GitHub will create a blob server-side.\n tree.push({ path: change.path, mode: '100644', type: 'blob', content: change.content });\n continue;\n }\n // Binary: must upload as a blob first, then reference its SHA.\n const { data: blob } = await octokit.rest.git.createBlob({\n owner,\n repo,\n content: change.content.toString('base64'),\n encoding: 'base64',\n });\n tree.push({ path: change.path, mode: '100644', type: 'blob', sha: blob.sha });\n }\n\n let newTreeSha: string;\n try {\n const { data: newTree } = await octokit.rest.git.createTree({\n owner,\n repo,\n base_tree: baseTreeSha,\n tree,\n });\n newTreeSha = newTree.sha;\n } catch (error: any) {\n throw new Error(`GitHub createTree failed: ${formatGitHubApiError(error)}`);\n }\n\n if (newTreeSha === baseTreeSha) {\n throw new Error('commitMultipleFilesToGitHub: changes produced no diff (every file was already up to date)');\n }\n\n const { data: newCommit } = await octokit.rest.git.createCommit({\n owner,\n repo,\n message,\n tree: newTreeSha,\n parents: [headSha],\n });\n\n try {\n await octokit.rest.git.updateRef({\n owner,\n repo,\n ref: `heads/${targetBranch}`,\n sha: newCommit.sha,\n });\n } catch (error: any) {\n if (error.status === 422) {\n throw new Error(\n `Branch \"${targetBranch}\" moved while committing. Retry the schema save: ${formatGitHubApiError(error)}`,\n );\n }\n throw new Error(`GitHub updateRef failed: ${formatGitHubApiError(error)}`);\n }\n\n return { sha: newCommit.sha };\n};\n\n/**\n * Delete a file from the GitHub repo via a commit.\n */\nexport const deleteGitHubFile = async (filePath: string, message: string, branch?: string) => {\n const octokit = await getWriteOctokit();\n const { owner, repo, branch: configBranch } = assertGitHubConfig();\n const targetBranch = branch || configBranch;\n\n const existing = await getGitHubFile(filePath, targetBranch);\n\n if (!existing) {\n return; // File doesn't exist, nothing to delete\n }\n\n await octokit.rest.repos.deleteFile({\n owner,\n repo,\n path: filePath,\n message,\n sha: existing.sha,\n branch: targetBranch,\n });\n};\n\n/**\n * Create a new branch in the GitHub repo from `config.git.baseBranch`.\n */\nexport const createGitHubBranch = async (branchName: string): Promise<void> => {\n const octokit = await getWriteOctokit();\n const { owner, repo, branch: baseBranch } = assertGitHubConfig();\n\n const { data: baseRef } = await octokit.rest.git.getRef({\n owner,\n repo,\n ref: `heads/${baseBranch}`,\n });\n\n await octokit.rest.git.createRef({\n owner,\n repo,\n ref: `refs/heads/${branchName}`,\n sha: baseRef.object.sha,\n });\n};\n\nconst ensureLabelExists = async (octokit: Octokit, owner: string, repo: string, name: string, color: string) => {\n try {\n await octokit.rest.issues.getLabel({ owner, repo, name });\n } catch (error: any) {\n if (error.status === 404) {\n await octokit.rest.issues.createLabel({\n owner,\n repo,\n name,\n color,\n description: 'Content updates from the CMS editor',\n });\n } else {\n throw error;\n }\n }\n};\n\n/**\n * Create a draft pull request for a CMS branch, adding the 'cms-update' label.\n */\nexport const createGitHubCMSPullRequest = async (\n headBranch: string,\n title: string,\n body: string,\n targetBranch: string,\n): Promise<{ url: string; number: number }> => {\n const octokit = await getWriteOctokit();\n const { owner, repo } = assertGitHubConfig();\n\n let data: { html_url: string; number: number };\n\n try {\n const res = await octokit.rest.pulls.create({\n owner,\n repo,\n title,\n body,\n head: headBranch,\n base: targetBranch,\n draft: true,\n });\n data = res.data;\n } catch (error: any) {\n const detail = formatGitHubApiError(error);\n logCmsServerError({\n operation: 'pulls.create',\n branch: headBranch,\n status: error?.status,\n message: detail,\n });\n\n if (error?.status === 422) {\n const lower = detail.toLowerCase();\n if (lower.includes('no commits between')) {\n throw new Error(\n 'Could not open draft pull request: GitHub reports no new commits on this branch versus the base branch. Save branch metadata should have created a commit — please try again or open a pull request manually on GitHub.',\n );\n }\n\n throw new Error(`Could not open draft pull request: ${detail}`);\n }\n\n throw new Error(`Could not open draft pull request: ${detail}`);\n }\n\n try {\n await ensureLabelExists(octokit, owner, repo, 'cms-update', '0075ca');\n await octokit.rest.issues.addLabels({\n owner,\n repo,\n issue_number: data.number,\n labels: ['cms-update'],\n });\n } catch (_e) {\n // Label is cosmetic — don't fail branch creation if labelling errors\n }\n\n return { url: data.html_url, number: data.number };\n};\n\n/**\n * List all open PRs tagged with the 'cms-update' label.\n */\nexport const listGitHubCMSPullRequests = async (): Promise<\n { branch: string; prUrl: string; prNumber: number; title: string }[]\n> => {\n const octokit = await getWriteOctokit();\n const { owner, repo } = assertGitHubConfig();\n\n const { data: prs } = await octokit.rest.pulls.list({\n owner,\n repo,\n state: 'open',\n per_page: 100,\n });\n\n return prs\n .filter((pr) => pr.labels.some((l) => l.name === 'cms-update'))\n .map((pr) => ({\n branch: pr.head.ref,\n prUrl: pr.html_url,\n prNumber: pr.number,\n title: pr.title,\n }));\n};\n\nexport type RecentCMSPullRequestState = 'open' | 'merged' | 'closed';\n\n/**\n * List the most-recently-updated PRs tagged with the `cms-update` label,\n * across all states (open / merged / closed). Used by the dashboard\n * \"Recent pull requests\" card.\n */\nexport const listGitHubRecentCMSPullRequests = async (\n limit = 5,\n): Promise<\n {\n branch: string;\n prUrl: string;\n prNumber: number;\n title: string;\n state: RecentCMSPullRequestState;\n updatedAt: string;\n authorLogin: string | null;\n authorAvatarUrl: string | null;\n }[]\n> => {\n const octokit = await getWriteOctokit();\n const { owner, repo } = assertGitHubConfig();\n\n const { data: prs } = await octokit.rest.pulls.list({\n owner,\n repo,\n state: 'all',\n sort: 'updated',\n direction: 'desc',\n per_page: 50,\n });\n\n return prs\n .filter((pr) => pr.labels.some((l) => l.name === 'cms-update'))\n .slice(0, Math.max(1, limit))\n .map((pr) => {\n const state: RecentCMSPullRequestState = pr.merged_at ? 'merged' : pr.state === 'open' ? 'open' : 'closed';\n return {\n branch: pr.head.ref,\n prUrl: pr.html_url,\n prNumber: pr.number,\n title: pr.title,\n state,\n updatedAt: pr.updated_at,\n authorLogin: pr.user?.login ?? null,\n authorAvatarUrl: pr.user?.avatar_url ?? null,\n };\n });\n};\n\n/**\n * Create a pull request from the configured branch to the target branch.\n */\nexport const createGitHubPullRequest = async (\n title: string,\n body: string,\n targetBranch: string = 'main',\n): Promise<{ url: string; number: number }> => {\n const octokit = await getWriteOctokit();\n const { owner, repo, branch } = assertGitHubConfig();\n\n if (branch === targetBranch) {\n throw new Error(`Cannot create PR: source branch \"${branch}\" is the same as target branch \"${targetBranch}\"`);\n }\n\n const { data } = await octokit.rest.pulls.create({\n owner,\n repo,\n title,\n body,\n head: branch,\n base: targetBranch,\n });\n\n return { url: data.html_url, number: data.number };\n};\n\n/**\n * Merge a pull request via squash.\n */\nexport const mergePullRequest = async (prNumber: number): Promise<void> => {\n const octokit = await getWriteOctokit();\n const { owner, repo } = assertGitHubConfig();\n\n await octokit.rest.pulls.merge({\n owner,\n repo,\n pull_number: prNumber,\n merge_method: 'squash',\n });\n};\n\n/**\n * Convert a draft pull request for a CMS branch to \"Ready for Review\".\n * No-op if no open PR exists for the branch or if it is already non-draft.\n */\nexport const markPRReadyForReview = async (branchName: string): Promise<void> => {\n const octokit = await getWriteOctokit();\n const { owner, repo } = assertGitHubConfig();\n\n const { data: prs } = await octokit.rest.pulls.list({\n owner,\n repo,\n head: `${owner}:${branchName}`,\n state: 'open',\n per_page: 1,\n });\n\n const pr = prs[0];\n if (!pr || !pr.draft) return;\n\n await octokit.graphql(\n `mutation MarkReadyForReview($pullRequestId: ID!) {\n markPullRequestReadyForReview(input: { pullRequestId: $pullRequestId }) {\n pullRequest { isDraft }\n }\n }`,\n { pullRequestId: pr.node_id },\n );\n};\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Most-recently-updated PRs tagged `cms-update`, across all states. Powers the
|
|
3
|
+
* dashboard "Recent pull requests" card. Tier `realtime` — PR state is
|
|
4
|
+
* mutated outside the admin UI (review, merge, close).
|
|
5
|
+
*/
|
|
6
|
+
export declare function useRecentCMSPullRequests(limit?: number): import("@tanstack/react-query").UseQueryResult<import("../../actions").RecentCMSPullRequest[], Error>;
|
|
7
|
+
//# sourceMappingURL=useRecentCMSPullRequests.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRecentCMSPullRequests.d.ts","sourceRoot":"","sources":["../../../../admin/query/hooks/useRecentCMSPullRequests.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,SAAI,yGAMjD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import "../../../chunk-B5LE2OEC.js";
|
|
3
|
+
import { getRecentCMSPullRequests } from "../../actions/git";
|
|
4
|
+
import { queryKeys } from "../keys";
|
|
5
|
+
import { useAdminQuery } from "../useAdminQuery";
|
|
6
|
+
function useRecentCMSPullRequests(limit = 5) {
|
|
7
|
+
return useAdminQuery({
|
|
8
|
+
queryKey: queryKeys.git.recentCMSPRs(limit),
|
|
9
|
+
queryFn: () => getRecentCMSPullRequests(limit),
|
|
10
|
+
tier: "realtime"
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
useRecentCMSPullRequests
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=useRecentCMSPullRequests.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../admin/query/hooks/useRecentCMSPullRequests.ts"],"sourcesContent":["'use client';\n\nimport { getRecentCMSPullRequests } from '../../actions/git';\nimport { queryKeys } from '../keys';\nimport { useAdminQuery } from '../useAdminQuery';\n\n/**\n * Most-recently-updated PRs tagged `cms-update`, across all states. Powers the\n * dashboard \"Recent pull requests\" card. Tier `realtime` — PR state is\n * mutated outside the admin UI (review, merge, close).\n */\nexport function useRecentCMSPullRequests(limit = 5) {\n return useAdminQuery({\n queryKey: queryKeys.git.recentCMSPRs(limit),\n queryFn: () => getRecentCMSPullRequests(limit),\n tier: 'realtime',\n });\n}\n"],"mappings":";;AAEA,SAAS,gCAAgC;AACzC,SAAS,iBAAiB;AAC1B,SAAS,qBAAqB;AAOvB,SAAS,yBAAyB,QAAQ,GAAG;AAClD,SAAO,cAAc;AAAA,IACnB,UAAU,UAAU,IAAI,aAAa,KAAK;AAAA,IAC1C,SAAS,MAAM,yBAAyB,KAAK;AAAA,IAC7C,MAAM;AAAA,EACR,CAAC;AACH;","names":[]}
|
|
@@ -31,6 +31,7 @@ export declare const queryKeys: {
|
|
|
31
31
|
readonly hasActive: () => readonly ["git", "hasActive"];
|
|
32
32
|
readonly branches: () => readonly ["git", "branches"];
|
|
33
33
|
readonly isProduction: () => readonly ["git", "isProduction"];
|
|
34
|
+
readonly recentCMSPRs: (limit?: number) => readonly ["git", "recentCMSPRs", number];
|
|
34
35
|
};
|
|
35
36
|
readonly agent: {
|
|
36
37
|
readonly all: readonly ["agent"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../../admin/query/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS;;;qCAGE,MAAM;oCACP,MAAM;qCACL,MAAM;2CACA,MAAM;kCACf,MAAM;;;;;6BAKX,MAAM;;;;;sCAKG,MAAM
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../../admin/query/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS;;;qCAGE,MAAM;oCACP,MAAM;qCACL,MAAM;2CACA,MAAM;kCACf,MAAM;;;;;6BAKX,MAAM;;;;;sCAKG,MAAM;;;;;;;;wCAQJ,MAAM;;;;;;CAMvB,CAAC"}
|
package/dist/admin/query/keys.js
CHANGED
|
@@ -23,7 +23,8 @@ const queryKeys = {
|
|
|
23
23
|
branch: () => ["git", "branch"],
|
|
24
24
|
hasActive: () => ["git", "hasActive"],
|
|
25
25
|
branches: () => ["git", "branches"],
|
|
26
|
-
isProduction: () => ["git", "isProduction"]
|
|
26
|
+
isProduction: () => ["git", "isProduction"],
|
|
27
|
+
recentCMSPRs: (limit) => ["git", "recentCMSPRs", limit != null ? limit : 5]
|
|
27
28
|
},
|
|
28
29
|
agent: {
|
|
29
30
|
all: ["agent"],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../admin/query/keys.ts"],"sourcesContent":["/**\n * Centralized query-key registry.\n *\n * Keys are tuples; the first element is the **domain** so a coarse\n * `invalidateQueries({ queryKey: ['entries'] })` covers every entries-related\n * query at once. Add new keys here rather than ad-hoc inline tuples — the\n * `invalidateAfterMutation` helper in `./invalidate.ts` reads this object.\n */\nexport const queryKeys = {\n entries: {\n all: ['entries'] as const,\n list: (collection?: string) => ['entries', 'list', collection ?? '*'] as const,\n detail: (filePath: string) => ['entries', 'detail', filePath] as const,\n commits: (filePath: string) => ['entries', 'commits', filePath] as const,\n backlinks: (referenceKey: string) => ['entries', 'backlinks', referenceKey] as const,\n diff: (filePath: string) => ['entries', 'diff', filePath] as const,\n },\n media: {\n all: ['media'] as const,\n list: () => ['media', 'list'] as const,\n asset: (id: string) => ['media', 'asset', id] as const,\n },\n schema: {\n all: ['schema'] as const,\n current: () => ['schema', 'current'] as const,\n impact: (proposalId: string) => ['schema', 'impact', proposalId] as const,\n },\n git: {\n all: ['git'] as const,\n branch: () => ['git', 'branch'] as const,\n hasActive: () => ['git', 'hasActive'] as const,\n branches: () => ['git', 'branches'] as const,\n isProduction: () => ['git', 'isProduction'] as const,\n },\n agent: {\n all: ['agent'] as const,\n status: () => ['agent', 'status'] as const,\n },\n} as const;\n"],"mappings":";AAQO,MAAM,YAAY;AAAA,EACvB,SAAS;AAAA,IACP,KAAK,CAAC,SAAS;AAAA,IACf,MAAM,CAAC,eAAwB,CAAC,WAAW,QAAQ,kCAAc,GAAG;AAAA,IACpE,QAAQ,CAAC,aAAqB,CAAC,WAAW,UAAU,QAAQ;AAAA,IAC5D,SAAS,CAAC,aAAqB,CAAC,WAAW,WAAW,QAAQ;AAAA,IAC9D,WAAW,CAAC,iBAAyB,CAAC,WAAW,aAAa,YAAY;AAAA,IAC1E,MAAM,CAAC,aAAqB,CAAC,WAAW,QAAQ,QAAQ;AAAA,EAC1D;AAAA,EACA,OAAO;AAAA,IACL,KAAK,CAAC,OAAO;AAAA,IACb,MAAM,MAAM,CAAC,SAAS,MAAM;AAAA,IAC5B,OAAO,CAAC,OAAe,CAAC,SAAS,SAAS,EAAE;AAAA,EAC9C;AAAA,EACA,QAAQ;AAAA,IACN,KAAK,CAAC,QAAQ;AAAA,IACd,SAAS,MAAM,CAAC,UAAU,SAAS;AAAA,IACnC,QAAQ,CAAC,eAAuB,CAAC,UAAU,UAAU,UAAU;AAAA,EACjE;AAAA,EACA,KAAK;AAAA,IACH,KAAK,CAAC,KAAK;AAAA,IACX,QAAQ,MAAM,CAAC,OAAO,QAAQ;AAAA,IAC9B,WAAW,MAAM,CAAC,OAAO,WAAW;AAAA,IACpC,UAAU,MAAM,CAAC,OAAO,UAAU;AAAA,IAClC,cAAc,MAAM,CAAC,OAAO,cAAc;AAAA,
|
|
1
|
+
{"version":3,"sources":["../../../admin/query/keys.ts"],"sourcesContent":["/**\n * Centralized query-key registry.\n *\n * Keys are tuples; the first element is the **domain** so a coarse\n * `invalidateQueries({ queryKey: ['entries'] })` covers every entries-related\n * query at once. Add new keys here rather than ad-hoc inline tuples — the\n * `invalidateAfterMutation` helper in `./invalidate.ts` reads this object.\n */\nexport const queryKeys = {\n entries: {\n all: ['entries'] as const,\n list: (collection?: string) => ['entries', 'list', collection ?? '*'] as const,\n detail: (filePath: string) => ['entries', 'detail', filePath] as const,\n commits: (filePath: string) => ['entries', 'commits', filePath] as const,\n backlinks: (referenceKey: string) => ['entries', 'backlinks', referenceKey] as const,\n diff: (filePath: string) => ['entries', 'diff', filePath] as const,\n },\n media: {\n all: ['media'] as const,\n list: () => ['media', 'list'] as const,\n asset: (id: string) => ['media', 'asset', id] as const,\n },\n schema: {\n all: ['schema'] as const,\n current: () => ['schema', 'current'] as const,\n impact: (proposalId: string) => ['schema', 'impact', proposalId] as const,\n },\n git: {\n all: ['git'] as const,\n branch: () => ['git', 'branch'] as const,\n hasActive: () => ['git', 'hasActive'] as const,\n branches: () => ['git', 'branches'] as const,\n isProduction: () => ['git', 'isProduction'] as const,\n recentCMSPRs: (limit?: number) => ['git', 'recentCMSPRs', limit ?? 5] as const,\n },\n agent: {\n all: ['agent'] as const,\n status: () => ['agent', 'status'] as const,\n },\n} as const;\n"],"mappings":";AAQO,MAAM,YAAY;AAAA,EACvB,SAAS;AAAA,IACP,KAAK,CAAC,SAAS;AAAA,IACf,MAAM,CAAC,eAAwB,CAAC,WAAW,QAAQ,kCAAc,GAAG;AAAA,IACpE,QAAQ,CAAC,aAAqB,CAAC,WAAW,UAAU,QAAQ;AAAA,IAC5D,SAAS,CAAC,aAAqB,CAAC,WAAW,WAAW,QAAQ;AAAA,IAC9D,WAAW,CAAC,iBAAyB,CAAC,WAAW,aAAa,YAAY;AAAA,IAC1E,MAAM,CAAC,aAAqB,CAAC,WAAW,QAAQ,QAAQ;AAAA,EAC1D;AAAA,EACA,OAAO;AAAA,IACL,KAAK,CAAC,OAAO;AAAA,IACb,MAAM,MAAM,CAAC,SAAS,MAAM;AAAA,IAC5B,OAAO,CAAC,OAAe,CAAC,SAAS,SAAS,EAAE;AAAA,EAC9C;AAAA,EACA,QAAQ;AAAA,IACN,KAAK,CAAC,QAAQ;AAAA,IACd,SAAS,MAAM,CAAC,UAAU,SAAS;AAAA,IACnC,QAAQ,CAAC,eAAuB,CAAC,UAAU,UAAU,UAAU;AAAA,EACjE;AAAA,EACA,KAAK;AAAA,IACH,KAAK,CAAC,KAAK;AAAA,IACX,QAAQ,MAAM,CAAC,OAAO,QAAQ;AAAA,IAC9B,WAAW,MAAM,CAAC,OAAO,WAAW;AAAA,IACpC,UAAU,MAAM,CAAC,OAAO,UAAU;AAAA,IAClC,cAAc,MAAM,CAAC,OAAO,cAAc;AAAA,IAC1C,cAAc,CAAC,UAAmB,CAAC,OAAO,gBAAgB,wBAAS,CAAC;AAAA,EACtE;AAAA,EACA,OAAO;AAAA,IACL,KAAK,CAAC,OAAO;AAAA,IACb,QAAQ,MAAM,CAAC,SAAS,QAAQ;AAAA,EAClC;AACF;","names":[]}
|
package/dist/agent/index.cjs
CHANGED
|
@@ -836,6 +836,7 @@ __export(github_exports, {
|
|
|
836
836
|
listGitHubCMSPullRequests: () => listGitHubCMSPullRequests,
|
|
837
837
|
listGitHubFiles: () => listGitHubFiles,
|
|
838
838
|
listGitHubFilesRecursive: () => listGitHubFilesRecursive,
|
|
839
|
+
listGitHubRecentCMSPullRequests: () => listGitHubRecentCMSPullRequests,
|
|
839
840
|
markPRReadyForReview: () => markPRReadyForReview,
|
|
840
841
|
mergePullRequest: () => mergePullRequest,
|
|
841
842
|
readGitHubBinaryFilePublic: () => readGitHubBinaryFilePublic,
|
|
@@ -844,7 +845,7 @@ __export(github_exports, {
|
|
|
844
845
|
saveGitHubBinaryFile: () => saveGitHubBinaryFile,
|
|
845
846
|
saveGitHubFile: () => saveGitHubFile
|
|
846
847
|
});
|
|
847
|
-
var import_octokit2, import_next_auth, getErrorMessage, formatGitHubApiError, getDefaultBranchRef, ensureBranchExists, getOctokit, getWriteOctokit, readGitHubBinaryFilePublic, getGitHubFile, saveGitHubFile, saveGitHubBinaryFile, commitMultipleFilesToGitHub, deleteGitHubFile, createGitHubBranch, ensureLabelExists, createGitHubCMSPullRequest, listGitHubCMSPullRequests, createGitHubPullRequest, mergePullRequest, markPRReadyForReview;
|
|
848
|
+
var import_octokit2, import_next_auth, getErrorMessage, formatGitHubApiError, getDefaultBranchRef, ensureBranchExists, getOctokit, getWriteOctokit, readGitHubBinaryFilePublic, getGitHubFile, saveGitHubFile, saveGitHubBinaryFile, commitMultipleFilesToGitHub, deleteGitHubFile, createGitHubBranch, ensureLabelExists, createGitHubCMSPullRequest, listGitHubCMSPullRequests, listGitHubRecentCMSPullRequests, createGitHubPullRequest, mergePullRequest, markPRReadyForReview;
|
|
848
849
|
var init_github = __esm({
|
|
849
850
|
"admin/github.ts"() {
|
|
850
851
|
"use strict";
|
|
@@ -1218,6 +1219,32 @@ var init_github = __esm({
|
|
|
1218
1219
|
title: pr.title
|
|
1219
1220
|
}));
|
|
1220
1221
|
};
|
|
1222
|
+
listGitHubRecentCMSPullRequests = async (limit = 5) => {
|
|
1223
|
+
const octokit = await getWriteOctokit();
|
|
1224
|
+
const { owner, repo } = assertGitHubConfig();
|
|
1225
|
+
const { data: prs } = await octokit.rest.pulls.list({
|
|
1226
|
+
owner,
|
|
1227
|
+
repo,
|
|
1228
|
+
state: "all",
|
|
1229
|
+
sort: "updated",
|
|
1230
|
+
direction: "desc",
|
|
1231
|
+
per_page: 50
|
|
1232
|
+
});
|
|
1233
|
+
return prs.filter((pr) => pr.labels.some((l) => l.name === "cms-update")).slice(0, Math.max(1, limit)).map((pr) => {
|
|
1234
|
+
var _a, _b, _c, _d;
|
|
1235
|
+
const state2 = pr.merged_at ? "merged" : pr.state === "open" ? "open" : "closed";
|
|
1236
|
+
return {
|
|
1237
|
+
branch: pr.head.ref,
|
|
1238
|
+
prUrl: pr.html_url,
|
|
1239
|
+
prNumber: pr.number,
|
|
1240
|
+
title: pr.title,
|
|
1241
|
+
state: state2,
|
|
1242
|
+
updatedAt: pr.updated_at,
|
|
1243
|
+
authorLogin: (_b = (_a = pr.user) == null ? void 0 : _a.login) != null ? _b : null,
|
|
1244
|
+
authorAvatarUrl: (_d = (_c = pr.user) == null ? void 0 : _c.avatar_url) != null ? _d : null
|
|
1245
|
+
};
|
|
1246
|
+
});
|
|
1247
|
+
};
|
|
1221
1248
|
createGitHubPullRequest = async (title, body, targetBranch = "main") => {
|
|
1222
1249
|
const octokit = await getWriteOctokit();
|
|
1223
1250
|
const { owner, repo, branch } = assertGitHubConfig();
|