Monty  Boehm

Monty Boehm

1659483960

GitHub.jl: A Julia Package for interfacing with GitHub

GitHub.jl 

GitHub.jl provides a Julia interface to the GitHub API v3. Using GitHub.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 GitHub)

Response Types

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

All fields are Union{Nothing, T}.

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

GitHubTypes can be passed as arguments to API methods in place of (and in combination with) regular identifying properties. For example, create_status(repo, commit) could be called as:

  • create_status(::GitHub.Repo, ::GitHub.Commit)
  • create_status(::GitHub.Repo, ::AbstractString) where the second argument is the SHA
  • create_status(::AbstractString, ::GitHub.Commit) where the first argument is the full qualified repo name
  • create_status(::AbstractString, ::AbstractString) where the first argument is the repo name, and the second is the SHA

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

typealternative identifying propertylink(s) to documentation
Ownerlogin, e.g. "octocat"organizations, users
Repofull_name, e.g. "JuliaWeb/GitHub.jl"repositories
Commitsha, e.g. "d069993b320c57b2ba27336406f6ec3a9ae39375"repository commits
GitCommitsha, e.g. "d069993b320c57b2ba27336406f6ec3a9ae39375"raw git commits
Branchname, e.g. masterrepository branches
Contentpath, e.g. "src/owners/owners.jl"repository contents
Commentid, e.g. 162224613commit comments, issue comments, PR review comments
Labelname, e.g. bugissue labels
Statusid, e.g. 366961773commit statuses
PullRequestnumber, e.g. 44pull requests
Issuenumber, e.g. 31issues
Teamid, e.g. 1teams
Gistid, e.g. 0bace7cc774df4b3a4b0ee9aaa271ef6gists
Reviewid, e.g. 1reviews
Blobsha, e.g. "95c8d1aa2a7b1e6d672e15b67e0df4abbe57dcbe"raw git blobs
Treesha, e.g. "78e524d5e979e326a7c144ce195bf94ca9b04fa0"raw git trees
Tagtag name, e.g. v1.0git tags
Referencesreference name, e.g. heads/master (note: omits leading refs/)references
Secretssecret name, e.g. TAGBOT_SECRETsecrets
DeployKeysid, e.g., 12345deploy keys

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

REST Methods

GitHub.jl implements a bunch of methods that make REST requests to GitHub'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

methodreturn typedocumentation
whoami()Ownerget currently authenticated user as a user
owner(owner[, isorg = false])Ownerget owner as a user or organization
orgs(owner)Tuple{Vector{Owner}, Dict}get the owner's organizations
followers(owner)Tuple{Vector{Owner}, Dict}get the owner's followers
following(owner)Tuple{Vector{Owner}, Dict}get the users followed by owner
repos(owner[, isorg = false])Tuple{Vector{Repo}, Dict}get the owner's repositories/get an organization's repositories
teams(owner)Tuple{Vector{Team}, Dict}get the organizations's teams repositories
sshkeys(owner)Tuple{Vector{Dict}, Dict}get the owner's public ssh keys
gpgkeys(owner)Tuple{Vector{Dict}, Dict}get the owner's public gpg keys

Teams

methodreturn typedocumentation
members(team)Tuple{Vector{Owner}, Dict}get team members as users
repos(owner, team)Tuple{Vector{Repo}, Dict}get team repositories as users

Repositories

methodreturn typedocumentation
repo(repo)Repoget repo
create_repo(owner, name, params=Dict{String,String}())Repocreate a repository of the given name in the given owner's account
create_fork(repo)Repocreate a fork of repo
forks(repo)Tuple{Vector{Repo}, Dict}get repo's forks
contributors(repo)Dictget repo's contributors
collaborators(repo)Tuple{Vector{Owner}, Dict}get repo's collaborators
iscollaborator(repo, user)Boolcheck if user is a collaborator on repo
add_collaborator(repo, user)HTTP.Responseadd user as a collaborator to repo
remove_collaborator(repo, user)HTTP.Responseremove user as a collaborator from repo
collaborator_permission(repo, user)HTTP.Responseget the repo permission of a collaborator
stats(repo, stat[, attempts = 3])HTTP.Responseget information on stat (e.g. "contributors", "code_frequency", "commit_activity", etc.)
commit(repo, sha)Commitget the commit specified by sha
commits(repo)Tuple{Vector{Commit}, Dict}get repo's commits
branch(repo, branch)Branchget the branch specified by branch
branches(repo)Tuple{Vector{Branch}, Dict}get repo's branches
file(repo, path)Contentget the file specified by path
directory(repo, path)Tuple{Vector{Content}, Dict}get the contents of the directory specified by path
create_file(repo, path)Dictcreate a file at path in repo
update_file(repo, path)Dictupdate a file at path in repo
delete_file(repo, path)Dictdelete a file at path in repo
permalink(content::Content, commit)URIs.URIget a permalink for content at the SHA specified by commit
readme(repo)Contentget repo's README
create_status(repo, sha)Statuscreate a status for the commit specified by sha
statuses(repo, ref)Tuple{Vector{Status}, Dict}get the statuses posted to ref
status(repo, ref)Statusget the combined status for ref
create_webhook(owner, repo)Webhookcreate a webhook for repo
secrets(repo; auth)Tuple{Vector{Secret}, Dict}get names of all secrets for repo
secret(repo, name; auth)Secretget status of secret in repo
create_secret(repo, name; value, auth)nothingcreate a secret for repo
delete_secret(repo, name; auth)nothingdelete a secret for repo
deploykeys(repo; auth)Tuple{Vector{DeployKey}, Dict}get all deploy keys for repo
deploykey(repo, key; auth)DeployKeyget the deploy key in repo
create_deploykey(repo; params=..., auth)nothingcreate a deploy key for repo
delete_deploykey(repo, key; auth)nothingdelete a deploy key for repo

