Compare commits

..

6 Commits

Author SHA1 Message Date
8a8e746159 fix(*): Use vendored dependencies in build 🔧
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2024-04-08 14:04:15 +12:00
ff1798b10b fix(*): Vendored project dependencies
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-04-08 14:00:37 +12:00
fa40e6ee71 fix(ci): Fix repository URL for release 🔧
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2024-04-03 11:26:36 +13:00
8a05b72ec8 fix(ci): Correct Repository name 🔨
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2024-04-03 11:18:25 +13:00
732b1ee1a0 fix(ci): Credential fixing 🔨
Some checks failed
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline failed
2024-04-03 11:11:02 +13:00
5636fecc14 fix(ci): Forgot the logon credentials 🔧
Some checks failed
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline failed
2024-04-03 10:55:11 +13:00
1604 changed files with 485267 additions and 6 deletions

View File

@@ -21,7 +21,7 @@ assets:
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:

View File

@@ -28,30 +28,35 @@ steps:
GOOS=linux GOOS=linux
GOARCH=amd64 GOARCH=amd64
CGO_ENABLED=0 CGO_ENABLED=0
GOFLAGS=-mod=vendor
go build -o build/go-semantic-release.linux_amd64 -ldflags "-w -s --X main.version=`go-semantic-release next`" 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
GOFLAGS=-mod=vendor
go build -o build/go-semantic-release.windows_amd64.exe -ldflags "-w -s -X main.version=`go-semantic-release next`" 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
GOFLAGS=-mod=vendor
go build -o build/go-semantic-release.darwin_amd64 -ldflags "-w -s -X main.version=`go-semantic-release next`" 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
@@ -71,10 +76,8 @@ steps:
pull: true pull: true
settings: settings:
<<: *docker_creds <<: *docker_creds
token:
from_secret: depot_token
registry: hub.cybercinch.nz registry: hub.cybercinch.nz
repo: cybercinch/${CI_REPO_NAME} repo: hub.cybercinch.nz/cybercinch/${CI_REPO_NAME}
dockerfile: Dockerfile dockerfile: Dockerfile
platforms: *platforms platforms: *platforms
build_args: build_args:

View File

@@ -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
View File

@@ -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

20
vendor/code.gitea.io/sdk/gitea/LICENSE generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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, &times)
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, &times)
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, &times)
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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
[![Stability:
Active](https://masterminds.github.io/stability/active.svg)](https://masterminds.github.io/stability/active.html)
[![Build Status](https://travis-ci.org/Masterminds/semver.svg)](https://travis-ci.org/Masterminds/semver) [![Build status](https://ci.appveyor.com/api/projects/status/jfk66lib7hb985k8/branch/master?svg=true&passingText=windows%20build%20passing&failingText=windows%20build%20failing)](https://ci.appveyor.com/project/mattfarina/semver/branch/master) [![GoDoc](https://godoc.org/github.com/Masterminds/semver?status.svg)](https://godoc.org/github.com/Masterminds/semver) [![Go Report Card](https://goreportcard.com/badge/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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1 @@
* text=auto eol=lf

10
vendor/github.com/Microsoft/go-winio/.gitignore generated vendored Normal file
View 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
View 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
View File

@@ -0,0 +1 @@
* @microsoft/containerplat

22
vendor/github.com/Microsoft/go-winio/LICENSE generated vendored Normal file
View 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
View File

@@ -0,0 +1,89 @@
# go-winio [![Build Status](https://github.com/microsoft/go-winio/actions/workflows/ci.yml/badge.svg)](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
View 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
View 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
View 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
View 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
View 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
View 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
View 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)
}

View 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)
}

View 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
}

View 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
}

521
vendor/github.com/Microsoft/go-winio/pipe.go generated vendored Normal file
View File

