You've already forked go-semantic-release
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a8e746159 | |||
| ff1798b10b | |||
| fa40e6ee71 | |||
| 8a05b72ec8 | |||
| 732b1ee1a0 | |||
| 5636fecc14 | |||
| b13699d2f8 | |||
| f7f395c30f | |||
| 0bcede8594 | |||
| 34589749ba | |||
| b38abd00f2 | |||
| 913783cf08 | |||
| d701ce2352 | |||
| 4d0d1e4319 | |||
| c4cecdebb4 |
12
.release.yml
12
.release.yml
@@ -8,20 +8,20 @@ branch:
|
|||||||
master: release
|
master: release
|
||||||
develop: beta
|
develop: beta
|
||||||
assets:
|
assets:
|
||||||
- name: ./build/go-semantic-release.linux_x86_64
|
- path: ./build/go-semantic-release.linux_amd64
|
||||||
compress: true
|
compress: true
|
||||||
- name: ./build/go-semantic-release.linux_arm64
|
- path: ./build/go-semantic-release.linux_arm64
|
||||||
compress: true
|
compress: true
|
||||||
- name: ./build/go-semantic-release.windows_i386.exe
|
- path: ./build/go-semantic-release.windows_i386.exe
|
||||||
compress: true
|
compress: true
|
||||||
- name: ./build/go-semantic-release.windows_x86_64.exe
|
- path: ./build/go-semantic-release.windows_amd64.exe
|
||||||
compress: true
|
compress: true
|
||||||
- name: ./build/go-semantic-release.darwin_x86_64
|
- path: ./build/go-semantic-release.darwin_amd64
|
||||||
compress: true
|
compress: true
|
||||||
changelog:
|
changelog:
|
||||||
docker:
|
docker:
|
||||||
latest: true
|
latest: true
|
||||||
repository: "cybercinch/go-semantic-release"
|
repository: hub.cybercinch.nz/cybercinch/go-semantic-release
|
||||||
showAuthors: true
|
showAuthors: true
|
||||||
|
|
||||||
hooks:
|
hooks:
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ variables:
|
|||||||
from_secret: hub_username_cybercinch
|
from_secret: hub_username_cybercinch
|
||||||
password:
|
password:
|
||||||
from_secret: docker_password_cybercinch
|
from_secret: docker_password_cybercinch
|
||||||
|
- build_args: &build_args
|
||||||
|
- TAG: ${CI_COMMIT_TAG}
|
||||||
clone:
|
clone:
|
||||||
git:
|
git:
|
||||||
image: woodpeckerci/plugin-git
|
image: woodpeckerci/plugin-git
|
||||||
@@ -26,31 +28,36 @@ steps:
|
|||||||
GOOS=linux
|
GOOS=linux
|
||||||
GOARCH=amd64
|
GOARCH=amd64
|
||||||
CGO_ENABLED=0
|
CGO_ENABLED=0
|
||||||
go build -o build/go-semantic-release.linux_x86_64 -ldflags "-w -s --X main.version=`go-semantic-release next`"
|
GOFLAGS=-mod=vendor
|
||||||
|
go build -o build/go-semantic-release.linux_amd64 -ldflags "-w -s --X main.version=`go-semantic-release next`"
|
||||||
./cmd/go-semantic-release/
|
./cmd/go-semantic-release/
|
||||||
- >
|
- >
|
||||||
GOOS=linux
|
GOOS=linux
|
||||||
GOARCH=arm64
|
GOARCH=arm64
|
||||||
CGO_ENABLED=0
|
CGO_ENABLED=0
|
||||||
|
GOFLAGS=-mod=vendor
|
||||||
go build -o build/go-semantic-release.linux_arm64 -ldflags "-w -s --X main.version=`go-semantic-release next`"
|
go build -o build/go-semantic-release.linux_arm64 -ldflags "-w -s --X main.version=`go-semantic-release next`"
|
||||||
./cmd/go-semantic-release/
|
./cmd/go-semantic-release/
|
||||||
- >
|
- >
|
||||||
GOOS=windows
|
GOOS=windows
|
||||||
GOARCH=386
|
GOARCH=386
|
||||||
CGO_ENABLED=0
|
CGO_ENABLED=0
|
||||||
|
GOFLAGS=-mod=vendor
|
||||||
go build -o build/go-semantic-release.windows_i386.exe -ldflags "-w -s -X main.version=`go-semantic-release next`"
|
go build -o build/go-semantic-release.windows_i386.exe -ldflags "-w -s -X main.version=`go-semantic-release next`"
|
||||||
./cmd/go-semantic-release/
|
./cmd/go-semantic-release/
|
||||||
- >
|
- >
|
||||||
GOOS=windows
|
GOOS=windows
|
||||||
GOARCH=amd64
|
GOARCH=amd64
|
||||||
CGO_ENABLED=0
|
CGO_ENABLED=0
|
||||||
go build -o build/go-semantic-release.windows_x86_64.exe -ldflags "-w -s -X main.version=`go-semantic-release next`"
|
GOFLAGS=-mod=vendor
|
||||||
|
go build -o build/go-semantic-release.windows_amd64.exe -ldflags "-w -s -X main.version=`go-semantic-release next`"
|
||||||
./cmd/go-semantic-release/
|
./cmd/go-semantic-release/
|
||||||
- >
|
- >
|
||||||
GOOS=darwin
|
GOOS=darwin
|
||||||
GOARCH=amd64
|
GOARCH=amd64
|
||||||
CGO_ENABLED=0
|
CGO_ENABLED=0
|
||||||
go build -o build/go-semantic-release.darwin_x86_64 -ldflags "-w -s -X main.version=`go-semantic-release next`"
|
GOFLAGS=-mod=vendor
|
||||||
|
go build -o build/go-semantic-release.darwin_amd64 -ldflags "-w -s -X main.version=`go-semantic-release next`"
|
||||||
./cmd/go-semantic-release/
|
./cmd/go-semantic-release/
|
||||||
- go-semantic-release --loglevel debug release # Actually make the release on Gitea. Uploading assets
|
- go-semantic-release --loglevel debug release # Actually make the release on Gitea. Uploading assets
|
||||||
environment:
|
environment:
|
||||||
@@ -64,25 +71,24 @@ steps:
|
|||||||
- push
|
- push
|
||||||
- manual
|
- manual
|
||||||
|
|
||||||
# publish-docker-tagged:
|
publish-docker-tagged:
|
||||||
# image: docker.io/cybercinch/woodpecker-plugin-depot
|
image: woodpeckerci/plugin-docker-buildx
|
||||||
# pull: true
|
pull: true
|
||||||
# settings:
|
settings:
|
||||||
# <<: *docker_creds
|
<<: *docker_creds
|
||||||
# token:
|
registry: hub.cybercinch.nz
|
||||||
# from_secret: depot_token
|
repo: hub.cybercinch.nz/cybercinch/${CI_REPO_NAME}
|
||||||
# repohost: hub.cybercinch.nz
|
dockerfile: Dockerfile
|
||||||
# repo: cybercinch/${CI_REPO_NAME}
|
platforms: *platforms
|
||||||
# project:
|
build_args:
|
||||||
# from_secret: depot_project
|
- TAG=${CI_COMMIT_TAG}
|
||||||
# dockerfile: Dockerfile
|
tags:
|
||||||
# push: true
|
- latest
|
||||||
# platforms: *platforms
|
- ${CI_COMMIT_TAG}
|
||||||
# tags: ["latest", "$CI_COMMIT_TAG"]
|
when:
|
||||||
# when:
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
# branch: ${CI_REPO_DEFAULT_BRANCH}
|
event:
|
||||||
# event:
|
- tag
|
||||||
# - tag
|
|
||||||
|
|
||||||
publish-docker-develop:
|
publish-docker-develop:
|
||||||
image: docker.io/cybercinch/woodpecker-plugin-depot
|
image: docker.io/cybercinch/woodpecker-plugin-depot
|
||||||
|
|||||||
14
Dockerfile
14
Dockerfile
@@ -1,8 +1,20 @@
|
|||||||
FROM alpine:3.14
|
FROM alpine:3.14
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
# Set by build-arg
|
||||||
|
ARG TAG=v2.2.3
|
||||||
|
|
||||||
|
# Strip out the prefix
|
||||||
|
ENV PLAT=${TARGETPLATFORM//"linux/"/}
|
||||||
|
|
||||||
WORKDIR /code
|
WORKDIR /code
|
||||||
|
|
||||||
COPY ./build/go-semantic-release.linux_x86_64 /usr/local/bin/go-semantic-release
|
ADD https://hub.cybercinch.nz/cybercinch/go-semantic-release/releases/download/${TAG}/go-semantic-release.linux_${PLAT}.zip /tmp
|
||||||
|
RUN apk add --no-cache unzip && \
|
||||||
|
unzip -d /tmp /tmp/go-semantic-release.linux_${PLAT}.zip && \
|
||||||
|
ls && \
|
||||||
|
ls /tmp && \
|
||||||
|
mv /tmp/go-semantic-release.linux_${PLAT} /usr/local/bin/go-semantic-release && \
|
||||||
|
rm -f /tmp/go-semantic-release.linux_${PLAT}.zip
|
||||||
|
|
||||||
USER 1000
|
USER 1000
|
||||||
|
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -5,7 +5,7 @@ all: build
|
|||||||
.PHONY: build
|
.PHONY: build
|
||||||
|
|
||||||
build:
|
build:
|
||||||
go build -o build/go-semantic-release-temp ./cmd/go-semantic-release/
|
GOFLAGS=-mod=vendor go build -o build/go-semantic-release-temp ./cmd/go-semantic-release/
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
golangci-lint run --print-issued-lines=false --fix ./...
|
golangci-lint run --print-issued-lines=false --fix ./...
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -1,6 +1,6 @@
|
|||||||
module github.com/Nightapes/go-semantic-release
|
module github.com/Nightapes/go-semantic-release
|
||||||
|
|
||||||
go 1.18
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/semver v1.5.0
|
github.com/Masterminds/semver v1.5.0
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ func New(c *config.GiteaProvider, checkConfig bool) (*GiteaClient, error) {
|
|||||||
if err != nil && checkConfig {
|
if err != nil && checkConfig {
|
||||||
return &GiteaClient{}, err
|
return &GiteaClient{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.AccessToken = token
|
c.AccessToken = token
|
||||||
|
|
||||||
if c.URL == "" {
|
if c.URL == "" {
|
||||||
@@ -114,11 +115,13 @@ func (g *GiteaClient) makeRelease(releaseVersion *shared.ReleaseVersion, generat
|
|||||||
Title: generatedChangelog.Title,
|
Title: generatedChangelog.Title,
|
||||||
Note: generatedChangelog.Content,
|
Note: generatedChangelog.Content,
|
||||||
IsPrerelease: prerelease}
|
IsPrerelease: prerelease}
|
||||||
|
// TODO Test if this prevents release double-up
|
||||||
g.client.CreateRelease(g.config.User, g.config.Repo, opt)
|
|
||||||
|
|
||||||
release, _, err := g.client.CreateRelease(g.config.User, g.config.Repo, opt)
|
release, _, err := g.client.CreateRelease(g.config.User, g.config.Repo, opt)
|
||||||
|
|
||||||
|
g.log.Debugf("Release response: %+v", *release)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
g.log.Debugf("Release Error response: %+v", err)
|
||||||
if strings.Contains(err.Error(), "Release is has no Tag") {
|
if strings.Contains(err.Error(), "Release is has no Tag") {
|
||||||
g.log.Infof("A release with tag %s already exits, will not perform a release or update", tag)
|
g.log.Infof("A release with tag %s already exits, will not perform a release or update", tag)
|
||||||
return nil
|
return nil
|
||||||
@@ -126,7 +129,6 @@ func (g *GiteaClient) makeRelease(releaseVersion *shared.ReleaseVersion, generat
|
|||||||
return fmt.Errorf("could not create release: %s", err.Error())
|
return fmt.Errorf("could not create release: %s", err.Error())
|
||||||
}
|
}
|
||||||
g.release = release
|
g.release = release
|
||||||
g.log.Debugf("Release response: %+v", *release)
|
|
||||||
g.log.Infof("Created release")
|
g.log.Infof("Created release")
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
|||||||
199
internal/releaser/gitea/gitea_test.go
Normal file
199
internal/releaser/gitea/gitea_test.go
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver"
|
||||||
|
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/shared"
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testHelperMethodStruct struct {
|
||||||
|
config config.GitHubProvider
|
||||||
|
valid bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type testReleaseStruct struct {
|
||||||
|
config config.GitHubProvider
|
||||||
|
releaseVersion *shared.ReleaseVersion
|
||||||
|
generatedChangelog *shared.GeneratedChangelog
|
||||||
|
requestResponseBody string
|
||||||
|
requestResponseCode int
|
||||||
|
valid bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var testNewClient = []testHelperMethodStruct{
|
||||||
|
{config: config.GitHubProvider{
|
||||||
|
Repo: "foo",
|
||||||
|
User: "bar",
|
||||||
|
},
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{config: config.GitHubProvider{
|
||||||
|
Repo: "foo",
|
||||||
|
User: "bar",
|
||||||
|
CustomURL: "https://test.com",
|
||||||
|
},
|
||||||
|
valid: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastVersion, _ = semver.NewVersion("1.0.0")
|
||||||
|
var newVersion, _ = semver.NewVersion("2.0.0")
|
||||||
|
|
||||||
|
var testReleases = []testReleaseStruct{
|
||||||
|
{
|
||||||
|
config: config.GitHubProvider{
|
||||||
|
Repo: "foo",
|
||||||
|
User: "bar",
|
||||||
|
},
|
||||||
|
releaseVersion: &shared.ReleaseVersion{
|
||||||
|
Last: shared.ReleaseVersionEntry{
|
||||||
|
Version: lastVersion,
|
||||||
|
Commit: "foo",
|
||||||
|
},
|
||||||
|
Next: shared.ReleaseVersionEntry{
|
||||||
|
Version: newVersion,
|
||||||
|
Commit: "bar",
|
||||||
|
},
|
||||||
|
Branch: "master",
|
||||||
|
},
|
||||||
|
generatedChangelog: &shared.GeneratedChangelog{
|
||||||
|
Title: "title",
|
||||||
|
Content: "content",
|
||||||
|
},
|
||||||
|
requestResponseBody: "{ \"url\": \"https://api.github.com/repos/octocat/Hello-World/releases/1\", \"html_url\": \"https://github.com/octocat/Hello-World/releases/v1.0.0\", \"assets_url\": \"https://api.github.com/repos/octocat/Hello-World/releases/1/assets\", \"upload_url\": \"https://uploads.github.com/repos/octocat/Hello-World/releases/1/assets{?name,label}\", \"tarball_url\": \"https://api.github.com/repos/octocat/Hello-World/tarball/v1.0.0\", \"zipball_url\": \"https://api.github.com/repos/octocat/Hello-World/zipball/v1.0.0\", \"id\": 1, \"node_id\": \"MDc6UmVsZWFzZTE=\", \"tag_name\": \"v1.0.0\", \"target_commitish\": \"master\", \"name\": \"v1.0.0\", \"body\": \"Description of the release\", \"draft\": false, \"prerelease\": false, \"created_at\": \"2013-02-27T19:35:32Z\", \"published_at\": \"2013-02-27T19:35:32Z\", \"author\": { \"login\": \"octocat\", \"id\": 1, \"node_id\": \"MDQ6VXNlcjE=\", \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\", \"gravatar_id\": \"\", \"url\": \"https://api.github.com/users/octocat\", \"html_url\": \"https://github.com/octocat\", \"followers_url\": \"https://api.github.com/users/octocat/followers\", \"following_url\": \"https://api.github.com/users/octocat/following{/other_user}\", \"gists_url\": \"https://api.github.com/users/octocat/gists{/gist_id}\", \"starred_url\": \"https://api.github.com/users/octocat/starred{/owner}{/repo}\", \"subscriptions_url\": \"https://api.github.com/users/octocat/subscriptions\", \"organizations_url\": \"https://api.github.com/users/octocat/orgs\", \"repos_url\": \"https://api.github.com/users/octocat/repos\", \"events_url\": \"https://api.github.com/users/octocat/events{/privacy}\", \"received_events_url\": \"https://api.github.com/users/octocat/received_events\", \"type\": \"User\", \"site_admin\": false }, \"assets\": [ ]}",
|
||||||
|
requestResponseCode: 200,
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: config.GitHubProvider{
|
||||||
|
Repo: "foo",
|
||||||
|
User: "bar",
|
||||||
|
},
|
||||||
|
releaseVersion: &shared.ReleaseVersion{
|
||||||
|
Last: shared.ReleaseVersionEntry{
|
||||||
|
Version: lastVersion,
|
||||||
|
Commit: "foo",
|
||||||
|
},
|
||||||
|
Next: shared.ReleaseVersionEntry{
|
||||||
|
Version: newVersion,
|
||||||
|
Commit: "bar",
|
||||||
|
},
|
||||||
|
Branch: "master",
|
||||||
|
},
|
||||||
|
generatedChangelog: &shared.GeneratedChangelog{
|
||||||
|
Title: "title",
|
||||||
|
Content: "content",
|
||||||
|
},
|
||||||
|
requestResponseCode: 400,
|
||||||
|
valid: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func initHTTPServer(respCode int, body string) *httptest.Server {
|
||||||
|
|
||||||
|
return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
|
||||||
|
log.Infof("Got call from %s %s", req.Method, req.URL.String())
|
||||||
|
|
||||||
|
rw.WriteHeader(respCode)
|
||||||
|
rw.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
if _, err := rw.Write([]byte(body)); err != nil {
|
||||||
|
log.Info(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
for _, testOject := range testNewClient {
|
||||||
|
if testOject.valid {
|
||||||
|
os.Setenv("GITHUB_TOKEN", "XXX")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := New(&testOject.config, true)
|
||||||
|
assert.Equal(t, testOject.valid, err == nil)
|
||||||
|
|
||||||
|
os.Unsetenv("GITHUB_TOKEN")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetCommitURL(t *testing.T) {
|
||||||
|
os.Setenv("GITHUB_TOKEN", "XX")
|
||||||
|
for _, testOject := range testNewClient {
|
||||||
|
client, _ := New(&testOject.config, false)
|
||||||
|
actualURL := client.GetCommitURL()
|
||||||
|
if testOject.config.CustomURL != "" {
|
||||||
|
expectedURL := fmt.Sprintf("%s/api/v3/%s/%s/commit/{{hash}}", testOject.config.CustomURL, testOject.config.User, testOject.config.Repo)
|
||||||
|
assert.EqualValues(t, expectedURL, actualURL)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
expectedURL := fmt.Sprintf("%s/%s/%s/commit/{{hash}}", "https://github.com", testOject.config.User, testOject.config.Repo)
|
||||||
|
assert.EqualValues(t, expectedURL, actualURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.Unsetenv("GITHUB_TOKEN")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetCompareURL(t *testing.T) {
|
||||||
|
os.Setenv("GITHUB_TOKEN", "XX")
|
||||||
|
for _, testOject := range testNewClient {
|
||||||
|
client, _ := New(&testOject.config, false)
|
||||||
|
actualURL := client.GetCompareURL("1", "2")
|
||||||
|
if testOject.config.CustomURL != "" {
|
||||||
|
expectedURL := fmt.Sprintf("%s/api/v3/%s/%s/compare/%s...%s", testOject.config.CustomURL, testOject.config.User, testOject.config.Repo, "1", "2")
|
||||||
|
assert.EqualValues(t, expectedURL, actualURL)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
expectedURL := fmt.Sprintf("%s/%s/%s/compare/%s...%s", "https://github.com", testOject.config.User, testOject.config.Repo, "1", "2")
|
||||||
|
assert.EqualValues(t, expectedURL, actualURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.Unsetenv("GITHUB_TOKEN")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateRelease(t *testing.T) {
|
||||||
|
os.Setenv("GITHUB_TOKEN", "XX")
|
||||||
|
|
||||||
|
for _, testObejct := range testReleases {
|
||||||
|
if testObejct.valid {
|
||||||
|
server := initHTTPServer(testObejct.requestResponseCode, testObejct.requestResponseBody)
|
||||||
|
testObejct.config.CustomURL = server.URL
|
||||||
|
client, _ := New(&testObejct.config, false)
|
||||||
|
|
||||||
|
err := client.makeRelease(testObejct.releaseVersion, testObejct.generatedChangelog)
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, testObejct.valid, err == nil)
|
||||||
|
|
||||||
|
server.Close()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
testObejct.config.CustomURL = "http://foo"
|
||||||
|
client, _ := New(&testObejct.config, false)
|
||||||
|
|
||||||
|
err := client.makeRelease(testObejct.releaseVersion, testObejct.generatedChangelog)
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.Unsetenv("GITHUB_TOKEN")
|
||||||
|
|
||||||
|
}
|
||||||
20
vendor/code.gitea.io/sdk/gitea/LICENSE
generated
vendored
Normal file
20
vendor/code.gitea.io/sdk/gitea/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2016 The Gitea Authors
|
||||||
|
Copyright (c) 2014 The Gogs Authors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
47
vendor/code.gitea.io/sdk/gitea/admin_cron.go
generated
vendored
Normal file
47
vendor/code.gitea.io/sdk/gitea/admin_cron.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CronTask represents a Cron task
|
||||||
|
type CronTask struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Schedule string `json:"schedule"`
|
||||||
|
Next time.Time `json:"next"`
|
||||||
|
Prev time.Time `json:"prev"`
|
||||||
|
ExecTimes int64 `json:"exec_times"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListCronTaskOptions list options for ListCronTasks
|
||||||
|
type ListCronTaskOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListCronTasks list available cron tasks
|
||||||
|
func (c *Client) ListCronTasks(opt ListCronTaskOptions) ([]*CronTask, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
ct := make([]*CronTask, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/admin/cron?%s", opt.getURLQuery().Encode()), jsonHeader, nil, &ct)
|
||||||
|
return ct, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunCronTasks run a cron task
|
||||||
|
func (c *Client) RunCronTasks(task string) (*Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := escapeValidatePathSegments(&task); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("POST", fmt.Sprintf("/admin/cron/%s", task), jsonHeader, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
39
vendor/code.gitea.io/sdk/gitea/admin_org.go
generated
vendored
Normal file
39
vendor/code.gitea.io/sdk/gitea/admin_org.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminListOrgsOptions options for listing admin's organizations
|
||||||
|
type AdminListOrgsOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdminListOrgs lists all orgs
|
||||||
|
func (c *Client) AdminListOrgs(opt AdminListOrgsOptions) ([]*Organization, *Response, error) {
|
||||||
|
opt.setDefaults()
|
||||||
|
orgs := make([]*Organization, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/admin/orgs?%s", opt.getURLQuery().Encode()), nil, nil, &orgs)
|
||||||
|
return orgs, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdminCreateOrg create an organization
|
||||||
|
func (c *Client) AdminCreateOrg(user string, opt CreateOrgOption) (*Organization, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
org := new(Organization)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/admin/users/%s/orgs", user), jsonHeader, bytes.NewReader(body), org)
|
||||||
|
return org, resp, err
|
||||||
|
}
|
||||||
25
vendor/code.gitea.io/sdk/gitea/admin_repo.go
generated
vendored
Normal file
25
vendor/code.gitea.io/sdk/gitea/admin_repo.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminCreateRepo create a repo
|
||||||
|
func (c *Client) AdminCreateRepo(user string, opt CreateRepoOption) (*Repository, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
repo := new(Repository)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/admin/users/%s/repos", user), jsonHeader, bytes.NewReader(body), repo)
|
||||||
|
return repo, resp, err
|
||||||
|
}
|
||||||
130
vendor/code.gitea.io/sdk/gitea/admin_user.go
generated
vendored
Normal file
130
vendor/code.gitea.io/sdk/gitea/admin_user.go
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminListUsersOptions options for listing admin users
|
||||||
|
type AdminListUsersOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdminListUsers lists all users
|
||||||
|
func (c *Client) AdminListUsers(opt AdminListUsersOptions) ([]*User, *Response, error) {
|
||||||
|
opt.setDefaults()
|
||||||
|
users := make([]*User, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/admin/users?%s", opt.getURLQuery().Encode()), nil, nil, &users)
|
||||||
|
return users, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUserOption create user options
|
||||||
|
type CreateUserOption struct {
|
||||||
|
SourceID int64 `json:"source_id"`
|
||||||
|
LoginName string `json:"login_name"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
MustChangePassword *bool `json:"must_change_password"`
|
||||||
|
SendNotify bool `json:"send_notify"`
|
||||||
|
Visibility *VisibleType `json:"visibility"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the CreateUserOption struct
|
||||||
|
func (opt CreateUserOption) Validate() error {
|
||||||
|
if len(opt.Email) == 0 {
|
||||||
|
return fmt.Errorf("email is empty")
|
||||||
|
}
|
||||||
|
if len(opt.Username) == 0 {
|
||||||
|
return fmt.Errorf("username is empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdminCreateUser create a user
|
||||||
|
func (c *Client) AdminCreateUser(opt CreateUserOption) (*User, *Response, error) {
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
user := new(User)
|
||||||
|
resp, err := c.getParsedResponse("POST", "/admin/users", jsonHeader, bytes.NewReader(body), user)
|
||||||
|
return user, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditUserOption edit user options
|
||||||
|
type EditUserOption struct {
|
||||||
|
SourceID int64 `json:"source_id"`
|
||||||
|
LoginName string `json:"login_name"`
|
||||||
|
Email *string `json:"email"`
|
||||||
|
FullName *string `json:"full_name"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Description *string `json:"description"`
|
||||||
|
MustChangePassword *bool `json:"must_change_password"`
|
||||||
|
Website *string `json:"website"`
|
||||||
|
Location *string `json:"location"`
|
||||||
|
Active *bool `json:"active"`
|
||||||
|
Admin *bool `json:"admin"`
|
||||||
|
AllowGitHook *bool `json:"allow_git_hook"`
|
||||||
|
AllowImportLocal *bool `json:"allow_import_local"`
|
||||||
|
MaxRepoCreation *int `json:"max_repo_creation"`
|
||||||
|
ProhibitLogin *bool `json:"prohibit_login"`
|
||||||
|
AllowCreateOrganization *bool `json:"allow_create_organization"`
|
||||||
|
Restricted *bool `json:"restricted"`
|
||||||
|
Visibility *VisibleType `json:"visibility"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdminEditUser modify user informations
|
||||||
|
func (c *Client) AdminEditUser(user string, opt EditUserOption) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("PATCH", fmt.Sprintf("/admin/users/%s", user), jsonHeader, bytes.NewReader(body))
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdminDeleteUser delete one user according name
|
||||||
|
func (c *Client) AdminDeleteUser(user string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/admin/users/%s", user), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdminCreateUserPublicKey adds a public key for the user
|
||||||
|
func (c *Client) AdminCreateUserPublicKey(user string, opt CreateKeyOption) (*PublicKey, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
key := new(PublicKey)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/admin/users/%s/keys", user), jsonHeader, bytes.NewReader(body), key)
|
||||||
|
return key, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdminDeleteUserPublicKey deletes a user's public key
|
||||||
|
func (c *Client) AdminDeleteUserPublicKey(user string, keyID int) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/admin/users/%s/keys/%d", user, keyID), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
38
vendor/code.gitea.io/sdk/gitea/agent.go
generated
vendored
Normal file
38
vendor/code.gitea.io/sdk/gitea/agent.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh/agent"
|
||||||
|
)
|
||||||
|
|
||||||
|
// hasAgent returns true if the ssh agent is available
|
||||||
|
func hasAgent() bool {
|
||||||
|
if _, err := os.Stat(os.Getenv("SSH_AUTH_SOCK")); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAgent returns a ssh agent
|
||||||
|
func GetAgent() (agent.Agent, error) {
|
||||||
|
if !hasAgent() {
|
||||||
|
return nil, fmt.Errorf("no ssh agent available")
|
||||||
|
}
|
||||||
|
|
||||||
|
sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return agent.NewClient(sshAgent), nil
|
||||||
|
}
|
||||||
28
vendor/code.gitea.io/sdk/gitea/agent_windows.go
generated
vendored
Normal file
28
vendor/code.gitea.io/sdk/gitea/agent_windows.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/davidmz/go-pageant"
|
||||||
|
"golang.org/x/crypto/ssh/agent"
|
||||||
|
)
|
||||||
|
|
||||||
|
// hasAgent returns true if pageant is available
|
||||||
|
func hasAgent() bool {
|
||||||
|
return pageant.Available()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAgent returns a ssh agent
|
||||||
|
func GetAgent() (agent.Agent, error) {
|
||||||
|
if !hasAgent() {
|
||||||
|
return nil, fmt.Errorf("no pageant available")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pageant.New(), nil
|
||||||
|
}
|
||||||
111
vendor/code.gitea.io/sdk/gitea/attachment.go
generated
vendored
Normal file
111
vendor/code.gitea.io/sdk/gitea/attachment.go
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea // import "code.gitea.io/sdk/gitea"
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Attachment a generic attachment
|
||||||
|
type Attachment struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
DownloadCount int64 `json:"download_count"`
|
||||||
|
Created time.Time `json:"created_at"`
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
DownloadURL string `json:"browser_download_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListReleaseAttachmentsOptions options for listing release's attachments
|
||||||
|
type ListReleaseAttachmentsOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListReleaseAttachments list release's attachments
|
||||||
|
func (c *Client) ListReleaseAttachments(user, repo string, release int64, opt ListReleaseAttachmentsOptions) ([]*Attachment, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
attachments := make([]*Attachment, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/releases/%d/assets?%s", user, repo, release, opt.getURLQuery().Encode()),
|
||||||
|
nil, nil, &attachments)
|
||||||
|
return attachments, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReleaseAttachment returns the requested attachment
|
||||||
|
func (c *Client) GetReleaseAttachment(user, repo string, release, id int64) (*Attachment, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
a := new(Attachment)
|
||||||
|
resp, err := c.getParsedResponse("GET",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id),
|
||||||
|
nil, nil, &a)
|
||||||
|
return a, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateReleaseAttachment creates an attachment for the given release
|
||||||
|
func (c *Client) CreateReleaseAttachment(user, repo string, release int64, file io.Reader, filename string) (*Attachment, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
// Write file to body
|
||||||
|
body := new(bytes.Buffer)
|
||||||
|
writer := multipart.NewWriter(body)
|
||||||
|
part, err := writer.CreateFormFile("attachment", filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = io.Copy(part, file); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err = writer.Close(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send request
|
||||||
|
attachment := new(Attachment)
|
||||||
|
resp, err := c.getParsedResponse("POST",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/releases/%d/assets", user, repo, release),
|
||||||
|
http.Header{"Content-Type": {writer.FormDataContentType()}}, body, &attachment)
|
||||||
|
return attachment, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditAttachmentOptions options for editing attachments
|
||||||
|
type EditAttachmentOptions struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditReleaseAttachment updates the given attachment with the given options
|
||||||
|
func (c *Client) EditReleaseAttachment(user, repo string, release, attachment int64, form EditAttachmentOptions) (*Attachment, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&form)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
attach := new(Attachment)
|
||||||
|
resp, err := c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, attachment), jsonHeader, bytes.NewReader(body), attach)
|
||||||
|
return attach, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteReleaseAttachment deletes the given attachment including the uploaded file
|
||||||
|
func (c *Client) DeleteReleaseAttachment(user, repo string, release, id int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
499
vendor/code.gitea.io/sdk/gitea/client.go
generated
vendored
Normal file
499
vendor/code.gitea.io/sdk/gitea/client.go
generated
vendored
Normal file
@@ -0,0 +1,499 @@
|
|||||||
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-version"
|
||||||
|
)
|
||||||
|
|
||||||
|
var jsonHeader = http.Header{"content-type": []string{"application/json"}}
|
||||||
|
|
||||||
|
// Version return the library version
|
||||||
|
func Version() string {
|
||||||
|
return "0.16.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client represents a thread-safe Gitea API client.
|
||||||
|
type Client struct {
|
||||||
|
url string
|
||||||
|
accessToken string
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
otp string
|
||||||
|
sudo string
|
||||||
|
userAgent string
|
||||||
|
debug bool
|
||||||
|
httpsigner *HTTPSign
|
||||||
|
client *http.Client
|
||||||
|
ctx context.Context
|
||||||
|
mutex sync.RWMutex
|
||||||
|
serverVersion *version.Version
|
||||||
|
getVersionOnce sync.Once
|
||||||
|
ignoreVersion bool // only set by SetGiteaVersion so don't need a mutex lock
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response represents the gitea response
|
||||||
|
type Response struct {
|
||||||
|
*http.Response
|
||||||
|
|
||||||
|
FirstPage int
|
||||||
|
PrevPage int
|
||||||
|
NextPage int
|
||||||
|
LastPage int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientOption are functions used to init a new client
|
||||||
|
type ClientOption func(*Client) error
|
||||||
|
|
||||||
|
// NewClient initializes and returns a API client.
|
||||||
|
// Usage of all gitea.Client methods is concurrency-safe.
|
||||||
|
func NewClient(url string, options ...ClientOption) (*Client, error) {
|
||||||
|
client := &Client{
|
||||||
|
url: strings.TrimSuffix(url, "/"),
|
||||||
|
client: &http.Client{},
|
||||||
|
ctx: context.Background(),
|
||||||
|
}
|
||||||
|
for _, opt := range options {
|
||||||
|
if err := opt(client); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := client.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil {
|
||||||
|
if errors.Is(err, &ErrUnknownVersion{}) {
|
||||||
|
return client, err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientWithHTTP creates an API client with a custom http client
|
||||||
|
// Deprecated use SetHTTPClient option
|
||||||
|
func NewClientWithHTTP(url string, httpClient *http.Client) *Client {
|
||||||
|
client, _ := NewClient(url, SetHTTPClient(httpClient))
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHTTPClient is an option for NewClient to set custom http client
|
||||||
|
func SetHTTPClient(httpClient *http.Client) ClientOption {
|
||||||
|
return func(client *Client) error {
|
||||||
|
client.SetHTTPClient(httpClient)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHTTPClient replaces default http.Client with user given one.
|
||||||
|
func (c *Client) SetHTTPClient(client *http.Client) {
|
||||||
|
c.mutex.Lock()
|
||||||
|
c.client = client
|
||||||
|
c.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetToken is an option for NewClient to set token
|
||||||
|
func SetToken(token string) ClientOption {
|
||||||
|
return func(client *Client) error {
|
||||||
|
client.mutex.Lock()
|
||||||
|
client.accessToken = token
|
||||||
|
client.mutex.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBasicAuth is an option for NewClient to set username and password
|
||||||
|
func SetBasicAuth(username, password string) ClientOption {
|
||||||
|
return func(client *Client) error {
|
||||||
|
client.SetBasicAuth(username, password)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseSSHCert is an option for NewClient to enable SSH certificate authentication via HTTPSign
|
||||||
|
// If you want to auth against the ssh-agent you'll need to set a principal, if you want to
|
||||||
|
// use a file on disk you'll need to specify sshKey.
|
||||||
|
// If you have an encrypted sshKey you'll need to also set the passphrase.
|
||||||
|
func UseSSHCert(principal, sshKey, passphrase string) ClientOption {
|
||||||
|
return func(client *Client) error {
|
||||||
|
if err := client.checkServerVersionGreaterThanOrEqual(version1_17_0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client.mutex.Lock()
|
||||||
|
defer client.mutex.Unlock()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
client.httpsigner, err = NewHTTPSignWithCert(principal, sshKey, passphrase)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseSSHPubkey is an option for NewClient to enable SSH pubkey authentication via HTTPSign
|
||||||
|
// If you want to auth against the ssh-agent you'll need to set a fingerprint, if you want to
|
||||||
|
// use a file on disk you'll need to specify sshKey.
|
||||||
|
// If you have an encrypted sshKey you'll need to also set the passphrase.
|
||||||
|
func UseSSHPubkey(fingerprint, sshKey, passphrase string) ClientOption {
|
||||||
|
return func(client *Client) error {
|
||||||
|
if err := client.checkServerVersionGreaterThanOrEqual(version1_17_0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client.mutex.Lock()
|
||||||
|
defer client.mutex.Unlock()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
client.httpsigner, err = NewHTTPSignWithPubkey(fingerprint, sshKey, passphrase)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBasicAuth sets username and password
|
||||||
|
func (c *Client) SetBasicAuth(username, password string) {
|
||||||
|
c.mutex.Lock()
|
||||||
|
c.username, c.password = username, password
|
||||||
|
c.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOTP is an option for NewClient to set OTP for 2FA
|
||||||
|
func SetOTP(otp string) ClientOption {
|
||||||
|
return func(client *Client) error {
|
||||||
|
client.SetOTP(otp)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOTP sets OTP for 2FA
|
||||||
|
func (c *Client) SetOTP(otp string) {
|
||||||
|
c.mutex.Lock()
|
||||||
|
c.otp = otp
|
||||||
|
c.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetContext is an option for NewClient to set the default context
|
||||||
|
func SetContext(ctx context.Context) ClientOption {
|
||||||
|
return func(client *Client) error {
|
||||||
|
client.SetContext(ctx)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetContext set default context witch is used for http requests
|
||||||
|
func (c *Client) SetContext(ctx context.Context) {
|
||||||
|
c.mutex.Lock()
|
||||||
|
c.ctx = ctx
|
||||||
|
c.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSudo is an option for NewClient to set sudo header
|
||||||
|
func SetSudo(sudo string) ClientOption {
|
||||||
|
return func(client *Client) error {
|
||||||
|
client.SetSudo(sudo)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSudo sets username to impersonate.
|
||||||
|
func (c *Client) SetSudo(sudo string) {
|
||||||
|
c.mutex.Lock()
|
||||||
|
c.sudo = sudo
|
||||||
|
c.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUserAgent is an option for NewClient to set user-agent header
|
||||||
|
func SetUserAgent(userAgent string) ClientOption {
|
||||||
|
return func(client *Client) error {
|
||||||
|
client.SetUserAgent(userAgent)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUserAgent sets the user-agent to send with every request.
|
||||||
|
func (c *Client) SetUserAgent(userAgent string) {
|
||||||
|
c.mutex.Lock()
|
||||||
|
c.userAgent = userAgent
|
||||||
|
c.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDebugMode is an option for NewClient to enable debug mode
|
||||||
|
func SetDebugMode() ClientOption {
|
||||||
|
return func(client *Client) error {
|
||||||
|
client.mutex.Lock()
|
||||||
|
client.debug = true
|
||||||
|
client.mutex.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newResponse(r *http.Response) *Response {
|
||||||
|
response := &Response{Response: r}
|
||||||
|
response.parseLinkHeader()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Response) parseLinkHeader() {
|
||||||
|
link := r.Header.Get("Link")
|
||||||
|
if link == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
links := strings.Split(link, ",")
|
||||||
|
for _, l := range links {
|
||||||
|
u, param, ok := strings.Cut(l, ";")
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
u = strings.Trim(u, " <>")
|
||||||
|
|
||||||
|
key, value, ok := strings.Cut(strings.TrimSpace(param), "=")
|
||||||
|
if !ok || key != "rel" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
value = strings.Trim(value, "\"")
|
||||||
|
|
||||||
|
parsed, err := url.Parse(u)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
page := parsed.Query().Get("page")
|
||||||
|
if page == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch value {
|
||||||
|
case "first":
|
||||||
|
r.FirstPage, _ = strconv.Atoi(page)
|
||||||
|
case "prev":
|
||||||
|
r.PrevPage, _ = strconv.Atoi(page)
|
||||||
|
case "next":
|
||||||
|
r.NextPage, _ = strconv.Atoi(page)
|
||||||
|
case "last":
|
||||||
|
r.LastPage, _ = strconv.Atoi(page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getWebResponse(method, path string, body io.Reader) ([]byte, *Response, error) {
|
||||||
|
c.mutex.RLock()
|
||||||
|
debug := c.debug
|
||||||
|
if debug {
|
||||||
|
fmt.Printf("%s: %s\nBody: %v\n", method, c.url+path, body)
|
||||||
|
}
|
||||||
|
req, err := http.NewRequestWithContext(c.ctx, method, c.url+path, body)
|
||||||
|
|
||||||
|
client := c.client // client ref can change from this point on so safe it
|
||||||
|
c.mutex.RUnlock()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
data, err := io.ReadAll(resp.Body)
|
||||||
|
if debug {
|
||||||
|
fmt.Printf("Response: %v\n\n", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, newResponse(resp), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) doRequest(method, path string, header http.Header, body io.Reader) (*Response, error) {
|
||||||
|
c.mutex.RLock()
|
||||||
|
debug := c.debug
|
||||||
|
if debug {
|
||||||
|
var bodyStr string
|
||||||
|
if body != nil {
|
||||||
|
bs, _ := io.ReadAll(body)
|
||||||
|
body = bytes.NewReader(bs)
|
||||||
|
bodyStr = string(bs)
|
||||||
|
}
|
||||||
|
fmt.Printf("%s: %s\nHeader: %v\nBody: %s\n", method, c.url+"/api/v1"+path, header, bodyStr)
|
||||||
|
}
|
||||||
|
req, err := http.NewRequestWithContext(c.ctx, method, c.url+"/api/v1"+path, body)
|
||||||
|
if err != nil {
|
||||||
|
c.mutex.RUnlock()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(c.accessToken) != 0 {
|
||||||
|
req.Header.Set("Authorization", "token "+c.accessToken)
|
||||||
|
}
|
||||||
|
if len(c.otp) != 0 {
|
||||||
|
req.Header.Set("X-GITEA-OTP", c.otp)
|
||||||
|
}
|
||||||
|
if len(c.username) != 0 {
|
||||||
|
req.SetBasicAuth(c.username, c.password)
|
||||||
|
}
|
||||||
|
if len(c.sudo) != 0 {
|
||||||
|
req.Header.Set("Sudo", c.sudo)
|
||||||
|
}
|
||||||
|
if len(c.userAgent) != 0 {
|
||||||
|
req.Header.Set("User-Agent", c.userAgent)
|
||||||
|
}
|
||||||
|
|
||||||
|
client := c.client // client ref can change from this point on so safe it
|
||||||
|
c.mutex.RUnlock()
|
||||||
|
|
||||||
|
for k, v := range header {
|
||||||
|
req.Header[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.httpsigner != nil {
|
||||||
|
err = c.SignRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if debug {
|
||||||
|
fmt.Printf("Response: %v\n\n", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newResponse(resp), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts a response for a HTTP status code indicating an error condition
|
||||||
|
// (non-2XX) to a well-known error value and response body. For non-problematic
|
||||||
|
// (2XX) status codes nil will be returned. Note that on a non-2XX response, the
|
||||||
|
// response body stream will have been read and, hence, is closed on return.
|
||||||
|
func statusCodeToErr(resp *Response) (body []byte, err error) {
|
||||||
|
// no error
|
||||||
|
if resp.StatusCode/100 == 2 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// error: body will be read for details
|
||||||
|
//
|
||||||
|
defer resp.Body.Close()
|
||||||
|
data, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("body read on HTTP error %d: %v", resp.StatusCode, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to unmarshal and get an error message
|
||||||
|
errMap := make(map[string]interface{})
|
||||||
|
if err = json.Unmarshal(data, &errMap); err != nil {
|
||||||
|
// when the JSON can't be parsed, data was probably empty or a
|
||||||
|
// plain string, so we try to return a helpful error anyway
|
||||||
|
path := resp.Request.URL.Path
|
||||||
|
method := resp.Request.Method
|
||||||
|
header := resp.Request.Header
|
||||||
|
return data, fmt.Errorf("Unknown API Error: %d\nRequest: '%s' with '%s' method '%s' header and '%s' body", resp.StatusCode, path, method, header, string(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg, ok := errMap["message"]; ok {
|
||||||
|
return data, fmt.Errorf("%v", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no error message, at least give status and data
|
||||||
|
return data, fmt.Errorf("%s: %s", resp.Status, string(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getResponseReader(method, path string, header http.Header, body io.Reader) (io.ReadCloser, *Response, error) {
|
||||||
|
resp, err := c.doRequest(method, path, header, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for errors
|
||||||
|
data, err := statusCodeToErr(resp)
|
||||||
|
if err != nil {
|
||||||
|
return io.NopCloser(bytes.NewReader(data)), resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.Body, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getResponse(method, path string, header http.Header, body io.Reader) ([]byte, *Response, error) {
|
||||||
|
resp, err := c.doRequest(method, path, header, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// check for errors
|
||||||
|
data, err := statusCodeToErr(resp)
|
||||||
|
if err != nil {
|
||||||
|
return data, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// success (2XX), read body
|
||||||
|
data, err = io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getParsedResponse(method, path string, header http.Header, body io.Reader, obj interface{}) (*Response, error) {
|
||||||
|
data, resp, err := c.getResponse(method, path, header, body)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
return resp, json.Unmarshal(data, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getStatusCode(method, path string, header http.Header, body io.Reader) (int, *Response, error) {
|
||||||
|
resp, err := c.doRequest(method, path, header, body)
|
||||||
|
if err != nil {
|
||||||
|
return -1, resp, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
return resp.StatusCode, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// pathEscapeSegments escapes segments of a path while not escaping forward slash
|
||||||
|
func pathEscapeSegments(path string) string {
|
||||||
|
slice := strings.Split(path, "/")
|
||||||
|
for index := range slice {
|
||||||
|
slice[index] = url.PathEscape(slice[index])
|
||||||
|
}
|
||||||
|
escapedPath := strings.Join(slice, "/")
|
||||||
|
return escapedPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// escapeValidatePathSegments is a help function to validate and encode url path segments
|
||||||
|
func escapeValidatePathSegments(seg ...*string) error {
|
||||||
|
for i := range seg {
|
||||||
|
if seg[i] == nil || len(*seg[i]) == 0 {
|
||||||
|
return fmt.Errorf("path segment [%d] is empty", i)
|
||||||
|
}
|
||||||
|
*seg[i] = url.PathEscape(*seg[i])
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
9
vendor/code.gitea.io/sdk/gitea/doc.go
generated
vendored
Normal file
9
vendor/code.gitea.io/sdk/gitea/doc.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package gitea implements a client for the Gitea API.
|
||||||
|
// The version corresponds to the highest supported version
|
||||||
|
// of the gitea API, but backwards-compatibility is mostly
|
||||||
|
// given.
|
||||||
|
package gitea // import "code.gitea.io/sdk/gitea"
|
||||||
51
vendor/code.gitea.io/sdk/gitea/fork.go
generated
vendored
Normal file
51
vendor/code.gitea.io/sdk/gitea/fork.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListForksOptions options for listing repository's forks
|
||||||
|
type ListForksOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListForks list a repository's forks
|
||||||
|
func (c *Client) ListForks(user, repo string, opt ListForksOptions) ([]*Repository, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
forks := make([]*Repository, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/forks?%s", user, repo, opt.getURLQuery().Encode()),
|
||||||
|
nil, nil, &forks)
|
||||||
|
return forks, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateForkOption options for creating a fork
|
||||||
|
type CreateForkOption struct {
|
||||||
|
// organization name, if forking into an organization
|
||||||
|
Organization *string `json:"organization"`
|
||||||
|
// name of the forked repository
|
||||||
|
Name *string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateFork create a fork of a repository
|
||||||
|
func (c *Client) CreateFork(user, repo string, form CreateForkOption) (*Repository, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(form)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
fork := new(Repository)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/forks", user, repo), jsonHeader, bytes.NewReader(body), &fork)
|
||||||
|
return fork, resp, err
|
||||||
|
}
|
||||||
28
vendor/code.gitea.io/sdk/gitea/git_blob.go
generated
vendored
Normal file
28
vendor/code.gitea.io/sdk/gitea/git_blob.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GitBlobResponse represents a git blob
|
||||||
|
type GitBlobResponse struct {
|
||||||
|
Content string `json:"content"`
|
||||||
|
Encoding string `json:"encoding"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
SHA string `json:"sha"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlob get the blob of a repository file
|
||||||
|
func (c *Client) GetBlob(user, repo, sha string) (*GitBlobResponse, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &sha); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
blob := new(GitBlobResponse)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/blobs/%s", user, repo, sha), nil, nil, blob)
|
||||||
|
return blob, resp, err
|
||||||
|
}
|
||||||
71
vendor/code.gitea.io/sdk/gitea/git_hook.go
generated
vendored
Normal file
71
vendor/code.gitea.io/sdk/gitea/git_hook.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GitHook represents a Git repository hook
|
||||||
|
type GitHook struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
IsActive bool `json:"is_active"`
|
||||||
|
Content string `json:"content,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoGitHooksOptions options for listing repository's githooks
|
||||||
|
type ListRepoGitHooksOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoGitHooks list all the Git hooks of one repository
|
||||||
|
func (c *Client) ListRepoGitHooks(user, repo string, opt ListRepoGitHooksOptions) ([]*GitHook, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
hooks := make([]*GitHook, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/git?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &hooks)
|
||||||
|
return hooks, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRepoGitHook get a Git hook of a repository
|
||||||
|
func (c *Client) GetRepoGitHook(user, repo, id string) (*GitHook, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &id); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
h := new(GitHook)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/git/%s", user, repo, id), nil, nil, h)
|
||||||
|
return h, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditGitHookOption options when modifying one Git hook
|
||||||
|
type EditGitHookOption struct {
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditRepoGitHook modify one Git hook of a repository
|
||||||
|
func (c *Client) EditRepoGitHook(user, repo, id string, opt EditGitHookOption) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &id); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("PATCH", fmt.Sprintf("/repos/%s/%s/hooks/git/%s", user, repo, id), jsonHeader, bytes.NewReader(body))
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRepoGitHook delete one Git hook from a repository
|
||||||
|
func (c *Client) DeleteRepoGitHook(user, repo, id string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &id); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/hooks/git/%s", user, repo, id), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
20
vendor/code.gitea.io/sdk/gitea/helper.go
generated
vendored
Normal file
20
vendor/code.gitea.io/sdk/gitea/helper.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
// OptionalBool convert a bool to a bool reference
|
||||||
|
func OptionalBool(v bool) *bool {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionalString convert a string to a string reference
|
||||||
|
func OptionalString(v string) *string {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionalInt64 convert a int64 to a int64 reference
|
||||||
|
func OptionalInt64(v int64) *int64 {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
196
vendor/code.gitea.io/sdk/gitea/hook.go
generated
vendored
Normal file
196
vendor/code.gitea.io/sdk/gitea/hook.go
generated
vendored
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||||
|
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hook a hook is a web hook when one repository changed
|
||||||
|
type Hook struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
URL string `json:"-"`
|
||||||
|
Config map[string]string `json:"config"`
|
||||||
|
Events []string `json:"events"`
|
||||||
|
Active bool `json:"active"`
|
||||||
|
Updated time.Time `json:"updated_at"`
|
||||||
|
Created time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HookType represent all webhook types gitea currently offer
|
||||||
|
type HookType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// HookTypeDingtalk webhook that dingtalk understand
|
||||||
|
HookTypeDingtalk HookType = "dingtalk"
|
||||||
|
// HookTypeDiscord webhook that discord understand
|
||||||
|
HookTypeDiscord HookType = "discord"
|
||||||
|
// HookTypeGitea webhook that gitea understand
|
||||||
|
HookTypeGitea HookType = "gitea"
|
||||||
|
// HookTypeGogs webhook that gogs understand
|
||||||
|
HookTypeGogs HookType = "gogs"
|
||||||
|
// HookTypeMsteams webhook that msteams understand
|
||||||
|
HookTypeMsteams HookType = "msteams"
|
||||||
|
// HookTypeSlack webhook that slack understand
|
||||||
|
HookTypeSlack HookType = "slack"
|
||||||
|
// HookTypeTelegram webhook that telegram understand
|
||||||
|
HookTypeTelegram HookType = "telegram"
|
||||||
|
// HookTypeFeishu webhook that feishu understand
|
||||||
|
HookTypeFeishu HookType = "feishu"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListHooksOptions options for listing hooks
|
||||||
|
type ListHooksOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOrgHooks list all the hooks of one organization
|
||||||
|
func (c *Client) ListOrgHooks(org string, opt ListHooksOptions) ([]*Hook, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
hooks := make([]*Hook, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/hooks?%s", org, opt.getURLQuery().Encode()), nil, nil, &hooks)
|
||||||
|
return hooks, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoHooks list all the hooks of one repository
|
||||||
|
func (c *Client) ListRepoHooks(user, repo string, opt ListHooksOptions) ([]*Hook, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
hooks := make([]*Hook, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &hooks)
|
||||||
|
return hooks, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrgHook get a hook of an organization
|
||||||
|
func (c *Client) GetOrgHook(org string, id int64) (*Hook, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
h := new(Hook)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/hooks/%d", org, id), nil, nil, h)
|
||||||
|
return h, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRepoHook get a hook of a repository
|
||||||
|
func (c *Client) GetRepoHook(user, repo string, id int64) (*Hook, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
h := new(Hook)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), nil, nil, h)
|
||||||
|
return h, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateHookOption options when create a hook
|
||||||
|
type CreateHookOption struct {
|
||||||
|
Type HookType `json:"type"`
|
||||||
|
Config map[string]string `json:"config"`
|
||||||
|
Events []string `json:"events"`
|
||||||
|
BranchFilter string `json:"branch_filter"`
|
||||||
|
Active bool `json:"active"`
|
||||||
|
AuthorizationHeader string `json:"authorization_header"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the CreateHookOption struct
|
||||||
|
func (opt CreateHookOption) Validate() error {
|
||||||
|
if len(opt.Type) == 0 {
|
||||||
|
return fmt.Errorf("hook type needed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOrgHook create one hook for an organization, with options
|
||||||
|
func (c *Client) CreateOrgHook(org string, opt CreateHookOption) (*Hook, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
h := new(Hook)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/orgs/%s/hooks", org), jsonHeader, bytes.NewReader(body), h)
|
||||||
|
return h, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRepoHook create one hook for a repository, with options
|
||||||
|
func (c *Client) CreateRepoHook(user, repo string, opt CreateHookOption) (*Hook, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
h := new(Hook)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/hooks", user, repo), jsonHeader, bytes.NewReader(body), h)
|
||||||
|
return h, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditHookOption options when modify one hook
|
||||||
|
type EditHookOption struct {
|
||||||
|
Config map[string]string `json:"config"`
|
||||||
|
Events []string `json:"events"`
|
||||||
|
BranchFilter string `json:"branch_filter"`
|
||||||
|
Active *bool `json:"active"`
|
||||||
|
AuthorizationHeader string `json:"authorization_header"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditOrgHook modify one hook of an organization, with hook id and options
|
||||||
|
func (c *Client) EditOrgHook(org string, id int64, opt EditHookOption) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("PATCH", fmt.Sprintf("/orgs/%s/hooks/%d", org, id), jsonHeader, bytes.NewReader(body))
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditRepoHook modify one hook of a repository, with hook id and options
|
||||||
|
func (c *Client) EditRepoHook(user, repo string, id int64, opt EditHookOption) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("PATCH", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), jsonHeader, bytes.NewReader(body))
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteOrgHook delete one hook from an organization, with hook id
|
||||||
|
func (c *Client) DeleteOrgHook(org string, id int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s/hooks/%d", org, id), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRepoHook delete one hook from a repository, with hook id
|
||||||
|
func (c *Client) DeleteRepoHook(user, repo string, id int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
59
vendor/code.gitea.io/sdk/gitea/hook_validate.go
generated
vendored
Normal file
59
vendor/code.gitea.io/sdk/gitea/hook_validate.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VerifyWebhookSignature verifies that a payload matches the X-Gitea-Signature based on a secret
|
||||||
|
func VerifyWebhookSignature(secret, expected string, payload []byte) (bool, error) {
|
||||||
|
hash := hmac.New(sha256.New, []byte(secret))
|
||||||
|
if _, err := hash.Write(payload); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
expectedSum, err := hex.DecodeString(expected)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return hmac.Equal(hash.Sum(nil), expectedSum), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyWebhookSignatureMiddleware is a http.Handler for verifying X-Gitea-Signature on incoming webhooks
|
||||||
|
func VerifyWebhookSignatureMiddleware(secret string) func(http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
if _, err := io.Copy(&b, r.Body); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := r.Header.Get("X-Gitea-Signature")
|
||||||
|
if expected == "" {
|
||||||
|
http.Error(w, "no signature found", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := VerifyWebhookSignature(secret, expected, b.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
http.Error(w, "invalid payload", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Body = io.NopCloser(&b)
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
253
vendor/code.gitea.io/sdk/gitea/httpsign.go
generated
vendored
Normal file
253
vendor/code.gitea.io/sdk/gitea/httpsign.go
generated
vendored
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-fed/httpsig"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPSign contains the signer used for signing requests
|
||||||
|
type HTTPSign struct {
|
||||||
|
ssh.Signer
|
||||||
|
cert bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPSignConfig contains the configuration for creating a HTTPSign
|
||||||
|
type HTTPSignConfig struct {
|
||||||
|
fingerprint string
|
||||||
|
principal string
|
||||||
|
pubkey bool
|
||||||
|
cert bool
|
||||||
|
sshKey string
|
||||||
|
passphrase string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHTTPSignWithPubkey can be used to create a HTTPSign with a public key
|
||||||
|
// if no fingerprint is specified it returns the first public key found
|
||||||
|
func NewHTTPSignWithPubkey(fingerprint, sshKey, passphrase string) (*HTTPSign, error) {
|
||||||
|
return newHTTPSign(&HTTPSignConfig{
|
||||||
|
fingerprint: fingerprint,
|
||||||
|
pubkey: true,
|
||||||
|
sshKey: sshKey,
|
||||||
|
passphrase: passphrase,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHTTPSignWithCert can be used to create a HTTPSign with a certificate
|
||||||
|
// if no principal is specified it returns the first certificate found
|
||||||
|
func NewHTTPSignWithCert(principal, sshKey, passphrase string) (*HTTPSign, error) {
|
||||||
|
return newHTTPSign(&HTTPSignConfig{
|
||||||
|
principal: principal,
|
||||||
|
cert: true,
|
||||||
|
sshKey: sshKey,
|
||||||
|
passphrase: passphrase,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHTTPSign returns a new HTTPSign
|
||||||
|
// It will check the ssh-agent or a local file is config.sshKey is set.
|
||||||
|
// Depending on the configuration it will either use a certificate or a public key
|
||||||
|
func newHTTPSign(config *HTTPSignConfig) (*HTTPSign, error) {
|
||||||
|
var signer ssh.Signer
|
||||||
|
|
||||||
|
if config.sshKey != "" {
|
||||||
|
priv, err := os.ReadFile(config.sshKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.passphrase == "" {
|
||||||
|
signer, err = ssh.ParsePrivateKey(priv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
signer, err = ssh.ParsePrivateKeyWithPassphrase(priv, []byte(config.passphrase))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.cert {
|
||||||
|
certbytes, err := os.ReadFile(config.sshKey + "-cert.pub")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pub, _, _, _, err := ssh.ParseAuthorizedKey(certbytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, ok := pub.(*ssh.Certificate)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("failed to parse certificate")
|
||||||
|
}
|
||||||
|
|
||||||
|
signer, err = ssh.NewCertSigner(cert, signer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if no sshKey is specified, check if we have a ssh-agent and use it
|
||||||
|
agent, err := GetAgent()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
signers, err := agent.Signers()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(signers) == 0 {
|
||||||
|
return nil, fmt.Errorf("no signers found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.cert {
|
||||||
|
signer = findCertSigner(signers, config.principal)
|
||||||
|
if signer == nil {
|
||||||
|
return nil, fmt.Errorf("no certificate found for %s", config.principal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.pubkey {
|
||||||
|
signer = findPubkeySigner(signers, config.fingerprint)
|
||||||
|
if signer == nil {
|
||||||
|
return nil, fmt.Errorf("no public key found for %s", config.fingerprint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &HTTPSign{
|
||||||
|
Signer: signer,
|
||||||
|
cert: config.cert,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignRequest signs a HTTP request
|
||||||
|
func (c *Client) SignRequest(r *http.Request) error {
|
||||||
|
var contents []byte
|
||||||
|
|
||||||
|
headersToSign := []string{httpsig.RequestTarget, "(created)", "(expires)"}
|
||||||
|
|
||||||
|
if c.httpsigner.cert {
|
||||||
|
// add our certificate to the headers to sign
|
||||||
|
pubkey, _ := ssh.ParsePublicKey(c.httpsigner.Signer.PublicKey().Marshal())
|
||||||
|
if cert, ok := pubkey.(*ssh.Certificate); ok {
|
||||||
|
certString := base64.RawStdEncoding.EncodeToString(cert.Marshal())
|
||||||
|
r.Header.Add("x-ssh-certificate", certString)
|
||||||
|
|
||||||
|
headersToSign = append(headersToSign, "x-ssh-certificate")
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("no ssh certificate found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have a body, the Digest header will be added and we'll include this also in
|
||||||
|
// our signature.
|
||||||
|
if r.Body != nil {
|
||||||
|
body, err := r.GetBody()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("getBody() failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
contents, err = io.ReadAll(body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed reading body: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
headersToSign = append(headersToSign, "Digest")
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a signer for the request and headers, the signature will be valid for 10 seconds
|
||||||
|
signer, _, err := httpsig.NewSSHSigner(c.httpsigner.Signer, httpsig.DigestSha512, headersToSign, httpsig.Signature, 10)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("httpsig.NewSSHSigner failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sign the request, use the fingerprint if we don't have a certificate
|
||||||
|
keyID := "gitea"
|
||||||
|
if !c.httpsigner.cert {
|
||||||
|
keyID = ssh.FingerprintSHA256(c.httpsigner.Signer.PublicKey())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = signer.SignRequest(keyID, r, contents)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("httpsig.Signrequest failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// findCertSigner returns the Signer containing a valid certificate
|
||||||
|
// if no principal is specified it returns the first certificate found
|
||||||
|
func findCertSigner(sshsigners []ssh.Signer, principal string) ssh.Signer {
|
||||||
|
for _, s := range sshsigners {
|
||||||
|
// Check if the key is a certificate
|
||||||
|
if !strings.Contains(s.PublicKey().Type(), "cert-v01@openssh.com") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert the ssh.Signer to a ssh.Certificate
|
||||||
|
mpubkey, _ := ssh.ParsePublicKey(s.PublicKey().Marshal())
|
||||||
|
cryptopub := mpubkey.(crypto.PublicKey)
|
||||||
|
cert := cryptopub.(*ssh.Certificate)
|
||||||
|
t := time.Unix(int64(cert.ValidBefore), 0)
|
||||||
|
|
||||||
|
// make sure the certificate is at least 10 seconds valid
|
||||||
|
if time.Until(t) <= time.Second*10 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if principal == "" {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range cert.ValidPrincipals {
|
||||||
|
if p == principal {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// findPubkeySigner returns the Signer containing a valid public key
|
||||||
|
// if no fingerprint is specified it returns the first public key found
|
||||||
|
func findPubkeySigner(sshsigners []ssh.Signer, fingerprint string) ssh.Signer {
|
||||||
|
for _, s := range sshsigners {
|
||||||
|
// Check if the key is a certificate
|
||||||
|
if strings.Contains(s.PublicKey().Type(), "cert-v01@openssh.com") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if fingerprint == "" {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.TrimSpace(string(ssh.MarshalAuthorizedKey(s.PublicKey()))) == fingerprint {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
if ssh.FingerprintSHA256(s.PublicKey()) == fingerprint {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
309
vendor/code.gitea.io/sdk/gitea/issue.go
generated
vendored
Normal file
309
vendor/code.gitea.io/sdk/gitea/issue.go
generated
vendored
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PullRequestMeta PR info if an issue is a PR
|
||||||
|
type PullRequestMeta struct {
|
||||||
|
HasMerged bool `json:"merged"`
|
||||||
|
Merged *time.Time `json:"merged_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepositoryMeta basic repository information
|
||||||
|
type RepositoryMeta struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Owner string `json:"owner"`
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue represents an issue in a repository
|
||||||
|
type Issue struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
HTMLURL string `json:"html_url"`
|
||||||
|
Index int64 `json:"number"`
|
||||||
|
Poster *User `json:"user"`
|
||||||
|
OriginalAuthor string `json:"original_author"`
|
||||||
|
OriginalAuthorID int64 `json:"original_author_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
Ref string `json:"ref"`
|
||||||
|
Labels []*Label `json:"labels"`
|
||||||
|
Milestone *Milestone `json:"milestone"`
|
||||||
|
Assignees []*User `json:"assignees"`
|
||||||
|
// Whether the issue is open or closed
|
||||||
|
State StateType `json:"state"`
|
||||||
|
IsLocked bool `json:"is_locked"`
|
||||||
|
Comments int `json:"comments"`
|
||||||
|
Created time.Time `json:"created_at"`
|
||||||
|
Updated time.Time `json:"updated_at"`
|
||||||
|
Closed *time.Time `json:"closed_at"`
|
||||||
|
Deadline *time.Time `json:"due_date"`
|
||||||
|
PullRequest *PullRequestMeta `json:"pull_request"`
|
||||||
|
Repository *RepositoryMeta `json:"repository"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListIssueOption list issue options
|
||||||
|
type ListIssueOption struct {
|
||||||
|
ListOptions
|
||||||
|
State StateType
|
||||||
|
Type IssueType
|
||||||
|
Labels []string
|
||||||
|
Milestones []string
|
||||||
|
KeyWord string
|
||||||
|
Since time.Time
|
||||||
|
Before time.Time
|
||||||
|
// filter by created by username
|
||||||
|
CreatedBy string
|
||||||
|
// filter by assigned to username
|
||||||
|
AssignedBy string
|
||||||
|
// filter by username mentioned
|
||||||
|
MentionedBy string
|
||||||
|
// filter by owner (only works on ListIssues on User)
|
||||||
|
Owner string
|
||||||
|
// filter by team (requires organization owner parameter to be provided and only works on ListIssues on User)
|
||||||
|
Team string
|
||||||
|
}
|
||||||
|
|
||||||
|
// StateType issue state type
|
||||||
|
type StateType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// StateOpen pr/issue is opend
|
||||||
|
StateOpen StateType = "open"
|
||||||
|
// StateClosed pr/issue is closed
|
||||||
|
StateClosed StateType = "closed"
|
||||||
|
// StateAll is all
|
||||||
|
StateAll StateType = "all"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IssueType is issue a pull or only an issue
|
||||||
|
type IssueType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// IssueTypeAll pr and issue
|
||||||
|
IssueTypeAll IssueType = ""
|
||||||
|
// IssueTypeIssue only issues
|
||||||
|
IssueTypeIssue IssueType = "issues"
|
||||||
|
// IssueTypePull only pulls
|
||||||
|
IssueTypePull IssueType = "pulls"
|
||||||
|
)
|
||||||
|
|
||||||
|
// QueryEncode turns options into querystring argument
|
||||||
|
func (opt *ListIssueOption) QueryEncode() string {
|
||||||
|
query := opt.getURLQuery()
|
||||||
|
|
||||||
|
if len(opt.State) > 0 {
|
||||||
|
query.Add("state", string(opt.State))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opt.Labels) > 0 {
|
||||||
|
query.Add("labels", strings.Join(opt.Labels, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opt.KeyWord) > 0 {
|
||||||
|
query.Add("q", opt.KeyWord)
|
||||||
|
}
|
||||||
|
|
||||||
|
query.Add("type", string(opt.Type))
|
||||||
|
|
||||||
|
if len(opt.Milestones) > 0 {
|
||||||
|
query.Add("milestones", strings.Join(opt.Milestones, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !opt.Since.IsZero() {
|
||||||
|
query.Add("since", opt.Since.Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
if !opt.Before.IsZero() {
|
||||||
|
query.Add("before", opt.Before.Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opt.CreatedBy) > 0 {
|
||||||
|
query.Add("created_by", opt.CreatedBy)
|
||||||
|
}
|
||||||
|
if len(opt.AssignedBy) > 0 {
|
||||||
|
query.Add("assigned_by", opt.AssignedBy)
|
||||||
|
}
|
||||||
|
if len(opt.MentionedBy) > 0 {
|
||||||
|
query.Add("mentioned_by", opt.MentionedBy)
|
||||||
|
}
|
||||||
|
if len(opt.Owner) > 0 {
|
||||||
|
query.Add("owner", opt.Owner)
|
||||||
|
}
|
||||||
|
if len(opt.Team) > 0 {
|
||||||
|
query.Add("team", opt.MentionedBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListIssues returns all issues assigned the authenticated user
|
||||||
|
func (c *Client) ListIssues(opt ListIssueOption) ([]*Issue, *Response, error) {
|
||||||
|
opt.setDefaults()
|
||||||
|
issues := make([]*Issue, 0, opt.PageSize)
|
||||||
|
|
||||||
|
link, _ := url.Parse("/repos/issues/search")
|
||||||
|
link.RawQuery = opt.QueryEncode()
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &issues)
|
||||||
|
if e := c.checkServerVersionGreaterThanOrEqual(version1_12_0); e != nil {
|
||||||
|
for i := 0; i < len(issues); i++ {
|
||||||
|
if issues[i].Repository != nil {
|
||||||
|
issues[i].Repository.Owner = strings.Split(issues[i].Repository.FullName, "/")[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range issues {
|
||||||
|
c.issueBackwardsCompatibility(issues[i])
|
||||||
|
}
|
||||||
|
return issues, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoIssues returns all issues for a given repository
|
||||||
|
func (c *Client) ListRepoIssues(owner, repo string, opt ListIssueOption) ([]*Issue, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
issues := make([]*Issue, 0, opt.PageSize)
|
||||||
|
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues", owner, repo))
|
||||||
|
link.RawQuery = opt.QueryEncode()
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &issues)
|
||||||
|
if e := c.checkServerVersionGreaterThanOrEqual(version1_12_0); e != nil {
|
||||||
|
for i := 0; i < len(issues); i++ {
|
||||||
|
if issues[i].Repository != nil {
|
||||||
|
issues[i].Repository.Owner = strings.Split(issues[i].Repository.FullName, "/")[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range issues {
|
||||||
|
c.issueBackwardsCompatibility(issues[i])
|
||||||
|
}
|
||||||
|
return issues, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIssue returns a single issue for a given repository
|
||||||
|
func (c *Client) GetIssue(owner, repo string, index int64) (*Issue, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
issue := new(Issue)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index), nil, nil, issue)
|
||||||
|
if e := c.checkServerVersionGreaterThanOrEqual(version1_12_0); e != nil && issue.Repository != nil {
|
||||||
|
issue.Repository.Owner = strings.Split(issue.Repository.FullName, "/")[0]
|
||||||
|
}
|
||||||
|
c.issueBackwardsCompatibility(issue)
|
||||||
|
return issue, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateIssueOption options to create one issue
|
||||||
|
type CreateIssueOption struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
Ref string `json:"ref"`
|
||||||
|
Assignees []string `json:"assignees"`
|
||||||
|
Deadline *time.Time `json:"due_date"`
|
||||||
|
// milestone id
|
||||||
|
Milestone int64 `json:"milestone"`
|
||||||
|
// list of label ids
|
||||||
|
Labels []int64 `json:"labels"`
|
||||||
|
Closed bool `json:"closed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the CreateIssueOption struct
|
||||||
|
func (opt CreateIssueOption) Validate() error {
|
||||||
|
if len(strings.TrimSpace(opt.Title)) == 0 {
|
||||||
|
return fmt.Errorf("title is empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateIssue create a new issue for a given repository
|
||||||
|
func (c *Client) CreateIssue(owner, repo string, opt CreateIssueOption) (*Issue, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
issue := new(Issue)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/issues", owner, repo),
|
||||||
|
jsonHeader, bytes.NewReader(body), issue)
|
||||||
|
c.issueBackwardsCompatibility(issue)
|
||||||
|
return issue, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditIssueOption options for editing an issue
|
||||||
|
type EditIssueOption struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Body *string `json:"body"`
|
||||||
|
Ref *string `json:"ref"`
|
||||||
|
Assignees []string `json:"assignees"`
|
||||||
|
Milestone *int64 `json:"milestone"`
|
||||||
|
State *StateType `json:"state"`
|
||||||
|
Deadline *time.Time `json:"due_date"`
|
||||||
|
RemoveDeadline *bool `json:"unset_due_date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the EditIssueOption struct
|
||||||
|
func (opt EditIssueOption) Validate() error {
|
||||||
|
if len(opt.Title) != 0 && len(strings.TrimSpace(opt.Title)) == 0 {
|
||||||
|
return fmt.Errorf("title is empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditIssue modify an existing issue for a given repository
|
||||||
|
func (c *Client) EditIssue(owner, repo string, index int64, opt EditIssueOption) (*Issue, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
issue := new(Issue)
|
||||||
|
resp, err := c.getParsedResponse("PATCH",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index),
|
||||||
|
jsonHeader, bytes.NewReader(body), issue)
|
||||||
|
c.issueBackwardsCompatibility(issue)
|
||||||
|
return issue, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteIssue delete a issue from a repository
|
||||||
|
func (c *Client) DeleteIssue(user, repo string, id int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/issues/%d", user, repo, id),
|
||||||
|
nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) issueBackwardsCompatibility(issue *Issue) {
|
||||||
|
if c.checkServerVersionGreaterThanOrEqual(version1_12_0) != nil {
|
||||||
|
c.mutex.RLock()
|
||||||
|
issue.HTMLURL = fmt.Sprintf("%s/%s/issues/%d", c.url, issue.Repository.FullName, issue.Index)
|
||||||
|
c.mutex.RUnlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
154
vendor/code.gitea.io/sdk/gitea/issue_comment.go
generated
vendored
Normal file
154
vendor/code.gitea.io/sdk/gitea/issue_comment.go
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Comment represents a comment on a commit or issue
|
||||||
|
type Comment struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
HTMLURL string `json:"html_url"`
|
||||||
|
PRURL string `json:"pull_request_url"`
|
||||||
|
IssueURL string `json:"issue_url"`
|
||||||
|
Poster *User `json:"user"`
|
||||||
|
OriginalAuthor string `json:"original_author"`
|
||||||
|
OriginalAuthorID int64 `json:"original_author_id"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
Created time.Time `json:"created_at"`
|
||||||
|
Updated time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListIssueCommentOptions list comment options
|
||||||
|
type ListIssueCommentOptions struct {
|
||||||
|
ListOptions
|
||||||
|
Since time.Time
|
||||||
|
Before time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryEncode turns options into querystring argument
|
||||||
|
func (opt *ListIssueCommentOptions) QueryEncode() string {
|
||||||
|
query := opt.getURLQuery()
|
||||||
|
if !opt.Since.IsZero() {
|
||||||
|
query.Add("since", opt.Since.Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
if !opt.Before.IsZero() {
|
||||||
|
query.Add("before", opt.Before.Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
return query.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListIssueComments list comments on an issue.
|
||||||
|
func (c *Client) ListIssueComments(owner, repo string, index int64, opt ListIssueCommentOptions) ([]*Comment, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/%d/comments", owner, repo, index))
|
||||||
|
link.RawQuery = opt.QueryEncode()
|
||||||
|
comments := make([]*Comment, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &comments)
|
||||||
|
return comments, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoIssueComments list comments for a given repo.
|
||||||
|
func (c *Client) ListRepoIssueComments(owner, repo string, opt ListIssueCommentOptions) ([]*Comment, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/comments", owner, repo))
|
||||||
|
link.RawQuery = opt.QueryEncode()
|
||||||
|
comments := make([]*Comment, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &comments)
|
||||||
|
return comments, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIssueComment get a comment for a given repo by id.
|
||||||
|
func (c *Client) GetIssueComment(owner, repo string, id int64) (*Comment, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
comment := new(Comment)
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return comment, nil, err
|
||||||
|
}
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/comments/%d", owner, repo, id), nil, nil, &comment)
|
||||||
|
return comment, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateIssueCommentOption options for creating a comment on an issue
|
||||||
|
type CreateIssueCommentOption struct {
|
||||||
|
Body string `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the CreateIssueCommentOption struct
|
||||||
|
func (opt CreateIssueCommentOption) Validate() error {
|
||||||
|
if len(opt.Body) == 0 {
|
||||||
|
return fmt.Errorf("body is empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateIssueComment create comment on an issue.
|
||||||
|
func (c *Client) CreateIssueComment(owner, repo string, index int64, opt CreateIssueCommentOption) (*Comment, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
comment := new(Comment)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/comments", owner, repo, index), jsonHeader, bytes.NewReader(body), comment)
|
||||||
|
return comment, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditIssueCommentOption options for editing a comment
|
||||||
|
type EditIssueCommentOption struct {
|
||||||
|
Body string `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the EditIssueCommentOption struct
|
||||||
|
func (opt EditIssueCommentOption) Validate() error {
|
||||||
|
if len(opt.Body) == 0 {
|
||||||
|
return fmt.Errorf("body is empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditIssueComment edits an issue comment.
|
||||||
|
func (c *Client) EditIssueComment(owner, repo string, commentID int64, opt EditIssueCommentOption) (*Comment, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
comment := new(Comment)
|
||||||
|
resp, err := c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/issues/comments/%d", owner, repo, commentID), jsonHeader, bytes.NewReader(body), comment)
|
||||||
|
return comment, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteIssueComment deletes an issue comment.
|
||||||
|
func (c *Client) DeleteIssueComment(owner, repo string, commentID int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/comments/%d", owner, repo, commentID), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
211
vendor/code.gitea.io/sdk/gitea/issue_label.go
generated
vendored
Normal file
211
vendor/code.gitea.io/sdk/gitea/issue_label.go
generated
vendored
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Label a label to an issue or a pr
|
||||||
|
type Label struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
// example: 00aabb
|
||||||
|
Color string `json:"color"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListLabelsOptions options for listing repository's labels
|
||||||
|
type ListLabelsOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoLabels list labels of one repository
|
||||||
|
func (c *Client) ListRepoLabels(owner, repo string, opt ListLabelsOptions) ([]*Label, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
labels := make([]*Label, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/labels?%s", owner, repo, opt.getURLQuery().Encode()), nil, nil, &labels)
|
||||||
|
return labels, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRepoLabel get one label of repository by repo it
|
||||||
|
func (c *Client) GetRepoLabel(owner, repo string, id int64) (*Label, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
label := new(Label)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/labels/%d", owner, repo, id), nil, nil, label)
|
||||||
|
return label, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateLabelOption options for creating a label
|
||||||
|
type CreateLabelOption struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
// example: #00aabb
|
||||||
|
Color string `json:"color"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the CreateLabelOption struct
|
||||||
|
func (opt CreateLabelOption) Validate() error {
|
||||||
|
aw, err := regexp.MatchString("^#?[0-9,a-f,A-F]{6}$", opt.Color)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !aw {
|
||||||
|
return fmt.Errorf("invalid color format")
|
||||||
|
}
|
||||||
|
if len(strings.TrimSpace(opt.Name)) == 0 {
|
||||||
|
return fmt.Errorf("empty name not allowed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateLabel create one label of repository
|
||||||
|
func (c *Client) CreateLabel(owner, repo string, opt CreateLabelOption) (*Label, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if len(opt.Color) == 6 {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
opt.Color = "#" + opt.Color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
label := new(Label)
|
||||||
|
resp, err := c.getParsedResponse("POST",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/labels", owner, repo),
|
||||||
|
jsonHeader, bytes.NewReader(body), label)
|
||||||
|
return label, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditLabelOption options for editing a label
|
||||||
|
type EditLabelOption struct {
|
||||||
|
Name *string `json:"name"`
|
||||||
|
Color *string `json:"color"`
|
||||||
|
Description *string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the EditLabelOption struct
|
||||||
|
func (opt EditLabelOption) Validate() error {
|
||||||
|
if opt.Color != nil {
|
||||||
|
aw, err := regexp.MatchString("^#?[0-9,a-f,A-F]{6}$", *opt.Color)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !aw {
|
||||||
|
return fmt.Errorf("invalid color format")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opt.Name != nil {
|
||||||
|
if len(strings.TrimSpace(*opt.Name)) == 0 {
|
||||||
|
return fmt.Errorf("empty name not allowed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditLabel modify one label with options
|
||||||
|
func (c *Client) EditLabel(owner, repo string, id int64, opt EditLabelOption) (*Label, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
label := new(Label)
|
||||||
|
resp, err := c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/labels/%d", owner, repo, id), jsonHeader, bytes.NewReader(body), label)
|
||||||
|
return label, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteLabel delete one label of repository by id
|
||||||
|
func (c *Client) DeleteLabel(owner, repo string, id int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/labels/%d", owner, repo, id), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIssueLabels get labels of one issue via issue id
|
||||||
|
func (c *Client) GetIssueLabels(owner, repo string, index int64, opts ListLabelsOptions) ([]*Label, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
labels := make([]*Label, 0, 5)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/labels?%s", owner, repo, index, opts.getURLQuery().Encode()), nil, nil, &labels)
|
||||||
|
return labels, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IssueLabelsOption a collection of labels
|
||||||
|
type IssueLabelsOption struct {
|
||||||
|
// list of label IDs
|
||||||
|
Labels []int64 `json:"labels"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddIssueLabels add one or more labels to one issue
|
||||||
|
func (c *Client) AddIssueLabels(owner, repo string, index int64, opt IssueLabelsOption) ([]*Label, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
var labels []*Label
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/labels", owner, repo, index), jsonHeader, bytes.NewReader(body), &labels)
|
||||||
|
return labels, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceIssueLabels replace old labels of issue with new labels
|
||||||
|
func (c *Client) ReplaceIssueLabels(owner, repo string, index int64, opt IssueLabelsOption) ([]*Label, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
var labels []*Label
|
||||||
|
resp, err := c.getParsedResponse("PUT", fmt.Sprintf("/repos/%s/%s/issues/%d/labels", owner, repo, index), jsonHeader, bytes.NewReader(body), &labels)
|
||||||
|
return labels, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteIssueLabel delete one label of one issue by issue id and label id
|
||||||
|
// TODO: maybe we need delete by label name and issue id
|
||||||
|
func (c *Client) DeleteIssueLabel(owner, repo string, index, label int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/labels/%d", owner, repo, index, label), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearIssueLabels delete all the labels of one issue.
|
||||||
|
func (c *Client) ClearIssueLabels(owner, repo string, index int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/labels", owner, repo, index), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
237
vendor/code.gitea.io/sdk/gitea/issue_milestone.go
generated
vendored
Normal file
237
vendor/code.gitea.io/sdk/gitea/issue_milestone.go
generated
vendored
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Milestone milestone is a collection of issues on one repository
|
||||||
|
type Milestone struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
State StateType `json:"state"`
|
||||||
|
OpenIssues int `json:"open_issues"`
|
||||||
|
ClosedIssues int `json:"closed_issues"`
|
||||||
|
Created time.Time `json:"created_at"`
|
||||||
|
Updated *time.Time `json:"updated_at"`
|
||||||
|
Closed *time.Time `json:"closed_at"`
|
||||||
|
Deadline *time.Time `json:"due_on"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMilestoneOption list milestone options
|
||||||
|
type ListMilestoneOption struct {
|
||||||
|
ListOptions
|
||||||
|
// open, closed, all
|
||||||
|
State StateType
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryEncode turns options into querystring argument
|
||||||
|
func (opt *ListMilestoneOption) QueryEncode() string {
|
||||||
|
query := opt.getURLQuery()
|
||||||
|
if opt.State != "" {
|
||||||
|
query.Add("state", string(opt.State))
|
||||||
|
}
|
||||||
|
if len(opt.Name) != 0 {
|
||||||
|
query.Add("name", opt.Name)
|
||||||
|
}
|
||||||
|
return query.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoMilestones list all the milestones of one repository
|
||||||
|
func (c *Client) ListRepoMilestones(owner, repo string, opt ListMilestoneOption) ([]*Milestone, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
milestones := make([]*Milestone, 0, opt.PageSize)
|
||||||
|
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/milestones", owner, repo))
|
||||||
|
link.RawQuery = opt.QueryEncode()
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &milestones)
|
||||||
|
return milestones, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMilestone get one milestone by repo name and milestone id
|
||||||
|
func (c *Client) GetMilestone(owner, repo string, id int64) (*Milestone, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
milestone := new(Milestone)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/milestones/%d", owner, repo, id), nil, nil, milestone)
|
||||||
|
return milestone, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMilestoneByName get one milestone by repo and milestone name
|
||||||
|
func (c *Client) GetMilestoneByName(owner, repo, name string) (*Milestone, *Response, error) {
|
||||||
|
if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil {
|
||||||
|
// backwards compatibility mode
|
||||||
|
m, resp, err := c.resolveMilestoneByName(owner, repo, name)
|
||||||
|
return m, resp, err
|
||||||
|
}
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
milestone := new(Milestone)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/milestones/%s", owner, repo, name), nil, nil, milestone)
|
||||||
|
return milestone, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateMilestoneOption options for creating a milestone
|
||||||
|
type CreateMilestoneOption struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
State StateType `json:"state"`
|
||||||
|
Deadline *time.Time `json:"due_on"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the CreateMilestoneOption struct
|
||||||
|
func (opt CreateMilestoneOption) Validate() error {
|
||||||
|
if len(strings.TrimSpace(opt.Title)) == 0 {
|
||||||
|
return fmt.Errorf("title is empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateMilestone create one milestone with options
|
||||||
|
func (c *Client) CreateMilestone(owner, repo string, opt CreateMilestoneOption) (*Milestone, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
milestone := new(Milestone)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/milestones", owner, repo), jsonHeader, bytes.NewReader(body), milestone)
|
||||||
|
|
||||||
|
// make creating closed milestones need gitea >= v1.13.0
|
||||||
|
// this make it backwards compatible
|
||||||
|
if err == nil && opt.State == StateClosed && milestone.State != StateClosed {
|
||||||
|
closed := StateClosed
|
||||||
|
return c.EditMilestone(owner, repo, milestone.ID, EditMilestoneOption{
|
||||||
|
State: &closed,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return milestone, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditMilestoneOption options for editing a milestone
|
||||||
|
type EditMilestoneOption struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Description *string `json:"description"`
|
||||||
|
State *StateType `json:"state"`
|
||||||
|
Deadline *time.Time `json:"due_on"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the EditMilestoneOption struct
|
||||||
|
func (opt EditMilestoneOption) Validate() error {
|
||||||
|
if len(opt.Title) != 0 && len(strings.TrimSpace(opt.Title)) == 0 {
|
||||||
|
return fmt.Errorf("title is empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditMilestone modify milestone with options
|
||||||
|
func (c *Client) EditMilestone(owner, repo string, id int64, opt EditMilestoneOption) (*Milestone, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
milestone := new(Milestone)
|
||||||
|
resp, err := c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/milestones/%d", owner, repo, id), jsonHeader, bytes.NewReader(body), milestone)
|
||||||
|
return milestone, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditMilestoneByName modify milestone with options
|
||||||
|
func (c *Client) EditMilestoneByName(owner, repo, name string, opt EditMilestoneOption) (*Milestone, *Response, error) {
|
||||||
|
if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil {
|
||||||
|
// backwards compatibility mode
|
||||||
|
m, _, err := c.resolveMilestoneByName(owner, repo, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return c.EditMilestone(owner, repo, m.ID, opt)
|
||||||
|
}
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
milestone := new(Milestone)
|
||||||
|
resp, err := c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/milestones/%s", owner, repo, name), jsonHeader, bytes.NewReader(body), milestone)
|
||||||
|
return milestone, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMilestone delete one milestone by id
|
||||||
|
func (c *Client) DeleteMilestone(owner, repo string, id int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/milestones/%d", owner, repo, id), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMilestoneByName delete one milestone by name
|
||||||
|
func (c *Client) DeleteMilestoneByName(owner, repo, name string) (*Response, error) {
|
||||||
|
if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil {
|
||||||
|
// backwards compatibility mode
|
||||||
|
m, _, err := c.resolveMilestoneByName(owner, repo, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.DeleteMilestone(owner, repo, m.ID)
|
||||||
|
}
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/milestones/%s", owner, repo, name), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveMilestoneByName is a fallback method to find milestone id by name
|
||||||
|
func (c *Client) resolveMilestoneByName(owner, repo, name string) (*Milestone, *Response, error) {
|
||||||
|
for i := 1; ; i++ {
|
||||||
|
miles, resp, err := c.ListRepoMilestones(owner, repo, ListMilestoneOption{
|
||||||
|
ListOptions: ListOptions{
|
||||||
|
Page: i,
|
||||||
|
},
|
||||||
|
State: "all",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if len(miles) == 0 {
|
||||||
|
return nil, nil, fmt.Errorf("milestone '%s' do not exist", name)
|
||||||
|
}
|
||||||
|
for _, m := range miles {
|
||||||
|
if strings.EqualFold(strings.TrimSpace(m.Title), strings.TrimSpace(name)) {
|
||||||
|
return m, resp, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
104
vendor/code.gitea.io/sdk/gitea/issue_reaction.go
generated
vendored
Normal file
104
vendor/code.gitea.io/sdk/gitea/issue_reaction.go
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reaction contain one reaction
|
||||||
|
type Reaction struct {
|
||||||
|
User *User `json:"user"`
|
||||||
|
Reaction string `json:"content"`
|
||||||
|
Created time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIssueReactions get a list reactions of an issue
|
||||||
|
func (c *Client) GetIssueReactions(owner, repo string, index int64) ([]*Reaction, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
reactions := make([]*Reaction, 0, 10)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/reactions", owner, repo, index), nil, nil, &reactions)
|
||||||
|
return reactions, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIssueCommentReactions get a list of reactions from a comment of an issue
|
||||||
|
func (c *Client) GetIssueCommentReactions(owner, repo string, commentID int64) ([]*Reaction, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
reactions := make([]*Reaction, 0, 10)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/comments/%d/reactions", owner, repo, commentID), nil, nil, &reactions)
|
||||||
|
return reactions, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// editReactionOption contain the reaction type
|
||||||
|
type editReactionOption struct {
|
||||||
|
Reaction string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostIssueReaction add a reaction to an issue
|
||||||
|
func (c *Client) PostIssueReaction(owner, repo string, index int64, reaction string) (*Reaction, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
reactionResponse := new(Reaction)
|
||||||
|
body, err := json.Marshal(&editReactionOption{Reaction: reaction})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
resp, err := c.getParsedResponse("POST",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/issues/%d/reactions", owner, repo, index),
|
||||||
|
jsonHeader, bytes.NewReader(body), reactionResponse)
|
||||||
|
return reactionResponse, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteIssueReaction remove a reaction from an issue
|
||||||
|
func (c *Client) DeleteIssueReaction(owner, repo string, index int64, reaction string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&editReactionOption{Reaction: reaction})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/reactions", owner, repo, index), jsonHeader, bytes.NewReader(body))
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostIssueCommentReaction add a reaction to a comment of an issue
|
||||||
|
func (c *Client) PostIssueCommentReaction(owner, repo string, commentID int64, reaction string) (*Reaction, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
reactionResponse := new(Reaction)
|
||||||
|
body, err := json.Marshal(&editReactionOption{Reaction: reaction})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
resp, err := c.getParsedResponse("POST",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/issues/comments/%d/reactions", owner, repo, commentID),
|
||||||
|
jsonHeader, bytes.NewReader(body), reactionResponse)
|
||||||
|
return reactionResponse, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteIssueCommentReaction remove a reaction from a comment of an issue
|
||||||
|
func (c *Client) DeleteIssueCommentReaction(owner, repo string, commentID int64, reaction string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&editReactionOption{Reaction: reaction})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/issues/comments/%d/reactions", owner, repo, commentID),
|
||||||
|
jsonHeader, bytes.NewReader(body))
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
57
vendor/code.gitea.io/sdk/gitea/issue_stopwatch.go
generated
vendored
Normal file
57
vendor/code.gitea.io/sdk/gitea/issue_stopwatch.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StopWatch represents a running stopwatch of an issue / pr
|
||||||
|
type StopWatch struct {
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
Seconds int64 `json:"seconds"`
|
||||||
|
Duration string `json:"duration"`
|
||||||
|
IssueIndex int64 `json:"issue_index"`
|
||||||
|
IssueTitle string `json:"issue_title"`
|
||||||
|
RepoOwnerName string `json:"repo_owner_name"`
|
||||||
|
RepoName string `json:"repo_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMyStopwatches list all stopwatches
|
||||||
|
func (c *Client) GetMyStopwatches() ([]*StopWatch, *Response, error) {
|
||||||
|
stopwatches := make([]*StopWatch, 0, 1)
|
||||||
|
resp, err := c.getParsedResponse("GET", "/user/stopwatches", nil, nil, &stopwatches)
|
||||||
|
return stopwatches, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteIssueStopwatch delete / cancel a specific stopwatch
|
||||||
|
func (c *Client) DeleteIssueStopwatch(owner, repo string, index int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/delete", owner, repo, index), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartIssueStopWatch starts a stopwatch for an existing issue for a given
|
||||||
|
// repository
|
||||||
|
func (c *Client) StartIssueStopWatch(owner, repo string, index int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/start", owner, repo, index), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopIssueStopWatch stops an existing stopwatch for an issue in a given
|
||||||
|
// repository
|
||||||
|
func (c *Client) StopIssueStopWatch(owner, repo string, index int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/stop", owner, repo, index), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
87
vendor/code.gitea.io/sdk/gitea/issue_subscription.go
generated
vendored
Normal file
87
vendor/code.gitea.io/sdk/gitea/issue_subscription.go
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetIssueSubscribers get list of users who subscribed on an issue
|
||||||
|
func (c *Client) GetIssueSubscribers(owner, repo string, index int64) ([]*User, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
subscribers := make([]*User, 0, 10)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions", owner, repo, index), nil, nil, &subscribers)
|
||||||
|
return subscribers, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddIssueSubscription Subscribe user to issue
|
||||||
|
func (c *Client) AddIssueSubscription(owner, repo string, index int64, user string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo, &user); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
status, resp, err := c.getStatusCode("PUT", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions/%s", owner, repo, index, user), nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
if status == http.StatusCreated {
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
if status == http.StatusOK {
|
||||||
|
return resp, fmt.Errorf("already subscribed")
|
||||||
|
}
|
||||||
|
return resp, fmt.Errorf("unexpected Status: %d", status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteIssueSubscription unsubscribe user from issue
|
||||||
|
func (c *Client) DeleteIssueSubscription(owner, repo string, index int64, user string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo, &user); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
status, resp, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions/%s", owner, repo, index, user), nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
if status == http.StatusCreated {
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
if status == http.StatusOK {
|
||||||
|
return resp, fmt.Errorf("already unsubscribed")
|
||||||
|
}
|
||||||
|
return resp, fmt.Errorf("unexpected Status: %d", status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckIssueSubscription check if current user is subscribed to an issue
|
||||||
|
func (c *Client) CheckIssueSubscription(owner, repo string, index int64) (*WatchInfo, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
wi := new(WatchInfo)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions/check", owner, repo, index), nil, nil, wi)
|
||||||
|
return wi, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IssueSubscribe subscribe current user to an issue
|
||||||
|
func (c *Client) IssueSubscribe(owner, repo string, index int64) (*Response, error) {
|
||||||
|
u, _, err := c.GetMyUserInfo()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.AddIssueSubscription(owner, repo, index, u.UserName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IssueUnSubscribe unsubscribe current user from an issue
|
||||||
|
func (c *Client) IssueUnSubscribe(owner, repo string, index int64) (*Response, error) {
|
||||||
|
u, _, err := c.GetMyUserInfo()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.DeleteIssueSubscription(owner, repo, index, u.UserName)
|
||||||
|
}
|
||||||
97
vendor/code.gitea.io/sdk/gitea/issue_template.go
generated
vendored
Normal file
97
vendor/code.gitea.io/sdk/gitea/issue_template.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IssueTemplate provides metadata and content on an issue template.
|
||||||
|
// There are two types of issue templates: .Markdown- and .Form-based.
|
||||||
|
type IssueTemplate struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
About string `json:"about"`
|
||||||
|
Filename string `json:"file_name"`
|
||||||
|
IssueTitle string `json:"title"`
|
||||||
|
IssueLabels []string `json:"labels"`
|
||||||
|
IssueRef string `json:"ref"`
|
||||||
|
// If non-nil, this is a form-based template
|
||||||
|
Form []IssueFormElement `json:"body"`
|
||||||
|
// Should only be used when .Form is nil.
|
||||||
|
MarkdownContent string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IssueFormElement describes a part of a IssueTemplate form
|
||||||
|
type IssueFormElement struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Type IssueFormElementType `json:"type"`
|
||||||
|
Attributes IssueFormElementAttributes `json:"attributes"`
|
||||||
|
Validations IssueFormElementValidations `json:"validations"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IssueFormElementAttributes contains the combined set of attributes available on all element types.
|
||||||
|
type IssueFormElementAttributes struct {
|
||||||
|
// required for all element types.
|
||||||
|
// A brief description of the expected user input, which is also displayed in the form.
|
||||||
|
Label string `json:"label"`
|
||||||
|
// required for element types "dropdown", "checkboxes"
|
||||||
|
// for dropdown, contains the available options
|
||||||
|
Options []string `json:"options"`
|
||||||
|
// for element types "markdown", "textarea", "input"
|
||||||
|
// Text that is pre-filled in the input
|
||||||
|
Value string `json:"value"`
|
||||||
|
// for element types "textarea", "input", "dropdown", "checkboxes"
|
||||||
|
// A description of the text area to provide context or guidance, which is displayed in the form.
|
||||||
|
Description string `json:"description"`
|
||||||
|
// for element types "textarea", "input"
|
||||||
|
// A semi-opaque placeholder that renders in the text area when empty.
|
||||||
|
Placeholder string `json:"placeholder"`
|
||||||
|
// for element types "textarea"
|
||||||
|
// A language specifier. If set, the input is rendered as codeblock with syntax highlighting.
|
||||||
|
SyntaxHighlighting string `json:"render"`
|
||||||
|
// for element types "dropdown"
|
||||||
|
Multiple bool `json:"multiple"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IssueFormElementValidations contains the combined set of validations available on all element types.
|
||||||
|
type IssueFormElementValidations struct {
|
||||||
|
// for all element types
|
||||||
|
Required bool `json:"required"`
|
||||||
|
// for element types "input"
|
||||||
|
IsNumber bool `json:"is_number"`
|
||||||
|
// for element types "input"
|
||||||
|
Regex string `json:"regex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IssueFormElementType is an enum
|
||||||
|
type IssueFormElementType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// IssueFormElementMarkdown is markdown rendered to the form for context, but omitted in the resulting issue
|
||||||
|
IssueFormElementMarkdown IssueFormElementType = "markdown"
|
||||||
|
// IssueFormElementTextarea is a multi line input
|
||||||
|
IssueFormElementTextarea IssueFormElementType = "textarea"
|
||||||
|
// IssueFormElementInput is a single line input
|
||||||
|
IssueFormElementInput IssueFormElementType = "input"
|
||||||
|
// IssueFormElementDropdown is a select form
|
||||||
|
IssueFormElementDropdown IssueFormElementType = "dropdown"
|
||||||
|
// IssueFormElementCheckboxes are a multi checkbox input
|
||||||
|
IssueFormElementCheckboxes IssueFormElementType = "checkboxes"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetIssueTemplates lists all issue templates of the repository
|
||||||
|
func (c *Client) GetIssueTemplates(owner, repo string) ([]*IssueTemplate, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
templates := new([]*IssueTemplate)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issue_templates", owner, repo), nil, nil, templates)
|
||||||
|
return *templates, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsForm tells if this template is a form instead of a markdown-based template.
|
||||||
|
func (t IssueTemplate) IsForm() bool {
|
||||||
|
return t.Form != nil
|
||||||
|
}
|
||||||
142
vendor/code.gitea.io/sdk/gitea/issue_tracked_time.go
generated
vendored
Normal file
142
vendor/code.gitea.io/sdk/gitea/issue_tracked_time.go
generated
vendored
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TrackedTime worked time for an issue / pr
|
||||||
|
type TrackedTime struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
// Time in seconds
|
||||||
|
Time int64 `json:"time"`
|
||||||
|
// deprecated (only for backwards compatibility)
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
UserName string `json:"user_name"`
|
||||||
|
// deprecated (only for backwards compatibility)
|
||||||
|
IssueID int64 `json:"issue_id"`
|
||||||
|
Issue *Issue `json:"issue"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListTrackedTimesOptions options for listing repository's tracked times
|
||||||
|
type ListTrackedTimesOptions struct {
|
||||||
|
ListOptions
|
||||||
|
Since time.Time
|
||||||
|
Before time.Time
|
||||||
|
// User filter is only used by ListRepoTrackedTimes !!!
|
||||||
|
User string
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryEncode turns options into querystring argument
|
||||||
|
func (opt *ListTrackedTimesOptions) QueryEncode() string {
|
||||||
|
query := opt.getURLQuery()
|
||||||
|
|
||||||
|
if !opt.Since.IsZero() {
|
||||||
|
query.Add("since", opt.Since.Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
if !opt.Before.IsZero() {
|
||||||
|
query.Add("before", opt.Before.Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opt.User) != 0 {
|
||||||
|
query.Add("user", opt.User)
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoTrackedTimes list tracked times of a repository
|
||||||
|
func (c *Client) ListRepoTrackedTimes(owner, repo string, opt ListTrackedTimesOptions) ([]*TrackedTime, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/times", owner, repo))
|
||||||
|
opt.setDefaults()
|
||||||
|
link.RawQuery = opt.QueryEncode()
|
||||||
|
times := make([]*TrackedTime, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, ×)
|
||||||
|
return times, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMyTrackedTimes list tracked times of the current user
|
||||||
|
func (c *Client) GetMyTrackedTimes() ([]*TrackedTime, *Response, error) {
|
||||||
|
times := make([]*TrackedTime, 0, 10)
|
||||||
|
resp, err := c.getParsedResponse("GET", "/user/times", jsonHeader, nil, ×)
|
||||||
|
return times, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTimeOption options for adding time to an issue
|
||||||
|
type AddTimeOption struct {
|
||||||
|
// time in seconds
|
||||||
|
Time int64 `json:"time"`
|
||||||
|
// optional
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
// optional
|
||||||
|
User string `json:"user_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the AddTimeOption struct
|
||||||
|
func (opt AddTimeOption) Validate() error {
|
||||||
|
if opt.Time == 0 {
|
||||||
|
return fmt.Errorf("no time to add")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTime adds time to issue with the given index
|
||||||
|
func (c *Client) AddTime(owner, repo string, index int64, opt AddTimeOption) (*TrackedTime, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
t := new(TrackedTime)
|
||||||
|
resp, err := c.getParsedResponse("POST",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/issues/%d/times", owner, repo, index),
|
||||||
|
jsonHeader, bytes.NewReader(body), t)
|
||||||
|
return t, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListIssueTrackedTimes list tracked times of a single issue for a given repository
|
||||||
|
func (c *Client) ListIssueTrackedTimes(owner, repo string, index int64, opt ListTrackedTimesOptions) ([]*TrackedTime, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/%d/times", owner, repo, index))
|
||||||
|
opt.setDefaults()
|
||||||
|
link.RawQuery = opt.QueryEncode()
|
||||||
|
times := make([]*TrackedTime, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, ×)
|
||||||
|
return times, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetIssueTime reset tracked time of a single issue for a given repository
|
||||||
|
func (c *Client) ResetIssueTime(owner, repo string, index int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/times", owner, repo, index), jsonHeader, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteTime delete a specific tracked time by id of a single issue for a given repository
|
||||||
|
func (c *Client) DeleteTime(owner, repo string, index, timeID int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/times/%d", owner, repo, index, timeID), jsonHeader, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
40
vendor/code.gitea.io/sdk/gitea/list_options.go
generated
vendored
Normal file
40
vendor/code.gitea.io/sdk/gitea/list_options.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListOptions options for using Gitea's API pagination
|
||||||
|
type ListOptions struct {
|
||||||
|
// Setting Page to -1 disables pagination on endpoints that support it.
|
||||||
|
// Page numbering starts at 1.
|
||||||
|
Page int
|
||||||
|
// The default value depends on the server config DEFAULT_PAGING_NUM
|
||||||
|
// The highest valid value depends on the server config MAX_RESPONSE_ITEMS
|
||||||
|
PageSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o ListOptions) getURLQuery() url.Values {
|
||||||
|
query := make(url.Values)
|
||||||
|
query.Add("page", fmt.Sprintf("%d", o.Page))
|
||||||
|
query.Add("limit", fmt.Sprintf("%d", o.PageSize))
|
||||||
|
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
// setDefaults applies default pagination options.
|
||||||
|
// If .Page is set to -1, it will disable pagination.
|
||||||
|
// WARNING: This function is not idempotent, make sure to never call this method twice!
|
||||||
|
func (o *ListOptions) setDefaults() {
|
||||||
|
if o.Page < 0 {
|
||||||
|
o.Page, o.PageSize = 0, 0
|
||||||
|
return
|
||||||
|
} else if o.Page == 0 {
|
||||||
|
o.Page = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
257
vendor/code.gitea.io/sdk/gitea/notifications.go
generated
vendored
Normal file
257
vendor/code.gitea.io/sdk/gitea/notifications.go
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NotificationThread expose Notification on API
|
||||||
|
type NotificationThread struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Repository *Repository `json:"repository"`
|
||||||
|
Subject *NotificationSubject `json:"subject"`
|
||||||
|
Unread bool `json:"unread"`
|
||||||
|
Pinned bool `json:"pinned"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotificationSubject contains the notification subject (Issue/Pull/Commit)
|
||||||
|
type NotificationSubject struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
HTMLURL string `json:"html_url"`
|
||||||
|
LatestCommentURL string `json:"latest_comment_url"`
|
||||||
|
LatestCommentHTMLURL string `json:"latest_comment_html_url"`
|
||||||
|
Type NotifySubjectType `json:"type"`
|
||||||
|
State NotifySubjectState `json:"state"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifyStatus notification status type
|
||||||
|
type NotifyStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NotifyStatusUnread was not read
|
||||||
|
NotifyStatusUnread NotifyStatus = "unread"
|
||||||
|
// NotifyStatusRead was already read by user
|
||||||
|
NotifyStatusRead NotifyStatus = "read"
|
||||||
|
// NotifyStatusPinned notification is pinned by user
|
||||||
|
NotifyStatusPinned NotifyStatus = "pinned"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NotifySubjectType represent type of notification subject
|
||||||
|
type NotifySubjectType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NotifySubjectIssue an issue is subject of an notification
|
||||||
|
NotifySubjectIssue NotifySubjectType = "Issue"
|
||||||
|
// NotifySubjectPull an pull is subject of an notification
|
||||||
|
NotifySubjectPull NotifySubjectType = "Pull"
|
||||||
|
// NotifySubjectCommit an commit is subject of an notification
|
||||||
|
NotifySubjectCommit NotifySubjectType = "Commit"
|
||||||
|
// NotifySubjectRepository an repository is subject of an notification
|
||||||
|
NotifySubjectRepository NotifySubjectType = "Repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NotifySubjectState reflect state of notification subject
|
||||||
|
type NotifySubjectState string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NotifySubjectOpen if subject is a pull/issue and is open at the moment
|
||||||
|
NotifySubjectOpen NotifySubjectState = "open"
|
||||||
|
// NotifySubjectClosed if subject is a pull/issue and is closed at the moment
|
||||||
|
NotifySubjectClosed NotifySubjectState = "closed"
|
||||||
|
// NotifySubjectMerged if subject is a pull and got merged
|
||||||
|
NotifySubjectMerged NotifySubjectState = "merged"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListNotificationOptions represents the filter options
|
||||||
|
type ListNotificationOptions struct {
|
||||||
|
ListOptions
|
||||||
|
Since time.Time
|
||||||
|
Before time.Time
|
||||||
|
Status []NotifyStatus
|
||||||
|
SubjectTypes []NotifySubjectType
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkNotificationOptions represents the filter & modify options
|
||||||
|
type MarkNotificationOptions struct {
|
||||||
|
LastReadAt time.Time
|
||||||
|
Status []NotifyStatus
|
||||||
|
ToStatus NotifyStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryEncode encode options to url query
|
||||||
|
func (opt *ListNotificationOptions) QueryEncode() string {
|
||||||
|
query := opt.getURLQuery()
|
||||||
|
if !opt.Since.IsZero() {
|
||||||
|
query.Add("since", opt.Since.Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
if !opt.Before.IsZero() {
|
||||||
|
query.Add("before", opt.Before.Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
for _, s := range opt.Status {
|
||||||
|
query.Add("status-types", string(s))
|
||||||
|
}
|
||||||
|
for _, s := range opt.SubjectTypes {
|
||||||
|
query.Add("subject-type", string(s))
|
||||||
|
}
|
||||||
|
return query.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the CreateUserOption struct
|
||||||
|
func (opt ListNotificationOptions) Validate(c *Client) error {
|
||||||
|
if len(opt.Status) != 0 {
|
||||||
|
return c.checkServerVersionGreaterThanOrEqual(version1_12_3)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryEncode encode options to url query
|
||||||
|
func (opt *MarkNotificationOptions) QueryEncode() string {
|
||||||
|
query := make(url.Values)
|
||||||
|
if !opt.LastReadAt.IsZero() {
|
||||||
|
query.Add("last_read_at", opt.LastReadAt.Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
for _, s := range opt.Status {
|
||||||
|
query.Add("status-types", string(s))
|
||||||
|
}
|
||||||
|
if len(opt.ToStatus) != 0 {
|
||||||
|
query.Add("to-status", string(opt.ToStatus))
|
||||||
|
}
|
||||||
|
return query.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the CreateUserOption struct
|
||||||
|
func (opt MarkNotificationOptions) Validate(c *Client) error {
|
||||||
|
if len(opt.Status) != 0 || len(opt.ToStatus) != 0 {
|
||||||
|
return c.checkServerVersionGreaterThanOrEqual(version1_12_3)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckNotifications list users's notification threads
|
||||||
|
func (c *Client) CheckNotifications() (int64, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
new := struct {
|
||||||
|
New int64 `json:"new"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
resp, err := c.getParsedResponse("GET", "/notifications/new", jsonHeader, nil, &new)
|
||||||
|
return new.New, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNotification get notification thread by ID
|
||||||
|
func (c *Client) GetNotification(id int64) (*NotificationThread, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
thread := new(NotificationThread)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/notifications/threads/%d", id), nil, nil, thread)
|
||||||
|
return thread, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadNotification mark notification thread as read by ID
|
||||||
|
// It optionally takes a second argument if status has to be set other than 'read'
|
||||||
|
// The relevant notification will be returned as the first parameter when the Gitea server is 1.16.0 or higher.
|
||||||
|
func (c *Client) ReadNotification(id int64, status ...NotifyStatus) (*NotificationThread, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
link := fmt.Sprintf("/notifications/threads/%d", id)
|
||||||
|
if len(status) != 0 {
|
||||||
|
link += fmt.Sprintf("?to-status=%s", status[0])
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_16_0); err == nil {
|
||||||
|
thread := &NotificationThread{}
|
||||||
|
resp, err := c.getParsedResponse("PATCH", link, nil, nil, thread)
|
||||||
|
return thread, resp, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("PATCH", link, nil, nil)
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListNotifications list users's notification threads
|
||||||
|
func (c *Client) ListNotifications(opt ListNotificationOptions) ([]*NotificationThread, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(c); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
link, _ := url.Parse("/notifications")
|
||||||
|
link.RawQuery = opt.QueryEncode()
|
||||||
|
threads := make([]*NotificationThread, 0, 10)
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &threads)
|
||||||
|
return threads, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadNotifications mark notification threads as read
|
||||||
|
// The relevant notifications will only be returned as the first parameter when the Gitea server is 1.16.0 or higher.
|
||||||
|
func (c *Client) ReadNotifications(opt MarkNotificationOptions) ([]*NotificationThread, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(c); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
link, _ := url.Parse("/notifications")
|
||||||
|
link.RawQuery = opt.QueryEncode()
|
||||||
|
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_16_0); err == nil {
|
||||||
|
threads := make([]*NotificationThread, 0, 10)
|
||||||
|
resp, err := c.getParsedResponse("PUT", link.String(), nil, nil, &threads)
|
||||||
|
return threads, resp, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("PUT", link.String(), nil, nil)
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoNotifications list users's notification threads on a specific repo
|
||||||
|
func (c *Client) ListRepoNotifications(owner, repo string, opt ListNotificationOptions) ([]*NotificationThread, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(c); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/notifications", owner, repo))
|
||||||
|
link.RawQuery = opt.QueryEncode()
|
||||||
|
threads := make([]*NotificationThread, 0, 10)
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &threads)
|
||||||
|
return threads, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadRepoNotifications mark notification threads as read on a specific repo
|
||||||
|
// The relevant notifications will only be returned as the first parameter when the Gitea server is 1.16.0 or higher.
|
||||||
|
func (c *Client) ReadRepoNotifications(owner, repo string, opt MarkNotificationOptions) ([]*NotificationThread, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(c); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/notifications", owner, repo))
|
||||||
|
link.RawQuery = opt.QueryEncode()
|
||||||
|
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_16_0); err == nil {
|
||||||
|
threads := make([]*NotificationThread, 0, 10)
|
||||||
|
resp, err := c.getParsedResponse("PUT", link.String(), nil, nil, &threads)
|
||||||
|
return threads, resp, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("PUT", link.String(), nil, nil)
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
93
vendor/code.gitea.io/sdk/gitea/oauth2.go
generated
vendored
Normal file
93
vendor/code.gitea.io/sdk/gitea/oauth2.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Oauth2 represents an Oauth2 Application
|
||||||
|
type Oauth2 struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
ClientSecret string `json:"client_secret"`
|
||||||
|
RedirectURIs []string `json:"redirect_uris"`
|
||||||
|
ConfidentialClient bool `json:"confidential_client"`
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOauth2Option for listing Oauth2 Applications
|
||||||
|
type ListOauth2Option struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOauth2Option required options for creating an Application
|
||||||
|
type CreateOauth2Option struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ConfidentialClient bool `json:"confidential_client"`
|
||||||
|
RedirectURIs []string `json:"redirect_uris"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOauth2 create an Oauth2 Application and returns a completed Oauth2 object.
|
||||||
|
func (c *Client) CreateOauth2(opt CreateOauth2Option) (*Oauth2, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
oauth := new(Oauth2)
|
||||||
|
resp, err := c.getParsedResponse("POST", "/user/applications/oauth2", jsonHeader, bytes.NewReader(body), oauth)
|
||||||
|
return oauth, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOauth2 a specific Oauth2 Application by ID and return a completed Oauth2 object.
|
||||||
|
func (c *Client) UpdateOauth2(oauth2id int64, opt CreateOauth2Option) (*Oauth2, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
oauth := new(Oauth2)
|
||||||
|
resp, err := c.getParsedResponse("PATCH", fmt.Sprintf("/user/applications/oauth2/%d", oauth2id), jsonHeader, bytes.NewReader(body), oauth)
|
||||||
|
return oauth, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOauth2 a specific Oauth2 Application by ID.
|
||||||
|
func (c *Client) GetOauth2(oauth2id int64) (*Oauth2, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
oauth2s := &Oauth2{}
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/applications/oauth2/%d", oauth2id), nil, nil, &oauth2s)
|
||||||
|
return oauth2s, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOauth2 all of your Oauth2 Applications.
|
||||||
|
func (c *Client) ListOauth2(opt ListOauth2Option) ([]*Oauth2, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
oauth2s := make([]*Oauth2, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/applications/oauth2?%s", opt.getURLQuery().Encode()), nil, nil, &oauth2s)
|
||||||
|
return oauth2s, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteOauth2 delete an Oauth2 application by ID
|
||||||
|
func (c *Client) DeleteOauth2(oauth2id int64) (*Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/applications/oauth2/%d", oauth2id), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
155
vendor/code.gitea.io/sdk/gitea/org.go
generated
vendored
Normal file
155
vendor/code.gitea.io/sdk/gitea/org.go
generated
vendored
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Organization represents an organization
|
||||||
|
type Organization struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
UserName string `json:"username"`
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Website string `json:"website"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
Visibility string `json:"visibility"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisibleType defines the visibility
|
||||||
|
type VisibleType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// VisibleTypePublic Visible for everyone
|
||||||
|
VisibleTypePublic VisibleType = "public"
|
||||||
|
|
||||||
|
// VisibleTypeLimited Visible for every connected user
|
||||||
|
VisibleTypeLimited VisibleType = "limited"
|
||||||
|
|
||||||
|
// VisibleTypePrivate Visible only for organization's members
|
||||||
|
VisibleTypePrivate VisibleType = "private"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListOrgsOptions options for listing organizations
|
||||||
|
type ListOrgsOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMyOrgs list all of current user's organizations
|
||||||
|
func (c *Client) ListMyOrgs(opt ListOrgsOptions) ([]*Organization, *Response, error) {
|
||||||
|
opt.setDefaults()
|
||||||
|
orgs := make([]*Organization, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/orgs?%s", opt.getURLQuery().Encode()), nil, nil, &orgs)
|
||||||
|
return orgs, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUserOrgs list all of some user's organizations
|
||||||
|
func (c *Client) ListUserOrgs(user string, opt ListOrgsOptions) ([]*Organization, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
orgs := make([]*Organization, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/orgs?%s", user, opt.getURLQuery().Encode()), nil, nil, &orgs)
|
||||||
|
return orgs, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrg get one organization by name
|
||||||
|
func (c *Client) GetOrg(orgname string) (*Organization, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&orgname); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
org := new(Organization)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s", orgname), nil, nil, org)
|
||||||
|
return org, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOrgOption options for creating an organization
|
||||||
|
type CreateOrgOption struct {
|
||||||
|
Name string `json:"username"`
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Website string `json:"website"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
Visibility VisibleType `json:"visibility"`
|
||||||
|
RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkVisibilityOpt check if mode exist
|
||||||
|
func checkVisibilityOpt(v VisibleType) bool {
|
||||||
|
return v == VisibleTypePublic || v == VisibleTypeLimited || v == VisibleTypePrivate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the CreateOrgOption struct
|
||||||
|
func (opt CreateOrgOption) Validate() error {
|
||||||
|
if len(opt.Name) == 0 {
|
||||||
|
return fmt.Errorf("empty org name")
|
||||||
|
}
|
||||||
|
if len(opt.Visibility) != 0 && !checkVisibilityOpt(opt.Visibility) {
|
||||||
|
return fmt.Errorf("infalid bisibility option")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOrg creates an organization
|
||||||
|
func (c *Client) CreateOrg(opt CreateOrgOption) (*Organization, *Response, error) {
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
org := new(Organization)
|
||||||
|
resp, err := c.getParsedResponse("POST", "/orgs", jsonHeader, bytes.NewReader(body), org)
|
||||||
|
return org, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditOrgOption options for editing an organization
|
||||||
|
type EditOrgOption struct {
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Website string `json:"website"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
Visibility VisibleType `json:"visibility"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the EditOrgOption struct
|
||||||
|
func (opt EditOrgOption) Validate() error {
|
||||||
|
if len(opt.Visibility) != 0 && !checkVisibilityOpt(opt.Visibility) {
|
||||||
|
return fmt.Errorf("infalid bisibility option")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditOrg modify one organization via options
|
||||||
|
func (c *Client) EditOrg(orgname string, opt EditOrgOption) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&orgname); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("PATCH", fmt.Sprintf("/orgs/%s", orgname), jsonHeader, bytes.NewReader(body))
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteOrg deletes an organization
|
||||||
|
func (c *Client) DeleteOrg(orgname string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&orgname); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s", orgname), jsonHeader, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
29
vendor/code.gitea.io/sdk/gitea/org_action.go
generated
vendored
Normal file
29
vendor/code.gitea.io/sdk/gitea/org_action.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListOrgMembershipOption list OrgMembership options
|
||||||
|
type ListOrgActionSecretOption struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOrgMembership list an organization's members
|
||||||
|
func (c *Client) ListOrgActionSecret(org string, opt ListOrgActionSecretOption) ([]*Secret, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
secrets := make([]*Secret, 0, opt.PageSize)
|
||||||
|
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/orgs/%s/actions/secrets", org))
|
||||||
|
link.RawQuery = opt.getURLQuery().Encode()
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &secrets)
|
||||||
|
return secrets, resp, err
|
||||||
|
}
|
||||||
142
vendor/code.gitea.io/sdk/gitea/org_member.go
generated
vendored
Normal file
142
vendor/code.gitea.io/sdk/gitea/org_member.go
generated
vendored
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeleteOrgMembership remove a member from an organization
|
||||||
|
func (c *Client) DeleteOrgMembership(org, user string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org, &user); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s/members/%s", org, user), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOrgMembershipOption list OrgMembership options
|
||||||
|
type ListOrgMembershipOption struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOrgMembership list an organization's members
|
||||||
|
func (c *Client) ListOrgMembership(org string, opt ListOrgMembershipOption) ([]*User, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
users := make([]*User, 0, opt.PageSize)
|
||||||
|
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/orgs/%s/members", org))
|
||||||
|
link.RawQuery = opt.getURLQuery().Encode()
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &users)
|
||||||
|
return users, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPublicOrgMembership list an organization's members
|
||||||
|
func (c *Client) ListPublicOrgMembership(org string, opt ListOrgMembershipOption) ([]*User, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
users := make([]*User, 0, opt.PageSize)
|
||||||
|
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/orgs/%s/public_members", org))
|
||||||
|
link.RawQuery = opt.getURLQuery().Encode()
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &users)
|
||||||
|
return users, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckOrgMembership Check if a user is a member of an organization
|
||||||
|
func (c *Client) CheckOrgMembership(org, user string) (bool, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org, &user); err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/orgs/%s/members/%s", org, user), nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return false, resp, err
|
||||||
|
}
|
||||||
|
switch status {
|
||||||
|
case http.StatusNoContent:
|
||||||
|
return true, resp, nil
|
||||||
|
case http.StatusNotFound:
|
||||||
|
return false, resp, nil
|
||||||
|
default:
|
||||||
|
return false, resp, fmt.Errorf("unexpected Status: %d", status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckPublicOrgMembership Check if a user is a member of an organization
|
||||||
|
func (c *Client) CheckPublicOrgMembership(org, user string) (bool, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org, &user); err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/orgs/%s/public_members/%s", org, user), nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return false, resp, err
|
||||||
|
}
|
||||||
|
switch status {
|
||||||
|
case http.StatusNoContent:
|
||||||
|
return true, resp, nil
|
||||||
|
case http.StatusNotFound:
|
||||||
|
return false, resp, nil
|
||||||
|
default:
|
||||||
|
return false, resp, fmt.Errorf("unexpected Status: %d", status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPublicOrgMembership publicize/conceal a user's membership
|
||||||
|
func (c *Client) SetPublicOrgMembership(org, user string, visible bool) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org, &user); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
status int
|
||||||
|
err error
|
||||||
|
resp *Response
|
||||||
|
)
|
||||||
|
if visible {
|
||||||
|
status, resp, err = c.getStatusCode("PUT", fmt.Sprintf("/orgs/%s/public_members/%s", org, user), nil, nil)
|
||||||
|
} else {
|
||||||
|
status, resp, err = c.getStatusCode("DELETE", fmt.Sprintf("/orgs/%s/public_members/%s", org, user), nil, nil)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
switch status {
|
||||||
|
case http.StatusNoContent:
|
||||||
|
return resp, nil
|
||||||
|
case http.StatusNotFound:
|
||||||
|
return resp, fmt.Errorf("forbidden")
|
||||||
|
default:
|
||||||
|
return resp, fmt.Errorf("unexpected Status: %d", status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrgPermissions represents the permissions for an user in an organization
|
||||||
|
type OrgPermissions struct {
|
||||||
|
CanCreateRepository bool `json:"can_create_repository"`
|
||||||
|
CanRead bool `json:"can_read"`
|
||||||
|
CanWrite bool `json:"can_write"`
|
||||||
|
IsAdmin bool `json:"is_admin"`
|
||||||
|
IsOwner bool `json:"is_owner"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrgPermissions returns user permissions for specific organization.
|
||||||
|
func (c *Client) GetOrgPermissions(org, user string) (*OrgPermissions, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org, &user); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
perm := &OrgPermissions{}
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/orgs/%s/permissions", user, org), jsonHeader, nil, &perm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return perm, resp, nil
|
||||||
|
}
|
||||||
285
vendor/code.gitea.io/sdk/gitea/org_team.go
generated
vendored
Normal file
285
vendor/code.gitea.io/sdk/gitea/org_team.go
generated
vendored
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Team represents a team in an organization
|
||||||
|
type Team struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Organization *Organization `json:"organization"`
|
||||||
|
Permission AccessMode `json:"permission"`
|
||||||
|
CanCreateOrgRepo bool `json:"can_create_org_repo"`
|
||||||
|
IncludesAllRepositories bool `json:"includes_all_repositories"`
|
||||||
|
Units []RepoUnitType `json:"units"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepoUnitType represent all unit types of a repo gitea currently offer
|
||||||
|
type RepoUnitType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// RepoUnitCode represent file view of a repository
|
||||||
|
RepoUnitCode RepoUnitType = "repo.code"
|
||||||
|
// RepoUnitIssues represent issues of a repository
|
||||||
|
RepoUnitIssues RepoUnitType = "repo.issues"
|
||||||
|
// RepoUnitPulls represent pulls of a repository
|
||||||
|
RepoUnitPulls RepoUnitType = "repo.pulls"
|
||||||
|
// RepoUnitExtIssues represent external issues of a repository
|
||||||
|
RepoUnitExtIssues RepoUnitType = "repo.ext_issues"
|
||||||
|
// RepoUnitWiki represent wiki of a repository
|
||||||
|
RepoUnitWiki RepoUnitType = "repo.wiki"
|
||||||
|
// RepoUnitExtWiki represent external wiki of a repository
|
||||||
|
RepoUnitExtWiki RepoUnitType = "repo.ext_wiki"
|
||||||
|
// RepoUnitReleases represent releases of a repository
|
||||||
|
RepoUnitReleases RepoUnitType = "repo.releases"
|
||||||
|
// RepoUnitProjects represent projects of a repository
|
||||||
|
RepoUnitProjects RepoUnitType = "repo.projects"
|
||||||
|
// RepoUnitPackages represents packages of a repository
|
||||||
|
RepoUnitPackages RepoUnitType = "repo.packages"
|
||||||
|
// RepoUnitActions represents actions of a repository
|
||||||
|
RepoUnitActions RepoUnitType = "repo.actions"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListTeamsOptions options for listing teams
|
||||||
|
type ListTeamsOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOrgTeams lists all teams of an organization
|
||||||
|
func (c *Client) ListOrgTeams(org string, opt ListTeamsOptions) ([]*Team, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
teams := make([]*Team, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/teams?%s", org, opt.getURLQuery().Encode()), nil, nil, &teams)
|
||||||
|
return teams, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMyTeams lists all the teams of the current user
|
||||||
|
func (c *Client) ListMyTeams(opt *ListTeamsOptions) ([]*Team, *Response, error) {
|
||||||
|
opt.setDefaults()
|
||||||
|
teams := make([]*Team, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/teams?%s", opt.getURLQuery().Encode()), nil, nil, &teams)
|
||||||
|
return teams, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTeam gets a team by ID
|
||||||
|
func (c *Client) GetTeam(id int64) (*Team, *Response, error) {
|
||||||
|
t := new(Team)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/teams/%d", id), nil, nil, t)
|
||||||
|
return t, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchTeamsOptions options for searching teams.
|
||||||
|
type SearchTeamsOptions struct {
|
||||||
|
ListOptions
|
||||||
|
Query string
|
||||||
|
IncludeDescription bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o SearchTeamsOptions) getURLQuery() url.Values {
|
||||||
|
query := make(url.Values)
|
||||||
|
query.Add("page", fmt.Sprintf("%d", o.Page))
|
||||||
|
query.Add("limit", fmt.Sprintf("%d", o.PageSize))
|
||||||
|
query.Add("q", o.Query)
|
||||||
|
query.Add("include_desc", fmt.Sprintf("%t", o.IncludeDescription))
|
||||||
|
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
// TeamSearchResults is the JSON struct that is returned from Team search API.
|
||||||
|
type TeamSearchResults struct {
|
||||||
|
OK bool `json:"ok"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
Data []*Team `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchOrgTeams search for teams in a org.
|
||||||
|
func (c *Client) SearchOrgTeams(org string, opt *SearchTeamsOptions) ([]*Team, *Response, error) {
|
||||||
|
responseBody := TeamSearchResults{}
|
||||||
|
opt.setDefaults()
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/teams/search?%s", org, opt.getURLQuery().Encode()), nil, nil, &responseBody)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if !responseBody.OK {
|
||||||
|
return nil, resp, fmt.Errorf("gitea error: %v", responseBody.Error)
|
||||||
|
}
|
||||||
|
return responseBody.Data, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateTeamOption options for creating a team
|
||||||
|
type CreateTeamOption struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Permission AccessMode `json:"permission"`
|
||||||
|
CanCreateOrgRepo bool `json:"can_create_org_repo"`
|
||||||
|
IncludesAllRepositories bool `json:"includes_all_repositories"`
|
||||||
|
Units []RepoUnitType `json:"units"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the CreateTeamOption struct
|
||||||
|
func (opt *CreateTeamOption) Validate() error {
|
||||||
|
if opt.Permission == AccessModeOwner {
|
||||||
|
opt.Permission = AccessModeAdmin
|
||||||
|
} else if opt.Permission != AccessModeRead && opt.Permission != AccessModeWrite && opt.Permission != AccessModeAdmin {
|
||||||
|
return fmt.Errorf("permission mode invalid")
|
||||||
|
}
|
||||||
|
if len(opt.Name) == 0 {
|
||||||
|
return fmt.Errorf("name required")
|
||||||
|
}
|
||||||
|
if len(opt.Name) > 30 {
|
||||||
|
return fmt.Errorf("name to long")
|
||||||
|
}
|
||||||
|
if len(opt.Description) > 255 {
|
||||||
|
return fmt.Errorf("description to long")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateTeam creates a team for an organization
|
||||||
|
func (c *Client) CreateTeam(org string, opt CreateTeamOption) (*Team, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := (&opt).Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
t := new(Team)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/orgs/%s/teams", org), jsonHeader, bytes.NewReader(body), t)
|
||||||
|
return t, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditTeamOption options for editing a team
|
||||||
|
type EditTeamOption struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description *string `json:"description"`
|
||||||
|
Permission AccessMode `json:"permission"`
|
||||||
|
CanCreateOrgRepo *bool `json:"can_create_org_repo"`
|
||||||
|
IncludesAllRepositories *bool `json:"includes_all_repositories"`
|
||||||
|
Units []RepoUnitType `json:"units"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the EditTeamOption struct
|
||||||
|
func (opt *EditTeamOption) Validate() error {
|
||||||
|
if opt.Permission == AccessModeOwner {
|
||||||
|
opt.Permission = AccessModeAdmin
|
||||||
|
} else if opt.Permission != AccessModeRead && opt.Permission != AccessModeWrite && opt.Permission != AccessModeAdmin {
|
||||||
|
return fmt.Errorf("permission mode invalid")
|
||||||
|
}
|
||||||
|
if len(opt.Name) == 0 {
|
||||||
|
return fmt.Errorf("name required")
|
||||||
|
}
|
||||||
|
if len(opt.Name) > 30 {
|
||||||
|
return fmt.Errorf("name to long")
|
||||||
|
}
|
||||||
|
if opt.Description != nil && len(*opt.Description) > 255 {
|
||||||
|
return fmt.Errorf("description to long")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditTeam edits a team of an organization
|
||||||
|
func (c *Client) EditTeam(id int64, opt EditTeamOption) (*Response, error) {
|
||||||
|
if err := (&opt).Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("PATCH", fmt.Sprintf("/teams/%d", id), jsonHeader, bytes.NewReader(body))
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteTeam deletes a team of an organization
|
||||||
|
func (c *Client) DeleteTeam(id int64) (*Response, error) {
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/teams/%d", id), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListTeamMembersOptions options for listing team's members
|
||||||
|
type ListTeamMembersOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListTeamMembers lists all members of a team
|
||||||
|
func (c *Client) ListTeamMembers(id int64, opt ListTeamMembersOptions) ([]*User, *Response, error) {
|
||||||
|
opt.setDefaults()
|
||||||
|
members := make([]*User, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/teams/%d/members?%s", id, opt.getURLQuery().Encode()), nil, nil, &members)
|
||||||
|
return members, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTeamMember gets a member of a team
|
||||||
|
func (c *Client) GetTeamMember(id int64, user string) (*User, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
m := new(User)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil, m)
|
||||||
|
return m, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTeamMember adds a member to a team
|
||||||
|
func (c *Client) AddTeamMember(id int64, user string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveTeamMember removes a member from a team
|
||||||
|
func (c *Client) RemoveTeamMember(id int64, user string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListTeamRepositoriesOptions options for listing team's repositories
|
||||||
|
type ListTeamRepositoriesOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListTeamRepositories lists all repositories of a team
|
||||||
|
func (c *Client) ListTeamRepositories(id int64, opt ListTeamRepositoriesOptions) ([]*Repository, *Response, error) {
|
||||||
|
opt.setDefaults()
|
||||||
|
repos := make([]*Repository, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/teams/%d/repos?%s", id, opt.getURLQuery().Encode()), nil, nil, &repos)
|
||||||
|
return repos, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTeamRepository adds a repository to a team
|
||||||
|
func (c *Client) AddTeamRepository(id int64, org, repo string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/teams/%d/repos/%s/%s", id, org, repo), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveTeamRepository removes a repository from a team
|
||||||
|
func (c *Client) RemoveTeamRepository(id int64, org, repo string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/teams/%d/repos/%s/%s", id, org, repo), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
93
vendor/code.gitea.io/sdk/gitea/package.go
generated
vendored
Normal file
93
vendor/code.gitea.io/sdk/gitea/package.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Package represents a package
|
||||||
|
type Package struct {
|
||||||
|
// the package's id
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
// the package's owner
|
||||||
|
Owner User `json:"owner"`
|
||||||
|
// the repo this package belongs to (if any)
|
||||||
|
Repository *string `json:"repository"`
|
||||||
|
// the package's creator
|
||||||
|
Creator User `json:"creator"`
|
||||||
|
// the type of package:
|
||||||
|
Type string `json:"type"`
|
||||||
|
// the name of the package
|
||||||
|
Name string `json:"name"`
|
||||||
|
// the version of the package
|
||||||
|
Version string `json:"version"`
|
||||||
|
// the date the package was uploaded
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PackageFile represents a file from a package
|
||||||
|
type PackageFile struct {
|
||||||
|
// the file's ID
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
// the size of the file in bytes
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
// the name of the file
|
||||||
|
Name string `json:"name"`
|
||||||
|
// the md5 hash of the file
|
||||||
|
MD5 string `json:"md5"`
|
||||||
|
// the sha1 hash of the file
|
||||||
|
SHA1 string `json:"sha1"`
|
||||||
|
// the sha256 hash of the file
|
||||||
|
SHA256 string `json:"sha256"`
|
||||||
|
// the sha512 hash of the file
|
||||||
|
SHA512 string `json:"sha512"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPackagesOptions options for listing packages
|
||||||
|
type ListPackagesOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPackages lists all the packages owned by a given owner (user, organisation)
|
||||||
|
func (c *Client) ListPackages(owner string, opt ListPackagesOptions) ([]*Package, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
packages := make([]*Package, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/packages/%s?%s", owner, opt.getURLQuery().Encode()), nil, nil, &packages)
|
||||||
|
return packages, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPackage gets the details of a specific package version
|
||||||
|
func (c *Client) GetPackage(owner, packageType, name, version string) (*Package, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &packageType, &name, &version); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
foundPackage := new(Package)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/packages/%s/%s/%s/%s", owner, packageType, name, version), nil, nil, foundPackage)
|
||||||
|
return foundPackage, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePackage deletes a specific package version
|
||||||
|
func (c *Client) DeletePackage(owner, packageType, name, version string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &packageType, &name, &version); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/packages/%s/%s/%s/%s", owner, packageType, name, version), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPackageFiles lists the files within a package
|
||||||
|
func (c *Client) ListPackageFiles(owner, packageType, name, version string) ([]*PackageFile, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &packageType, &name, &version); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
packageFiles := make([]*PackageFile, 0)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/packages/%s/%s/%s/%s/files", owner, packageType, name, version), nil, nil, &packageFiles)
|
||||||
|
return packageFiles, resp, err
|
||||||
|
}
|
||||||
383
vendor/code.gitea.io/sdk/gitea/pull.go
generated
vendored
Normal file
383
vendor/code.gitea.io/sdk/gitea/pull.go
generated
vendored
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PRBranchInfo information about a branch
|
||||||
|
type PRBranchInfo struct {
|
||||||
|
Name string `json:"label"`
|
||||||
|
Ref string `json:"ref"`
|
||||||
|
Sha string `json:"sha"`
|
||||||
|
RepoID int64 `json:"repo_id"`
|
||||||
|
Repository *Repository `json:"repo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullRequest represents a pull request
|
||||||
|
type PullRequest struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Index int64 `json:"number"`
|
||||||
|
Poster *User `json:"user"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
Labels []*Label `json:"labels"`
|
||||||
|
Milestone *Milestone `json:"milestone"`
|
||||||
|
Assignee *User `json:"assignee"`
|
||||||
|
Assignees []*User `json:"assignees"`
|
||||||
|
State StateType `json:"state"`
|
||||||
|
IsLocked bool `json:"is_locked"`
|
||||||
|
Comments int `json:"comments"`
|
||||||
|
|
||||||
|
HTMLURL string `json:"html_url"`
|
||||||
|
DiffURL string `json:"diff_url"`
|
||||||
|
PatchURL string `json:"patch_url"`
|
||||||
|
|
||||||
|
Mergeable bool `json:"mergeable"`
|
||||||
|
HasMerged bool `json:"merged"`
|
||||||
|
Merged *time.Time `json:"merged_at"`
|
||||||
|
MergedCommitID *string `json:"merge_commit_sha"`
|
||||||
|
MergedBy *User `json:"merged_by"`
|
||||||
|
AllowMaintainerEdit bool `json:"allow_maintainer_edit"`
|
||||||
|
|
||||||
|
Base *PRBranchInfo `json:"base"`
|
||||||
|
Head *PRBranchInfo `json:"head"`
|
||||||
|
MergeBase string `json:"merge_base"`
|
||||||
|
|
||||||
|
Deadline *time.Time `json:"due_date"`
|
||||||
|
Created *time.Time `json:"created_at"`
|
||||||
|
Updated *time.Time `json:"updated_at"`
|
||||||
|
Closed *time.Time `json:"closed_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangedFile is a changed file in a diff
|
||||||
|
type ChangedFile struct {
|
||||||
|
Filename string `json:"filename"`
|
||||||
|
PreviousFilename string `json:"previous_filename"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Additions int `json:"additions"`
|
||||||
|
Deletions int `json:"deletions"`
|
||||||
|
Changes int `json:"changes"`
|
||||||
|
HTMLURL string `json:"html_url"`
|
||||||
|
ContentsURL string `json:"contents_url"`
|
||||||
|
RawURL string `json:"raw_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPullRequestsOptions options for listing pull requests
|
||||||
|
type ListPullRequestsOptions struct {
|
||||||
|
ListOptions
|
||||||
|
State StateType `json:"state"`
|
||||||
|
// oldest, recentupdate, leastupdate, mostcomment, leastcomment, priority
|
||||||
|
Sort string
|
||||||
|
Milestone int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeStyle is used specify how a pull is merged
|
||||||
|
type MergeStyle string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MergeStyleMerge merge pull as usual
|
||||||
|
MergeStyleMerge MergeStyle = "merge"
|
||||||
|
// MergeStyleRebase rebase pull
|
||||||
|
MergeStyleRebase MergeStyle = "rebase"
|
||||||
|
// MergeStyleRebaseMerge rebase and merge pull
|
||||||
|
MergeStyleRebaseMerge MergeStyle = "rebase-merge"
|
||||||
|
// MergeStyleSquash squash and merge pull
|
||||||
|
MergeStyleSquash MergeStyle = "squash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// QueryEncode turns options into querystring argument
|
||||||
|
func (opt *ListPullRequestsOptions) QueryEncode() string {
|
||||||
|
query := opt.getURLQuery()
|
||||||
|
if len(opt.State) > 0 {
|
||||||
|
query.Add("state", string(opt.State))
|
||||||
|
}
|
||||||
|
if len(opt.Sort) > 0 {
|
||||||
|
query.Add("sort", opt.Sort)
|
||||||
|
}
|
||||||
|
if opt.Milestone > 0 {
|
||||||
|
query.Add("milestone", fmt.Sprintf("%d", opt.Milestone))
|
||||||
|
}
|
||||||
|
return query.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoPullRequests list PRs of one repository
|
||||||
|
func (c *Client) ListRepoPullRequests(owner, repo string, opt ListPullRequestsOptions) ([]*PullRequest, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
prs := make([]*PullRequest, 0, opt.PageSize)
|
||||||
|
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls", owner, repo))
|
||||||
|
link.RawQuery = opt.QueryEncode()
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &prs)
|
||||||
|
if c.checkServerVersionGreaterThanOrEqual(version1_14_0) != nil {
|
||||||
|
for i := range prs {
|
||||||
|
if err := fixPullHeadSha(c, prs[i]); err != nil {
|
||||||
|
return prs, resp, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prs, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPullRequest get information of one PR
|
||||||
|
func (c *Client) GetPullRequest(owner, repo string, index int64) (*PullRequest, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
pr := new(PullRequest)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d", owner, repo, index), nil, nil, pr)
|
||||||
|
if c.checkServerVersionGreaterThanOrEqual(version1_14_0) != nil {
|
||||||
|
if err := fixPullHeadSha(c, pr); err != nil {
|
||||||
|
return pr, resp, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pr, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePullRequestOption options when creating a pull request
|
||||||
|
type CreatePullRequestOption struct {
|
||||||
|
Head string `json:"head"`
|
||||||
|
Base string `json:"base"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
Assignee string `json:"assignee"`
|
||||||
|
Assignees []string `json:"assignees"`
|
||||||
|
Milestone int64 `json:"milestone"`
|
||||||
|
Labels []int64 `json:"labels"`
|
||||||
|
Deadline *time.Time `json:"due_date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePullRequest create pull request with options
|
||||||
|
func (c *Client) CreatePullRequest(owner, repo string, opt CreatePullRequestOption) (*PullRequest, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
pr := new(PullRequest)
|
||||||
|
resp, err := c.getParsedResponse("POST",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/pulls", owner, repo),
|
||||||
|
jsonHeader, bytes.NewReader(body), pr)
|
||||||
|
return pr, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditPullRequestOption options when modify pull request
|
||||||
|
type EditPullRequestOption struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
Base string `json:"base"`
|
||||||
|
Assignee string `json:"assignee"`
|
||||||
|
Assignees []string `json:"assignees"`
|
||||||
|
Milestone int64 `json:"milestone"`
|
||||||
|
Labels []int64 `json:"labels"`
|
||||||
|
State *StateType `json:"state"`
|
||||||
|
Deadline *time.Time `json:"due_date"`
|
||||||
|
RemoveDeadline *bool `json:"unset_due_date"`
|
||||||
|
AllowMaintainerEdit *bool `json:"allow_maintainer_edit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the EditPullRequestOption struct
|
||||||
|
func (opt EditPullRequestOption) Validate(c *Client) error {
|
||||||
|
if len(opt.Title) != 0 && len(strings.TrimSpace(opt.Title)) == 0 {
|
||||||
|
return fmt.Errorf("title is empty")
|
||||||
|
}
|
||||||
|
if len(opt.Base) != 0 {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return fmt.Errorf("can not change base gitea to old")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditPullRequest modify pull request with PR id and options
|
||||||
|
func (c *Client) EditPullRequest(owner, repo string, index int64, opt EditPullRequestOption) (*PullRequest, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(c); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
pr := new(PullRequest)
|
||||||
|
resp, err := c.getParsedResponse("PATCH",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/pulls/%d", owner, repo, index),
|
||||||
|
jsonHeader, bytes.NewReader(body), pr)
|
||||||
|
return pr, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergePullRequestOption options when merging a pull request
|
||||||
|
type MergePullRequestOption struct {
|
||||||
|
Style MergeStyle `json:"Do"`
|
||||||
|
MergeCommitID string `json:"MergeCommitID"`
|
||||||
|
Title string `json:"MergeTitleField"`
|
||||||
|
Message string `json:"MergeMessageField"`
|
||||||
|
DeleteBranchAfterMerge bool `json:"delete_branch_after_merge"`
|
||||||
|
ForceMerge bool `json:"force_merge"`
|
||||||
|
HeadCommitId string `json:"head_commit_id"`
|
||||||
|
MergeWhenChecksSucceed bool `json:"merge_when_checks_succeed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the MergePullRequestOption struct
|
||||||
|
func (opt MergePullRequestOption) Validate(c *Client) error {
|
||||||
|
if opt.Style == MergeStyleSquash {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_5); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergePullRequest merge a PR to repository by PR id
|
||||||
|
func (c *Client) MergePullRequest(owner, repo string, index int64, opt MergePullRequestOption) (bool, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(c); err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
status, resp, err := c.getStatusCode("POST", fmt.Sprintf("/repos/%s/%s/pulls/%d/merge", owner, repo, index), jsonHeader, bytes.NewReader(body))
|
||||||
|
if err != nil {
|
||||||
|
return false, resp, err
|
||||||
|
}
|
||||||
|
return status == 200, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPullRequestMerged test if one PR is merged to one repository
|
||||||
|
func (c *Client) IsPullRequestMerged(owner, repo string, index int64) (bool, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d/merge", owner, repo, index), nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return false, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return status == 204, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullRequestDiffOptions options for GET /repos/<owner>/<repo>/pulls/<idx>.[diff|patch]
|
||||||
|
type PullRequestDiffOptions struct {
|
||||||
|
// Include binary file changes when requesting a .diff
|
||||||
|
Binary bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryEncode converts the options to a query string
|
||||||
|
func (o PullRequestDiffOptions) QueryEncode() string {
|
||||||
|
query := make(url.Values)
|
||||||
|
query.Add("binary", fmt.Sprintf("%v", o.Binary))
|
||||||
|
return query.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
type pullRequestDiffType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
pullRequestDiffTypeDiff pullRequestDiffType = "diff"
|
||||||
|
pullRequestDiffTypePatch pullRequestDiffType = "patch"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getPullRequestDiffOrPatch gets the patch or diff file as bytes for a PR
|
||||||
|
func (c *Client) getPullRequestDiffOrPatch(owner, repo string, kind pullRequestDiffType, index int64, opts PullRequestDiffOptions) ([]byte, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||||
|
r, _, err2 := c.GetRepo(owner, repo)
|
||||||
|
if err2 != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if r.Private {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
url := fmt.Sprintf("/%s/%s/pulls/%d.%s?%s", owner, repo, index, kind, opts.QueryEncode())
|
||||||
|
return c.getWebResponse("GET", url, nil)
|
||||||
|
}
|
||||||
|
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d.%s", owner, repo, index, kind), nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPullRequestPatch gets the git patchset of a PR
|
||||||
|
func (c *Client) GetPullRequestPatch(owner, repo string, index int64) ([]byte, *Response, error) {
|
||||||
|
return c.getPullRequestDiffOrPatch(owner, repo, pullRequestDiffTypePatch, index, PullRequestDiffOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPullRequestDiff gets the diff of a PR. For Gitea >= 1.16, you must set includeBinary to get an applicable diff
|
||||||
|
func (c *Client) GetPullRequestDiff(owner, repo string, index int64, opts PullRequestDiffOptions) ([]byte, *Response, error) {
|
||||||
|
return c.getPullRequestDiffOrPatch(owner, repo, pullRequestDiffTypeDiff, index, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPullRequestCommitsOptions options for listing pull requests
|
||||||
|
type ListPullRequestCommitsOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPullRequestCommits list commits for a pull request
|
||||||
|
func (c *Client) ListPullRequestCommits(owner, repo string, index int64, opt ListPullRequestCommitsOptions) ([]*Commit, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls/%d/commits", owner, repo, index))
|
||||||
|
opt.setDefaults()
|
||||||
|
commits := make([]*Commit, 0, opt.PageSize)
|
||||||
|
link.RawQuery = opt.getURLQuery().Encode()
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &commits)
|
||||||
|
return commits, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixPullHeadSha is a workaround for https://github.com/go-gitea/gitea/issues/12675
|
||||||
|
// When no head sha is available, this is because the branch got deleted in the base repo.
|
||||||
|
// pr.Head.Ref points in this case not to the head repo branch name, but the base repo ref,
|
||||||
|
// which stays available to resolve the commit sha. This is fixed for gitea >= 1.14.0
|
||||||
|
func fixPullHeadSha(client *Client, pr *PullRequest) error {
|
||||||
|
if pr.Base != nil && pr.Base.Repository != nil && pr.Base.Repository.Owner != nil &&
|
||||||
|
pr.Head != nil && pr.Head.Ref != "" && pr.Head.Sha == "" {
|
||||||
|
owner := pr.Base.Repository.Owner.UserName
|
||||||
|
repo := pr.Base.Repository.Name
|
||||||
|
refs, _, err := client.GetRepoRefs(owner, repo, pr.Head.Ref)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if len(refs) == 0 {
|
||||||
|
return fmt.Errorf("unable to resolve PR ref '%s'", pr.Head.Ref)
|
||||||
|
}
|
||||||
|
pr.Head.Sha = refs[0].Object.SHA
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPullRequestFilesOptions options for listing pull request files
|
||||||
|
type ListPullRequestFilesOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPullRequestFiles list changed files for a pull request
|
||||||
|
func (c *Client) ListPullRequestFiles(owner, repo string, index int64, opt ListPullRequestFilesOptions) ([]*ChangedFile, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls/%d/files", owner, repo, index))
|
||||||
|
opt.setDefaults()
|
||||||
|
files := make([]*ChangedFile, 0, opt.PageSize)
|
||||||
|
link.RawQuery = opt.getURLQuery().Encode()
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &files)
|
||||||
|
return files, resp, err
|
||||||
|
}
|
||||||
325
vendor/code.gitea.io/sdk/gitea/pull_review.go
generated
vendored
Normal file
325
vendor/code.gitea.io/sdk/gitea/pull_review.go
generated
vendored
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReviewStateType review state type
|
||||||
|
type ReviewStateType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ReviewStateApproved pr is approved
|
||||||
|
ReviewStateApproved ReviewStateType = "APPROVED"
|
||||||
|
// ReviewStatePending pr state is pending
|
||||||
|
ReviewStatePending ReviewStateType = "PENDING"
|
||||||
|
// ReviewStateComment is a comment review
|
||||||
|
ReviewStateComment ReviewStateType = "COMMENT"
|
||||||
|
// ReviewStateRequestChanges changes for pr are requested
|
||||||
|
ReviewStateRequestChanges ReviewStateType = "REQUEST_CHANGES"
|
||||||
|
// ReviewStateRequestReview review is requested from user
|
||||||
|
ReviewStateRequestReview ReviewStateType = "REQUEST_REVIEW"
|
||||||
|
// ReviewStateUnknown state of pr is unknown
|
||||||
|
ReviewStateUnknown ReviewStateType = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
// PullReview represents a pull request review
|
||||||
|
type PullReview struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Reviewer *User `json:"user"`
|
||||||
|
ReviewerTeam *Team `json:"team"`
|
||||||
|
State ReviewStateType `json:"state"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
CommitID string `json:"commit_id"`
|
||||||
|
// Stale indicates if the pull has changed since the review
|
||||||
|
Stale bool `json:"stale"`
|
||||||
|
// Official indicates if the review counts towards the required approval limit, if PR base is a protected branch
|
||||||
|
Official bool `json:"official"`
|
||||||
|
Dismissed bool `json:"dismissed"`
|
||||||
|
CodeCommentsCount int `json:"comments_count"`
|
||||||
|
Submitted time.Time `json:"submitted_at"`
|
||||||
|
|
||||||
|
HTMLURL string `json:"html_url"`
|
||||||
|
HTMLPullURL string `json:"pull_request_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullReviewComment represents a comment on a pull request review
|
||||||
|
type PullReviewComment struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
Reviewer *User `json:"user"`
|
||||||
|
ReviewID int64 `json:"pull_request_review_id"`
|
||||||
|
Resolver *User `json:"resolver"`
|
||||||
|
|
||||||
|
Created time.Time `json:"created_at"`
|
||||||
|
Updated time.Time `json:"updated_at"`
|
||||||
|
|
||||||
|
Path string `json:"path"`
|
||||||
|
CommitID string `json:"commit_id"`
|
||||||
|
OrigCommitID string `json:"original_commit_id"`
|
||||||
|
DiffHunk string `json:"diff_hunk"`
|
||||||
|
LineNum uint64 `json:"position"`
|
||||||
|
OldLineNum uint64 `json:"original_position"`
|
||||||
|
|
||||||
|
HTMLURL string `json:"html_url"`
|
||||||
|
HTMLPullURL string `json:"pull_request_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePullReviewOptions are options to create a pull review
|
||||||
|
type CreatePullReviewOptions struct {
|
||||||
|
State ReviewStateType `json:"event"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
CommitID string `json:"commit_id"`
|
||||||
|
Comments []CreatePullReviewComment `json:"comments"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePullReviewComment represent a review comment for creation api
|
||||||
|
type CreatePullReviewComment struct {
|
||||||
|
// the tree path
|
||||||
|
Path string `json:"path"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
// if comment to old file line or 0
|
||||||
|
OldLineNum int64 `json:"old_position"`
|
||||||
|
// if comment to new file line or 0
|
||||||
|
NewLineNum int64 `json:"new_position"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitPullReviewOptions are options to submit a pending pull review
|
||||||
|
type SubmitPullReviewOptions struct {
|
||||||
|
State ReviewStateType `json:"event"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DismissPullReviewOptions are options to dismiss a pull review
|
||||||
|
type DismissPullReviewOptions struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullReviewRequestOptions are options to add or remove pull review requests
|
||||||
|
type PullReviewRequestOptions struct {
|
||||||
|
Reviewers []string `json:"reviewers"`
|
||||||
|
TeamReviewers []string `json:"team_reviewers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPullReviewsOptions options for listing PullReviews
|
||||||
|
type ListPullReviewsOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the CreatePullReviewOptions struct
|
||||||
|
func (opt CreatePullReviewOptions) Validate() error {
|
||||||
|
if opt.State != ReviewStateApproved && len(opt.Comments) == 0 && len(strings.TrimSpace(opt.Body)) == 0 {
|
||||||
|
return fmt.Errorf("body is empty")
|
||||||
|
}
|
||||||
|
for i := range opt.Comments {
|
||||||
|
if err := opt.Comments[i].Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the SubmitPullReviewOptions struct
|
||||||
|
func (opt SubmitPullReviewOptions) Validate() error {
|
||||||
|
if opt.State != ReviewStateApproved && len(strings.TrimSpace(opt.Body)) == 0 {
|
||||||
|
return fmt.Errorf("body is empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the CreatePullReviewComment struct
|
||||||
|
func (opt CreatePullReviewComment) Validate() error {
|
||||||
|
if len(strings.TrimSpace(opt.Body)) == 0 {
|
||||||
|
return fmt.Errorf("body is empty")
|
||||||
|
}
|
||||||
|
if opt.NewLineNum != 0 && opt.OldLineNum != 0 {
|
||||||
|
return fmt.Errorf("old and new line num are set, cant identify the code comment position")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPullReviews lists all reviews of a pull request
|
||||||
|
func (c *Client) ListPullReviews(owner, repo string, index int64, opt ListPullReviewsOptions) ([]*PullReview, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
rs := make([]*PullReview, 0, opt.PageSize)
|
||||||
|
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews", owner, repo, index))
|
||||||
|
link.RawQuery = opt.ListOptions.getURLQuery().Encode()
|
||||||
|
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &rs)
|
||||||
|
return rs, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPullReview gets a specific review of a pull request
|
||||||
|
func (c *Client) GetPullReview(owner, repo string, index, id int64) (*PullReview, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r := new(PullReview)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d", owner, repo, index, id), jsonHeader, nil, &r)
|
||||||
|
return r, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPullReviewComments lists all comments of a pull request review
|
||||||
|
func (c *Client) ListPullReviewComments(owner, repo string, index, id int64) ([]*PullReviewComment, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
rcl := make([]*PullReviewComment, 0, 4)
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d/comments", owner, repo, index, id))
|
||||||
|
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &rcl)
|
||||||
|
return rcl, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePullReview delete a specific review from a pull request
|
||||||
|
func (c *Client) DeletePullReview(owner, repo string, index, id int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d", owner, repo, index, id), jsonHeader, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePullReview create a review to an pull request
|
||||||
|
func (c *Client) CreatePullReview(owner, repo string, index int64, opt CreatePullReviewOptions) (*PullReview, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r := new(PullReview)
|
||||||
|
resp, err := c.getParsedResponse("POST",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews", owner, repo, index),
|
||||||
|
jsonHeader, bytes.NewReader(body), r)
|
||||||
|
return r, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitPullReview submit a pending review to an pull request
|
||||||
|
func (c *Client) SubmitPullReview(owner, repo string, index, id int64, opt SubmitPullReviewOptions) (*PullReview, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r := new(PullReview)
|
||||||
|
resp, err := c.getParsedResponse("POST",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d", owner, repo, index, id),
|
||||||
|
jsonHeader, bytes.NewReader(body), r)
|
||||||
|
return r, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateReviewRequests create review requests to an pull request
|
||||||
|
func (c *Client) CreateReviewRequests(owner, repo string, index int64, opt PullReviewRequestOptions) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, resp, err := c.getResponse("POST",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/pulls/%d/requested_reviewers", owner, repo, index),
|
||||||
|
jsonHeader, bytes.NewReader(body))
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteReviewRequests delete review requests to an pull request
|
||||||
|
func (c *Client) DeleteReviewRequests(owner, repo string, index int64, opt PullReviewRequestOptions) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, resp, err := c.getResponse("DELETE",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/pulls/%d/requested_reviewers", owner, repo, index),
|
||||||
|
jsonHeader, bytes.NewReader(body))
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DismissPullReview dismiss a review for a pull request
|
||||||
|
func (c *Client) DismissPullReview(owner, repo string, index, id int64, opt DismissPullReviewOptions) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, resp, err := c.getResponse("POST",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d/dismissals", owner, repo, index, id),
|
||||||
|
jsonHeader, bytes.NewReader(body))
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnDismissPullReview cancel to dismiss a review for a pull request
|
||||||
|
func (c *Client) UnDismissPullReview(owner, repo string, index, id int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, resp, err := c.getResponse("POST",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d/undismissals", owner, repo, index, id),
|
||||||
|
jsonHeader, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
202
vendor/code.gitea.io/sdk/gitea/release.go
generated
vendored
Normal file
202
vendor/code.gitea.io/sdk/gitea/release.go
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Release represents a repository release
|
||||||
|
type Release struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
TagName string `json:"tag_name"`
|
||||||
|
Target string `json:"target_commitish"`
|
||||||
|
Title string `json:"name"`
|
||||||
|
Note string `json:"body"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
HTMLURL string `json:"html_url"`
|
||||||
|
TarURL string `json:"tarball_url"`
|
||||||
|
ZipURL string `json:"zipball_url"`
|
||||||
|
IsDraft bool `json:"draft"`
|
||||||
|
IsPrerelease bool `json:"prerelease"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
PublishedAt time.Time `json:"published_at"`
|
||||||
|
Publisher *User `json:"author"`
|
||||||
|
Attachments []*Attachment `json:"assets"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListReleasesOptions options for listing repository's releases
|
||||||
|
type ListReleasesOptions struct {
|
||||||
|
ListOptions
|
||||||
|
IsDraft *bool
|
||||||
|
IsPreRelease *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryEncode turns options into querystring argument
|
||||||
|
func (opt *ListReleasesOptions) QueryEncode() string {
|
||||||
|
query := opt.getURLQuery()
|
||||||
|
|
||||||
|
if opt.IsDraft != nil {
|
||||||
|
query.Add("draft", fmt.Sprintf("%t", *opt.IsDraft))
|
||||||
|
}
|
||||||
|
if opt.IsPreRelease != nil {
|
||||||
|
query.Add("pre-release", fmt.Sprintf("%t", *opt.IsPreRelease))
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListReleases list releases of a repository
|
||||||
|
func (c *Client) ListReleases(owner, repo string, opt ListReleasesOptions) ([]*Release, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
releases := make([]*Release, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/releases?%s", owner, repo, opt.QueryEncode()),
|
||||||
|
nil, nil, &releases)
|
||||||
|
return releases, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRelease get a release of a repository by id
|
||||||
|
func (c *Client) GetRelease(owner, repo string, id int64) (*Release, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
r := new(Release)
|
||||||
|
resp, err := c.getParsedResponse("GET",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/releases/%d", owner, repo, id),
|
||||||
|
jsonHeader, nil, &r)
|
||||||
|
return r, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReleaseByTag get a release of a repository by tag
|
||||||
|
func (c *Client) GetReleaseByTag(owner, repo, tag string) (*Release, *Response, error) {
|
||||||
|
if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil {
|
||||||
|
return c.fallbackGetReleaseByTag(owner, repo, tag)
|
||||||
|
}
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo, &tag); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
r := new(Release)
|
||||||
|
resp, err := c.getParsedResponse("GET",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/releases/tags/%s", owner, repo, tag),
|
||||||
|
nil, nil, &r)
|
||||||
|
return r, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateReleaseOption options when creating a release
|
||||||
|
type CreateReleaseOption struct {
|
||||||
|
TagName string `json:"tag_name"`
|
||||||
|
Target string `json:"target_commitish"`
|
||||||
|
Title string `json:"name"`
|
||||||
|
Note string `json:"body"`
|
||||||
|
IsDraft bool `json:"draft"`
|
||||||
|
IsPrerelease bool `json:"prerelease"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the CreateReleaseOption struct
|
||||||
|
func (opt CreateReleaseOption) Validate() error {
|
||||||
|
if len(strings.TrimSpace(opt.Title)) == 0 {
|
||||||
|
return fmt.Errorf("title is empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRelease create a release
|
||||||
|
func (c *Client) CreateRelease(owner, repo string, opt CreateReleaseOption) (*Release, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
r := new(Release)
|
||||||
|
resp, err := c.getParsedResponse("POST",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/releases", owner, repo),
|
||||||
|
jsonHeader, bytes.NewReader(body), r)
|
||||||
|
return r, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditReleaseOption options when editing a release
|
||||||
|
type EditReleaseOption struct {
|
||||||
|
TagName string `json:"tag_name"`
|
||||||
|
Target string `json:"target_commitish"`
|
||||||
|
Title string `json:"name"`
|
||||||
|
Note string `json:"body"`
|
||||||
|
IsDraft *bool `json:"draft"`
|
||||||
|
IsPrerelease *bool `json:"prerelease"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditRelease edit a release
|
||||||
|
func (c *Client) EditRelease(owner, repo string, id int64, form EditReleaseOption) (*Release, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(form)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
r := new(Release)
|
||||||
|
resp, err := c.getParsedResponse("PATCH",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/releases/%d", owner, repo, id),
|
||||||
|
jsonHeader, bytes.NewReader(body), r)
|
||||||
|
return r, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRelease delete a release from a repository, keeping its tag
|
||||||
|
func (c *Client) DeleteRelease(user, repo string, id int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/releases/%d", user, repo, id),
|
||||||
|
nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteReleaseByTag deletes a release frm a repository by tag
|
||||||
|
func (c *Client) DeleteReleaseByTag(user, repo, tag string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &tag); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/releases/tags/%s", user, repo, tag),
|
||||||
|
nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallbackGetReleaseByTag is fallback for old gitea installations ( < 1.13.0 )
|
||||||
|
func (c *Client) fallbackGetReleaseByTag(owner, repo, tag string) (*Release, *Response, error) {
|
||||||
|
for i := 1; ; i++ {
|
||||||
|
rl, resp, err := c.ListReleases(owner, repo, ListReleasesOptions{ListOptions: ListOptions{Page: i}})
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if len(rl) == 0 {
|
||||||
|
return nil,
|
||||||
|
newResponse(&http.Response{StatusCode: 404}),
|
||||||
|
fmt.Errorf("release with tag '%s' not found", tag)
|
||||||
|
}
|
||||||
|
for _, r := range rl {
|
||||||
|
if r.TagName == tag {
|
||||||
|
return r, resp, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
546
vendor/code.gitea.io/sdk/gitea/repo.go
generated
vendored
Normal file
546
vendor/code.gitea.io/sdk/gitea/repo.go
generated
vendored
Normal file
@@ -0,0 +1,546 @@
|
|||||||
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Permission represents a set of permissions
|
||||||
|
type Permission struct {
|
||||||
|
Admin bool `json:"admin"`
|
||||||
|
Push bool `json:"push"`
|
||||||
|
Pull bool `json:"pull"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalTracker represents settings for internal tracker
|
||||||
|
type InternalTracker struct {
|
||||||
|
// Enable time tracking (Built-in issue tracker)
|
||||||
|
EnableTimeTracker bool `json:"enable_time_tracker"`
|
||||||
|
// Let only contributors track time (Built-in issue tracker)
|
||||||
|
AllowOnlyContributorsToTrackTime bool `json:"allow_only_contributors_to_track_time"`
|
||||||
|
// Enable dependencies for issues and pull requests (Built-in issue tracker)
|
||||||
|
EnableIssueDependencies bool `json:"enable_issue_dependencies"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalTracker represents settings for external tracker
|
||||||
|
type ExternalTracker struct {
|
||||||
|
// URL of external issue tracker.
|
||||||
|
ExternalTrackerURL string `json:"external_tracker_url"`
|
||||||
|
// External Issue Tracker URL Format. Use the placeholders {user}, {repo} and {index} for the username, repository name and issue index.
|
||||||
|
ExternalTrackerFormat string `json:"external_tracker_format"`
|
||||||
|
// External Issue Tracker Number Format, either `numeric` or `alphanumeric`
|
||||||
|
ExternalTrackerStyle string `json:"external_tracker_style"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalWiki represents setting for external wiki
|
||||||
|
type ExternalWiki struct {
|
||||||
|
// URL of external wiki.
|
||||||
|
ExternalWikiURL string `json:"external_wiki_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repository represents a repository
|
||||||
|
type Repository struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Owner *User `json:"owner"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Empty bool `json:"empty"`
|
||||||
|
Private bool `json:"private"`
|
||||||
|
Fork bool `json:"fork"`
|
||||||
|
Template bool `json:"template"`
|
||||||
|
Parent *Repository `json:"parent"`
|
||||||
|
Mirror bool `json:"mirror"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
HTMLURL string `json:"html_url"`
|
||||||
|
SSHURL string `json:"ssh_url"`
|
||||||
|
CloneURL string `json:"clone_url"`
|
||||||
|
OriginalURL string `json:"original_url"`
|
||||||
|
Website string `json:"website"`
|
||||||
|
Stars int `json:"stars_count"`
|
||||||
|
Forks int `json:"forks_count"`
|
||||||
|
Watchers int `json:"watchers_count"`
|
||||||
|
OpenIssues int `json:"open_issues_count"`
|
||||||
|
OpenPulls int `json:"open_pr_counter"`
|
||||||
|
Releases int `json:"release_counter"`
|
||||||
|
DefaultBranch string `json:"default_branch"`
|
||||||
|
Archived bool `json:"archived"`
|
||||||
|
Created time.Time `json:"created_at"`
|
||||||
|
Updated time.Time `json:"updated_at"`
|
||||||
|
Permissions *Permission `json:"permissions,omitempty"`
|
||||||
|
HasIssues bool `json:"has_issues"`
|
||||||
|
InternalTracker *InternalTracker `json:"internal_tracker,omitempty"`
|
||||||
|
ExternalTracker *ExternalTracker `json:"external_tracker,omitempty"`
|
||||||
|
HasWiki bool `json:"has_wiki"`
|
||||||
|
ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"`
|
||||||
|
HasPullRequests bool `json:"has_pull_requests"`
|
||||||
|
HasProjects bool `json:"has_projects"`
|
||||||
|
HasReleases bool `json:"has_releases,omitempty"`
|
||||||
|
HasPackages bool `json:"has_packages,omitempty"`
|
||||||
|
HasActions bool `json:"has_actions,omitempty"`
|
||||||
|
IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"`
|
||||||
|
AllowMerge bool `json:"allow_merge_commits"`
|
||||||
|
AllowRebase bool `json:"allow_rebase"`
|
||||||
|
AllowRebaseMerge bool `json:"allow_rebase_explicit"`
|
||||||
|
AllowSquash bool `json:"allow_squash_merge"`
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
Internal bool `json:"internal"`
|
||||||
|
MirrorInterval string `json:"mirror_interval"`
|
||||||
|
MirrorUpdated time.Time `json:"mirror_updated,omitempty"`
|
||||||
|
DefaultMergeStyle MergeStyle `json:"default_merge_style"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepoType represent repo type
|
||||||
|
type RepoType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// RepoTypeNone dont specify a type
|
||||||
|
RepoTypeNone RepoType = ""
|
||||||
|
// RepoTypeSource is the default repo type
|
||||||
|
RepoTypeSource RepoType = "source"
|
||||||
|
// RepoTypeFork is a repo witch was forked from an other one
|
||||||
|
RepoTypeFork RepoType = "fork"
|
||||||
|
// RepoTypeMirror represents an mirror repo
|
||||||
|
RepoTypeMirror RepoType = "mirror"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TrustModel represent how git signatures are handled in a repository
|
||||||
|
type TrustModel string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TrustModelDefault use TM set by global config
|
||||||
|
TrustModelDefault TrustModel = "default"
|
||||||
|
// TrustModelCollaborator gpg signature has to be owned by a repo collaborator
|
||||||
|
TrustModelCollaborator TrustModel = "collaborator"
|
||||||
|
// TrustModelCommitter gpg signature has to match committer
|
||||||
|
TrustModelCommitter TrustModel = "committer"
|
||||||
|
// TrustModelCollaboratorCommitter gpg signature has to match committer and owned by a repo collaborator
|
||||||
|
TrustModelCollaboratorCommitter TrustModel = "collaboratorcommitter"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListReposOptions options for listing repositories
|
||||||
|
type ListReposOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMyRepos lists all repositories for the authenticated user that has access to.
|
||||||
|
func (c *Client) ListMyRepos(opt ListReposOptions) ([]*Repository, *Response, error) {
|
||||||
|
opt.setDefaults()
|
||||||
|
repos := make([]*Repository, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/repos?%s", opt.getURLQuery().Encode()), nil, nil, &repos)
|
||||||
|
return repos, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUserRepos list all repositories of one user by user's name
|
||||||
|
func (c *Client) ListUserRepos(user string, opt ListReposOptions) ([]*Repository, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
repos := make([]*Repository, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/repos?%s", user, opt.getURLQuery().Encode()), nil, nil, &repos)
|
||||||
|
return repos, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOrgReposOptions options for a organization's repositories
|
||||||
|
type ListOrgReposOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOrgRepos list all repositories of one organization by organization's name
|
||||||
|
func (c *Client) ListOrgRepos(org string, opt ListOrgReposOptions) ([]*Repository, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
repos := make([]*Repository, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/repos?%s", org, opt.getURLQuery().Encode()), nil, nil, &repos)
|
||||||
|
return repos, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchRepoOptions options for searching repositories
|
||||||
|
type SearchRepoOptions struct {
|
||||||
|
ListOptions
|
||||||
|
|
||||||
|
// The keyword to query
|
||||||
|
Keyword string
|
||||||
|
// Limit search to repositories with keyword as topic
|
||||||
|
KeywordIsTopic bool
|
||||||
|
// Include search of keyword within repository description
|
||||||
|
KeywordInDescription bool
|
||||||
|
|
||||||
|
/*
|
||||||
|
User Filter
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Repo Owner
|
||||||
|
OwnerID int64
|
||||||
|
// Stared By UserID
|
||||||
|
StarredByUserID int64
|
||||||
|
|
||||||
|
/*
|
||||||
|
Repo Attributes
|
||||||
|
*/
|
||||||
|
|
||||||
|
// pubic, private or all repositories (defaults to all)
|
||||||
|
IsPrivate *bool
|
||||||
|
// archived, non-archived or all repositories (defaults to all)
|
||||||
|
IsArchived *bool
|
||||||
|
// Exclude template repos from search
|
||||||
|
ExcludeTemplate bool
|
||||||
|
// Filter by "fork", "source", "mirror"
|
||||||
|
Type RepoType
|
||||||
|
|
||||||
|
/*
|
||||||
|
Sort Filters
|
||||||
|
*/
|
||||||
|
|
||||||
|
// sort repos by attribute. Supported values are "alpha", "created", "updated", "size", and "id". Default is "alpha"
|
||||||
|
Sort string
|
||||||
|
// sort order, either "asc" (ascending) or "desc" (descending). Default is "asc", ignored if "sort" is not specified.
|
||||||
|
Order string
|
||||||
|
// Repo owner to prioritize in the results
|
||||||
|
PrioritizedByOwnerID int64
|
||||||
|
|
||||||
|
/*
|
||||||
|
Cover EdgeCases
|
||||||
|
*/
|
||||||
|
// if set all other options are ignored and this string is used as query
|
||||||
|
RawQuery string
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryEncode turns options into querystring argument
|
||||||
|
func (opt *SearchRepoOptions) QueryEncode() string {
|
||||||
|
query := opt.getURLQuery()
|
||||||
|
if opt.Keyword != "" {
|
||||||
|
query.Add("q", opt.Keyword)
|
||||||
|
}
|
||||||
|
if opt.KeywordIsTopic {
|
||||||
|
query.Add("topic", "true")
|
||||||
|
}
|
||||||
|
if opt.KeywordInDescription {
|
||||||
|
query.Add("includeDesc", "true")
|
||||||
|
}
|
||||||
|
|
||||||
|
// User Filter
|
||||||
|
if opt.OwnerID > 0 {
|
||||||
|
query.Add("uid", fmt.Sprintf("%d", opt.OwnerID))
|
||||||
|
query.Add("exclusive", "true")
|
||||||
|
}
|
||||||
|
if opt.StarredByUserID > 0 {
|
||||||
|
query.Add("starredBy", fmt.Sprintf("%d", opt.StarredByUserID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repo Attributes
|
||||||
|
if opt.IsPrivate != nil {
|
||||||
|
query.Add("is_private", fmt.Sprintf("%v", opt.IsPrivate))
|
||||||
|
}
|
||||||
|
if opt.IsArchived != nil {
|
||||||
|
query.Add("archived", fmt.Sprintf("%v", opt.IsArchived))
|
||||||
|
}
|
||||||
|
if opt.ExcludeTemplate {
|
||||||
|
query.Add("template", "false")
|
||||||
|
}
|
||||||
|
if len(opt.Type) != 0 {
|
||||||
|
query.Add("mode", string(opt.Type))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort Filters
|
||||||
|
if opt.Sort != "" {
|
||||||
|
query.Add("sort", opt.Sort)
|
||||||
|
}
|
||||||
|
if opt.PrioritizedByOwnerID > 0 {
|
||||||
|
query.Add("priority_owner_id", fmt.Sprintf("%d", opt.PrioritizedByOwnerID))
|
||||||
|
}
|
||||||
|
if opt.Order != "" {
|
||||||
|
query.Add("order", opt.Order)
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
type searchRepoResponse struct {
|
||||||
|
Repos []*Repository `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchRepos searches for repositories matching the given filters
|
||||||
|
func (c *Client) SearchRepos(opt SearchRepoOptions) ([]*Repository, *Response, error) {
|
||||||
|
opt.setDefaults()
|
||||||
|
repos := new(searchRepoResponse)
|
||||||
|
|
||||||
|
link, _ := url.Parse("/repos/search")
|
||||||
|
|
||||||
|
if len(opt.RawQuery) != 0 {
|
||||||
|
link.RawQuery = opt.RawQuery
|
||||||
|
} else {
|
||||||
|
link.RawQuery = opt.QueryEncode()
|
||||||
|
// IsPrivate only works on gitea >= 1.12.0
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil && opt.IsPrivate != nil {
|
||||||
|
if *opt.IsPrivate {
|
||||||
|
// private repos only not supported on gitea <= 1.11.x
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
newQuery := link.Query()
|
||||||
|
newQuery.Add("private", "false")
|
||||||
|
link.RawQuery = newQuery.Encode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &repos)
|
||||||
|
return repos.Repos, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRepoOption options when creating repository
|
||||||
|
type CreateRepoOption struct {
|
||||||
|
// Name of the repository to create
|
||||||
|
Name string `json:"name"`
|
||||||
|
// Description of the repository to create
|
||||||
|
Description string `json:"description"`
|
||||||
|
// Whether the repository is private
|
||||||
|
Private bool `json:"private"`
|
||||||
|
// Issue Label set to use
|
||||||
|
IssueLabels string `json:"issue_labels"`
|
||||||
|
// Whether the repository should be auto-intialized?
|
||||||
|
AutoInit bool `json:"auto_init"`
|
||||||
|
// Whether the repository is template
|
||||||
|
Template bool `json:"template"`
|
||||||
|
// Gitignores to use
|
||||||
|
Gitignores string `json:"gitignores"`
|
||||||
|
// License to use
|
||||||
|
License string `json:"license"`
|
||||||
|
// Readme of the repository to create
|
||||||
|
Readme string `json:"readme"`
|
||||||
|
// DefaultBranch of the repository (used when initializes and in template)
|
||||||
|
DefaultBranch string `json:"default_branch"`
|
||||||
|
// TrustModel of the repository
|
||||||
|
TrustModel TrustModel `json:"trust_model"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the CreateRepoOption struct
|
||||||
|
func (opt CreateRepoOption) Validate(c *Client) error {
|
||||||
|
if len(strings.TrimSpace(opt.Name)) == 0 {
|
||||||
|
return fmt.Errorf("name is empty")
|
||||||
|
}
|
||||||
|
if len(opt.Name) > 100 {
|
||||||
|
return fmt.Errorf("name has more than 100 chars")
|
||||||
|
}
|
||||||
|
if len(opt.Description) > 2048 {
|
||||||
|
return fmt.Errorf("description has more than 2048 chars")
|
||||||
|
}
|
||||||
|
if len(opt.DefaultBranch) > 100 {
|
||||||
|
return fmt.Errorf("default branch name has more than 100 chars")
|
||||||
|
}
|
||||||
|
if len(opt.TrustModel) != 0 {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRepo creates a repository for authenticated user.
|
||||||
|
func (c *Client) CreateRepo(opt CreateRepoOption) (*Repository, *Response, error) {
|
||||||
|
if err := opt.Validate(c); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
repo := new(Repository)
|
||||||
|
resp, err := c.getParsedResponse("POST", "/user/repos", jsonHeader, bytes.NewReader(body), repo)
|
||||||
|
return repo, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOrgRepo creates an organization repository for authenticated user.
|
||||||
|
func (c *Client) CreateOrgRepo(org string, opt CreateRepoOption) (*Repository, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&org); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(c); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
repo := new(Repository)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/org/%s/repos", org), jsonHeader, bytes.NewReader(body), repo)
|
||||||
|
return repo, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRepo returns information of a repository of given owner.
|
||||||
|
func (c *Client) GetRepo(owner, reponame string) (*Repository, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
repo := new(Repository)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s", owner, reponame), nil, nil, repo)
|
||||||
|
return repo, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRepoByID returns information of a repository by a giver repository ID.
|
||||||
|
func (c *Client) GetRepoByID(id int64) (*Repository, *Response, error) {
|
||||||
|
repo := new(Repository)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repositories/%d", id), nil, nil, repo)
|
||||||
|
return repo, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditRepoOption options when editing a repository's properties
|
||||||
|
type EditRepoOption struct {
|
||||||
|
// name of the repository
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
// a short description of the repository.
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
// a URL with more information about the repository.
|
||||||
|
Website *string `json:"website,omitempty"`
|
||||||
|
// either `true` to make the repository private or `false` to make it public.
|
||||||
|
// Note: you will get a 422 error if the organization restricts changing repository visibility to organization
|
||||||
|
// owners and a non-owner tries to change the value of private.
|
||||||
|
Private *bool `json:"private,omitempty"`
|
||||||
|
// either `true` to make this repository a template or `false` to make it a normal repository
|
||||||
|
Template *bool `json:"template,omitempty"`
|
||||||
|
// either `true` to enable issues for this repository or `false` to disable them.
|
||||||
|
HasIssues *bool `json:"has_issues,omitempty"`
|
||||||
|
// set this structure to configure internal issue tracker (requires has_issues)
|
||||||
|
InternalTracker *InternalTracker `json:"internal_tracker,omitempty"`
|
||||||
|
// set this structure to use external issue tracker (requires has_issues)
|
||||||
|
ExternalTracker *ExternalTracker `json:"external_tracker,omitempty"`
|
||||||
|
// either `true` to enable the wiki for this repository or `false` to disable it.
|
||||||
|
HasWiki *bool `json:"has_wiki,omitempty"`
|
||||||
|
// set this structure to use external wiki instead of internal (requires has_wiki)
|
||||||
|
ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"`
|
||||||
|
// sets the default branch for this repository.
|
||||||
|
DefaultBranch *string `json:"default_branch,omitempty"`
|
||||||
|
// either `true` to allow pull requests, or `false` to prevent pull request.
|
||||||
|
HasPullRequests *bool `json:"has_pull_requests,omitempty"`
|
||||||
|
// either `true` to enable project unit, or `false` to disable them.
|
||||||
|
HasProjects *bool `json:"has_projects,omitempty"`
|
||||||
|
// either `true` to enable release, or `false` to disable them.
|
||||||
|
HasReleases *bool `json:"has_releases,omitempty"`
|
||||||
|
// either `true` to enable packages, or `false` to disable them.
|
||||||
|
HasPackages *bool `json:"has_packages,omitempty"`
|
||||||
|
// either `true` to enable actions, or `false` to disable them.
|
||||||
|
HasActions *bool `json:"has_actions,omitempty"`
|
||||||
|
// either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. `has_pull_requests` must be `true`.
|
||||||
|
IgnoreWhitespaceConflicts *bool `json:"ignore_whitespace_conflicts,omitempty"`
|
||||||
|
// either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. `has_pull_requests` must be `true`.
|
||||||
|
AllowMerge *bool `json:"allow_merge_commits,omitempty"`
|
||||||
|
// either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `has_pull_requests` must be `true`.
|
||||||
|
AllowRebase *bool `json:"allow_rebase,omitempty"`
|
||||||
|
// either `true` to allow rebase with explicit merge commits (--no-ff), or `false` to prevent rebase with explicit merge commits. `has_pull_requests` must be `true`.
|
||||||
|
AllowRebaseMerge *bool `json:"allow_rebase_explicit,omitempty"`
|
||||||
|
// either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `has_pull_requests` must be `true`.
|
||||||
|
AllowSquash *bool `json:"allow_squash_merge,omitempty"`
|
||||||
|
// set to `true` to archive this repository.
|
||||||
|
Archived *bool `json:"archived,omitempty"`
|
||||||
|
// set to a string like `8h30m0s` to set the mirror interval time
|
||||||
|
MirrorInterval *string `json:"mirror_interval,omitempty"`
|
||||||
|
// either `true` to allow mark pr as merged manually, or `false` to prevent it. `has_pull_requests` must be `true`.
|
||||||
|
AllowManualMerge *bool `json:"allow_manual_merge,omitempty"`
|
||||||
|
// either `true` to enable AutodetectManualMerge, or `false` to prevent it. `has_pull_requests` must be `true`, Note: In some special cases, misjudgments can occur.
|
||||||
|
AutodetectManualMerge *bool `json:"autodetect_manual_merge,omitempty"`
|
||||||
|
// set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", or "squash". `has_pull_requests` must be `true`.
|
||||||
|
DefaultMergeStyle *MergeStyle `json:"default_merge_style,omitempty"`
|
||||||
|
// set to `true` to archive this repository.
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditRepo edit the properties of a repository
|
||||||
|
func (c *Client) EditRepo(owner, reponame string, opt EditRepoOption) (*Repository, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
repo := new(Repository)
|
||||||
|
resp, err := c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s", owner, reponame), jsonHeader, bytes.NewReader(body), repo)
|
||||||
|
return repo, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRepo deletes a repository of user or organization.
|
||||||
|
func (c *Client) DeleteRepo(owner, repo string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s", owner, repo), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MirrorSync adds a mirrored repository to the mirror sync queue.
|
||||||
|
func (c *Client) MirrorSync(owner, repo string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/mirror-sync", owner, repo), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRepoLanguages return language stats of a repo
|
||||||
|
func (c *Client) GetRepoLanguages(owner, repo string) (map[string]int64, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
langMap := make(map[string]int64)
|
||||||
|
|
||||||
|
data, resp, err := c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/languages", owner, repo), jsonHeader, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if err = json.Unmarshal(data, &langMap); err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return langMap, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArchiveType represent supported archive formats by gitea
|
||||||
|
type ArchiveType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ZipArchive represent zip format
|
||||||
|
ZipArchive ArchiveType = ".zip"
|
||||||
|
// TarGZArchive represent tar.gz format
|
||||||
|
TarGZArchive ArchiveType = ".tar.gz"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetArchive get an archive of a repository by git reference
|
||||||
|
// e.g.: ref -> master, 70b7c74b33, v1.2.1, ...
|
||||||
|
func (c *Client) GetArchive(owner, repo, ref string, ext ArchiveType) ([]byte, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
ref = pathEscapeSegments(ref)
|
||||||
|
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/archive/%s%s", owner, repo, ref, ext), nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetArchiveReader gets a `git archive` for a particular tree-ish git reference
|
||||||
|
// such as a branch name (`master`), a commit hash (`70b7c74b33`), a tag
|
||||||
|
// (`v1.2.1`). The archive is returned as a byte stream in a ReadCloser. It is
|
||||||
|
// the responsibility of the client to close the reader.
|
||||||
|
func (c *Client) GetArchiveReader(owner, repo, ref string, ext ArchiveType) (io.ReadCloser, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
ref = pathEscapeSegments(ref)
|
||||||
|
resp, err := c.doRequest("GET", fmt.Sprintf("/repos/%s/%s/archive/%s%s", owner, repo, ref, ext), nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := statusCodeToErr(resp); err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.Body, resp, nil
|
||||||
|
}
|
||||||
143
vendor/code.gitea.io/sdk/gitea/repo_branch.go
generated
vendored
Normal file
143
vendor/code.gitea.io/sdk/gitea/repo_branch.go
generated
vendored
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PayloadUser represents the author or committer of a commit
|
||||||
|
type PayloadUser struct {
|
||||||
|
// Full name of the commit author
|
||||||
|
Name string `json:"name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
UserName string `json:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PayloadCommit represents a commit
|
||||||
|
type PayloadCommit struct {
|
||||||
|
// sha1 hash of the commit
|
||||||
|
ID string `json:"id"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Author *PayloadUser `json:"author"`
|
||||||
|
Committer *PayloadUser `json:"committer"`
|
||||||
|
Verification *PayloadCommitVerification `json:"verification"`
|
||||||
|
Timestamp time.Time `json:"timestamp"`
|
||||||
|
Added []string `json:"added"`
|
||||||
|
Removed []string `json:"removed"`
|
||||||
|
Modified []string `json:"modified"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PayloadCommitVerification represents the GPG verification of a commit
|
||||||
|
type PayloadCommitVerification struct {
|
||||||
|
Verified bool `json:"verified"`
|
||||||
|
Reason string `json:"reason"`
|
||||||
|
Signature string `json:"signature"`
|
||||||
|
Payload string `json:"payload"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Branch represents a repository branch
|
||||||
|
type Branch struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Commit *PayloadCommit `json:"commit"`
|
||||||
|
Protected bool `json:"protected"`
|
||||||
|
RequiredApprovals int64 `json:"required_approvals"`
|
||||||
|
EnableStatusCheck bool `json:"enable_status_check"`
|
||||||
|
StatusCheckContexts []string `json:"status_check_contexts"`
|
||||||
|
UserCanPush bool `json:"user_can_push"`
|
||||||
|
UserCanMerge bool `json:"user_can_merge"`
|
||||||
|
EffectiveBranchProtectionName string `json:"effective_branch_protection_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoBranchesOptions options for listing a repository's branches
|
||||||
|
type ListRepoBranchesOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoBranches list all the branches of one repository
|
||||||
|
func (c *Client) ListRepoBranches(user, repo string, opt ListRepoBranchesOptions) ([]*Branch, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
branches := make([]*Branch, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &branches)
|
||||||
|
return branches, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRepoBranch get one branch's information of one repository
|
||||||
|
func (c *Client) GetRepoBranch(user, repo, branch string) (*Branch, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &branch); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
b := new(Branch)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches/%s", user, repo, branch), nil, nil, &b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return b, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRepoBranch delete a branch in a repository
|
||||||
|
func (c *Client) DeleteRepoBranch(user, repo, branch string) (bool, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &branch); err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
status, resp, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/branches/%s", user, repo, branch), nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return false, resp, err
|
||||||
|
}
|
||||||
|
return status == 204, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateBranchOption options when creating a branch in a repository
|
||||||
|
type CreateBranchOption struct {
|
||||||
|
// Name of the branch to create
|
||||||
|
BranchName string `json:"new_branch_name"`
|
||||||
|
// Name of the old branch to create from (optional)
|
||||||
|
OldBranchName string `json:"old_branch_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the CreateBranchOption struct
|
||||||
|
func (opt CreateBranchOption) Validate() error {
|
||||||
|
if len(opt.BranchName) == 0 {
|
||||||
|
return fmt.Errorf("BranchName is empty")
|
||||||
|
}
|
||||||
|
if len(opt.BranchName) > 100 {
|
||||||
|
return fmt.Errorf("BranchName to long")
|
||||||
|
}
|
||||||
|
if len(opt.OldBranchName) > 100 {
|
||||||
|
return fmt.Errorf("OldBranchName to long")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateBranch creates a branch for a user's repository
|
||||||
|
func (c *Client) CreateBranch(owner, repo string, opt CreateBranchOption) (*Branch, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
branch := new(Branch)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/branches", owner, repo), jsonHeader, bytes.NewReader(body), branch)
|
||||||
|
return branch, resp, err
|
||||||
|
}
|
||||||
173
vendor/code.gitea.io/sdk/gitea/repo_branch_protection.go
generated
vendored
Normal file
173
vendor/code.gitea.io/sdk/gitea/repo_branch_protection.go
generated
vendored
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BranchProtection represents a branch protection for a repository
|
||||||
|
type BranchProtection struct {
|
||||||
|
BranchName string `json:"branch_name"`
|
||||||
|
RuleName string `json:"rule_name"`
|
||||||
|
EnablePush bool `json:"enable_push"`
|
||||||
|
EnablePushWhitelist bool `json:"enable_push_whitelist"`
|
||||||
|
PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
|
||||||
|
PushWhitelistTeams []string `json:"push_whitelist_teams"`
|
||||||
|
PushWhitelistDeployKeys bool `json:"push_whitelist_deploy_keys"`
|
||||||
|
EnableMergeWhitelist bool `json:"enable_merge_whitelist"`
|
||||||
|
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
|
||||||
|
MergeWhitelistTeams []string `json:"merge_whitelist_teams"`
|
||||||
|
EnableStatusCheck bool `json:"enable_status_check"`
|
||||||
|
StatusCheckContexts []string `json:"status_check_contexts"`
|
||||||
|
RequiredApprovals int64 `json:"required_approvals"`
|
||||||
|
EnableApprovalsWhitelist bool `json:"enable_approvals_whitelist"`
|
||||||
|
ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
|
||||||
|
ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"`
|
||||||
|
BlockOnRejectedReviews bool `json:"block_on_rejected_reviews"`
|
||||||
|
BlockOnOfficialReviewRequests bool `json:"block_on_official_review_requests"`
|
||||||
|
BlockOnOutdatedBranch bool `json:"block_on_outdated_branch"`
|
||||||
|
DismissStaleApprovals bool `json:"dismiss_stale_approvals"`
|
||||||
|
RequireSignedCommits bool `json:"require_signed_commits"`
|
||||||
|
ProtectedFilePatterns string `json:"protected_file_patterns"`
|
||||||
|
UnprotectedFilePatterns string `json:"unprotected_file_patterns"`
|
||||||
|
Created time.Time `json:"created_at"`
|
||||||
|
Updated time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateBranchProtectionOption options for creating a branch protection
|
||||||
|
type CreateBranchProtectionOption struct {
|
||||||
|
BranchName string `json:"branch_name"`
|
||||||
|
RuleName string `json:"rule_name"`
|
||||||
|
EnablePush bool `json:"enable_push"`
|
||||||
|
EnablePushWhitelist bool `json:"enable_push_whitelist"`
|
||||||
|
PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
|
||||||
|
PushWhitelistTeams []string `json:"push_whitelist_teams"`
|
||||||
|
PushWhitelistDeployKeys bool `json:"push_whitelist_deploy_keys"`
|
||||||
|
EnableMergeWhitelist bool `json:"enable_merge_whitelist"`
|
||||||
|
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
|
||||||
|
MergeWhitelistTeams []string `json:"merge_whitelist_teams"`
|
||||||
|
EnableStatusCheck bool `json:"enable_status_check"`
|
||||||
|
StatusCheckContexts []string `json:"status_check_contexts"`
|
||||||
|
RequiredApprovals int64 `json:"required_approvals"`
|
||||||
|
EnableApprovalsWhitelist bool `json:"enable_approvals_whitelist"`
|
||||||
|
ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
|
||||||
|
ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"`
|
||||||
|
BlockOnRejectedReviews bool `json:"block_on_rejected_reviews"`
|
||||||
|
BlockOnOfficialReviewRequests bool `json:"block_on_official_review_requests"`
|
||||||
|
BlockOnOutdatedBranch bool `json:"block_on_outdated_branch"`
|
||||||
|
DismissStaleApprovals bool `json:"dismiss_stale_approvals"`
|
||||||
|
RequireSignedCommits bool `json:"require_signed_commits"`
|
||||||
|
ProtectedFilePatterns string `json:"protected_file_patterns"`
|
||||||
|
UnprotectedFilePatterns string `json:"unprotected_file_patterns"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditBranchProtectionOption options for editing a branch protection
|
||||||
|
type EditBranchProtectionOption struct {
|
||||||
|
EnablePush *bool `json:"enable_push"`
|
||||||
|
EnablePushWhitelist *bool `json:"enable_push_whitelist"`
|
||||||
|
PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
|
||||||
|
PushWhitelistTeams []string `json:"push_whitelist_teams"`
|
||||||
|
PushWhitelistDeployKeys *bool `json:"push_whitelist_deploy_keys"`
|
||||||
|
EnableMergeWhitelist *bool `json:"enable_merge_whitelist"`
|
||||||
|
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
|
||||||
|
MergeWhitelistTeams []string `json:"merge_whitelist_teams"`
|
||||||
|
EnableStatusCheck *bool `json:"enable_status_check"`
|
||||||
|
StatusCheckContexts []string `json:"status_check_contexts"`
|
||||||
|
RequiredApprovals *int64 `json:"required_approvals"`
|
||||||
|
EnableApprovalsWhitelist *bool `json:"enable_approvals_whitelist"`
|
||||||
|
ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
|
||||||
|
ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"`
|
||||||
|
BlockOnRejectedReviews *bool `json:"block_on_rejected_reviews"`
|
||||||
|
BlockOnOfficialReviewRequests *bool `json:"block_on_official_review_requests"`
|
||||||
|
BlockOnOutdatedBranch *bool `json:"block_on_outdated_branch"`
|
||||||
|
DismissStaleApprovals *bool `json:"dismiss_stale_approvals"`
|
||||||
|
RequireSignedCommits *bool `json:"require_signed_commits"`
|
||||||
|
ProtectedFilePatterns *string `json:"protected_file_patterns"`
|
||||||
|
UnprotectedFilePatterns *string `json:"unprotected_file_patterns"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListBranchProtectionsOptions list branch protection options
|
||||||
|
type ListBranchProtectionsOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListBranchProtections list branch protections for a repo
|
||||||
|
func (c *Client) ListBranchProtections(owner, repo string, opt ListBranchProtectionsOptions) ([]*BranchProtection, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
bps := make([]*BranchProtection, 0, opt.PageSize)
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/branch_protections", owner, repo))
|
||||||
|
link.RawQuery = opt.getURLQuery().Encode()
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &bps)
|
||||||
|
return bps, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBranchProtection gets a branch protection
|
||||||
|
func (c *Client) GetBranchProtection(owner, repo, name string) (*BranchProtection, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
bp := new(BranchProtection)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branch_protections/%s", owner, repo, name), jsonHeader, nil, bp)
|
||||||
|
return bp, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateBranchProtection creates a branch protection for a repo
|
||||||
|
func (c *Client) CreateBranchProtection(owner, repo string, opt CreateBranchProtectionOption) (*BranchProtection, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
bp := new(BranchProtection)
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/branch_protections", owner, repo), jsonHeader, bytes.NewReader(body), bp)
|
||||||
|
return bp, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditBranchProtection edits a branch protection for a repo
|
||||||
|
func (c *Client) EditBranchProtection(owner, repo, name string, opt EditBranchProtectionOption) (*BranchProtection, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
bp := new(BranchProtection)
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
resp, err := c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/branch_protections/%s", owner, repo, name), jsonHeader, bytes.NewReader(body), bp)
|
||||||
|
return bp, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteBranchProtection deletes a branch protection for a repo
|
||||||
|
func (c *Client) DeleteBranchProtection(owner, repo, name string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/branch_protections/%s", owner, repo, name), jsonHeader, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
163
vendor/code.gitea.io/sdk/gitea/repo_collaborator.go
generated
vendored
Normal file
163
vendor/code.gitea.io/sdk/gitea/repo_collaborator.go
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListCollaboratorsOptions options for listing a repository's collaborators
|
||||||
|
type ListCollaboratorsOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// CollaboratorPermissionResult result type for CollaboratorPermission
|
||||||
|
type CollaboratorPermissionResult struct {
|
||||||
|
Permission AccessMode `json:"permission"`
|
||||||
|
Role string `json:"role_name"`
|
||||||
|
User *User `json:"user"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListCollaborators list a repository's collaborators
|
||||||
|
func (c *Client) ListCollaborators(user, repo string, opt ListCollaboratorsOptions) ([]*User, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
collaborators := make([]*User, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/collaborators?%s", user, repo, opt.getURLQuery().Encode()),
|
||||||
|
nil, nil, &collaborators)
|
||||||
|
return collaborators, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCollaborator check if a user is a collaborator of a repository
|
||||||
|
func (c *Client) IsCollaborator(user, repo, collaborator string) (bool, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &collaborator); err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return false, resp, err
|
||||||
|
}
|
||||||
|
if status == 204 {
|
||||||
|
return true, resp, nil
|
||||||
|
}
|
||||||
|
return false, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CollaboratorPermission gets collaborator permission of a repository
|
||||||
|
func (c *Client) CollaboratorPermission(user, repo, collaborator string) (*CollaboratorPermissionResult, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &collaborator); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
rv := new(CollaboratorPermissionResult)
|
||||||
|
resp, err := c.getParsedResponse("GET",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/collaborators/%s/permission", user, repo, collaborator),
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
rv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
rv = nil
|
||||||
|
}
|
||||||
|
return rv, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCollaboratorOption options when adding a user as a collaborator of a repository
|
||||||
|
type AddCollaboratorOption struct {
|
||||||
|
Permission *AccessMode `json:"permission"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccessMode represent the grade of access you have to something
|
||||||
|
type AccessMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AccessModeNone no access
|
||||||
|
AccessModeNone AccessMode = "none"
|
||||||
|
// AccessModeRead read access
|
||||||
|
AccessModeRead AccessMode = "read"
|
||||||
|
// AccessModeWrite write access
|
||||||
|
AccessModeWrite AccessMode = "write"
|
||||||
|
// AccessModeAdmin admin access
|
||||||
|
AccessModeAdmin AccessMode = "admin"
|
||||||
|
// AccessModeOwner owner
|
||||||
|
AccessModeOwner AccessMode = "owner"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validate the AddCollaboratorOption struct
|
||||||
|
func (opt *AddCollaboratorOption) Validate() error {
|
||||||
|
if opt.Permission != nil {
|
||||||
|
if *opt.Permission == AccessModeOwner {
|
||||||
|
*opt.Permission = AccessModeAdmin
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if *opt.Permission == AccessModeNone {
|
||||||
|
opt.Permission = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if *opt.Permission != AccessModeRead && *opt.Permission != AccessModeWrite && *opt.Permission != AccessModeAdmin {
|
||||||
|
return fmt.Errorf("permission mode invalid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCollaborator add some user as a collaborator of a repository
|
||||||
|
func (c *Client) AddCollaborator(user, repo, collaborator string, opt AddCollaboratorOption) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &collaborator); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := (&opt).Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), jsonHeader, bytes.NewReader(body))
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteCollaborator remove a collaborator from a repository
|
||||||
|
func (c *Client) DeleteCollaborator(user, repo, collaborator string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &collaborator); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReviewers return all users that can be requested to review in this repo
|
||||||
|
func (c *Client) GetReviewers(user, repo string) ([]*User, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
reviewers := make([]*User, 0, 5)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/reviewers", user, repo), nil, nil, &reviewers)
|
||||||
|
return reviewers, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAssignees return all users that have write access and can be assigned to issues
|
||||||
|
func (c *Client) GetAssignees(user, repo string) ([]*User, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
assignees := make([]*User, 0, 5)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/assignees", user, repo), nil, nil, &assignees)
|
||||||
|
return assignees, resp, err
|
||||||
|
}
|
||||||
141
vendor/code.gitea.io/sdk/gitea/repo_commit.go
generated
vendored
Normal file
141
vendor/code.gitea.io/sdk/gitea/repo_commit.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
// Copyright 2018 The Gogs Authors. All rights reserved.
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Identity for a person's identity like an author or committer
|
||||||
|
type Identity struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommitMeta contains meta information of a commit in terms of API.
|
||||||
|
type CommitMeta struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
SHA string `json:"sha"`
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommitUser contains information of a user in the context of a commit.
|
||||||
|
type CommitUser struct {
|
||||||
|
Identity
|
||||||
|
Date string `json:"date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepoCommit contains information of a commit in the context of a repository.
|
||||||
|
type RepoCommit struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
Author *CommitUser `json:"author"`
|
||||||
|
Committer *CommitUser `json:"committer"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Tree *CommitMeta `json:"tree"`
|
||||||
|
Verification *PayloadCommitVerification `json:"verification"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommitStats contains stats from a Git commit
|
||||||
|
type CommitStats struct {
|
||||||
|
Total int `json:"total"`
|
||||||
|
Additions int `json:"additions"`
|
||||||
|
Deletions int `json:"deletions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit contains information generated from a Git commit.
|
||||||
|
type Commit struct {
|
||||||
|
*CommitMeta
|
||||||
|
HTMLURL string `json:"html_url"`
|
||||||
|
RepoCommit *RepoCommit `json:"commit"`
|
||||||
|
Author *User `json:"author"`
|
||||||
|
Committer *User `json:"committer"`
|
||||||
|
Parents []*CommitMeta `json:"parents"`
|
||||||
|
Files []*CommitAffectedFiles `json:"files"`
|
||||||
|
Stats *CommitStats `json:"stats"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE
|
||||||
|
type CommitDateOptions struct {
|
||||||
|
Author time.Time `json:"author"`
|
||||||
|
Committer time.Time `json:"committer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommitAffectedFiles store information about files affected by the commit
|
||||||
|
type CommitAffectedFiles struct {
|
||||||
|
Filename string `json:"filename"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSingleCommit returns a single commit
|
||||||
|
func (c *Client) GetSingleCommit(user, repo, commitID string) (*Commit, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &commitID); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
commit := new(Commit)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/commits/%s", user, repo, commitID), nil, nil, &commit)
|
||||||
|
return commit, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListCommitOptions list commit options
|
||||||
|
type ListCommitOptions struct {
|
||||||
|
ListOptions
|
||||||
|
// SHA or branch to start listing commits from (usually 'master')
|
||||||
|
SHA string
|
||||||
|
// Path indicates that only commits that include the path's file/dir should be returned.
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryEncode turns options into querystring argument
|
||||||
|
func (opt *ListCommitOptions) QueryEncode() string {
|
||||||
|
query := opt.getURLQuery()
|
||||||
|
if opt.SHA != "" {
|
||||||
|
query.Add("sha", opt.SHA)
|
||||||
|
}
|
||||||
|
if opt.Path != "" {
|
||||||
|
query.Add("path", opt.Path)
|
||||||
|
}
|
||||||
|
return query.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoCommits return list of commits from a repo
|
||||||
|
func (c *Client) ListRepoCommits(user, repo string, opt ListCommitOptions) ([]*Commit, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/commits", user, repo))
|
||||||
|
opt.setDefaults()
|
||||||
|
commits := make([]*Commit, 0, opt.PageSize)
|
||||||
|
link.RawQuery = opt.QueryEncode()
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &commits)
|
||||||
|
return commits, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCommitDiff returns the commit's raw diff.
|
||||||
|
func (c *Client) GetCommitDiff(user, repo, commitID string) ([]byte, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_16_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/git/commits/%s.%s", user, repo, commitID, pullRequestDiffTypeDiff), nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCommitPatch returns the commit's raw patch.
|
||||||
|
func (c *Client) GetCommitPatch(user, repo, commitID string) ([]byte, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_16_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/git/commits/%s.%s", user, repo, commitID, pullRequestDiffTypePatch), nil, nil)
|
||||||
|
}
|
||||||
277
vendor/code.gitea.io/sdk/gitea/repo_file.go
generated
vendored
Normal file
277
vendor/code.gitea.io/sdk/gitea/repo_file.go
generated
vendored
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileOptions options for all file APIs
|
||||||
|
type FileOptions struct {
|
||||||
|
// message (optional) for the commit of this file. if not supplied, a default message will be used
|
||||||
|
Message string `json:"message"`
|
||||||
|
// branch (optional) to base this file from. if not given, the default branch is used
|
||||||
|
BranchName string `json:"branch"`
|
||||||
|
// new_branch (optional) will make a new branch from `branch` before creating the file
|
||||||
|
NewBranchName string `json:"new_branch"`
|
||||||
|
// `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
|
||||||
|
Author Identity `json:"author"`
|
||||||
|
Committer Identity `json:"committer"`
|
||||||
|
Dates CommitDateOptions `json:"dates"`
|
||||||
|
// Add a Signed-off-by trailer by the committer at the end of the commit log message.
|
||||||
|
Signoff bool `json:"signoff"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateFileOptions options for creating files
|
||||||
|
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
|
||||||
|
type CreateFileOptions struct {
|
||||||
|
FileOptions
|
||||||
|
// content must be base64 encoded
|
||||||
|
// required: true
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteFileOptions options for deleting files (used for other File structs below)
|
||||||
|
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
|
||||||
|
type DeleteFileOptions struct {
|
||||||
|
FileOptions
|
||||||
|
// sha is the SHA for the file that already exists
|
||||||
|
// required: true
|
||||||
|
SHA string `json:"sha"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateFileOptions options for updating files
|
||||||
|
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
|
||||||
|
type UpdateFileOptions struct {
|
||||||
|
FileOptions
|
||||||
|
// sha is the SHA for the file that already exists
|
||||||
|
// required: true
|
||||||
|
SHA string `json:"sha"`
|
||||||
|
// content must be base64 encoded
|
||||||
|
// required: true
|
||||||
|
Content string `json:"content"`
|
||||||
|
// from_path (optional) is the path of the original file which will be moved/renamed to the path in the URL
|
||||||
|
FromPath string `json:"from_path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileLinksResponse contains the links for a repo's file
|
||||||
|
type FileLinksResponse struct {
|
||||||
|
Self *string `json:"self"`
|
||||||
|
GitURL *string `json:"git"`
|
||||||
|
HTMLURL *string `json:"html"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content
|
||||||
|
type ContentsResponse struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
SHA string `json:"sha"`
|
||||||
|
// `type` will be `file`, `dir`, `symlink`, or `submodule`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
// `encoding` is populated when `type` is `file`, otherwise null
|
||||||
|
Encoding *string `json:"encoding"`
|
||||||
|
// `content` is populated when `type` is `file`, otherwise null
|
||||||
|
Content *string `json:"content"`
|
||||||
|
// `target` is populated when `type` is `symlink`, otherwise null
|
||||||
|
Target *string `json:"target"`
|
||||||
|
URL *string `json:"url"`
|
||||||
|
HTMLURL *string `json:"html_url"`
|
||||||
|
GitURL *string `json:"git_url"`
|
||||||
|
DownloadURL *string `json:"download_url"`
|
||||||
|
// `submodule_git_url` is populated when `type` is `submodule`, otherwise null
|
||||||
|
SubmoduleGitURL *string `json:"submodule_git_url"`
|
||||||
|
Links *FileLinksResponse `json:"_links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileCommitResponse contains information generated from a Git commit for a repo's file.
|
||||||
|
type FileCommitResponse struct {
|
||||||
|
CommitMeta
|
||||||
|
HTMLURL string `json:"html_url"`
|
||||||
|
Author *CommitUser `json:"author"`
|
||||||
|
Committer *CommitUser `json:"committer"`
|
||||||
|
Parents []*CommitMeta `json:"parents"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Tree *CommitMeta `json:"tree"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileResponse contains information about a repo's file
|
||||||
|
type FileResponse struct {
|
||||||
|
Content *ContentsResponse `json:"content"`
|
||||||
|
Commit *FileCommitResponse `json:"commit"`
|
||||||
|
Verification *PayloadCommitVerification `json:"verification"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileDeleteResponse contains information about a repo's file that was deleted
|
||||||
|
type FileDeleteResponse struct {
|
||||||
|
Content interface{} `json:"content"` // to be set to nil
|
||||||
|
Commit *FileCommitResponse `json:"commit"`
|
||||||
|
Verification *PayloadCommitVerification `json:"verification"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFile downloads a file of repository, ref can be branch/tag/commit.
|
||||||
|
// it optional can resolve lfs pointers and server the file instead
|
||||||
|
// e.g.: ref -> master, filepath -> README.md (no leading slash)
|
||||||
|
func (c *Client) GetFile(owner, repo, ref, filepath string, resolveLFS ...bool) ([]byte, *Response, error) {
|
||||||
|
reader, resp, err := c.GetFileReader(owner, repo, ref, filepath, resolveLFS...)
|
||||||
|
if reader == nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
defer reader.Close()
|
||||||
|
|
||||||
|
data, err2 := io.ReadAll(reader)
|
||||||
|
if err2 != nil {
|
||||||
|
return nil, resp, err2
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileReader return reader for download a file of repository, ref can be branch/tag/commit.
|
||||||
|
// it optional can resolve lfs pointers and server the file instead
|
||||||
|
// e.g.: ref -> master, filepath -> README.md (no leading slash)
|
||||||
|
func (c *Client) GetFileReader(owner, repo, ref, filepath string, resolveLFS ...bool) (io.ReadCloser, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve lfs
|
||||||
|
if len(resolveLFS) != 0 && resolveLFS[0] {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_17_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return c.getResponseReader("GET", fmt.Sprintf("/repos/%s/%s/media/%s?ref=%s", owner, repo, filepath, url.QueryEscape(ref)), nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// normal get
|
||||||
|
filepath = pathEscapeSegments(filepath)
|
||||||
|
if c.checkServerVersionGreaterThanOrEqual(version1_14_0) != nil {
|
||||||
|
ref = pathEscapeSegments(ref)
|
||||||
|
return c.getResponseReader("GET", fmt.Sprintf("/repos/%s/%s/raw/%s/%s", owner, repo, ref, filepath), nil, nil)
|
||||||
|
}
|
||||||
|
return c.getResponseReader("GET", fmt.Sprintf("/repos/%s/%s/raw/%s?ref=%s", owner, repo, filepath, url.QueryEscape(ref)), nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContents get the metadata and contents of a file in a repository
|
||||||
|
// ref is optional
|
||||||
|
func (c *Client) GetContents(owner, repo, ref, filepath string) (*ContentsResponse, *Response, error) {
|
||||||
|
data, resp, err := c.getDirOrFileContents(owner, repo, ref, filepath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
cr := new(ContentsResponse)
|
||||||
|
if json.Unmarshal(data, &cr) != nil {
|
||||||
|
return nil, resp, fmt.Errorf("expect file, got directory")
|
||||||
|
}
|
||||||
|
return cr, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListContents gets a list of entries in a dir
|
||||||
|
// ref is optional
|
||||||
|
func (c *Client) ListContents(owner, repo, ref, filepath string) ([]*ContentsResponse, *Response, error) {
|
||||||
|
data, resp, err := c.getDirOrFileContents(owner, repo, ref, filepath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
crl := make([]*ContentsResponse, 0)
|
||||||
|
if json.Unmarshal(data, &crl) != nil {
|
||||||
|
return nil, resp, fmt.Errorf("expect directory, got file")
|
||||||
|
}
|
||||||
|
return crl, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getDirOrFileContents(owner, repo, ref, filepath string) ([]byte, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
filepath = pathEscapeSegments(strings.TrimPrefix(filepath, "/"))
|
||||||
|
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/contents/%s?ref=%s", owner, repo, filepath, url.QueryEscape(ref)), jsonHeader, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateFile create a file in a repository
|
||||||
|
func (c *Client) CreateFile(owner, repo, filepath string, opt CreateFileOptions) (*FileResponse, *Response, error) {
|
||||||
|
var err error
|
||||||
|
if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
filepath = pathEscapeSegments(filepath)
|
||||||
|
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
fr := new(FileResponse)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/contents/%s", owner, repo, filepath), jsonHeader, bytes.NewReader(body), fr)
|
||||||
|
return fr, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateFile update a file in a repository
|
||||||
|
func (c *Client) UpdateFile(owner, repo, filepath string, opt UpdateFileOptions) (*FileResponse, *Response, error) {
|
||||||
|
var err error
|
||||||
|
if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
filepath = pathEscapeSegments(filepath)
|
||||||
|
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
fr := new(FileResponse)
|
||||||
|
resp, err := c.getParsedResponse("PUT", fmt.Sprintf("/repos/%s/%s/contents/%s", owner, repo, filepath), jsonHeader, bytes.NewReader(body), fr)
|
||||||
|
return fr, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteFile delete a file from repository
|
||||||
|
func (c *Client) DeleteFile(owner, repo, filepath string, opt DeleteFileOptions) (*Response, error) {
|
||||||
|
var err error
|
||||||
|
if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
filepath = pathEscapeSegments(filepath)
|
||||||
|
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
status, resp, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/contents/%s", owner, repo, filepath), jsonHeader, bytes.NewReader(body))
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
if status != 200 && status != 204 {
|
||||||
|
return resp, fmt.Errorf("unexpected Status: %d", status)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) setDefaultBranchForOldVersions(owner, repo, branch string) (string, error) {
|
||||||
|
if len(branch) == 0 {
|
||||||
|
// Gitea >= 1.12.0 Use DefaultBranch on "", mimic this for older versions
|
||||||
|
if c.checkServerVersionGreaterThanOrEqual(version1_12_0) != nil {
|
||||||
|
r, _, err := c.GetRepo(owner, repo)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return r.DefaultBranch, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return branch, nil
|
||||||
|
}
|
||||||
91
vendor/code.gitea.io/sdk/gitea/repo_key.go
generated
vendored
Normal file
91
vendor/code.gitea.io/sdk/gitea/repo_key.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeployKey a deploy key
|
||||||
|
type DeployKey struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
KeyID int64 `json:"key_id"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Fingerprint string `json:"fingerprint"`
|
||||||
|
Created time.Time `json:"created_at"`
|
||||||
|
ReadOnly bool `json:"read_only"`
|
||||||
|
Repository *Repository `json:"repository,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListDeployKeysOptions options for listing a repository's deploy keys
|
||||||
|
type ListDeployKeysOptions struct {
|
||||||
|
ListOptions
|
||||||
|
KeyID int64
|
||||||
|
Fingerprint string
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryEncode turns options into querystring argument
|
||||||
|
func (opt *ListDeployKeysOptions) QueryEncode() string {
|
||||||
|
query := opt.getURLQuery()
|
||||||
|
if opt.KeyID > 0 {
|
||||||
|
query.Add("key_id", fmt.Sprintf("%d", opt.KeyID))
|
||||||
|
}
|
||||||
|
if len(opt.Fingerprint) > 0 {
|
||||||
|
query.Add("fingerprint", opt.Fingerprint)
|
||||||
|
}
|
||||||
|
return query.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListDeployKeys list all the deploy keys of one repository
|
||||||
|
func (c *Client) ListDeployKeys(user, repo string, opt ListDeployKeysOptions) ([]*DeployKey, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/keys", user, repo))
|
||||||
|
opt.setDefaults()
|
||||||
|
link.RawQuery = opt.QueryEncode()
|
||||||
|
keys := make([]*DeployKey, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &keys)
|
||||||
|
return keys, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDeployKey get one deploy key with key id
|
||||||
|
func (c *Client) GetDeployKey(user, repo string, keyID int64) (*DeployKey, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
key := new(DeployKey)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/keys/%d", user, repo, keyID), nil, nil, &key)
|
||||||
|
return key, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDeployKey options when create one deploy key
|
||||||
|
func (c *Client) CreateDeployKey(user, repo string, opt CreateKeyOption) (*DeployKey, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
key := new(DeployKey)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/keys", user, repo), jsonHeader, bytes.NewReader(body), key)
|
||||||
|
return key, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteDeployKey delete deploy key with key id
|
||||||
|
func (c *Client) DeleteDeployKey(owner, repo string, keyID int64) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/keys/%d", owner, repo, keyID), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
132
vendor/code.gitea.io/sdk/gitea/repo_migrate.go
generated
vendored
Normal file
132
vendor/code.gitea.io/sdk/gitea/repo_migrate.go
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GitServiceType represents a git service
|
||||||
|
type GitServiceType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// GitServicePlain represents a plain git service
|
||||||
|
GitServicePlain GitServiceType = "git"
|
||||||
|
// GitServiceGithub represents github.com
|
||||||
|
GitServiceGithub GitServiceType = "github"
|
||||||
|
// GitServiceGitlab represents a gitlab service
|
||||||
|
GitServiceGitlab GitServiceType = "gitlab"
|
||||||
|
// GitServiceGitea represents a gitea service
|
||||||
|
GitServiceGitea GitServiceType = "gitea"
|
||||||
|
// GitServiceGogs represents a gogs service
|
||||||
|
GitServiceGogs GitServiceType = "gogs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MigrateRepoOption options for migrating a repository from an external service
|
||||||
|
type MigrateRepoOption struct {
|
||||||
|
RepoName string `json:"repo_name"`
|
||||||
|
RepoOwner string `json:"repo_owner"`
|
||||||
|
// deprecated use RepoOwner
|
||||||
|
RepoOwnerID int64 `json:"uid"`
|
||||||
|
CloneAddr string `json:"clone_addr"`
|
||||||
|
Service GitServiceType `json:"service"`
|
||||||
|
AuthUsername string `json:"auth_username"`
|
||||||
|
AuthPassword string `json:"auth_password"`
|
||||||
|
AuthToken string `json:"auth_token"`
|
||||||
|
Mirror bool `json:"mirror"`
|
||||||
|
Private bool `json:"private"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Wiki bool `json:"wiki"`
|
||||||
|
Milestones bool `json:"milestones"`
|
||||||
|
Labels bool `json:"labels"`
|
||||||
|
Issues bool `json:"issues"`
|
||||||
|
PullRequests bool `json:"pull_requests"`
|
||||||
|
Releases bool `json:"releases"`
|
||||||
|
MirrorInterval string `json:"mirror_interval"`
|
||||||
|
LFS bool `json:"lfs"`
|
||||||
|
LFSEndpoint string `json:"lfs_endpoint"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the MigrateRepoOption struct
|
||||||
|
func (opt *MigrateRepoOption) Validate(c *Client) error {
|
||||||
|
// check user options
|
||||||
|
if len(opt.CloneAddr) == 0 {
|
||||||
|
return fmt.Errorf("CloneAddr required")
|
||||||
|
}
|
||||||
|
if len(opt.RepoName) == 0 {
|
||||||
|
return fmt.Errorf("RepoName required")
|
||||||
|
} else if len(opt.RepoName) > 100 {
|
||||||
|
return fmt.Errorf("RepoName to long")
|
||||||
|
}
|
||||||
|
if len(opt.Description) > 2048 {
|
||||||
|
return fmt.Errorf("Description to long")
|
||||||
|
}
|
||||||
|
switch opt.Service {
|
||||||
|
case GitServiceGithub:
|
||||||
|
if len(opt.AuthToken) == 0 {
|
||||||
|
return fmt.Errorf("github requires token authentication")
|
||||||
|
}
|
||||||
|
case GitServiceGitlab, GitServiceGitea:
|
||||||
|
if len(opt.AuthToken) == 0 {
|
||||||
|
return fmt.Errorf("%s requires token authentication", opt.Service)
|
||||||
|
}
|
||||||
|
// Gitlab is supported since 1.12.0 but api cant handle it until 1.13.0
|
||||||
|
// https://github.com/go-gitea/gitea/pull/12672
|
||||||
|
if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil {
|
||||||
|
return fmt.Errorf("migrate from service %s need gitea >= 1.13.0", opt.Service)
|
||||||
|
}
|
||||||
|
case GitServiceGogs:
|
||||||
|
if len(opt.AuthToken) == 0 {
|
||||||
|
return fmt.Errorf("gogs requires token authentication")
|
||||||
|
}
|
||||||
|
if c.checkServerVersionGreaterThanOrEqual(version1_14_0) != nil {
|
||||||
|
return fmt.Errorf("migrate from service gogs need gitea >= 1.14.0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MigrateRepo migrates a repository from other Git hosting sources for the authenticated user.
|
||||||
|
//
|
||||||
|
// To migrate a repository for a organization, the authenticated user must be a
|
||||||
|
// owner of the specified organization.
|
||||||
|
func (c *Client) MigrateRepo(opt MigrateRepoOption) (*Repository, *Response, error) {
|
||||||
|
if err := opt.Validate(c); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||||
|
if len(opt.AuthToken) != 0 {
|
||||||
|
// gitea <= 1.12 dont understand AuthToken
|
||||||
|
opt.AuthUsername = opt.AuthToken
|
||||||
|
opt.AuthPassword, opt.AuthToken = "", ""
|
||||||
|
}
|
||||||
|
if len(opt.RepoOwner) != 0 {
|
||||||
|
// gitea <= 1.12 dont understand RepoOwner
|
||||||
|
u, _, err := c.GetUserInfo(opt.RepoOwner)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.RepoOwnerID = u.ID
|
||||||
|
} else if opt.RepoOwnerID == 0 {
|
||||||
|
// gitea <= 1.12 require RepoOwnerID
|
||||||
|
u, _, err := c.GetMyUserInfo()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.RepoOwnerID = u.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
repo := new(Repository)
|
||||||
|
resp, err := c.getParsedResponse("POST", "/repos/migrate", jsonHeader, bytes.NewReader(body), repo)
|
||||||
|
return repo, resp, err
|
||||||
|
}
|
||||||
45
vendor/code.gitea.io/sdk/gitea/repo_mirror.go
generated
vendored
Normal file
45
vendor/code.gitea.io/sdk/gitea/repo_mirror.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreatePushMirrorOption struct {
|
||||||
|
Interval string `json:"interval"`
|
||||||
|
RemoteAddress string `json:"remote_address"`
|
||||||
|
RemotePassword string `json:"remote_password"`
|
||||||
|
RemoteUsername string `json:"remote_username"`
|
||||||
|
SyncONCommit bool `json:"sync_on_commit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushMirrorResponse returns a git push mirror
|
||||||
|
type PushMirrorResponse struct {
|
||||||
|
Created string `json:"created"`
|
||||||
|
Interval string `json:"interval"`
|
||||||
|
LastError string `json:"last_error"`
|
||||||
|
LastUpdate string `json:"last_update"`
|
||||||
|
RemoteAddress string `json:"remote_address"`
|
||||||
|
RemoteName string `json:"remote_name"`
|
||||||
|
RepoName string `json:"repo_name"`
|
||||||
|
SyncONCommit bool `json:"sync_on_commit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushMirrors add a push mirror to the repository
|
||||||
|
func (c *Client) PushMirrors(user, repo string, opt CreatePushMirrorOption) (*PushMirrorResponse, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
pm := new(PushMirrorResponse)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/push_mirrors", user, repo), jsonHeader, bytes.NewReader(body), &pm)
|
||||||
|
return pm, resp, err
|
||||||
|
}
|
||||||
78
vendor/code.gitea.io/sdk/gitea/repo_refs.go
generated
vendored
Normal file
78
vendor/code.gitea.io/sdk/gitea/repo_refs.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference represents a Git reference.
|
||||||
|
type Reference struct {
|
||||||
|
Ref string `json:"ref"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Object *GitObject `json:"object"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GitObject represents a Git object.
|
||||||
|
type GitObject struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
SHA string `json:"sha"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRepoRef get one ref's information of one repository
|
||||||
|
func (c *Client) GetRepoRef(user, repo, ref string) (*Reference, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
ref = strings.TrimPrefix(ref, "refs/")
|
||||||
|
ref = pathEscapeSegments(ref)
|
||||||
|
r := new(Reference)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/refs/%s", user, repo, ref), nil, nil, &r)
|
||||||
|
if _, ok := err.(*json.UnmarshalTypeError); ok {
|
||||||
|
// Multiple refs
|
||||||
|
return nil, resp, errors.New("no exact match found for this ref")
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRepoRefs get list of ref's information of one repository
|
||||||
|
func (c *Client) GetRepoRefs(user, repo, ref string) ([]*Reference, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
ref = strings.TrimPrefix(ref, "refs/")
|
||||||
|
ref = pathEscapeSegments(ref)
|
||||||
|
|
||||||
|
data, resp, err := c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/git/refs/%s", user, repo, ref), nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to unmarshal single returned ref.
|
||||||
|
r := new(Reference)
|
||||||
|
refErr := json.Unmarshal(data, r)
|
||||||
|
if refErr == nil {
|
||||||
|
return []*Reference{r}, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to unmarshal multiple refs.
|
||||||
|
var rs []*Reference
|
||||||
|
refsErr := json.Unmarshal(data, &rs)
|
||||||
|
if refsErr == nil {
|
||||||
|
if len(rs) == 0 {
|
||||||
|
return nil, resp, errors.New("unexpected response: an array of refs with length 0")
|
||||||
|
}
|
||||||
|
return rs, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, resp, fmt.Errorf("unmarshalling failed for both single and multiple refs: %s and %s", refErr, refsErr)
|
||||||
|
}
|
||||||
96
vendor/code.gitea.io/sdk/gitea/repo_stars.go
generated
vendored
Normal file
96
vendor/code.gitea.io/sdk/gitea/repo_stars.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListStargazersOptions options for listing a repository's stargazers
|
||||||
|
type ListStargazersOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoStargazers list a repository's stargazers
|
||||||
|
func (c *Client) ListRepoStargazers(user, repo string, opt ListStargazersOptions) ([]*User, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
stargazers := make([]*User, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/stargazers?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &stargazers)
|
||||||
|
return stargazers, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStarredRepos returns the repos that the given user has starred
|
||||||
|
func (c *Client) GetStarredRepos(user string) ([]*Repository, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
repos := make([]*Repository, 0, 10)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/starred", user), jsonHeader, nil, &repos)
|
||||||
|
return repos, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMyStarredRepos returns the repos that the authenticated user has starred
|
||||||
|
func (c *Client) GetMyStarredRepos() ([]*Repository, *Response, error) {
|
||||||
|
repos := make([]*Repository, 0, 10)
|
||||||
|
resp, err := c.getParsedResponse("GET", "/user/starred", jsonHeader, nil, &repos)
|
||||||
|
return repos, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsRepoStarring returns whether the authenticated user has starred the repo or not
|
||||||
|
func (c *Client) IsRepoStarring(user, repo string) (bool, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("GET", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil)
|
||||||
|
if resp != nil {
|
||||||
|
switch resp.StatusCode {
|
||||||
|
case http.StatusNotFound:
|
||||||
|
return false, resp, nil
|
||||||
|
case http.StatusNoContent:
|
||||||
|
return true, resp, nil
|
||||||
|
default:
|
||||||
|
return false, resp, fmt.Errorf("unexpected status code '%d'", resp.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// StarRepo star specified repo as the authenticated user
|
||||||
|
func (c *Client) StarRepo(user, repo string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil)
|
||||||
|
if resp != nil {
|
||||||
|
switch resp.StatusCode {
|
||||||
|
case http.StatusNoContent:
|
||||||
|
return resp, nil
|
||||||
|
default:
|
||||||
|
return resp, fmt.Errorf("unexpected status code '%d'", resp.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnStarRepo remove star to specified repo as the authenticated user
|
||||||
|
func (c *Client) UnStarRepo(user, repo string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil)
|
||||||
|
if resp != nil {
|
||||||
|
switch resp.StatusCode {
|
||||||
|
case http.StatusNoContent:
|
||||||
|
return resp, nil
|
||||||
|
default:
|
||||||
|
return resp, fmt.Errorf("unexpected status code '%d'", resp.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
130
vendor/code.gitea.io/sdk/gitea/repo_tag.go
generated
vendored
Normal file
130
vendor/code.gitea.io/sdk/gitea/repo_tag.go
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tag represents a repository tag
|
||||||
|
type Tag struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Commit *CommitMeta `json:"commit"`
|
||||||
|
ZipballURL string `json:"zipball_url"`
|
||||||
|
TarballURL string `json:"tarball_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnnotatedTag represents an annotated tag
|
||||||
|
type AnnotatedTag struct {
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
SHA string `json:"sha"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Tagger *CommitUser `json:"tagger"`
|
||||||
|
Object *AnnotatedTagObject `json:"object"`
|
||||||
|
Verification *PayloadCommitVerification `json:"verification"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnnotatedTagObject contains meta information of the tag object
|
||||||
|
type AnnotatedTagObject struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
SHA string `json:"sha"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoTagsOptions options for listing a repository's tags
|
||||||
|
type ListRepoTagsOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoTags list all the branches of one repository
|
||||||
|
func (c *Client) ListRepoTags(user, repo string, opt ListRepoTagsOptions) ([]*Tag, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
tags := make([]*Tag, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/tags?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &tags)
|
||||||
|
return tags, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTag get the tag of a repository
|
||||||
|
func (c *Client) GetTag(user, repo, tag string) (*Tag, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &tag); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
t := new(Tag)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/tags/%s", user, repo, tag), nil, nil, &t)
|
||||||
|
return t, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAnnotatedTag get the tag object of an annotated tag (not lightweight tags) of a repository
|
||||||
|
func (c *Client) GetAnnotatedTag(user, repo, sha string) (*AnnotatedTag, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &sha); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
t := new(AnnotatedTag)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/tags/%s", user, repo, sha), nil, nil, &t)
|
||||||
|
return t, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateTagOption options when creating a tag
|
||||||
|
type CreateTagOption struct {
|
||||||
|
TagName string `json:"tag_name"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Target string `json:"target"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates CreateTagOption
|
||||||
|
func (opt CreateTagOption) Validate() error {
|
||||||
|
if len(opt.TagName) == 0 {
|
||||||
|
return fmt.Errorf("TagName is required")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateTag create a new git tag in a repository
|
||||||
|
func (c *Client) CreateTag(user, repo string, opt CreateTagOption) (*Tag, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
t := new(Tag)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/tags", user, repo), jsonHeader, bytes.NewReader(body), &t)
|
||||||
|
return t, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteTag deletes a tag from a repository, if no release refers to it
|
||||||
|
func (c *Client) DeleteTag(user, repo, tag string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &tag); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE",
|
||||||
|
fmt.Sprintf("/repos/%s/%s/tags/%s", user, repo, tag),
|
||||||
|
nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
65
vendor/code.gitea.io/sdk/gitea/repo_team.go
generated
vendored
Normal file
65
vendor/code.gitea.io/sdk/gitea/repo_team.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetRepoTeams return teams from a repository
|
||||||
|
func (c *Client) GetRepoTeams(user, repo string) ([]*Team, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
teams := make([]*Team, 0, 5)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/teams", user, repo), nil, nil, &teams)
|
||||||
|
return teams, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRepoTeam add a team to a repository
|
||||||
|
func (c *Client) AddRepoTeam(user, repo, team string) (*Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &team); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/teams/%s", user, repo, team), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveRepoTeam delete a team from a repository
|
||||||
|
func (c *Client) RemoveRepoTeam(user, repo, team string) (*Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &team); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/teams/%s", user, repo, team), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckRepoTeam check if team is assigned to repo by name and return it.
|
||||||
|
// If not assigned, it will return nil.
|
||||||
|
func (c *Client) CheckRepoTeam(user, repo, team string) (*Team, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &team); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
t := new(Team)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/teams/%s", user, repo, team), nil, nil, &t)
|
||||||
|
if resp != nil && resp.StatusCode == http.StatusNotFound {
|
||||||
|
// if not found it's not an error, it indicates it's not assigned
|
||||||
|
return nil, resp, nil
|
||||||
|
}
|
||||||
|
return t, resp, err
|
||||||
|
}
|
||||||
65
vendor/code.gitea.io/sdk/gitea/repo_template.go
generated
vendored
Normal file
65
vendor/code.gitea.io/sdk/gitea/repo_template.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateRepoFromTemplateOption options when creating repository using a template
|
||||||
|
type CreateRepoFromTemplateOption struct {
|
||||||
|
// Owner is the organization or person who will own the new repository
|
||||||
|
Owner string `json:"owner"`
|
||||||
|
// Name of the repository to create
|
||||||
|
Name string `json:"name"`
|
||||||
|
// Description of the repository to create
|
||||||
|
Description string `json:"description"`
|
||||||
|
// Private is whether the repository is private
|
||||||
|
Private bool `json:"private"`
|
||||||
|
// GitContent include git content of default branch in template repo
|
||||||
|
GitContent bool `json:"git_content"`
|
||||||
|
// Topics include topics of template repo
|
||||||
|
Topics bool `json:"topics"`
|
||||||
|
// GitHooks include git hooks of template repo
|
||||||
|
GitHooks bool `json:"git_hooks"`
|
||||||
|
// Webhooks include webhooks of template repo
|
||||||
|
Webhooks bool `json:"webhooks"`
|
||||||
|
// Avatar include avatar of the template repo
|
||||||
|
Avatar bool `json:"avatar"`
|
||||||
|
// Labels include labels of template repo
|
||||||
|
Labels bool `json:"labels"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates CreateRepoFromTemplateOption
|
||||||
|
func (opt CreateRepoFromTemplateOption) Validate() error {
|
||||||
|
if len(opt.Owner) == 0 {
|
||||||
|
return fmt.Errorf("field Owner is required")
|
||||||
|
}
|
||||||
|
if len(opt.Name) == 0 {
|
||||||
|
return fmt.Errorf("field Name is required")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRepoFromTemplate create a repository using a template
|
||||||
|
func (c *Client) CreateRepoFromTemplate(templateOwner, templateRepo string, opt CreateRepoFromTemplateOption) (*Repository, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&templateOwner, &templateRepo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
repo := new(Repository)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/generate", templateOwner, templateRepo), jsonHeader, bytes.NewReader(body), &repo)
|
||||||
|
return repo, resp, err
|
||||||
|
}
|
||||||
68
vendor/code.gitea.io/sdk/gitea/repo_topics.go
generated
vendored
Normal file
68
vendor/code.gitea.io/sdk/gitea/repo_topics.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListRepoTopicsOptions options for listing repo's topics
|
||||||
|
type ListRepoTopicsOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// topicsList represents a list of repo's topics
|
||||||
|
type topicsList struct {
|
||||||
|
Topics []string `json:"topics"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoTopics list all repository's topics
|
||||||
|
func (c *Client) ListRepoTopics(user, repo string, opt ListRepoTopicsOptions) ([]string, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
|
||||||
|
list := new(topicsList)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/topics?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, list)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return list.Topics, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRepoTopics replaces the list of repo's topics
|
||||||
|
func (c *Client) SetRepoTopics(user, repo string, list []string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
l := topicsList{Topics: list}
|
||||||
|
body, err := json.Marshal(&l)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/topics", user, repo), jsonHeader, bytes.NewReader(body))
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRepoTopic adds a topic to a repo's topics list
|
||||||
|
func (c *Client) AddRepoTopic(user, repo, topic string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &topic); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/topics/%s", user, repo, topic), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRepoTopic deletes a topic from repo's topics list
|
||||||
|
func (c *Client) DeleteRepoTopic(user, repo, topic string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &topic); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/topics/%s", user, repo, topic), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
62
vendor/code.gitea.io/sdk/gitea/repo_transfer.go
generated
vendored
Normal file
62
vendor/code.gitea.io/sdk/gitea/repo_transfer.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TransferRepoOption options when transfer a repository's ownership
|
||||||
|
type TransferRepoOption struct {
|
||||||
|
// required: true
|
||||||
|
NewOwner string `json:"new_owner"`
|
||||||
|
// ID of the team or teams to add to the repository. Teams can only be added to organization-owned repositories.
|
||||||
|
TeamIDs *[]int64 `json:"team_ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransferRepo transfers the ownership of a repository
|
||||||
|
func (c *Client) TransferRepo(owner, reponame string, opt TransferRepoOption) (*Repository, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
repo := new(Repository)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/transfer", owner, reponame), jsonHeader, bytes.NewReader(body), repo)
|
||||||
|
return repo, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AcceptRepoTransfer accepts a repo transfer.
|
||||||
|
func (c *Client) AcceptRepoTransfer(owner, reponame string) (*Repository, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_16_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
repo := new(Repository)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/transfer/accept", owner, reponame), jsonHeader, nil, repo)
|
||||||
|
return repo, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RejectRepoTransfer rejects a repo transfer.
|
||||||
|
func (c *Client) RejectRepoTransfer(owner, reponame string) (*Repository, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_16_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
repo := new(Repository)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/transfer/reject", owner, reponame), jsonHeader, nil, repo)
|
||||||
|
return repo, resp, err
|
||||||
|
}
|
||||||
44
vendor/code.gitea.io/sdk/gitea/repo_tree.go
generated
vendored
Normal file
44
vendor/code.gitea.io/sdk/gitea/repo_tree.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GitEntry represents a git tree
|
||||||
|
type GitEntry struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Mode string `json:"mode"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
SHA string `json:"sha"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GitTreeResponse returns a git tree
|
||||||
|
type GitTreeResponse struct {
|
||||||
|
SHA string `json:"sha"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Entries []GitEntry `json:"tree"`
|
||||||
|
Truncated bool `json:"truncated"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
TotalCount int `json:"total_count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTrees downloads a file of repository, ref can be branch/tag/commit.
|
||||||
|
// e.g.: ref -> master, tree -> macaron.go(no leading slash)
|
||||||
|
func (c *Client) GetTrees(user, repo, ref string, recursive bool) (*GitTreeResponse, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &repo, &ref); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
trees := new(GitTreeResponse)
|
||||||
|
path := fmt.Sprintf("/repos/%s/%s/git/trees/%s", user, repo, ref)
|
||||||
|
if recursive {
|
||||||
|
path += "?recursive=1"
|
||||||
|
}
|
||||||
|
resp, err := c.getParsedResponse("GET", path, nil, nil, trees)
|
||||||
|
return trees, resp, err
|
||||||
|
}
|
||||||
87
vendor/code.gitea.io/sdk/gitea/repo_watch.go
generated
vendored
Normal file
87
vendor/code.gitea.io/sdk/gitea/repo_watch.go
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WatchInfo represents an API watch status of one repository
|
||||||
|
type WatchInfo struct {
|
||||||
|
Subscribed bool `json:"subscribed"`
|
||||||
|
Ignored bool `json:"ignored"`
|
||||||
|
Reason interface{} `json:"reason"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
RepositoryURL string `json:"repository_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWatchedRepos list all the watched repos of user
|
||||||
|
func (c *Client) GetWatchedRepos(user string) ([]*Repository, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
repos := make([]*Repository, 0, 10)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/subscriptions", user), nil, nil, &repos)
|
||||||
|
return repos, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMyWatchedRepos list repositories watched by the authenticated user
|
||||||
|
func (c *Client) GetMyWatchedRepos() ([]*Repository, *Response, error) {
|
||||||
|
repos := make([]*Repository, 0, 10)
|
||||||
|
resp, err := c.getParsedResponse("GET", "/user/subscriptions", nil, nil, &repos)
|
||||||
|
return repos, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckRepoWatch check if the current user is watching a repo
|
||||||
|
func (c *Client) CheckRepoWatch(owner, repo string) (bool, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/subscription", owner, repo), nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return false, resp, err
|
||||||
|
}
|
||||||
|
switch status {
|
||||||
|
case http.StatusNotFound:
|
||||||
|
return false, resp, nil
|
||||||
|
case http.StatusOK:
|
||||||
|
return true, resp, nil
|
||||||
|
default:
|
||||||
|
return false, resp, fmt.Errorf("unexpected Status: %d", status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WatchRepo start to watch a repository
|
||||||
|
func (c *Client) WatchRepo(owner, repo string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
status, resp, err := c.getStatusCode("PUT", fmt.Sprintf("/repos/%s/%s/subscription", owner, repo), nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
if status == http.StatusOK {
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
return resp, fmt.Errorf("unexpected Status: %d", status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnWatchRepo stop to watch a repository
|
||||||
|
func (c *Client) UnWatchRepo(owner, repo string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
status, resp, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/subscription", owner, repo), nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
if status == http.StatusNoContent {
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
return resp, fmt.Errorf("unexpected Status: %d", status)
|
||||||
|
}
|
||||||
14
vendor/code.gitea.io/sdk/gitea/secret.go
generated
vendored
Normal file
14
vendor/code.gitea.io/sdk/gitea/secret.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Secret struct {
|
||||||
|
// the secret's name
|
||||||
|
Name string `json:"name"`
|
||||||
|
// Date and Time of secret creation
|
||||||
|
Created time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
78
vendor/code.gitea.io/sdk/gitea/settings.go
generated
vendored
Normal file
78
vendor/code.gitea.io/sdk/gitea/settings.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
// GlobalUISettings represent the global ui settings of a gitea instance witch is exposed by API
|
||||||
|
type GlobalUISettings struct {
|
||||||
|
DefaultTheme string `json:"default_theme"`
|
||||||
|
AllowedReactions []string `json:"allowed_reactions"`
|
||||||
|
CustomEmojis []string `json:"custom_emojis"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalRepoSettings represent the global repository settings of a gitea instance witch is exposed by API
|
||||||
|
type GlobalRepoSettings struct {
|
||||||
|
MirrorsDisabled bool `json:"mirrors_disabled"`
|
||||||
|
HTTPGitDisabled bool `json:"http_git_disabled"`
|
||||||
|
MigrationsDisabled bool `json:"migrations_disabled"`
|
||||||
|
StarsDisabled bool `json:"stars_disabled"`
|
||||||
|
TimeTrackingDisabled bool `json:"time_tracking_disabled"`
|
||||||
|
LFSDisabled bool `json:"lfs_disabled"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalAPISettings contains global api settings exposed by it
|
||||||
|
type GlobalAPISettings struct {
|
||||||
|
MaxResponseItems int `json:"max_response_items"`
|
||||||
|
DefaultPagingNum int `json:"default_paging_num"`
|
||||||
|
DefaultGitTreesPerPage int `json:"default_git_trees_per_page"`
|
||||||
|
DefaultMaxBlobSize int64 `json:"default_max_blob_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalAttachmentSettings contains global Attachment settings exposed by API
|
||||||
|
type GlobalAttachmentSettings struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
AllowedTypes string `json:"allowed_types"`
|
||||||
|
MaxSize int64 `json:"max_size"`
|
||||||
|
MaxFiles int `json:"max_files"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGlobalUISettings get global ui settings witch are exposed by API
|
||||||
|
func (c *Client) GetGlobalUISettings() (*GlobalUISettings, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
conf := new(GlobalUISettings)
|
||||||
|
resp, err := c.getParsedResponse("GET", "/settings/ui", jsonHeader, nil, &conf)
|
||||||
|
return conf, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGlobalRepoSettings get global repository settings witch are exposed by API
|
||||||
|
func (c *Client) GetGlobalRepoSettings() (*GlobalRepoSettings, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
conf := new(GlobalRepoSettings)
|
||||||
|
resp, err := c.getParsedResponse("GET", "/settings/repository", jsonHeader, nil, &conf)
|
||||||
|
return conf, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGlobalAPISettings get global api settings witch are exposed by it
|
||||||
|
func (c *Client) GetGlobalAPISettings() (*GlobalAPISettings, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
conf := new(GlobalAPISettings)
|
||||||
|
resp, err := c.getParsedResponse("GET", "/settings/api", jsonHeader, nil, &conf)
|
||||||
|
return conf, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGlobalAttachmentSettings get global repository settings witch are exposed by API
|
||||||
|
func (c *Client) GetGlobalAttachmentSettings() (*GlobalAttachmentSettings, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
conf := new(GlobalAttachmentSettings)
|
||||||
|
resp, err := c.getParsedResponse("GET", "/settings/attachment", jsonHeader, nil, &conf)
|
||||||
|
return conf, resp, err
|
||||||
|
}
|
||||||
108
vendor/code.gitea.io/sdk/gitea/status.go
generated
vendored
Normal file
108
vendor/code.gitea.io/sdk/gitea/status.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StatusState holds the state of a Status
|
||||||
|
// It can be "pending", "success", "error", "failure", and "warning"
|
||||||
|
type StatusState string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// StatusPending is for when the Status is Pending
|
||||||
|
StatusPending StatusState = "pending"
|
||||||
|
// StatusSuccess is for when the Status is Success
|
||||||
|
StatusSuccess StatusState = "success"
|
||||||
|
// StatusError is for when the Status is Error
|
||||||
|
StatusError StatusState = "error"
|
||||||
|
// StatusFailure is for when the Status is Failure
|
||||||
|
StatusFailure StatusState = "failure"
|
||||||
|
// StatusWarning is for when the Status is Warning
|
||||||
|
StatusWarning StatusState = "warning"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Status holds a single Status of a single Commit
|
||||||
|
type Status struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
State StatusState `json:"status"`
|
||||||
|
TargetURL string `json:"target_url"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Context string `json:"context"`
|
||||||
|
Creator *User `json:"creator"`
|
||||||
|
Created time.Time `json:"created_at"`
|
||||||
|
Updated time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateStatusOption holds the information needed to create a new Status for a Commit
|
||||||
|
type CreateStatusOption struct {
|
||||||
|
State StatusState `json:"state"`
|
||||||
|
TargetURL string `json:"target_url"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Context string `json:"context"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateStatus creates a new Status for a given Commit
|
||||||
|
func (c *Client) CreateStatus(owner, repo, sha string, opts CreateStatusOption) (*Status, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
status := new(Status)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/statuses/%s", owner, repo, url.QueryEscape(sha)), jsonHeader, bytes.NewReader(body), status)
|
||||||
|
return status, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListStatusesOption options for listing a repository's commit's statuses
|
||||||
|
type ListStatusesOption struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListStatuses returns all statuses for a given Commit by ref
|
||||||
|
func (c *Client) ListStatuses(owner, repo, ref string, opt ListStatusesOption) ([]*Status, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo, &ref); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
statuses := make([]*Status, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/statuses?%s", owner, repo, ref, opt.getURLQuery().Encode()), jsonHeader, nil, &statuses)
|
||||||
|
return statuses, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CombinedStatus holds the combined state of several statuses for a single commit
|
||||||
|
type CombinedStatus struct {
|
||||||
|
State StatusState `json:"state"`
|
||||||
|
SHA string `json:"sha"`
|
||||||
|
TotalCount int `json:"total_count"`
|
||||||
|
Statuses []*Status `json:"statuses"`
|
||||||
|
Repository *Repository `json:"repository"`
|
||||||
|
CommitURL string `json:"commit_url"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCombinedStatus returns the CombinedStatus for a given Commit
|
||||||
|
func (c *Client) GetCombinedStatus(owner, repo, ref string) (*CombinedStatus, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&owner, &repo, &ref); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
status := new(CombinedStatus)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/status", owner, repo, ref), jsonHeader, nil, status)
|
||||||
|
|
||||||
|
// gitea api return empty body if nothing here jet
|
||||||
|
if resp != nil && resp.StatusCode == 200 && err != nil {
|
||||||
|
return status, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return status, resp, err
|
||||||
|
}
|
||||||
89
vendor/code.gitea.io/sdk/gitea/user.go
generated
vendored
Normal file
89
vendor/code.gitea.io/sdk/gitea/user.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// User represents a user
|
||||||
|
type User struct {
|
||||||
|
// the user's id
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
// the user's username
|
||||||
|
UserName string `json:"login"`
|
||||||
|
// the user's full name
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
// URL to the user's avatar
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
// User locale
|
||||||
|
Language string `json:"language"`
|
||||||
|
// Is the user an administrator
|
||||||
|
IsAdmin bool `json:"is_admin"`
|
||||||
|
// Date and Time of last login
|
||||||
|
LastLogin time.Time `json:"last_login"`
|
||||||
|
// Date and Time of user creation
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
// Is user restricted
|
||||||
|
Restricted bool `json:"restricted"`
|
||||||
|
// Is user active
|
||||||
|
IsActive bool `json:"active"`
|
||||||
|
// Is user login prohibited
|
||||||
|
ProhibitLogin bool `json:"prohibit_login"`
|
||||||
|
// the user's location
|
||||||
|
Location string `json:"location"`
|
||||||
|
// the user's website
|
||||||
|
Website string `json:"website"`
|
||||||
|
// the user's description
|
||||||
|
Description string `json:"description"`
|
||||||
|
// User visibility level option
|
||||||
|
Visibility VisibleType `json:"visibility"`
|
||||||
|
|
||||||
|
// user counts
|
||||||
|
FollowerCount int `json:"followers_count"`
|
||||||
|
FollowingCount int `json:"following_count"`
|
||||||
|
StarredRepoCount int `json:"starred_repos_count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserInfo get user info by user's name
|
||||||
|
func (c *Client) GetUserInfo(user string) (*User, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := new(User)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s", user), nil, nil, u)
|
||||||
|
return u, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMyUserInfo get user info of current user
|
||||||
|
func (c *Client) GetMyUserInfo() (*User, *Response, error) {
|
||||||
|
u := new(User)
|
||||||
|
resp, err := c.getParsedResponse("GET", "/user", nil, nil, u)
|
||||||
|
return u, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserByID returns user by a given user ID
|
||||||
|
func (c *Client) GetUserByID(id int64) (*User, *Response, error) {
|
||||||
|
if id < 0 {
|
||||||
|
return nil, nil, fmt.Errorf("invalid user id %d", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
query := make(url.Values)
|
||||||
|
query.Add("uid", strconv.FormatInt(id, 10))
|
||||||
|
users, resp, err := c.searchUsers(query.Encode())
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(users) == 1 {
|
||||||
|
return users[0], resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, resp, fmt.Errorf("user not found with id %d", id)
|
||||||
|
}
|
||||||
143
vendor/code.gitea.io/sdk/gitea/user_app.go
generated
vendored
Normal file
143
vendor/code.gitea.io/sdk/gitea/user_app.go
generated
vendored
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AccessTokenScope represents the scope for an access token.
|
||||||
|
type AccessTokenScope string
|
||||||
|
|
||||||
|
const (
|
||||||
|
AccessTokenScopeAll AccessTokenScope = "all"
|
||||||
|
|
||||||
|
AccessTokenScopeRepo AccessTokenScope = "repo"
|
||||||
|
AccessTokenScopeRepoStatus AccessTokenScope = "repo:status"
|
||||||
|
AccessTokenScopePublicRepo AccessTokenScope = "public_repo"
|
||||||
|
|
||||||
|
AccessTokenScopeAdminOrg AccessTokenScope = "admin:org"
|
||||||
|
AccessTokenScopeWriteOrg AccessTokenScope = "write:org"
|
||||||
|
AccessTokenScopeReadOrg AccessTokenScope = "read:org"
|
||||||
|
|
||||||
|
AccessTokenScopeAdminPublicKey AccessTokenScope = "admin:public_key"
|
||||||
|
AccessTokenScopeWritePublicKey AccessTokenScope = "write:public_key"
|
||||||
|
AccessTokenScopeReadPublicKey AccessTokenScope = "read:public_key"
|
||||||
|
|
||||||
|
AccessTokenScopeAdminRepoHook AccessTokenScope = "admin:repo_hook"
|
||||||
|
AccessTokenScopeWriteRepoHook AccessTokenScope = "write:repo_hook"
|
||||||
|
AccessTokenScopeReadRepoHook AccessTokenScope = "read:repo_hook"
|
||||||
|
|
||||||
|
AccessTokenScopeAdminOrgHook AccessTokenScope = "admin:org_hook"
|
||||||
|
|
||||||
|
AccessTokenScopeAdminUserHook AccessTokenScope = "admin:user_hook"
|
||||||
|
|
||||||
|
AccessTokenScopeNotification AccessTokenScope = "notification"
|
||||||
|
|
||||||
|
AccessTokenScopeUser AccessTokenScope = "user"
|
||||||
|
AccessTokenScopeReadUser AccessTokenScope = "read:user"
|
||||||
|
AccessTokenScopeUserEmail AccessTokenScope = "user:email"
|
||||||
|
AccessTokenScopeUserFollow AccessTokenScope = "user:follow"
|
||||||
|
|
||||||
|
AccessTokenScopeDeleteRepo AccessTokenScope = "delete_repo"
|
||||||
|
|
||||||
|
AccessTokenScopePackage AccessTokenScope = "package"
|
||||||
|
AccessTokenScopeWritePackage AccessTokenScope = "write:package"
|
||||||
|
AccessTokenScopeReadPackage AccessTokenScope = "read:package"
|
||||||
|
AccessTokenScopeDeletePackage AccessTokenScope = "delete:package"
|
||||||
|
|
||||||
|
AccessTokenScopeAdminGPGKey AccessTokenScope = "admin:gpg_key"
|
||||||
|
AccessTokenScopeWriteGPGKey AccessTokenScope = "write:gpg_key"
|
||||||
|
AccessTokenScopeReadGPGKey AccessTokenScope = "read:gpg_key"
|
||||||
|
|
||||||
|
AccessTokenScopeAdminApplication AccessTokenScope = "admin:application"
|
||||||
|
AccessTokenScopeWriteApplication AccessTokenScope = "write:application"
|
||||||
|
AccessTokenScopeReadApplication AccessTokenScope = "read:application"
|
||||||
|
|
||||||
|
AccessTokenScopeSudo AccessTokenScope = "sudo"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AccessToken represents an API access token.
|
||||||
|
type AccessToken struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Token string `json:"sha1"`
|
||||||
|
TokenLastEight string `json:"token_last_eight"`
|
||||||
|
Scopes []AccessTokenScope `json:"scopes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAccessTokensOptions options for listing a users's access tokens
|
||||||
|
type ListAccessTokensOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAccessTokens lists all the access tokens of user
|
||||||
|
func (c *Client) ListAccessTokens(opts ListAccessTokensOptions) ([]*AccessToken, *Response, error) {
|
||||||
|
c.mutex.RLock()
|
||||||
|
username := c.username
|
||||||
|
c.mutex.RUnlock()
|
||||||
|
if len(username) == 0 {
|
||||||
|
return nil, nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed")
|
||||||
|
}
|
||||||
|
opts.setDefaults()
|
||||||
|
tokens := make([]*AccessToken, 0, opts.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/tokens?%s", url.PathEscape(username), opts.getURLQuery().Encode()), jsonHeader, nil, &tokens)
|
||||||
|
return tokens, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateAccessTokenOption options when create access token
|
||||||
|
type CreateAccessTokenOption struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Scopes []AccessTokenScope `json:"scopes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateAccessToken create one access token with options
|
||||||
|
func (c *Client) CreateAccessToken(opt CreateAccessTokenOption) (*AccessToken, *Response, error) {
|
||||||
|
c.mutex.RLock()
|
||||||
|
username := c.username
|
||||||
|
c.mutex.RUnlock()
|
||||||
|
if len(username) == 0 {
|
||||||
|
return nil, nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed")
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
t := new(AccessToken)
|
||||||
|
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/users/%s/tokens", url.PathEscape(username)), jsonHeader, bytes.NewReader(body), t)
|
||||||
|
return t, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAccessToken delete token, identified by ID and if not available by name
|
||||||
|
func (c *Client) DeleteAccessToken(value interface{}) (*Response, error) {
|
||||||
|
c.mutex.RLock()
|
||||||
|
username := c.username
|
||||||
|
c.mutex.RUnlock()
|
||||||
|
if len(username) == 0 {
|
||||||
|
return nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
token := ""
|
||||||
|
|
||||||
|
switch reflect.ValueOf(value).Kind() {
|
||||||
|
case reflect.Int64:
|
||||||
|
token = fmt.Sprintf("%d", value.(int64))
|
||||||
|
case reflect.String:
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
token = value.(string)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("only string and int64 supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/users/%s/tokens/%s", url.PathEscape(username), url.PathEscape(token)), jsonHeader, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
64
vendor/code.gitea.io/sdk/gitea/user_email.go
generated
vendored
Normal file
64
vendor/code.gitea.io/sdk/gitea/user_email.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Email an email address belonging to a user
|
||||||
|
type Email struct {
|
||||||
|
Email string `json:"email"`
|
||||||
|
Verified bool `json:"verified"`
|
||||||
|
Primary bool `json:"primary"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListEmailsOptions options for listing current's user emails
|
||||||
|
type ListEmailsOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListEmails all the email addresses of user
|
||||||
|
func (c *Client) ListEmails(opt ListEmailsOptions) ([]*Email, *Response, error) {
|
||||||
|
opt.setDefaults()
|
||||||
|
emails := make([]*Email, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/emails?%s", opt.getURLQuery().Encode()), nil, nil, &emails)
|
||||||
|
return emails, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateEmailOption options when creating email addresses
|
||||||
|
type CreateEmailOption struct {
|
||||||
|
// email addresses to add
|
||||||
|
Emails []string `json:"emails"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEmail add one email to current user with options
|
||||||
|
func (c *Client) AddEmail(opt CreateEmailOption) ([]*Email, *Response, error) {
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
emails := make([]*Email, 0, 3)
|
||||||
|
resp, err := c.getParsedResponse("POST", "/user/emails", jsonHeader, bytes.NewReader(body), &emails)
|
||||||
|
return emails, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteEmailOption options when deleting email addresses
|
||||||
|
type DeleteEmailOption struct {
|
||||||
|
// email addresses to delete
|
||||||
|
Emails []string `json:"emails"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteEmail delete one email of current users'
|
||||||
|
func (c *Client) DeleteEmail(opt DeleteEmailOption) (*Response, error) {
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", "/user/emails", jsonHeader, bytes.NewReader(body))
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
93
vendor/code.gitea.io/sdk/gitea/user_follow.go
generated
vendored
Normal file
93
vendor/code.gitea.io/sdk/gitea/user_follow.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// ListFollowersOptions options for listing followers
|
||||||
|
type ListFollowersOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMyFollowers list all the followers of current user
|
||||||
|
func (c *Client) ListMyFollowers(opt ListFollowersOptions) ([]*User, *Response, error) {
|
||||||
|
opt.setDefaults()
|
||||||
|
users := make([]*User, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/followers?%s", opt.getURLQuery().Encode()), nil, nil, &users)
|
||||||
|
return users, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListFollowers list all the followers of one user
|
||||||
|
func (c *Client) ListFollowers(user string, opt ListFollowersOptions) ([]*User, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
users := make([]*User, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/followers?%s", user, opt.getURLQuery().Encode()), nil, nil, &users)
|
||||||
|
return users, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListFollowingOptions options for listing a user's users being followed
|
||||||
|
type ListFollowingOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMyFollowing list all the users current user followed
|
||||||
|
func (c *Client) ListMyFollowing(opt ListFollowingOptions) ([]*User, *Response, error) {
|
||||||
|
opt.setDefaults()
|
||||||
|
users := make([]*User, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/following?%s", opt.getURLQuery().Encode()), nil, nil, &users)
|
||||||
|
return users, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListFollowing list all the users the user followed
|
||||||
|
func (c *Client) ListFollowing(user string, opt ListFollowingOptions) ([]*User, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
users := make([]*User, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/following?%s", user, opt.getURLQuery().Encode()), nil, nil, &users)
|
||||||
|
return users, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFollowing if current user followed the target
|
||||||
|
func (c *Client) IsFollowing(target string) (bool, *Response) {
|
||||||
|
if err := escapeValidatePathSegments(&target); err != nil {
|
||||||
|
// ToDo return err
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("GET", fmt.Sprintf("/user/following/%s", target), nil, nil)
|
||||||
|
return err == nil, resp
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUserFollowing if the user followed the target
|
||||||
|
func (c *Client) IsUserFollowing(user, target string) (bool, *Response) {
|
||||||
|
if err := escapeValidatePathSegments(&user, &target); err != nil {
|
||||||
|
// ToDo return err
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("GET", fmt.Sprintf("/users/%s/following/%s", user, target), nil, nil)
|
||||||
|
return err == nil, resp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Follow set current user follow the target
|
||||||
|
func (c *Client) Follow(target string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&target); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/user/following/%s", target), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unfollow set current user unfollow the target
|
||||||
|
func (c *Client) Unfollow(target string) (*Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&target); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/following/%s", target), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
89
vendor/code.gitea.io/sdk/gitea/user_gpgkey.go
generated
vendored
Normal file
89
vendor/code.gitea.io/sdk/gitea/user_gpgkey.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright 2017 Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GPGKey a user GPG key to sign commit and tag in repository
|
||||||
|
type GPGKey struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
PrimaryKeyID string `json:"primary_key_id"`
|
||||||
|
KeyID string `json:"key_id"`
|
||||||
|
PublicKey string `json:"public_key"`
|
||||||
|
Emails []*GPGKeyEmail `json:"emails"`
|
||||||
|
SubsKey []*GPGKey `json:"subkeys"`
|
||||||
|
CanSign bool `json:"can_sign"`
|
||||||
|
CanEncryptComms bool `json:"can_encrypt_comms"`
|
||||||
|
CanEncryptStorage bool `json:"can_encrypt_storage"`
|
||||||
|
CanCertify bool `json:"can_certify"`
|
||||||
|
Created time.Time `json:"created_at,omitempty"`
|
||||||
|
Expires time.Time `json:"expires_at,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GPGKeyEmail an email attached to a GPGKey
|
||||||
|
type GPGKeyEmail struct {
|
||||||
|
Email string `json:"email"`
|
||||||
|
Verified bool `json:"verified"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListGPGKeysOptions options for listing a user's GPGKeys
|
||||||
|
type ListGPGKeysOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListGPGKeys list all the GPG keys of the user
|
||||||
|
func (c *Client) ListGPGKeys(user string, opt ListGPGKeysOptions) ([]*GPGKey, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
keys := make([]*GPGKey, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/gpg_keys?%s", user, opt.getURLQuery().Encode()), nil, nil, &keys)
|
||||||
|
return keys, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMyGPGKeys list all the GPG keys of current user
|
||||||
|
func (c *Client) ListMyGPGKeys(opt *ListGPGKeysOptions) ([]*GPGKey, *Response, error) {
|
||||||
|
opt.setDefaults()
|
||||||
|
keys := make([]*GPGKey, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/gpg_keys?%s", opt.getURLQuery().Encode()), nil, nil, &keys)
|
||||||
|
return keys, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGPGKey get current user's GPG key by key id
|
||||||
|
func (c *Client) GetGPGKey(keyID int64) (*GPGKey, *Response, error) {
|
||||||
|
key := new(GPGKey)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/gpg_keys/%d", keyID), nil, nil, &key)
|
||||||
|
return key, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateGPGKeyOption options create user GPG key
|
||||||
|
type CreateGPGKeyOption struct {
|
||||||
|
// An armored GPG key to add
|
||||||
|
//
|
||||||
|
ArmoredKey string `json:"armored_public_key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateGPGKey create GPG key with options
|
||||||
|
func (c *Client) CreateGPGKey(opt CreateGPGKeyOption) (*GPGKey, *Response, error) {
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
key := new(GPGKey)
|
||||||
|
resp, err := c.getParsedResponse("POST", "/user/gpg_keys", jsonHeader, bytes.NewReader(body), key)
|
||||||
|
return key, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteGPGKey delete GPG key with key id
|
||||||
|
func (c *Client) DeleteGPGKey(keyID int64) (*Response, error) {
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/gpg_keys/%d", keyID), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
83
vendor/code.gitea.io/sdk/gitea/user_key.go
generated
vendored
Normal file
83
vendor/code.gitea.io/sdk/gitea/user_key.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PublicKey publickey is a user key to push code to repository
|
||||||
|
type PublicKey struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
URL string `json:"url,omitempty"`
|
||||||
|
Title string `json:"title,omitempty"`
|
||||||
|
Fingerprint string `json:"fingerprint,omitempty"`
|
||||||
|
Created time.Time `json:"created_at,omitempty"`
|
||||||
|
Owner *User `json:"user,omitempty"`
|
||||||
|
ReadOnly bool `json:"read_only,omitempty"`
|
||||||
|
KeyType string `json:"key_type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPublicKeysOptions options for listing a user's PublicKeys
|
||||||
|
type ListPublicKeysOptions struct {
|
||||||
|
ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPublicKeys list all the public keys of the user
|
||||||
|
func (c *Client) ListPublicKeys(user string, opt ListPublicKeysOptions) ([]*PublicKey, *Response, error) {
|
||||||
|
if err := escapeValidatePathSegments(&user); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
opt.setDefaults()
|
||||||
|
keys := make([]*PublicKey, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/keys?%s", user, opt.getURLQuery().Encode()), nil, nil, &keys)
|
||||||
|
return keys, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMyPublicKeys list all the public keys of current user
|
||||||
|
func (c *Client) ListMyPublicKeys(opt ListPublicKeysOptions) ([]*PublicKey, *Response, error) {
|
||||||
|
opt.setDefaults()
|
||||||
|
keys := make([]*PublicKey, 0, opt.PageSize)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/keys?%s", opt.getURLQuery().Encode()), nil, nil, &keys)
|
||||||
|
return keys, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublicKey get current user's public key by key id
|
||||||
|
func (c *Client) GetPublicKey(keyID int64) (*PublicKey, *Response, error) {
|
||||||
|
key := new(PublicKey)
|
||||||
|
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/keys/%d", keyID), nil, nil, &key)
|
||||||
|
return key, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateKeyOption options when creating a key
|
||||||
|
type CreateKeyOption struct {
|
||||||
|
// Title of the key to add
|
||||||
|
Title string `json:"title"`
|
||||||
|
// An armored SSH key to add
|
||||||
|
Key string `json:"key"`
|
||||||
|
// Describe if the key has only read access or read/write
|
||||||
|
ReadOnly bool `json:"read_only"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePublicKey create public key with options
|
||||||
|
func (c *Client) CreatePublicKey(opt CreateKeyOption) (*PublicKey, *Response, error) {
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
key := new(PublicKey)
|
||||||
|
resp, err := c.getParsedResponse("POST", "/user/keys", jsonHeader, bytes.NewReader(body), key)
|
||||||
|
return key, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePublicKey delete public key with key id
|
||||||
|
func (c *Client) DeletePublicKey(keyID int64) (*Response, error) {
|
||||||
|
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/keys/%d", keyID), nil, nil)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
48
vendor/code.gitea.io/sdk/gitea/user_search.go
generated
vendored
Normal file
48
vendor/code.gitea.io/sdk/gitea/user_search.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
type searchUsersResponse struct {
|
||||||
|
Users []*User `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchUsersOption options for SearchUsers
|
||||||
|
type SearchUsersOption struct {
|
||||||
|
ListOptions
|
||||||
|
KeyWord string
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryEncode turns options into querystring argument
|
||||||
|
func (opt *SearchUsersOption) QueryEncode() string {
|
||||||
|
query := make(url.Values)
|
||||||
|
if opt.Page > 0 {
|
||||||
|
query.Add("page", fmt.Sprintf("%d", opt.Page))
|
||||||
|
}
|
||||||
|
if opt.PageSize > 0 {
|
||||||
|
query.Add("limit", fmt.Sprintf("%d", opt.PageSize))
|
||||||
|
}
|
||||||
|
if len(opt.KeyWord) > 0 {
|
||||||
|
query.Add("q", opt.KeyWord)
|
||||||
|
}
|
||||||
|
return query.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) searchUsers(rawQuery string) ([]*User, *Response, error) {
|
||||||
|
link, _ := url.Parse("/users/search")
|
||||||
|
link.RawQuery = rawQuery
|
||||||
|
userResp := new(searchUsersResponse)
|
||||||
|
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &userResp)
|
||||||
|
return userResp.Users, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchUsers finds users by query
|
||||||
|
func (c *Client) SearchUsers(opt SearchUsersOption) ([]*User, *Response, error) {
|
||||||
|
return c.searchUsers(opt.QueryEncode())
|
||||||
|
}
|
||||||
62
vendor/code.gitea.io/sdk/gitea/user_settings.go
generated
vendored
Normal file
62
vendor/code.gitea.io/sdk/gitea/user_settings.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserSettings represents user settings
|
||||||
|
type UserSettings struct {
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
Website string `json:"website"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
Language string `json:"language"`
|
||||||
|
Theme string `json:"theme"`
|
||||||
|
DiffViewStyle string `json:"diff_view_style"`
|
||||||
|
// Privacy
|
||||||
|
HideEmail bool `json:"hide_email"`
|
||||||
|
HideActivity bool `json:"hide_activity"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserSettingsOptions represents options to change user settings
|
||||||
|
type UserSettingsOptions struct {
|
||||||
|
FullName *string `json:"full_name,omitempty"`
|
||||||
|
Website *string `json:"website,omitempty"`
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
Location *string `json:"location,omitempty"`
|
||||||
|
Language *string `json:"language,omitempty"`
|
||||||
|
Theme *string `json:"theme,omitempty"`
|
||||||
|
DiffViewStyle *string `json:"diff_view_style,omitempty"`
|
||||||
|
// Privacy
|
||||||
|
HideEmail *bool `json:"hide_email,omitempty"`
|
||||||
|
HideActivity *bool `json:"hide_activity,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserSettings returns user settings
|
||||||
|
func (c *Client) GetUserSettings() (*UserSettings, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
userConfig := new(UserSettings)
|
||||||
|
resp, err := c.getParsedResponse("GET", "/user/settings", nil, nil, userConfig)
|
||||||
|
return userConfig, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUserSettings returns user settings
|
||||||
|
func (c *Client) UpdateUserSettings(opt UserSettingsOptions) (*UserSettings, *Response, error) {
|
||||||
|
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
userConfig := new(UserSettings)
|
||||||
|
resp, err := c.getParsedResponse("PATCH", "/user/settings", jsonHeader, bytes.NewReader(body), userConfig)
|
||||||
|
return userConfig, resp, err
|
||||||
|
}
|
||||||
126
vendor/code.gitea.io/sdk/gitea/version.go
generated
vendored
Normal file
126
vendor/code.gitea.io/sdk/gitea/version.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-version"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServerVersion returns the version of the server
|
||||||
|
func (c *Client) ServerVersion() (string, *Response, error) {
|
||||||
|
v := struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
}{}
|
||||||
|
resp, err := c.getParsedResponse("GET", "/version", nil, nil, &v)
|
||||||
|
return v.Version, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckServerVersionConstraint validates that the login's server satisfies a
|
||||||
|
// given version constraint such as ">= 1.11.0+dev"
|
||||||
|
func (c *Client) CheckServerVersionConstraint(constraint string) error {
|
||||||
|
if err := c.loadServerVersion(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
check, err := version.NewConstraint(constraint)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !check.Check(c.serverVersion) {
|
||||||
|
c.mutex.RLock()
|
||||||
|
url := c.url
|
||||||
|
c.mutex.RUnlock()
|
||||||
|
return fmt.Errorf("gitea server at %s does not satisfy version constraint %s", url, constraint)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGiteaVersion configures the Client to assume the given version of the
|
||||||
|
// Gitea server, instead of querying the server for it when initializing.
|
||||||
|
// Use "" to skip all canonical ways in the SDK to check for versions
|
||||||
|
func SetGiteaVersion(v string) ClientOption {
|
||||||
|
if v == "" {
|
||||||
|
return func(c *Client) error {
|
||||||
|
c.ignoreVersion = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return func(c *Client) (err error) {
|
||||||
|
c.getVersionOnce.Do(func() {
|
||||||
|
c.serverVersion, err = version.NewVersion(v)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// predefined versions only have to be parsed by library once
|
||||||
|
var (
|
||||||
|
version1_11_0 = version.Must(version.NewVersion("1.11.0"))
|
||||||
|
version1_11_5 = version.Must(version.NewVersion("1.11.5"))
|
||||||
|
version1_12_0 = version.Must(version.NewVersion("1.12.0"))
|
||||||
|
version1_12_3 = version.Must(version.NewVersion("1.12.3"))
|
||||||
|
version1_13_0 = version.Must(version.NewVersion("1.13.0"))
|
||||||
|
version1_14_0 = version.Must(version.NewVersion("1.14.0"))
|
||||||
|
version1_15_0 = version.Must(version.NewVersion("1.15.0"))
|
||||||
|
version1_16_0 = version.Must(version.NewVersion("1.16.0"))
|
||||||
|
version1_17_0 = version.Must(version.NewVersion("1.17.0"))
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrUnknownVersion is an unknown version from the API
|
||||||
|
type ErrUnknownVersion struct {
|
||||||
|
raw string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error fulfills error
|
||||||
|
func (e ErrUnknownVersion) Error() string {
|
||||||
|
return fmt.Sprintf("unknown version: %s", e.raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_ ErrUnknownVersion) Is(target error) bool {
|
||||||
|
_, ok1 := target.(*ErrUnknownVersion)
|
||||||
|
_, ok2 := target.(ErrUnknownVersion)
|
||||||
|
return ok1 || ok2
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkServerVersionGreaterThanOrEqual is the canonical way in the SDK to check for versions for API compatibility reasons
|
||||||
|
func (c *Client) checkServerVersionGreaterThanOrEqual(v *version.Version) error {
|
||||||
|
if c.ignoreVersion {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := c.loadServerVersion(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.serverVersion.GreaterThanOrEqual(v) {
|
||||||
|
c.mutex.RLock()
|
||||||
|
url := c.url
|
||||||
|
c.mutex.RUnlock()
|
||||||
|
return fmt.Errorf("gitea server at %s is older than %s", url, v.Original())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadServerVersion init the serverVersion variable
|
||||||
|
func (c *Client) loadServerVersion() (err error) {
|
||||||
|
c.getVersionOnce.Do(func() {
|
||||||
|
raw, _, err2 := c.ServerVersion()
|
||||||
|
if err2 != nil {
|
||||||
|
err = err2
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.serverVersion, err = version.NewVersion(raw); err != nil {
|
||||||
|
if strings.TrimSpace(raw) != "" {
|
||||||
|
// Version was something, just not recognized
|
||||||
|
c.serverVersion = version1_11_0
|
||||||
|
err = &ErrUnknownVersion{raw: raw}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
29
vendor/github.com/Masterminds/semver/.travis.yml
generated
vendored
Normal file
29
vendor/github.com/Masterminds/semver/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.6.x
|
||||||
|
- 1.7.x
|
||||||
|
- 1.8.x
|
||||||
|
- 1.9.x
|
||||||
|
- 1.10.x
|
||||||
|
- 1.11.x
|
||||||
|
- 1.12.x
|
||||||
|
- tip
|
||||||
|
|
||||||
|
# Setting sudo access to false will let Travis CI use containers rather than
|
||||||
|
# VMs to run the tests. For more details see:
|
||||||
|
# - http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
||||||
|
# - http://docs.travis-ci.com/user/workers/standard-infrastructure/
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
script:
|
||||||
|
- make setup
|
||||||
|
- make test
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
webhooks:
|
||||||
|
urls:
|
||||||
|
- https://webhooks.gitter.im/e/06e3328629952dabe3e0
|
||||||
|
on_success: change # options: [always|never|change] default: always
|
||||||
|
on_failure: always # options: [always|never|change] default: always
|
||||||
|
on_start: never # options: [always|never|change] default: always
|
||||||
19
vendor/github.com/Masterminds/semver/LICENSE.txt
generated
vendored
Normal file
19
vendor/github.com/Masterminds/semver/LICENSE.txt
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright (C) 2014-2019, Matt Butcher and Matt Farina
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
36
vendor/github.com/Masterminds/semver/Makefile
generated
vendored
Normal file
36
vendor/github.com/Masterminds/semver/Makefile
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
.PHONY: setup
|
||||||
|
setup:
|
||||||
|
go get -u gopkg.in/alecthomas/gometalinter.v1
|
||||||
|
gometalinter.v1 --install
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test: validate lint
|
||||||
|
@echo "==> Running tests"
|
||||||
|
go test -v
|
||||||
|
|
||||||
|
.PHONY: validate
|
||||||
|
validate:
|
||||||
|
@echo "==> Running static validations"
|
||||||
|
@gometalinter.v1 \
|
||||||
|
--disable-all \
|
||||||
|
--enable deadcode \
|
||||||
|
--severity deadcode:error \
|
||||||
|
--enable gofmt \
|
||||||
|
--enable gosimple \
|
||||||
|
--enable ineffassign \
|
||||||
|
--enable misspell \
|
||||||
|
--enable vet \
|
||||||
|
--tests \
|
||||||
|
--vendor \
|
||||||
|
--deadline 60s \
|
||||||
|
./... || exit_code=1
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint:
|
||||||
|
@echo "==> Running linters"
|
||||||
|
@gometalinter.v1 \
|
||||||
|
--disable-all \
|
||||||
|
--enable golint \
|
||||||
|
--vendor \
|
||||||
|
--deadline 60s \
|
||||||
|
./... || :
|
||||||
194
vendor/github.com/Masterminds/semver/README.md
generated
vendored
Normal file
194
vendor/github.com/Masterminds/semver/README.md
generated
vendored
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
# SemVer
|
||||||
|
|
||||||
|
The `semver` package provides the ability to work with [Semantic Versions](http://semver.org) in Go. Specifically it provides the ability to:
|
||||||
|
|
||||||
|
* Parse semantic versions
|
||||||
|
* Sort semantic versions
|
||||||
|
* Check if a semantic version fits within a set of constraints
|
||||||
|
* Optionally work with a `v` prefix
|
||||||
|
|
||||||
|
[](https://masterminds.github.io/stability/active.html)
|
||||||
|
[](https://travis-ci.org/Masterminds/semver) [](https://ci.appveyor.com/project/mattfarina/semver/branch/master) [](https://godoc.org/github.com/Masterminds/semver) [](https://goreportcard.com/report/github.com/Masterminds/semver)
|
||||||
|
|
||||||
|
If you are looking for a command line tool for version comparisons please see
|
||||||
|
[vert](https://github.com/Masterminds/vert) which uses this library.
|
||||||
|
|
||||||
|
## Parsing Semantic Versions
|
||||||
|
|
||||||
|
To parse a semantic version use the `NewVersion` function. For example,
|
||||||
|
|
||||||
|
```go
|
||||||
|
v, err := semver.NewVersion("1.2.3-beta.1+build345")
|
||||||
|
```
|
||||||
|
|
||||||
|
If there is an error the version wasn't parseable. The version object has methods
|
||||||
|
to get the parts of the version, compare it to other versions, convert the
|
||||||
|
version back into a string, and get the original string. For more details
|
||||||
|
please see the [documentation](https://godoc.org/github.com/Masterminds/semver).
|
||||||
|
|
||||||
|
## Sorting Semantic Versions
|
||||||
|
|
||||||
|
A set of versions can be sorted using the [`sort`](https://golang.org/pkg/sort/)
|
||||||
|
package from the standard library. For example,
|
||||||
|
|
||||||
|
```go
|
||||||
|
raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",}
|
||||||
|
vs := make([]*semver.Version, len(raw))
|
||||||
|
for i, r := range raw {
|
||||||
|
v, err := semver.NewVersion(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error parsing version: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vs[i] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(semver.Collection(vs))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Checking Version Constraints
|
||||||
|
|
||||||
|
Checking a version against version constraints is one of the most featureful
|
||||||
|
parts of the package.
|
||||||
|
|
||||||
|
```go
|
||||||
|
c, err := semver.NewConstraint(">= 1.2.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle constraint not being parseable.
|
||||||
|
}
|
||||||
|
|
||||||
|
v, _ := semver.NewVersion("1.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle version not being parseable.
|
||||||
|
}
|
||||||
|
// Check if the version meets the constraints. The a variable will be true.
|
||||||
|
a := c.Check(v)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Basic Comparisons
|
||||||
|
|
||||||
|
There are two elements to the comparisons. First, a comparison string is a list
|
||||||
|
of comma separated and comparisons. These are then separated by || separated or
|
||||||
|
comparisons. For example, `">= 1.2, < 3.0.0 || >= 4.2.3"` is looking for a
|
||||||
|
comparison that's greater than or equal to 1.2 and less than 3.0.0 or is
|
||||||
|
greater than or equal to 4.2.3.
|
||||||
|
|
||||||
|
The basic comparisons are:
|
||||||
|
|
||||||
|
* `=`: equal (aliased to no operator)
|
||||||
|
* `!=`: not equal
|
||||||
|
* `>`: greater than
|
||||||
|
* `<`: less than
|
||||||
|
* `>=`: greater than or equal to
|
||||||
|
* `<=`: less than or equal to
|
||||||
|
|
||||||
|
## Working With Pre-release Versions
|
||||||
|
|
||||||
|
Pre-releases, for those not familiar with them, are used for software releases
|
||||||
|
prior to stable or generally available releases. Examples of pre-releases include
|
||||||
|
development, alpha, beta, and release candidate releases. A pre-release may be
|
||||||
|
a version such as `1.2.3-beta.1` while the stable release would be `1.2.3`. In the
|
||||||
|
order of precidence, pre-releases come before their associated releases. In this
|
||||||
|
example `1.2.3-beta.1 < 1.2.3`.
|
||||||
|
|
||||||
|
According to the Semantic Version specification pre-releases may not be
|
||||||
|
API compliant with their release counterpart. It says,
|
||||||
|
|
||||||
|
> A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version.
|
||||||
|
|
||||||
|
SemVer comparisons without a pre-release comparator will skip pre-release versions.
|
||||||
|
For example, `>=1.2.3` will skip pre-releases when looking at a list of releases
|
||||||
|
while `>=1.2.3-0` will evaluate and find pre-releases.
|
||||||
|
|
||||||
|
The reason for the `0` as a pre-release version in the example comparison is
|
||||||
|
because pre-releases can only contain ASCII alphanumerics and hyphens (along with
|
||||||
|
`.` separators), per the spec. Sorting happens in ASCII sort order, again per the spec. The lowest character is a `0` in ASCII sort order (see an [ASCII Table](http://www.asciitable.com/))
|
||||||
|
|
||||||
|
Understanding ASCII sort ordering is important because A-Z comes before a-z. That
|
||||||
|
means `>=1.2.3-BETA` will return `1.2.3-alpha`. What you might expect from case
|
||||||
|
sensitivity doesn't apply here. This is due to ASCII sort ordering which is what
|
||||||
|
the spec specifies.
|
||||||
|
|
||||||
|
## Hyphen Range Comparisons
|
||||||
|
|
||||||
|
There are multiple methods to handle ranges and the first is hyphens ranges.
|
||||||
|
These look like:
|
||||||
|
|
||||||
|
* `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5`
|
||||||
|
* `2.3.4 - 4.5` which is equivalent to `>= 2.3.4, <= 4.5`
|
||||||
|
|
||||||
|
## Wildcards In Comparisons
|
||||||
|
|
||||||
|
The `x`, `X`, and `*` characters can be used as a wildcard character. This works
|
||||||
|
for all comparison operators. When used on the `=` operator it falls
|
||||||
|
back to the pack level comparison (see tilde below). For example,
|
||||||
|
|
||||||
|
* `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
|
||||||
|
* `>= 1.2.x` is equivalent to `>= 1.2.0`
|
||||||
|
* `<= 2.x` is equivalent to `< 3`
|
||||||
|
* `*` is equivalent to `>= 0.0.0`
|
||||||
|
|
||||||
|
## Tilde Range Comparisons (Patch)
|
||||||
|
|
||||||
|
The tilde (`~`) comparison operator is for patch level ranges when a minor
|
||||||
|
version is specified and major level changes when the minor number is missing.
|
||||||
|
For example,
|
||||||
|
|
||||||
|
* `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0`
|
||||||
|
* `~1` is equivalent to `>= 1, < 2`
|
||||||
|
* `~2.3` is equivalent to `>= 2.3, < 2.4`
|
||||||
|
* `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
|
||||||
|
* `~1.x` is equivalent to `>= 1, < 2`
|
||||||
|
|
||||||
|
## Caret Range Comparisons (Major)
|
||||||
|
|
||||||
|
The caret (`^`) comparison operator is for major level changes. This is useful
|
||||||
|
when comparisons of API versions as a major change is API breaking. For example,
|
||||||
|
|
||||||
|
* `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0`
|
||||||
|
* `^0.0.1` is equivalent to `>= 0.0.1, < 1.0.0`
|
||||||
|
* `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0`
|
||||||
|
* `^2.3` is equivalent to `>= 2.3, < 3`
|
||||||
|
* `^2.x` is equivalent to `>= 2.0.0, < 3`
|
||||||
|
|
||||||
|
# Validation
|
||||||
|
|
||||||
|
In addition to testing a version against a constraint, a version can be validated
|
||||||
|
against a constraint. When validation fails a slice of errors containing why a
|
||||||
|
version didn't meet the constraint is returned. For example,
|
||||||
|
|
||||||
|
```go
|
||||||
|
c, err := semver.NewConstraint("<= 1.2.3, >= 1.4")
|
||||||
|
if err != nil {
|
||||||
|
// Handle constraint not being parseable.
|
||||||
|
}
|
||||||
|
|
||||||
|
v, _ := semver.NewVersion("1.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle version not being parseable.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate a version against a constraint.
|
||||||
|
a, msgs := c.Validate(v)
|
||||||
|
// a is false
|
||||||
|
for _, m := range msgs {
|
||||||
|
fmt.Println(m)
|
||||||
|
|
||||||
|
// Loops over the errors which would read
|
||||||
|
// "1.3 is greater than 1.2.3"
|
||||||
|
// "1.3 is less than 1.4"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# Fuzzing
|
||||||
|
|
||||||
|
[dvyukov/go-fuzz](https://github.com/dvyukov/go-fuzz) is used for fuzzing.
|
||||||
|
|
||||||
|
1. `go-fuzz-build`
|
||||||
|
2. `go-fuzz -workdir=fuzz`
|
||||||
|
|
||||||
|
# Contribute
|
||||||
|
|
||||||
|
If you find an issue or want to contribute please file an [issue](https://github.com/Masterminds/semver/issues)
|
||||||
|
or [create a pull request](https://github.com/Masterminds/semver/pulls).
|
||||||
44
vendor/github.com/Masterminds/semver/appveyor.yml
generated
vendored
Normal file
44
vendor/github.com/Masterminds/semver/appveyor.yml
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
version: build-{build}.{branch}
|
||||||
|
|
||||||
|
clone_folder: C:\gopath\src\github.com\Masterminds\semver
|
||||||
|
shallow_clone: true
|
||||||
|
|
||||||
|
environment:
|
||||||
|
GOPATH: C:\gopath
|
||||||
|
|
||||||
|
platform:
|
||||||
|
- x64
|
||||||
|
|
||||||
|
install:
|
||||||
|
- go version
|
||||||
|
- go env
|
||||||
|
- go get -u gopkg.in/alecthomas/gometalinter.v1
|
||||||
|
- set PATH=%PATH%;%GOPATH%\bin
|
||||||
|
- gometalinter.v1.exe --install
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- go install -v ./...
|
||||||
|
|
||||||
|
test_script:
|
||||||
|
- "gometalinter.v1 \
|
||||||
|
--disable-all \
|
||||||
|
--enable deadcode \
|
||||||
|
--severity deadcode:error \
|
||||||
|
--enable gofmt \
|
||||||
|
--enable gosimple \
|
||||||
|
--enable ineffassign \
|
||||||
|
--enable misspell \
|
||||||
|
--enable vet \
|
||||||
|
--tests \
|
||||||
|
--vendor \
|
||||||
|
--deadline 60s \
|
||||||
|
./... || exit_code=1"
|
||||||
|
- "gometalinter.v1 \
|
||||||
|
--disable-all \
|
||||||
|
--enable golint \
|
||||||
|
--vendor \
|
||||||
|
--deadline 60s \
|
||||||
|
./... || :"
|
||||||
|
- go test -v
|
||||||
|
|
||||||
|
deploy: off
|
||||||
24
vendor/github.com/Masterminds/semver/collection.go
generated
vendored
Normal file
24
vendor/github.com/Masterminds/semver/collection.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package semver
|
||||||
|
|
||||||
|
// Collection is a collection of Version instances and implements the sort
|
||||||
|
// interface. See the sort package for more details.
|
||||||
|
// https://golang.org/pkg/sort/
|
||||||
|
type Collection []*Version
|
||||||
|
|
||||||
|
// Len returns the length of a collection. The number of Version instances
|
||||||
|
// on the slice.
|
||||||
|
func (c Collection) Len() int {
|
||||||
|
return len(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less is needed for the sort interface to compare two Version objects on the
|
||||||
|
// slice. If checks if one is less than the other.
|
||||||
|
func (c Collection) Less(i, j int) bool {
|
||||||
|
return c[i].LessThan(c[j])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap is needed for the sort interface to replace the Version objects
|
||||||
|
// at two different positions in the slice.
|
||||||
|
func (c Collection) Swap(i, j int) {
|
||||||
|
c[i], c[j] = c[j], c[i]
|
||||||
|
}
|
||||||
423
vendor/github.com/Masterminds/semver/constraints.go
generated
vendored
Normal file
423
vendor/github.com/Masterminds/semver/constraints.go
generated
vendored
Normal file
@@ -0,0 +1,423 @@
|
|||||||
|
package semver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Constraints is one or more constraint that a semantic version can be
|
||||||
|
// checked against.
|
||||||
|
type Constraints struct {
|
||||||
|
constraints [][]*constraint
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConstraint returns a Constraints instance that a Version instance can
|
||||||
|
// be checked against. If there is a parse error it will be returned.
|
||||||
|
func NewConstraint(c string) (*Constraints, error) {
|
||||||
|
|
||||||
|
// Rewrite - ranges into a comparison operation.
|
||||||
|
c = rewriteRange(c)
|
||||||
|
|
||||||
|
ors := strings.Split(c, "||")
|
||||||
|
or := make([][]*constraint, len(ors))
|
||||||
|
for k, v := range ors {
|
||||||
|
cs := strings.Split(v, ",")
|
||||||
|
result := make([]*constraint, len(cs))
|
||||||
|
for i, s := range cs {
|
||||||
|
pc, err := parseConstraint(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result[i] = pc
|
||||||
|
}
|
||||||
|
or[k] = result
|
||||||
|
}
|
||||||
|
|
||||||
|
o := &Constraints{constraints: or}
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check tests if a version satisfies the constraints.
|
||||||
|
func (cs Constraints) Check(v *Version) bool {
|
||||||
|
// loop over the ORs and check the inner ANDs
|
||||||
|
for _, o := range cs.constraints {
|
||||||
|
joy := true
|
||||||
|
for _, c := range o {
|
||||||
|
if !c.check(v) {
|
||||||
|
joy = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if joy {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate checks if a version satisfies a constraint. If not a slice of
|
||||||
|
// reasons for the failure are returned in addition to a bool.
|
||||||
|
func (cs Constraints) Validate(v *Version) (bool, []error) {
|
||||||
|
// loop over the ORs and check the inner ANDs
|
||||||
|
var e []error
|
||||||
|
|
||||||
|
// Capture the prerelease message only once. When it happens the first time
|
||||||
|
// this var is marked
|
||||||
|
var prerelesase bool
|
||||||
|
for _, o := range cs.constraints {
|
||||||
|
joy := true
|
||||||
|
for _, c := range o {
|
||||||
|
// Before running the check handle the case there the version is
|
||||||
|
// a prerelease and the check is not searching for prereleases.
|
||||||
|
if c.con.pre == "" && v.pre != "" {
|
||||||
|
if !prerelesase {
|
||||||
|
em := fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
|
||||||
|
e = append(e, em)
|
||||||
|
prerelesase = true
|
||||||
|
}
|
||||||
|
joy = false
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if !c.check(v) {
|
||||||
|
em := fmt.Errorf(c.msg, v, c.orig)
|
||||||
|
e = append(e, em)
|
||||||
|
joy = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if joy {
|
||||||
|
return true, []error{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, e
|
||||||
|
}
|
||||||
|
|
||||||
|
var constraintOps map[string]cfunc
|
||||||
|
var constraintMsg map[string]string
|
||||||
|
var constraintRegex *regexp.Regexp
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
constraintOps = map[string]cfunc{
|
||||||
|
"": constraintTildeOrEqual,
|
||||||
|
"=": constraintTildeOrEqual,
|
||||||
|
"!=": constraintNotEqual,
|
||||||
|
">": constraintGreaterThan,
|
||||||
|
"<": constraintLessThan,
|
||||||
|
">=": constraintGreaterThanEqual,
|
||||||
|
"=>": constraintGreaterThanEqual,
|
||||||
|
"<=": constraintLessThanEqual,
|
||||||
|
"=<": constraintLessThanEqual,
|
||||||
|
"~": constraintTilde,
|
||||||
|
"~>": constraintTilde,
|
||||||
|
"^": constraintCaret,
|
||||||
|
}
|
||||||
|
|
||||||
|
constraintMsg = map[string]string{
|
||||||
|
"": "%s is not equal to %s",
|
||||||
|
"=": "%s is not equal to %s",
|
||||||
|
"!=": "%s is equal to %s",
|
||||||
|
">": "%s is less than or equal to %s",
|
||||||
|
"<": "%s is greater than or equal to %s",
|
||||||
|
">=": "%s is less than %s",
|
||||||
|
"=>": "%s is less than %s",
|
||||||
|
"<=": "%s is greater than %s",
|
||||||
|
"=<": "%s is greater than %s",
|
||||||
|
"~": "%s does not have same major and minor version as %s",
|
||||||
|
"~>": "%s does not have same major and minor version as %s",
|
||||||
|
"^": "%s does not have same major version as %s",
|
||||||
|
}
|
||||||
|
|
||||||
|
ops := make([]string, 0, len(constraintOps))
|
||||||
|
for k := range constraintOps {
|
||||||
|
ops = append(ops, regexp.QuoteMeta(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
constraintRegex = regexp.MustCompile(fmt.Sprintf(
|
||||||
|
`^\s*(%s)\s*(%s)\s*$`,
|
||||||
|
strings.Join(ops, "|"),
|
||||||
|
cvRegex))
|
||||||
|
|
||||||
|
constraintRangeRegex = regexp.MustCompile(fmt.Sprintf(
|
||||||
|
`\s*(%s)\s+-\s+(%s)\s*`,
|
||||||
|
cvRegex, cvRegex))
|
||||||
|
}
|
||||||
|
|
||||||
|
// An individual constraint
|
||||||
|
type constraint struct {
|
||||||
|
// The callback function for the restraint. It performs the logic for
|
||||||
|
// the constraint.
|
||||||
|
function cfunc
|
||||||
|
|
||||||
|
msg string
|
||||||
|
|
||||||
|
// The version used in the constraint check. For example, if a constraint
|
||||||
|
// is '<= 2.0.0' the con a version instance representing 2.0.0.
|
||||||
|
con *Version
|
||||||
|
|
||||||
|
// The original parsed version (e.g., 4.x from != 4.x)
|
||||||
|
orig string
|
||||||
|
|
||||||
|
// When an x is used as part of the version (e.g., 1.x)
|
||||||
|
minorDirty bool
|
||||||
|
dirty bool
|
||||||
|
patchDirty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a version meets the constraint
|
||||||
|
func (c *constraint) check(v *Version) bool {
|
||||||
|
return c.function(v, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
type cfunc func(v *Version, c *constraint) bool
|
||||||
|
|
||||||
|
func parseConstraint(c string) (*constraint, error) {
|
||||||
|
m := constraintRegex.FindStringSubmatch(c)
|
||||||
|
if m == nil {
|
||||||
|
return nil, fmt.Errorf("improper constraint: %s", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
ver := m[2]
|
||||||
|
orig := ver
|
||||||
|
minorDirty := false
|
||||||
|
patchDirty := false
|
||||||
|
dirty := false
|
||||||
|
if isX(m[3]) {
|
||||||
|
ver = "0.0.0"
|
||||||
|
dirty = true
|
||||||
|
} else if isX(strings.TrimPrefix(m[4], ".")) || m[4] == "" {
|
||||||
|
minorDirty = true
|
||||||
|
dirty = true
|
||||||
|
ver = fmt.Sprintf("%s.0.0%s", m[3], m[6])
|
||||||
|
} else if isX(strings.TrimPrefix(m[5], ".")) {
|
||||||
|
dirty = true
|
||||||
|
patchDirty = true
|
||||||
|
ver = fmt.Sprintf("%s%s.0%s", m[3], m[4], m[6])
|
||||||
|
}
|
||||||
|
|
||||||
|
con, err := NewVersion(ver)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
// The constraintRegex should catch any regex parsing errors. So,
|
||||||
|
// we should never get here.
|
||||||
|
return nil, errors.New("constraint Parser Error")
|
||||||
|
}
|
||||||
|
|
||||||
|
cs := &constraint{
|
||||||
|
function: constraintOps[m[1]],
|
||||||
|
msg: constraintMsg[m[1]],
|
||||||
|
con: con,
|
||||||
|
orig: orig,
|
||||||
|
minorDirty: minorDirty,
|
||||||
|
patchDirty: patchDirty,
|
||||||
|
dirty: dirty,
|
||||||
|
}
|
||||||
|
return cs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constraint functions
|
||||||
|
func constraintNotEqual(v *Version, c *constraint) bool {
|
||||||
|
if c.dirty {
|
||||||
|
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.con.Major() != v.Major() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if c.con.Minor() != v.Minor() && !c.minorDirty {
|
||||||
|
return true
|
||||||
|
} else if c.minorDirty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return !v.Equal(c.con)
|
||||||
|
}
|
||||||
|
|
||||||
|
func constraintGreaterThan(v *Version, c *constraint) bool {
|
||||||
|
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.Compare(c.con) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func constraintLessThan(v *Version, c *constraint) bool {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.dirty {
|
||||||
|
return v.Compare(c.con) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Major() > c.con.Major() {
|
||||||
|
return false
|
||||||
|
} else if v.Minor() > c.con.Minor() && !c.minorDirty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func constraintGreaterThanEqual(v *Version, c *constraint) bool {
|
||||||
|
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.Compare(c.con) >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func constraintLessThanEqual(v *Version, c *constraint) bool {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.dirty {
|
||||||
|
return v.Compare(c.con) <= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Major() > c.con.Major() {
|
||||||
|
return false
|
||||||
|
} else if v.Minor() > c.con.Minor() && !c.minorDirty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~*, ~>* --> >= 0.0.0 (any)
|
||||||
|
// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0, <3.0.0
|
||||||
|
// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0, <2.1.0
|
||||||
|
// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0, <1.3.0
|
||||||
|
// ~1.2.3, ~>1.2.3 --> >=1.2.3, <1.3.0
|
||||||
|
// ~1.2.0, ~>1.2.0 --> >=1.2.0, <1.3.0
|
||||||
|
func constraintTilde(v *Version, c *constraint) bool {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.LessThan(c.con) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~0.0.0 is a special case where all constraints are accepted. It's
|
||||||
|
// equivalent to >= 0.0.0.
|
||||||
|
if c.con.Major() == 0 && c.con.Minor() == 0 && c.con.Patch() == 0 &&
|
||||||
|
!c.minorDirty && !c.patchDirty {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Major() != c.con.Major() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Minor() != c.con.Minor() && !c.minorDirty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// When there is a .x (dirty) status it automatically opts in to ~. Otherwise
|
||||||
|
// it's a straight =
|
||||||
|
func constraintTildeOrEqual(v *Version, c *constraint) bool {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.dirty {
|
||||||
|
c.msg = constraintMsg["~"]
|
||||||
|
return constraintTilde(v, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.Equal(c.con)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ^* --> (any)
|
||||||
|
// ^2, ^2.x, ^2.x.x --> >=2.0.0, <3.0.0
|
||||||
|
// ^2.0, ^2.0.x --> >=2.0.0, <3.0.0
|
||||||
|
// ^1.2, ^1.2.x --> >=1.2.0, <2.0.0
|
||||||
|
// ^1.2.3 --> >=1.2.3, <2.0.0
|
||||||
|
// ^1.2.0 --> >=1.2.0, <2.0.0
|
||||||
|
func constraintCaret(v *Version, c *constraint) bool {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.LessThan(c.con) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Major() != c.con.Major() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var constraintRangeRegex *regexp.Regexp
|
||||||
|
|
||||||
|
const cvRegex string = `v?([0-9|x|X|\*]+)(\.[0-9|x|X|\*]+)?(\.[0-9|x|X|\*]+)?` +
|
||||||
|
`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
|
||||||
|
`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
|
||||||
|
|
||||||
|
func isX(x string) bool {
|
||||||
|
switch x {
|
||||||
|
case "x", "*", "X":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rewriteRange(i string) string {
|
||||||
|
m := constraintRangeRegex.FindAllStringSubmatch(i, -1)
|
||||||
|
if m == nil {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
o := i
|
||||||
|
for _, v := range m {
|
||||||
|
t := fmt.Sprintf(">= %s, <= %s", v[1], v[11])
|
||||||
|
o = strings.Replace(o, v[0], t, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return o
|
||||||
|
}
|
||||||
115
vendor/github.com/Masterminds/semver/doc.go
generated
vendored
Normal file
115
vendor/github.com/Masterminds/semver/doc.go
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
Package semver provides the ability to work with Semantic Versions (http://semver.org) in Go.
|
||||||
|
|
||||||
|
Specifically it provides the ability to:
|
||||||
|
|
||||||
|
* Parse semantic versions
|
||||||
|
* Sort semantic versions
|
||||||
|
* Check if a semantic version fits within a set of constraints
|
||||||
|
* Optionally work with a `v` prefix
|
||||||
|
|
||||||
|
Parsing Semantic Versions
|
||||||
|
|
||||||
|
To parse a semantic version use the `NewVersion` function. For example,
|
||||||
|
|
||||||
|
v, err := semver.NewVersion("1.2.3-beta.1+build345")
|
||||||
|
|
||||||
|
If there is an error the version wasn't parseable. The version object has methods
|
||||||
|
to get the parts of the version, compare it to other versions, convert the
|
||||||
|
version back into a string, and get the original string. For more details
|
||||||
|
please see the documentation at https://godoc.org/github.com/Masterminds/semver.
|
||||||
|
|
||||||
|
Sorting Semantic Versions
|
||||||
|
|
||||||
|
A set of versions can be sorted using the `sort` package from the standard library.
|
||||||
|
For example,
|
||||||
|
|
||||||
|
raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",}
|
||||||
|
vs := make([]*semver.Version, len(raw))
|
||||||
|
for i, r := range raw {
|
||||||
|
v, err := semver.NewVersion(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error parsing version: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vs[i] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(semver.Collection(vs))
|
||||||
|
|
||||||
|
Checking Version Constraints
|
||||||
|
|
||||||
|
Checking a version against version constraints is one of the most featureful
|
||||||
|
parts of the package.
|
||||||
|
|
||||||
|
c, err := semver.NewConstraint(">= 1.2.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle constraint not being parseable.
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := semver.NewVersion("1.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle version not being parseable.
|
||||||
|
}
|
||||||
|
// Check if the version meets the constraints. The a variable will be true.
|
||||||
|
a := c.Check(v)
|
||||||
|
|
||||||
|
Basic Comparisons
|
||||||
|
|
||||||
|
There are two elements to the comparisons. First, a comparison string is a list
|
||||||
|
of comma separated and comparisons. These are then separated by || separated or
|
||||||
|
comparisons. For example, `">= 1.2, < 3.0.0 || >= 4.2.3"` is looking for a
|
||||||
|
comparison that's greater than or equal to 1.2 and less than 3.0.0 or is
|
||||||
|
greater than or equal to 4.2.3.
|
||||||
|
|
||||||
|
The basic comparisons are:
|
||||||
|
|
||||||
|
* `=`: equal (aliased to no operator)
|
||||||
|
* `!=`: not equal
|
||||||
|
* `>`: greater than
|
||||||
|
* `<`: less than
|
||||||
|
* `>=`: greater than or equal to
|
||||||
|
* `<=`: less than or equal to
|
||||||
|
|
||||||
|
Hyphen Range Comparisons
|
||||||
|
|
||||||
|
There are multiple methods to handle ranges and the first is hyphens ranges.
|
||||||
|
These look like:
|
||||||
|
|
||||||
|
* `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5`
|
||||||
|
* `2.3.4 - 4.5` which is equivalent to `>= 2.3.4, <= 4.5`
|
||||||
|
|
||||||
|
Wildcards In Comparisons
|
||||||
|
|
||||||
|
The `x`, `X`, and `*` characters can be used as a wildcard character. This works
|
||||||
|
for all comparison operators. When used on the `=` operator it falls
|
||||||
|
back to the pack level comparison (see tilde below). For example,
|
||||||
|
|
||||||
|
* `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
|
||||||
|
* `>= 1.2.x` is equivalent to `>= 1.2.0`
|
||||||
|
* `<= 2.x` is equivalent to `<= 3`
|
||||||
|
* `*` is equivalent to `>= 0.0.0`
|
||||||
|
|
||||||
|
Tilde Range Comparisons (Patch)
|
||||||
|
|
||||||
|
The tilde (`~`) comparison operator is for patch level ranges when a minor
|
||||||
|
version is specified and major level changes when the minor number is missing.
|
||||||
|
For example,
|
||||||
|
|
||||||
|
* `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0`
|
||||||
|
* `~1` is equivalent to `>= 1, < 2`
|
||||||
|
* `~2.3` is equivalent to `>= 2.3, < 2.4`
|
||||||
|
* `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
|
||||||
|
* `~1.x` is equivalent to `>= 1, < 2`
|
||||||
|
|
||||||
|
Caret Range Comparisons (Major)
|
||||||
|
|
||||||
|
The caret (`^`) comparison operator is for major level changes. This is useful
|
||||||
|
when comparisons of API versions as a major change is API breaking. For example,
|
||||||
|
|
||||||
|
* `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0`
|
||||||
|
* `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0`
|
||||||
|
* `^2.3` is equivalent to `>= 2.3, < 3`
|
||||||
|
* `^2.x` is equivalent to `>= 2.0.0, < 3`
|
||||||
|
*/
|
||||||
|
package semver
|
||||||
425
vendor/github.com/Masterminds/semver/version.go
generated
vendored
Normal file
425
vendor/github.com/Masterminds/semver/version.go
generated
vendored
Normal file
@@ -0,0 +1,425 @@
|
|||||||
|
package semver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The compiled version of the regex created at init() is cached here so it
|
||||||
|
// only needs to be created once.
|
||||||
|
var versionRegex *regexp.Regexp
|
||||||
|
var validPrereleaseRegex *regexp.Regexp
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrInvalidSemVer is returned a version is found to be invalid when
|
||||||
|
// being parsed.
|
||||||
|
ErrInvalidSemVer = errors.New("Invalid Semantic Version")
|
||||||
|
|
||||||
|
// ErrInvalidMetadata is returned when the metadata is an invalid format
|
||||||
|
ErrInvalidMetadata = errors.New("Invalid Metadata string")
|
||||||
|
|
||||||
|
// ErrInvalidPrerelease is returned when the pre-release is an invalid format
|
||||||
|
ErrInvalidPrerelease = errors.New("Invalid Prerelease string")
|
||||||
|
)
|
||||||
|
|
||||||
|
// SemVerRegex is the regular expression used to parse a semantic version.
|
||||||
|
const SemVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` +
|
||||||
|
`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
|
||||||
|
`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
|
||||||
|
|
||||||
|
// ValidPrerelease is the regular expression which validates
|
||||||
|
// both prerelease and metadata values.
|
||||||
|
const ValidPrerelease string = `^([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*)$`
|
||||||
|
|
||||||
|
// Version represents a single semantic version.
|
||||||
|
type Version struct {
|
||||||
|
major, minor, patch int64
|
||||||
|
pre string
|
||||||
|
metadata string
|
||||||
|
original string
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
versionRegex = regexp.MustCompile("^" + SemVerRegex + "$")
|
||||||
|
validPrereleaseRegex = regexp.MustCompile(ValidPrerelease)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewVersion parses a given version and returns an instance of Version or
|
||||||
|
// an error if unable to parse the version.
|
||||||
|
func NewVersion(v string) (*Version, error) {
|
||||||
|
m := versionRegex.FindStringSubmatch(v)
|
||||||
|
if m == nil {
|
||||||
|
return nil, ErrInvalidSemVer
|
||||||
|
}
|
||||||
|
|
||||||
|
sv := &Version{
|
||||||
|
metadata: m[8],
|
||||||
|
pre: m[5],
|
||||||
|
original: v,
|
||||||
|
}
|
||||||
|
|
||||||
|
var temp int64
|
||||||
|
temp, err := strconv.ParseInt(m[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error parsing version segment: %s", err)
|
||||||
|
}
|
||||||
|
sv.major = temp
|
||||||
|
|
||||||
|
if m[2] != "" {
|
||||||
|
temp, err = strconv.ParseInt(strings.TrimPrefix(m[2], "."), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error parsing version segment: %s", err)
|
||||||
|
}
|
||||||
|
sv.minor = temp
|
||||||
|
} else {
|
||||||
|
sv.minor = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if m[3] != "" {
|
||||||
|
temp, err = strconv.ParseInt(strings.TrimPrefix(m[3], "."), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error parsing version segment: %s", err)
|
||||||
|
}
|
||||||
|
sv.patch = temp
|
||||||
|
} else {
|
||||||
|
sv.patch = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return sv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustParse parses a given version and panics on error.
|
||||||
|
func MustParse(v string) *Version {
|
||||||
|
sv, err := NewVersion(v)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return sv
|
||||||
|
}
|
||||||
|
|
||||||
|
// String converts a Version object to a string.
|
||||||
|
// Note, if the original version contained a leading v this version will not.
|
||||||
|
// See the Original() method to retrieve the original value. Semantic Versions
|
||||||
|
// don't contain a leading v per the spec. Instead it's optional on
|
||||||
|
// implementation.
|
||||||
|
func (v *Version) String() string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
fmt.Fprintf(&buf, "%d.%d.%d", v.major, v.minor, v.patch)
|
||||||
|
if v.pre != "" {
|
||||||
|
fmt.Fprintf(&buf, "-%s", v.pre)
|
||||||
|
}
|
||||||
|
if v.metadata != "" {
|
||||||
|
fmt.Fprintf(&buf, "+%s", v.metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Original returns the original value passed in to be parsed.
|
||||||
|
func (v *Version) Original() string {
|
||||||
|
return v.original
|
||||||
|
}
|
||||||
|
|
||||||
|
// Major returns the major version.
|
||||||
|
func (v *Version) Major() int64 {
|
||||||
|
return v.major
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minor returns the minor version.
|
||||||
|
func (v *Version) Minor() int64 {
|
||||||
|
return v.minor
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch returns the patch version.
|
||||||
|
func (v *Version) Patch() int64 {
|
||||||
|
return v.patch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prerelease returns the pre-release version.
|
||||||
|
func (v *Version) Prerelease() string {
|
||||||
|
return v.pre
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata returns the metadata on the version.
|
||||||
|
func (v *Version) Metadata() string {
|
||||||
|
return v.metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
// originalVPrefix returns the original 'v' prefix if any.
|
||||||
|
func (v *Version) originalVPrefix() string {
|
||||||
|
|
||||||
|
// Note, only lowercase v is supported as a prefix by the parser.
|
||||||
|
if v.original != "" && v.original[:1] == "v" {
|
||||||
|
return v.original[:1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncPatch produces the next patch version.
|
||||||
|
// If the current version does not have prerelease/metadata information,
|
||||||
|
// it unsets metadata and prerelease values, increments patch number.
|
||||||
|
// If the current version has any of prerelease or metadata information,
|
||||||
|
// it unsets both values and keeps curent patch value
|
||||||
|
func (v Version) IncPatch() Version {
|
||||||
|
vNext := v
|
||||||
|
// according to http://semver.org/#spec-item-9
|
||||||
|
// Pre-release versions have a lower precedence than the associated normal version.
|
||||||
|
// according to http://semver.org/#spec-item-10
|
||||||
|
// Build metadata SHOULD be ignored when determining version precedence.
|
||||||
|
if v.pre != "" {
|
||||||
|
vNext.metadata = ""
|
||||||
|
vNext.pre = ""
|
||||||
|
} else {
|
||||||
|
vNext.metadata = ""
|
||||||
|
vNext.pre = ""
|
||||||
|
vNext.patch = v.patch + 1
|
||||||
|
}
|
||||||
|
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||||
|
return vNext
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncMinor produces the next minor version.
|
||||||
|
// Sets patch to 0.
|
||||||
|
// Increments minor number.
|
||||||
|
// Unsets metadata.
|
||||||
|
// Unsets prerelease status.
|
||||||
|
func (v Version) IncMinor() Version {
|
||||||
|
vNext := v
|
||||||
|
vNext.metadata = ""
|
||||||
|
vNext.pre = ""
|
||||||
|
vNext.patch = 0
|
||||||
|
vNext.minor = v.minor + 1
|
||||||
|
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||||
|
return vNext
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncMajor produces the next major version.
|
||||||
|
// Sets patch to 0.
|
||||||
|
// Sets minor to 0.
|
||||||
|
// Increments major number.
|
||||||
|
// Unsets metadata.
|
||||||
|
// Unsets prerelease status.
|
||||||
|
func (v Version) IncMajor() Version {
|
||||||
|
vNext := v
|
||||||
|
vNext.metadata = ""
|
||||||
|
vNext.pre = ""
|
||||||
|
vNext.patch = 0
|
||||||
|
vNext.minor = 0
|
||||||
|
vNext.major = v.major + 1
|
||||||
|
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||||
|
return vNext
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPrerelease defines the prerelease value.
|
||||||
|
// Value must not include the required 'hypen' prefix.
|
||||||
|
func (v Version) SetPrerelease(prerelease string) (Version, error) {
|
||||||
|
vNext := v
|
||||||
|
if len(prerelease) > 0 && !validPrereleaseRegex.MatchString(prerelease) {
|
||||||
|
return vNext, ErrInvalidPrerelease
|
||||||
|
}
|
||||||
|
vNext.pre = prerelease
|
||||||
|
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||||
|
return vNext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMetadata defines metadata value.
|
||||||
|
// Value must not include the required 'plus' prefix.
|
||||||
|
func (v Version) SetMetadata(metadata string) (Version, error) {
|
||||||
|
vNext := v
|
||||||
|
if len(metadata) > 0 && !validPrereleaseRegex.MatchString(metadata) {
|
||||||
|
return vNext, ErrInvalidMetadata
|
||||||
|
}
|
||||||
|
vNext.metadata = metadata
|
||||||
|
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||||
|
return vNext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LessThan tests if one version is less than another one.
|
||||||
|
func (v *Version) LessThan(o *Version) bool {
|
||||||
|
return v.Compare(o) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GreaterThan tests if one version is greater than another one.
|
||||||
|
func (v *Version) GreaterThan(o *Version) bool {
|
||||||
|
return v.Compare(o) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal tests if two versions are equal to each other.
|
||||||
|
// Note, versions can be equal with different metadata since metadata
|
||||||
|
// is not considered part of the comparable version.
|
||||||
|
func (v *Version) Equal(o *Version) bool {
|
||||||
|
return v.Compare(o) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare compares this version to another one. It returns -1, 0, or 1 if
|
||||||
|
// the version smaller, equal, or larger than the other version.
|
||||||
|
//
|
||||||
|
// Versions are compared by X.Y.Z. Build metadata is ignored. Prerelease is
|
||||||
|
// lower than the version without a prerelease.
|
||||||
|
func (v *Version) Compare(o *Version) int {
|
||||||
|
// Compare the major, minor, and patch version for differences. If a
|
||||||
|
// difference is found return the comparison.
|
||||||
|
if d := compareSegment(v.Major(), o.Major()); d != 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
if d := compareSegment(v.Minor(), o.Minor()); d != 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
if d := compareSegment(v.Patch(), o.Patch()); d != 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point the major, minor, and patch versions are the same.
|
||||||
|
ps := v.pre
|
||||||
|
po := o.Prerelease()
|
||||||
|
|
||||||
|
if ps == "" && po == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if ps == "" {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if po == "" {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return comparePrerelease(ps, po)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements JSON.Unmarshaler interface.
|
||||||
|
func (v *Version) UnmarshalJSON(b []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(b, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
temp, err := NewVersion(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.major = temp.major
|
||||||
|
v.minor = temp.minor
|
||||||
|
v.patch = temp.patch
|
||||||
|
v.pre = temp.pre
|
||||||
|
v.metadata = temp.metadata
|
||||||
|
v.original = temp.original
|
||||||
|
temp = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements JSON.Marshaler interface.
|
||||||
|
func (v *Version) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(v.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareSegment(v, o int64) int {
|
||||||
|
if v < o {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if v > o {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func comparePrerelease(v, o string) int {
|
||||||
|
|
||||||
|
// split the prelease versions by their part. The separator, per the spec,
|
||||||
|
// is a .
|
||||||
|
sparts := strings.Split(v, ".")
|
||||||
|
oparts := strings.Split(o, ".")
|
||||||
|
|
||||||
|
// Find the longer length of the parts to know how many loop iterations to
|
||||||
|
// go through.
|
||||||
|
slen := len(sparts)
|
||||||
|
olen := len(oparts)
|
||||||
|
|
||||||
|
l := slen
|
||||||
|
if olen > slen {
|
||||||
|
l = olen
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over each part of the prereleases to compare the differences.
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
// Since the lentgh of the parts can be different we need to create
|
||||||
|
// a placeholder. This is to avoid out of bounds issues.
|
||||||
|
stemp := ""
|
||||||
|
if i < slen {
|
||||||
|
stemp = sparts[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
otemp := ""
|
||||||
|
if i < olen {
|
||||||
|
otemp = oparts[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
d := comparePrePart(stemp, otemp)
|
||||||
|
if d != 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reaching here means two versions are of equal value but have different
|
||||||
|
// metadata (the part following a +). They are not identical in string form
|
||||||
|
// but the version comparison finds them to be equal.
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func comparePrePart(s, o string) int {
|
||||||
|
// Fastpath if they are equal
|
||||||
|
if s == o {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// When s or o are empty we can use the other in an attempt to determine
|
||||||
|
// the response.
|
||||||
|
if s == "" {
|
||||||
|
if o != "" {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if o == "" {
|
||||||
|
if s != "" {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// When comparing strings "99" is greater than "103". To handle
|
||||||
|
// cases like this we need to detect numbers and compare them. According
|
||||||
|
// to the semver spec, numbers are always positive. If there is a - at the
|
||||||
|
// start like -99 this is to be evaluated as an alphanum. numbers always
|
||||||
|
// have precedence over alphanum. Parsing as Uints because negative numbers
|
||||||
|
// are ignored.
|
||||||
|
|
||||||
|
oi, n1 := strconv.ParseUint(o, 10, 64)
|
||||||
|
si, n2 := strconv.ParseUint(s, 10, 64)
|
||||||
|
|
||||||
|
// The case where both are strings compare the strings
|
||||||
|
if n1 != nil && n2 != nil {
|
||||||
|
if s > o {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
} else if n1 != nil {
|
||||||
|
// o is a string and s is a number
|
||||||
|
return -1
|
||||||
|
} else if n2 != nil {
|
||||||
|
// s is a string and o is a number
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
// Both are numbers
|
||||||
|
if si > oi {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
|
||||||
|
}
|
||||||
10
vendor/github.com/Masterminds/semver/version_fuzz.go
generated
vendored
Normal file
10
vendor/github.com/Masterminds/semver/version_fuzz.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// +build gofuzz
|
||||||
|
|
||||||
|
package semver
|
||||||
|
|
||||||
|
func Fuzz(data []byte) int {
|
||||||
|
if _, err := NewVersion(string(data)); err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
1
vendor/github.com/Microsoft/go-winio/.gitattributes
generated
vendored
Normal file
1
vendor/github.com/Microsoft/go-winio/.gitattributes
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* text=auto eol=lf
|
||||||
10
vendor/github.com/Microsoft/go-winio/.gitignore
generated
vendored
Normal file
10
vendor/github.com/Microsoft/go-winio/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
.vscode/
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
|
||||||
|
# testing
|
||||||
|
testdata
|
||||||
|
|
||||||
|
# go workspaces
|
||||||
|
go.work
|
||||||
|
go.work.sum
|
||||||
144
vendor/github.com/Microsoft/go-winio/.golangci.yml
generated
vendored
Normal file
144
vendor/github.com/Microsoft/go-winio/.golangci.yml
generated
vendored
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
run:
|
||||||
|
skip-dirs:
|
||||||
|
- pkg/etw/sample
|
||||||
|
|
||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
# style
|
||||||
|
- containedctx # struct contains a context
|
||||||
|
- dupl # duplicate code
|
||||||
|
- errname # erorrs are named correctly
|
||||||
|
- goconst # strings that should be constants
|
||||||
|
- godot # comments end in a period
|
||||||
|
- misspell
|
||||||
|
- nolintlint # "//nolint" directives are properly explained
|
||||||
|
- revive # golint replacement
|
||||||
|
- stylecheck # golint replacement, less configurable than revive
|
||||||
|
- unconvert # unnecessary conversions
|
||||||
|
- wastedassign
|
||||||
|
|
||||||
|
# bugs, performance, unused, etc ...
|
||||||
|
- contextcheck # function uses a non-inherited context
|
||||||
|
- errorlint # errors not wrapped for 1.13
|
||||||
|
- exhaustive # check exhaustiveness of enum switch statements
|
||||||
|
- gofmt # files are gofmt'ed
|
||||||
|
- gosec # security
|
||||||
|
- nestif # deeply nested ifs
|
||||||
|
- nilerr # returns nil even with non-nil error
|
||||||
|
- prealloc # slices that can be pre-allocated
|
||||||
|
- structcheck # unused struct fields
|
||||||
|
- unparam # unused function params
|
||||||
|
|
||||||
|
issues:
|
||||||
|
exclude-rules:
|
||||||
|
# err is very often shadowed in nested scopes
|
||||||
|
- linters:
|
||||||
|
- govet
|
||||||
|
text: '^shadow: declaration of "err" shadows declaration'
|
||||||
|
|
||||||
|
# ignore long lines for skip autogen directives
|
||||||
|
- linters:
|
||||||
|
- revive
|
||||||
|
text: "^line-length-limit: "
|
||||||
|
source: "^//(go:generate|sys) "
|
||||||
|
|
||||||
|
# allow unjustified ignores of error checks in defer statements
|
||||||
|
- linters:
|
||||||
|
- nolintlint
|
||||||
|
text: "^directive `//nolint:errcheck` should provide explanation"
|
||||||
|
source: '^\s*defer '
|
||||||
|
|
||||||
|
# allow unjustified ignores of error lints for io.EOF
|
||||||
|
- linters:
|
||||||
|
- nolintlint
|
||||||
|
text: "^directive `//nolint:errorlint` should provide explanation"
|
||||||
|
source: '[=|!]= io.EOF'
|
||||||
|
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
govet:
|
||||||
|
enable-all: true
|
||||||
|
disable:
|
||||||
|
# struct order is often for Win32 compat
|
||||||
|
# also, ignore pointer bytes/GC issues for now until performance becomes an issue
|
||||||
|
- fieldalignment
|
||||||
|
check-shadowing: true
|
||||||
|
nolintlint:
|
||||||
|
allow-leading-space: false
|
||||||
|
require-explanation: true
|
||||||
|
require-specific: true
|
||||||
|
revive:
|
||||||
|
# revive is more configurable than static check, so likely the preferred alternative to static-check
|
||||||
|
# (once the perf issue is solved: https://github.com/golangci/golangci-lint/issues/2997)
|
||||||
|
enable-all-rules:
|
||||||
|
true
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md
|
||||||
|
rules:
|
||||||
|
# rules with required arguments
|
||||||
|
- name: argument-limit
|
||||||
|
disabled: true
|
||||||
|
- name: banned-characters
|
||||||
|
disabled: true
|
||||||
|
- name: cognitive-complexity
|
||||||
|
disabled: true
|
||||||
|
- name: cyclomatic
|
||||||
|
disabled: true
|
||||||
|
- name: file-header
|
||||||
|
disabled: true
|
||||||
|
- name: function-length
|
||||||
|
disabled: true
|
||||||
|
- name: function-result-limit
|
||||||
|
disabled: true
|
||||||
|
- name: max-public-structs
|
||||||
|
disabled: true
|
||||||
|
# geneally annoying rules
|
||||||
|
- name: add-constant # complains about any and all strings and integers
|
||||||
|
disabled: true
|
||||||
|
- name: confusing-naming # we frequently use "Foo()" and "foo()" together
|
||||||
|
disabled: true
|
||||||
|
- name: flag-parameter # excessive, and a common idiom we use
|
||||||
|
disabled: true
|
||||||
|
# general config
|
||||||
|
- name: line-length-limit
|
||||||
|
arguments:
|
||||||
|
- 140
|
||||||
|
- name: var-naming
|
||||||
|
arguments:
|
||||||
|
- []
|
||||||
|
- - CID
|
||||||
|
- CRI
|
||||||
|
- CTRD
|
||||||
|
- DACL
|
||||||
|
- DLL
|
||||||
|
- DOS
|
||||||
|
- ETW
|
||||||
|
- FSCTL
|
||||||
|
- GCS
|
||||||
|
- GMSA
|
||||||
|
- HCS
|
||||||
|
- HV
|
||||||
|
- IO
|
||||||
|
- LCOW
|
||||||
|
- LDAP
|
||||||
|
- LPAC
|
||||||
|
- LTSC
|
||||||
|
- MMIO
|
||||||
|
- NT
|
||||||
|
- OCI
|
||||||
|
- PMEM
|
||||||
|
- PWSH
|
||||||
|
- RX
|
||||||
|
- SACl
|
||||||
|
- SID
|
||||||
|
- SMB
|
||||||
|
- TX
|
||||||
|
- VHD
|
||||||
|
- VHDX
|
||||||
|
- VMID
|
||||||
|
- VPCI
|
||||||
|
- WCOW
|
||||||
|
- WIM
|
||||||
|
stylecheck:
|
||||||
|
checks:
|
||||||
|
- "all"
|
||||||
|
- "-ST1003" # use revive's var naming
|
||||||
1
vendor/github.com/Microsoft/go-winio/CODEOWNERS
generated
vendored
Normal file
1
vendor/github.com/Microsoft/go-winio/CODEOWNERS
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* @microsoft/containerplat
|
||||||
22
vendor/github.com/Microsoft/go-winio/LICENSE
generated
vendored
Normal file
22
vendor/github.com/Microsoft/go-winio/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Microsoft
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
89
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
Normal file
89
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# go-winio [](https://github.com/microsoft/go-winio/actions/workflows/ci.yml)
|
||||||
|
|
||||||
|
This repository contains utilities for efficiently performing Win32 IO operations in
|
||||||
|
Go. Currently, this is focused on accessing named pipes and other file handles, and
|
||||||
|
for using named pipes as a net transport.
|
||||||
|
|
||||||
|
This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go
|
||||||
|
to reuse the thread to schedule another goroutine. This limits support to Windows Vista and
|
||||||
|
newer operating systems. This is similar to the implementation of network sockets in Go's net
|
||||||
|
package.
|
||||||
|
|
||||||
|
Please see the LICENSE file for licensing information.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
This project welcomes contributions and suggestions.
|
||||||
|
Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that
|
||||||
|
you have the right to, and actually do, grant us the rights to use your contribution.
|
||||||
|
For details, visit [Microsoft CLA](https://cla.microsoft.com).
|
||||||
|
|
||||||
|
When you submit a pull request, a CLA-bot will automatically determine whether you need to
|
||||||
|
provide a CLA and decorate the PR appropriately (e.g., label, comment).
|
||||||
|
Simply follow the instructions provided by the bot.
|
||||||
|
You will only need to do this once across all repos using our CLA.
|
||||||
|
|
||||||
|
Additionally, the pull request pipeline requires the following steps to be performed before
|
||||||
|
mergining.
|
||||||
|
|
||||||
|
### Code Sign-Off
|
||||||
|
|
||||||
|
We require that contributors sign their commits using [`git commit --signoff`][git-commit-s]
|
||||||
|
to certify they either authored the work themselves or otherwise have permission to use it in this project.
|
||||||
|
|
||||||
|
A range of commits can be signed off using [`git rebase --signoff`][git-rebase-s].
|
||||||
|
|
||||||
|
Please see [the developer certificate](https://developercertificate.org) for more info,
|
||||||
|
as well as to make sure that you can attest to the rules listed.
|
||||||
|
Our CI uses the DCO Github app to ensure that all commits in a given PR are signed-off.
|
||||||
|
|
||||||
|
### Linting
|
||||||
|
|
||||||
|
Code must pass a linting stage, which uses [`golangci-lint`][lint].
|
||||||
|
The linting settings are stored in [`.golangci.yaml`](./.golangci.yaml), and can be run
|
||||||
|
automatically with VSCode by adding the following to your workspace or folder settings:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"go.lintTool": "golangci-lint",
|
||||||
|
"go.lintOnSave": "package",
|
||||||
|
```
|
||||||
|
|
||||||
|
Additional editor [integrations options are also available][lint-ide].
|
||||||
|
|
||||||
|
Alternatively, `golangci-lint` can be [installed locally][lint-install] and run from the repo root:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# use . or specify a path to only lint a package
|
||||||
|
# to show all lint errors, use flags "--max-issues-per-linter=0 --max-same-issues=0"
|
||||||
|
> golangci-lint run ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Go Generate
|
||||||
|
|
||||||
|
The pipeline checks that auto-generated code, via `go generate`, are up to date.
|
||||||
|
|
||||||
|
This can be done for the entire repo:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
> go generate ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||||
|
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||||
|
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||||
|
|
||||||
|
## Special Thanks
|
||||||
|
|
||||||
|
Thanks to [natefinch][natefinch] for the inspiration for this library.
|
||||||
|
See [npipe](https://github.com/natefinch/npipe) for another named pipe implementation.
|
||||||
|
|
||||||
|
[lint]: https://golangci-lint.run/
|
||||||
|
[lint-ide]: https://golangci-lint.run/usage/integrations/#editor-integration
|
||||||
|
[lint-install]: https://golangci-lint.run/usage/install/#local-installation
|
||||||
|
|
||||||
|
[git-commit-s]: https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s
|
||||||
|
[git-rebase-s]: https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---signoff
|
||||||
|
|
||||||
|
[natefinch]: https://github.com/natefinch
|
||||||
41
vendor/github.com/Microsoft/go-winio/SECURITY.md
generated
vendored
Normal file
41
vendor/github.com/Microsoft/go-winio/SECURITY.md
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.7 BLOCK -->
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
|
||||||
|
|
||||||
|
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
|
||||||
|
|
||||||
|
## Reporting Security Issues
|
||||||
|
|
||||||
|
**Please do not report security vulnerabilities through public GitHub issues.**
|
||||||
|
|
||||||
|
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
|
||||||
|
|
||||||
|
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
|
||||||
|
|
||||||
|
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
|
||||||
|
|
||||||
|
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||||
|
|
||||||
|
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||||
|
* Full paths of source file(s) related to the manifestation of the issue
|
||||||
|
* The location of the affected source code (tag/branch/commit or direct URL)
|
||||||
|
* Any special configuration required to reproduce the issue
|
||||||
|
* Step-by-step instructions to reproduce the issue
|
||||||
|
* Proof-of-concept or exploit code (if possible)
|
||||||
|
* Impact of the issue, including how an attacker might exploit the issue
|
||||||
|
|
||||||
|
This information will help us triage your report more quickly.
|
||||||
|
|
||||||
|
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
|
||||||
|
|
||||||
|
## Preferred Languages
|
||||||
|
|
||||||
|
We prefer all communications to be in English.
|
||||||
|
|
||||||
|
## Policy
|
||||||
|
|
||||||
|
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
|
||||||
|
|
||||||
|
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
||||||
290
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
Normal file
290
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
||||||
|
//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
||||||
|
|
||||||
|
const (
|
||||||
|
BackupData = uint32(iota + 1)
|
||||||
|
BackupEaData
|
||||||
|
BackupSecurity
|
||||||
|
BackupAlternateData
|
||||||
|
BackupLink
|
||||||
|
BackupPropertyData
|
||||||
|
BackupObjectId //revive:disable-line:var-naming ID, not Id
|
||||||
|
BackupReparseData
|
||||||
|
BackupSparseBlock
|
||||||
|
BackupTxfsData
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
StreamSparseAttributes = uint32(8)
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint:revive // var-naming: ALL_CAPS
|
||||||
|
const (
|
||||||
|
WRITE_DAC = windows.WRITE_DAC
|
||||||
|
WRITE_OWNER = windows.WRITE_OWNER
|
||||||
|
ACCESS_SYSTEM_SECURITY = windows.ACCESS_SYSTEM_SECURITY
|
||||||
|
)
|
||||||
|
|
||||||
|
// BackupHeader represents a backup stream of a file.
|
||||||
|
type BackupHeader struct {
|
||||||
|
//revive:disable-next-line:var-naming ID, not Id
|
||||||
|
Id uint32 // The backup stream ID
|
||||||
|
Attributes uint32 // Stream attributes
|
||||||
|
Size int64 // The size of the stream in bytes
|
||||||
|
Name string // The name of the stream (for BackupAlternateData only).
|
||||||
|
Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
|
||||||
|
}
|
||||||
|
|
||||||
|
type win32StreamID struct {
|
||||||
|
StreamID uint32
|
||||||
|
Attributes uint32
|
||||||
|
Size uint64
|
||||||
|
NameSize uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series
|
||||||
|
// of BackupHeader values.
|
||||||
|
type BackupStreamReader struct {
|
||||||
|
r io.Reader
|
||||||
|
bytesLeft int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBackupStreamReader produces a BackupStreamReader from any io.Reader.
|
||||||
|
func NewBackupStreamReader(r io.Reader) *BackupStreamReader {
|
||||||
|
return &BackupStreamReader{r, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if
|
||||||
|
// it was not completely read.
|
||||||
|
func (r *BackupStreamReader) Next() (*BackupHeader, error) {
|
||||||
|
if r.bytesLeft > 0 { //nolint:nestif // todo: flatten this
|
||||||
|
if s, ok := r.r.(io.Seeker); ok {
|
||||||
|
// Make sure Seek on io.SeekCurrent sometimes succeeds
|
||||||
|
// before trying the actual seek.
|
||||||
|
if _, err := s.Seek(0, io.SeekCurrent); err == nil {
|
||||||
|
if _, err = s.Seek(r.bytesLeft, io.SeekCurrent); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r.bytesLeft = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(io.Discard, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var wsi win32StreamID
|
||||||
|
if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hdr := &BackupHeader{
|
||||||
|
Id: wsi.StreamID,
|
||||||
|
Attributes: wsi.Attributes,
|
||||||
|
Size: int64(wsi.Size),
|
||||||
|
}
|
||||||
|
if wsi.NameSize != 0 {
|
||||||
|
name := make([]uint16, int(wsi.NameSize/2))
|
||||||
|
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hdr.Name = syscall.UTF16ToString(name)
|
||||||
|
}
|
||||||
|
if wsi.StreamID == BackupSparseBlock {
|
||||||
|
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hdr.Size -= 8
|
||||||
|
}
|
||||||
|
r.bytesLeft = hdr.Size
|
||||||
|
return hdr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads from the current backup stream.
|
||||||
|
func (r *BackupStreamReader) Read(b []byte) (int, error) {
|
||||||
|
if r.bytesLeft == 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
if int64(len(b)) > r.bytesLeft {
|
||||||
|
b = b[:r.bytesLeft]
|
||||||
|
}
|
||||||
|
n, err := r.r.Read(b)
|
||||||
|
r.bytesLeft -= int64(n)
|
||||||
|
if err == io.EOF {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
} else if r.bytesLeft == 0 && err == nil {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API.
|
||||||
|
type BackupStreamWriter struct {
|
||||||
|
w io.Writer
|
||||||
|
bytesLeft int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer.
|
||||||
|
func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter {
|
||||||
|
return &BackupStreamWriter{w, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteHeader writes the next backup stream header and prepares for calls to Write().
|
||||||
|
func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error {
|
||||||
|
if w.bytesLeft != 0 {
|
||||||
|
return fmt.Errorf("missing %d bytes", w.bytesLeft)
|
||||||
|
}
|
||||||
|
name := utf16.Encode([]rune(hdr.Name))
|
||||||
|
wsi := win32StreamID{
|
||||||
|
StreamID: hdr.Id,
|
||||||
|
Attributes: hdr.Attributes,
|
||||||
|
Size: uint64(hdr.Size),
|
||||||
|
NameSize: uint32(len(name) * 2),
|
||||||
|
}
|
||||||
|
if hdr.Id == BackupSparseBlock {
|
||||||
|
// Include space for the int64 block offset
|
||||||
|
wsi.Size += 8
|
||||||
|
}
|
||||||
|
if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(name) != 0 {
|
||||||
|
if err := binary.Write(w.w, binary.LittleEndian, name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hdr.Id == BackupSparseBlock {
|
||||||
|
if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.bytesLeft = hdr.Size
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes to the current backup stream.
|
||||||
|
func (w *BackupStreamWriter) Write(b []byte) (int, error) {
|
||||||
|
if w.bytesLeft < int64(len(b)) {
|
||||||
|
return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft)
|
||||||
|
}
|
||||||
|
n, err := w.w.Write(b)
|
||||||
|
w.bytesLeft -= int64(n)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API.
|
||||||
|
type BackupFileReader struct {
|
||||||
|
f *os.File
|
||||||
|
includeSecurity bool
|
||||||
|
ctx uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true,
|
||||||
|
// Read will attempt to read the security descriptor of the file.
|
||||||
|
func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
|
||||||
|
r := &BackupFileReader{f, includeSecurity, 0}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
||||||
|
func (r *BackupFileReader) Read(b []byte) (int, error) {
|
||||||
|
var bytesRead uint32
|
||||||
|
err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, &os.PathError{Op: "BackupRead", Path: r.f.Name(), Err: err}
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(r.f)
|
||||||
|
if bytesRead == 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
return int(bytesRead), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close frees Win32 resources associated with the BackupFileReader. It does not close
|
||||||
|
// the underlying file.
|
||||||
|
func (r *BackupFileReader) Close() error {
|
||||||
|
if r.ctx != 0 {
|
||||||
|
_ = backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
|
||||||
|
runtime.KeepAlive(r.f)
|
||||||
|
r.ctx = 0
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API.
|
||||||
|
type BackupFileWriter struct {
|
||||||
|
f *os.File
|
||||||
|
includeSecurity bool
|
||||||
|
ctx uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBackupFileWriter returns a new BackupFileWriter from a file handle. If includeSecurity is true,
|
||||||
|
// Write() will attempt to restore the security descriptor from the stream.
|
||||||
|
func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
|
||||||
|
w := &BackupFileWriter{f, includeSecurity, 0}
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write restores a portion of the file using the provided backup stream.
|
||||||
|
func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
||||||
|
var bytesWritten uint32
|
||||||
|
err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, &os.PathError{Op: "BackupWrite", Path: w.f.Name(), Err: err}
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(w.f)
|
||||||
|
if int(bytesWritten) != len(b) {
|
||||||
|
return int(bytesWritten), errors.New("not all bytes could be written")
|
||||||
|
}
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close frees Win32 resources associated with the BackupFileWriter. It does not
|
||||||
|
// close the underlying file.
|
||||||
|
func (w *BackupFileWriter) Close() error {
|
||||||
|
if w.ctx != 0 {
|
||||||
|
_ = backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
|
||||||
|
runtime.KeepAlive(w.f)
|
||||||
|
w.ctx = 0
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenForBackup opens a file or directory, potentially skipping access checks if the backup
|
||||||
|
// or restore privileges have been acquired.
|
||||||
|
//
|
||||||
|
// If the file opened was a directory, it cannot be used with Readdir().
|
||||||
|
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
|
||||||
|
winPath, err := syscall.UTF16FromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
h, err := syscall.CreateFile(&winPath[0],
|
||||||
|
access,
|
||||||
|
share,
|
||||||
|
nil,
|
||||||
|
createmode,
|
||||||
|
syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT,
|
||||||
|
0)
|
||||||
|
if err != nil {
|
||||||
|
err = &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return os.NewFile(uintptr(h), path), nil
|
||||||
|
}
|
||||||
22
vendor/github.com/Microsoft/go-winio/doc.go
generated
vendored
Normal file
22
vendor/github.com/Microsoft/go-winio/doc.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// This package provides utilities for efficiently performing Win32 IO operations in Go.
|
||||||
|
// Currently, this package is provides support for genreal IO and management of
|
||||||
|
// - named pipes
|
||||||
|
// - files
|
||||||
|
// - [Hyper-V sockets]
|
||||||
|
//
|
||||||
|
// This code is similar to Go's [net] package, and uses IO completion ports to avoid
|
||||||
|
// blocking IO on system threads, allowing Go to reuse the thread to schedule other goroutines.
|
||||||
|
//
|
||||||
|
// This limits support to Windows Vista and newer operating systems.
|
||||||
|
//
|
||||||
|
// Additionally, this package provides support for:
|
||||||
|
// - creating and managing GUIDs
|
||||||
|
// - writing to [ETW]
|
||||||
|
// - opening and manageing VHDs
|
||||||
|
// - parsing [Windows Image files]
|
||||||
|
// - auto-generating Win32 API code
|
||||||
|
//
|
||||||
|
// [Hyper-V sockets]: https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service
|
||||||
|
// [ETW]: https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/event-tracing-for-windows--etw-
|
||||||
|
// [Windows Image files]: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/work-with-windows-images
|
||||||
|
package winio
|
||||||
137
vendor/github.com/Microsoft/go-winio/ea.go
generated
vendored
Normal file
137
vendor/github.com/Microsoft/go-winio/ea.go
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fileFullEaInformation struct {
|
||||||
|
NextEntryOffset uint32
|
||||||
|
Flags uint8
|
||||||
|
NameLength uint8
|
||||||
|
ValueLength uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
fileFullEaInformationSize = binary.Size(&fileFullEaInformation{})
|
||||||
|
|
||||||
|
errInvalidEaBuffer = errors.New("invalid extended attribute buffer")
|
||||||
|
errEaNameTooLarge = errors.New("extended attribute name too large")
|
||||||
|
errEaValueTooLarge = errors.New("extended attribute value too large")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExtendedAttribute represents a single Windows EA.
|
||||||
|
type ExtendedAttribute struct {
|
||||||
|
Name string
|
||||||
|
Value []byte
|
||||||
|
Flags uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
|
||||||
|
var info fileFullEaInformation
|
||||||
|
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
|
||||||
|
if err != nil {
|
||||||
|
err = errInvalidEaBuffer
|
||||||
|
return ea, nb, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nameOffset := fileFullEaInformationSize
|
||||||
|
nameLen := int(info.NameLength)
|
||||||
|
valueOffset := nameOffset + int(info.NameLength) + 1
|
||||||
|
valueLen := int(info.ValueLength)
|
||||||
|
nextOffset := int(info.NextEntryOffset)
|
||||||
|
if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
|
||||||
|
err = errInvalidEaBuffer
|
||||||
|
return ea, nb, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ea.Name = string(b[nameOffset : nameOffset+nameLen])
|
||||||
|
ea.Value = b[valueOffset : valueOffset+valueLen]
|
||||||
|
ea.Flags = info.Flags
|
||||||
|
if info.NextEntryOffset != 0 {
|
||||||
|
nb = b[info.NextEntryOffset:]
|
||||||
|
}
|
||||||
|
return ea, nb, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
|
||||||
|
// buffer retrieved from BackupRead, ZwQueryEaFile, etc.
|
||||||
|
func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
|
||||||
|
for len(b) != 0 {
|
||||||
|
ea, nb, err := parseEa(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
eas = append(eas, ea)
|
||||||
|
b = nb
|
||||||
|
}
|
||||||
|
return eas, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
|
||||||
|
if int(uint8(len(ea.Name))) != len(ea.Name) {
|
||||||
|
return errEaNameTooLarge
|
||||||
|
}
|
||||||
|
if int(uint16(len(ea.Value))) != len(ea.Value) {
|
||||||
|
return errEaValueTooLarge
|
||||||
|
}
|
||||||
|
entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value))
|
||||||
|
withPadding := (entrySize + 3) &^ 3
|
||||||
|
nextOffset := uint32(0)
|
||||||
|
if !last {
|
||||||
|
nextOffset = withPadding
|
||||||
|
}
|
||||||
|
info := fileFullEaInformation{
|
||||||
|
NextEntryOffset: nextOffset,
|
||||||
|
Flags: ea.Flags,
|
||||||
|
NameLength: uint8(len(ea.Name)),
|
||||||
|
ValueLength: uint16(len(ea.Value)),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := binary.Write(buf, binary.LittleEndian, &info)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = buf.Write([]byte(ea.Name))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = buf.WriteByte(0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = buf.Write(ea.Value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
|
||||||
|
// buffer for use with BackupWrite, ZwSetEaFile, etc.
|
||||||
|
func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for i := range eas {
|
||||||
|
last := false
|
||||||
|
if i == len(eas)-1 {
|
||||||
|
last = true
|
||||||
|
}
|
||||||
|
|
||||||
|
err := writeEa(&buf, &eas[i], last)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
331
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
Normal file
331
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
Normal file
@@ -0,0 +1,331 @@
|
|||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
|
||||||
|
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
|
||||||
|
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
||||||
|
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
||||||
|
//sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
|
||||||
|
|
||||||
|
type atomicBool int32
|
||||||
|
|
||||||
|
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
|
||||||
|
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
|
||||||
|
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
|
||||||
|
|
||||||
|
//revive:disable-next-line:predeclared Keep "new" to maintain consistency with "atomic" pkg
|
||||||
|
func (b *atomicBool) swap(new bool) bool {
|
||||||
|
var newInt int32
|
||||||
|
if new {
|
||||||
|
newInt = 1
|
||||||
|
}
|
||||||
|
return atomic.SwapInt32((*int32)(b), newInt) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrFileClosed = errors.New("file has already been closed")
|
||||||
|
ErrTimeout = &timeoutError{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type timeoutError struct{}
|
||||||
|
|
||||||
|
func (*timeoutError) Error() string { return "i/o timeout" }
|
||||||
|
func (*timeoutError) Timeout() bool { return true }
|
||||||
|
func (*timeoutError) Temporary() bool { return true }
|
||||||
|
|
||||||
|
type timeoutChan chan struct{}
|
||||||
|
|
||||||
|
var ioInitOnce sync.Once
|
||||||
|
var ioCompletionPort syscall.Handle
|
||||||
|
|
||||||
|
// ioResult contains the result of an asynchronous IO operation.
|
||||||
|
type ioResult struct {
|
||||||
|
bytes uint32
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ioOperation represents an outstanding asynchronous Win32 IO.
|
||||||
|
type ioOperation struct {
|
||||||
|
o syscall.Overlapped
|
||||||
|
ch chan ioResult
|
||||||
|
}
|
||||||
|
|
||||||
|
func initIO() {
|
||||||
|
h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ioCompletionPort = h
|
||||||
|
go ioCompletionProcessor(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
||||||
|
// It takes ownership of this handle and will close it if it is garbage collected.
|
||||||
|
type win32File struct {
|
||||||
|
handle syscall.Handle
|
||||||
|
wg sync.WaitGroup
|
||||||
|
wgLock sync.RWMutex
|
||||||
|
closing atomicBool
|
||||||
|
socket bool
|
||||||
|
readDeadline deadlineHandler
|
||||||
|
writeDeadline deadlineHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
type deadlineHandler struct {
|
||||||
|
setLock sync.Mutex
|
||||||
|
channel timeoutChan
|
||||||
|
channelLock sync.RWMutex
|
||||||
|
timer *time.Timer
|
||||||
|
timedout atomicBool
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeWin32File makes a new win32File from an existing file handle.
|
||||||
|
func makeWin32File(h syscall.Handle) (*win32File, error) {
|
||||||
|
f := &win32File{handle: h}
|
||||||
|
ioInitOnce.Do(initIO)
|
||||||
|
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = setFileCompletionNotificationModes(h, windows.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS|windows.FILE_SKIP_SET_EVENT_ON_HANDLE)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f.readDeadline.channel = make(timeoutChan)
|
||||||
|
f.writeDeadline.channel = make(timeoutChan)
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
||||||
|
// If we return the result of makeWin32File directly, it can result in an
|
||||||
|
// interface-wrapped nil, rather than a nil interface value.
|
||||||
|
f, err := makeWin32File(h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// closeHandle closes the resources associated with a Win32 handle.
|
||||||
|
func (f *win32File) closeHandle() {
|
||||||
|
f.wgLock.Lock()
|
||||||
|
// Atomically set that we are closing, releasing the resources only once.
|
||||||
|
if !f.closing.swap(true) {
|
||||||
|
f.wgLock.Unlock()
|
||||||
|
// cancel all IO and wait for it to complete
|
||||||
|
_ = cancelIoEx(f.handle, nil)
|
||||||
|
f.wg.Wait()
|
||||||
|
// at this point, no new IO can start
|
||||||
|
syscall.Close(f.handle)
|
||||||
|
f.handle = 0
|
||||||
|
} else {
|
||||||
|
f.wgLock.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes a win32File.
|
||||||
|
func (f *win32File) Close() error {
|
||||||
|
f.closeHandle()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClosed checks if the file has been closed.
|
||||||
|
func (f *win32File) IsClosed() bool {
|
||||||
|
return f.closing.isSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareIO prepares for a new IO operation.
|
||||||
|
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
|
||||||
|
func (f *win32File) prepareIO() (*ioOperation, error) {
|
||||||
|
f.wgLock.RLock()
|
||||||
|
if f.closing.isSet() {
|
||||||
|
f.wgLock.RUnlock()
|
||||||
|
return nil, ErrFileClosed
|
||||||
|
}
|
||||||
|
f.wg.Add(1)
|
||||||
|
f.wgLock.RUnlock()
|
||||||
|
c := &ioOperation{}
|
||||||
|
c.ch = make(chan ioResult)
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ioCompletionProcessor processes completed async IOs forever.
|
||||||
|
func ioCompletionProcessor(h syscall.Handle) {
|
||||||
|
for {
|
||||||
|
var bytes uint32
|
||||||
|
var key uintptr
|
||||||
|
var op *ioOperation
|
||||||
|
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE)
|
||||||
|
if op == nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
op.ch <- ioResult{bytes, err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: helsaawy - create an asyncIO version that takes a context
|
||||||
|
|
||||||
|
// asyncIO processes the return value from ReadFile or WriteFile, blocking until
|
||||||
|
// the operation has actually completed.
|
||||||
|
func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
|
||||||
|
if err != syscall.ERROR_IO_PENDING { //nolint:errorlint // err is Errno
|
||||||
|
return int(bytes), err
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.closing.isSet() {
|
||||||
|
_ = cancelIoEx(f.handle, &c.o)
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeout timeoutChan
|
||||||
|
if d != nil {
|
||||||
|
d.channelLock.Lock()
|
||||||
|
timeout = d.channel
|
||||||
|
d.channelLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
var r ioResult
|
||||||
|
select {
|
||||||
|
case r = <-c.ch:
|
||||||
|
err = r.err
|
||||||
|
if err == syscall.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
|
||||||
|
if f.closing.isSet() {
|
||||||
|
err = ErrFileClosed
|
||||||
|
}
|
||||||
|
} else if err != nil && f.socket {
|
||||||
|
// err is from Win32. Query the overlapped structure to get the winsock error.
|
||||||
|
var bytes, flags uint32
|
||||||
|
err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags)
|
||||||
|
}
|
||||||
|
case <-timeout:
|
||||||
|
_ = cancelIoEx(f.handle, &c.o)
|
||||||
|
r = <-c.ch
|
||||||
|
err = r.err
|
||||||
|
if err == syscall.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
|
||||||
|
err = ErrTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// runtime.KeepAlive is needed, as c is passed via native
|
||||||
|
// code to ioCompletionProcessor, c must remain alive
|
||||||
|
// until the channel read is complete.
|
||||||
|
// todo: (de)allocate *ioOperation via win32 heap functions, instead of needing to KeepAlive?
|
||||||
|
runtime.KeepAlive(c)
|
||||||
|
return int(r.bytes), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads from a file handle.
|
||||||
|
func (f *win32File) Read(b []byte) (int, error) {
|
||||||
|
c, err := f.prepareIO()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer f.wg.Done()
|
||||||
|
|
||||||
|
if f.readDeadline.timedout.isSet() {
|
||||||
|
return 0, ErrTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes uint32
|
||||||
|
err = syscall.ReadFile(f.handle, b, &bytes, &c.o)
|
||||||
|
n, err := f.asyncIO(c, &f.readDeadline, bytes, err)
|
||||||
|
runtime.KeepAlive(b)
|
||||||
|
|
||||||
|
// Handle EOF conditions.
|
||||||
|
if err == nil && n == 0 && len(b) != 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
} else if err == syscall.ERROR_BROKEN_PIPE { //nolint:errorlint // err is Errno
|
||||||
|
return 0, io.EOF
|
||||||
|
} else {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes to a file handle.
|
||||||
|
func (f *win32File) Write(b []byte) (int, error) {
|
||||||
|
c, err := f.prepareIO()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer f.wg.Done()
|
||||||
|
|
||||||
|
if f.writeDeadline.timedout.isSet() {
|
||||||
|
return 0, ErrTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes uint32
|
||||||
|
err = syscall.WriteFile(f.handle, b, &bytes, &c.o)
|
||||||
|
n, err := f.asyncIO(c, &f.writeDeadline, bytes, err)
|
||||||
|
runtime.KeepAlive(b)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32File) SetReadDeadline(deadline time.Time) error {
|
||||||
|
return f.readDeadline.set(deadline)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32File) SetWriteDeadline(deadline time.Time) error {
|
||||||
|
return f.writeDeadline.set(deadline)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32File) Flush() error {
|
||||||
|
return syscall.FlushFileBuffers(f.handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32File) Fd() uintptr {
|
||||||
|
return uintptr(f.handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *deadlineHandler) set(deadline time.Time) error {
|
||||||
|
d.setLock.Lock()
|
||||||
|
defer d.setLock.Unlock()
|
||||||
|
|
||||||
|
if d.timer != nil {
|
||||||
|
if !d.timer.Stop() {
|
||||||
|
<-d.channel
|
||||||
|
}
|
||||||
|
d.timer = nil
|
||||||
|
}
|
||||||
|
d.timedout.setFalse()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-d.channel:
|
||||||
|
d.channelLock.Lock()
|
||||||
|
d.channel = make(chan struct{})
|
||||||
|
d.channelLock.Unlock()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
if deadline.IsZero() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
timeoutIO := func() {
|
||||||
|
d.timedout.setTrue()
|
||||||
|
close(d.channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
duration := deadline.Sub(now)
|
||||||
|
if deadline.After(now) {
|
||||||
|
// Deadline is in the future, set a timer to wait
|
||||||
|
d.timer = time.AfterFunc(duration, timeoutIO)
|
||||||
|
} else {
|
||||||
|
// Deadline is in the past. Cancel all pending IO now.
|
||||||
|
timeoutIO()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
92
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
Normal file
92
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileBasicInfo contains file access time and file attributes information.
|
||||||
|
type FileBasicInfo struct {
|
||||||
|
CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime
|
||||||
|
FileAttributes uint32
|
||||||
|
_ uint32 // padding
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileBasicInfo retrieves times and attributes for a file.
|
||||||
|
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
||||||
|
bi := &FileBasicInfo{}
|
||||||
|
if err := windows.GetFileInformationByHandleEx(
|
||||||
|
windows.Handle(f.Fd()),
|
||||||
|
windows.FileBasicInfo,
|
||||||
|
(*byte)(unsafe.Pointer(bi)),
|
||||||
|
uint32(unsafe.Sizeof(*bi)),
|
||||||
|
); err != nil {
|
||||||
|
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(f)
|
||||||
|
return bi, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFileBasicInfo sets times and attributes for a file.
|
||||||
|
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
|
||||||
|
if err := windows.SetFileInformationByHandle(
|
||||||
|
windows.Handle(f.Fd()),
|
||||||
|
windows.FileBasicInfo,
|
||||||
|
(*byte)(unsafe.Pointer(bi)),
|
||||||
|
uint32(unsafe.Sizeof(*bi)),
|
||||||
|
); err != nil {
|
||||||
|
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(f)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileStandardInfo contains extended information for the file.
|
||||||
|
// FILE_STANDARD_INFO in WinBase.h
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_standard_info
|
||||||
|
type FileStandardInfo struct {
|
||||||
|
AllocationSize, EndOfFile int64
|
||||||
|
NumberOfLinks uint32
|
||||||
|
DeletePending, Directory bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileStandardInfo retrieves ended information for the file.
|
||||||
|
func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) {
|
||||||
|
si := &FileStandardInfo{}
|
||||||
|
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()),
|
||||||
|
windows.FileStandardInfo,
|
||||||
|
(*byte)(unsafe.Pointer(si)),
|
||||||
|
uint32(unsafe.Sizeof(*si))); err != nil {
|
||||||
|
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(f)
|
||||||
|
return si, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
|
||||||
|
// unique on a system.
|
||||||
|
type FileIDInfo struct {
|
||||||
|
VolumeSerialNumber uint64
|
||||||
|
FileID [16]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileID retrieves the unique (volume, file ID) pair for a file.
|
||||||
|
func GetFileID(f *os.File) (*FileIDInfo, error) {
|
||||||
|
fileID := &FileIDInfo{}
|
||||||
|
if err := windows.GetFileInformationByHandleEx(
|
||||||
|
windows.Handle(f.Fd()),
|
||||||
|
windows.FileIdInfo,
|
||||||
|
(*byte)(unsafe.Pointer(fileID)),
|
||||||
|
uint32(unsafe.Sizeof(*fileID)),
|
||||||
|
); err != nil {
|
||||||
|
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(f)
|
||||||
|
return fileID, nil
|
||||||
|
}
|
||||||
575
vendor/github.com/Microsoft/go-winio/hvsock.go
generated
vendored
Normal file
575
vendor/github.com/Microsoft/go-winio/hvsock.go
generated
vendored
Normal file
@@ -0,0 +1,575 @@
|
|||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio/internal/socket"
|
||||||
|
"github.com/Microsoft/go-winio/pkg/guid"
|
||||||
|
)
|
||||||
|
|
||||||
|
const afHVSock = 34 // AF_HYPERV
|
||||||
|
|
||||||
|
// Well known Service and VM IDs
|
||||||
|
//https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service#vmid-wildcards
|
||||||
|
|
||||||
|
// HvsockGUIDWildcard is the wildcard VmId for accepting connections from all partitions.
|
||||||
|
func HvsockGUIDWildcard() guid.GUID { // 00000000-0000-0000-0000-000000000000
|
||||||
|
return guid.GUID{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HvsockGUIDBroadcast is the wildcard VmId for broadcasting sends to all partitions.
|
||||||
|
func HvsockGUIDBroadcast() guid.GUID { //ffffffff-ffff-ffff-ffff-ffffffffffff
|
||||||
|
return guid.GUID{
|
||||||
|
Data1: 0xffffffff,
|
||||||
|
Data2: 0xffff,
|
||||||
|
Data3: 0xffff,
|
||||||
|
Data4: [8]uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HvsockGUIDLoopback is the Loopback VmId for accepting connections to the same partition as the connector.
|
||||||
|
func HvsockGUIDLoopback() guid.GUID { // e0e16197-dd56-4a10-9195-5ee7a155a838
|
||||||
|
return guid.GUID{
|
||||||
|
Data1: 0xe0e16197,
|
||||||
|
Data2: 0xdd56,
|
||||||
|
Data3: 0x4a10,
|
||||||
|
Data4: [8]uint8{0x91, 0x95, 0x5e, 0xe7, 0xa1, 0x55, 0xa8, 0x38},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HvsockGUIDSiloHost is the address of a silo's host partition:
|
||||||
|
// - The silo host of a hosted silo is the utility VM.
|
||||||
|
// - The silo host of a silo on a physical host is the physical host.
|
||||||
|
func HvsockGUIDSiloHost() guid.GUID { // 36bd0c5c-7276-4223-88ba-7d03b654c568
|
||||||
|
return guid.GUID{
|
||||||
|
Data1: 0x36bd0c5c,
|
||||||
|
Data2: 0x7276,
|
||||||
|
Data3: 0x4223,
|
||||||
|
Data4: [8]byte{0x88, 0xba, 0x7d, 0x03, 0xb6, 0x54, 0xc5, 0x68},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HvsockGUIDChildren is the wildcard VmId for accepting connections from the connector's child partitions.
|
||||||
|
func HvsockGUIDChildren() guid.GUID { // 90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd
|
||||||
|
return guid.GUID{
|
||||||
|
Data1: 0x90db8b89,
|
||||||
|
Data2: 0xd35,
|
||||||
|
Data3: 0x4f79,
|
||||||
|
Data4: [8]uint8{0x8c, 0xe9, 0x49, 0xea, 0xa, 0xc8, 0xb7, 0xcd},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HvsockGUIDParent is the wildcard VmId for accepting connections from the connector's parent partition.
|
||||||
|
// Listening on this VmId accepts connection from:
|
||||||
|
// - Inside silos: silo host partition.
|
||||||
|
// - Inside hosted silo: host of the VM.
|
||||||
|
// - Inside VM: VM host.
|
||||||
|
// - Physical host: Not supported.
|
||||||
|
func HvsockGUIDParent() guid.GUID { // a42e7cda-d03f-480c-9cc2-a4de20abb878
|
||||||
|
return guid.GUID{
|
||||||
|
Data1: 0xa42e7cda,
|
||||||
|
Data2: 0xd03f,
|
||||||
|
Data3: 0x480c,
|
||||||
|
Data4: [8]uint8{0x9c, 0xc2, 0xa4, 0xde, 0x20, 0xab, 0xb8, 0x78},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hvsockVsockServiceTemplate is the Service GUID used for the VSOCK protocol.
|
||||||
|
func hvsockVsockServiceTemplate() guid.GUID { // 00000000-facb-11e6-bd58-64006a7986d3
|
||||||
|
return guid.GUID{
|
||||||
|
Data2: 0xfacb,
|
||||||
|
Data3: 0x11e6,
|
||||||
|
Data4: [8]uint8{0xbd, 0x58, 0x64, 0x00, 0x6a, 0x79, 0x86, 0xd3},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An HvsockAddr is an address for a AF_HYPERV socket.
|
||||||
|
type HvsockAddr struct {
|
||||||
|
VMID guid.GUID
|
||||||
|
ServiceID guid.GUID
|
||||||
|
}
|
||||||
|
|
||||||
|
type rawHvsockAddr struct {
|
||||||
|
Family uint16
|
||||||
|
_ uint16
|
||||||
|
VMID guid.GUID
|
||||||
|
ServiceID guid.GUID
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ socket.RawSockaddr = &rawHvsockAddr{}
|
||||||
|
|
||||||
|
// Network returns the address's network name, "hvsock".
|
||||||
|
func (*HvsockAddr) Network() string {
|
||||||
|
return "hvsock"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (addr *HvsockAddr) String() string {
|
||||||
|
return fmt.Sprintf("%s:%s", &addr.VMID, &addr.ServiceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port.
|
||||||
|
func VsockServiceID(port uint32) guid.GUID {
|
||||||
|
g := hvsockVsockServiceTemplate() // make a copy
|
||||||
|
g.Data1 = port
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (addr *HvsockAddr) raw() rawHvsockAddr {
|
||||||
|
return rawHvsockAddr{
|
||||||
|
Family: afHVSock,
|
||||||
|
VMID: addr.VMID,
|
||||||
|
ServiceID: addr.ServiceID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (addr *HvsockAddr) fromRaw(raw *rawHvsockAddr) {
|
||||||
|
addr.VMID = raw.VMID
|
||||||
|
addr.ServiceID = raw.ServiceID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sockaddr returns a pointer to and the size of this struct.
|
||||||
|
//
|
||||||
|
// Implements the [socket.RawSockaddr] interface, and allows use in
|
||||||
|
// [socket.Bind] and [socket.ConnectEx].
|
||||||
|
func (r *rawHvsockAddr) Sockaddr() (unsafe.Pointer, int32, error) {
|
||||||
|
return unsafe.Pointer(r), int32(unsafe.Sizeof(rawHvsockAddr{})), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sockaddr interface allows use with `sockets.Bind()` and `.ConnectEx()`.
|
||||||
|
func (r *rawHvsockAddr) FromBytes(b []byte) error {
|
||||||
|
n := int(unsafe.Sizeof(rawHvsockAddr{}))
|
||||||
|
|
||||||
|
if len(b) < n {
|
||||||
|
return fmt.Errorf("got %d, want %d: %w", len(b), n, socket.ErrBufferSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(unsafe.Slice((*byte)(unsafe.Pointer(r)), n), b[:n])
|
||||||
|
if r.Family != afHVSock {
|
||||||
|
return fmt.Errorf("got %d, want %d: %w", r.Family, afHVSock, socket.ErrAddrFamily)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HvsockListener is a socket listener for the AF_HYPERV address family.
|
||||||
|
type HvsockListener struct {
|
||||||
|
sock *win32File
|
||||||
|
addr HvsockAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ net.Listener = &HvsockListener{}
|
||||||
|
|
||||||
|
// HvsockConn is a connected socket of the AF_HYPERV address family.
|
||||||
|
type HvsockConn struct {
|
||||||
|
sock *win32File
|
||||||
|
local, remote HvsockAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ net.Conn = &HvsockConn{}
|
||||||
|
|
||||||
|
func newHVSocket() (*win32File, error) {
|
||||||
|
fd, err := syscall.Socket(afHVSock, syscall.SOCK_STREAM, 1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, os.NewSyscallError("socket", err)
|
||||||
|
}
|
||||||
|
f, err := makeWin32File(fd)
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(fd)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f.socket = true
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenHvsock listens for connections on the specified hvsock address.
|
||||||
|
func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) {
|
||||||
|
l := &HvsockListener{addr: *addr}
|
||||||
|
sock, err := newHVSocket()
|
||||||
|
if err != nil {
|
||||||
|
return nil, l.opErr("listen", err)
|
||||||
|
}
|
||||||
|
sa := addr.raw()
|
||||||
|
err = socket.Bind(windows.Handle(sock.handle), &sa)
|
||||||
|
if err != nil {
|
||||||
|
return nil, l.opErr("listen", os.NewSyscallError("socket", err))
|
||||||
|
}
|
||||||
|
err = syscall.Listen(sock.handle, 16)
|
||||||
|
if err != nil {
|
||||||
|
return nil, l.opErr("listen", os.NewSyscallError("listen", err))
|
||||||
|
}
|
||||||
|
return &HvsockListener{sock: sock, addr: *addr}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *HvsockListener) opErr(op string, err error) error {
|
||||||
|
return &net.OpError{Op: op, Net: "hvsock", Addr: &l.addr, Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr returns the listener's network address.
|
||||||
|
func (l *HvsockListener) Addr() net.Addr {
|
||||||
|
return &l.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept waits for the next connection and returns it.
|
||||||
|
func (l *HvsockListener) Accept() (_ net.Conn, err error) {
|
||||||
|
sock, err := newHVSocket()
|
||||||
|
if err != nil {
|
||||||
|
return nil, l.opErr("accept", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if sock != nil {
|
||||||
|
sock.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
c, err := l.sock.prepareIO()
|
||||||
|
if err != nil {
|
||||||
|
return nil, l.opErr("accept", err)
|
||||||
|
}
|
||||||
|
defer l.sock.wg.Done()
|
||||||
|
|
||||||
|
// AcceptEx, per documentation, requires an extra 16 bytes per address.
|
||||||
|
//
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-acceptex
|
||||||
|
const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{}))
|
||||||
|
var addrbuf [addrlen * 2]byte
|
||||||
|
|
||||||
|
var bytes uint32
|
||||||
|
err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /*rxdatalen*/, addrlen, addrlen, &bytes, &c.o)
|
||||||
|
if _, err = l.sock.asyncIO(c, nil, bytes, err); err != nil {
|
||||||
|
return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := &HvsockConn{
|
||||||
|
sock: sock,
|
||||||
|
}
|
||||||
|
// The local address returned in the AcceptEx buffer is the same as the Listener socket's
|
||||||
|
// address. However, the service GUID reported by GetSockName is different from the Listeners
|
||||||
|
// socket, and is sometimes the same as the local address of the socket that dialed the
|
||||||
|
// address, with the service GUID.Data1 incremented, but othertimes is different.
|
||||||
|
// todo: does the local address matter? is the listener's address or the actual address appropriate?
|
||||||
|
conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0])))
|
||||||
|
conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen])))
|
||||||
|
|
||||||
|
// initialize the accepted socket and update its properties with those of the listening socket
|
||||||
|
if err = windows.Setsockopt(windows.Handle(sock.handle),
|
||||||
|
windows.SOL_SOCKET, windows.SO_UPDATE_ACCEPT_CONTEXT,
|
||||||
|
(*byte)(unsafe.Pointer(&l.sock.handle)), int32(unsafe.Sizeof(l.sock.handle))); err != nil {
|
||||||
|
return nil, conn.opErr("accept", os.NewSyscallError("setsockopt", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
sock = nil
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the listener, causing any pending Accept calls to fail.
|
||||||
|
func (l *HvsockListener) Close() error {
|
||||||
|
return l.sock.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HvsockDialer configures and dials a Hyper-V Socket (ie, [HvsockConn]).
|
||||||
|
type HvsockDialer struct {
|
||||||
|
// Deadline is the time the Dial operation must connect before erroring.
|
||||||
|
Deadline time.Time
|
||||||
|
|
||||||
|
// Retries is the number of additional connects to try if the connection times out, is refused,
|
||||||
|
// or the host is unreachable
|
||||||
|
Retries uint
|
||||||
|
|
||||||
|
// RetryWait is the time to wait after a connection error to retry
|
||||||
|
RetryWait time.Duration
|
||||||
|
|
||||||
|
rt *time.Timer // redial wait timer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial the Hyper-V socket at addr.
|
||||||
|
//
|
||||||
|
// See [HvsockDialer.Dial] for more information.
|
||||||
|
func Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) {
|
||||||
|
return (&HvsockDialer{}).Dial(ctx, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial attempts to connect to the Hyper-V socket at addr, and returns a connection if successful.
|
||||||
|
// Will attempt (HvsockDialer).Retries if dialing fails, waiting (HvsockDialer).RetryWait between
|
||||||
|
// retries.
|
||||||
|
//
|
||||||
|
// Dialing can be cancelled either by providing (HvsockDialer).Deadline, or cancelling ctx.
|
||||||
|
func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) {
|
||||||
|
op := "dial"
|
||||||
|
// create the conn early to use opErr()
|
||||||
|
conn = &HvsockConn{
|
||||||
|
remote: *addr,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !d.Deadline.IsZero() {
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
ctx, cancel = context.WithDeadline(ctx, d.Deadline)
|
||||||
|
defer cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
// preemptive timeout/cancellation check
|
||||||
|
if err = ctx.Err(); err != nil {
|
||||||
|
return nil, conn.opErr(op, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sock, err := newHVSocket()
|
||||||
|
if err != nil {
|
||||||
|
return nil, conn.opErr(op, err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if sock != nil {
|
||||||
|
sock.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
sa := addr.raw()
|
||||||
|
err = socket.Bind(windows.Handle(sock.handle), &sa)
|
||||||
|
if err != nil {
|
||||||
|
return nil, conn.opErr(op, os.NewSyscallError("bind", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := sock.prepareIO()
|
||||||
|
if err != nil {
|
||||||
|
return nil, conn.opErr(op, err)
|
||||||
|
}
|
||||||
|
defer sock.wg.Done()
|
||||||
|
var bytes uint32
|
||||||
|
for i := uint(0); i <= d.Retries; i++ {
|
||||||
|
err = socket.ConnectEx(
|
||||||
|
windows.Handle(sock.handle),
|
||||||
|
&sa,
|
||||||
|
nil, // sendBuf
|
||||||
|
0, // sendDataLen
|
||||||
|
&bytes,
|
||||||
|
(*windows.Overlapped)(unsafe.Pointer(&c.o)))
|
||||||
|
_, err = sock.asyncIO(c, nil, bytes, err)
|
||||||
|
if i < d.Retries && canRedial(err) {
|
||||||
|
if err = d.redialWait(ctx); err == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, conn.opErr(op, os.NewSyscallError("connectex", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the connection properties, so shutdown can be used
|
||||||
|
if err = windows.Setsockopt(
|
||||||
|
windows.Handle(sock.handle),
|
||||||
|
windows.SOL_SOCKET,
|
||||||
|
windows.SO_UPDATE_CONNECT_CONTEXT,
|
||||||
|
nil, // optvalue
|
||||||
|
0, // optlen
|
||||||
|
); err != nil {
|
||||||
|
return nil, conn.opErr(op, os.NewSyscallError("setsockopt", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the local name
|
||||||
|
var sal rawHvsockAddr
|
||||||
|
err = socket.GetSockName(windows.Handle(sock.handle), &sal)
|
||||||
|
if err != nil {
|
||||||
|
return nil, conn.opErr(op, os.NewSyscallError("getsockname", err))
|
||||||
|
}
|
||||||
|
conn.local.fromRaw(&sal)
|
||||||
|
|
||||||
|
// one last check for timeout, since asyncIO doesn't check the context
|
||||||
|
if err = ctx.Err(); err != nil {
|
||||||
|
return nil, conn.opErr(op, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.sock = sock
|
||||||
|
sock = nil
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// redialWait waits before attempting to redial, resetting the timer as appropriate.
|
||||||
|
func (d *HvsockDialer) redialWait(ctx context.Context) (err error) {
|
||||||
|
if d.RetryWait == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.rt == nil {
|
||||||
|
d.rt = time.NewTimer(d.RetryWait)
|
||||||
|
} else {
|
||||||
|
// should already be stopped and drained
|
||||||
|
d.rt.Reset(d.RetryWait)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case <-d.rt.C:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop and drain the timer
|
||||||
|
if !d.rt.Stop() {
|
||||||
|
<-d.rt.C
|
||||||
|
}
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// assumes error is a plain, unwrapped syscall.Errno provided by direct syscall.
|
||||||
|
func canRedial(err error) bool {
|
||||||
|
//nolint:errorlint // guaranteed to be an Errno
|
||||||
|
switch err {
|
||||||
|
case windows.WSAECONNREFUSED, windows.WSAENETUNREACH, windows.WSAETIMEDOUT,
|
||||||
|
windows.ERROR_CONNECTION_REFUSED, windows.ERROR_CONNECTION_UNAVAIL:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *HvsockConn) opErr(op string, err error) error {
|
||||||
|
// translate from "file closed" to "socket closed"
|
||||||
|
if errors.Is(err, ErrFileClosed) {
|
||||||
|
err = socket.ErrSocketClosed
|
||||||
|
}
|
||||||
|
return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *HvsockConn) Read(b []byte) (int, error) {
|
||||||
|
c, err := conn.sock.prepareIO()
|
||||||
|
if err != nil {
|
||||||
|
return 0, conn.opErr("read", err)
|
||||||
|
}
|
||||||
|
defer conn.sock.wg.Done()
|
||||||
|
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
||||||
|
var flags, bytes uint32
|
||||||
|
err = syscall.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
|
||||||
|
n, err := conn.sock.asyncIO(c, &conn.sock.readDeadline, bytes, err)
|
||||||
|
if err != nil {
|
||||||
|
var eno windows.Errno
|
||||||
|
if errors.As(err, &eno) {
|
||||||
|
err = os.NewSyscallError("wsarecv", eno)
|
||||||
|
}
|
||||||
|
return 0, conn.opErr("read", err)
|
||||||
|
} else if n == 0 {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *HvsockConn) Write(b []byte) (int, error) {
|
||||||
|
t := 0
|
||||||
|
for len(b) != 0 {
|
||||||
|
n, err := conn.write(b)
|
||||||
|
if err != nil {
|
||||||
|
return t + n, err
|
||||||
|
}
|
||||||
|
t += n
|
||||||
|
b = b[n:]
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *HvsockConn) write(b []byte) (int, error) {
|
||||||
|
c, err := conn.sock.prepareIO()
|
||||||
|
if err != nil {
|
||||||
|
return 0, conn.opErr("write", err)
|
||||||
|
}
|
||||||
|
defer conn.sock.wg.Done()
|
||||||
|
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
||||||
|
var bytes uint32
|
||||||
|
err = syscall.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
|
||||||
|
n, err := conn.sock.asyncIO(c, &conn.sock.writeDeadline, bytes, err)
|
||||||
|
if err != nil {
|
||||||
|
var eno windows.Errno
|
||||||
|
if errors.As(err, &eno) {
|
||||||
|
err = os.NewSyscallError("wsasend", eno)
|
||||||
|
}
|
||||||
|
return 0, conn.opErr("write", err)
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the socket connection, failing any pending read or write calls.
|
||||||
|
func (conn *HvsockConn) Close() error {
|
||||||
|
return conn.sock.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *HvsockConn) IsClosed() bool {
|
||||||
|
return conn.sock.IsClosed()
|
||||||
|
}
|
||||||
|
|
||||||
|
// shutdown disables sending or receiving on a socket.
|
||||||
|
func (conn *HvsockConn) shutdown(how int) error {
|
||||||
|
if conn.IsClosed() {
|
||||||
|
return socket.ErrSocketClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
err := syscall.Shutdown(conn.sock.handle, how)
|
||||||
|
if err != nil {
|
||||||
|
// If the connection was closed, shutdowns fail with "not connected"
|
||||||
|
if errors.Is(err, windows.WSAENOTCONN) ||
|
||||||
|
errors.Is(err, windows.WSAESHUTDOWN) {
|
||||||
|
err = socket.ErrSocketClosed
|
||||||
|
}
|
||||||
|
return os.NewSyscallError("shutdown", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseRead shuts down the read end of the socket, preventing future read operations.
|
||||||
|
func (conn *HvsockConn) CloseRead() error {
|
||||||
|
err := conn.shutdown(syscall.SHUT_RD)
|
||||||
|
if err != nil {
|
||||||
|
return conn.opErr("closeread", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseWrite shuts down the write end of the socket, preventing future write operations and
|
||||||
|
// notifying the other endpoint that no more data will be written.
|
||||||
|
func (conn *HvsockConn) CloseWrite() error {
|
||||||
|
err := conn.shutdown(syscall.SHUT_WR)
|
||||||
|
if err != nil {
|
||||||
|
return conn.opErr("closewrite", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalAddr returns the local address of the connection.
|
||||||
|
func (conn *HvsockConn) LocalAddr() net.Addr {
|
||||||
|
return &conn.local
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoteAddr returns the remote address of the connection.
|
||||||
|
func (conn *HvsockConn) RemoteAddr() net.Addr {
|
||||||
|
return &conn.remote
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDeadline implements the net.Conn SetDeadline method.
|
||||||
|
func (conn *HvsockConn) SetDeadline(t time.Time) error {
|
||||||
|
// todo: implement `SetDeadline` for `win32File`
|
||||||
|
if err := conn.SetReadDeadline(t); err != nil {
|
||||||
|
return fmt.Errorf("set read deadline: %w", err)
|
||||||
|
}
|
||||||
|
if err := conn.SetWriteDeadline(t); err != nil {
|
||||||
|
return fmt.Errorf("set write deadline: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReadDeadline implements the net.Conn SetReadDeadline method.
|
||||||
|
func (conn *HvsockConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return conn.sock.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriteDeadline implements the net.Conn SetWriteDeadline method.
|
||||||
|
func (conn *HvsockConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return conn.sock.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
20
vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go
generated
vendored
Normal file
20
vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package socket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RawSockaddr allows structs to be used with [Bind] and [ConnectEx]. The
|
||||||
|
// struct must meet the Win32 sockaddr requirements specified here:
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/winsock/sockaddr-2
|
||||||
|
//
|
||||||
|
// Specifically, the struct size must be least larger than an int16 (unsigned short)
|
||||||
|
// for the address family.
|
||||||
|
type RawSockaddr interface {
|
||||||
|
// Sockaddr returns a pointer to the RawSockaddr and its struct size, allowing
|
||||||
|
// for the RawSockaddr's data to be overwritten by syscalls (if necessary).
|
||||||
|
//
|
||||||
|
// It is the callers responsibility to validate that the values are valid; invalid
|
||||||
|
// pointers or size can cause a panic.
|
||||||
|
Sockaddr() (unsafe.Pointer, int32, error)
|
||||||
|
}
|
||||||
179
vendor/github.com/Microsoft/go-winio/internal/socket/socket.go
generated
vendored
Normal file
179
vendor/github.com/Microsoft/go-winio/internal/socket/socket.go
generated
vendored
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package socket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio/pkg/guid"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go socket.go
|
||||||
|
|
||||||
|
//sys getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getsockname
|
||||||
|
//sys getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getpeername
|
||||||
|
//sys bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind
|
||||||
|
|
||||||
|
const socketError = uintptr(^uint32(0))
|
||||||
|
|
||||||
|
var (
|
||||||
|
// todo(helsaawy): create custom error types to store the desired vs actual size and addr family?
|
||||||
|
|
||||||
|
ErrBufferSize = errors.New("buffer size")
|
||||||
|
ErrAddrFamily = errors.New("address family")
|
||||||
|
ErrInvalidPointer = errors.New("invalid pointer")
|
||||||
|
ErrSocketClosed = fmt.Errorf("socket closed: %w", net.ErrClosed)
|
||||||
|
)
|
||||||
|
|
||||||
|
// todo(helsaawy): replace these with generics, ie: GetSockName[S RawSockaddr](s windows.Handle) (S, error)
|
||||||
|
|
||||||
|
// GetSockName writes the local address of socket s to the [RawSockaddr] rsa.
|
||||||
|
// If rsa is not large enough, the [windows.WSAEFAULT] is returned.
|
||||||
|
func GetSockName(s windows.Handle, rsa RawSockaddr) error {
|
||||||
|
ptr, l, err := rsa.Sockaddr()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not retrieve socket pointer and size: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// although getsockname returns WSAEFAULT if the buffer is too small, it does not set
|
||||||
|
// &l to the correct size, so--apart from doubling the buffer repeatedly--there is no remedy
|
||||||
|
return getsockname(s, ptr, &l)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPeerName returns the remote address the socket is connected to.
|
||||||
|
//
|
||||||
|
// See [GetSockName] for more information.
|
||||||
|
func GetPeerName(s windows.Handle, rsa RawSockaddr) error {
|
||||||
|
ptr, l, err := rsa.Sockaddr()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not retrieve socket pointer and size: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return getpeername(s, ptr, &l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Bind(s windows.Handle, rsa RawSockaddr) (err error) {
|
||||||
|
ptr, l, err := rsa.Sockaddr()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not retrieve socket pointer and size: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bind(s, ptr, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
// "golang.org/x/sys/windows".ConnectEx and .Bind only accept internal implementations of the
|
||||||
|
// their sockaddr interface, so they cannot be used with HvsockAddr
|
||||||
|
// Replicate functionality here from
|
||||||
|
// https://cs.opensource.google/go/x/sys/+/master:windows/syscall_windows.go
|
||||||
|
|
||||||
|
// The function pointers to `AcceptEx`, `ConnectEx` and `GetAcceptExSockaddrs` must be loaded at
|
||||||
|
// runtime via a WSAIoctl call:
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/Mswsock/nc-mswsock-lpfn_connectex#remarks
|
||||||
|
|
||||||
|
type runtimeFunc struct {
|
||||||
|
id guid.GUID
|
||||||
|
once sync.Once
|
||||||
|
addr uintptr
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *runtimeFunc) Load() error {
|
||||||
|
f.once.Do(func() {
|
||||||
|
var s windows.Handle
|
||||||
|
s, f.err = windows.Socket(windows.AF_INET, windows.SOCK_STREAM, windows.IPPROTO_TCP)
|
||||||
|
if f.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer windows.CloseHandle(s) //nolint:errcheck
|
||||||
|
|
||||||
|
var n uint32
|
||||||
|
f.err = windows.WSAIoctl(s,
|
||||||
|
windows.SIO_GET_EXTENSION_FUNCTION_POINTER,
|
||||||
|
(*byte)(unsafe.Pointer(&f.id)),
|
||||||
|
uint32(unsafe.Sizeof(f.id)),
|
||||||
|
(*byte)(unsafe.Pointer(&f.addr)),
|
||||||
|
uint32(unsafe.Sizeof(f.addr)),
|
||||||
|
&n,
|
||||||
|
nil, //overlapped
|
||||||
|
0, //completionRoutine
|
||||||
|
)
|
||||||
|
})
|
||||||
|
return f.err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// todo: add `AcceptEx` and `GetAcceptExSockaddrs`
|
||||||
|
WSAID_CONNECTEX = guid.GUID{ //revive:disable-line:var-naming ALL_CAPS
|
||||||
|
Data1: 0x25a207b9,
|
||||||
|
Data2: 0xddf3,
|
||||||
|
Data3: 0x4660,
|
||||||
|
Data4: [8]byte{0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e},
|
||||||
|
}
|
||||||
|
|
||||||
|
connectExFunc = runtimeFunc{id: WSAID_CONNECTEX}
|
||||||
|
)
|
||||||
|
|
||||||
|
func ConnectEx(
|
||||||
|
fd windows.Handle,
|
||||||
|
rsa RawSockaddr,
|
||||||
|
sendBuf *byte,
|
||||||
|
sendDataLen uint32,
|
||||||
|
bytesSent *uint32,
|
||||||
|
overlapped *windows.Overlapped,
|
||||||
|
) error {
|
||||||
|
if err := connectExFunc.Load(); err != nil {
|
||||||
|
return fmt.Errorf("failed to load ConnectEx function pointer: %w", err)
|
||||||
|
}
|
||||||
|
ptr, n, err := rsa.Sockaddr()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return connectEx(fd, ptr, n, sendBuf, sendDataLen, bytesSent, overlapped)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOOL LpfnConnectex(
|
||||||
|
// [in] SOCKET s,
|
||||||
|
// [in] const sockaddr *name,
|
||||||
|
// [in] int namelen,
|
||||||
|
// [in, optional] PVOID lpSendBuffer,
|
||||||
|
// [in] DWORD dwSendDataLength,
|
||||||
|
// [out] LPDWORD lpdwBytesSent,
|
||||||
|
// [in] LPOVERLAPPED lpOverlapped
|
||||||
|
// )
|
||||||
|
|
||||||
|
func connectEx(
|
||||||
|
s windows.Handle,
|
||||||
|
name unsafe.Pointer,
|
||||||
|
namelen int32,
|
||||||
|
sendBuf *byte,
|
||||||
|
sendDataLen uint32,
|
||||||
|
bytesSent *uint32,
|
||||||
|
overlapped *windows.Overlapped,
|
||||||
|
) (err error) {
|
||||||
|
// todo: after upgrading to 1.18, switch from syscall.Syscall9 to syscall.SyscallN
|
||||||
|
r1, _, e1 := syscall.Syscall9(connectExFunc.addr,
|
||||||
|
7,
|
||||||
|
uintptr(s),
|
||||||
|
uintptr(name),
|
||||||
|
uintptr(namelen),
|
||||||
|
uintptr(unsafe.Pointer(sendBuf)),
|
||||||
|
uintptr(sendDataLen),
|
||||||
|
uintptr(unsafe.Pointer(bytesSent)),
|
||||||
|
uintptr(unsafe.Pointer(overlapped)),
|
||||||
|
0,
|
||||||
|
0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
72
vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go
generated
vendored
Normal file
72
vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package socket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
// Do the interface allocations only once for common
|
||||||
|
// Errno values.
|
||||||
|
const (
|
||||||
|
errnoERROR_IO_PENDING = 997
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||||
|
errERROR_EINVAL error = syscall.EINVAL
|
||||||
|
)
|
||||||
|
|
||||||
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
// allocations at runtime.
|
||||||
|
func errnoErr(e syscall.Errno) error {
|
||||||
|
switch e {
|
||||||
|
case 0:
|
||||||
|
return errERROR_EINVAL
|
||||||
|
case errnoERROR_IO_PENDING:
|
||||||
|
return errERROR_IO_PENDING
|
||||||
|
}
|
||||||
|
// TODO: add more here, after collecting data on the common
|
||||||
|
// error values see on Windows. (perhaps when running
|
||||||
|
// all.bat?)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
|
||||||
|
|
||||||
|
procbind = modws2_32.NewProc("bind")
|
||||||
|
procgetpeername = modws2_32.NewProc("getpeername")
|
||||||
|
procgetsockname = modws2_32.NewProc("getsockname")
|
||||||
|
)
|
||||||
|
|
||||||
|
func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
|
||||||
|
if r1 == socketError {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procgetpeername.Addr(), 3, uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
|
||||||
|
if r1 == socketError {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procgetsockname.Addr(), 3, uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
|
||||||
|
if r1 == socketError {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user