@@ -2,13 +2,11 @@ import {
22 BLOG_INDEX ,
33 BLOG_NOJEKYLL ,
44 BLOG_POSTS_GITKEEP ,
5- BLOG_ROOT ,
65 BLOG_STYLE ,
76 PAGES_WORKFLOW ,
87} from './blogPaths'
98import { READ_ONLY_TEMPLATES } from './readOnlyTemplates'
109import { fetchRepoFileText , getFileSha , upsertFile } from './github'
11- import { getFriendlyGithubError } from './githubFriendlyMessages'
1210import {
1311 MARKER_END ,
1412 MARKER_START ,
@@ -19,18 +17,38 @@ import {
1917export const PAGES_SETUP_HINT =
2018 'Enable GitHub Pages: Settings → Pages → Source → GitHub Actions, then run “Deploy blog to GitHub Pages” in Actions.'
2119
22- async function putIfMissing ( { token, owner, repo, branch, path, content, message, created } ) {
23- if ( await getFileSha ( { token, owner, repo, path, branch } ) ) return
24- await upsertFile ( { token, owner, repo, path, branch, content, message, sha : null } )
25- created . push ( path )
20+ export const WORKFLOW_PERMISSION_WARNING =
21+ 'Blog files were created, but the GitHub Pages workflow could not be added. Your GitHub token may need workflow permission. Add workflow permission to your token or create .github/workflows/deploy-blog-pages.yml manually.'
22+
23+ /** @typedef {'created' | 'found' | 'failed' } BootstrapFileStatus */
24+
25+ /**
26+ * @param {Array<{ path: string, status: BootstrapFileStatus }> } fileLog
27+ */
28+ export function formatBootstrapStatusMessage ( fileLog ) {
29+ return fileLog
30+ . map ( ( { path, status } ) => {
31+ if ( status === 'created' ) return `${ path } created`
32+ if ( status === 'found' ) return `${ path } found`
33+ return `${ path } missing`
34+ } )
35+ . join ( ' · ' )
2636}
2737
28- function workflowYaml ( branch ) {
38+ export function buildPagesWorkflowYaml ( branch = 'main' ) {
2939 const b = String ( branch ?? '' ) . trim ( ) || 'main'
3040 const branchRef = / ^ [ a - z A - Z 0 - 9 . _ / - ] + $ / . test ( b ) ? b : `"${ b . replace ( / " / g, '\\"' ) } "`
31- return READ_ONLY_TEMPLATES . githubPagesWorkflowYaml
32- . replace ( / \{ \{ B L O G _ D I R \} \} / g, BLOG_ROOT )
33- . replace ( / \{ \{ B R A N C H \} \} / g, branchRef )
41+ return READ_ONLY_TEMPLATES . githubPagesWorkflowYaml . replace ( / \{ \{ B R A N C H \} \} / g, branchRef )
42+ }
43+
44+ async function recordFile ( { token, owner, repo, branch, path, content, message, fileLog } ) {
45+ const sha = await getFileSha ( { token, owner, repo, path, branch } )
46+ if ( sha ) {
47+ fileLog . push ( { path, status : 'found' } )
48+ return
49+ }
50+ await upsertFile ( { token, owner, repo, path, branch, content, message, sha : null } )
51+ fileLog . push ( { path, status : 'created' } )
3452}
3553
3654function createIndexHtml ( ) {
@@ -61,48 +79,55 @@ function prepareIndex(html) {
6179 return { html : h , modified }
6280}
6381
64- /** Ensure blog/, starter files, and the Pages workflow exist. */
82+ /**
83+ * Create blog/ starter files and .github/workflows/deploy-blog-pages.yml at repo root.
84+ */
6585export async function bootstrapBlogSite ( { token, owner, repo, branch } ) {
66- const created = [ ]
67- const warnings = [ ]
86+ /** @type {Array<{ path: string, status: BootstrapFileStatus }> } */
87+ const fileLog = [ ]
88+ let workflowWarning = null
89+ let workflowOk = false
6890
69- await putIfMissing ( {
91+ await recordFile ( {
7092 token,
7193 owner,
7294 repo,
7395 branch,
7496 path : BLOG_STYLE ,
7597 content : READ_ONLY_TEMPLATES . stylesCss ,
7698 message : 'Add blog/style.css' ,
77- created ,
99+ fileLog ,
78100 } )
79- await putIfMissing ( {
101+ await recordFile ( {
80102 token,
81103 owner,
82104 repo,
83105 branch,
84106 path : BLOG_POSTS_GITKEEP ,
85107 content : '' ,
86108 message : 'Create blog/posts' ,
87- created ,
109+ fileLog ,
88110 } )
89- await putIfMissing ( {
111+ const postsDirStatus = fileLog [ fileLog . length - 1 ] ?. status ?? 'failed'
112+ fileLog . push ( { path : 'blog/posts/' , status : postsDirStatus } )
113+ await recordFile ( {
90114 token,
91115 owner,
92116 repo,
93117 branch,
94118 path : BLOG_NOJEKYLL ,
95119 content : '' ,
96120 message : 'Add blog/.nojekyll' ,
97- created ,
121+ fileLog ,
98122 } )
99123
100124 let indexCreated = false
101125 let indexModified = false
102126 let indexText = ''
103127 let indexSha = null
104128
105- if ( ! ( await getFileSha ( { token, owner, repo, path : BLOG_INDEX , branch } ) ) ) {
129+ const indexShaBefore = await getFileSha ( { token, owner, repo, path : BLOG_INDEX , branch } )
130+ if ( ! indexShaBefore ) {
106131 indexText = createIndexHtml ( )
107132 await upsertFile ( {
108133 token,
@@ -114,10 +139,11 @@ export async function bootstrapBlogSite({ token, owner, repo, branch }) {
114139 message : 'Create blog/index.html' ,
115140 sha : null ,
116141 } )
117- created . push ( BLOG_INDEX )
142+ fileLog . push ( { path : BLOG_INDEX , status : 'created' } )
118143 indexCreated = true
119144 indexSha = await getFileSha ( { token, owner, repo, path : BLOG_INDEX , branch } )
120145 } else {
146+ fileLog . push ( { path : BLOG_INDEX , status : 'found' } )
121147 const res = await fetchRepoFileText ( { token, owner, repo, path : BLOG_INDEX , branch } )
122148 indexText = res . text
123149 indexSha = res . sha
@@ -126,31 +152,46 @@ export async function bootstrapBlogSite({ token, owner, repo, branch }) {
126152 indexModified = prep . modified
127153 }
128154
129- try {
130- await putIfMissing ( {
131- token,
132- owner,
133- repo,
134- branch,
135- path : PAGES_WORKFLOW ,
136- content : workflowYaml ( branch ) ,
137- message : 'Add GitHub Pages workflow' ,
138- created,
139- } )
140- } catch ( err ) {
141- const { friendly } = getFriendlyGithubError ( err , 'publish' )
142- warnings . push ( `${ friendly } Add ${ PAGES_WORKFLOW } manually if needed.` )
155+ // Workflow lives at repo root (.github/workflows/…), not under blog/
156+ const workflowSha = await getFileSha ( { token, owner, repo, path : PAGES_WORKFLOW , branch } )
157+ if ( workflowSha ) {
158+ fileLog . push ( { path : PAGES_WORKFLOW , status : 'found' } )
159+ workflowOk = true
160+ } else {
161+ try {
162+ await upsertFile ( {
163+ token,
164+ owner,
165+ repo,
166+ path : PAGES_WORKFLOW ,
167+ branch,
168+ content : buildPagesWorkflowYaml ( branch ) ,
169+ message : 'Add GitHub Pages workflow for blog/' ,
170+ sha : null ,
171+ } )
172+ const verified = await getFileSha ( { token, owner, repo, path : PAGES_WORKFLOW , branch } )
173+ if ( verified ) {
174+ fileLog . push ( { path : PAGES_WORKFLOW , status : 'created' } )
175+ workflowOk = true
176+ } else {
177+ fileLog . push ( { path : PAGES_WORKFLOW , status : 'failed' } )
178+ workflowWarning = WORKFLOW_PERMISSION_WARNING
179+ }
180+ } catch {
181+ fileLog . push ( { path : PAGES_WORKFLOW , status : 'failed' } )
182+ workflowWarning = WORKFLOW_PERMISSION_WARNING
183+ }
143184 }
144185
145- const workflowCreated = created . includes ( PAGES_WORKFLOW )
146-
147186 return {
148187 indexText,
149188 indexSha,
150189 indexCreated,
151190 indexModified,
152- created,
153- warnings,
154- pagesSetupHint : workflowCreated ? PAGES_SETUP_HINT : null ,
191+ fileLog,
192+ bootstrapStatusMessage : formatBootstrapStatusMessage ( fileLog ) ,
193+ workflowOk,
194+ workflowWarning,
195+ pagesSetupHint : workflowOk ? PAGES_SETUP_HINT : null ,
155196 }
156197}
0 commit comments