diff options
author | EuAndreh <eu@euandre.org> | 2021-09-08 06:48:58 -0300 |
---|---|---|
committer | EuAndreh <eu@euandre.org> | 2021-09-08 07:01:48 -0300 |
commit | b03b35d768a60c81b28138e2f4f5cd9a0a5ca721 (patch) | |
tree | ca3f079e87b9085fe429af330b9698292221d351 | |
parent | TODOs.md: Add #question-ab994373-9c09-c4f9-07cf-962f64443231 (diff) | |
download | gistatic-b03b35d768a60c81b28138e2f4f5cd9a0a5ca721.tar.gz gistatic-b03b35d768a60c81b28138e2f4f5cd9a0a5ca721.tar.xz |
src/gistatic.in: Initial sh version
I got a bit frustrated that libgit2 didn't offer an API
or "git archive" commands. I started implementing generating tarballs
from scratch in src/tar.c and I'm quite liking it: the specification is
very small, and the code can be very simple, since all I'm doing is
writing fresh tarballs, and not reading or updating them.
However I felt a bit locked-in to libgit2 itself, and what a detour from
my original goal that is, and the question "what should libgit2 provide"
came up to my mind. This made me realize that libgit2 is playing
catch-up with Git itself, for as long as Git doesn't explicit has an
explicit API, a standard, a public version of its internal libgit.a, or
something like that. In fact, I'm locked in to Git, even.
So even though a C version would probably be much faster, it wouldn't
really have less dependencies, and that's what I'm actually optimising
for: having the software be as portable as possible. On that front, C
is unbeatable with sh as a close second. But the extreme portability
of C aren't being fully exploited here: libgit2 does depend on non-POSIX
things like CMake (and quick grep even shows
references to -D_GNU_SOURCE!!), and Git's Makefile itself isn't POSIX
at all. The point is: by depending on either Git or libgit2, I'm
already loosing many selling points of writing the software in C, and
sh becomes much more attractive. Had existed a common DVCS interface
that could make me decouple gistatic from Git somehow I would insist a
bit more in C, but now I'm switching to sh.
The fact that I was able to get further with sh in one sitting than I
did with C shows that a) I'm a bit less fluent in C than I would like
(at least for now ^^) and b) that it is actually much simpler to do.
I am quite satisfied with the quality of C code that I got so far. The
error handling and propagation is pretty robust, and the implementation
is very disciplined. I did most of the development with Valgrind, and
other sanitizers would help even further, with some fuzzers on top.
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 5 | ||||
-rwxr-xr-x | src/gistatic.in | 752 |
3 files changed, 756 insertions, 2 deletions
@@ -1,4 +1,5 @@ /public/ +/src/gistatic /src/config.h /libgistatic.a /gistatic @@ -19,6 +19,7 @@ LDLIBS = -lgit2 -e 's:@DATE@:$(DATE):g' \ -e 's:@NAME@:$(NAME):g' \ < $< > $@ + if [ -x $< ]; then chmod +x $@; fi .c.o: $(CC) $(CFLAGS) -o $@ -c $< @@ -44,7 +45,7 @@ all-objects = $(lib-objects) src/main.o t-objects = $(sources:.c=.to) src/tests-lib.to src/main.to -all: libgistatic.a gistatic $(manpages) +all: libgistatic.a gistatic src/gistatic $(manpages) libgistatic.a: $(lib-objects) $(AR) $(ARFLAGS) $@ $(lib-objects) @@ -85,7 +86,7 @@ clean: rm -rf \ public/ $(manpages) README.*.md CHANGELOG.*.md messages.mo \ vgcore.* tmp/ src/config.h $(all-objects) $(t-objects) \ - libgistatic.a gistatic gistatic-tests \ + libgistatic.a gistatic gistatic-tests src/gistatic \ tests/resources/repositories/repo-1/.git \ tests/resources/repositories/repo-2/.git diff --git a/src/gistatic.in b/src/gistatic.in new file mode 100755 index 0000000..f18742a --- /dev/null +++ b/src/gistatic.in @@ -0,0 +1,752 @@ +#!/bin/sh +# shellcheck disable=2034 disable=2059 +set -eu + +MSG_USAGE_EN='Usage: + @NAME@ -i -o DIRECTORY REPOSITORY... + @NAME@ -o DIRECTORY -u CLONE_URL REPOSITORY + @NAME@ [-hV] +' + +MSG_HELP_EN='Options: + -i build the index page of the repositories + -u CLONE_URL address to be shown alongside "git clone" + -o DIRECTORY output where to place the generated files + -h, --help show this help + -V, --version print the version information + +See "man @NAME@" for more information.' + +MSG_MISSING_CLIARG_EN='Missing %s\n' +MSG_INCOMPATIBLE_OPTIONS_EN='Incompatible options -u and -i' +MSG_MISSING_ARGS_EN='Missing [PATH | [PATHS]]' +MSG_LANGNAME_EN='en' +MSG_INDEX_DESCRIPTION_EN='Index of repositories' +MSG_DEFAULT_TITLE_EN='Repositories' +MSG_LOGO_ALT_INDEX_EN='Logo image of the repository list' +MSG_LOGO_ALT_REPOSITORY_EN='Logo image of the repository' +MSG_NAME_EN='Name' +MSG_DESCRIPTION_EN='Description' +MSG_LAST_COMMIT_EN='Last commit' +MSG_FOOTER_TEMPLATE_EN='Generated with <a href="https://euandreh.xyz/@NAME@/">@NAME@</a>.' +MSG_COMMIT_FEED_EN='commit feed' +MSG_TAGS_FEED_EN='tags feed' +MSG_NAV_FILES_EN='files' +MSG_NAV_LOG_EN='log' +MSG_NAV_REFS_EN='refs' +MSG_THEAD_BRANCH_EN='Branch' +MSG_THEAD_COMMITMSG_EN='Commit message' +MSG_THEAD_AUTHOR_EN='Author' +MSG_THEAD_DATE_EN='Date' +MSG_THEAD_TAG_EN='Tag' +MSG_TITLE_REFS_EN='refs' + +set_lang() { + lang="$1" + eval " + MSG_USAGE=\$MSG_USAGE_$lang + MSG_HELP=\$MSG_HELP_$lang + MSG_MISSING_CLIARG=\$MSG_MISSING_CLIARG_$lang + MSG_INCOMPATIBLE_OPTIONS=\$MSG_INCOMPATIBLE_OPTIONS_$lang + MSG_MISSING_ARGS=\$MSG_MISSING_ARGS_$lang + MSG_LANGNAME=\$MSG_LANGNAME_$lang + MSG_INDEX_DESCRIPTION=\$MSG_INDEX_DESCRIPTION_$lang + MSG_DEFAULT_TITLE=\$MSG_DEFAULT_TITLE_$lang + MSG_LOGO_ALT_INDEX=\$MSG_LOGO_ALT_INDEX_$lang + MSG_LOGO_ALT_REPOSITORY=\$MSG_LOGO_ALT_REPOSITORY_$lang + MSG_NAME=\$MSG_NAME_$lang + MSG_DESCRIPTION=\$MSG_DESCRIPTION_$lang + MSG_LAST_COMMIT=\$MSG_LAST_COMMIT_$lang + MSG_FOOTER_TEMPLATE=\$MSG_FOOTER_TEMPLATE_$lang + MSG_COMMIT_FEED=\$MSG_COMMIT_FEED_$lang + MSG_TAGS_FEED=\$MSG_TAGS_FEED_$lang + MSG_NAV_FILES=\$MSG_NAV_FILES_$lang + MSG_NAV_LOG=\$MSG_NAV_LOG_$lang + MSG_NAV_REFS=\$MSG_NAV_REFS_$lang + MSG_THEAD_BRANCH=\$MSG_THEAD_BRANCH_$lang + MSG_THEAD_COMMITMSG=\$MSG_THEAD_COMMITMSG_$lang + MSG_THEAD_AUTHOR=\$MSG_THEAD_AUTHOR_$lang + MSG_THEAD_DATE=\$MSG_THEAD_DATE_$lang + MSG_THEAD_TAG=\$MSG_THEAD_TAG_$lang + MSG_TITLE_REFS=\$MSG_TITLE_REFS_$lang +" +} + +get_lang() { + # LC_MESSAGES="ll_CC.CODESET@modifier" -> ll_CC, where quotes + # are optional + locale 2>/dev/null | + grep LC_MESSAGES | + cut -d. -f1 | + cut -d\" -f2 | + cut -d= -f2 +} + +case "$(get_lang)" in + *) + set_lang EN + ;; +esac + +# Utilities + +escape() { + # FIXME: HTML escape + echo "$1" +} + +realpath() { + mkdir -p "$1" + cd "$1" + pwd +} + + +# Helpers + +usage() { + printf '%s\n' "$MSG_USAGE" +} + +help() { + printf '%s\n' "$MSG_HELP" +} + +version() { + printf '@NAME@-@VERSION@ @DATE@\n' +} + +print_logo() { + cat <<EOF +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"> + <path d="M 0 8 L 1 8 L 1 9 L 0 9 L 0 8 Z" /> + <path d="M 0 13 L 1 13 L 1 14 L 0 14 L 0 13 Z" /> + <path d="M 1 8 L 2 8 L 2 9 L 1 9 L 1 8 Z" /> + <path d="M 1 13 L 2 13 L 2 14 L 1 14 L 1 13 Z" /> + <path d="M 2 8 L 3 8 L 3 9 L 2 9 L 2 8 Z" /> + <path d="M 2 13 L 3 13 L 3 14 L 2 14 L 2 13 Z" /> + <path d="M 3 8 L 4 8 L 4 9 L 3 9 L 3 8 Z" /> + <path d="M 3 13 L 4 13 L 4 14 L 3 14 L 3 13 Z" /> + <path d="M 4 7 L 5 7 L 5 8 L 4 8 L 4 7 Z" /> + <path d="M 4 8 L 5 8 L 5 9 L 4 9 L 4 8 Z" /> + <path d="M 4 13 L 5 13 L 5 14 L 4 14 L 4 13 Z" /> + <path d="M 5 6 L 6 6 L 6 7 L 5 7 L 5 6 Z" /> + <path d="M 5 7 L 6 7 L 6 8 L 5 8 L 5 7 Z" /> + <path d="M 5 13 L 6 13 L 6 14 L 5 14 L 5 13 Z" /> + <path d="M 6 5 L 7 5 L 7 6 L 6 6 L 6 5 Z" /> + <path d="M 6 6 L 7 6 L 7 7 L 6 7 L 6 6 Z" /> + <path d="M 6 14 L 7 14 L 7 15 L 6 15 L 6 14 Z" /> + <path d="M 7 1 L 8 1 L 8 2 L 7 2 L 7 1 Z" /> + <path d="M 7 14 L 8 14 L 8 15 L 7 15 L 7 14 Z" /> + <path d="M 7 15 L 8 15 L 8 16 L 7 16 L 7 15 Z" /> + <path d="M 7 2 L 8 2 L 8 3 L 7 3 L 7 2 Z" /> + <path d="M 7 3 L 8 3 L 8 4 L 7 4 L 7 3 Z" /> + <path d="M 7 4 L 8 4 L 8 5 L 7 5 L 7 4 Z" /> + <path d="M 7 5 L 8 5 L 8 6 L 7 6 L 7 5 Z" /> + <path d="M 8 1 L 9 1 L 9 2 L 8 2 L 8 1 Z" /> + <path d="M 8 15 L 9 15 L 9 16 L 8 16 L 8 15 Z" /> + <path d="M 9 1 L 10 1 L 10 2 L 9 2 L 9 1 Z" /> + <path d="M 9 2 L 10 2 L 10 3 L 9 3 L 9 2 Z" /> + <path d="M 9 6 L 10 6 L 10 7 L 9 7 L 9 6 Z" /> + <path d="M 9 15 L 10 15 L 10 16 L 9 16 L 9 15 Z" /> + <path d="M 10 2 L 11 2 L 11 3 L 10 3 L 10 2 Z" /> + <path d="M 10 3 L 11 3 L 11 4 L 10 4 L 10 3 Z" /> + <path d="M 10 4 L 11 4 L 11 5 L 10 5 L 10 4 Z" /> + <path d="M 10 5 L 11 5 L 11 6 L 10 6 L 10 5 Z" /> + <path d="M 10 6 L 11 6 L 11 7 L 10 7 L 10 6 Z" /> + <path d="M 11 6 L 12 6 L 12 7 L 11 7 L 11 6 Z" /> + <path d="M 11 8 L 12 8 L 12 9 L 11 9 L 11 8 Z" /> + <path d="M 10 15 L 11 15 L 11 16 L 10 16 L 10 15 Z" /> + <path d="M 11 10 L 12 10 L 12 11 L 11 11 L 11 10 Z" /> + <path d="M 11 12 L 12 12 L 12 13 L 11 13 L 11 12 Z" /> + <path d="M 11 14 L 12 14 L 12 15 L 11 15 L 11 14 Z" /> + <path d="M 11 15 L 12 15 L 12 16 L 11 16 L 11 15 Z" /> + <path d="M 12 6 L 13 6 L 13 7 L 12 7 L 12 6 Z" /> + <path d="M 12 8 L 13 8 L 13 9 L 12 9 L 12 8 Z" /> + <path d="M 12 10 L 13 10 L 13 11 L 12 11 L 12 10 Z" /> + <path d="M 12 12 L 13 12 L 13 13 L 12 13 L 12 12 Z" /> + <path d="M 12 14 L 13 14 L 13 15 L 12 15 L 12 14 Z" /> + <path d="M 13 6 L 14 6 L 14 7 L 13 7 L 13 6 Z" /> + <path d="M 13 8 L 14 8 L 14 9 L 13 9 L 13 8 Z" /> + <path d="M 13 10 L 14 10 L 14 11 L 13 11 L 13 10 Z" /> + <path d="M 13 12 L 14 12 L 14 13 L 13 13 L 13 12 Z" /> + <path d="M 13 13 L 14 13 L 14 14 L 13 14 L 13 13 Z" /> + <path d="M 13 14 L 14 14 L 14 15 L 13 15 L 13 14 Z" /> + <path d="M 14 7 L 15 7 L 15 8 L 14 8 L 14 7 Z" /> + <path d="M 14 8 L 15 8 L 15 9 L 14 9 L 14 8 Z" /> + <path d="M 14 9 L 15 9 L 15 10 L 14 10 L 14 9 Z" /> + <path d="M 14 10 L 15 10 L 15 11 L 14 11 L 14 10 Z" /> + <path d="M 14 11 L 15 11 L 15 12 L 14 12 L 14 11 Z" /> + <path d="M 14 12 L 15 12 L 15 13 L 14 13 L 14 12 Z" /> +</svg> +EOF +} + +print_style() { + cat <<EOF +:root { + --color: black; + --background-color: white; + --hover-color: hsl(0, 0%, 93%); + --nav-color: hsl(0, 0%, 87%); +} + +@media(prefers-color-scheme: dark) { + :root { + --color: white; + --background-color: black; + --hover-color: hsl(0, 0%, 7%); + --nav-color: hsl(0, 0%, 13%); + } + + body { + color: var(--color); + background-color: var(--background-color); + } + + a { + color: hsl(211, 100%, 60%); + } + + a:visited { + color: hsl(242, 100%, 80%); + } +} + +body { + font-family: monospace; + max-width: 1100px; + margin: 0 auto 0 auto; +} + +.logo { + height: 6em; + width: 6em; +} + +.header-horizontal-grouping { + display: flex; + align-items: center; + margin-top: 1em; + margin-bottom: 1em; +} + +.header-description { + margin-left: 2em; +} + +nav { + margin-top: 2em; +} + +nav ul { + display: flex; + list-style-type: none; + margin-bottom: 0; +} + +nav li { + margin-left: 10px; +} + +nav a, nav a:visited { + padding: 2px 8px 0px 8px; + color: var(--color); +} + +.selected-nav-item { + background-color: var(--nav-color); +} + +hr { + margin-top: 0; + border: 0; + border-top: 3px solid var(--nav-color); +} + +table { + margin: 2em auto; +} + +th { + padding-bottom: 1em; +} + +tbody tr:hover { + background-color: var(--hover-color); +} + +td { + padding-left: 1em; + padding-right: 1em; +} + +footer { + text-align: center; +} +EOF +} + +print_index_header() { + cat <<EOF +<!DOCTYPE html> +<html lang="$(escape "$MSG_LANGNAME")"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <meta name="description" content="$(escape "$MSG_INDEX_DESCRIPTION")" /> + <link rel="icon" type="image/svg+xml" href="logo.svg" /> + <link rel="stylesheet" type="text/css" href="style.css" /> + <title>$(escape "$TITLE")"</title> + </head> + <body> + <header> + <div class="header-horizontal-grouping"> + <img alt="$(escape "$MSG_LOGO_ALT_INDEX")" src="logo.svg" /> + <h1 class="header-description"> + $(escape "$TITLE") + </h1> + </div> + <hr /> + </header> + <main> + <table> + <thead> + <tr> + <th> + $(escape "$MSG_NAME") + </th> + <th> + $(escape "$MSG_DESCRIPTION") + </th> + <th> + $(escape "$MSG_LAST_COMMIT") + </th> + </tr> + </thead> + <tbody> +EOF +} + +print_index_row() { + repo="$(basename "$(realpath "${1%.git}")")" + description="$(cat "$1/description" 2>/dev/null ||:)" + last_commit_date="$(git -C "$1" log -1 --format=%cd --date='format:%Y-%m:%d %H:%M' 2>/dev/null ||:)" + cat <<EOF + <tr> + <td> + <a href="$(escape "$repo")"> + $(escape "$repo") + </a> + </td> + <td> + $(escape "$description") + </td> + <td> + $(escape "$last_commit_date") + </td> + </tr> +EOF +} + +print_index_footer() { + cat <<EOF + </tbody> + </table> + </main> + <footer> + <hr /> + <p> + $(escape "$MSG_FOOTER_TEMPLATE") + </p> + </footer> + </body> +</html> +EOF +} + +index_write() { + rm -f "$OUTDIR/index.html" + print_index_header >> "$OUTDIR/index.html" + for r in "$@"; do + print_index_row "$r" >> "$OUTDIR/index.html" + done + print_index_footer >> "$OUTDIR/index.html" + + print_logo > "$OUTDIR/logo.svg" + print_style > "$OUTDIR/style.css" +} + +print_repo_refs() { + repo="$1" + description="$2" + show_signature="$3" + cat <<EOF +<!DOCTYPE html> +<html lang="$(escape "$MSG_LANGNAME")"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <meta name="description" content="$(escape "$description")" /> + <link rel="icon" type="image/svg+xml" href="logo.svg" /> + <link rel="stylesheet" type="text/css" href="style.css" /> + <link rel="alternate" type="application/atom+xml" href="commits.xml" title="$(escape "$repo") - $(escape "$MSG_COMMIT_FEED")" hreflang="$(escape "$MSG_LANGNAME")" /> + <link rel="alternate" type="application/atom+xml" href="tags.xml" title="$(escape "$repo") - $(escape "$MSG_TAGS_FEED")" hreflang="$(escape "$MSG_LANGNAME")" /> + <title>$(escape "$repo") - $(escape "$MSG_TITLE_REFS")</title> + </head> + <body> + <header> + <div class="header-horizontal-grouping"> + <a href="../"> + <img alt="$(escape "$MSG_LOGO_ALT_REPOSITORY")" class="logo" src="logo.svg" /> + </a> + <div class="header-description"> + <h1> + <a href="./"> + $(escape "$repo") + </a> + </h1> + <h2> + $(escape "$description") </h2> <code> git clone $(escape "$CLONE_URL") </code> </div> + </div> + <nav> + <ul> + <li> + <a href="files.html"> + $(escape "$MSG_NAV_FILES") + </a> + </li> + <li> + <a href="log.html"> + $(escape "$MSG_NAV_LOG") + </a> + </li> + <li class="selected-nav-item"> + <a href="refs.html"> + $(escape "$MSG_NAV_REFS") + </a> + </li> + </ul> + </nav> + <hr /> + </header> + <main> + <table> + <thead> + <tr> + <th> + $(escape "$MSG_THEAD_BRANCH") + </th> + <th> + $(escape "$MSG_THEAD_COMMITMSG") + </th> + <th> + $(escape "$MSG_THEAD_AUTHOR") + </th> + <th> + $(escape "$MSG_THEAD_DATE") + </th> + </tr> + </thead> + <tbody> +EOF + + for branch in $(git branch --format '%(refname:lstrip=2)'); do + sha="$(git rev-parse "$branch")" + cat <<EOF + <tr> + <td> + <a href="log/$(escape "$branch").html"> + $(escape "$branch") + </a> + </td> + <td> + <a href="commit/$(escape "$sha").html"> + $(escape "$(git log -1 --format=%B "$sha")") + </a> + </td> + <td> + $(escape "$(git log -1 --format=%an "$sha")") + </td> + <td> + $(escape "$(git log -1 --format=%cd --date='format:%Y-%m:%d %H:%M' "$sha")") + </td> + </tr> +EOF + done + + cat <<EOF + </tbody> + </table> + <table> + <thead> + <tr> + <th> + $(escape "$MSG_THEAD_TAG") + </th> + <th> + $(escape "$MSG_THEAD_AUTHOR") + </th> + <th> + $(escape "$MSG_THEAD_DATE") + </th> + </tr> + </thead> + <tbody> +EOF + + for tag in $(git tag); do + if [ "$show_signature" = true ]; then + tarball_link="(<a href=\"tarballs/$(escape "$repo")-$(escape "$tag").tar.gz\">tarball</a>, + <a href=\"tarballs/$(escape "$repo")-$(escape "$tag").tar.gz.asc\">sig</a>)" + else + tarball_link="(<a href=\"tarballs/$(escape "$repo")-$(escape "$tag").tar.gz\">tarball</a>)" + fi + cat <<EOF + <tr> + <td> + <a href="tag/$(escape "$tag").html"> + $(escape "$tag") + </a> + $tarball_link + </td> + <td> + $(escape "$(git log -1 --format=%an "$tag")") + </td> + <td> + $(escape "$(git log -1 --format=%cd --date='format:%Y-%m:%d %H:%M' "$tag")") + </td> + </tr> +EOF + done + + cat <<EOF + </tbody> + </table> + </main> + <footer> + <hr /> + <p> + $(escape "$MSG_FOOTER_TEMPLATE") + </p> + </footer> + </body> +</html> +EOF +} + +print_repo_commit_page() { + cat <<EOF +<!DOCTYPE html> +<html lang="$(escape "$MSG_LANGNAME")"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <meta name="description" content="$(escape "$description")" /> + <link rel="icon" type="image/svg+xml" href="../logo.svg" /> + <link rel="stylesheet" type="text/css" href="../style.css" /> + <link rel="alternate" type="application/atom+xml" href="../commits.xml" title="$(escape "$repo") - $(escape "$MSG_COMMIT_FEED")" hreflang="$(escape "$MSG_LANGNAME")" /> + <link rel="alternate" type="application/atom+xml" href="../tags.xml" title="$(escape "$repo") - $(escape "$MSG_TAGS_FEED")" hreflang="$(escape "$MSG_LANGNAME")" /> + <title>$(escape "$repo") - FIXME put content here</title> + </head> + <body> + <header> + <div class="header-horizontal-grouping"> + <a href="../../"> + <img alt="$(escape "$MSG_LOGO_ALT_REPOSITORY")" class="logo" src="../logo.svg" /> + </a> + <div class="header-description"> + <h1> + <a href="../"> + $(escape "$repo") + </a> + </h1> + <h2> + $(escape "$description") + </h2> + <code> + git clone $(escape "$CLONE_URL") + </code> + </div> + </div> + <nav> + <ul> + <li> + <a href="files.html"> + $(escape "$MSG_NAV_FILES") + </a> + </li> + <li> + <a href="log.html"> + $(escape "$MSG_NAV_LOG") + </a> + </li> + <li> + <a href="refs.html"> + $(escape "$MSG_NAV_REFS") + </a> + </li> + </ul> + </nav> + <hr /> + </header> + <main> + CODE HR + </main> + <footer> + <hr /> + <p> + $(escape "$MSG_FOOTER_TEMPLATE") + </p> + </footer> + </body> +</html> +EOF +} + +repo_tarballs_write() { + repo="$1" + mkdir -p "$OUTDIR/tarballs" "$CACHE_DIR/tarballs" + for branch in $(git branch --format '%(refname:lstrip=2)'); do + git archive --prefix "$repo-$branch/" "$branch" \ + -o "$OUTDIR/tarballs/$repo-$branch.tar.xz" + done + + for tag in $(git tag); do + TARBALL_PATH="tarballs/$repo-$tag.tar.gz" + if [ -e "$OUTDIR/$TARBALL_PATH" ]; then + continue + fi + if [ ! -e "$CACHE_DIR/$TARBALL_PATH" ]; then + git archive --prefix "$repo-$tag/" "$tag" \ + -o "$CACHE_DIR/$TARBALL_PATH" + fi + cp "$CACHE_DIR/$TARBALL_PATH" "$OUTDIR/$TARBALL_PATH" + done + + # FIXME: write signatures +} + +repo_commits_write() { + mkdir -p "$OUTDIR/commit" "$CACHE_DIR/commit" + for branch in $(git branch --format '%(refname:lstrip=2)'); do + for commit in $(git rev-list "$branch"); do + COMMIT_PATH="commit/$commit.html" + if [ -e "$OUTDIR/$COMMIT_PATH" ]; then + # Assume all previous history exists, too + break + fi + if [ ! -e "CACHE_DIR/$COMMIT_PATH" ]; then + print_repo_commit_page "$repo" "$commit" + print_repo_commit_page "$repo" "$commit" \ + > "$CACHE_DIR/$COMMIT_PATH" + fi + cp "$CACHE_DIR/$COMMIT_PATH" "$OUTDIR/$COMMIT_PATH" + + # FIXME: dbg + return + done + done +} + +repo_write() { + cd "$1" + repo="$(basename "$(realpath "${1%.git}")")" + description="$(cat "$1/description" 2>/dev/null ||:)" + + # FIXME: hardcoded "true" + repo_tarballs_write "$repo" + print_repo_refs "$repo" "$description" true > "$OUTDIR/refs.html" + + repo_commits_write "$repo" + + print_logo > "$OUTDIR/logo.svg" + print_style > "$OUTDIR/style.css" + cd - > /dev/null +} + + +for flag in "$@"; do + case "$flag" in + --) + break + ;; + --help) + usage + help + exit + ;; + --version) + version + exit + ;; + *) + ;; + esac +done + +INDEX=false +TITLE="$MSG_DEFAULT_TITLE" +OUTDIR= +CLONE_URL= +CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/gistatic" +while getopts 'o:t:u:ihV' flag; do + case "$flag" in + i) + INDEX=true + ;; + t) + TITLE="$OPTARG" + ;; + o) + OUTDIR="$(realpath "$OPTARG")" + ;; + u) + CLONE_URL="$OPTARG" + ;; + h) + usage + help + exit + ;; + V) + version + exit + ;; + *) + usage >&2 + exit 2 + ;; + esac +done +shift $((OPTIND - 1)) + +assert_arg() { + if [ -z "$1" ]; then + printf "$MSG_MISSING_CLIARG" "$2" >&2 + exit 2 + fi +} + +assert_arg "$OUTDIR" '-o DIRECTORY' + +if [ "$INDEX" = false ]; then + assert_arg "$CLONE_URL" '-u CLONE_URL' +elif [ -n "$CLONE_URL" ]; then + printf '%s\n' "$MSG_INCOMPATIBLE_OPTIONS" >&2 + exit 2 +fi + +if [ -z "${1:-}" ]; then + printf '%s\n' "$MSG_MISSING_ARGS" >&2 + exit 2 +fi + +mkdir -p "$OUTDIR" + +if [ "$INDEX" = true ]; then + index_write "$@" +else + repo_write "$1" +fi |