Add basic pages

Julien LepillerSun Sep 27 21:12:53+0200 2020

e22544e

Add basic pages

.gitignore unknown status 1

1+
*.go
2+
*.log
3+
configure
4+
config.status
5+
Makefile
6+
Makefile.in
7+
aclocal.m4
8+
autom4te.cache
9+
install-sh
10+
missing
11+
pre-inst-env
12+
scripts/gitile

Makefile.am unknown status 1

1+
# Copyright (C) 2020 Julien Lepiller <julien@lepiller.eu>
2+
#
3+
# This library is free software; you can redistribute it and/or
4+
# modify it under the terms of the GNU Lesser General Public
5+
# License as published by the Free Software Foundation; either
6+
# version 3 of the License, or (at your option) any later version.
7+
#
8+
# This library is distributed in the hope that it will be useful,
9+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11+
# Lesser General Public License for more details.
12+
#
13+
# You should have received a copy of the GNU Lesser General Public
14+
# License along with this library; if not, write to the Free Software
15+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16+
17+
include guile.am
18+
19+
SOURCES= \
20+
    gitile/config.scm \
21+
    gitile/handler.scm \
22+
    gitile/pages.scm \
23+
    gitile/repo.scm
24+
25+
clean-go:
26+
	find . -name '*.go' -delete

bootstrap unknown status 1

1+
autoreconf -fiv

configure.ac unknown status 1

1+
#                                               -*- Autoconf -*-
2+
# Process this file with autoconf to produce a configure script.
3+
4+
AC_PREREQ([2.69])
5+
AC_INIT([social], [0.1], [julien@lepiller.eu])
6+
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
7+
8+
AC_CONFIG_MACRO_DIR([m4])
9+
10+
# Checks for programs.
11+
GUILE_SITE_DIR
12+
GUILE_PROGS
13+
14+
# Checks for libraries.
15+
GUILE_MODULE_AVAILABLE([have_git], [(git)])
16+
if test "x$have_git" != "xyes"; then
17+
  AC_MSG_ERROR([The Guile bindings of libgit2 are missing; please install them.])
18+
fi
19+
20+
GUILE_MODULE_AVAILABLE([have_gnutls], [(gnutls)])
21+
if test "x$have_gnutls" != "xyes"; then
22+
  AC_MSG_ERROR([The Guile bindings of GnuTLS are missing; please install them.])
23+
fi
24+
25+
GUILE_MODULE_AVAILABLE([have_fibers], [(fibers web server)])
26+
if test "x$have_fibers" != "xyes"; then
27+
  AC_MSG_ERROR([The Guile Fibers library is missing; please install it.])
28+
fi
29+
30+
AC_CONFIG_FILES([pre-inst-env], [chmod +x pre-inst-env])
31+
AC_CONFIG_FILES([scripts/gitile], [chmod +x scripts/gitile])
32+
AC_CONFIG_FILES(Makefile)
33+
34+
AC_OUTPUT

gitile/config.scm

1919
  #:use-module (srfi srfi-9)
2020
  #:export (make-config
2121
            config?
22+
	    <config>
2223
            config-port
2324
            config-host
2425
            config-database
2526
            config-repositories))
2627
27-
(define-record-type config
28+
(define-record-type <config>
2829
  (make-config port host database repositories)
2930
  config?
3031
  (port         config-port)

gitile/handler.scm

1818
(define-module (gitile handler)
1919
  #:use-module (fibers web server)
2020
  #:use-module (git)
21+
  #:use-module (gitile config)
2122
  #:use-module (gitile pages)
2223
  #:use-module (ice-9 match)
24+
  #:use-module (sxml simple)
2325
  #:use-module (web request)
2426
  #:use-module (web response)
2527
  #:use-module (web uri)

4547
         (nav
4648
           (ul
4749
             (li (@ (class "first")) (a (@ (href "/")) "Projects"))
48-
             (li (a (@ (href "/" project)) "Repository"))
49-
             (li (a (@ (href "/" project "/tree/" ref)) "Files"))
50-
             (li (a (@ (href "/" project "/ommits")) "Commits"))
51-
             (li (a (@ (href "/" project "/tags")) "Tags")))))
50+
             (li (a (@ (href "/" ,project)) "Repository"))
51+
             (li (a (@ (href "/" ,project "/tree/" ,ref)) "Files"))
52+
             (li (a (@ (href "/" ,project "/commits")) "Commits"))
53+
             (li (a (@ (href "/" ,project "/tags")) "Tags")))))
5254
       (div (@ (id "content"))
5355
            ,@page)