@@ -0,0 +1,521 @@
//go:build windows
// +build windows
package winio
import (
"context"
"errors"
"fmt"
"io"
"net"
"os"
"runtime"
"syscall"
"time"
"unsafe"
"golang.org/x/sys/windows"
)
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
//sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc
//sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) = ntdll.NtCreateNamedPipeFile
//sys rtlNtStatusToDosError(status ntStatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) = ntdll.RtlDosPathNameToNtPathName_U
//sys rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) = ntdll.RtlDefaultNpAcl
type ioStatusBlock struct {
Status, Information uintptr
}
type objectAttributes struct {
Length uintptr
RootDirectory uintptr
ObjectName *unicodeString
Attributes uintptr
SecurityDescriptor *securityDescriptor
SecurityQoS uintptr
}
type unicodeString struct {
Length uint16
MaximumLength uint16
Buffer uintptr
}
type securityDescriptor struct {
Revision byte
Sbz1 byte
Control uint16
Owner uintptr
Group uintptr
Sacl uintptr //revive:disable-line:var-naming SACL, not Sacl
Dacl uintptr //revive:disable-line:var-naming DACL, not Dacl
}
type ntStatus int32
func (status ntStatus) Err() error {
if status >= 0 {
return nil
}
return rtlNtStatusToDosError(status)
}
var (
// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
ErrPipeListenerClosed = net.ErrClosed
errPipeWriteClosed = errors.New("pipe has been closed for write")
)
type win32Pipe struct {
*win32File
path string
}
type win32MessageBytePipe struct {
win32Pipe
writeClosed bool
readEOF bool
}
type pipeAddress string
func (f *win32Pipe) LocalAddr() net.Addr {
return pipeAddress(f.path)
}
func (f *win32Pipe) RemoteAddr() net.Addr {
return pipeAddress(f.path)
}
func (f *win32Pipe) SetDeadline(t time.Time) error {
if err := f.SetReadDeadline(t); err != nil {
return err
}
return f.SetWriteDeadline(t)
}
// CloseWrite closes the write side of a message pipe in byte mode.
func (f *win32MessageBytePipe) CloseWrite() error {
if f.writeClosed {
return errPipeWriteClosed
}
err := f.win32File.Flush()
if err != nil {
return err
}
_, err = f.win32File.Write(nil)
if err != nil {
return err
}
f.writeClosed = true
return nil
}
// Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since
// they are used to implement CloseWrite().
func (f *win32MessageBytePipe) Write(b []byte) (int, error) {
if f.writeClosed {
return 0, errPipeWriteClosed
}
if len(b) == 0 {
return 0, nil
}
return f.win32File.Write(b)
}
// Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message
// mode pipe will return io.EOF, as will all subsequent reads.
func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
if f.readEOF {
return 0, io.EOF
}
n, err := f.win32File.Read(b)
if err == io.EOF { //nolint:errorlint
// If this was the result of a zero-byte read, then
// it is possible that the read was due to a zero-size
// message. Since we are simulating CloseWrite with a
// zero-byte message, ensure that all future Read() calls
// also return EOF.
f.readEOF = true
} else if err == syscall.ERROR_MORE_DATA { //nolint:errorlint // err is Errno
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
// and the message still has more bytes. Treat this as a success, since
// this package presents all named pipes as byte streams.
err = nil
}
return n, err
}
func (pipeAddress) Network() string {
return "pipe"
}
func (s pipeAddress) String() string {
return string(s)
}
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
func tryDialPipe(ctx context.Context, path *string, access uint32) (syscall.Handle, error) {
for {
select {
case <-ctx.Done():
return syscall.Handle(0), ctx.Err()
default:
h, err := createFile(*path,
access,
0,
nil,
syscall.OPEN_EXISTING,
windows.FILE_FLAG_OVERLAPPED|windows.SECURITY_SQOS_PRESENT|windows.SECURITY_ANONYMOUS,
0)
if err == nil {
return h, nil
}
if err != windows.ERROR_PIPE_BUSY { //nolint:errorlint // err is Errno
return h, &os.PathError{Err: err, Op: "open", Path: *path}
}
// Wait 10 msec and try again. This is a rather simplistic
// view, as we always try each 10 milliseconds.
time.Sleep(10 * time.Millisecond)
}
}
}
// DialPipe connects to a named pipe by path, timing out if the connection
// takes longer than the specified duration. If timeout is nil, then we use
// a default timeout of 2 seconds. (We do not use WaitNamedPipe.)
func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
var absTimeout time.Time
if timeout != nil {
absTimeout = time.Now().Add(*timeout)
} else {
absTimeout = time.Now().Add(2 * time.Second)
}
ctx, cancel := context.WithDeadline(context.Background(), absTimeout)
defer cancel()
conn, err := DialPipeContext(ctx, path)
if errors.Is(err, context.DeadlineExceeded) {
return nil, ErrTimeout
}
return conn, err
}
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
// cancellation or timeout.
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
return DialPipeAccess(ctx, path, syscall.GENERIC_READ|syscall.GENERIC_WRITE)
}
// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx`
// cancellation or timeout.
func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) {
var err error
var h syscall.Handle
h, err = tryDialPipe(ctx, &path, access)
if err != nil {
return nil, err
}
var flags uint32
err = getNamedPipeInfo(h, &flags, nil, nil, nil)
if err != nil {
return nil, err
}
f, err := makeWin32File(h)
if err != nil {
syscall.Close(h)
return nil, err
}
// If the pipe is in message mode, return a message byte pipe, which
// supports CloseWrite().
if flags&windows.PIPE_TYPE_MESSAGE != 0 {
return &win32MessageBytePipe{
win32Pipe: win32Pipe{win32File: f, path: path},
}, nil
}
return &win32Pipe{win32File: f, path: path}, nil
}
type acceptResponse struct {
f *win32File
err error
}
type win32PipeListener struct {
firstHandle syscall.Handle
path string
config PipeConfig
acceptCh chan (chan acceptResponse)
closeCh chan int
doneCh chan int
}
func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (syscall.Handle, error) {
path16, err := syscall.UTF16FromString(path)
if err != nil {
return 0, &os.PathError{Op: "open", Path: path, Err: err}
}
var oa objectAttributes
oa.Length = unsafe.Sizeof(oa)
var ntPath unicodeString
if err := rtlDosPathNameToNtPathName(&path16[0],
&ntPath,
0,
0,
).Err(); err != nil {
return 0, &os.PathError{Op: "open", Path: path, Err: err}
}
defer localFree(ntPath.Buffer)
oa.ObjectName = &ntPath
// The security descriptor is only needed for the first pipe.
if first {
if sd != nil {
l := uint32(len(sd))
sdb := localAlloc(0, l)
defer localFree(sdb)
copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd)
oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb))
} else {
// Construct the default named pipe security descriptor.
var dacl uintptr
if err := rtlDefaultNpAcl(&dacl).Err(); err != nil {
return 0, fmt.Errorf("getting default named pipe ACL: %w", err)
}
defer localFree(dacl)
sdb := &securityDescriptor{
Revision: 1,
Control: windows.SE_DACL_PRESENT,
Dacl: dacl,
}
oa.SecurityDescriptor = sdb
}
}
typ := uint32(windows.FILE_PIPE_REJECT_REMOTE_CLIENTS)
if c.MessageMode {
typ |= windows.FILE_PIPE_MESSAGE_TYPE
}
disposition := uint32(windows.FILE_OPEN)
access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE)
if first {
disposition = windows.FILE_CREATE
// By not asking for read or write access, the named pipe file system
// will put this pipe into an initially disconnected state, blocking
// client connections until the next call with first == false.
access = syscall.SYNCHRONIZE
}
timeout := int64(-50 * 10000) // 50ms
var (
h syscall.Handle
iosb ioStatusBlock
)
err = ntCreateNamedPipeFile(&h,
access,
&oa,
&iosb,
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE,
disposition,
0,
typ,
0,
0,
0xffffffff,
uint32(c.InputBufferSize),
uint32(c.OutputBufferSize),
&timeout).Err()
if err != nil {
return 0, &os.PathError{Op: "open", Path: path, Err: err}
}
runtime.KeepAlive(ntPath)
return h, nil
}
func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
h, err := makeServerPipeHandle(l.path, nil, &l.config, false)
if err != nil {
return nil, err
}
f, err := makeWin32File(h)
if err != nil {
syscall.Close(h)
return nil, err
}
return f, nil
}
func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) {
p, err := l.makeServerPipe()
if err != nil {
return nil, err
}
// Wait for the client to connect.
ch := make(chan error)
go func(p *win32File) {
ch <- connectPipe(p)
}(p)
select {
case err = <-ch:
if err != nil {
p.Close()
p = nil
}
case <-l.closeCh:
// Abort the connect request by closing the handle.
p.Close()
p = nil
err = <-ch
if err == nil || err == ErrFileClosed { //nolint:errorlint // err is Errno
err = ErrPipeListenerClosed
}
}
return p, err
}
func (l *win32PipeListener) listenerRoutine() {
closed := false
for !closed {
select {
case <-l.closeCh:
closed = true
case responseCh := <-l.acceptCh:
var (
p *win32File
err error
)
for {
p, err = l.makeConnectedServerPipe()
// If the connection was immediately closed by the client, try
// again.
if err != windows.ERROR_NO_DATA { //nolint:errorlint // err is Errno
break
}
}
responseCh <- acceptResponse{p, err}
closed = err == ErrPipeListenerClosed //nolint:errorlint // err is Errno
}
}
syscall.Close(l.firstHandle)
l.firstHandle = 0
// Notify Close() and Accept() callers that the handle has been closed.
close(l.doneCh)
}
// PipeConfig contain configuration for the pipe listener.
type PipeConfig struct {
// SecurityDescriptor contains a Windows security descriptor in SDDL format.
SecurityDescriptor string
// MessageMode determines whether the pipe is in byte or message mode. In either
// case the pipe is read in byte mode by default. The only practical difference in
// this implementation is that CloseWrite() is only supported for message mode pipes;
// CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only
// transferred to the reader (and returned as io.EOF in this implementation)
// when the pipe is in message mode.
MessageMode bool
// InputBufferSize specifies the size of the input buffer, in bytes.
InputBufferSize int32
// OutputBufferSize specifies the size of the output buffer, in bytes.
OutputBufferSize int32
}
// ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe.
// The pipe must not already exist.
func ListenPipe(path string, c *PipeConfig) (net.Listener, error) {
var (
sd []byte
err error
)
if c == nil {
c = &PipeConfig{}
}
if c.SecurityDescriptor != "" {
sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor)
if err != nil {
return nil, err
}
}
h, err := makeServerPipeHandle(path, sd, c, true)
if err != nil {
return nil, err
}
l := &win32PipeListener{
firstHandle: h,
path: path,
config: *c,
acceptCh: make(chan (chan acceptResponse)),
closeCh: make(chan int),
doneCh: make(chan int),
}
go l.listenerRoutine()
return l, nil
}
func connectPipe(p *win32File) error {
c, err := p.prepareIO()
if err != nil {
return err
}
defer p.wg.Done()
err = connectNamedPipe(p.handle, &c.o)
_, err = p.asyncIO(c, nil, 0, err)
if err != nil && err != windows.ERROR_PIPE_CONNECTED { //nolint:errorlint // err is Errno
return err
}
return nil
}
func (l *win32PipeListener) Accept() (net.Conn, error) {
ch := make(chan acceptResponse)
select {
case l.acceptCh <- ch:
response := <-ch
err := response.err
if err != nil {
return nil, err
}
if l.config.MessageMode {
return &win32MessageBytePipe{
win32Pipe: win32Pipe{win32File: response.f, path: l.path},
}, nil
}
return &win32Pipe{win32File: response.f, path: l.path}, nil
case <-l.doneCh:
return nil, ErrPipeListenerClosed
}
}
func (l *win32PipeListener) Close() error {
select {
case l.closeCh <- 1:
<-l.doneCh
case <-l.doneCh:
}
return nil
}
func (l *win32PipeListener) Addr() net.Addr {
return pipeAddress(l.path)
}

