Skip to content

Commit cba7855

Browse files
Add GitHub Pages deployment workflow and documentation
Co-authored-by: ADefWebserver <1857799+ADefWebserver@users.noreply.github.com>
1 parent edfea29 commit cba7855

3 files changed

Lines changed: 312 additions & 1 deletion

File tree

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Deploy to GitHub Pages
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: write
11+
12+
jobs:
13+
deploy:
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v4
19+
20+
- name: Setup .NET
21+
uses: actions/setup-dotnet@v4
22+
with:
23+
dotnet-version: '9.x'
24+
25+
- name: Publish Blazor WASM client
26+
run: |
27+
dotnet publish RFPResponsePOC/RFPResponsePOC.Client/RFPResponseAPP.Client.csproj \
28+
--configuration Release \
29+
--output ./release-output
30+
31+
- name: Prepare gh-pages directory
32+
run: |
33+
mkdir -p ./release
34+
cp -r ./release-output/wwwroot/. ./release/
35+
36+
# Disable Jekyll so _framework assets are served correctly
37+
touch ./release/.nojekyll
38+
39+
# Fix base href for GitHub Pages sub-path /RFPAPP/
40+
sed -i 's|<base href="/" />|<base href="/RFPAPP/" />|g' ./release/index.html
41+
42+
# Copy index.html as 404.html for SPA client-side routing fallback
43+
cp ./release/index.html ./release/404.html
44+
45+
- name: Deploy to GitHub Pages
46+
uses: peaceiris/actions-gh-pages@v4
47+
with:
48+
github_token: ${{ secrets.GITHUB_TOKEN }}
49+
publish_dir: ./release
50+
publish_branch: gh-pages

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
# RFP Response Creator
22

