Skip to content

JuliaComputing/GitLab.jl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GitLab.jl

NOTE:

This is a repo for providing API access to GitLab repos. Majority of the code from GitHub.jl has been reused here. Also, the interfaces and terminologies have been retained to the extent possible.

GitLab.jl provides a Julia interface to the GitLab API v3. Using GitLab.jl, you can do things like:

  • query for basic repository, organization, and user information
  • programmatically take user-level actions (e.g. starring a repository, commenting on an issue, etc.)
  • set up listeners that can detect and respond to repository events
  • create and retrieve commit statuses (i.e. report CI pending/failure/success statuses to GitLab)

Here's a table of contents for this rather lengthy README:

1. Response Types

2. REST Methods

3. Authentication

4. Pagination

5. Handling Webhook Events

Response Types

GitLab's JSON responses are parsed and returned to the caller as types of the form G<:GitLab.GitLabType. Here's some useful information about these types:

  • All fields are Nullable.
  • Field names generally match the corresponding field in GitLab's JSON representation (the exception is "type", which has the corresponding field name typ to avoid the obvious language conflict).

Here's a table that matches up the provided GitLabTypes with their corresponding API documentation, as well as alternative identifying values:

type alternative identifying property link(s) to documentation
Owner login, e.g. "octocat" organizations, users
Repo full_name, e.g. "JuliaComputing/GitLab.jl" repositories
Commit sha, e.g. "d069993b320c57b2ba27336406f6ec3a9ae39375" repository commits
Branch name, e.g. master repository branches
Content path, e.g. "src/owners/owners.jl" repository contents
Comment id, e.g. 162224613 commit comments, issue comments, PR review comments
Status id, e.g. 366961773 commit statuses
PullRequest number, e.g. 44 pull requests
Issue number, e.g. 31 issues

You can inspect which fields are available for a type G<:GitLabType by calling fieldnames(G).

REST Methods

GitLab.jl implements a bunch of methods that make REST requests to GitLab's API. The below sections list these methods (note that a return type of Tuple{Vector{T}, Dict} means the result is paginated).

Users and Organizations

method return type documentation
owner(owner[, isorg = false]) Owner get owner as a user or organization
repos(owner[, isorg = false]) Tuple{Vector{Repo}, Dict} get the owner's repositories

Repositories

method return type documentation
repo_by_name(repo) Repo get repo
create_fork(repo) Repo create a fork of repo
contributors(repo) Tuple{Vector{Dict}, Dict} get repo's contributors
collaborators(repo) Tuple{Vector{Owner}, Dict} get repo's collaborators
iscollaborator(repo, user) Bool check if user is a collaborator on repo
add_collaborator(repo, user) HttpCommon.Response add user as a collaborator to repo
remove_collaborator(repo, user) HttpCommon.Response remove user as a collaborator from repo
stats(repo, stat[, attempts = 3]) HttpCommon.Response get information on stat (e.g. "queue_metrics", "process_metrics", "job_stats" & "compound_metrics".) - This may require additional configuration
commit(repo, sha) Commit get the commit specified by sha
commits(repo) Tuple{Vector{Commit}, Dict} get repo's commits
branch(repo, branch) Branch get the branch specified by branch
branches(repo) Tuple{Vector{Branch}, Dict} get repo's branches
file(repo, path, ref) Content get the file specified by path
create_file(repo, path) Dict create a file at path in repo
update_file(repo, path) Dict update a file at path in repo
delete_file(repo, path) Dict delete a file at path in repo
readme(repo) Content get repo's README.md
create_status(repo, sha) Status create a status for the commit specified by sha
statuses(repo, ref) Tuple{Vector{Status}, Dict} get the statuses posted to ref

Pull Requests and Issues

method return type documentation
pull_request(repo, pr) PullRequest get the pull request specified by pr
pull_requests(repo) Tuple{Vector{PullRequest}, Dict} get repo's pull requests
issue(repo, issue) Issue get the issue specified by issue
issues(repo) Tuple{Vector{Issue}, Dict} get repo's issues
create_issue(repo) Issue create an issue in repo
edit_issue(repo, issue) Issue edit issue in repo
delete_issue(repo, issue) Issue delete issue in repo

Comments

method return type documentation
comment(repo, comment, :issue) Comment get an issue comment from repo
comment(repo, comment, :pr) Comment get a PR comment from repo
comment(repo, comment, :review) Comment get an review comment from repo
comment(repo, comment, :commit) Comment get a commit comment from repo
comments(repo, issue, :issue) Tuple{Vector{Comment}, Dict} get the comments on issue in repo
comments(repo, pr, :pr) Tuple{Vector{Comment}, Dict} get the comments on pr in repo
comments(repo, pr, :review) Tuple{Vector{Comment}, Dict} get the review comments on pr in repo
comments(repo, commit, :commit) Tuple{Vector{Comment}, Dict} get the comments on commit in repo
create_comment(repo, issue, :issue) Comment create a comment on issue in repo
create_comment(repo, pr, :pr) Comment create a comment on pr in repo
create_comment(repo, pr, :review) Comment create a review comment on pr in repo
create_comment(repo, commit, :commit) Comment create a comment on commit in repo
edit_comment(repo, comment, :issue) Comment edit the issue comment in repo
edit_comment(repo, comment, :pr) Comment edit the PR comment in repo
edit_comment(repo, comment, :review) Comment edit the review comment in repo
edit_comment(repo, comment, :commit) Comment edit the commit comment in repo
delete_comment(repo, comment, :issue) HttpCommon.Response delete the issue comment from repo
delete_comment(repo, comment, :pr) HttpCommon.Response delete the PR comment from repo
delete_comment(repo, comment, :review) HttpCommon.Response delete the review comment from repo

Social Activity

method return type documentation
star(repo) HttpCommon.Response star repo
unstar(repo) HttpCommon.Response unstar repo
starred(user) Tuple{Vector{Repo}, Dict} get repositories starred by user

Miscellaneous

method return type documentation
authenticate(token) OAuth2 validate token and return an authentication object

Keyword Arguments

All REST methods accept the following keyword arguments:

keyword type default value description
auth GitLab.Authorization GitLab.AnonymousAuth() The request's authorization
params Dict Dict() The request's query parameters
headers Dict Dict() The request's headers. Note that these headers will be mutated by GitLab.jl request methods.
handle_error Bool true If true, a Julia error will be thrown in the event that GitLab's response reports an error.
page_limit Real Inf The number of pages to return (only applies to paginated results, obviously)

Authentication

To authenticate your requests to GitLab, you'll need to generate an appropriate access token. Then, you can do stuff like the following (this example assumes that you set an environmental variable GITLAB_AUTH containing the access token):

import GitLab

myauth = GitLab.authenticate(ENV["GITLAB_AUTH"]) # don't hardcode your access tokens!

GitLab.star("JuliaComputing/GitLab.jl"; auth = myauth)  # star the GitLab.jl repo as the user identified by myauth

As you can see, you can propagate the identity/permissions of the myauth token to GitLab.jl's methods by passing auth = myauth as a keyword argument.

Handling Webhook Events

GitLab.jl comes with configurable EventListener and CommentListener types that can be used as basic servers for parsing and responding to events delivered by GitLab's repository Webhooks.

EventListener

When an EventListener receives an event, it performs some basic validation and wraps the event payload (and some other data) in a WebhookEvent type. This WebhookEvent instance, along with the provided Authorization, is then fed to the server's handler function, which the user defines to determine the server's response behavior. The handler function is expected to return an HttpCommon.Response that is then sent back to GitLab.

The EventListener constructor takes the following keyword arguments:

  • auth: GitLab authorization (usually with repo-level permissions).
  • secret: A string used to verify the event source. If the event is from a GitLab Webhook, it's the Webhook's secret. If a secret is not provided, the server won't validate the secret signature of incoming requests.
  • repos: A vector of Repos (or fully qualified repository names) listing all acceptable repositories. All repositories are whitelisted by default.
  • events: A vector of event names listing all acceptable events (e.g. ["commit_comment", "pull_request"]). All events are whitelisted by default.
  • forwards: A vector of HttpCommon.URIs (or URI strings) to which any incoming requests should be forwarded (after being validated by the listener)

Here's an example that demonstrates how to construct and run an EventListener that does benchmarking on every commit and PR:

import GitLab

# EventListener settings
myauth = GitLab.authenticate(ENV["GITLAB_AUTH"])
options = Dict("private_token" => myauth.token)
mysecret = ENV["MY_SECRET"]
myevents = ["Note Hook", "MergeRequest"]
myrepos = [GitLab.repo_by_name("MyTestProject1")]
myforwards = [HttpCommon.URI("http://myforward1.com"), "http://myforward2.com"] # can be HttpCommon.URIs or URI strings

# Set up Status parameters
pending_params = Dict(
    "state" => "pending",
    "context" => "Benchmarker",
    "description" => "Running benchmarks..."
)

success_params = Dict(
    "state" => "success",
    "context" => "Benchmarker",
    "description" => "Benchmarks complete!"
)