5456
       (footer (p (a (@ (href "https://lepiller.eu")) "Who am I?"))))))

6365
6466
(define (gitile-handler config)
6567
  (define (get-repo name)
66-
    (repository-open (string-append repositories "/" name ".git")))
68+
    (let ((repo (string-append (config-repositories config)
69+
                               "/" name ".git")))
70+
      (pk 'repo repo)
71+
      (pk 'exists (file-exists? (string-append repo "/git-daemon-export-ok")))
72+
      (if (file-exists? (string-append repo "/git-daemon-export-ok"))
73+
          (repository-open repo)
74+
          #f)))
6775
6876
  (match config
69-
    (($ config port host database repositories)
77+
    (($ <config> port host database repositories)
7078
     (lambda (request body)
7179
       (pk 'request request)
7280
       (pk 'body (if body (utf8->string body) body))
7381
       (match (request-path-components request)
74-
         ((project-name) (show (project-index project-name)))
82+
         ((project-name)
83+
          (show (style (project-index project-name (get-repo project-name))
84+
                       project-name "-")))
7585
         ((project-name "tree" ref path ...)
76-
          (show (style (project-files (get-repo project-name) #:ref ref #:path path)
86+
          (show (style (project-files project-name (get-repo project-name)
87+
				      #:ref ref #:path path)
7788
                       project-name ref)))
7889
         ((project-name "raw" ref path ...)
7990
          (show-raw (project-file-raw (get-repo project-name) path #:ref ref)))
8091
         ((project-name "commits")
81-
          (style (show not-yet-page) project-name "-"))
92+
          (show (style (project-commits project-name (get-repo project-name) #f)
93+
		       project-name "-")))
8294
         ((project-name "commits" ref)
95+
          (show (style (project-commits project-name (get-repo project-name) ref)
96+
		       project-name ref)))
97+
         ((project-name "commit" ref)
8398
          (show (style not-yet-page project-name ref)))
8499
         ((project-name "tags")
85-
          (show (style not-yet-page project-name "-"))))))))
100+
          (show (style not-yet-page project-name "-")))
101+
	 (_ (not-found request)))))))

gitile/pages.scm

1717
1818
(define-module (gitile pages)
1919
  #:use-module (gitile repo)
20+
  #:use-module (git)
21+
  #:use-module (git types)
22+
  #:use-module (ice-9 match)
23+
  #:use-module (rnrs bytevectors)
24+
  #:use-module (srfi srfi-19); date/time
25+
  #:use-module (system foreign)
2026
  #:export (not-yet-page
2127
            project-file-raw
22-
            project-files))
28+
            project-files
29+
            project-index
30+
	    project-commits))
2331
2432
(define not-yet-page
25-
  `(p "Not yet available, sorry :/"))
33+
  `((p "Not yet available, sorry :/")))
2634
27-
(define* (project-file-raw repo path #:key (ref #f))
28-
  (get-file-content repo path #:ref ref))
35+
(define* (project-file-raw repo path #:key (ref "-"))
36+
  (let* ((ref (if (equal? ref "-") #f ref))
37+
         (path (if (list? path)
38+
                   (string-join
39+
                     (filter (lambda (p) (not (string-null? p))) path)
40+
                     "/")
41+
                   path)))
42+
    (get-file-content repo path #:ref ref)))
2943
30-
(define* (project-files repo #:key (ref #f) (path ""))
31-
  (let ((files (get-files repo #:ref ref #:path path)))
44+
(define* (project-files repository-name repo #:key (ref "-") (path '()))
45+
  (let* ((ref (if (equal? ref "-") #f ref))
46+
         (path (if (list? path)
47+
                   (string-join
48+
                     (filter (lambda (p) (not (string-null? p))) path)
49+
                     "/")
50+
                   path))
51+
         (dir-path (if (string-null? path) path (string-append path "/")))
52+
         (files (sort (get-files repo #:ref ref #:path dir-path)
53+
                      (lambda (f1 f2)
54+
                        (string<=? (file-name f1) (file-name f2))))))
55+
    (pk 'path dir-path)
56+
    (pk 'files files)
3257
    (if (and (string-null? path) (null? files))
3358
        `(p "Empty repository")
3459
        (if (null? files)
35-
            `((p "file content")
36-
              (pre ,@(get-file-content repo path #:ref ref)))
37-
            `((p "directory content")
38-
              ,@(map
39-
                  (lambda (file)
40-
                    `(p ,file))
41-
                  files))))))
60+
            `((p (@ (class "content")) "file content")
61+
              (pre ,(pk (utf8->string (get-file-content repo path #:ref ref)))))
62+
            `(,(last-commit-infobox repository-name repo ref)
63+
              (table
64+
                (thead
65+
                  (tr
66+
                    (td "name")
67+
                    (td "last commit")
68+
                    (td "date")))
69+
                (tbody
70+
                 ,@(map
71+
                     (match-lambda
72+
                       (($ <file> name type commit)
73+
                        (let ((name (if (= type 2) (string-append name "/") name)))
74+
                          `(tr (td (a (@ (href "/" ,repository-name
75+
                                               "/tree/" ,(or ref "-")
76+
                                               "/" ,path "/" ,name))
77+
                                      (img (@ (src "/images/file-type-" ,type ".png")))
78+
                                      ,name))
79+
                               (td (a (@ (href "/" ,repository-name
80+
                                               "/commit/" ,(oid->string
81+
							     (commit-id commit))))
82+
                                      ,(commit-message commit)))
83+
                               (td ,(commit->date-string commit))))))
84+
                     files))))))))
85+
86+
(define get-config-string
87+
  (let ((proc (libgit2->procedure int "git_config_get_string" '(* * *))))
88+
    (lambda (config name)
89+
      (let ((out (make-double-pointer)))
90+
        (proc out (config->pointer config) (string->pointer name))
91+
        (pointer->string out)))))
92+
93+
(define (project-index repository-name repo)
94+
  `((h1 ,repository-name)
95+
    (p ,(get-config-string (repository-config repo)
96+
                           "gitweb.description"))
97+
    ,(project-files repository-name repo)))
98+
99+
(define (author-image author)
100+
  "/images/unknown.png")
101+
102+
(define (commit->date-string commit)
103+
  (date->string
104+
    (time-utc->date
105+
      (make-time time-utc 0 (commit-time commit)))))
106+
107+
(define (last-commit-infobox repository-name repo ref)
108+
  (let* ((ref (if (equal? ref "-") #f ref))
109+
         (commit (last-commit repo ref)))
110+
    (commit-infobox repository-name commit)))
111+
112+
(define (commit-infobox repository-name commit)
113+
  `(div (@ (class "commit-info"))
114+
     (p (img (@ (src ,(author-image (commit-author commit))))))
115+
     (div (@ (class "commit"))
116+
       (p (@ (class "message"))
117+
	  (a (@ (href "/" ,repository-name "/commit/"
118+
		      ,(oid->string (commit-id commit))))
119+
	     ,(commit-message commit)))
120+
       (p (span (@ (class "author"))
121+
                ,(signature-name (commit-author commit)))
122+
          (span (@ (class "date"))
123+
            ,(commit->date-string commit))))
124+
     (div (@ (class "commit-id"))
125+
          (p (@ (class "short-id"))
126+
             ,(string-take (oid->string (commit-id commit)) 7))
127+
          (button (@ (data-clipboard-copy ,(oid->string (commit-id commit)))
128+
                     (class "copy"))
129+
              (img (@ (src "/images/copy.png")))))))
130+
131+
(define (project-commits repository-name repo ref)
132+
  (let* ((commits (get-commits repo ref))
133+
	 (next (cdr commits))
134+
	 (commits (car commits)))
135+
    (pk 'commits commits)
136+
    `(,(map (lambda (commit)
137+
	      (commit-infobox repository-name commit))
138+
	    commits)
139+
      ,(if next
140+
	   `(p (a (@ (href "/" ,repository-name "/commits/"
141+
			   ,(oid->string (commit-id next))))))
142+
	   '()))))

gitile/repo.scm

1717
1818
(define-module (gitile repo)
1919
  #:use-module (git)
20+
  #:use-module (git types)
21+
  #:use-module (srfi srfi-9)
22+
  #:use-module (system foreign)
2023
  #:export (get-branches
2124
            get-tags
22-
            get-files))
25+
            get-files
26+
            get-file-content
27+
	    get-commits
28+
            last-commit
29+
30+
            <file>
31+
            make-file
32+
            file?
33+
            file-name
34+
            file-type))
35+
36+
(define tree-entry-type
37+
  (let ((proc (libgit2->procedure int "git_tree_entry_type" '(*))))
38+
    (lambda (entry)
39+
      (proc (tree-entry->pointer entry)))))
2340
2441
(define (get-branches repo)
2542
  (map (lambda (ref) (cons (reference-shorthand ref) (reference-name ref)))

4259
                      (false-if-exception (reference-name (repository-head repo))))))
4360
    (or (false-if-exception (string->oid ref-name))
4461
        (false-if-exception (reference-name->oid repo ref-name))
45-
        (reference-name->oid (search-reference repo ref-name)))))
62+
        (reference-name->oid repo (search-reference repo ref-name)))))
63+
64+
(define-record-type <file>
65+
  (make-file name type commit)
66+
  file?
67+
  (name   file-name)
68+
  (type   file-type)
69+
  (commit file-commit))
4670
4771
(define* (get-files repo #:key (path "") (ref #f))
4872
  (let* ((oid (ref->oid repo ref))

5377
        tree TREEWALK-PRE
5478
        (lambda (root entry)
5579
          (when (equal? root path)
56-
            (set! result (cons (string-append root (tree-entry-name entry))
57-
                               result)))
58-
          0)))))
80+
            (set! result
81+
              (cons (make-file (tree-entry-name entry)
82+
                               (tree-entry-type entry)
83+
                               (last-commit-for-file
84+
                                 (string-append root (tree-entry-name entry))
85+
                                 commit))
86+
                    result)))
87+
          0))
88+
      result)))
5989
60-
(define (find-tree-item tree name)
61-
  (let ((result #f))
62-
    (tree-walk
63-
      tree TREEWALK-PRE
64-
      (lambda (root entry)
65-
        (let ((filepath (string-append root (tree-entry-name entry))))
66-
          (when (equal? name filepath)
67-
            (set! result entry))
68-
          0)))
69-
    result))
90+
(define (last-commit repo ref)
91+
  (let ((oid (ref->oid repo ref)))
92+
    (commit-lookup repo oid)))
7093
7194
(define* (get-file-content repo path #:key (ref #f))
72-
  (let* ((oid (ref->oid ref))
95+
  (let* ((oid (ref->oid repo ref))
7396
         (commit (commit-lookup repo oid))
7497
         (tree (commit-tree commit))
75-
         (entry (find-tree-item tree path))
76-
         (entry-oid (entry-id entry))
98+
         (entry (pk 'tree-item (tree-entry-bypath tree (pk 'path path))))
99+
         (entry-oid (tree-entry-id entry))
77100
         (blob (blob-lookup repo entry-oid)))
78101
    (blob-content blob)))
102+
103+
(define (last-commit-for-file path commit)
104+
  (let* ((initial-tree (commit-tree commit))
105+
         (initial-entry (false-if-exception (tree-entry-bypath initial-tree path))))
106+
    (let loop ((commit commit))
107+
      (let ((pcommit (false-if-exception (commit-parent commit))))
108+
        (if pcommit
109+
            (let* ((ptree (commit-tree pcommit))
110+
                   (pentry (false-if-exception (tree-entry-bypath ptree path))))
111+
              (if (or (and (not initial-entry) (not pentry))
112+
                      (and initial-entry pentry
113+
                           (equal? (tree-entry-id initial-entry)
114+
                                   (tree-entry-id pentry))))
115+
                  (loop pcommit)
116+
                  commit))
117+
            commit)))))
118+
119+
(define (get-commits repo ref)
120+
  (let* ((oid (ref->oid repo ref))
121+
	 (commit (commit-lookup repo oid)))
122+
    (let loop ((result (list commit)) (commit commit))
123+
      (let ((pcommit (false-if-exception (commit-parent commit))))
124+
	(if (and pcommit (< (length result) 20))
125+
	    (loop (cons pcommit result) pcommit)
126+
	    (cons (reverse result) pcommit))))))

guile.am unknown status 1

1+
moddir=$(datadir)/guile/site/$(GUILE_EFFECTIVE_VERSION)
2+
godir=$(libdir)/guile/$(GUILE_EFFECTIVE_VERSION)/site-ccache
3+
4+
GOBJECTS = $(SOURCES:%.scm=%.go)
5+
6+
nobase_mod_DATA = $(SOURCES) $(NOCOMP_SOURCES)
7+
nobase_go_DATA = $(GOBJECTS)
8+
9+
# Make sure source files are installed first, so that the mtime of
10+
# installed compiled files is greater than that of installed source
11+
# files.  See
12+
# <http://lists.gnu.org/archive/html/guile-devel/2010-07/msg00125.html>
13+
# for details.
14+
guile_install_go_files = install-nobase_goDATA
15+
$(guile_install_go_files): install-nobase_modDATA
16+
17+
CLEANFILES = $(GOBJECTS)
18+
EXTRA_DIST = $(SOURCES) $(NOCOMP_SOURCES)
19+
GUILE_WARNINGS = -Wunbound-variable -Warity-mismatch -Wformat
20+
SUFFIXES = .scm .go
21+
.scm.go:
22+
	$(AM_V_GEN)$(top_builddir)/pre-inst-env $(GUILD) compile $(GUILE_WARNINGS) -o "$@" "$<"

guix.scm unknown status 1

1+
;;;; Copyright (C) 2020 Julien Lepiller <julien@lepiller.eu>
2+
;;;; 
3+
;;;; This library is free software; you can redistribute it and/or
4+
;;;; modify it under the terms of the GNU Lesser General Public
5+
;;;; License as published by the Free Software Foundation; either
6+
;;;; version 3 of the License, or (at your option) any later version.
7+
;;;; 
8+
;;;; This library is distributed in the hope that it will be useful,
9+
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11+
;;;; Lesser General Public License for more details.
12+
;;;; 
13+
;;;; You should have received a copy of the GNU Lesser General Public
14+
;;;; License along with this library; if not, write to the Free Software
15+
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16+
;;;; 
17+
18+
(use-modules (guix packages)
19+
             (guix licenses)
20+
             (guix build-system gnu)
21+
             (gnu packages autotools)
22+
             (gnu packages guile)
23+
             (gnu packages guile-xyz)
24+
             (gnu packages pkg-config)
25+
             (gnu packages tls))
26+
27+
(package
28+
  (name "gitile")
29+
  (version "0.1")
30+
  (source #f)
31+
  (build-system gnu-build-system)
32+
  (inputs
33+
   `(("guile-git" ,guile-git)
34+
     ("gnutls" ,gnutls)
35+
     ("guile-fibers" ,guile-fibers)))
36+
  (native-inputs
37+
   `(("autoconf" ,autoconf)
38+
     ("automake" ,automake)
39+
     ("libtool" ,libtool)
40+
     ("pkg-config" ,pkg-config)
41+
     ("guile" ,guile-3.0)))
42+
  (home-page "https://git.lepiller.eu")
43+
  (synopsis "")
44+
  (description "")
45+
  (license gpl3+))

pre-inst-env.in unknown status 1

1+
#!/bin/sh
2+
3+
abs_top_srcdir="`cd "@abs_top_srcdir@" > /dev/null; pwd`"
4+
abs_top_builddir="`cd "@abs_top_builddir@" > /dev/null; pwd`"
5+
6+
GUILE_LOAD_COMPILED_PATH="$abs_top_builddir${GUILE_LOAD_COMPILED_PATH:+:}$GUILE_LOAD_COMPILED_PATH"
7+
GUILE_LOAD_PATH="$abs_top_builddir:$abs_top_srcdir${GUILE_LOAD_PATH:+:}:$GUILE_LOAD_PATH"
8+
export GUILE_LOAD_COMPILED_PATH GUILE_LOAD_PATH
9+
10+
PATH="$abs_top_builddir:$PATH"
11+
export PATH
12+
13+
exec "$@"

scripts/gitile.in unknown status 1

1+
#!@GUILE@ \
2+
--no-auto-compile -e main -s
3+
!#
4+
5+
;;;; Copyright (C) 2020 Julien Lepiller <julien@lepiller.eu>
6+
;;;;
7+
;;;; This library is free software; you can redistribute it and/or
8+
;;;; modify it under the terms of the GNU Lesser General Public
9+
;;;; License as published by the Free Software Foundation; either
10+
;;;; version 3 of the License, or (at your option) any later version.
11+
;;;;
12+
;;;; This library is distributed in the hope that it will be useful,
13+
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15+
;;;; Lesser General Public License for more details.
16+
;;;;
17+
;;;; You should have received a copy of the GNU Lesser General Public
18+
;;;; License along with this library; if not, write to the Free Software
19+
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+
;;;;
21+
22+
(use-modules (fibers web server))
23+
(use-modules (ice-9 match))
24+
(use-modules (gitile config) (gitile handler))
25+
26+
(define* (main #:optional (args (command-line)))
27+
  (let ((config
28+
         (match args
29+
      ((_) (make-config 8080 "localhost" "/var/lib/gitile/gitile-db.sql"
30+
                        "/var/lib/gitolite/repositories"))
31+
      ((_ "-c" file)
32+
       (let ((content (call-with-input-file file read)))
33+
         (match content
34+
           (('config ('port port) ('host host) ('database database)
35+
	             ('repositories repositories))
36+
            (make-config port host database repositories)))))
37+
      (_ (format #t "Usage: ~a [-c config-file]~%" (car args))))))
38+
    (run-server (gitile-handler config)
39+
                #:port (config-port config))))