aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuchen Pei <hi@ypei.me>2023-03-22 00:42:34 +1100
committerYuchen Pei <hi@ypei.me>2023-03-22 00:42:34 +1100
commitf9792cdcfab9b2728b3db8152e62e119b135b7ac (patch)
treef07140f0a9d8fbcec0e6d30d83337eaabd8ff234
first commit
-rw-r--r--.gitignore1
-rw-r--r--buildbot-build.el4
-rw-r--r--buildbot-client.el78
-rw-r--r--buildbot-revision.el70
-rw-r--r--buildbot-utils.el50
-rw-r--r--buildbot.el10
6 files changed, 213 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e4e5f6c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*~ \ No newline at end of file
diff --git a/buildbot-build.el b/buildbot-build.el
new file mode 100644
index 0000000..ca1a8bc
--- /dev/null
+++ b/buildbot-build.el
@@ -0,0 +1,4 @@
+(define-derived-mode buildbot-build-mode special-mode "Buildbot build"
+ "Buildbot view for a build")
+
+(provide 'buildbot-build)
diff --git a/buildbot-client.el b/buildbot-client.el
new file mode 100644
index 0000000..1bb8310
--- /dev/null
+++ b/buildbot-client.el
@@ -0,0 +1,78 @@
+;; -*- lexical-binding: t; -*-
+(require 'buildbot-utils)
+
+(defun buildbot-api-change (attr)
+ (buildbot-url-fetch-json
+ (format
+ "%s/api/v2/changes?%s"
+ buildbot-host (buildbot-format-attr attr))))
+
+(defun buildbot-api-logs (stepid)
+ (buildbot-url-fetch-json
+ (format
+ "%s/api/v2/steps/%s/logs"
+ buildbot-host stepid)))
+
+(defun buildbot-api-builders ()
+ (buildbot-url-fetch-json
+ (format
+ "%s/api/v2/builders"
+ buildbot-host)))
+
+(defun buildbot-api-build (attr)
+ (buildbot-url-fetch-json
+ (format
+ "%s/api/v2/builds?%s"
+ buildbot-host (buildbot-format-attr attr))))
+
+(defun buildbot-api-step (buildid)
+ (buildbot-url-fetch-json
+ (format
+ "%s/api/v2/builds/%s/steps"
+ buildbot-host buildid)))
+
+(defun buildbot-format-log-url (logid)
+ (format "%s/api/v2/logs/%s/raw" buildbot-host logid))
+
+(defun buildbot-api-recent-changes (limit)
+ (buildbot-api-change (list (cons 'order "-changeid") (cons 'limit limit))))
+
+(defun buildbot-get-all-builders ()
+ (alist-get 'builders (buildbot-api-builders)))
+
+(defun buildbot-builder-by-id (builderid)
+ (cl-find-if
+ (lambda (builder)
+ (= (alist-get 'builderid builder) builderid))
+ buildbot-builders))
+
+(defun buildbot-get-builder-name-by-id (id)
+ (alist-get 'name (buildbot-builder-by-id id)))
+
+(defun buildbot-get-change-by-revision (revision)
+ (elt
+ (alist-get 'changes
+ (buildbot-api-change (list (cons 'revision revision))))
+ 0))
+
+(defun buildbot-get-builds-by-revision (revision)
+ (alist-get 'builds (buildbot-get-change-by-revision revision)))
+
+(defun buildbot-get-failed-builds-by-revision (revision)
+ (seq-filter
+ (lambda (build)
+ (not (equal (alist-get 'state_string build) "build successful")))
+ (buildbot-get-builds-by-revision revision)))
+
+(defun buildbot-format-builds-by-revision (revision)
+ (mapcar
+ 'buildbot-format-build
+ (buildbot-get-builds-by-revision revision)))
+
+(defun buildbot-get-steps-by-buildid (buildid)
+ (alist-get 'steps (buildbot-api-step buildid)))
+
+(defun buildbot-get-logs-by-stepid (stepid)
+ (alist-get 'logs (buildbot-api-logs stepid)))
+
+(provide 'buildbot-client)
diff --git a/buildbot-revision.el b/buildbot-revision.el
new file mode 100644
index 0000000..4f8ad57
--- /dev/null
+++ b/buildbot-revision.el
@@ -0,0 +1,70 @@
+;; -*- lexical-binding: t; -*-
+(require 'buildbot-client)
+
+(defvar-local buildbot-revision-revision-id nil)
+(defvar buildbot-revision-header-regex "^\\[.*\\]$")
+
+(define-derived-mode buildbot-revision-mode special-mode "Buildbot revision"
+ "Buildbot view for a revision")
+
+(define-key buildbot-revision-mode-map "g" 'buildbot-revision-reload)
+
+(defun buildbot-revision-buffer-name (revision)
+ (concat "*buildbot revision " revision "*"))
+
+(defun buildbot-revision-load (revision)
+ (let ((buffer-name (buildbot-revision-buffer-name revision)))
+ (with-current-buffer (get-buffer-create buffer-name)
+ (buildbot-revision-mode)
+ (setq buildbot-revision-revision-id revision)
+ (buildbot-revision-update))
+ (switch-to-buffer buffer-name)))
+
+(defun buildbot-revision-update ()
+ (unless (derived-mode-p 'buildbot-revision-mode)
+ (error "Not in buildbot revision mode"))
+ (let ((inhibit-read-only t))
+ (erase-buffer)
+ (insert (buildbot-revision-format buildbot-revision-revision-id))))
+
+(defun buildbot-revision-open (revision)
+ (interactive "sRevision (commit hash): ")
+ (buildbot-revision-load revision))
+
+(defun buildbot-revision-reload ()
+ (interactive)
+ (buildbot-revision-update))
+
+(defun buildbot-revision-format (revision)
+ (string-join
+ (mapcar 'buildbot-revision-format-build
+ (buildbot-get-builds-by-revision revision))
+ "\n"))
+
+(defun buildbot-revision-next-header (n)
+ (interactive "p")
+ (dotimes (_ n)
+ (end-of-line 1)
+ (re-search-forward buildbot-revision-header-regex)
+ (beginning-of-line 1)))
+(define-key buildbot-revision-mode-map "n" 'buildbot-revision-next-header)
+
+(defun buildbot-revision-previous-header (n)
+ (interactive "p")
+ (beginning-of-line 1)
+ (unless (looking-at buildbot-revision-header-regex)
+ (re-search-backward buildbot-revision-header-regex))
+ (dotimes (_ n)
+ (re-search-backward buildbot-revision-header-regex)))
+(define-key buildbot-revision-mode-map "p" 'buildbot-revision-previous-header)
+
+(defun buildbot-revision-format-build (build)
+ (format "[%s %s]\n%s\n"
+ (buildbot-get-builder-name-by-id (alist-get 'builderid build))
+ (alist-get 'state_string build)
+ (string-join
+ (mapcar (lambda (test) (alist-get 'test_name test))
+ (alist-get 'failed_tests build))
+ "\n")))
+
+(provide 'buildbot-revision)
diff --git a/buildbot-utils.el b/buildbot-utils.el
new file mode 100644
index 0000000..ebac706
--- /dev/null
+++ b/buildbot-utils.el
@@ -0,0 +1,50 @@
+(defun buildbot-parse-http-header (text)
+ (let ((status) (fields))
+ (with-temp-buffer
+ (insert text)
+ (goto-char (point-min))
+ (re-search-forward "^HTTP.*\\([0-9]\\{3\\}\\).*$")
+ (setq status (match-string 1))
+ (while (re-search-forward "^\\(.*?\\): \\(.*\\)$" nil t)
+ (push (cons (intern (match-string 1)) (match-string 2)) fields)))
+ (list (cons 'status status) (cons 'fields fields))))
+
+(defun buildbot-delete-http-header ()
+ (save-excursion
+ (goto-char (point-min))
+ (kill-region (point) (progn (re-search-forward "\r?\n\r?\n")
+ (point)))))
+
+(defun buildbot-url-fetch-json (url &optional decompression with-header)
+ (with-current-buffer (get-buffer-create buildbot-client-buffer-name)
+ (goto-char (point-max))
+ (insert "[" (current-time-string) "] Request: " url "\n"))
+ (with-current-buffer (url-retrieve-synchronously url t)
+ (let ((header) (status) (fields))
+ (buildbot-delete-http-header)
+ (goto-char (point-min))
+ (setq header (buildbot-parse-http-header (car kill-ring))
+ status (alist-get 'status header)
+ fields (alist-get 'fields header))
+ (with-current-buffer buildbot-client-buffer-name
+ (insert "[" (current-time-string) "] Response: " status "\n"))
+ (when decompression
+ (call-process-region (point) (point-max) "gunzip" t t t)
+ (goto-char (point-min)))
+ (call-interactively 'delete-trailing-whitespace)
+ (if (string= status "200")
+ (unless (= (point) (point-max))
+ (if with-header
+ (list
+ (cons 'header fields)
+ (cons 'json (json-read)))
+ (json-read)))
+ (error "HTTP error: %s" (buffer-substring (point) (point-max)))))))
+
+(defun buildbot-format-attr (attr)
+ (string-join (mapcar (lambda (pair)
+ (format "%s=%s" (car pair) (cdr pair)))
+ attr)
+ "&"))
+
+(provide 'buildbot-utils)
diff --git a/buildbot.el b/buildbot.el
new file mode 100644
index 0000000..7c62017
--- /dev/null
+++ b/buildbot.el
@@ -0,0 +1,10 @@
+;; -*- lexical-binding: t; -*-
+(defvar buildbot-builders (buildbot-get-all-builders))
+(defvar buildbot-client-buffer-name "*buildbot api*")
+(defvar buildbot-host nil)
+
+(require 'buildbot-revision)
+(require 'buildbot-build)
+(require 'buildbot-client)
+
+(provide 'buildbot)