Pull Requests and Issues

methodreturn typedocumentation
pull_request(repo, pr)PullRequestget the pull request specified by pr
pull_requests(repo)Tuple{Vector{PullRequest}, Dict}get repo's pull requests
create_pull_request(repo)PullRequestcreate pull request in repo
update_pull_request(repo, pr)PullRequestupdate the given pr in repo
close_pull_request(repo, pr)PullRequestclose the given pr in repo
issue(repo, issue)Issueget the issue specified by issue
issues(repo)Tuple{Vector{Issue}, Dict}get repo's issues
create_issue(repo)Issuecreate an issue in repo
edit_issue(repo, issue)Issueedit issue in repo
reviews(repo, pr)Tuple{Vector{PullRequest}, Dict}get a pr's reviews
dismiss_review(repo, review)HTTP.Responsedismiss review in repo

Comments

methodreturn typedocumentation
comment(repo, comment, :issue)Commentget an issue comment from repo
comment(repo, comment, :pr)Commentget a PR comment from repo
comment(repo, comment, :review)Commentget an review comment from repo
comment(repo, comment, :commit)Commentget 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)Commentcreate a comment on issue in repo
create_comment(repo, pr, :pr)Commentcreate a comment on pr in repo
create_comment(repo, pr, :review)Commentcreate a review comment on pr in repo
create_comment(repo, commit, :commit)Commentcreate a comment on commit in repo
edit_comment(repo, comment, :issue)Commentedit the issue comment in repo
edit_comment(repo, comment, :pr)Commentedit the PR comment in repo
edit_comment(repo, comment, :review)Commentedit the review comment in repo
edit_comment(repo, comment, :commit)Commentedit the commit comment in repo
delete_comment(repo, comment, :issue)HTTP.Responsedelete the issue comment from repo
delete_comment(repo, comment, :pr)HTTP.Responsedelete the PR comment from repo
delete_comment(repo, comment, :review)HTTP.Responsedelete the review comment from repo
delete_comment(repo, comment, :commit)HTTP.Responsedelete the commitcomment from repo
delete_comment(repo, comment, :commit)HTTP.Responsedelete the commitcomment from repo
reply_to(repo, review, comment, body)HTTP.Responsereply to the comment (of review in repo) creating a new comment with the specified body

Labels

methodreturn typedocumentation
labels(repo,issue)Vector{Label}list labels from issue
add_labels(repo, issue, labels)Vector{Label}add labels to an issue
set_labels(repo, issue, labels)Vector{Label}set the labels for an issue
remove_all_labels(repo, issue)HTTP.Responseremove all labels from an issue
remove_label(repo, issue, label)HTTP.Responseremove a label from an issue

Social Activity

methodreturn typedocumentation
star(repo)HTTP.Responsestar repo
unstar(repo)HTTP.Responseunstar repo
stargazers(repo)Tuple{Vector{Owner}, Dict}get repo's stargazers
starred(user)Tuple{Vector{Repo}, Dict}get repositories starred by user
watchers(repo)Tuple{Vector{Owner}, Dict}get repo's watchers
watched(user)Tuple{Vector{Repo}, Dict}get repositories watched by user
watch(repo)HTTP.Responsewatch repo
unwatch(repo)HTTP.Responseunwatch repo

Gists

methodreturn typedocumentation
gist(id)Gistget the gist specified by id
gist(id, revision)Gistget the gist specified by id and revision
gists()Tuple{Vector{Gist}, Dict}get all public gists
gists(owner)Tuple{Vector{Gist}, Dict}get all gists for owner
create_gist()Gistcreate a gist
edit_gist(gist)Gistedit a gist
delete_gist(gist)HTTP.Responsedelete a gist
create_gist_fork(gist)Gistfork a gist
gist_forks(gist)Tuple{Vector{Gist}, Dict}list the forks of a gist
star_gist(gist)HTTP.Responsestar gist
starred_gists()Tuple{Vector{Gist}, Dict}get the starred gists
unstar_gist(gist)HTTP.Responseunstar gist

Git Data

methodreturn typedocumentation
blob(repo, sha)BlobLook up a blob in the repo by its SHA
create_blob(repo)BlobCreate a blob in the repo
gitcommit(repo, sha)GitCommitLook up a commit in the repo by its SHA
create_gitcommit(repo)GitCommitCreate a commit in the repo
tree(repo, sha)TreeLook up a tree in the repo by its SHA
create_tree(repo)TreeCreate a tree in the repo
tag(repo, sha)TagLook up a tag in the repo by its name
create_tag(repo)TagCreate a tag in the repo
reference(repo, name)ReferenceLook up a ref in the repo by its name
references(repo)Vector{Reference}Get all refs of the repo
create_reference(repo)ReferenceCreate a reference in the repo
update_reference(repo)ReferenceUpdate a reference in the repo
delete_reference(repo)GitCommitDelete a the repo
tag(repo)ReferenceUpdate a reference in the repo
delete_reference(repo)GitCommitDelete a the repo

GitHub Apps

methodreturn typedocumentation
app(id)Appget the GitHub app with the specified id
app(slug)Appget the GitHub app with the specified slug
app(;auth=auth)Appget the GitHub app authenticated by the corresponding auth
installations(auth)Vector{Installation}get the installations for the GitHub app authenticated by the corresponding auth
repos(i::Installation)Tuple{Vector{Repo}, Dict}get the active repositories for this installation

GitHub Check Runs

methodreturn typedocumentation
create_check_run(repo; params=...)CheckRunCreate a new check run
update_check_run(repo, id::Int; params=...)CheckRunUpdate the check run with the given id

Licenses

methodreturn typedocumentation
licenses(; params=...)Vector{License}Get all commonly used licenses
license(license_id; params=...)LicenseGet a license
repo_license(repo; params=...)ContentGet the license for a respository

Miscellaneous

methodreturn typedocumentation
rate_limit()Dictget your rate limit status
authenticate(token)OAuth2validate token and return an authentication object

Keyword Arguments

All REST methods accept the following keyword arguments:

keywordtypedefault valuedescription
authGitHub.AuthorizationGitHub.AnonymousAuth()The request's authorization
paramsDictDict()The request's query parameters
headersDictDict()The request's headers. Note that these headers will be mutated by GitHub.jl request methods.
handle_errorBooltrueIf true, a Julia error will be thrown in the event that GitHub's response reports an error.
page_limitRealInfThe number of pages to return (only applies to paginated results, obviously)

Authentication

To authenticate your requests to GitHub, 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 GITHUB_AUTH containing the access token):

import GitHub
myauth = GitHub.authenticate(ENV["GITHUB_AUTH"]) # don't hardcode your access tokens!
GitHub.star("JuliaWeb/GitHub.jl"; auth = myauth)  # star the GitHub.jl repo as the user identified by myauth

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

Note that if authentication is not provided, they'll be subject to the restrictions GitHub imposes on unauthenticated requests (such as stricter rate limiting)

Authenticating as a GitHub app

GitHub apps (formerly called integrations) have their own authentication format based on JSON Web Tokens. When creating a GitHub app, you will be prompted to download your app's private key. You can use this private key to authenticate as a Github App using the JWTAuth type:

appauth = JWTAuth(1234, "privkey.pem") # Replace with your app id/privkey file

The following shows a complete example that opens an issue on every repository on which your application gets installed:

listener = GitHub.EventListener() do event
    # On installation, open an issue on every repository we got installed in
    if event.kind == "installation"
        # Authenticate as the application
        appauth = GitHub.JWTAuth(1234, "privkey.pem")
        # Now, get permissions for this particular installation
        installation = Installation(event.payload["installation"])
        auth = create_access_token(installation, appauth)
        for repo in event.payload["repositories"]
            create_issue(GitHub.Repo(repo), auth=auth,
                params = Dict(
                    :title => "Hello World",
                    :body => "Thank you for installing me - I needed that"
            ))
        end
    end
    return HTTP.Response(200)
end
GitHub.run(listener, host=IPv4(0,0,0,0), port=8888)

Pagination

GitHub will often paginate results for requests that return multiple items. On the GitHub.jl side of things, it's pretty easy to see which methods return paginated results by referring to the REST Methods documentation; if a method returns a Tuple{Vector{T}, Dict}, that means its results are paginated.

Paginated methods return both the response values, and some pagination metadata. You can use the per_page/page query parameters and the page_limit keyword argument to configure result pagination.

For example, let's request a couple pages of GitHub.jl's PRs, and configure our result pagination to see how it works:

# show all PRs (both open and closed), and give me 3 items per page starting at page 2
julia> myparams = Dict("state" => "all", "per_page" => 3, "page" => 2);

julia> prs, page_data = pull_requests("JuliaWeb/GitHub.jl"; params = myparams, page_limit = 2);

julia> prs # 3 items per page * 2 page limit == 6 items, as expected
6-element Array{GitHub.PullRequest,1}:
 GitHub.PullRequest(44)
 GitHub.PullRequest(43)
 GitHub.PullRequest(42)
 GitHub.PullRequest(41)
 GitHub.PullRequest(39)
 GitHub.PullRequest(38)

julia> page_data
Dict{String,String} with 4 entries:
  "prev"  => "https://api.github.com/repositories/16635105/pulls?page=2&per_page=3&state=all"
  "next"  => "https://api.github.com/repositories/16635105/pulls?page=4&per_page=3&state=all"
  "first" => "https://api.github.com/repositories/16635105/pulls?page=1&per_page=3&state=all"
  "last"  => "https://api.github.com/repositories/16635105/pulls?page=7&per_page=3&state=all"

In the above, prs contains the results from page 2 and 3. We know this because we specified page 2 as our starting page ("page" => 2), and limited the response to 2 pages max (page_limit = 2). In addition, we know that exactly 2 pages were actually retrieved, since there are 6 items and we said each page should only contain 3 items ("per_page" => 3).

The values provided by page_data are the same values that are included in the Link header of the last requested item. You can continue paginating by starting a new paginated request at one of these links using the start_page keyword argument:

# Continue paging, starting with `page_data["next"]`.
# Note that the `params` kwarg can't be used here because
# the link passed to `start_page` has its own parameters
julia> prs2, page_data2 = pull_requests("JuliaWeb/GitHub.jl"; page_limit = 2, start_page = page_data["next"]);

julia> prs2
6-element Array{GitHub.PullRequest,1}:
 GitHub.PullRequest(37)
 GitHub.PullRequest(34)
 GitHub.PullRequest(32)
 GitHub.PullRequest(30)
 GitHub.PullRequest(24)
 GitHub.PullRequest(22)

julia> page_data2
Dict{String,String} with 4 entries:
  "prev"  => "https://api.github.com/repositories/16635105/pulls?page=4&per_page=3&state=all"
  "next"  => "https://api.github.com/repositories/16635105/pulls?page=6&per_page=3&state=all"
  "first" => "https://api.github.com/repositories/16635105/pulls?page=1&per_page=3&state=all"
  "last"  => "https://api.github.com/repositories/16635105/pulls?page=7&per_page=3&state=all"

Handling Webhook Events

GitHub.jl comes with configurable EventListener and CommentListener types that can be used as basic servers for parsing and responding to events delivered by GitHub'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 HTTP.Response that is then sent back to GitHub.

