first release of this action
This commit is contained in:
commit
259af57371
8 changed files with 853 additions and 0 deletions
305
github.go
Normal file
305
github.go
Normal file
|
@ -0,0 +1,305 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ApiClient interface {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
type GitHub struct {
|
||||
client ApiClient
|
||||
}
|
||||
|
||||
type ApiError struct {
|
||||
Message string `json:"message"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
func (e ApiError) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
type ApiNotFoundError struct {
|
||||
ApiError
|
||||
Errors []string `json:"errors"`
|
||||
}
|
||||
|
||||
type ApiValidationError struct {
|
||||
ApiError
|
||||
}
|
||||
|
||||
func NewGitHub(client ApiClient) *GitHub {
|
||||
return &GitHub{client: client}
|
||||
}
|
||||
|
||||
// Determine whether the request `content-type` includes a
|
||||
// server-acceptable mime-type
|
||||
//
|
||||
// Failure should yield an HTTP 415 (`http.StatusUnsupportedMediaType`)
|
||||
func HasContentType(r *http.Response, mimetype string) bool {
|
||||
contentType := r.Header.Get("Content-type")
|
||||
if contentType == "" {
|
||||
return mimetype == "application/octet-stream"
|
||||
}
|
||||
|
||||
for _, v := range strings.Split(contentType, ",") {
|
||||
t, _, err := mime.ParseMediaType(v)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if t == mimetype {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type GitTag struct {
|
||||
Commit struct {
|
||||
Created string `json:"created,omitempty"`
|
||||
SHA string `json:"sha,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
} `json:"commit"`
|
||||
Id string `json:"id,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
TarballURL string `json:"tarball_url,omitempty"`
|
||||
ZipballURL string `json:"zipball_url,omitempty"`
|
||||
}
|
||||
|
||||
func (gh *GitHub) GetGitTagInfo(owner, repo, tag string, ctx context.Context) (*GitTag, error) {
|
||||
url := fmt.Sprintf("/repos/%s/%s/tags/%s", owner, repo, tag)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("accept", "application/json")
|
||||
|
||||
res, err := gh.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if !HasContentType(res, "application/json") {
|
||||
return nil, fmt.Errorf("content-type: %q should be %q",
|
||||
res.Header.Get("content-type"), "application/json")
|
||||
}
|
||||
dec := json.NewDecoder(io.LimitReader(res.Body, 32*1024))
|
||||
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
var cres GitTag
|
||||
err := dec.Decode(&cres)
|
||||
return &cres, err
|
||||
} else {
|
||||
switch res.StatusCode {
|
||||
case 404:
|
||||
var error ApiNotFoundError
|
||||
err := dec.Decode(&error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, error
|
||||
default:
|
||||
return nil, fmt.Errorf("an unexpected error occurred")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type CreateReleaseRequest struct {
|
||||
Owner string `json:"-"`
|
||||
Repo string `json:"-"`
|
||||
|
||||
Body string `json:"body,omitempty"`
|
||||
Draft bool `json:"draft,omitempty"`
|
||||
HideArchiveLinks bool `json:"hide_archive_links,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
PreRelease bool `json:"prerelease,omitempty"`
|
||||
TagName string `json:"tag_name"`
|
||||
TargetCommitish string `json:"target_commitish,omitempty"`
|
||||
}
|
||||
type CreateReleaseResponse struct {
|
||||
Body string `json:"body"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
Draft bool `json:"draft"`
|
||||
HideArchiveLinks bool `json:"hide_archive_links"`
|
||||
HtmlURL string `json:"html_url"`
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
PreRelease bool `json:"prerelease"`
|
||||
PublishedAt string `json:"published_at"`
|
||||
TagName string `json:"tag_name"`
|
||||
TarballURL string `json:"tarball_url"`
|
||||
TargetCommitish string `json:"target_commitish"`
|
||||
UploadURL string `json:"upload_url"`
|
||||
URL string `json:"url"`
|
||||
ZipballURL string `json:"zipball_url"`
|
||||
}
|
||||
|
||||
func (gh *GitHub) CreateRelease(creq *CreateReleaseRequest, ctx context.Context) (*CreateReleaseResponse, error) {
|
||||
var b bytes.Buffer
|
||||
|
||||
if err := json.NewEncoder(&b).Encode(creq); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("/repos/%s/%s/releases", creq.Owner, creq.Repo)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, &b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("content-type", "application/json")
|
||||
req.Header.Set("accept", "application/json")
|
||||
|
||||
res, err := gh.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if !HasContentType(res, "application/json") {
|
||||
return nil, fmt.Errorf("content-type: %q should be %q",
|
||||
res.Header.Get("content-type"), "application/json")
|
||||
}
|
||||
dec := json.NewDecoder(io.LimitReader(res.Body, 32*1024))
|
||||
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
var cres CreateReleaseResponse
|
||||
err := dec.Decode(&cres)
|
||||
return &cres, err
|
||||
} else {
|
||||
switch res.StatusCode {
|
||||
case 404:
|
||||
var error ApiNotFoundError
|
||||
err := dec.Decode(&error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, error
|
||||
case 409:
|
||||
var error ApiError
|
||||
err := dec.Decode(&error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, error
|
||||
case 422:
|
||||
var error ApiValidationError
|
||||
err := dec.Decode(&error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, error
|
||||
default:
|
||||
return nil, fmt.Errorf("an unexpected error occurred")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type CreateReleaseArtifactRequest struct {
|
||||
Owner string `json:"-"`
|
||||
Repo string `json:"-"`
|
||||
|
||||
Id int64 `json:"-"`
|
||||
|
||||
Name string `json:"-"`
|
||||
ExternalUrl string `json:"-"`
|
||||
|
||||
Attachment io.Reader `json:"-"`
|
||||
}
|
||||
type CreateReleaseArtifactResponse struct {
|
||||
BrowserDownloadUrl string `json:"browser_download_url"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
DownloadCount int64 `json:"download_count"`
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
Type string `json:"type"`
|
||||
Uuid string `json:"uuid"`
|
||||
}
|
||||
|
||||
func (gh *GitHub) CreateReleaseArtifact(creq *CreateReleaseArtifactRequest, ctx context.Context) (*CreateReleaseArtifactResponse, error) {
|
||||
var b bytes.Buffer
|
||||
|
||||
w := multipart.NewWriter(&b)
|
||||
|
||||
if c, ok := creq.Attachment.(io.Closer); ok {
|
||||
defer c.Close()
|
||||
}
|
||||
var fw io.Writer
|
||||
var rd io.Reader
|
||||
var err error
|
||||
if f, ok := creq.Attachment.(*os.File); ok {
|
||||
fw, err = w.CreateFormFile("attachment", f.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rd = f
|
||||
} else {
|
||||
fw, err = w.CreateFormField("external_url")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rd = bytes.NewBufferString(creq.ExternalUrl)
|
||||
}
|
||||
if _, err := io.Copy(fw, rd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
w.Close()
|
||||
|
||||
url := fmt.Sprintf("/repos/%s/%s/releases/%d/assets", creq.Owner, creq.Repo, creq.Id)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, &b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("content-type", w.FormDataContentType())
|
||||
req.Header.Set("accept", "application/json")
|
||||
|
||||
res, err := gh.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if !HasContentType(res, "application/json") {
|
||||
return nil, fmt.Errorf("content-type: %q should be %q",
|
||||
res.Header.Get("content-type"), "application/json")
|
||||
}
|
||||
dec := json.NewDecoder(io.LimitReader(res.Body, 32*1024))
|
||||
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
var cres CreateReleaseArtifactResponse
|
||||
err := dec.Decode(&cres)
|
||||
return &cres, err
|
||||
} else {
|
||||
switch res.StatusCode {
|
||||
case 400:
|
||||
var error ApiError
|
||||
err := dec.Decode(&error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, error
|
||||
case 404:
|
||||
var error ApiNotFoundError
|
||||
err := dec.Decode(&error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, error
|
||||
case 413:
|
||||
fallthrough // quota?
|
||||
default:
|
||||
return nil, fmt.Errorf("an unexpected error occurred")
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue