Skip to content

Commit d23c084

Browse files
committed
update
1 parent 91e7818 commit d23c084

5 files changed

Lines changed: 127 additions & 64 deletions

File tree

src/App.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,8 @@ export default function App() {
350350
const publishedDraftId = draftId
351351
const s = slug.trim() || slugify(title) || 'post'
352352
const path = postRepoPath(s)
353-
const { indexHomeBanner, indexErrorToast, successMessage } = await publishPostAndIndex({
353+
const { indexHomeBanner, indexErrorToast, successMessage, workflowWarning } =
354+
await publishPostAndIndex({
354355
form,
355356
path,
356357
slug: s,
@@ -362,6 +363,7 @@ export default function App() {
362363
})
363364
setIndexHomeBanner(indexHomeBanner)
364365
if (indexErrorToast) pushToast(indexErrorToast)
366+
if (workflowWarning) pushToast(workflowWarning)
365367

366368
setGithubSettings({ ...form })
367369
if (!persistGithubSettings({ ...form })) {
Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,30 @@
11
import { describe, expect, it } from 'vitest'
2+
import { PAGES_WORKFLOW } from '../blogPaths'
3+
import {
4+
buildPagesWorkflowYaml,
5+
formatBootstrapStatusMessage,
6+
} from '../repoSiteBootstrap'
27
import { READ_ONLY_TEMPLATES } from '../readOnlyTemplates'
38

4-
describe('pages workflow template', () => {
5-
it('deploys the blog folder', () => {
6-
const yml = READ_ONLY_TEMPLATES.githubPagesWorkflowYaml.replace(/\{\{BLOG_DIR\}\}/g, 'blog')
7-
expect(yml).toContain('path: blog')
9+
describe('buildPagesWorkflowYaml', () => {
10+
it('deploys ./blog and targets the repo-root workflow path', () => {
11+
const yml = buildPagesWorkflowYaml('main')
12+
expect(yml).toContain('path: ./blog')
13+
expect(yml).toContain('branches: [main]')
814
expect(yml).toContain('configure-pages@v5')
15+
expect(yml).toContain('name: Upload blog artifact')
16+
expect(READ_ONLY_TEMPLATES.githubPagesWorkflowYaml).toContain('./blog')
17+
expect(PAGES_WORKFLOW).toBe('.github/workflows/deploy-blog-pages.yml')
18+
})
19+
})
20+
21+
describe('formatBootstrapStatusMessage', () => {
22+
it('lists created and found paths', () => {
23+
const msg = formatBootstrapStatusMessage([
24+
{ path: 'blog/index.html', status: 'created' },
25+
{ path: PAGES_WORKFLOW, status: 'failed' },
26+
])
27+
expect(msg).toContain('blog/index.html created')
28+
expect(msg).toContain(`${PAGES_WORKFLOW} missing`)
929
})
1030
})

src/lib/publishPipeline.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export async function publishPostAndIndex({
7474

7575
let indexHomeBanner = { show: false, text: '' }
7676
let indexErrorToast = null
77+
let workflowWarning = site.workflowWarning
7778

7879
try {
7980
const indexResult = tryUpdateIndexWithCard({
@@ -102,10 +103,16 @@ export async function publishPostAndIndex({
102103
indexErrorToast = `${friendly} Post was saved; blog/index.html was not updated.`
103104
}
104105

105-
let successMessage = `Published ${path}`
106-
if (site.created.length) successMessage += ` · Bootstrapped: ${site.created.join(', ')}`
107-
if (site.warnings[0]) successMessage += ` · ${site.warnings[0]}`
108-
if (site.pagesSetupHint) successMessage += ` ${site.pagesSetupHint}`
106+
let successMessage = `Published ${path} · ${site.bootstrapStatusMessage}`
107+
if (site.pagesSetupHint && site.workflowOk) {
108+
successMessage += ` · ${site.pagesSetupHint}`
109+
}
109110

110-
return { successMessage, indexHomeBanner, indexErrorToast }
111+
return {
112+
successMessage,
113+
workflowWarning,
114+
workflowOk: site.workflowOk,
115+
indexHomeBanner,
116+
indexErrorToast,
117+
}
111118
}

src/lib/repoSiteBootstrap.js

Lines changed: 81 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -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'
98
import { READ_ONLY_TEMPLATES } from './readOnlyTemplates'
109
import { fetchRepoFileText, getFileSha, upsertFile } from './github'
11-
import { getFriendlyGithubError } from './githubFriendlyMessages'
1210
import {
1311
MARKER_END,
1412
MARKER_START,
@@ -19,18 +17,38 @@ import {
1917
export 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-zA-Z0-9._/-]+$/.test(b) ? b : `"${b.replace(/"/g, '\\"')}"`
31-
return READ_ONLY_TEMPLATES.githubPagesWorkflowYaml
32-
.replace(/\{\{BLOG_DIR\}\}/g, BLOG_ROOT)
33-
.replace(/\{\{BRANCH\}\}/g, branchRef)
41+
return READ_ONLY_TEMPLATES.githubPagesWorkflowYaml.replace(/\{\{BRANCH\}\}/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

3654
function 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+
*/
6585
export 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
}

templates/github-pages-blog.yml

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
1-
# Deploys the blog/ folder (blog/index.html as the site homepage) to GitHub Pages.
2-
#
3-
# If GitHub Pages is not set up yet on this repository:
4-
# 1. Push this file to your default branch.
5-
# 2. Repo → Settings → Pages → Build and deployment → Source: "GitHub Actions".
6-
# 3. Re-run this workflow from the Actions tab if the first run fails before step 2.
7-
#
8-
# Project site URL: https://<owner>.github.io/<repo>/
9-
# User/org site (repo named <owner>.github.io): https://<owner>.github.io/
1+
# Deploys blog/ to GitHub Pages (blog/index.html is the site homepage).
2+
# One-time: repo Settings → Pages → Build and deployment → Source → GitHub Actions.
103

114
name: Deploy blog to GitHub Pages
125

@@ -21,24 +14,24 @@ permissions:
2114
id-token: write
2215

2316
concurrency:
24-
group: pages
17+
group: "pages"
2518
cancel-in-progress: false
2619

2720
jobs:
2821
deploy:
29-
runs-on: ubuntu-latest
3022
environment:
3123
name: github-pages
3224
url: ${{ steps.deployment.outputs.page_url }}
25+
runs-on: ubuntu-latest
3326
steps:
3427
- name: Checkout
3528
uses: actions/checkout@v4
36-
- name: Configure GitHub Pages
29+
- name: Setup Pages
3730
uses: actions/configure-pages@v5
38-
- name: Upload site artifact (blog/)
31+
- name: Upload blog artifact
3932
uses: actions/upload-pages-artifact@v3
4033
with:
41-
path: {{BLOG_DIR}}
34+
path: ./blog
4235
- name: Deploy to GitHub Pages
4336
id: deployment
4437
uses: actions/deploy-pages@v4

0 commit comments

Comments
 (0)