The EventListener constructor takes the following keyword arguments:

  • auth: GitHub authorization (usually with repo-level permissions).
  • secret: A string used to verify the event source. If the event is from a GitHub 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 URIs.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 GitHub
import URIs
# EventListener settings
myauth = GitHub.authenticate(ENV["GITHUB_AUTH"])
mysecret = ENV["MY_SECRET"]
myevents = ["pull_request", "push"]
myrepos = [GitHub.Repo("owner1/repo1"), "owner2/repo2"] # can be Repos or repo names
myforwards = [URIs.URI("http://myforward1.com"), "http://myforward2.com"] # can be URIs.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 = GitHub.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 HTTP.Response(200)
    end

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

    GitHub.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
        GitHub.create_status(repo, sha; auth = myauth, params = error_params(err))
        return HTTP.Response(500)
    end

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

    return HTTP.Response(200)
end

# Start the listener on localhost at port 8000
GitHub.run(listener, IPv4(127,0,0,1), 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.
  • use_access_token: If check_collab is set to true and auth is using JWT authentication for GitHub Apps, then set this to true.

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 GitHub

# CommentListener settings
trigger = r"`sayhello\(.*?\)`"
myauth = GitHub.authenticate(ENV["GITHUB_AUTH"])
mysecret = ENV["MY_SECRET"]

# 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 = GitHub.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 = GitHub.Comment(event.payload["comment"])

    if event.kind == "issue_comment"
        comment_kind = :issue
        reply_to = event.payload["issue"]["number"]
    elseif event.kind == "commit_comment"
        comment_kind = :commit
        reply_to = comment.commit_id
    elseif event.kind == "pull_request_review_comment"
        comment_kind = :review
        reply_to = event.payload["pull_request"]["number"]
        # load required query params for review comment creation
        comment_params["commit_id"] = comment.commit_id
        comment_params["path"] = comment.path
        comment_params["position"] = comment.position
    end

    # send the comment creation request to GitHub
    GitHub.create_comment(event.repository, reply_to, comment_kind; auth = myauth, params = comment_params)

    return HTTP.Response(200)
end

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

GitHub Enterprise

This library work with github.com, and also with self-hosted github, a.k.a. GitHub Enterprise.

To use it with self-hosted github, you need to create GitHubWebAPI structure and pass it to functions when needed. Following example shows obtaining repository info private/Package.jl on github instance with API https://git.company.com/api/v3.

import GitHub
import URIs

api = GitHub.GitHubWebAPI(URIs.URI("https://git.company.com/api/v3"))
myauth = GitHub.authenticate(api, ENV["GITHUB_AUTH"])
myrepo = GitHub.repo(api, "private/Package.jl", auth=myauth)

SSH keys

You can generate public-private key pairs with GitHub.genkeys. Here's an example adding a deploy key and secret, in this case to deploy documentation via GitHub Actions:

pubkey, privkey = GitHub.genkeys()
create_deploykey(repo; auth, params=Dict("key"=>pubkey, "title"=>"Documenter", "read_only"=>false))
create_secret(repo, "DOCUMENTER_KEY"; auth, value=privkey)

privkey is sent in encrypted form to GitHub. Do not share privkey with others or post it publicly; doing so breaches the security of your repository. You can read more about the meaning of SSH keys and their security implications.

Author: JuliaWeb
Source Code: https://github.com/JuliaWeb/GitHub.jl 
License: MIT license

#julia #github 

What is GEEK

Buddha Community

GitHub.jl: A Julia Package for interfacing with GitHub
Monty  Boehm

Monty Boehm

1659483960

GitHub.jl: A Julia Package for interfacing with GitHub

GitHub.jl 

GitHub.jl provides a Julia interface to the GitHub API v3. Using GitHub.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 GitHub)

Response Types

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

All fields are Union{Nothing, T}.

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

GitHubTypes can be passed as arguments to API methods in place of (and in combination with) regular identifying properties. For example, create_status(repo, commit) could be called as:

  • create_status(::GitHub.Repo, ::GitHub.Commit)
  • create_status(::GitHub.Repo, ::AbstractString) where the second argument is the SHA
  • create_status(::AbstractString, ::GitHub.Commit) where the first argument is the full qualified repo name
  • create_status(::AbstractString, ::AbstractString) where the first argument is the repo name, and the second is the SHA

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

typealternative identifying propertylink(s) to documentation
Ownerlogin, e.g. "octocat"organizations, users
Repofull_name, e.g. "JuliaWeb/GitHub.jl"repositories
Commitsha, e.g. "d069993b320c57b2ba27336406f6ec3a9ae39375"repository commits
GitCommitsha, e.g. "d069993b320c57b2ba27336406f6ec3a9ae39375"raw git commits
Branchname, e.g. masterrepository branches
Contentpath, e.g. "src/owners/owners.jl"repository contents
Commentid, e.g. 162224613commit comments, issue comments, PR review comments
Labelname, e.g. bugissue labels
Statusid, e.g. 366961773commit statuses
PullRequestnumber, e.g. 44pull requests
Issuenumber, e.g. 31issues
Teamid, e.g. 1teams
Gistid, e.g. 0bace7cc774df4b3a4b0ee9aaa271ef6gists
Reviewid, e.g. 1reviews
Blobsha, e.g. "95c8d1aa2a7b1e6d672e15b67e0df4abbe57dcbe"raw git blobs
Treesha, e.g. "78e524d5e979e326a7c144ce195bf94ca9b04fa0"raw git trees
Tagtag name, e.g. v1.0git tags
Referencesreference name, e.g. heads/master (note: omits leading refs/)references
Secretssecret name, e.g. TAGBOT_SECRETsecrets
DeployKeysid, e.g., 12345deploy keys

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

