summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/tissue63
-rw-r--r--tissue/web/server.scm52
2 files changed, 87 insertions, 28 deletions
diff --git a/bin/tissue b/bin/tissue
index 1f91d48..2b5ab7d 100755
--- a/bin/tissue
+++ b/bin/tissue
@@ -252,23 +252,39 @@ port."
(call-with-current-directory temporary-checkout
load-config)))))
-(define (normalize-host state-directory host)
- "Normalize @var{host} from @file{tissue.conf} adding in default and
+(define (normalize-project state-directory project)
+ "Normalize @var{project} from @file{tissue.conf} adding in default and
extra parameters for convenient processing later on.
@var{state-directory} is the tissue state directory."
- (match-lambda
+ (match project
((name . parameters)
- ;; Add repository directory, website directory, xapian directory
- ;; settings for each host.
(let ((repository-directory
(string-append state-directory "/" name "/repository")))
(cons name
- `((project . ,(load-config-from-repository repository-directory))
+ `((base-path . ,(or (assq-ref parameters 'base-path)
+ (string-append "/" name)))
+ (project . ,(load-config-from-repository repository-directory))
(repository-directory . ,repository-directory)
- (website-directory . ,(string-append state-directory "/" name "/website"))
- (xapian-directory . ,(string-append state-directory "/" name "/xapian"))
+ (website-directory . ,(string-append state-directory
+ "/" name "/website"))
+ (xapian-directory . ,(string-append state-directory
+ "/" name "/xapian"))
,@parameters))))))
+(define (normalize-host state-directory host)
+ "Normalize @var{host} from @file{tissue.conf} adding in default and
+extra parameters for convenient processing later on.
+@var{state-directory} is the tissue state directory."
+ (match host
+ ((host-name . host-parameters)
+ ;; Add repository directory, website directory, xapian directory
+ ;; settings for each host.
+ (cons host-name
+ `((projects
+ ,@(map (cut normalize-project state-directory <>)
+ (assq-ref host-parameters 'projects)))
+ ,@host-parameters)))))
+
(define tissue-web
(match-lambda*
(("--help")
@@ -455,7 +471,7 @@ HOSTNAME."
(define tissue-pull
(match-lambda*
(("--help")
- (format #t "Usage: ~a pull [OPTIONS] HOST
+ (format #t "Usage: ~a pull [OPTIONS] PROJECT
Pull latest from upstream repositories.
-C, --config=CONFIG-FILE read configuration parameters from CONFIG-FILE
@@ -470,8 +486,8 @@ Pull latest from upstream repositories.
read)
result))))
invalid-option
- (lambda (host result)
- (acons 'host host result))
+ (lambda (project result)
+ (acons 'project project result))
(default-configuration))))
(let ((state-directory (assq-ref args 'state-directory)))
;; Error out if state directory does not exist.
@@ -480,24 +496,27 @@ Pull latest from upstream repositories.
"State directory ~a does not exist.~%"
state-directory)
(exit #f))
- ;; Pull state for specificied host.
- ;; It is not a good idea to pull for all configured hosts
- ;; when no host is specified on the command line. Since
+ ;; Pull state for specificied project.
+ ;; It is not a good idea to pull for all configured projects
+ ;; when no project is specified on the command line. Since
;; pulling requires executing code in each repository,
- ;; pulling for multiple hosts in a single process can cause
- ;; interaction of code across hosts.
- (let ((hostname (assq-ref args 'host)))
+ ;; pulling for multiple projects in a single process can
+ ;; cause interaction of code across projects.
+ (let ((project-name (assq-ref args 'project)))
(cond
- ((assoc-ref (assq-ref args 'hosts)
- hostname)
+ ((assoc-ref (append-map (match-lambda
+ ((_ . parameters)
+ (assq-ref parameters 'projects)))
+ (assq-ref args 'hosts))
+ project-name)
=> (lambda (parameters)
(pull state-directory
- hostname
+ project-name
(assq-ref parameters 'upstream-repository))))
(else
(format (current-error-port)
- "Host ~a not found in configuration file."
- hostname)
+ "Project ~a not found in configuration file."
+ project-name)
(exit #f)))))))))
(define (main args)
diff --git a/tissue/web/server.scm b/tissue/web/server.scm
index e8ee9eb..afb259f 100644
--- a/tissue/web/server.scm
+++ b/tissue/web/server.scm
@@ -1,5 +1,5 @@
;;; tissue --- Text based issue tracker
-;;; Copyright © 2022, 2023 Arun Isaac <arunisaac@systemreboot.net>
+;;; Copyright © 2022, 2023, 2025 Arun Isaac <arunisaac@systemreboot.net>
;;;
;;; This file is part of tissue.
;;;
@@ -143,6 +143,36 @@ query. QUERY and FILTER are Xapian Query objects."
(list path
(string-append path ".html"))))
+(define (relative-file-name* file-name base)
+ "Work around @code{relative-file-name} from @code{(ice-9 filesystem)}
+for bug @url{https://gitlab.com/lilyp/guile-filesystem/issues/1}
+regarding handling root @var{base}"
+ (if (string=? base "/")
+ (relative-file-name (string-append "/foo" file-name)
+ "/foo")
+ (relative-file-name file-name base)))
+
+(define (rebase-uri-path path base)
+ "Rebase URI @var{path} on top of @var{base}, both absolute paths that
+start with a slash.
+
+Care is taken to preserve trailing slashes in @var{path} and ignore
+trailing slashes in @var{base}.
+
+For example,
+(rebase-uri-path \"/tissue/foo\" \"/tissue\") => \"/foo\"
+(rebase-uri-path \"/tissue/foo/\" \"/tissue\") => \"/foo/\"
+(rebase-uri-path \"/tissue/\" \"/tissue\") => \"/\"
+"
+ (let* ((relative-path (relative-file-name* path base))
+ (rebased-path (if (string=? relative-path ".")
+ "/"
+ (string-append "/" relative-path))))
+ (if (and (string-suffix? "/" path)
+ (not (string-suffix? "/" rebased-path)))
+ (string-append rebased-path "/")
+ rebased-path)))
+
(define (handler request body hosts)
"Handle web REQUEST with BODY and return two values---the response
headers and the body.
@@ -155,7 +185,15 @@ See `start-web-server' for documentation of HOSTS."
(raise (condition
(make-message-condition "Unknown host")
(make-irritants-condition hostname)))))
- (repository-directory (assq-ref host-parameters 'repository-directory)))
+ (project-parameters (any (match-lambda
+ ((_ . project-parameters)
+ (and (string-prefix? (assq-ref project-parameters 'base-path)
+ (uri-path (request-uri request)))
+ project-parameters)))
+ (assq-ref host-parameters 'projects)))
+ (path (rebase-uri-path path (assq-ref project-parameters 'base-path)))
+ (repository-directory (assq-ref project-parameters
+ 'repository-directory)))
(log-request request)
(parameterize ((%current-git-repository
(repository-open repository-directory)))
@@ -163,12 +201,14 @@ See `start-web-server' for documentation of HOSTS."
;; Static files
((let ((file-path
(find file-exists?
- (map (cut string-append (assq-ref host-parameters 'website-directory) <>)
+ (map (cut string-append
+ (assq-ref project-parameters 'website-directory)
+ <>)
(try-paths path)))))
(and file-path
;; Check that the file really is within the document
;; root.
- (string-prefix? (string-append (assq-ref host-parameters 'website-directory) "/")
+ (string-prefix? (string-append (assq-ref project-parameters 'website-directory) "/")
(canonicalize-path file-path))
(canonicalize-path file-path)))
=> (lambda (file-path)
@@ -181,8 +221,8 @@ See `start-web-server' for documentation of HOSTS."
;; necessary.
((member path (list "/" "/search"))
(search-handler request body
- (assq-ref host-parameters 'xapian-directory)
- (assq-ref host-parameters 'project)))
+ (assq-ref project-parameters 'xapian-directory)
+ (assq-ref project-parameters 'project)))
;; Not found
(else
(404-response request))))))