curl --request POST \
--url https://api.benchgen.com/api/git-connections/ \
--header 'Content-Type: application/json' \
--data '
{
"repo": "https://github.com/acme/benchmarks",
"token": "<string>",
"branch": "main",
"name": "<string>"
}
'import requests
url = "https://api.benchgen.com/api/git-connections/"
payload = {
"repo": "https://github.com/acme/benchmarks",
"token": "<string>",
"branch": "main",
"name": "<string>"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
repo: 'https://github.com/acme/benchmarks',
token: '<string>',
branch: 'main',
name: '<string>'
})
};
fetch('https://api.benchgen.com/api/git-connections/', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.benchgen.com/api/git-connections/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'repo' => 'https://github.com/acme/benchmarks',
'token' => '<string>',
'branch' => 'main',
'name' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.benchgen.com/api/git-connections/"
payload := strings.NewReader("{\n \"repo\": \"https://github.com/acme/benchmarks\",\n \"token\": \"<string>\",\n \"branch\": \"main\",\n \"name\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.benchgen.com/api/git-connections/")
.header("Content-Type", "application/json")
.body("{\n \"repo\": \"https://github.com/acme/benchmarks\",\n \"token\": \"<string>\",\n \"branch\": \"main\",\n \"name\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.benchgen.com/api/git-connections/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"repo\": \"https://github.com/acme/benchmarks\",\n \"token\": \"<string>\",\n \"branch\": \"main\",\n \"name\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"valid": true,
"connection": {
"id": 123,
"name": "<string>",
"repo": "https://github.com/acme/benchmarks",
"host": "github.com",
"branch": "main",
"token_hint": "ghp_...1122",
"is_default": true,
"created_at": "2023-11-07T05:31:56Z",
"last_used_at": "2023-11-07T05:31:56Z",
"last_checked_at": "2023-11-07T05:31:56Z",
"last_error": "<string>"
}
}Connect a git repository
Store a repository and an access token that may write to it, so benchmark versions can be recorded there. The token is checked against the git host before anything is stored, so a 201 means it really works: a token that can read but not write is refused with that reason, and so is one the host rejects. It is then held as ciphertext and is never returned by any endpoint but use. Connecting a repository you already have replaces its credential rather than adding a second entry. On GitHub use a fine-grained personal access token limited to that one repository with Contents read and write; on GitLab a project access token with the api scope. Requires the repo:write scope.
curl --request POST \
--url https://api.benchgen.com/api/git-connections/ \
--header 'Content-Type: application/json' \
--data '
{
"repo": "https://github.com/acme/benchmarks",
"token": "<string>",
"branch": "main",
"name": "<string>"
}
'import requests
url = "https://api.benchgen.com/api/git-connections/"
payload = {
"repo": "https://github.com/acme/benchmarks",
"token": "<string>",
"branch": "main",
"name": "<string>"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
repo: 'https://github.com/acme/benchmarks',
token: '<string>',
branch: 'main',
name: '<string>'
})
};
fetch('https://api.benchgen.com/api/git-connections/', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.benchgen.com/api/git-connections/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'repo' => 'https://github.com/acme/benchmarks',
'token' => '<string>',
'branch' => 'main',
'name' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.benchgen.com/api/git-connections/"
payload := strings.NewReader("{\n \"repo\": \"https://github.com/acme/benchmarks\",\n \"token\": \"<string>\",\n \"branch\": \"main\",\n \"name\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.benchgen.com/api/git-connections/")
.header("Content-Type", "application/json")
.body("{\n \"repo\": \"https://github.com/acme/benchmarks\",\n \"token\": \"<string>\",\n \"branch\": \"main\",\n \"name\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.benchgen.com/api/git-connections/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"repo\": \"https://github.com/acme/benchmarks\",\n \"token\": \"<string>\",\n \"branch\": \"main\",\n \"name\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"valid": true,
"connection": {
"id": 123,
"name": "<string>",
"repo": "https://github.com/acme/benchmarks",
"host": "github.com",
"branch": "main",
"token_hint": "ghp_...1122",
"is_default": true,
"created_at": "2023-11-07T05:31:56Z",
"last_used_at": "2023-11-07T05:31:56Z",
"last_checked_at": "2023-11-07T05:31:56Z",
"last_error": "<string>"
}
}201 means the credential really works rather than that it was written down. A
400 says which of the three things went wrong: the host rejected it, it can
read but not write, or it is a BenchGen bgn_ token pasted in by mistake.
curl -X POST "https://api.benchgen.com/api/git-connections/" \
-H "Authorization: Bearer $BENCHGEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"repo": "https://github.com/acme/benchmarks", "token": "github_pat_..."}'
Which token to create
The narrow token is no harder to make and decides what a leak costs you.GitHub
GitHub
repo scope also works, and reaches
every repository you can see, which is the reason not to use one.GitLab
GitLab
api scope and at
least the Developer role. A personal access token works too and, again,
reaches everything you can see.What happens to the token
It is encrypted before it is stored, with a key held in the environment rather than in the database, so a database dump on its own does not reveal it. After that it is returned by exactly one endpoint, read back a stored credential, which needs the separaterepo:use scope. Listing connections shows only a hint such as
ghp_...1122.
Reconnecting
Connecting a repository you already have replaces its credential instead of adding a second entry, so there is never a question about which token is current. That is how you rotate one: connect again with the new token, then revoke the old one on the git host.Related
Read back a credential
Check a credential
Create from a repository
Version a benchmark
Body
The repository address. A browse URL works too, and its branch is read from it.
"https://github.com/acme/benchmarks"
An access token that may write to that repository. Stored encrypted, never returned here.
Defaults to the branch in the URL, else main.
"main"
A label for your own use.