Include projectname in job output
[candle] / src / main / server.lisp
1 (in-package #:candle)
2
3 (defun server (port &optional (background t))
4  (when (not *candle-dir*) (error "Need a candle dir"))
5  (let*
6   ((data-dir (format nil "~Adata" *candle-dir*)))
7   (ensure-directories-exist *candle-dir*)
8   (ensure-directories-exist data-dir)
9   (lame-db:load-known-dbs data-dir)
10   (format t "Starting processor in ~(~A~) mode~%" *job-system*)
11   (start-save-thread data-dir)
12   (start-processor-thread)
13   (communication:start-listener port background)))
14
15 (defun start-save-thread (data-dir)
16  (format t "Starting Save Thread~%")
17  (let*
18   ((mutex (sb-thread:make-mutex))
19    (waitq (sb-thread:make-waitqueue))
20    (active t)
21    (save-thread
22     (sb-thread:make-thread
23      (lambda ()
24       (loop
25        :while active
26        :do
27        (sb-thread:with-mutex (mutex)
28         (sb-thread:condition-wait waitq mutex)))
29       (lame-db:save-known-dbs data-dir))
30      :name "Save Thread")))
31   (sb-thread:make-thread
32    (lambda ()
33     (loop
34      (sleep (* 1 60))
35      (sb-thread:with-mutex (mutex)
36       (sb-thread:condition-broadcast waitq))))
37    :name "Save Thread Trigger")
38   (push
39    (lambda ()
40     (format t "Shutting down save thread~%")
41     (sb-thread:with-mutex (mutex)
42      (setf active nil)
43      (sb-thread:condition-broadcast waitq))
44     (sb-thread:join-thread save-thread))
45    sb-ext:*exit-hooks*)))
46
47 (defun find-project-by-name-or-die (name)
48  (when name
49   (or
50    (find name *all-project* :test #'string= :key #'project-name)
51    (raise-candle-error :project-does-not-exist))))
52
53 (defun add-project (name src)
54  (when
55   (not (cl-ppcre:scan "^[0-9A-Za-z-]*$" name))
56   (raise-candle-error :invalid-project-name))
57  (when
58   (not (git nil "ls-remote" src))
59   (raise-candle-error :invalid-project-uri))
60  (when
61   (find name *all-project* :test #'string= :key #'project-name)
62   (raise-candle-error :project-name-taken))
63  (let
64   ((project (make-project :name name :src src)))
65   (ensure-directories-exist (project-dir project))
66   (git project "clone" src ".")
67   (refresh-project name))
68  t)
69
70 (defun refresh-project (name)
71  (let*
72   ((project (find-project-by-name-or-die name))
73    (branches (find-branch-by-project project)))
74   (git project "fetch" "origin" "--prune")
75   (multiple-value-bind (success code out err) (git project "branch" "-r" "--format" "%(refname) %(objectname)" "--list" "origin/*")
76    (declare (ignore code err))
77    (when (not success) (raise-candle-error :project-failed-to-get-branches))
78    (mapcar (lambda (branch) (set-branch-in-git branch nil)) branches)
79    (mapcar
80     (lambda (line)
81      (cl-ppcre:register-groups-bind (branch-name sha) ("refs/remotes/origin/(.*) (.*)" line)
82       (let*
83        ((job
84          (or
85           (find sha (find-job-by-project project) :test #'string= :key #'job-sha)
86           (make-job :status :queued :sha sha :project project :create-date (get-universal-time))))
87         (branch
88          (or
89           (find branch-name branches :test #'string= :key #'branch-name)
90           (make-branch :name branch-name :project project))))
91        (set-branch-in-git branch t)
92        (set-branch-job branch job))))
93     (cl-ppcre:split "\\n" out))))
94  (awaken-processor-thread))
95
96 (defun delete-project (name)
97  (let
98   ((project (find-project-by-name-or-die name)))
99   (sb-ext:delete-directory (project-dir project) :recursive t)
100   (mapcar #'nremove-job (find-job-by-project project))
101   (mapcar #'nremove-branch (find-branch-by-project project))
102   (nremove-project project)))
103
104 (defun job->job-information (job)
105  (list (project-name (job-project job)) (job-sha job) (job-status job) (job-create-date job)))
106
107 (defun project-branch-information (name)
108  (let
109   ((project (find-project-by-name-or-die name)))
110   (mapcar
111    (lambda (branch)
112     (list
113      (branch-name branch)
114      (job->job-information (branch-job branch))))
115    (remove-if-not #'branch-in-git (find-branch-by-project project)))))
116
117 (defun project-job-information (name)
118  (let
119   ((project (find-project-by-name-or-die name)))
120   (mapcar #'job->job-information
121    (if project
122     (find-job-by-project project)
123     *all-job*))))
124
125 (defun find-job-by-project-and-sha (project-name sha)
126  (let*
127   ((project (find project-name *all-project* :test #'string= :key #'project-name))
128    (job
129     (when project
130      (find-if
131       (lambda (job-sha)
132        (and (<= (length sha) (length job-sha)) (string= sha (subseq job-sha 0 (length sha)))))
133      (find-job-by-project project)
134      :key #'job-sha))))
135   (when (not job) (raise-candle-error :job-does-not-exist))
136   job))
137
138 (defun get-job-log (project-name sha)
139  (job-log (find-job-by-project-and-sha project-name sha)))
140
141 (defun retry-job (project-name sha)
142  (set-job-status (find-job-by-project-and-sha project-name sha) :queued)
143  (awaken-processor-thread))
144
145 (defun list-projects ()
146  (mapcar
147   (lambda (project)
148    (list
149     (project-name project)
150     (project-src project)
151     (failures (project-name project))))
152   *all-project*))
153
154 (defun failures (project-name)
155  (let
156   ((project (find-project-by-name-or-die project-name)))
157   (length
158    (remove-if-not
159     (lambda (branch) (eql :failed (job-status (branch-job branch))))
160     (if project (find-branch-by-project project) *all-branch*)))))