LoginSignup
23
21

More than 3 years have passed since last update.

GitLab API 利用

Last updated at Posted at 2019-04-06

GitLab API 利用

Personal Access Token を利用して GitLab API をたたく方法メモ と golang で実際に GitLab API を利用するサンプル。
GitLab API

Personal Access Token

GitLab API を利用するためにまずは Personal Access Token を取得する。取得方法は、こちらがとても分かりやすい。Personal Access Token は HTTPリクエストのヘッダか URL Query に埋め込んで利用する。

  $ curl https://gitlab.example.com/api/v4/projects?private_token=<your_access_token>

or

  $ curl --header "Private-Token: <your_access_token>" https://gitlab.example.com/api/v4/projects

URL など

URL(API v4)

  • ["https://" + [GitLabのHost] + "/api/v4/"] から始まるURLを利用。 その後ろに "projects/:id/" とか "groups/:id/" とかで続けると id 指定したprojectやgroupの情報をGETしたり、POSTでそのprojectやgroupの操作ができる。 Group and project members API

注意:取得できるレコード数 (19/5/30 追記)

pagination により、デフォルトで取得できるレコード数が 20 の場合があるので注意。

  $ curl --header "Private-Token: <your_access_token>" https://gitlab.example.com/api/v4/projects/

上記だと、Token を作成したユーザがアクセスできる Project 数が 20 より多い場合、全ての情報が取得できず 20 個までしか情報を取得できない。全ての情報を取得するには、URLクエリーに page=n (n は 1 から始まる) を指定して JSON 配列が空になるまで n をインクリメントするなどが必要となる。

  $ curl --header "Private-Token: <your_access_token>" https://gitlab.example.com/api/v4/projects/page=1
  $ curl --header "Private-Token: <your_access_token>" https://gitlab.example.com/api/v4/projects/page=2
  $ curl --header "Private-Token: <your_access_token>" https://gitlab.example.com/api/v4/projects/page=3
  ...

projects, gorups の [:id]

id には int かパス形式の string を指定することが可能。

  • int型の id: Webブラウザ でGitLabにアクセスすると確認できる。(Projectのページを開くと表示される Project ID が該当。)
  • string型の id: 例えば、"hoge/foo/bar" という階層構造の "bar" を指定したい場合 "/""%2F" に置換した値 ("hoge%2Ffoo%2Fbar") を利用。
  "https://" + [GitLabのHost] + "/api/v4/projects/hoge%2Ffoo%2Fbar"

FireFoxなどのWebブラウザで、上記の URL にアクセスすると GET で返される JSON形式が確認できる。
sample.png

curlで確認

  $ curl https://gitlab.com/api/v4/projects/tsuyuzaki%2Ftsuyu_p1?private_token=abcdefghijk
  {"id":11704151,"description":"","name":"tsuyu_p1","name_with_namespace":"tsuyuzaki / tsuyu_p1","path":"tsuyu_p1","path_with_namespace":"tsuyuzaki/tsuyu_p1","created_at":"2019-04-05T23:47:52.231Z","default_branch":null,"tag_list":[],"ssh_url_to_repo":"git@gitlab.com:tsuyuzaki/tsuyu_p1.git","http_url_to_repo":"https://gitlab.com/tsuyuzaki/tsuyu_p1.git","web_url":"https://gitlab.com/tsuyuzaki/tsuyu_p1","readme_url":null,"avatar_url":null,"star_count":0,"forks_count":0,"last_activity_at":"2019-04-05T23:47:52.231Z","namespace":{"id":4955386,"name":"tsuyuzaki","path":"tsuyuzaki","kind":"user","full_path":"tsuyuzaki","parent_id":null},"_links":{"self":"https://gitlab.com/api/v4/projects/11704151","issues":"https://gitlab.com/api/v4/projects/11704151/issues","merge_requests":"https://gitlab.com/api/v4/projects/11704151/merge_requests","repo_branches":"https://gitlab.com/api/v4/projects/11704151/repository/branches","labels":"https://gitlab.com/api/v4/projects/11704151/labels","events":"https://gitlab.com/api/v4/projects/11704151/events","members":"https://gitlab.com/api/v4/projects/11704151/members"},"archived":false,"visibility":"private","owner":{"id":3803336,"name":"tsuyuzaki","username":"tsuyuzaki","state":"active","avatar_url":"https://secure.gravatar.com/avatar/c03b2bddcaa4fb6d943e72ab23964a0f?s=80\u0026d=identicon","web_url":"https://gitlab.com/tsuyuzaki"},"resolve_outdated_diff_discussions":false,"container_registry_enabled":true,"issues_enabled":true,"merge_requests_enabled":true,"wiki_enabled":true,"jobs_enabled":true,"snippets_enabled":true,"shared_runners_enabled":true,"lfs_enabled":true,"creator_id":3803336,"import_status":"none","import_error":null,"open_issues_count":0,"runners_token":"WibD8zxQ-WsTjk1FmAZU","public_jobs":true,"ci_config_path":null,"shared_with_groups":[{"group_id":4958170,"group_name":"tsuyu_group","group_full_path":"tsuyu_group","group_access_level":30,"expires_at":null}],"only_allow_merge_if_pipeline_succeeds":false,"request_access_enabled":false,"only_allow_merge_if_all_discussions_are_resolved":false,"printing_merge_request_link_enabled":true,"merge_method":"merge","permissions":{"project_access":{"access_level":40,"notification_level":3},"group_access":null},"mirror":false,"external_authorization_classification_label":""}