REST Methods

GitHub.jl implements a bunch of methods that make REST requests to GitHub'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

methodreturn typedocumentation
whoami()Ownerget currently authenticated user as a user
owner(owner[, isorg = false])Ownerget owner as a user or organization
orgs(owner)Tuple{Vector{Owner}, Dict}get the owner's organizations
followers(owner)Tuple{Vector{Owner}, Dict}get the owner's followers
following(owner)Tuple{Vector{Owner}, Dict}get the users followed by owner
repos(owner[, isorg = false])Tuple{Vector{Repo}, Dict}get the owner's repositories/get an organization's repositories
teams(owner)Tuple{Vector{Team}, Dict}get the organizations's teams repositories
sshkeys(owner)Tuple{Vector{Dict}, Dict}get the owner's public ssh keys
gpgkeys(owner)Tuple{Vector{Dict}, Dict}get the owner's public gpg keys

Teams

methodreturn typedocumentation
members(team)Tuple{Vector{Owner}, Dict}get team members as users
repos(owner, team)Tuple{Vector{Repo}, Dict}get team repositories as users

Repositories

methodreturn typedocumentation
repo(repo)Repoget repo
create_repo(owner, name, params=Dict{String,String}())Repocreate a repository of the given name in the given owner's account
create_fork(repo)Repocreate a fork of repo
forks(repo)Tuple{Vector{Repo}, Dict}get repo's forks
contributors(repo)Dictget repo's contributors
collaborators(repo)Tuple{Vector{Owner}, Dict}get repo's collaborators
iscollaborator(repo, user)Boolcheck if user is a collaborator on repo
add_collaborator(repo, user)HTTP.Responseadd user as a collaborator to repo
remove_collaborator(repo, user)HTTP.Responseremove user as a collaborator from repo
collaborator_permission(repo, user)HTTP.Responseget the repo permission of a collaborator
stats(repo, stat[, attempts = 3])HTTP.Responseget information on stat (e.g. "contributors", "code_frequency", "commit_activity", etc.)
commit(repo, sha)Commitget the commit specified by sha
commits(repo)Tuple{Vector{Commit}, Dict}get repo's commits
branch(repo, branch)Branchget the branch specified by branch
branches(repo)Tuple{Vector{Branch}, Dict}get repo's branches
file(repo, path)Contentget the file specified by path
directory(repo, path)Tuple{Vector{Content}, Dict}get the contents of the directory specified by path
create_file(repo, path)Dictcreate a file at path in repo
update_file(repo, path)Dictupdate a file at path in repo
delete_file(repo, path)Dictdelete a file at path in repo
permalink(content::Content, commit)URIs.URIget a permalink for content at the SHA specified by commit
readme(repo)Contentget repo's README
create_status(repo, sha)Statuscreate a status for the commit specified by sha
statuses(repo, ref)Tuple{Vector{Status}, Dict}get the statuses posted to ref
status(repo, ref)Statusget the combined status for ref
create_webhook(owner, repo)Webhookcreate a webhook for repo
secrets(repo; auth)Tuple{Vector{Secret}, Dict}get names of all secrets for repo
secret(repo, name; auth)Secretget status of secret in repo
create_secret(repo, name; value, auth)nothingcreate a secret for repo
delete_secret(repo, name; auth)nothingdelete a secret for repo
deploykeys(repo; auth)Tuple{Vector{DeployKey}, Dict}get all deploy keys for repo
deploykey(repo, key; auth)DeployKeyget the deploy key in repo
create_deploykey(repo; params=..., auth)nothingcreate a deploy key for repo
delete_deploykey(repo, key; auth)nothingdelete a deploy key for repo

Pull Requests and Issues

methodreturn typedocumentation
pull_request(repo, pr)PullRequestget the pull request specified by pr
pull_requests(repo)Tuple{Vector{PullRequest}, Dict}get repo's pull requests
create_pull_request(repo)PullRequestcreate pull request in repo
update_pull_request(repo, pr)PullRequestupdate the given pr in repo
close_pull_request(repo, pr)PullRequestclose the given pr in repo
issue(repo, issue)Issueget the issue specified by issue
issues(repo)Tuple{Vector{Issue}, Dict}get repo's issues
create_issue(repo)Issuecreate an issue in repo
edit_issue(repo, issue)Issueedit issue in repo
reviews(repo, pr)Tuple{Vector{PullRequest}, Dict}get a pr's reviews
dismiss_review(repo, review)HTTP.Responsedismiss review in repo

Comments

methodreturn typedocumentation
comment(repo, comment, :issue)Commentget an issue comment from repo
comment(repo, comment, :pr)Commentget a PR comment from repo
comment(repo, comment, :review)Commentget an review comment from repo
comment(repo, comment, :commit)Commentget 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)Commentcreate a comment on issue in repo
create_comment(repo, pr, :pr)Commentcreate a comment on pr in repo
create_comment(repo, pr, :review)Commentcreate a review comment on pr in repo
create_comment(repo, commit, :commit)Commentcreate a comment on commit in repo
edit_comment(repo, comment, :issue)Commentedit the issue comment in repo
edit_comment(repo, comment, :pr)Commentedit the PR comment in repo
edit_comment(repo, comment, :review)Commentedit the review comment in repo
edit_comment(repo, comment, :commit)Commentedit the commit comment in repo
delete_comment(repo, comment, :issue)HTTP.Responsedelete the issue comment from repo
delete_comment(repo, comment, :pr)HTTP.Responsedelete the PR comment from repo
delete_comment(repo, comment, :review)HTTP.Responsedelete the review comment from repo
delete_comment(repo, comment, :commit)HTTP.Responsedelete the commitcomment from repo
delete_comment(repo, comment, :commit)HTTP.Responsedelete the commitcomment from repo
reply_to(repo, review, comment, body)HTTP.Responsereply to the comment (of review in repo) creating a new comment with the specified body

Labels

methodreturn typedocumentation
labels(repo,issue)Vector{Label}list labels from issue
add_labels(repo, issue, labels)Vector{Label}add labels to an issue
set_labels(repo, issue, labels)Vector{Label}set the labels for an issue
remove_all_labels(repo, issue)HTTP.Responseremove all labels from an issue
remove_label(repo, issue, label)HTTP.Responseremove a label from an issue

Social Activity

methodreturn typedocumentation
star(repo)HTTP.Responsestar repo
unstar(repo)HTTP.Responseunstar repo
stargazers(repo)Tuple{Vector{Owner}, Dict}get repo's stargazers
starred(user)Tuple{Vector{Repo}, Dict}get repositories starred by user
watchers(repo)Tuple{Vector{Owner}, Dict}get repo's watchers
watched(user)Tuple{Vector{Repo}, Dict}get repositories watched by user
watch(repo)HTTP.Responsewatch repo
unwatch(repo)HTTP.Responseunwatch repo

Gists

methodreturn typedocumentation
gist(id)Gistget the gist specified by id
gist(id, revision)Gistget the gist specified by id and revision
gists()Tuple{Vector{Gist}, Dict}get all public gists
gists(owner)Tuple{Vector{Gist}, Dict}get all gists for owner
create_gist()Gistcreate a gist
edit_gist(gist)Gistedit a gist
delete_gist(gist)HTTP.Responsedelete a gist
create_gist_fork(gist)Gistfork a gist
gist_forks(gist)Tuple{Vector{Gist}, Dict}list the forks of a gist
star_gist(gist)HTTP.Responsestar gist
starred_gists()Tuple{Vector{Gist}, Dict}get the starred gists
unstar_gist(gist)HTTP.Responseunstar gist

Git Data

methodreturn typedocumentation
blob(repo, sha)BlobLook up a blob in the repo by its SHA
create_blob(repo)BlobCreate a blob in the repo
gitcommit(repo, sha)GitCommitLook up a commit in the repo by its SHA
create_gitcommit(repo)GitCommitCreate a commit in the repo
tree(repo, sha)TreeLook up a tree in the repo by its SHA
create_tree(repo)TreeCreate a tree in the repo
tag(repo, sha)TagLook up a tag in the repo by its name
create_tag(repo)TagCreate a tag in the repo
reference(repo, name)ReferenceLook up a ref in the repo by its name
references(repo)Vector{Reference}Get all refs of the repo
create_reference(repo)ReferenceCreate a reference in the repo
update_reference(repo)ReferenceUpdate a reference in the repo
delete_reference(repo)GitCommitDelete a the repo
tag(repo)ReferenceUpdate a reference in the repo
delete_reference(repo)GitCommitDelete a the repo

GitHub Apps

methodreturn typedocumentation
app(id)Appget the GitHub app with the specified id
app(slug)Appget the GitHub app with the specified slug
app(;auth=auth)Appget the GitHub app authenticated by the corresponding auth
installations(auth)Vector{Installation}get the installations for the GitHub app authenticated by the corresponding auth
repos(i::Installation)Tuple{Vector{Repo}, Dict}get the active repositories for this installation

GitHub Check Runs

methodreturn typedocumentation
create_check_run(repo; params=...)CheckRunCreate a new check run
update_check_run(repo, id::Int; params=...)CheckRunUpdate the check run with the given id

Licenses

methodreturn typedocumentation
licenses(; params=...)Vector{License}Get all commonly used licenses
license(license_id; params=...)LicenseGet a license
repo_license(repo; params=...)ContentGet the license for a respository

Miscellaneous

methodreturn typedocumentation
rate_limit()Dictget your rate limit status
authenticate(token)OAuth2validate token and return an authentication object

Keyword Arguments

All REST methods accept the following keyword arguments:

keywordtypedefault valuedescription
authGitHub.AuthorizationGitHub.AnonymousAuth()The request's authorization
paramsDictDict()The request's query parameters
headersDictDict()The request's headers. Note that these headers will be mutated by GitHub.jl request methods.
handle_errorBooltrueIf true, a Julia error will be thrown in the event that GitHub's response reports an error.
page_limitRealInfThe number of pages to return (only applies to paginated results, obviously)

Authentication

To authenticate your requests to GitHub, 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 GITHUB_AUTH containing the access token):

import GitHub
myauth = GitHub.authenticate(ENV["GITHUB_AUTH"]) # don't hardcode your access tokens!
GitHub.star("JuliaWeb/GitHub.jl"; auth = myauth)  # star the GitHub.jl repo as the user identified by myauth

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

Note that if authentication is not provided, they'll be subject to the restrictions GitHub imposes on unauthenticated requests (such as stricter rate limiting)

Authenticating as a GitHub app

GitHub apps (formerly called integrations) have their own authentication format based on JSON Web Tokens. When creating a GitHub app, you will be prompted to download your app's private key. You can use this private key to authenticate as a Github App using the JWTAuth type:

appauth = JWTAuth(1234, "privkey.pem") # Replace with your app id/privkey file

The following shows a complete example that opens an issue on every repository on which your application gets installed:

listener = GitHub.EventListener() do event
    # On installation, open an issue on every repository we got installed in
    if event.kind == "installation"
        # Authenticate as the application
        appauth = GitHub.JWTAuth(1234, "privkey.pem")
        # Now, get permissions for this particular installation
        installation = Installation(event.payload["installation"])
        auth = create_access_token(installation, appauth)
        for repo in event.payload["repositories"]
            create_issue(GitHub.Repo(repo), auth=auth,
                params = Dict(
                    :title => "Hello World",
                    :body => "Thank you for installing me - I needed that"
            ))
        end
    end
    return HTTP.Response(200)
end
GitHub.run(listener, host=IPv4(0,0,0,0), port=8888)

Pagination

GitHub will often paginate results for requests that return multiple items. On the GitHub.jl side of things, it's pretty easy to see which methods return paginated results by referring to the REST Methods documentation; if a method returns a Tuple{Vector{T}, Dict}, that means its results are paginated.

Paginated methods return both the response values, and some pagination metadata. You can use the per_page/page query parameters and the page_limit keyword argument to configure result pagination.

For example, let's request a couple pages of GitHub.jl's PRs, and configure our result pagination to see how it works:

# show all PRs (both open and closed), and give me 3 items per page starting at page 2
julia> myparams = Dict("state" => "all", "per_page" => 3, "page" => 2);

julia> prs, page_data = pull_requests("JuliaWeb/GitHub.jl"; params = myparams, page_limit = 2);

julia> prs # 3 items per page * 2 page limit == 6 items, as expected
6-element Array{GitHub.PullRequest,1}:
 GitHub.PullRequest(44)
 GitHub.PullRequest(43)
 GitHub.PullRequest(42)
 GitHub.PullRequest(41)
 GitHub.PullRequest(39)
 GitHub.PullRequest(38)

julia> page_data
Dict{String,String} with 4 entries:
  "prev"  => "https://api.github.com/repositories/16635105/pulls?page=2&per_page=3&state=all"
  "next"  => "https://api.github.com/repositories/16635105/pulls?page=4&per_page=3&state=all"
  "first" => "https://api.github.com/repositories/16635105/pulls?page=1&per_page=3&state=all"
  "last"  => "https://api.github.com/repositories/16635105/pulls?page=7&per_page=3&state=all"

In the above, prs contains the results from page 2 and 3. We know this because we specified page 2 as our starting page ("page" => 2), and limited the response to 2 pages max (page_limit = 2). In addition, we know that exactly 2 pages were actually retrieved, since there are 6 items and we said each page should only contain 3 items ("per_page" => 3).

The values provided by page_data are the same values that are included in the Link header of the last requested item. You can continue paginating by starting a new paginated request at one of these links using the start_page keyword argument:

# Continue paging, starting with `page_data["next"]`.
# Note that the `params` kwarg can't be used here because
# the link passed to `start_page` has its own parameters
julia> prs2, page_data2 = pull_requests("JuliaWeb/GitHub.jl"; page_limit = 2, start_page = page_data["next"]);

julia> prs2
6-element Array{GitHub.PullRequest,1}:
 GitHub.PullRequest(37)
 GitHub.PullRequest(34)
 GitHub.PullRequest(32)
 GitHub.PullRequest(30)
 GitHub.PullRequest(24)
 GitHub.PullRequest(22)

julia> page_data2
Dict{String,String} with 4 entries:
  "prev"  => "https://api.github.com/repositories/16635105/pulls?page=4&per_page=3&state=all"
  "next"  => "https://api.github.com/repositories/16635105/pulls?page=6&per_page=3&state=all"
  "first" => "https://api.github.com/repositories/16635105/pulls?page=1&per_page=3&state=all"
  "last"  => "https://api.github.com/repositories/16635105/pulls?page=7&per_page=3&state=all"

Handling Webhook Events

GitHub.jl comes with configurable EventListener and CommentListener types that can be used as basic servers for parsing and responding to events delivered by GitHub'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 HTTP.Response that is then sent back to GitHub.

The EventListener constructor takes the following keyword arguments:

  • auth: GitHub authorization (usually with repo-level permissions).
  • secret: A string used to verify the event source. If the event is from a GitHub 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 URIs.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 GitHub
import URIs
# EventListener settings
myauth = GitHub.authenticate(ENV["GITHUB_AUTH"])
mysecret = ENV["MY_SECRET"]
myevents = ["pull_request", "push"]
myrepos = [GitHub.Repo("owner1/repo1"), "owner2/repo2"] # can be Repos or repo names
myforwards = [URIs.URI("http://myforward1.com"), "http://myforward2.com"] # can be URIs.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 = GitHub.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 HTTP.Response(200)
    end

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

    GitHub.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
        GitHub.create_status(repo, sha; auth = myauth, params = error_params(err))
        return HTTP.Response(500)
    end

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

    return HTTP.Response(200)
end

