Skip to content

Commit 2d88a3d

Browse files
committed
testing pages workflow
1 parent d437e1a commit 2d88a3d

1 file changed

Lines changed: 321 additions & 0 deletions

File tree

.github/workflows/pages.yml

Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
name: Deploy to GitHub Pages
2+
3+
on:
4+
push:
5+
branches: ["main"]
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: read
10+
pages: write
11+
id-token: write
12+
13+
concurrency:
14+
group: "pages"
15+
cancel-in-progress: false
16+
17+
jobs:
18+
build:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout
22+
uses: actions/checkout@v4
23+
24+
- name: Generate Jekyll site
25+
shell: bash
26+
env:
27+
REPO_NAME: ${{ github.event.repository.name }}
28+
REPO_FULL: ${{ github.repository }}
29+
REPO_DESC: ${{ github.event.repository.description }}
30+
run: |
31+
set -e
32+
33+
OWNER="${REPO_FULL%%/*}"
34+
REPO="${REPO_NAME}"
35+
DESC="${REPO_DESC:-$REPO}"
36+
BASE_URL="/${REPO}"
37+
STAGE="_site_source"
38+
39+
echo "::group::Staging allowed files"
40+
41+
# ── Stage ONLY allowed files into a clean directory ───────────
42+
# Whitelist: *.md files + LICENSE + LICENSE.txt
43+
# Everything else is excluded from the site.
44+
mkdir -p "${STAGE}"
45+
46+
# Copy all .md files preserving directory structure
47+
find . -name '*.md' \
48+
-not -path './.git/*' \
49+
-not -path "./${STAGE}/*" \
50+
| while read -r f; do
51+
dest="${STAGE}/${f#./}"
52+
mkdir -p "$(dirname "$dest")"
53+
cp "$f" "$dest"
54+
done
55+
56+
# Copy LICENSE files (plain text)
57+
for lf in LICENSE LICENSE.txt; do
58+
[ -f "$lf" ] && cp "$lf" "${STAGE}/"
59+
done
60+
61+
# Remove any .gitkeep files that got copied
62+
find "${STAGE}" -name '.gitkeep' -delete
63+
64+
echo "Staged files:"
65+
find "${STAGE}" -type f | sort
66+
echo "::endgroup::"
67+
68+
# ── Everything below operates inside the staging dir ──────────
69+
cd "${STAGE}"
70+
71+
echo "::group::Generating Jekyll config and layout"
72+
73+
# ── _config.yml ───────────────────────────────────────────────
74+
cat > _config.yml << CONFIGEOF
75+
title: "${REPO}"
76+
description: "${DESC}"
77+
permalink: pretty
78+
baseurl: "${BASE_URL}"
79+
80+
defaults:
81+
- scope:
82+
path: "docs"
83+
values:
84+
layout: default
85+
nav_section: docs
86+
CONFIGEOF
87+
88+
# ── _layouts/default.html ─────────────────────────────────────
89+
mkdir -p _layouts
90+
cat > _layouts/default.html << 'LAYOUTEOF'
91+
<!DOCTYPE html>
92+
<html lang="en">
93+
<head>
94+
<meta charset="UTF-8">
95+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
96+
<title>{{ page.title | default: site.title }}</title>
97+
<link rel="preconnect" href="https://fonts.googleapis.com">
98+
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
99+
<style>
100+
*{margin:0;padding:0;box-sizing:border-box}
101+
body{font-family:'IBM Plex Sans',-apple-system,sans-serif;color:#24292f;background:#f6f8fa;line-height:1.7}
102+
a{color:#0969da;text-decoration:none}
103+
a:hover{text-decoration:underline}
104+
.site-header{background:#fff;border-bottom:1px solid #d0d7de;padding:16px 24px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px}
105+
.site-title{font-family:'IBM Plex Mono',monospace;font-size:16px;font-weight:500;color:#24292f;text-decoration:none}
106+
.site-nav{display:flex;gap:20px;flex-wrap:wrap}
107+
.site-nav a{font-size:14px;color:#57606a;text-decoration:none;padding:4px 0}
108+
.site-nav a:hover{color:#24292f}
109+
.site-nav a.active{color:#24292f;font-weight:500;border-bottom:2px solid #fd8c73}
110+
.container{max-width:820px;margin:32px auto;padding:0 24px}
111+
.content{background:#fff;border:1px solid #d0d7de;border-radius:6px;padding:32px}
112+
.content h1{font-size:24px;font-weight:500;margin:0 0 16px;border-bottom:1px solid #d0d7de;padding-bottom:8px}
113+
.content h2{font-size:20px;font-weight:500;margin:24px 0 12px;border-bottom:1px solid #d0d7de;padding-bottom:6px}
114+
.content h3{font-size:16px;font-weight:500;margin:20px 0 8px}
115+
.content h4{font-size:14px;font-weight:500;margin:16px 0 6px}
116+
.content p{font-size:14px;color:#57606a;margin:0 0 16px}
117+
.content ul,.content ol{font-size:14px;color:#57606a;margin:0 0 16px;padding-left:24px}
118+
.content li{margin-bottom:4px}
119+
.content code{font-family:'IBM Plex Mono',monospace;font-size:13px;background:#f6f8fa;padding:2px 6px;border-radius:4px}
120+
.content pre{background:#f6f8fa;border:1px solid #d0d7de;border-radius:6px;padding:16px;overflow-x:auto;margin:8px 0 16px;line-height:1.5}
121+
.content pre code{background:transparent;padding:0}
122+
.content table{width:100%;border-collapse:collapse;margin:8px 0 16px;font-size:14px}
123+
.content th,.content td{border:1px solid #d0d7de;padding:8px 12px;text-align:left}
124+
.content th{background:#f6f8fa;font-weight:500}
125+
.content img{max-width:100%}
126+
#releases-container .release{border-bottom:1px solid #d0d7de;padding:20px 0}
127+
#releases-container .release:last-child{border-bottom:none}
128+
.release-tag{font-family:'IBM Plex Mono',monospace;font-size:16px;font-weight:500;color:#0969da}
129+
.release-date{font-size:13px;color:#8b949e;margin-left:8px}
130+
.release-latest{font-size:11px;padding:2px 8px;border-radius:16px;background:#dafbe1;color:#1a7f37;font-weight:500;margin-left:8px}
131+
.release-body{font-size:14px;color:#57606a;margin-top:8px;line-height:1.7}
132+
.release-body h2,.release-body h3{font-size:14px;font-weight:500;color:#24292f;margin:12px 0 4px;border:none;padding:0}
133+
.release-body ul{padding-left:20px}
134+
.release-body li{margin-bottom:2px;font-size:13px}
135+
.release-assets{margin-top:12px}
136+
.release-assets summary{font-size:13px;font-weight:500;cursor:pointer;color:#24292f}
137+
.release-asset{font-family:'IBM Plex Mono',monospace;font-size:12px;color:#57606a;padding:3px 0}
138+
.loading{text-align:center;padding:32px;color:#8b949e;font-size:14px}
139+
@media(max-width:640px){.site-header{padding:12px 16px}.container{padding:0 12px;margin:16px auto}.content{padding:20px}}
140+
</style>
141+
</head>
142+
<body>
143+
<header class="site-header">
144+
<a class="site-title" href="{{ '/' | relative_url }}">{{ site.title }}</a>
145+
<nav class="site-nav">
146+
<a href="{{ '/' | relative_url }}" {% if page.url == '/' %}class="active"{% endif %}>Home</a>
147+
{% assign has_docs = false %}{% for p in site.pages %}{% if p.path contains 'docs/' and p.name != 'index.md' %}{% assign has_docs = true %}{% break %}{% endif %}{% endfor %}
148+
{% if has_docs %}<a href="{{ '/docs/' | relative_url }}" {% if page.url contains '/docs' %}class="active"{% endif %}>Docs</a>{% endif %}
149+
<a href="{{ '/releases/' | relative_url }}" {% if page.url contains '/releases' %}class="active"{% endif %}>Releases</a>
150+
{% for p in site.pages %}{% if p.path == 'LICENSE.md' %}<a href="{{ '/license/' | relative_url }}" {% if page.url contains '/license' %}class="active"{% endif %}>License</a>{% break %}{% endif %}{% endfor %}
151+
{% for p in site.pages %}{% if p.path == 'CONTRIBUTING.md' %}<a href="{{ '/contributing/' | relative_url }}" {% if page.url contains '/contributing' %}class="active"{% endif %}>Contributing</a>{% break %}{% endif %}{% endfor %}
152+
{% for p in site.pages %}{% if p.path == 'CODE_OF_CONDUCT.md' %}<a href="{{ '/code-of-conduct/' | relative_url }}" {% if page.url contains '/code-of-conduct' %}class="active"{% endif %}>Code of conduct</a>{% break %}{% endif %}{% endfor %}
153+
</nav>
154+
</header>
155+
<main class="container">
156+
<div class="content">{{ content }}</div>
157+
</main>
158+
</body>
159+
</html>
160+
LAYOUTEOF
161+
echo "::endgroup::"
162+
163+
echo "::group::Processing content files"
164+
165+
# ── README.md → homepage ──────────────────────────────────────
166+
if [ -f "README.md" ] && ! head -1 README.md | grep -q '^\-\-\-'; then
167+
TEMP=$(mktemp)
168+
printf -- '---\nlayout: default\ntitle: Home\npermalink: /\n---\n\n' > "$TEMP"
169+
cat README.md >> "$TEMP"
170+
mv "$TEMP" README.md
171+
echo "Processed README.md → homepage"
172+
fi
173+
174+
# ── Docs: add front matter + create index ─────────────────────
175+
if [ -d "docs" ]; then
176+
for f in docs/*.md; do
177+
[ -f "$f" ] || continue
178+
[ "$(basename "$f")" = "index.md" ] && continue
179+
180+
if ! head -1 "$f" | grep -q '^\-\-\-'; then
181+
BASENAME=$(basename "$f" .md)
182+
SYNOPSIS=$(grep -m1 -A1 '## Synopsis' "$f" 2>/dev/null | tail -1 | sed 's/^[[:space:]]*//')
183+
[ -z "$SYNOPSIS" ] && SYNOPSIS="$BASENAME"
184+
TEMP=$(mktemp)
185+
printf -- '---\nlayout: default\ntitle: %s\ndescription: "%s"\n---\n\n' "$BASENAME" "$SYNOPSIS" > "$TEMP"
186+
cat "$f" >> "$TEMP"
187+
mv "$TEMP" "$f"
188+
echo "Processed $f"
189+
fi
190+
done
191+
192+
if [ ! -f "docs/index.md" ]; then
193+
cat > docs/index.md << 'DOCSEOF'
194+
---
195+
layout: default
196+
title: Docs
197+
permalink: /docs/
198+
---
199+
# Function reference
200+
{% assign doc_pages = site.pages | where_exp: "p", "p.path contains 'docs/'" | where_exp: "p", "p.name != 'index.md'" | sort: "title" %}
201+
{% if doc_pages.size > 0 %}
202+
<ul>
203+
{% for doc in doc_pages %}
204+
<li><a href="{{ doc.url | relative_url }}"><code>{{ doc.title }}</code></a> — {{ doc.description | default: doc.title }}</li>
205+
{% endfor %}
206+
</ul>
207+
{% else %}
208+
<p><em>No function documentation found yet.</em></p>
209+
{% endif %}
210+
DOCSEOF
211+
echo "Created docs/index.md"
212+
fi
213+
fi
214+
215+
# ── Releases page ─────────────────────────────────────────────
216+
cat > releases.md << RELEOF
217+
---
218+
layout: default
219+
title: Releases
220+
permalink: /releases/
221+
---
222+
# Releases
223+
<div id="releases-container"><div class="loading">Loading releases...</div></div>
224+
<script>
225+
(async function(){
226+
const c=document.getElementById('releases-container');
227+
try{
228+
const r=await fetch('https://api.github.com/repos/${REPO_FULL}/releases');
229+
if(!r.ok)throw new Error(r.status);
230+
const data=await r.json();
231+
if(!data.length){c.innerHTML='<p>No releases found.</p>';return}
232+
c.innerHTML=data.map((r,i)=>{
233+
const d=new Date(r.published_at).toLocaleDateString('en-GB',{day:'numeric',month:'short',year:'numeric'});
234+
const l=i===0?'<span class="release-latest">latest</span>':'';
235+
const a=r.assets.length?'<details class="release-assets"><summary>Assets ('+r.assets.length+')</summary>'+r.assets.map(a=>'<div class="release-asset">'+a.name+' <span style="color:#8b949e">'+(a.size/1024).toFixed(1)+' KB</span></div>').join('')+'</details>':'';
236+
let b=(r.body||'').replace(/^### (.+)$/gm,'<h3>$1</h3>').replace(/^## (.+)$/gm,'<h2>$1</h2>').replace(/^\* (.+)$/gm,'<li>$1</li>').replace(/(<li>.*<\/li>\n?)+/gs,'<ul>$&</ul>').replace(/\x60\x60\x60(\w*)\n([\s\S]*?)\x60\x60\x60/g,'<pre><code>$2</code></pre>').replace(/\x60([^\x60]+)\x60/g,'<code>$1</code>').replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2">$1</a>').replace(/\n\n/g,'<br><br>').replace(/\n/g,'<br>');
237+
return '<div class="release"><div><span class="release-tag">'+r.tag_name+'</span>'+l+'<span class="release-date">'+d+'</span></div><div class="release-body">'+b+'</div>'+a+'</div>';
238+
}).join('');
239+
}catch(e){
240+
c.innerHTML='<p>Unable to load releases. Visit <a href="https://github.com/${REPO_FULL}/releases">GitHub</a> directly.</p>';
241+
}
242+
})();
243+
</script>
244+
RELEOF
245+
echo "Created releases.md"
246+
247+
# ── License page (from LICENSE/LICENSE.txt) ────────────────────
248+
LICENSE_SRC=""
249+
for lf in LICENSE LICENSE.txt; do
250+
[ -f "$lf" ] && LICENSE_SRC="$lf" && break
251+
done
252+
253+
if [ -n "$LICENSE_SRC" ] || [ -f "LICENSE.md" ]; then
254+
SRC="${LICENSE_SRC:-LICENSE.md}"
255+
LICENSE_TYPE="License"
256+
grep -qi "apache" "$SRC" 2>/dev/null && LICENSE_TYPE="Apache License 2.0"
257+
grep -qi "mit license" "$SRC" 2>/dev/null && LICENSE_TYPE="MIT License"
258+
grep -qi "gnu general public" "$SRC" 2>/dev/null && LICENSE_TYPE="GPL"
259+
grep -qi "bsd" "$SRC" 2>/dev/null && LICENSE_TYPE="BSD License"
260+
261+
if ! ([ -f "LICENSE.md" ] && head -1 LICENSE.md | grep -q '^\-\-\-'); then
262+
cat > LICENSE.md << LICEOF
263+
---
264+
layout: default
265+
title: License
266+
permalink: /license/
267+
---
268+
# License
269+
This project is licensed under the **${LICENSE_TYPE}**.
270+
271+
See the [LICENSE](https://github.com/${REPO_FULL}/blob/main/${SRC}) file for the full license text.
272+
LICEOF
273+
echo "Created LICENSE.md (${LICENSE_TYPE})"
274+
fi
275+
fi
276+
277+
# ── CONTRIBUTING.md ───────────────────────────────────────────
278+
if [ -f "CONTRIBUTING.md" ] && ! head -1 CONTRIBUTING.md | grep -q '^\-\-\-'; then
279+
TEMP=$(mktemp)
280+
printf -- '---\nlayout: default\ntitle: Contributing\npermalink: /contributing/\n---\n\n' > "$TEMP"
281+
cat CONTRIBUTING.md >> "$TEMP"
282+
mv "$TEMP" CONTRIBUTING.md
283+
echo "Processed CONTRIBUTING.md"
284+
fi
285+
286+
# ── CODE_OF_CONDUCT.md ────────────────────────────────────────
287+
if [ -f "CODE_OF_CONDUCT.md" ] && ! head -1 CODE_OF_CONDUCT.md | grep -q '^\-\-\-'; then
288+
TEMP=$(mktemp)
289+
printf -- '---\nlayout: default\ntitle: Code of Conduct\npermalink: /code-of-conduct/\n---\n\n' > "$TEMP"
290+
cat CODE_OF_CONDUCT.md >> "$TEMP"
291+
mv "$TEMP" CODE_OF_CONDUCT.md
292+
echo "Processed CODE_OF_CONDUCT.md"
293+
fi
294+
295+
echo "::endgroup::"
296+
297+
echo "Final site source contents:"
298+
find . -type f | sort
299+
300+
- name: Setup Pages
301+
uses: actions/configure-pages@v5
302+
303+
- name: Build with Jekyll
304+
uses: actions/jekyll-build-pages@v1
305+
with:
306+
source: ./_site_source
307+
destination: ./_site
308+
309+
- name: Upload artifact
310+
uses: actions/upload-pages-artifact@v3
311+
312+
deploy:
313+
environment:
314+
name: github-pages
315+
url: ${{ steps.deployment.outputs.page_url }}
316+
runs-on: ubuntu-latest
317+
needs: build
318+
steps:
319+
- name: Deploy to GitHub Pages
320+
id: deployment
321+
uses: actions/deploy-pages@v5

0 commit comments

Comments
 (0)