Project に Member として Group 追加するPOST の例

こちらを参照。

  • URL: "https://"+host+"/api/v4/projects/"+[:id]+"/share"
  • Body(JSON): {id: string or int, group_id: int, group_access: int, expires_at: string}
    • group_access は 10,20,30,40,50 のいずれか。expires_at は指定しなくてもよい。
sample.json
{"id":"tsuyuzaki%2Ftsuyu_p1","group_id":4958170,"group_access":30,"expires_at":""}

サンプル

以下のサンプルを作成。

  • Group が Member に追加されている Project 一覧を取得するサンプル
  • 複数 Group、複数 User を Member として一括登録するサンプル

Group が Member に追加されている Project 一覧を取得するサンプル

引数に Personal Access Token と Group の URL を渡すと指定した Group が Member に追加されている ProjectURL の一覧を取得するコード。

getprjs.go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "net/url"
    "os"
    "strings"
)

type Node struct {
    ID     int
    WebURL string `json:"web_url"`
}
type GroupInfo struct {
    SharedProjects []*Node `json:"shared_projects"`
}

func main() {
    if len(os.Args) != 3 {
        fmt.Println("Please input your token and GroupURL.")
        return
    }
    url, err := url.Parse(os.Args[2])
    if err != nil {
        fmt.Fprintf(os.Stderr, "url.Parse error [%v]", err)
        return
    }

    prjID, ok := toStrID(url.Path)
    if !ok {
        return
    }
    req, err := http.NewRequest(
        "GET",
        "https://"+url.Host+"/api/v4/groups/"+prjID,
        nil)
    if err != nil {
        fmt.Fprintf(os.Stderr, "http.NewRequest error[%v]\n", err)
        return
    }
    req.Header.Set("Private-Token", os.Args[1])

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Fprintf(os.Stderr, "client.Do error[%v]\n", err)
        return
    }
    defer resp.Body.Close()

    var result GroupInfo
    if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
        fmt.Fprintf(os.Stderr, "Decode error[%v]\n", err)
        return
    }
    for _, p := range result.SharedProjects {
        fmt.Println(p.WebURL)
    }
}

func toStrID(path string) (string, bool) {
    if path == "" {
        fmt.Println("Invalid Path")
        return "", false
    }
    if path[0] == '/' {
        return strings.Replace(path[1:], `/`, `%2F`, -1), true
    } else {
        return strings.Replace(path, `/`, `%2F`, -1), true
    }
}

実行

  $ go build getprjs.go
  $ getprjs.exe xyzabcdefg https://gitlab.com/tsuyu_group
  https://gitlab.com/tsuyuzaki/tsuyu_p1

複数 Group、複数 User を Member として一括登録するサンプル

サンプル
以下の2つの機能を有します。
1. 複数 User を、複数の Group に Member として一括登録する。
2. 複数 Group を、複数の Project に Member として一括登録する。

実行

ExcelがインストールされたWindows上で実行してください。

 $ go build postto.go
 $ postto.exe [g or p] <Personal Access Token>
  • 第一引数に g 指定すると 1. の機能が、p 指定すると 2. の機能が利用できます。
  • posttoを実行すると、groups.csv または、users.csv というファイルが Excel で開かれるので、以下の形式の CSV を記述してください。(# から始まる行は読み飛ばします。)
    • プログラム実行ディレクトリに groups/users.csv というファイルがなければ作成し、ファイルが存在すれば、その CSV ファイルが開かれます。
users.csv
[Group1のURL],[AccessLevel(10/20/30 or 40)],[UserName1],[UserName2],...
[Group2のURL],[AccessLevel(10/20/30 or 40)],[UserName1],[UserName2],[UserName3],...
...

CSV を上書きして、Excelを閉じると、各行の Group に、同一の行で指定された AccessLevel の Role で User が一括で追加されます。

groups.csv
[Project1のURL],[GroupAccess(10/20/30/40 or 50)],[Group1のURL],[Group2のURL],...
[Project2のURL],[GroupAccess(10/20/30/40 or 50)],[Group1のURL],[Group2のURL],[Group3のURL],...
...

CSV を上書きして、Excelを閉じると、各行の Project の Member に、同一の行で指定された GroupAccess の Role で Group が一括で追加されます。

Role の詳細はこちら参照。

10 => Guest access
20 => Reporter access
30 => Developer access
40 => Maintainer access
50 => Owner access # Only valid for groups
23
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
21