3-
![License](https://img.shields.io/badge/license-MIT-blue.svg) ![Tech](https://img.shields.io/badge/Blazor-.NET%209-blueviolet) ![AI Powered](https://img.shields.io/badge/AI-OpenAI-success)
3+
![License](https://img.shields.io/badge/license-MIT-blue.svg) ![Tech](https://img.shields.io/badge/Blazor-.NET%209-blueviolet) ![AI Powered](https://img.shields.io/badge/AI-OpenAI-success) [![GitHub Pages](https://img.shields.io/badge/Demo-GitHub%20Pages-blue?logo=github)](https://blazordata-net.github.io/RFPAPP/)
44

55
🚀 **RFP Response Creator** is a SaaS application that automates professional Request for Proposal (RFP) responses.
66
Using AI, it extracts questions, generates answers from your knowledge base, and produces polished Word/PDF documents.
77
For venues, it includes a **smart scheduling engine** to assign rooms without conflicts.
88

9+
### 🌐 Live Demo (GitHub Pages)
10+
[https://blazordata-net.github.io/RFPAPP/](https://blazordata-net.github.io/RFPAPP/)
11+
912
### Online Live version:
1013
[https://RFP.BlazorData.net](https://RFP.BlazorData.net)
1114

docs/github-pages-deployment.md

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
# GitHub Pages Deployment Plan
2+
3+
## Overview
4+
5+
This document describes the plan for automatically building the **RFP Response Creator** Blazor WebAssembly application and deploying a live demo to GitHub Pages whenever changes are pushed to the `main` branch.
6+
7+
The deployed demo will be publicly accessible at:
8+
9+
```
10+
https://blazordata-net.github.io/RFPAPP/
11+
```
12+
13+
A link to this page will be added to the repository's `README.md` for easy discovery.
14+
15+
---
16+
17+
## Architecture
18+
19+
The repository contains two relevant projects for this deployment:
20+
21+
| Project | SDK | Role |
22+
|---|---|---|
23+
| `RFPResponsePOC/RFPResponsePOC.Client` | `Microsoft.NET.Sdk.BlazorWebAssembly` | Client-side Blazor WASM app — the static artifact that can be hosted on GitHub Pages |
24+
| `RFPResponsePOC/RFPResponsePOC` | `Microsoft.NET.Sdk.Web` | ASP.NET Core host — **not** deployed to Pages (requires a server) |
25+
26+
Only the **client** project is deployed to Pages, because GitHub Pages only hosts static files.
27+
28+
```mermaid
29+
graph TD
30+
A[Developer pushes to main] --> B[GitHub Actions Workflow triggered]
31+
B --> C[Checkout source code]
32+
C --> D[Setup .NET 9 SDK]
33+
D --> E[dotnet publish RFPResponseAPP.Client<br/>with --base-path /RFPAPP]
34+
E --> F[Copy publish output to staging dir]
35+
F --> G[Add .nojekyll file]
36+
G --> H[Add 404.html SPA redirect]
37+
H --> I[Deploy to gh-pages branch]
38+
I --> J[GitHub Pages serves static files]
39+
J --> K[Live at blazordata-net.github.io/RFPAPP/]
40+
```
41+
42+
---
43+
44+
## Key Considerations
45+
46+
### Base Path
47+
48+
Because GitHub Pages serves the app under a sub-path (`/RFPAPP/`), the Blazor app must be published with a matching base path. This is done by passing `--base-path /RFPAPP` to `dotnet publish`, which sets the `<base href>` in `index.html` automatically.
49+
50+
### SPA Client-Side Routing
51+
52+
GitHub Pages does not understand Blazor's client-side routing. If a user navigates directly to a deep link (e.g., `/RFPAPP/counter`), GitHub Pages returns a 404. The standard workaround is:
53+
54+
1. Place a `404.html` in the root that contains JavaScript to redirect back to `index.html`, preserving the intended path in the query string.
55+
2. Place a snippet in `index.html` to read the redirect path from the query string and restore `history.pushState`.
56+
57+
This is the same technique used by [spa-github-pages](https://github.com/rafgraph/spa-github-pages).
58+
59+
### `.nojekyll` File
60+
61+
GitHub Pages runs Jekyll processing by default, which ignores files and folders prefixed with `_` (such as Blazor's `_framework/` directory). Adding a `.nojekyll` file at the repository root of the `gh-pages` branch disables Jekyll and ensures all assets are served correctly.
62+
63+
---
64+
65+
## Workflow Design
66+
67+
```mermaid
68+
flowchart LR
69+
subgraph Trigger
70+
T1[push to main]
71+
T2[workflow_dispatch]
72+
end
73+
74+
subgraph Build Job
75+
B1[actions/checkout@v4]
76+
B2[actions/setup-dotnet@v4 .NET 9]
77+
B3[dotnet publish --configuration Release --base-path /RFPAPP]
78+
B4[Copy wwwroot output to ./release]
79+
B5[Touch .nojekyll]
80+
B6[Copy 404.html]
81+
end
82+
83+
subgraph Deploy Job
84+
D1[peaceiris/actions-gh-pages@v4<br/>publish_dir: ./release<br/>branch: gh-pages]
85+
end
86+
87+
T1 --> B1
88+
T2 --> B1
89+
B1 --> B2 --> B3 --> B4 --> B5 --> B6 --> D1
90+
```
91+
92+
---
93+
94+
## File Changes Required
95+
96+
### 1. New Workflow: `.github/workflows/deploy-ghpages.yml`
97+
98+
A new GitHub Actions workflow file that:
99+
100+
- Triggers on every push to `main` and supports manual dispatch.
101+
- Builds the Blazor WASM client with the correct base path.
102+
- Patches `index.html` for SPA routing support.
103+
- Deploys the static output to the `gh-pages` branch using `peaceiris/actions-gh-pages`.
104+
105+
```yaml
106+
name: Deploy to GitHub Pages
107+
108+
on:
109+
push:
110+
branches:
111+
- main
112+
workflow_dispatch:
113+
114+
permissions:
115+
contents: write
116+
117+
jobs:
118+
deploy:
119+
runs-on: ubuntu-latest
120+
121+
steps:
122+
- name: Checkout
123+
uses: actions/checkout@v4
124+
125+
- name: Setup .NET
126+
uses: actions/setup-dotnet@v4
127+
with:
128+
dotnet-version: '9.x'
129+
130+
- name: Publish Blazor WASM client
131+
run: |
132+
dotnet publish RFPResponsePOC/RFPResponsePOC.Client/RFPResponseAPP.Client.csproj \
133+
--configuration Release \
134+
--output ./release-output
135+
136+
- name: Prepare gh-pages directory
137+
run: |
138+
mkdir -p ./release
139+
cp -r ./release-output/wwwroot/. ./release/
140+
141+
# Disable Jekyll so _framework assets are served
142+
touch ./release/.nojekyll
143+
144+
# Fix index.html base href for GitHub Pages sub-path
145+
sed -i 's|<base href="/" />|<base href="/RFPAPP/" />|g' ./release/index.html
146+
147+
# Add 404.html for SPA client-side routing fallback
148+
cp ./release/index.html ./release/404.html
149+
150+
- name: Deploy to GitHub Pages
151+
uses: peaceiris/actions-gh-pages@v4
152+
with:
153+
github_token: ${{ secrets.GITHUB_TOKEN }}
154+
publish_dir: ./release
155+
publish_branch: gh-pages
156+
```
157+
158+
> **Note:** The `--base-path` flag was introduced in .NET 8. If `dotnet publish` does not automatically rewrite the `<base href>`, the `sed` step ensures the tag is patched correctly.
159+
160+
### 2. Update `README.md`
161+
162+
Add a GitHub Pages badge and live-demo link near the top of `README.md`, next to the existing badges:
163+
164+
```markdown
165+
[![GitHub Pages](https://img.shields.io/badge/Demo-GitHub%20Pages-blue?logo=github)](https://blazordata-net.github.io/RFPAPP/)
166+
```
167+
168+
And a dedicated section:
169+
170+
```markdown
171+
### 🌐 Live Demo (GitHub Pages)
172+
[https://blazordata-net.github.io/RFPAPP/](https://blazordata-net.github.io/RFPAPP/)
173+
```
174+
175+
---
176+
177+
## GitHub Repository Settings
178+
179+
After the first workflow run creates the `gh-pages` branch, the following setting must be configured once in the GitHub UI (or via the API):
180+
181+
1. Go to **Settings → Pages**.
182+
2. Under **Source**, select **Deploy from a branch**.
183+
3. Choose branch: `gh-pages`, folder: `/ (root)`.
184+
4. Save.
185+
186+
GitHub will then serve the contents of the `gh-pages` branch at `https://blazordata-net.github.io/RFPAPP/`.
187+
188+
```mermaid
189+
sequenceDiagram
190+
participant Dev as Developer
191+
participant GH as GitHub Actions
192+
participant Pages as GitHub Pages CDN
193+
194+
Dev->>GH: git push origin main
195+
GH->>GH: Build & publish Blazor WASM
196+
GH->>GH: Prepare static output (index.html, 404.html, .nojekyll)
197+
GH->>Pages: Force-push to gh-pages branch
198+
Pages-->>Dev: Site live at blazordata-net.github.io/RFPAPP/
199+
```
200+
201+
---
202+
203+
## SPA Routing: 404.html Redirect Pattern
204+
205+
The `404.html` file uses a small JavaScript snippet to redirect the browser back to `index.html` while preserving the original path:
206+
207+
```html
208+
<!DOCTYPE html>
209+
<html>
210+
<head>
211+
<meta charset="utf-8" />
212+
<title>RFP Response Creator</title>
213+
<script>
214+
// GitHub Pages SPA redirect
215+
// Stores the current path in sessionStorage and redirects to /
216+
var pathSegmentsToKeep = 1; // number of path segments in the base path (/RFPAPP)
217+
var l = window.location;
218+
l.replace(
219+
l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +
220+
l.pathname.split('/').slice(0, 1 + pathSegmentsToKeep).join('/') + '/?/' +
221+
l.pathname.slice(1).split('/').slice(pathSegmentsToKeep).join('/').replace(/&/g, '~and~') +
222+
(l.search ? '&' + l.search.slice(1).replace(/&/g, '~and~') : '') +
223+
l.hash
224+
);
225+
</script>
226+
</head>
227+
<body></body>
228+
</html>
229+
```
230+
231+
And in `index.html`, before `</body>`:
232+
233+
```html
234+
<script>
235+
// GitHub Pages SPA redirect handler
236+
(function(l) {
237+
if (l.search[1] === '/') {
238+
var decoded = l.search.slice(1).split('&').map(function(s) {
239+
return s.replace(/~and~/g, '&');
240+
}).join('?');
241+
window.history.replaceState(null, null,
242+
l.pathname.slice(0, -1) + decoded + l.hash
243+
);
244+
}
245+
}(window.location));
246+
</script>
247+
```
248+
249+
---
250+
251+
## Summary Checklist
252+
253+
- [x] Understand project structure (Blazor WASM client + ASP.NET Core host)
254+
- [ ] Create `.github/workflows/deploy-ghpages.yml`
255+
- [ ] Verify `.nojekyll`, `404.html`, and base-href rewriting in workflow
256+
- [ ] Update `README.md` with GitHub Pages badge and link
257+
- [ ] Enable GitHub Pages in repository **Settings → Pages** (branch: `gh-pages`)
258+
- [ ] Confirm live URL: `https://blazordata-net.github.io/RFPAPP/`

0 commit comments

Comments
 (0)