error_params(err) = Dict(
    "state" => "error",
    "context" => "Benchmarker",
    "description" => "Error: $err"
)

# We can use Julia's `do` notation to set up the listener's handler function
listener = GitLab.EventListener(auth = myauth,
                                secret = mysecret,
                                repos = myrepos,
                                events = myevents,
                                forwards = myforwards) do event
    kind, payload, repo = event.kind, event.payload, event.repository

    if kind == "pull_request" && payload["action"] == "closed"
        return HttpCommon.Response(200)
    end

    if event.kind == "push"
        sha = event.payload["after"]
    elseif event.kind == "pull_request"
        sha = event.payload["pull_request"]["head"]["sha"]
    end

    GitLab.create_status(repo, sha; auth = myauth, params = pending_params)

    try
        # run_and_log_benchmarks isn't actually a defined function, but you get the point
        run_and_log_benchmarks(event, "\$(sha)-benchmarks.csv")
    catch err
        GitLab.create_status(repo, sha; auth = myauth, params = error_params(err))
        return HttpCommon.Response(500)
    end

    GitLab.create_status(repo, sha; auth = myauth, params = success_params)

    return HttpCommon.Response(200)
end

# Start the listener on localhost at port 8000
GitLab.run(listener, host=IPv4(127,0,0,1), port=8000)

CommentListener

A CommentListener is a special kind of EventListener that allows users to pass data to the listener's handler function via commenting. This is useful for triggering events on repositories that require configuration settings.

A CommentListener automatically filters out all non-comment events, and then checks the body of each comment event against a trigger Regex supplied by the user. If a match is found in the comment, then the CommentListener calls its handler function, passing it the event and the corresponding RegexMatch.

The CommentListener constructor takes the following keyword arguments:

  • auth: same as EventListener
  • secret: same as EventListener
  • repos: same as EventListener
  • forwards: same as EventListener
  • check_collab: If true, only acknowledge comments made by repository collaborators. Note that, if check_collab is true, auth must have the appropriate permissions to query the comment's repository for the collaborator status of the commenter. check_collab is true by default.

For example, let's set up a silly CommentListener that responds to the commenter with a greeting. To give a demonstration of the desired behavior, if a collaborator makes a comment like:

Man, I really would like to be greeted today.

`sayhello("Bob", "outgoing")`

We want the CommentLister to reply:

Hello, Bob, you look very outgoing today!

Here's the code that will make this happen:

import GitLab

myauth = GitLab.authenticate(ENV["GITLAB_AUTH"]) # don't hardcode your access tokens!
println("Authentication successful")
options = Dict("private_token" => myauth.token)

# CommentListener settings
trigger = r"`sayhello\(.*?\)`"

# We can use Julia's `do` notation to set up the listener's handler function.
# Note that, in our example case, `phrase` will be "`sayhello(\"Bob\", \"outgoing\")`"
listener = GitLab.CommentListener(trigger; auth = myauth, secret = mysecret) do event, phrase
    # In our example case, this code sets name to "Bob" and adjective to "outgoing"
    name, adjective = matchall(r"\".*?\"", phrase)
    comment_params = Dict("body" => "Hello, $name, you look very $adjective today!")

    # Parse the original comment event for all the necessary reply info
    comment = GitLab.Comment(event.payload["comment"])

    if event.payload["object_attributes"]["noteable_type"] == "Issue"
        comment_kind = :issue
        reply_to = event.payload["object_attributes"]["noteable_id"]
    elseif event.payload["object_attributes"]["noteable_type"] == "Commit"
        comment_kind = :commit
        reply_to = get(comment.commit_id)
    elseif event.payload["object_attributes"]["noteable_type"] == "MergeRequest"
        comment_kind = :review
        reply_to = event.payload["object_attributes"]["noteable_id"]
        # load required query params for review comment creation
        comment_params["commit_id"] = "$(comment.id)"
        comment_params["path"] = "$(comment.url)"
        comment_params["position"] = "$(comment.id)"
    end

    # send the comment creation request to GitLab
    GitLab.create_comment(event.repository, reply_to, comment_kind; headers = options, params = comment_params)

    return HttpCommon.Response(200)
end

# Start the listener on localhost at port 8000
GitLab.run(listener, host=IPv4(127,0,0,1), port=8000)

About

No description, website, or topics provided.

Resources

License

MIT, MIT licenses found

Licenses found

MIT
LICENSE
MIT
LICENSE.md

Stars

Watchers

Forks

Packages

No packages published

Languages