diff options
| author | emmett1 <me@emmett1.my> | 2026-05-24 23:34:39 +0800 |
|---|---|---|
| committer | emmett1 <me@emmett1.my> | 2026-05-24 23:34:39 +0800 |
| commit | 76229f5bd496504363b58172e2708c7b9ad79c43 (patch) | |
| tree | 727299aa5ec686d6df797b5fb67c384453614ed9 /utils | |
| parent | c6e05105528b47a952d5367d5326a349608e456e (diff) | |
| download | alicelinux-76229f5bd496504363b58172e2708c7b9ad79c43.tar.gz alicelinux-76229f5bd496504363b58172e2708c7b9ad79c43.zip | |
updated .woodpecker and buildsite.sh script
Diffstat (limited to 'utils')
| -rwxr-xr-x | utils/buildsite.sh | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/utils/buildsite.sh b/utils/buildsite.sh new file mode 100755 index 00000000..e00d3088 --- /dev/null +++ b/utils/buildsite.sh @@ -0,0 +1,396 @@ +#!/bin/sh -e +# +# Build the Alice Linux static website into public/. +# Run from the project root. +# +# Dependencies: cmark, curl + +cd "$(dirname "$0")/.." + +for dep in cmark curl; do + command -v "$dep" >/dev/null 2>&1 || { + printf 'Error: "%s" is required but not found in PATH.\n' "$dep" >&2 + exit 1 + } +done + +rm -rf public +mkdir -p public + +html_escape() { + # takes a single string argument, prints escaped to stdout + printf '%s' "$1" | sed 's/\&/\&/g;s/</\</g;s/>/\>/g;s/"/\"/g' +} + +port_version() { + grep '^version=' "$1/abuild" | cut -d = -f2- || true +} + +port_release() { + grep '^release=' "$1/abuild" | cut -d = -f2- || true +} + +port_depends() { + [ -f "$1/depends" ] || return 0 + grep -Ev '^(#|$)' "$1/depends" | tr '\n' ' ' | sed 's/[[:space:]]*$//' || true +} + +generate_ports_page() { + { + sed "s/@TITLE@/ports/g" files/header + echo "<p>Package ports generated from <code>repos/core</code>, <code>repos/extra</code>, and <code>repos/community</code>.</p>" + echo "<div class=\"ports-toolbar\">" + echo "<button type=\"button\" class=\"active\" data-repo=\"all\">all</button>" + echo "<button type=\"button\" data-repo=\"core\">core</button>" + echo "<button type=\"button\" data-repo=\"extra\">extra</button>" + echo "<button type=\"button\" data-repo=\"community\">community</button>" + echo "<input id=\"ports-search\" type=\"search\" placeholder=\"filter ports\" autocomplete=\"off\">" + echo "</div>" + echo "<div class=\"ports-count\"><span id=\"ports-visible\">0</span> / <span id=\"ports-total\">0</span> ports</div>" + echo "<table id=\"ports-table\">" + echo "<thead><tr><th>repo</th><th>name</th><th>version</th><th>dependencies</th></tr></thead>" + echo "<tbody>" + + for repo in core extra community; do + for port in repos/$repo/*; do + [ -f "$port/abuild" ] || continue + name=${port##*/} + version=$(port_version "$port") + release=$(port_release "$port") + depends=$(port_depends "$port") + [ "$release" ] && version=$version-$release + + e_repo=$(html_escape "$repo") + e_name=$(html_escape "$name") + e_version=$(html_escape "$version") + e_depends=$(html_escape "$depends") + + printf '<tr data-repo="%s"><td>%s</td><td><a href="https://codeberg.org/emmett1/alicelinux/src/branch/main/repos/%s/%s">%s</a></td><td>%s</td><td>%s</td></tr>\n' \ + "$e_repo" \ + "$e_repo" \ + "$e_repo" \ + "$e_name" \ + "$e_name" \ + "$e_version" \ + "$e_depends" + done + done + + echo "</tbody>" + echo "</table>" + cat << 'EOF' +<script> +(function () { + var table = document.getElementById('ports-table'); + var rows = Array.prototype.slice.call(table.querySelectorAll('tbody tr')); + var search = document.getElementById('ports-search'); + var visible = document.getElementById('ports-visible'); + var total = document.getElementById('ports-total'); + var buttons = Array.prototype.slice.call(document.querySelectorAll('.ports-toolbar button')); + var repo = 'all'; + + total.textContent = rows.length; + + function filterPorts() { + var query = search.value.trim().toLowerCase(); + var count = 0; + + rows.forEach(function (row) { + var repoMatch = repo === 'all' || row.dataset.repo === repo; + var textMatch = !query || row.textContent.toLowerCase().indexOf(query) !== -1; + var show = repoMatch && textMatch; + row.style.display = show ? '' : 'none'; + if (show) count++; + }); + + visible.textContent = count; + } + + buttons.forEach(function (button) { + button.addEventListener('click', function () { + repo = button.dataset.repo; + buttons.forEach(function (b) { b.classList.remove('active'); }); + button.classList.add('active'); + filterPorts(); + }); + }); + + search.addEventListener('input', filterPorts); + filterPorts(); +}()); +</script> +EOF + cat files/footer + } > public/ports.html +} + +# copy static .html files (from repo root, readme, etc.) into public/ +find . -type f -name "*.html" ! -path './public/*' | sed 's|^\./||' | while IFS= read -r i; do + dir=${i%/*} + file=${i##*/} + title=${dir##*/} + [ "$dir" = "$file" ] && { + title=home; dir= + } + mkdir -p "public/$dir" + printf 'copy html for %s...\n' "$i" + { + sed "s/@TITLE@/$title/g" files/header + cat "$i" + cat files/footer + } > "public/$dir/$file" +done + +generate_ports_page + +generate_commits_page() { + { + sed "s/@TITLE@/commits/g" files/header + cat << 'EOF' +<p>Recent commits from <a href="https://codeberg.org/emmett1/alicelinux">codeberg.org/emmett1/alicelinux</a>.</p> +<ul id="commits"></ul> +<div id="commits-loading">loading…</div> +<div id="commits-error"></div> +<script> +(function () { + var list = document.getElementById('commits'); + var loading = document.getElementById('commits-loading'); + var errEl = document.getElementById('commits-error'); + var page = 1; + var loadingMore = false; + var hasMore = true; + var PER_PAGE = 30; + + function fmtHash(hash) { + return hash.substring(0, 7); + } + + function fmtDate(iso) { + var d = new Date(iso); + var y = d.getFullYear(); + var m = String(d.getMonth() + 1).padStart(2, '0'); + var day = String(d.getDate()).padStart(2, '0'); + var h = String(d.getHours()).padStart(2, '0'); + var min = String(d.getMinutes()).padStart(2, '0'); + return y + '-' + m + '-' + day + ' ' + h + ':' + min; + } + + function esc(s) { + var d = document.createElement('div'); + d.textContent = s; + return d.innerHTML; + } + + function loadCommits() { + if (!hasMore || loadingMore) return; + loadingMore = true; + loading.style.display = 'block'; + + var url = 'https://codeberg.org/api/v1/repos/emmett1/alicelinux/commits?limit=' + PER_PAGE + '&page=' + page; + + fetch(url) + .then(function (r) { + if (!r.ok) throw new Error('HTTP ' + r.status); + var link = r.headers.get('X-HasMore'); + if (link === 'false' || link === null) hasMore = false; + return r.json(); + }) + .then(function (data) { + if (!data.length || data.length < PER_PAGE) hasMore = false; + + data.forEach(function (c) { + var li = document.createElement('li'); + var hash = c.sha || ''; + var msg = c.commit.message.split('\n')[0] || ''; + var author = c.commit.author.name || 'unknown'; + var date = c.commit.author.date || ''; + var url = c.html_url || '#'; + + li.innerHTML = + '<span class="commit-hash"><a href="' + esc(url) + '">' + esc(fmtHash(hash)) + '</a></span>' + + '<span class="commit-msg"><a href="' + esc(url) + '">' + esc(msg) + '</a></span>' + + '<span class="commit-meta">' + esc(author) + '<br>' + esc(fmtDate(date)) + '</span>'; + list.appendChild(li); + }); + + page++; + loadingMore = false; + loading.style.display = 'none'; + }) + .catch(function (err) { + loadingMore = false; + loading.style.display = 'none'; + hasMore = false; + errEl.textContent = 'Failed to load commits: ' + err.message; + errEl.style.display = 'block'; + }); + } + + window.addEventListener('scroll', function () { + if (loadingMore || !hasMore) return; + var scrollBottom = window.scrollY + window.innerHeight; + var threshold = document.body.scrollHeight - 600; + if (scrollBottom >= threshold) loadCommits(); + }); + + loadCommits(); +}()); +</script> +EOF + cat files/footer + } > public/commits.html +} + +generate_commits_page + +generate_download_page() { + { + sed "s/@TITLE@/download/g" files/header + + listing=$(curl -sL --max-time 10 https://dl.alicelinux.org/ 2>/dev/null || true) + + cat << 'EOF' +<div id="dl-status" class="dl-empty">loading…</div> +<table class="dl-table" id="dl-table"><tbody> +EOF + + if [ -n "$listing" ]; then + echo "$listing" | sed -n '/<tbody>/,/<\/tbody>/p' | while IFS= read -r row; do + case $row in + *'<tr>'*) + href=$(printf '%s' "$row" | sed 's/.*<a href="\([^"]*\)">.*/\1/') + name=$(printf '%s' "$row" | sed 's/.*<a href="[^"]*">\([^<]*\)<.*/\1/') + size=$(printf '%s' "$row" | sed 's/.*<td class="s"[^>]*>\([^<]*\)<.*/\1/') + date=$(printf '%s' "$row" | sed 's/.*<td class="m">\([^<]*\)<.*/\1/') + type=$(printf '%s' "$row" | sed 's/.*<td class="t">\([^<]*\)<.*/\1/') + [ "$name" = "../" ] && continue + url="https://dl.alicelinux.org/$href" + e_url=$(html_escape "$url") + e_name=$(html_escape "$name") + e_size=$(html_escape "$size") + e_date=$(html_escape "$date") + if [ "$type" = "Directory" ]; then + printf '<tr><td><a href="%s">%s/</a></td><td>%s</td><td>%s</td></tr>\n' \ + "$e_url" "$e_name" "$e_size" "$e_date" + else + printf '<tr><td><a href="%s">%s</a></td><td>%s</td><td>%s</td></tr>\n' \ + "$e_url" "$e_name" "$e_size" "$e_date" + fi + ;; + esac + done + fi + + cat << 'EOF' +</tbody></table> +<script> +(function () { + var table = document.getElementById('dl-table'); + var tbody = table.querySelector('tbody'); + var status = document.getElementById('dl-status'); + + function esc(s) { + var d = document.createElement('div'); + d.textContent = s; + return d.innerHTML; + } + + function rowHTML(href, name, size, date, isDir) { + var url = 'https://dl.alicelinux.org/' + href; + var label = esc(name) + (isDir ? '/' : ''); + return '<tr><td><a href="' + esc(url) + '">' + label + '</a></td><td>' + esc(size) + '</td><td>' + esc(date) + '</td></tr>'; + } + + fetch('https://dl.alicelinux.org/') + .then(function (r) { + if (!r.ok) throw new Error('HTTP ' + r.status); + return r.text(); + }) + .then(function (html) { + var parser = new DOMParser(); + var doc = parser.parseFromString(html, 'text/html'); + var rows = doc.querySelectorAll('table tbody tr'); + var frag = document.createDocumentFragment(); + + rows.forEach(function (row) { + var cells = row.querySelectorAll('td'); + if (!cells.length) return; + var link = cells[0].querySelector('a'); + if (!link) return; + var href = link.getAttribute('href'); + var name = link.textContent; + if (name === '../') return; + var size = cells[2] ? cells[2].textContent.trim() : ''; + var date = cells[1] ? cells[1].textContent.trim() : ''; + var isDir = cells[3] && cells[3].textContent.trim() === 'Directory'; + var tr = document.createElement('tr'); + tr.innerHTML = rowHTML(href, name, size, date, isDir); + frag.appendChild(tr); + }); + + if (frag.childNodes.length) { + tbody.innerHTML = ''; + tbody.appendChild(frag); + } + status.textContent = ''; + status.style.display = 'none'; + }) + .catch(function () { + var rows = tbody.querySelectorAll('tr'); + if (rows.length) { + status.textContent = ''; + status.style.display = 'none'; + } else { + status.innerHTML = 'Unable to load. Visit <a href="https://dl.alicelinux.org">dl.alicelinux.org</a> directly.'; + } + }); +}()); +</script> +EOF + cat files/footer + } > public/download.html +} + +generate_download_page + +# build docs index +cat docs/readme.md > docs/index.md +for f in docs/*.md; do + case $f in */readme.md|*/index.md) continue;; esac + title=$(head -n1 "$f") + file=${f##*/} + echo "- [$title](./${file%.md}.html)" >> docs/index.md +done + +# convert markdown to html +find . -type f -name "*.md" ! -path './public/*' | sed 's|^\./||' | while IFS= read -r i; do + dir=${i%/*} + file=${i##*/} + title=${dir##*/} + [ "$dir" = "$file" ] && { + title=home; dir= + } + mkdir -p "public/$dir" + printf 'generating html for %s...\n' "$i" + { + sed "s/@TITLE@/$title/g" files/header + cmark "$i" + cat files/footer + } > "public/${i%.md}.html" +done + +# move readme.html to index.html +find public -type f -name "readme.html" | while IFS= read -r i; do + mv -n "$i" "${i%/*}/index.html" +done + +rm -f docs/index.md + +if [ -d files ]; then + cp -ra files public/ +fi + +echo alicelinux.org > public/.domains +echo alicelinux.emmett1.my >> public/.domains + +exit 0 |