232
vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go generated vendored Normal file
View File

@@ -0,0 +1,232 @@
// Package guid provides a GUID type. The backing structure for a GUID is
// identical to that used by the golang.org/x/sys/windows GUID type.
// There are two main binary encodings used for a GUID, the big-endian encoding,
// and the Windows (mixed-endian) encoding. See here for details:
// https://en.wikipedia.org/wiki/Universally_unique_identifier#Encoding
package guid
import (
"crypto/rand"
"crypto/sha1" //nolint:gosec // not used for secure application
"encoding"
"encoding/binary"
"fmt"
"strconv"
)
//go:generate go run golang.org/x/tools/cmd/stringer -type=Variant -trimprefix=Variant -linecomment
// Variant specifies which GUID variant (or "type") of the GUID. It determines
// how the entirety of the rest of the GUID is interpreted.
type Variant uint8
// The variants specified by RFC 4122 section 4.1.1.
const (
// VariantUnknown specifies a GUID variant which does not conform to one of
// the variant encodings specified in RFC 4122.
VariantUnknown Variant = iota
VariantNCS
VariantRFC4122 // RFC 4122
VariantMicrosoft
VariantFuture
)
// Version specifies how the bits in the GUID were generated. For instance, a
// version 4 GUID is randomly generated, and a version 5 is generated from the
// hash of an input string.
type Version uint8
func (v Version) String() string {
return strconv.FormatUint(uint64(v), 10)
}
var _ = (encoding.TextMarshaler)(GUID{})
var _ = (encoding.TextUnmarshaler)(&GUID{})
// NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122.
func NewV4() (GUID, error) {
var b [16]byte
if _, err := rand.Read(b[:]); err != nil {
return GUID{}, err
}
g := FromArray(b)
g.setVersion(4) // Version 4 means randomly generated.
g.setVariant(VariantRFC4122)
return g, nil
}
// NewV5 returns a new version 5 (generated from a string via SHA-1 hashing)
// GUID, as defined by RFC 4122. The RFC is unclear on the encoding of the name,
// and the sample code treats it as a series of bytes, so we do the same here.
//
// Some implementations, such as those found on Windows, treat the name as a
// big-endian UTF16 stream of bytes. If that is desired, the string can be
// encoded as such before being passed to this function.
func NewV5(namespace GUID, name []byte) (GUID, error) {
b := sha1.New() //nolint:gosec // not used for secure application
namespaceBytes := namespace.ToArray()
b.Write(namespaceBytes[:])
b.Write(name)
a := [16]byte{}
copy(a[:], b.Sum(nil))
g := FromArray(a)
g.setVersion(5) // Version 5 means generated from a string.
g.setVariant(VariantRFC4122)
return g, nil
}
func fromArray(b [16]byte, order binary.ByteOrder) GUID {
var g GUID
g.Data1 = order.Uint32(b[0:4])
g.Data2 = order.Uint16(b[4:6])
g.Data3 = order.Uint16(b[6:8])
copy(g.Data4[:], b[8:16])
return g
}
func (g GUID) toArray(order binary.ByteOrder) [16]byte {
b := [16]byte{}
order.PutUint32(b[0:4], g.Data1)
order.PutUint16(b[4:6], g.Data2)
order.PutUint16(b[6:8], g.Data3)
copy(b[8:16], g.Data4[:])
return b
}
// FromArray constructs a GUID from a big-endian encoding array of 16 bytes.
func FromArray(b [16]byte) GUID {
return fromArray(b, binary.BigEndian)
}
// ToArray returns an array of 16 bytes representing the GUID in big-endian
// encoding.
func (g GUID) ToArray() [16]byte {
return g.toArray(binary.BigEndian)
}
// FromWindowsArray constructs a GUID from a Windows encoding array of bytes.
func FromWindowsArray(b [16]byte) GUID {
return fromArray(b, binary.LittleEndian)
}
// ToWindowsArray returns an array of 16 bytes representing the GUID in Windows
// encoding.
func (g GUID) ToWindowsArray() [16]byte {
return g.toArray(binary.LittleEndian)
}
func (g GUID) String() string {
return fmt.Sprintf(
"%08x-%04x-%04x-%04x-%012x",
g.Data1,
g.Data2,
g.Data3,
g.Data4[:2],
g.Data4[2:])
}
// FromString parses a string containing a GUID and returns the GUID. The only
// format currently supported is the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
// format.
func FromString(s string) (GUID, error) {
if len(s) != 36 {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
var g GUID
data1, err := strconv.ParseUint(s[0:8], 16, 32)
if err != nil {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
g.Data1 = uint32(data1)
data2, err := strconv.ParseUint(s[9:13], 16, 16)
if err != nil {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
g.Data2 = uint16(data2)
data3, err := strconv.ParseUint(s[14:18], 16, 16)
if err != nil {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
g.Data3 = uint16(data3)
for i, x := range []int{19, 21, 24, 26, 28, 30, 32, 34} {
v, err := strconv.ParseUint(s[x:x+2], 16, 8)
if err != nil {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
g.Data4[i] = uint8(v)
}
return g, nil
}
func (g *GUID) setVariant(v Variant) {
d := g.Data4[0]
switch v {
case VariantNCS:
d = (d & 0x7f)
case VariantRFC4122:
d = (d & 0x3f) | 0x80
case VariantMicrosoft:
d = (d & 0x1f) | 0xc0
case VariantFuture:
d = (d & 0x0f) | 0xe0
case VariantUnknown:
fallthrough
default:
panic(fmt.Sprintf("invalid variant: %d", v))
}
g.Data4[0] = d
}
// Variant returns the GUID variant, as defined in RFC 4122.
func (g GUID) Variant() Variant {
b := g.Data4[0]
if b&0x80 == 0 {
return VariantNCS
} else if b&0xc0 == 0x80 {
return VariantRFC4122
} else if b&0xe0 == 0xc0 {
return VariantMicrosoft
} else if b&0xe0 == 0xe0 {
return VariantFuture
}
return VariantUnknown
}
func (g *GUID) setVersion(v Version) {
g.Data3 = (g.Data3 & 0x0fff) | (uint16(v) << 12)
}
// Version returns the GUID version, as defined in RFC 4122.
func (g GUID) Version() Version {
return Version((g.Data3 & 0xF000) >> 12)
}
// MarshalText returns the textual representation of the GUID.
func (g GUID) MarshalText() ([]byte, error) {
return []byte(g.String()), nil
}
// UnmarshalText takes the textual representation of a GUID, and unmarhals it
// into this GUID.
func (g *GUID) UnmarshalText(text []byte) error {
g2, err := FromString(string(text))
if err != nil {
return err
}
*g = g2
return nil
}

View File

@@ -0,0 +1,16 @@
//go:build !windows
// +build !windows
package guid
// GUID represents a GUID/UUID. It has the same structure as
// golang.org/x/sys/windows.GUID so that it can be used with functions expecting
// that type. It is defined as its own type as that is only available to builds
// targeted at `windows`. The representation matches that used by native Windows
// code.
type GUID struct {
Data1 uint32
Data2 uint16
Data3 uint16
Data4 [8]byte
}

Some files were not shown because too many files have changed in this diff Show More