어제 동료가 커밋한 코드를 HelixSwarm에서 읽어 org mode TODO 항목으로 추가

2 minute read

매일 동료가 어제 커밋한 코드를 읽는 방법 ver.2022 - ohyecloudy.com’ 글처럼 동료가 커밋한 코드를 읽는 루틴을 만들었다. 최근 코드 리뷰 툴이 깃랩(gitlab)에서 HelixSwarm으로 바뀌어서 HelixSwarm API를 사용한 함수를 구현했다.

HelixSwarm API를 사용해 리뷰 항목을 가져온다

HelixSwarm 2022.2 버전을 사용하고 있다.

(defun my/swarm--fetch-reviews (username ticket host project begin-date end-date)
  (let* ((base64-encoded (base64url-encode-string (format "%s:%s" username ticket)))
         (basic-authorization (format "Basic %s" base64-encoded))
         (api-url (format "%s/api/v11/reviews?max=500&project[]=%s" host project))
         (url-request-extra-headers `(("Authorization" . ,basic-authorization)
                                      ("Content-type" . "application/json; charset=utf-8")))
         (json-key-type 'keyword)
         (json-object-type 'plist)
         (reviews '()))
    (with-temp-buffer
      (url-insert-file-contents api-url)
      (let ((content (json-read)))
        (dolist (c content)
          (seq-doseq (r (plist-get c :reviews))
            (when (my/swarm--between-date-p begin-date
                                            end-date
                                            (my/swarm--to-iso8601 (plist-get r :created)))
              (add-to-list 'reviews
                           (list :id (plist-get r :id)
                                 :author (plist-get r :author)
                                 :title (my/swarm--extract-title (plist-get r :description))
                                 :description (plist-get r :description))))))))
    reviews))

(defun my/swarm--extract-title (desc)
  (car (split-string desc "\n")))

(defun my/swarm--to-iso8601 (unix-time)
  (format-time-string "%FT%T%z" (seconds-to-time unix-time)))

(defun my/swarm--between-date-p (begin end target)
  (let ((begin (parse-iso8601-time-string begin))
        (end (parse-iso8601-time-string end))
        (target (parse-iso8601-time-string target)))
    (and (>= (time-subtract target begin) 0)
         (>= (time-subtract end target) 0))))

/api/v11/reviews GET 메서드(method)를 사용해 리뷰 항목을 가져온다.

HTTP 기본 인증(basic authentication)

처음 사용해 봤다. 항상 키를 발급받곤 했는데, HelixSwarm은 HTTP 기본 인증만 지원한다.

(let* ((base64-encoded (base64url-encode-string (format "%s:%s" username ticket)))
       (basic-authorization (format "Basic %s" base64-encoded))
       (url-request-extra-headers `(("Authorization" . ,basic-authorization))))
  ;; ...
  )

user:password 스트링을 base64로 인코딩한 문자열 앞에 Basic 을 붙여주면 된다.

리뷰 생성 날짜를 검사해 가져오기

날짜를 두 개 입력받아 그사이에 생성한 리뷰 항목을 가져오고 싶었다.

(let* ((api-url (format "%s/api/v11/reviews?max=500&project[]=%s" host project))
       (json-key-type 'keyword)
       (json-object-type 'plist)
       (reviews '()))
  (with-temp-buffer
    (url-insert-file-contents api-url)
    (let ((content (json-read)))
      (dolist (c content)
        (seq-doseq (r (plist-get c :reviews))
          (when (my/swarm--between-date-p begin-date
                                          end-date
                                          (my/swarm--to-iso8601 (plist-get r :created)))
            ;;...
            ))))))

하지만 HelixSwarm에서는 afterUpdated 파라미터 하나만 지원한다. 그래서 한 번에 가져올 수 있는 최대 개수인 500개를 가져오고 ’emacs lisp로 ISO 8601 날짜와 시간 비교 - ohyecloudy.com’ 글을 참고해 생성 날짜를 직접 비교해서 필터링한다.

org-mode TODO 아이템으로 리뷰를 추가

my/swarm--fetch-reviews 함수에서 HelixSwarm API를 사용해 리뷰 정보를 가져와 가공하는 작업을 했다. 이걸 사용해서 org-mode TODO 항목으로 추가한다.

(defun my/swarm-insert-reviews ()
  (interactive)
  (let* ((begin-date (my/swarm--org-read-date-to-iso8601 (org-read-date)))
         (end-date (my/swarm--org-read-date-to-iso8601 (org-read-date)))
         (username (my/swarm--get-secret "username"))
         (ticket (my/swarm--get-secret "ticket"))
         (host (my/swarm--get-secret "host"))
         (project (my/swarm--get-secret "project"))
         (reviews (my/swarm--fetch-reviews username ticket host project begin-date end-date)))
    (message "fetched reviews. begin: %S after: %S count: %d" begin-date end-date (length reviews))
    (dolist (r reviews)
      (let ((web-url (format "%s/reviews/%s" host (plist-get r :id))))
        (insert "*** TODO ")
        (insert (format "%s" (plist-get r :id)))
        (insert (format " [%s] %s"
                        (plist-get r :author)
                        (plist-get r :title)))
        (org-return)
        (org-set-property "Url" web-url)
        (org-return)
        (insert "     #+BEGIN_QUOTE\n")
        (insert (replace-regexp-in-string "^.*?" "     " (plist-get r :description)))
        (insert "#+END_QUOTE\n")
        (org-return)))))

(defun my/swarm--org-read-date-to-iso8601 (date)
  (format "%sT00:00:00.000+09:00" date))

(defun my/swarm--get-secret (key)
  (funcall
   (plist-get
    (nth 0 (auth-source-search
            :host "myswarm"
            :user key
            :requires '(:secret)))
    :secret))
  )

함수를 실행하고 시작 날짜 종료 날짜를 고르면 그 사이에 생성한 리뷰를 *** TODO 1234 [ohyecloudy] :star: 새로운 피처 추가 같은 식으로 추가한다. 그리고 quote 사이에 커밋 메시지를 추가한다.

auth-source를 사용해 로컬 정보를 저장

퍼포스(Perforce) 암호 대신 사용하는 티켓(ticket) 정보만 저장하려고 했는데, 버전 컨트롤 안 하고 로컬에만 둘 설정을 그냥 싹 몰았다. key-value로 저장할 수 있는 로컬 설정은 그냥 편하게 auth-source로 몰아 넣으련다.

machine myswarm login username password MYUSERNAME
machine myswarm login ticket password SUPERSECRET
machine myswarm login host password https://helixswarm.com
machine myswarm login project password AWESOME_PROJECT

이런 식으로 ~/.authinfo 혹은 ~/.authinfo.gpg 파일에 저장하면 된다.

참고

C-x C-s C-x C-c