Help us understand the problem. What is going on with this article?

GitLab API 利用

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
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした