#gitlab 연관된 merge request가 없는 commit을 org todo로 만들기

1 minute read

merge request는 org todo로 만들어서 잘 쓰고 있다. merge request 단위로는 계속 읽고 있는데, 아쉽게도 이게 다가 아니다. master에 직접 push도 허용하고 있어서 연관된 merge request가 없는 커밋(commit)도 있다. 주로 아티스트 커밋이지만 종종 프로그래머 커밋도 있다. 놓치면 안 되는 게 있어서 만들었다.

(defun commits-url (begin-date end-date page)
  (let ((before (format "%sT00:00:00.000%%2B09:00" end-date))
        (after (format "%sT00:00:00.000%%2B09:00" begin-date)))
    (format "%s/repository/commits?page=%d&per_page=100&since=%s&until=%s&private_token=%s"
            gitlab-api-base-url
            page
            after
            before
            gitlab-private-key)))

list repository commits API를 사용했다. merge request를 org todo로 만들 때는 하루에 100개가 넘는 일이 없어 대충 넘어갔는데, 커밋은 100개 넘는 일이 허다하다. 그래서 pagination 파라미터를 추가했다.

(defun commits (begin-date end-date)
  (let ((id-titles '())
        (json-array-type 'list)
        (json-object-type 'plist)
        (json-key-type 'keyword))
    (dotimes (page 10) ;; 현명하게 그만둘 수 있는 방법이 있을텐데. 무식하게 10번 돈다 고고
      (with-temp-buffer
        (url-insert-file-contents (commits-url begin-date end-date (+ page 1)))
        (let ((content (json-read)))
          (dolist (commit content)
            (add-to-list 'id-titles
                         (list :id
                               (plist-get commit :id)
                               :title
                               (plist-get commit :title)))))))
    id-titles))

이제 보니 while 문 있네. 나중에 수정하지 뭐. 지금은 무식하게 10번 돈다. emacs에 property list 있는지 처음 알았다. json-object-type 변수에 ’plist를 바인딩하면 json-read 함수 리턴 값으로 property list를 리턴한다. key 타입도 바꿀 수 있다. 키워드 타입으로 변경.

(defun merge-request-url-from-commit (commit-id)
  (format "%s/repository/commits/%s/merge_requests?private_token=%s"
          gitlab-api-base-url
          commit-id
          gitlab-private-key))

(defun commits-without-merge-request (begin-date end-date)
  (let ((ret '())
        (json-array-type 'list)
        (source-commits (commits begin-date end-date)))
    (message (format "total commit count - %d" (length source-commits)))
    (dolist (c source-commits)
      (with-temp-buffer
        (url-insert-file-contents (merge-request-url-from-commit (plist-get c :id)))
        (let ((json-key-type 'string)
              (content (json-read)))
          (when (eq 0 (length content))
            (add-to-list 'ret c)))))
    ret))

commit 조회할 때, merge request id만이라도 있으면 좋으련만. 그런 거 없다. 그래서 commit을 순회하며 list merge requests associated with a commit API를 호출해서 merge request를 구한다. 비어있으면 연관된 merge request가 없는 거.

(defun insert-gitlab-commit-link (id)
  (insert (format "[[%s/commit/%s][%s]]" gitlab-base-url id (substring id 0 10))))

(defun insert-gitlab-commits-without-mr-range ()
  (interactive)
  (let* ((begin-date (org-read-date))
         (end-date (org-read-date))
         (source-commits (commits-without-merge-request begin-date end-date)))
    (dolist (c source-commits)
      (insert "*** TODO ")
      (insert-gitlab-commit-link (plist-get c :id))
      (insert (format " %s" (plist-get c :title)))
      (insert "\n"))))

merge request처럼 TODO 항목으로 추가한다. git commit id는 앞에서 10자 잘라서 표시.

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