# Start the listener on localhost at port 8000
GitHub.run(listener, IPv4(127,0,0,1), 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.
  • use_access_token: If check_collab is set to true and auth is using JWT authentication for GitHub Apps, then set this to true.

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 GitHub

# CommentListener settings
trigger = r"`sayhello\(.*?\)`"
myauth = GitHub.authenticate(ENV["GITHUB_AUTH"])
mysecret = ENV["MY_SECRET"]

# 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 = GitHub.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 = GitHub.Comment(event.payload["comment"])

    if event.kind == "issue_comment"
        comment_kind = :issue
        reply_to = event.payload["issue"]["number"]
    elseif event.kind == "commit_comment"
        comment_kind = :commit
        reply_to = comment.commit_id
    elseif event.kind == "pull_request_review_comment"
        comment_kind = :review
        reply_to = event.payload["pull_request"]["number"]
        # load required query params for review comment creation
        comment_params["commit_id"] = comment.commit_id
        comment_params["path"] = comment.path
        comment_params["position"] = comment.position
    end

    # send the comment creation request to GitHub
    GitHub.create_comment(event.repository, reply_to, comment_kind; auth = myauth, params = comment_params)

    return HTTP.Response(200)
end

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

GitHub Enterprise

This library work with github.com, and also with self-hosted github, a.k.a. GitHub Enterprise.

To use it with self-hosted github, you need to create GitHubWebAPI structure and pass it to functions when needed. Following example shows obtaining repository info private/Package.jl on github instance with API https://git.company.com/api/v3.

import GitHub
import URIs

api = GitHub.GitHubWebAPI(URIs.URI("https://git.company.com/api/v3"))
myauth = GitHub.authenticate(api, ENV["GITHUB_AUTH"])
myrepo = GitHub.repo(api, "private/Package.jl", auth=myauth)

SSH keys

You can generate public-private key pairs with GitHub.genkeys. Here's an example adding a deploy key and secret, in this case to deploy documentation via GitHub Actions:

pubkey, privkey = GitHub.genkeys()
create_deploykey(repo; auth, params=Dict("key"=>pubkey, "title"=>"Documenter", "read_only"=>false))
create_secret(repo, "DOCUMENTER_KEY"; auth, value=privkey)

privkey is sent in encrypted form to GitHub. Do not share privkey with others or post it publicly; doing so breaches the security of your repository. You can read more about the meaning of SSH keys and their security implications.

Author: JuliaWeb
Source Code: https://github.com/JuliaWeb/GitHub.jl 
License: MIT license

#julia #github 

Edison  Stark

Edison Stark

1603861600

How to Compare Multiple GitHub Projects with Our GitHub Stats tool

If you have project code hosted on GitHub, chances are you might be interested in checking some numbers and stats such as stars, commits and pull requests.

You might also want to compare some similar projects in terms of the above mentioned stats, for whatever reasons that interest you.

We have the right tool for you: the simple and easy-to-use little tool called GitHub Stats.

Let’s dive right in to what we can get out of it.

Getting started

This interactive tool is really easy to use. Follow the three steps below and you’ll get what you want in real-time:

1. Head to the GitHub repo of the tool

2. Enter as many projects as you need to check on

3. Hit the Update button beside each metric

In this article we are going to compare three most popular machine learning projects for you.

#github #tools #github-statistics-react #github-stats-tool #compare-github-projects #github-projects #software-development #programming

How to Install Pyenv on Ubuntu 18.04

What is Pyenv?
Pyenv is a fantastic tool for installing and managing multiple Python versions. It enables a developer to quickly gain access to newer versions of Python and keeps the system clean and free of unnecessary package bloat. It also offers the ability to quickly switch from one version of Python to another, as well as specify the version of Python a given project uses and can automatically switch to that version. This tutorial covers how to install pyenv on Ubuntu 18.04.

#tutorials #apt #debian #environment #git #github #linux #package #package management #package manager #personal package archive #ppa #pyenv #python #python 3 #python support #python-pip #repository #smb #software #source install #ubuntu #ubuntu 18.04 #venv #virtualenv #web application development

Jolie  Reichert

Jolie Reichert

1596161100

Stay Safe on GitHub: Security Practices to Follow

GitHub is undoubtedly the largest and most popular social development platform in the world. According to its 2019 State of the Octoverse Report, GitHub is home to over 40 million, and the community keeps expanding every day.

As developers in this deeply interconnected community use open source code to build software, Github security should be a top priority. This is because extensive code re-use increases the risk of distributing vulnerabilities from one dependency or repository to another. As such, every contributor should focus on creating a secure development environment.

Here are eight security practices that GitHub users can follow to stay safe and protect their code:

Strengthen Access Controls

Implementing proper access control is one of the best practices for enhancing security, not only on GitHub but in every other environment where code security is imperative.

GitHub offers several options that users can employ to reduce the risk of improper exposure. But to start with, it is important to employ the least privilege model where users are only granted necessary permissions.

Here are some basic access control guidelines that you should follow:

  • Restrict the creation of repositories to prevent users from exposing organization information in public repositories.
  • Enable branch protection and status checks to ensure users can merge commits or manipulate branches safely.
  • Allow or disallow forking private repositories to ensure users do not expose or share organizational code with unauthorized parties.
  • Revoke access for all inactive users who are no longer part of the contributors.
  • Review access rights to your GitHub projects periodically.
  • Ensure users do not share GitHub accounts or passwords.
  • Ensure every contributor uses two-factor authentication on their account.
  • Rotate personal access tokens and SSH keys

#tutorial #github #access control #software security #repository management #github issues #source code analysis #github apps #github enterprise #git best practices

Jolie  Reichert

Jolie Reichert

1595668020

Stay Safe on GitHub: Security Practices to Follow

GitHub is undoubtedly the largest and most popular social development platform in the world. According to its 2019 State of the Octoverse Report, GitHub is home to over 40 million, and the community keeps expanding every day.

As developers in this deeply interconnected community use open source code to build software, Github security should be a top priority. This is because extensive code re-use increases the risk of distributing vulnerabilities from one dependency or repository to another. As such, every contributor should focus on creating a secure development environment.

Here are eight security practices that GitHub users can follow to stay safe and protect their code:

Strengthen Access Controls

Implementing proper access control is one of the best practices for enhancing security, not only on GitHub but in every other environment where code security is imperative.

GitHub offers several options that users can employ to reduce the risk of improper exposure. But to start with, it is important to employ the least privilege model where users are only granted necessary permissions.

Here are some basic access control guidelines that you should follow:

  • Restrict the creation of repositories to prevent users from exposing organization information in public repositories.
  • Enable branch protection and status checks to ensure users can merge commits or manipulate branches safely.
  • Allow or disallow forking private repositories to ensure users do not expose or share organizational code with unauthorized parties.
  • Revoke access for all inactive users who are no longer part of the contributors.

#tutorial #github #access control #software security #repository management #github issues #source code analysis #github apps #github enterprise #git best practices