mirror of
https://github.com/thegeeklab/wp-gitea-release.git
synced 2024-06-02 18:29:43 +02:00
Compare commits
35 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
8671b7ef22 | ||
|
38b9523c8f | ||
|
328ba9c68c | ||
3f5b43cfdf | |||
|
6485a9b36d | ||
|
1181fcc6c6 | ||
070bf083f3 | |||
750b60a81f | |||
d88993d496 | |||
14e918c6cb | |||
a196e5f4ac | |||
|
8b057ea06b | ||
00b46b4eff | |||
|
917195656c | ||
|
bd08bdd7fc | ||
|
68b907846e | ||
|
0dff9dd40e | ||
|
0f8ed75513 | ||
|
37bd796447 | ||
|
db82f87db3 | ||
|
5f80426280 | ||
|
e0f12a17bb | ||
|
f24bfd501a | ||
|
2d5a62c9d4 | ||
|
0cdd879414 | ||
|
15c7f3d57f | ||
9eeb0d8ac1 | |||
|
6ddd3608aa | ||
|
d16a25c1bf | ||
|
0d2579dad8 | ||
|
6f67efabfb | ||
|
2988bd14d0 | ||
|
94ef9c793d | ||
557e3e18f1 | |||
|
127b468730 |
|
@ -23,7 +23,6 @@ linters:
|
|||
- errchkjson
|
||||
- errname
|
||||
- errorlint
|
||||
- execinquery
|
||||
- exhaustive
|
||||
- exportloopref
|
||||
- forcetypeassert
|
||||
|
@ -37,12 +36,12 @@ linters:
|
|||
- gocyclo
|
||||
- godot
|
||||
- godox
|
||||
- goerr113
|
||||
- err113
|
||||
- gofmt
|
||||
- gofumpt
|
||||
- goheader
|
||||
- goimports
|
||||
- gomnd
|
||||
- mnd
|
||||
- gomoddirectives
|
||||
- gomodguard
|
||||
- goprintffuncname
|
||||
|
|
6
.mockery.yaml
Normal file
6
.mockery.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
all: True
|
||||
dir: "{{.PackageName}}/mocks"
|
||||
outpkg: "mocks"
|
||||
packages:
|
||||
github.com/thegeeklab/wp-gitea-release/gitea:
|
|
@ -4,13 +4,10 @@ when:
|
|||
- event: [push, manual]
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
- event: [push]
|
||||
branch:
|
||||
- renovate/auto/*
|
||||
|
||||
steps:
|
||||
- name: dryrun
|
||||
image: quay.io/thegeeklab/wp-docker-buildx:3
|
||||
image: quay.io/thegeeklab/wp-docker-buildx:4
|
||||
settings:
|
||||
containerfile: Containerfile.multiarch
|
||||
dry_run: true
|
||||
|
@ -23,7 +20,7 @@ steps:
|
|||
- event: [pull_request]
|
||||
|
||||
- name: publish-dockerhub
|
||||
image: quay.io/thegeeklab/wp-docker-buildx:3
|
||||
image: quay.io/thegeeklab/wp-docker-buildx:4
|
||||
group: container
|
||||
settings:
|
||||
auto_tag: true
|
||||
|
@ -44,7 +41,7 @@ steps:
|
|||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
|
||||
- name: publish-quay
|
||||
image: quay.io/thegeeklab/wp-docker-buildx:3
|
||||
image: quay.io/thegeeklab/wp-docker-buildx:4
|
||||
group: container
|
||||
settings:
|
||||
auto_tag: true
|
||||
|
|
|
@ -4,9 +4,6 @@ when:
|
|||
- event: [push, manual]
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
- event: [push]
|
||||
branch:
|
||||
- renovate/auto/*
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
|
|
|
@ -4,9 +4,6 @@ when:
|
|||
- event: [push, manual]
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
- event: [push]
|
||||
branch:
|
||||
- renovate/auto/*
|
||||
|
||||
steps:
|
||||
- name: markdownlint
|
||||
|
|
|
@ -4,9 +4,6 @@ when:
|
|||
- event: [push, manual]
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
- event: [push]
|
||||
branch:
|
||||
- renovate/auto/*
|
||||
|
||||
steps:
|
||||
- name: lint
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.22@sha256:ef61a20960397f4d44b0e729298bf02327ca94f1519239ddc6d91689615b1367 as build
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.22@sha256:f43c6f049f04cbbaeb28f0aad3eea15274a7d0a7899a617d0037aec48d7ab010 as build
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
|
@ -8,7 +8,7 @@ WORKDIR /src
|
|||
|
||||
RUN make build
|
||||
|
||||
FROM docker.io/library/alpine:3.19@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b
|
||||
FROM docker.io/library/alpine:3.20@sha256:77726ef6b57ddf65bb551896826ec38bc3e53f75cdde31354fbffb4f25238ebd
|
||||
|
||||
LABEL maintainer="Robert Kaussow <mail@thegeeklab.de>"
|
||||
LABEL org.opencontainers.image.authors="Robert Kaussow <mail@thegeeklab.de>"
|
||||
|
|
11
Makefile
11
Makefile
|
@ -1,7 +1,7 @@
|
|||
# renovate: datasource=github-releases depName=mvdan/gofumpt
|
||||
GOFUMPT_PACKAGE_VERSION := v0.6.0
|
||||
# renovate: datasource=github-releases depName=golangci/golangci-lint
|
||||
GOLANGCI_LINT_PACKAGE_VERSION := v1.55.2
|
||||
GOLANGCI_LINT_PACKAGE_VERSION := v1.59.0
|
||||
|
||||
EXECUTABLE := wp-gitea-release
|
||||
|
||||
|
@ -18,8 +18,8 @@ GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@$(GOFUMPT_PACKAGE_VERSION)
|
|||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_PACKAGE_VERSION)
|
||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||
GOTESTSUM_PACKAGE ?= gotest.tools/gotestsum@latest
|
||||
MOCKERY_PACKAGE ?= github.com/vektra/mockery/v2@latest
|
||||
|
||||
GENERATE ?=
|
||||
XGO_VERSION := go-1.22.x
|
||||
XGO_TARGETS ?= linux/amd64,linux/arm-6,linux/arm-7,linux/arm64
|
||||
|
||||
|
@ -65,11 +65,8 @@ lint: golangci-lint
|
|||
|
||||
.PHONY: generate
|
||||
generate:
|
||||
$(GO) generate $(GENERATE)
|
||||
|
||||
.PHONY: generate-docs
|
||||
generate-docs:
|
||||
$(GO) generate ./cmd/$(EXECUTABLE)/flags.go
|
||||
$(GO) generate $(PACKAGES)
|
||||
$(GO) run $(MOCKERY_PACKAGE)
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
//go:build generate
|
||||
// +build generate
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"fmt"
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/thegeeklab/wp-gitea-release/plugin"
|
||||
"github.com/thegeeklab/wp-plugin-go/docs"
|
||||
wp "github.com/thegeeklab/wp-plugin-go/plugin"
|
||||
wp_template "github.com/thegeeklab/wp-plugin-go/template"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
//go:embed templates/docs-data.yaml.tmpl
|
||||
var yamlTemplate embed.FS
|
||||
|
||||
func main() {
|
||||
settings := &plugin.Settings{}
|
||||
app := &cli.App{
|
||||
Flags: settingsFlags(settings, wp.FlagsPluginCategory),
|
||||
}
|
||||
|
||||
out, err := toYAML(app)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fi, err := os.Create("../../docs/data/data-raw.yaml")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer fi.Close()
|
||||
if _, err := fi.WriteString(out); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func toYAML(app *cli.App) (string, error) {
|
||||
var w bytes.Buffer
|
||||
|
||||
yamlTmpl, err := template.New("docs").Funcs(wp_template.LoadFuncMap()).ParseFS(yamlTemplate, "templates/docs-data.yaml.tmpl")
|
||||
if err != nil {
|
||||
fmt.Println(yamlTmpl)
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := yamlTmpl.ExecuteTemplate(&w, "docs-data.yaml.tmpl", docs.GetTemplateData(app)); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return w.String(), nil
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/thegeeklab/wp-gitea-release/plugin"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// settingsFlags has the cli.Flags for the plugin.Settings.
|
||||
//
|
||||
//go:generate go run docs.go flags.go
|
||||
func settingsFlags(settings *plugin.Settings, category string) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "api-key",
|
||||
Usage: "api key to access Gitea API",
|
||||
EnvVars: []string{"PLUGIN_API_KEY", "GITEA_RELEASE_API_KEY", "GITEA_TOKEN"},
|
||||
Destination: &settings.APIKey,
|
||||
Category: category,
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "files",
|
||||
Usage: "list of files to upload",
|
||||
EnvVars: []string{"PLUGIN_FILES", "GITEA_RELEASE_FILES"},
|
||||
Category: category,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "file-exists",
|
||||
Value: "overwrite",
|
||||
Usage: "what to do if file already exist",
|
||||
EnvVars: []string{"PLUGIN_FILE_EXIST", "GITEA_RELEASE_FILE_EXIST"},
|
||||
Destination: &settings.FileExists,
|
||||
Category: category,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "checksum",
|
||||
Usage: "generate specific checksums",
|
||||
EnvVars: []string{"PLUGIN_CHECKSUM", "GITEA_RELEASE_CHECKSUM"},
|
||||
Destination: &settings.Checksum,
|
||||
Category: category,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "draft",
|
||||
Usage: "create a draft release",
|
||||
EnvVars: []string{"PLUGIN_DRAFT", "GITEA_RELEASE_DRAFT"},
|
||||
Destination: &settings.Draft,
|
||||
Category: category,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "prerelease",
|
||||
Usage: "set the release as prerelease",
|
||||
EnvVars: []string{"PLUGIN_PRERELEASE", "GITEA_RELEASE_PRERELEASE"},
|
||||
Destination: &settings.PreRelease,
|
||||
Category: category,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "base-url",
|
||||
Usage: "URL of the Gitea instance",
|
||||
EnvVars: []string{"PLUGIN_BASE_URL", "GITEA_RELEASE_BASE_URL"},
|
||||
Category: category,
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "note",
|
||||
Usage: "file or string with notes for the release",
|
||||
EnvVars: []string{"PLUGIN_NOTE", "GITEA_RELEASE_NOTE"},
|
||||
Destination: &settings.Note,
|
||||
Category: category,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "title",
|
||||
Usage: "file or string for the title shown in the Gitea release",
|
||||
EnvVars: []string{"PLUGIN_TITLE", "GITEA_RELEASE_TITLE", "CI_COMMIT_TAG"},
|
||||
Destination: &settings.Title,
|
||||
DefaultText: "$CI_COMMIT_TAG",
|
||||
Category: category,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "event",
|
||||
Value: "push",
|
||||
Usage: "build event",
|
||||
EnvVars: []string{"CI_PIPELINE_EVENT"},
|
||||
Destination: &settings.Event,
|
||||
DefaultText: "$CI_PIPELINE_EVENT",
|
||||
Category: category,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "commit-ref",
|
||||
Value: "refs/heads/main",
|
||||
Usage: "git commit ref",
|
||||
EnvVars: []string{"CI_COMMIT_REF"},
|
||||
Destination: &settings.CommitRef,
|
||||
Category: category,
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,11 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/thegeeklab/wp-gitea-release/plugin"
|
||||
|
||||
wp "github.com/thegeeklab/wp-plugin-go/plugin"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
|
@ -15,14 +11,5 @@ var (
|
|||
)
|
||||
|
||||
func main() {
|
||||
settings := &plugin.Settings{}
|
||||
options := wp.Options{
|
||||
Name: "wp-gitea-release",
|
||||
Description: "Publish files and artifacts to Gitea releases",
|
||||
Version: BuildVersion,
|
||||
VersionMetadata: fmt.Sprintf("date=%s", BuildDate),
|
||||
Flags: settingsFlags(settings, wp.FlagsPluginCategory),
|
||||
}
|
||||
|
||||
plugin.New(options, settings).Run()
|
||||
plugin.New(nil, BuildVersion, BuildDate).Run()
|
||||
}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
---
|
||||
{{- if .GlobalArgs }}
|
||||
properties:
|
||||
{{- range $v := .GlobalArgs }}
|
||||
- name: {{ $v.Name }}
|
||||
{{- with $v.Description }}
|
||||
description: |
|
||||
{{ . | ToSentence }}
|
||||
{{- end }}
|
||||
{{- with $v.Type }}
|
||||
type: {{ . }}
|
||||
{{- end }}
|
||||
{{- with $v.Default }}
|
||||
defaultValue: {{ . }}
|
||||
{{- end }}
|
||||
required: {{ default false $v.Required }}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
|
@ -25,14 +25,11 @@ Only tag events are supported by this plugin. Running the plugin on other events
|
|||
{{< /hint >}}
|
||||
|
||||
```YAML
|
||||
kind: pipeline
|
||||
name: default
|
||||
|
||||
steps:
|
||||
- name: publish
|
||||
image: quay.io/thegeeklab/wp-gitea-release
|
||||
settings:
|
||||
api_key: 3LbMg9Kncpdkhjp3bh3dMnKNXLjVMTsXk4sM
|
||||
api_key: randomstring
|
||||
base_url: https://gitea.rknet.org
|
||||
files: build/*
|
||||
```
|
||||
|
@ -64,7 +61,7 @@ docker build --file Containerfile.multiarch --tag thegeeklab/wp-gitea-release .
|
|||
```Shell
|
||||
docker run --rm \
|
||||
-e PLUGIN_BASE_URL=https://try.gitea.io \
|
||||
-e PLUGIN_API_KEY=your-api-key \
|
||||
-e PLUGIN_API_KEY=randomstring \
|
||||
-e PLUGIN_FILES=build/* \
|
||||
-e CI_REPO_OWNER=gitea \
|
||||
-e CI_REPO_NAME=test \
|
||||
|
|
|
@ -38,6 +38,22 @@ properties:
|
|||
type: list
|
||||
required: false
|
||||
|
||||
- name: insecure_skip_verify
|
||||
description: |
|
||||
Skip SSL verification.
|
||||
|
||||
Activating this option is insecure and should be avoided in most cases.
|
||||
type: bool
|
||||
defaultValue: false
|
||||
required: false
|
||||
|
||||
- name: log_level
|
||||
description: |
|
||||
Plugin log level.
|
||||
type: string
|
||||
defaultValue: "info"
|
||||
required: false
|
||||
|
||||
- name: note
|
||||
description: |
|
||||
File or string with notes for the release.
|
||||
|
|
16
gitea/api.go
Normal file
16
gitea/api.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
package gitea
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
)
|
||||
|
||||
//nolint:lll
|
||||
type APIClient interface {
|
||||
ListReleases(owner, repo string, opt gitea.ListReleasesOptions) ([]*gitea.Release, *gitea.Response, error)
|
||||
CreateRelease(owner, repo string, opt gitea.CreateReleaseOption) (*gitea.Release, *gitea.Response, error)
|
||||
ListReleaseAttachments(user, repo string, release int64, opt gitea.ListReleaseAttachmentsOptions) ([]*gitea.Attachment, *gitea.Response, error)
|
||||
CreateReleaseAttachment(user, repo string, release int64, file io.Reader, filename string) (*gitea.Attachment, *gitea.Response, error)
|
||||
DeleteReleaseAttachment(user, repo string, release, id int64) (*gitea.Response, error)
|
||||
}
|
175
gitea/gitea.go
Normal file
175
gitea/gitea.go
Normal file
|
@ -0,0 +1,175 @@
|
|||
package gitea
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrReleaseNotFound = errors.New("release not found")
|
||||
ErrFileExists = errors.New("asset file already exist")
|
||||
)
|
||||
|
||||
const (
|
||||
FileExistsOverwrite FileExists = "overwrite"
|
||||
FileExistsFail FileExists = "fail"
|
||||
FileExistsSkip FileExists = "skip"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
client APIClient
|
||||
Release *Release
|
||||
}
|
||||
|
||||
type Release struct {
|
||||
client APIClient
|
||||
Opt ReleaseOptions
|
||||
}
|
||||
|
||||
type ReleaseOptions struct {
|
||||
Owner string
|
||||
Repo string
|
||||
Tag string
|
||||
Draft bool
|
||||
Prerelease bool
|
||||
FileExists string
|
||||
Title string
|
||||
Note string
|
||||
}
|
||||
|
||||
type FileExists string
|
||||
|
||||
// NewClient creates a new Client instance with the provided Gitea client.
|
||||
func NewClient(url, key string, client *http.Client) (*Client, error) {
|
||||
c, err := gitea.NewClient(url, gitea.SetToken(key), gitea.SetHTTPClient(client))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Client{
|
||||
client: c,
|
||||
Release: &Release{
|
||||
client: c,
|
||||
Opt: ReleaseOptions{},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Find retrieves the release with the specified tag name from the repository.
|
||||
// If the release is not found, it returns an ErrReleaseNotFound error.
|
||||
func (r *Release) Find() (*gitea.Release, error) {
|
||||
releases, _, err := r.client.ListReleases(r.Opt.Owner, r.Opt.Repo, gitea.ListReleasesOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, release := range releases {
|
||||
if release.TagName == r.Opt.Tag {
|
||||
log.Info().Msgf("found release: %s", r.Opt.Tag)
|
||||
|
||||
return release, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%w: %s", ErrReleaseNotFound, r.Opt.Tag)
|
||||
}
|
||||
|
||||
// Create creates a new release on the Gitea repository with the specified options.
|
||||
// It returns the created release or an error if the creation failed.
|
||||
func (r *Release) Create() (*gitea.Release, error) {
|
||||
opts := gitea.CreateReleaseOption{
|
||||
TagName: r.Opt.Tag,
|
||||
IsDraft: r.Opt.Draft,
|
||||
IsPrerelease: r.Opt.Prerelease,
|
||||
Title: r.Opt.Title,
|
||||
Note: r.Opt.Note,
|
||||
}
|
||||
|
||||
release, _, err := r.client.CreateRelease(r.Opt.Owner, r.Opt.Repo, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create release: %w", err)
|
||||
}
|
||||
|
||||
log.Info().Msgf("created release: %s", r.Opt.Tag)
|
||||
|
||||
return release, nil
|
||||
}
|
||||
|
||||
// AddAttachments uploads the specified files as attachments to the release with the given ID.
|
||||
// It first checks for any existing attachments with the same names,
|
||||
// and handles them according to the FileExists option:
|
||||
//
|
||||
// - "overwrite": overwrites the existing attachment
|
||||
// - "fail": returns an error if the file already exists
|
||||
// - "skip": skips uploading the file and logs a warning
|
||||
//
|
||||
// If there are no conflicts, it uploads the new files as attachments to the release.
|
||||
func (r *Release) AddAttachments(releaseID int64, files []string) error {
|
||||
attachments, _, err := r.client.ListReleaseAttachments(
|
||||
r.Opt.Owner,
|
||||
r.Opt.Repo,
|
||||
releaseID,
|
||||
gitea.ListReleaseAttachmentsOptions{},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch attachments: %w", err)
|
||||
}
|
||||
|
||||
existingAttachments := make(map[string]bool)
|
||||
attachmentsMap := make(map[string]*gitea.Attachment)
|
||||
|
||||
for _, attachment := range attachments {
|
||||
attachmentsMap[attachment.Name] = attachment
|
||||
existingAttachments[attachment.Name] = true
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
fileName := path.Base(file)
|
||||
if existingAttachments[fileName] {
|
||||
switch FileExists(r.Opt.FileExists) {
|
||||
case FileExistsOverwrite:
|
||||
_, err := r.client.DeleteReleaseAttachment(r.Opt.Owner, r.Opt.Repo, releaseID, attachmentsMap[fileName].ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete artifact: %s: %w", fileName, err)
|
||||
}
|
||||
|
||||
log.Info().Msgf("deleted artifact: %s", fileName)
|
||||
case FileExistsFail:
|
||||
return fmt.Errorf("%w: %s", ErrFileExists, fileName)
|
||||
case FileExistsSkip:
|
||||
log.Warn().Msgf("skip existing artifact: %s", fileName)
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if err := r.uploadFile(releaseID, file); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Release) uploadFile(releaseID int64, file string) error {
|
||||
handle, err := os.Open(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read artifact: %s: %w", file, err)
|
||||
}
|
||||
defer handle.Close()
|
||||
|
||||
_, _, err = r.client.CreateReleaseAttachment(r.Opt.Owner, r.Opt.Repo, releaseID, handle, path.Base(file))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to upload artifact: %s: %w", file, err)
|
||||
}
|
||||
|
||||
log.Info().Msgf("uploaded artifact: %s", path.Base(file))
|
||||
|
||||
return nil
|
||||
}
|
321
gitea/gitea_test.go
Normal file
321
gitea/gitea_test.go
Normal file
|
@ -0,0 +1,321 @@
|
|||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/thegeeklab/wp-gitea-release/gitea/mocks"
|
||||
)
|
||||
|
||||
var ErrNoSuchFileOrDirectory = errors.New("no such file or directory")
|
||||
|
||||
func TestReleaseFind(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
opt ReleaseOptions
|
||||
want *gitea.Release
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "find release by tag",
|
||||
opt: ReleaseOptions{
|
||||
Owner: "test-owner",
|
||||
Repo: "test-repo",
|
||||
Tag: "v1.0.0",
|
||||
},
|
||||
want: &gitea.Release{
|
||||
TagName: "v1.0.0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "release not found",
|
||||
opt: ReleaseOptions{
|
||||
Owner: "test-owner",
|
||||
Repo: "test-repo",
|
||||
Tag: "v1.1.0",
|
||||
},
|
||||
want: nil,
|
||||
wantErr: ErrReleaseNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
mockClient := mocks.NewMockAPIClient(t)
|
||||
r := &Release{
|
||||
Opt: tt.opt,
|
||||
client: mockClient,
|
||||
}
|
||||
|
||||
mockClient.
|
||||
On("ListReleases", mock.Anything, mock.Anything, mock.Anything).
|
||||
Return([]*gitea.Release{
|
||||
{
|
||||
ID: 1,
|
||||
TagName: "v1.0.0",
|
||||
Title: "Release v1.0.0",
|
||||
Note: "This is the release notes for v1.0.0",
|
||||
IsDraft: false,
|
||||
IsPrerelease: false,
|
||||
},
|
||||
}, nil, nil)
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
release, err := r.Find()
|
||||
|
||||
if tt.wantErr != nil {
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, release)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.want.TagName, release.TagName)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseCreate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
opt ReleaseOptions
|
||||
want *gitea.Release
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "create release",
|
||||
opt: ReleaseOptions{
|
||||
Owner: "test-owner",
|
||||
Repo: "test-repo",
|
||||
Tag: "v1.1.0",
|
||||
Title: "Release v1.1.0",
|
||||
Note: "This is the release notes for v1.1.0",
|
||||
Draft: false,
|
||||
Prerelease: false,
|
||||
},
|
||||
want: &gitea.Release{
|
||||
TagName: "v1.1.0",
|
||||
Title: "Release v1.1.0",
|
||||
Note: "This is the release notes for v1.1.0",
|
||||
IsDraft: false,
|
||||
IsPrerelease: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create draft release",
|
||||
opt: ReleaseOptions{
|
||||
Owner: "test-owner",
|
||||
Repo: "test-repo",
|
||||
Tag: "v1.2.0",
|
||||
Title: "Release v1.2.0",
|
||||
Note: "This is the release notes for v1.2.0",
|
||||
Draft: true,
|
||||
Prerelease: false,
|
||||
},
|
||||
want: &gitea.Release{
|
||||
TagName: "v1.2.0",
|
||||
Title: "Release v1.2.0",
|
||||
Note: "This is the release notes for v1.2.0",
|
||||
IsDraft: true,
|
||||
IsPrerelease: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create prerelease",
|
||||
opt: ReleaseOptions{
|
||||
Owner: "test-owner",
|
||||
Repo: "test-repo",
|
||||
Tag: "v1.3.0-rc1",
|
||||
Title: "Release v1.3.0-rc1",
|
||||
Note: "This is the release notes for v1.3.0-rc1",
|
||||
Draft: false,
|
||||
Prerelease: true,
|
||||
},
|
||||
want: &gitea.Release{
|
||||
TagName: "v1.3.0-rc1",
|
||||
Title: "Release v1.3.0-rc1",
|
||||
Note: "This is the release notes for v1.3.0-rc1",
|
||||
IsDraft: false,
|
||||
IsPrerelease: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
mockClient := mocks.NewMockAPIClient(t)
|
||||
r := &Release{
|
||||
Opt: tt.opt,
|
||||
client: mockClient,
|
||||
}
|
||||
|
||||
mockClient.
|
||||
On("CreateRelease", mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(&gitea.Release{
|
||||
ID: 1,
|
||||
TagName: tt.opt.Tag,
|
||||
Title: tt.opt.Title,
|
||||
Note: tt.opt.Note,
|
||||
IsDraft: tt.opt.Draft,
|
||||
IsPrerelease: tt.opt.Prerelease,
|
||||
}, nil, nil)
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
release, err := r.Create()
|
||||
|
||||
if tt.wantErr != nil {
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, release)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.want.TagName, release.TagName)
|
||||
assert.Equal(t, tt.want.Title, release.Title)
|
||||
assert.Equal(t, tt.want.Note, release.Note)
|
||||
assert.Equal(t, tt.want.IsDraft, release.IsDraft)
|
||||
assert.Equal(t, tt.want.IsPrerelease, release.IsPrerelease)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseAddAttachments(t *testing.T) {
|
||||
logBuffer := &bytes.Buffer{}
|
||||
logger := zerolog.New(logBuffer)
|
||||
log.Logger = logger
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
opt ReleaseOptions
|
||||
files []string
|
||||
fileExists string
|
||||
wantErr error
|
||||
wantLogs []string
|
||||
}{
|
||||
{
|
||||
name: "add new attachments",
|
||||
opt: ReleaseOptions{
|
||||
Owner: "test-owner",
|
||||
Repo: "test-repo",
|
||||
Tag: "v2.0.0",
|
||||
Title: "Release v2.0.0",
|
||||
FileExists: "overwrite",
|
||||
},
|
||||
files: []string{createTempFile(t, "file1.txt"), createTempFile(t, "file2.txt")},
|
||||
wantLogs: []string{"uploaded artifact: file1.txt", "uploaded artifact: file2.txt"},
|
||||
},
|
||||
{
|
||||
name: "fail on existing attachments",
|
||||
opt: ReleaseOptions{
|
||||
Owner: "test-owner",
|
||||
Repo: "test-repo",
|
||||
Tag: "v2.0.0",
|
||||
Title: "Release v2.0.0",
|
||||
FileExists: "fail",
|
||||
},
|
||||
files: []string{createTempFile(t, "file1.txt"), createTempFile(t, "file2.txt")},
|
||||
wantErr: ErrFileExists,
|
||||
},
|
||||
{
|
||||
name: "overwrite on existing attachments",
|
||||
opt: ReleaseOptions{
|
||||
Owner: "test-owner",
|
||||
Repo: "test-repo",
|
||||
Tag: "v2.0.0",
|
||||
Title: "Release v2.0.0",
|
||||
FileExists: "overwrite",
|
||||
},
|
||||
files: []string{createTempFile(t, "file1.txt"), createTempFile(t, "file2.txt")},
|
||||
wantLogs: []string{"deleted artifact: file1.txt", "uploaded artifact: file1.txt"},
|
||||
},
|
||||
{
|
||||
name: "skip on existing attachments",
|
||||
opt: ReleaseOptions{
|
||||
Owner: "test-owner",
|
||||
Repo: "test-repo",
|
||||
Tag: "v2.0.0",
|
||||
Title: "Release v2.0.0",
|
||||
FileExists: "skip",
|
||||
},
|
||||
files: []string{createTempFile(t, "file1.txt"), createTempFile(t, "file2.txt")},
|
||||
wantLogs: []string{"skip existing artifact: file1"},
|
||||
},
|
||||
{
|
||||
name: "fail on invalid file",
|
||||
opt: ReleaseOptions{
|
||||
Owner: "test-owner",
|
||||
Repo: "test-repo",
|
||||
Tag: "v2.0.0",
|
||||
Title: "Release v2.0.0",
|
||||
FileExists: "overwrite",
|
||||
},
|
||||
files: []string{"testdata/file1.txt", "testdata/invalid.txt"},
|
||||
wantErr: ErrNoSuchFileOrDirectory,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
logBuffer.Reset()
|
||||
|
||||
mockClient := mocks.NewMockAPIClient(t)
|
||||
r := &Release{
|
||||
Opt: tt.opt,
|
||||
client: mockClient,
|
||||
}
|
||||
|
||||
mockClient.
|
||||
On("ListReleaseAttachments", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return([]*gitea.Attachment{
|
||||
{
|
||||
Name: "file1.txt",
|
||||
},
|
||||
}, nil, nil)
|
||||
|
||||
if FileExists(tt.opt.FileExists) == FileExistsOverwrite {
|
||||
mockClient.
|
||||
On("DeleteReleaseAttachment", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(nil, nil)
|
||||
}
|
||||
|
||||
if tt.wantErr == nil {
|
||||
mockClient.
|
||||
On("CreateReleaseAttachment", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(nil, nil, nil)
|
||||
}
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := r.AddAttachments(1, tt.files)
|
||||
|
||||
// Assert log output.
|
||||
for _, l := range tt.wantLogs {
|
||||
assert.Contains(t, logBuffer.String(), l)
|
||||
}
|
||||
|
||||
if tt.wantErr != nil {
|
||||
assert.Error(t, err)
|
||||
assert.ErrorContains(t, err, tt.wantErr.Error())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func createTempFile(t *testing.T, name string) string {
|
||||
t.Helper()
|
||||
|
||||
name = filepath.Join(t.TempDir(), name)
|
||||
_ = os.WriteFile(name, []byte("hello"), 0o600)
|
||||
|
||||
return name
|
||||
}
|
378
gitea/mocks/mock_APIClient.go
Normal file
378
gitea/mocks/mock_APIClient.go
Normal file
|
@ -0,0 +1,378 @@
|
|||
// Code generated by mockery v2.43.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
io "io"
|
||||
|
||||
gitea "code.gitea.io/sdk/gitea"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// MockAPIClient is an autogenerated mock type for the APIClient type
|
||||
type MockAPIClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type MockAPIClient_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *MockAPIClient) EXPECT() *MockAPIClient_Expecter {
|
||||
return &MockAPIClient_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// CreateRelease provides a mock function with given fields: owner, repo, opt
|
||||
func (_m *MockAPIClient) CreateRelease(owner string, repo string, opt gitea.CreateReleaseOption) (*gitea.Release, *gitea.Response, error) {
|
||||
ret := _m.Called(owner, repo, opt)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CreateRelease")
|
||||
}
|
||||
|
||||
var r0 *gitea.Release
|
||||
var r1 *gitea.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(string, string, gitea.CreateReleaseOption) (*gitea.Release, *gitea.Response, error)); ok {
|
||||
return rf(owner, repo, opt)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string, string, gitea.CreateReleaseOption) *gitea.Release); ok {
|
||||
r0 = rf(owner, repo, opt)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*gitea.Release)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string, string, gitea.CreateReleaseOption) *gitea.Response); ok {
|
||||
r1 = rf(owner, repo, opt)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*gitea.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(string, string, gitea.CreateReleaseOption) error); ok {
|
||||
r2 = rf(owner, repo, opt)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// MockAPIClient_CreateRelease_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateRelease'
|
||||
type MockAPIClient_CreateRelease_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// CreateRelease is a helper method to define mock.On call
|
||||
// - owner string
|
||||
// - repo string
|
||||
// - opt gitea.CreateReleaseOption
|
||||
func (_e *MockAPIClient_Expecter) CreateRelease(owner interface{}, repo interface{}, opt interface{}) *MockAPIClient_CreateRelease_Call {
|
||||
return &MockAPIClient_CreateRelease_Call{Call: _e.mock.On("CreateRelease", owner, repo, opt)}
|
||||
}
|
||||
|
||||
func (_c *MockAPIClient_CreateRelease_Call) Run(run func(owner string, repo string, opt gitea.CreateReleaseOption)) *MockAPIClient_CreateRelease_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(string), args[1].(string), args[2].(gitea.CreateReleaseOption))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockAPIClient_CreateRelease_Call) Return(_a0 *gitea.Release, _a1 *gitea.Response, _a2 error) *MockAPIClient_CreateRelease_Call {
|
||||
_c.Call.Return(_a0, _a1, _a2)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockAPIClient_CreateRelease_Call) RunAndReturn(run func(string, string, gitea.CreateReleaseOption) (*gitea.Release, *gitea.Response, error)) *MockAPIClient_CreateRelease_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// CreateReleaseAttachment provides a mock function with given fields: user, repo, release, file, filename
|
||||
func (_m *MockAPIClient) CreateReleaseAttachment(user string, repo string, release int64, file io.Reader, filename string) (*gitea.Attachment, *gitea.Response, error) {
|
||||
ret := _m.Called(user, repo, release, file, filename)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CreateReleaseAttachment")
|
||||
}
|
||||
|
||||
var r0 *gitea.Attachment
|
||||
var r1 *gitea.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(string, string, int64, io.Reader, string) (*gitea.Attachment, *gitea.Response, error)); ok {
|
||||
return rf(user, repo, release, file, filename)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string, string, int64, io.Reader, string) *gitea.Attachment); ok {
|
||||
r0 = rf(user, repo, release, file, filename)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*gitea.Attachment)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string, string, int64, io.Reader, string) *gitea.Response); ok {
|
||||
r1 = rf(user, repo, release, file, filename)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*gitea.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(string, string, int64, io.Reader, string) error); ok {
|
||||
r2 = rf(user, repo, release, file, filename)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// MockAPIClient_CreateReleaseAttachment_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateReleaseAttachment'
|
||||
type MockAPIClient_CreateReleaseAttachment_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// CreateReleaseAttachment is a helper method to define mock.On call
|
||||
// - user string
|
||||
// - repo string
|
||||
// - release int64
|
||||
// - file io.Reader
|
||||
// - filename string
|
||||
func (_e *MockAPIClient_Expecter) CreateReleaseAttachment(user interface{}, repo interface{}, release interface{}, file interface{}, filename interface{}) *MockAPIClient_CreateReleaseAttachment_Call {
|
||||
return &MockAPIClient_CreateReleaseAttachment_Call{Call: _e.mock.On("CreateReleaseAttachment", user, repo, release, file, filename)}
|
||||
}
|
||||
|
||||
func (_c *MockAPIClient_CreateReleaseAttachment_Call) Run(run func(user string, repo string, release int64, file io.Reader, filename string)) *MockAPIClient_CreateReleaseAttachment_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(string), args[1].(string), args[2].(int64), args[3].(io.Reader), args[4].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockAPIClient_CreateReleaseAttachment_Call) Return(_a0 *gitea.Attachment, _a1 *gitea.Response, _a2 error) *MockAPIClient_CreateReleaseAttachment_Call {
|
||||
_c.Call.Return(_a0, _a1, _a2)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockAPIClient_CreateReleaseAttachment_Call) RunAndReturn(run func(string, string, int64, io.Reader, string) (*gitea.Attachment, *gitea.Response, error)) *MockAPIClient_CreateReleaseAttachment_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// DeleteReleaseAttachment provides a mock function with given fields: user, repo, release, id
|
||||
func (_m *MockAPIClient) DeleteReleaseAttachment(user string, repo string, release int64, id int64) (*gitea.Response, error) {
|
||||
ret := _m.Called(user, repo, release, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DeleteReleaseAttachment")
|
||||
}
|
||||
|
||||
var r0 *gitea.Response
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string, string, int64, int64) (*gitea.Response, error)); ok {
|
||||
return rf(user, repo, release, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string, string, int64, int64) *gitea.Response); ok {
|
||||
r0 = rf(user, repo, release, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*gitea.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string, string, int64, int64) error); ok {
|
||||
r1 = rf(user, repo, release, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockAPIClient_DeleteReleaseAttachment_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteReleaseAttachment'
|
||||
type MockAPIClient_DeleteReleaseAttachment_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// DeleteReleaseAttachment is a helper method to define mock.On call
|
||||
// - user string
|
||||
// - repo string
|
||||
// - release int64
|
||||
// - id int64
|
||||
func (_e *MockAPIClient_Expecter) DeleteReleaseAttachment(user interface{}, repo interface{}, release interface{}, id interface{}) *MockAPIClient_DeleteReleaseAttachment_Call {
|
||||
return &MockAPIClient_DeleteReleaseAttachment_Call{Call: _e.mock.On("DeleteReleaseAttachment", user, repo, release, id)}
|
||||
}
|
||||
|
||||
func (_c *MockAPIClient_DeleteReleaseAttachment_Call) Run(run func(user string, repo string, release int64, id int64)) *MockAPIClient_DeleteReleaseAttachment_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(string), args[1].(string), args[2].(int64), args[3].(int64))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockAPIClient_DeleteReleaseAttachment_Call) Return(_a0 *gitea.Response, _a1 error) *MockAPIClient_DeleteReleaseAttachment_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockAPIClient_DeleteReleaseAttachment_Call) RunAndReturn(run func(string, string, int64, int64) (*gitea.Response, error)) *MockAPIClient_DeleteReleaseAttachment_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ListReleaseAttachments provides a mock function with given fields: user, repo, release, opt
|
||||
func (_m *MockAPIClient) ListReleaseAttachments(user string, repo string, release int64, opt gitea.ListReleaseAttachmentsOptions) ([]*gitea.Attachment, *gitea.Response, error) {
|
||||
ret := _m.Called(user, repo, release, opt)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListReleaseAttachments")
|
||||
}
|
||||
|
||||
var r0 []*gitea.Attachment
|
||||
var r1 *gitea.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(string, string, int64, gitea.ListReleaseAttachmentsOptions) ([]*gitea.Attachment, *gitea.Response, error)); ok {
|
||||
return rf(user, repo, release, opt)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string, string, int64, gitea.ListReleaseAttachmentsOptions) []*gitea.Attachment); ok {
|
||||
r0 = rf(user, repo, release, opt)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*gitea.Attachment)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string, string, int64, gitea.ListReleaseAttachmentsOptions) *gitea.Response); ok {
|
||||
r1 = rf(user, repo, release, opt)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*gitea.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(string, string, int64, gitea.ListReleaseAttachmentsOptions) error); ok {
|
||||
r2 = rf(user, repo, release, opt)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// MockAPIClient_ListReleaseAttachments_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListReleaseAttachments'
|
||||
type MockAPIClient_ListReleaseAttachments_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ListReleaseAttachments is a helper method to define mock.On call
|
||||
// - user string
|
||||
// - repo string
|
||||
// - release int64
|
||||
// - opt gitea.ListReleaseAttachmentsOptions
|
||||
func (_e *MockAPIClient_Expecter) ListReleaseAttachments(user interface{}, repo interface{}, release interface{}, opt interface{}) *MockAPIClient_ListReleaseAttachments_Call {
|
||||
return &MockAPIClient_ListReleaseAttachments_Call{Call: _e.mock.On("ListReleaseAttachments", user, repo, release, opt)}
|
||||
}
|
||||
|
||||
func (_c *MockAPIClient_ListReleaseAttachments_Call) Run(run func(user string, repo string, release int64, opt gitea.ListReleaseAttachmentsOptions)) *MockAPIClient_ListReleaseAttachments_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(string), args[1].(string), args[2].(int64), args[3].(gitea.ListReleaseAttachmentsOptions))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockAPIClient_ListReleaseAttachments_Call) Return(_a0 []*gitea.Attachment, _a1 *gitea.Response, _a2 error) *MockAPIClient_ListReleaseAttachments_Call {
|
||||
_c.Call.Return(_a0, _a1, _a2)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockAPIClient_ListReleaseAttachments_Call) RunAndReturn(run func(string, string, int64, gitea.ListReleaseAttachmentsOptions) ([]*gitea.Attachment, *gitea.Response, error)) *MockAPIClient_ListReleaseAttachments_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ListReleases provides a mock function with given fields: owner, repo, opt
|
||||
func (_m *MockAPIClient) ListReleases(owner string, repo string, opt gitea.ListReleasesOptions) ([]*gitea.Release, *gitea.Response, error) {
|
||||
ret := _m.Called(owner, repo, opt)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListReleases")
|
||||
}
|
||||
|
||||
var r0 []*gitea.Release
|
||||
var r1 *gitea.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(string, string, gitea.ListReleasesOptions) ([]*gitea.Release, *gitea.Response, error)); ok {
|
||||
return rf(owner, repo, opt)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string, string, gitea.ListReleasesOptions) []*gitea.Release); ok {
|
||||
r0 = rf(owner, repo, opt)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*gitea.Release)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string, string, gitea.ListReleasesOptions) *gitea.Response); ok {
|
||||
r1 = rf(owner, repo, opt)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*gitea.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(string, string, gitea.ListReleasesOptions) error); ok {
|
||||
r2 = rf(owner, repo, opt)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// MockAPIClient_ListReleases_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListReleases'
|
||||
type MockAPIClient_ListReleases_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ListReleases is a helper method to define mock.On call
|
||||
// - owner string
|
||||
// - repo string
|
||||
// - opt gitea.ListReleasesOptions
|
||||
func (_e *MockAPIClient_Expecter) ListReleases(owner interface{}, repo interface{}, opt interface{}) *MockAPIClient_ListReleases_Call {
|
||||
return &MockAPIClient_ListReleases_Call{Call: _e.mock.On("ListReleases", owner, repo, opt)}
|
||||
}
|
||||
|
||||
func (_c *MockAPIClient_ListReleases_Call) Run(run func(owner string, repo string, opt gitea.ListReleasesOptions)) *MockAPIClient_ListReleases_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(string), args[1].(string), args[2].(gitea.ListReleasesOptions))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockAPIClient_ListReleases_Call) Return(_a0 []*gitea.Release, _a1 *gitea.Response, _a2 error) *MockAPIClient_ListReleases_Call {
|
||||
_c.Call.Return(_a0, _a1, _a2)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockAPIClient_ListReleases_Call) RunAndReturn(run func(string, string, gitea.ListReleasesOptions) ([]*gitea.Release, *gitea.Response, error)) *MockAPIClient_ListReleases_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewMockAPIClient creates a new instance of MockAPIClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewMockAPIClient(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *MockAPIClient {
|
||||
mock := &MockAPIClient{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
23
go.mod
23
go.mod
|
@ -3,18 +3,20 @@ module github.com/thegeeklab/wp-gitea-release
|
|||
go 1.22
|
||||
|
||||
require (
|
||||
code.gitea.io/sdk/gitea v0.17.1
|
||||
github.com/rs/zerolog v1.32.0
|
||||
github.com/thegeeklab/wp-plugin-go v1.6.0
|
||||
github.com/urfave/cli/v2 v2.27.1
|
||||
golang.org/x/crypto v0.19.0
|
||||
code.gitea.io/sdk/gitea v0.18.0
|
||||
github.com/rs/zerolog v1.33.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/thegeeklab/wp-plugin-go/v3 v3.0.2
|
||||
github.com/urfave/cli/v2 v2.27.2
|
||||
golang.org/x/crypto v0.23.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/go-fed/httpsig v1.1.0 // indirect
|
||||
github.com/google/uuid v1.1.1 // indirect
|
||||
|
@ -26,10 +28,13 @@ require (
|
|||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mitchellh/copystructure v1.0.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
65
go.sum
65
go.sum
|
@ -1,5 +1,5 @@
|
|||
code.gitea.io/sdk/gitea v0.17.1 h1:3jCPOG2ojbl8AcfaUCRYLT5MUcBMFwS0OSK2mA5Zok8=
|
||||
code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM=
|
||||
code.gitea.io/sdk/gitea v0.18.0 h1:+zZrwVmujIrgobt6wVBWCqITz6bn1aBjnCUHmpZrerI=
|
||||
code.gitea.io/sdk/gitea v0.18.0/go.mod h1:IG9xZJoltDNeDSW0qiF2Vqx5orMWa7OhVWrjvrd5NpI=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
|
@ -8,8 +8,8 @@ github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYr
|
|||
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -18,8 +18,6 @@ github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9e
|
|||
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
|
||||
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||
|
@ -43,8 +41,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
|
||||
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
||||
|
@ -52,39 +50,36 @@ github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFR
|
|||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/thegeeklab/wp-plugin-go v1.6.0 h1:DQ+oLs67FusgeZe3881x9pxH+UrddWygE0eGHLM7jgg=
|
||||
github.com/thegeeklab/wp-plugin-go v1.6.0/go.mod h1:r+Oi0CpnnlG59u4Qp2n6xHGTxd6Krk+OPBZ9F7LQT8A=
|
||||
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
|
||||
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/thegeeklab/wp-plugin-go/v3 v3.0.2 h1:Mv5i8S1WY+BUNjTjX6lOnB3p8S9mvM+XwfY4R98gx0g=
|
||||
github.com/thegeeklab/wp-plugin-go/v3 v3.0.2/go.mod h1:ij1iJcAVgzerBTqXnmq0bu1VA+hhVVwzXKqiqfoGjjg=
|
||||
github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
|
||||
github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
|
||||
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw=
|
||||
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -93,37 +88,27 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
42
internal/doc/main.go
Normal file
42
internal/doc/main.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
//go:build generate
|
||||
// +build generate
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/thegeeklab/wp-gitea-release/plugin"
|
||||
plugin_docs "github.com/thegeeklab/wp-plugin-go/v3/docs"
|
||||
plugin_template "github.com/thegeeklab/wp-plugin-go/v3/template"
|
||||
)
|
||||
|
||||
func main() {
|
||||
tmpl := "https://raw.githubusercontent.com/thegeeklab/woodpecker-plugins/main/templates/docs-data.yaml.tmpl"
|
||||
client := http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
p := plugin.New(nil)
|
||||
|
||||
out, err := plugin_template.Render(context.Background(), client, tmpl, plugin_docs.GetTemplateData(p.App))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
outputFile := flag.String("output", "", "Output file path")
|
||||
flag.Parse()
|
||||
|
||||
if *outputFile == "" {
|
||||
panic("no output file specified")
|
||||
}
|
||||
|
||||
err = os.WriteFile(*outputFile, []byte(out), 0o644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
|
@ -4,13 +4,12 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
"github.com/thegeeklab/wp-plugin-go/file"
|
||||
"github.com/thegeeklab/wp-gitea-release/gitea"
|
||||
plugin_file "github.com/thegeeklab/wp-plugin-go/v3/file"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -54,13 +53,13 @@ func (p *Plugin) Validate() error {
|
|||
}
|
||||
|
||||
if p.Settings.Note != "" {
|
||||
if p.Settings.Note, _, err = file.ReadStringOrFile(p.Settings.Note); err != nil {
|
||||
if p.Settings.Note, _, err = plugin_file.ReadStringOrFile(p.Settings.Note); err != nil {
|
||||
return fmt.Errorf("error while reading %s: %w", p.Settings.Note, err)
|
||||
}
|
||||
}
|
||||
|
||||
if p.Settings.Title != "" {
|
||||
if p.Settings.Title, _, err = file.ReadStringOrFile(p.Settings.Title); err != nil {
|
||||
if p.Settings.Title, _, err = plugin_file.ReadStringOrFile(p.Settings.Title); err != nil {
|
||||
return fmt.Errorf("error while reading %s: %w", p.Settings.Title, err)
|
||||
}
|
||||
}
|
||||
|
@ -70,19 +69,12 @@ func (p *Plugin) Validate() error {
|
|||
|
||||
// Execute provides the implementation of the plugin.
|
||||
func (p *Plugin) Execute() error {
|
||||
httpClient := &http.Client{}
|
||||
|
||||
client, err := gitea.NewClient(
|
||||
p.Settings.baseURL.String(),
|
||||
gitea.SetToken(p.Settings.APIKey),
|
||||
gitea.SetHTTPClient(httpClient),
|
||||
)
|
||||
client, err := gitea.NewClient(p.Settings.baseURL.String(), p.Settings.APIKey, p.Network.Client)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to create Gitea client: %w", err)
|
||||
}
|
||||
|
||||
rc := releaseClient{
|
||||
Client: client,
|
||||
client.Release.Opt = gitea.ReleaseOptions{
|
||||
Owner: p.Metadata.Repository.Owner,
|
||||
Repo: p.Metadata.Repository.Name,
|
||||
Tag: strings.TrimPrefix(p.Settings.CommitRef, "refs/tags/"),
|
||||
|
@ -93,12 +85,20 @@ func (p *Plugin) Execute() error {
|
|||
Note: p.Settings.Note,
|
||||
}
|
||||
|
||||
release, err := rc.buildRelease()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create the release: %w", err)
|
||||
release, err := client.Release.Find()
|
||||
if err != nil && !errors.Is(err, gitea.ErrReleaseNotFound) {
|
||||
return fmt.Errorf("failed to retrieve release: %w", err)
|
||||
}
|
||||
|
||||
if err := rc.uploadFiles(release.ID, p.Settings.files); err != nil {
|
||||
// If no release was found by that tag, create a new one.
|
||||
if release == nil {
|
||||
release, err = client.Release.Create()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create release: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := client.Release.AddAttachments(release.ID, p.Settings.files); err != nil {
|
||||
return fmt.Errorf("failed to upload the files: %w", err)
|
||||
}
|
||||
|
||||
|
@ -136,8 +136,7 @@ func (p *Plugin) FlagsFromContext() error {
|
|||
if len(p.Settings.Checksum.Value()) > 0 {
|
||||
var err error
|
||||
|
||||
files, err = writeChecksums(files, p.Settings.Checksum.Value())
|
||||
|
||||
files, err = WriteChecksums(files, p.Settings.Checksum.Value(), "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write checksums: %w", err)
|
||||
}
|
||||
|
|
124
plugin/plugin.go
124
plugin/plugin.go
|
@ -1,15 +1,18 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
wp "github.com/thegeeklab/wp-plugin-go/plugin"
|
||||
plugin_base "github.com/thegeeklab/wp-plugin-go/v3/plugin"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
//go:generate go run ../internal/doc/main.go -output=../docs/data/data-raw.yaml
|
||||
|
||||
// Plugin implements provide the plugin.
|
||||
type Plugin struct {
|
||||
*wp.Plugin
|
||||
*plugin_base.Plugin
|
||||
Settings *Settings
|
||||
}
|
||||
|
||||
|
@ -29,13 +32,120 @@ type Settings struct {
|
|||
files []string
|
||||
}
|
||||
|
||||
func New(options wp.Options, settings *Settings) *Plugin {
|
||||
p := &Plugin{}
|
||||
func New(e plugin_base.ExecuteFunc, build ...string) *Plugin {
|
||||
p := &Plugin{
|
||||
Settings: &Settings{},
|
||||
}
|
||||
|
||||
options.Execute = p.run
|
||||
options := plugin_base.Options{
|
||||
Name: "wp-gitea-release",
|
||||
Description: "Publish files and artifacts to Gitea releases",
|
||||
Flags: Flags(p.Settings, plugin_base.FlagsPluginCategory),
|
||||
Execute: p.run,
|
||||
HideWoodpeckerFlags: true,
|
||||
}
|
||||
|
||||
p.Plugin = wp.New(options)
|
||||
p.Settings = settings
|
||||
if len(build) > 0 {
|
||||
options.Version = build[0]
|
||||
}
|
||||
|
||||
if len(build) > 1 {
|
||||
options.VersionMetadata = fmt.Sprintf("date=%s", build[1])
|
||||
}
|
||||
|
||||
if e != nil {
|
||||
options.Execute = e
|
||||
}
|
||||
|
||||
p.Plugin = plugin_base.New(options)
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// Flags returns a slice of CLI flags for the plugin.
|
||||
func Flags(settings *Settings, category string) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "api-key",
|
||||
Usage: "api key to access Gitea API",
|
||||
EnvVars: []string{"PLUGIN_API_KEY", "GITEA_RELEASE_API_KEY", "GITEA_TOKEN"},
|
||||
Destination: &settings.APIKey,
|
||||
Category: category,
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "files",
|
||||
Usage: "list of files to upload",
|
||||
EnvVars: []string{"PLUGIN_FILES", "GITEA_RELEASE_FILES"},
|
||||
Category: category,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "file-exists",
|
||||
Value: "overwrite",
|
||||
Usage: "what to do if file already exist",
|
||||
EnvVars: []string{"PLUGIN_FILE_EXIST", "GITEA_RELEASE_FILE_EXIST"},
|
||||
Destination: &settings.FileExists,
|
||||
Category: category,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "checksum",
|
||||
Usage: "generate specific checksums",
|
||||
EnvVars: []string{"PLUGIN_CHECKSUM", "GITEA_RELEASE_CHECKSUM"},
|
||||
Destination: &settings.Checksum,
|
||||
Category: category,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "draft",
|
||||
Usage: "create a draft release",
|
||||
EnvVars: []string{"PLUGIN_DRAFT", "GITEA_RELEASE_DRAFT"},
|
||||
Destination: &settings.Draft,
|
||||
Category: category,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "prerelease",
|
||||
Usage: "set the release as prerelease",
|
||||
EnvVars: []string{"PLUGIN_PRERELEASE", "GITEA_RELEASE_PRERELEASE"},
|
||||
Destination: &settings.PreRelease,
|
||||
Category: category,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "base-url",
|
||||
Usage: "URL of the Gitea instance",
|
||||
EnvVars: []string{"PLUGIN_BASE_URL", "GITEA_RELEASE_BASE_URL"},
|
||||
Category: category,
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "note",
|
||||
Usage: "file or string with notes for the release",
|
||||
EnvVars: []string{"PLUGIN_NOTE", "GITEA_RELEASE_NOTE"},
|
||||
Destination: &settings.Note,
|
||||
Category: category,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "title",
|
||||
Usage: "file or string for the title shown in the Gitea release",
|
||||
EnvVars: []string{"PLUGIN_TITLE", "GITEA_RELEASE_TITLE", "CI_COMMIT_TAG"},
|
||||
Destination: &settings.Title,
|
||||
DefaultText: "$CI_COMMIT_TAG",
|
||||
Category: category,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "event",
|
||||
Value: "push",
|
||||
Usage: "build event",
|
||||
EnvVars: []string{"CI_PIPELINE_EVENT"},
|
||||
Destination: &settings.Event,
|
||||
DefaultText: "$CI_PIPELINE_EVENT",
|
||||
Category: category,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "commit-ref",
|
||||
Value: "refs/heads/main",
|
||||
Usage: "git commit ref",
|
||||
EnvVars: []string{"CI_COMMIT_REF"},
|
||||
Destination: &settings.CommitRef,
|
||||
Category: category,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,144 +0,0 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrReleaseNotFound = errors.New("release not found")
|
||||
ErrFileExists = errors.New("asset file already exist")
|
||||
)
|
||||
|
||||
// Release holds ties the Woodpecker env data and gitea client together.
|
||||
type releaseClient struct {
|
||||
*gitea.Client
|
||||
Owner string
|
||||
Repo string
|
||||
Tag string
|
||||
Draft bool
|
||||
Prerelease bool
|
||||
FileExists string
|
||||
Title string
|
||||
Note string
|
||||
}
|
||||
|
||||
func (rc *releaseClient) buildRelease() (*gitea.Release, error) {
|
||||
// first attempt to get a release by that tag
|
||||
release, err := rc.getRelease()
|
||||
|
||||
if err != nil && release == nil {
|
||||
fmt.Println(err)
|
||||
} else if release != nil {
|
||||
return release, nil
|
||||
}
|
||||
|
||||
// if no release was found by that tag, create a new one
|
||||
release, err = rc.newRelease()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve or create a release: %w", err)
|
||||
}
|
||||
|
||||
return release, nil
|
||||
}
|
||||
|
||||
func (rc *releaseClient) getRelease() (*gitea.Release, error) {
|
||||
releases, _, err := rc.Client.ListReleases(rc.Owner, rc.Repo, gitea.ListReleasesOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, release := range releases {
|
||||
if release.TagName == rc.Tag {
|
||||
log.Info().Msgf("successfully retrieved %s release", rc.Tag)
|
||||
|
||||
return release, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%w: %s", ErrReleaseNotFound, rc.Tag)
|
||||
}
|
||||
|
||||
func (rc *releaseClient) newRelease() (*gitea.Release, error) {
|
||||
r := gitea.CreateReleaseOption{
|
||||
TagName: rc.Tag,
|
||||
IsDraft: rc.Draft,
|
||||
IsPrerelease: rc.Prerelease,
|
||||
Title: rc.Title,
|
||||
Note: rc.Note,
|
||||
}
|
||||
|
||||
release, _, err := rc.Client.CreateRelease(rc.Owner, rc.Repo, r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create release: %w", err)
|
||||
}
|
||||
|
||||
log.Info().Msgf("successfully created %s release", rc.Tag)
|
||||
|
||||
return release, nil
|
||||
}
|
||||
|
||||
func (rc *releaseClient) uploadFiles(releaseID int64, files []string) error {
|
||||
attachments, _, err := rc.Client.ListReleaseAttachments(
|
||||
rc.Owner,
|
||||
rc.Repo,
|
||||
releaseID,
|
||||
gitea.ListReleaseAttachmentsOptions{},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch existing assets: %w", err)
|
||||
}
|
||||
|
||||
var uploadFiles []string
|
||||
|
||||
files:
|
||||
for _, file := range files {
|
||||
for _, attachment := range attachments {
|
||||
if attachment.Name == path.Base(file) {
|
||||
switch rc.FileExists {
|
||||
case "overwrite":
|
||||
// do nothing
|
||||
case "fail":
|
||||
return fmt.Errorf("%w: %s", ErrFileExists, path.Base(file))
|
||||
case "skip":
|
||||
log.Warn().Msgf("skipping pre-existing %s artifact", attachment.Name)
|
||||
|
||||
continue files
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uploadFiles = append(uploadFiles, file)
|
||||
}
|
||||
|
||||
for _, file := range uploadFiles {
|
||||
handle, err := os.Open(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read %s artifact: %w", file, err)
|
||||
}
|
||||
|
||||
for _, attachment := range attachments {
|
||||
if attachment.Name == path.Base(file) {
|
||||
if _, err := rc.Client.DeleteReleaseAttachment(rc.Owner, rc.Repo, releaseID, attachment.ID); err != nil {
|
||||
return fmt.Errorf("failed to delete %s artifact: %w", file, err)
|
||||
}
|
||||
|
||||
log.Info().Msgf("successfully deleted old %s artifact", attachment.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if _, _, err = rc.Client.CreateReleaseAttachment(rc.Owner, rc.Repo, releaseID, handle, path.Base(file)); err != nil {
|
||||
return fmt.Errorf("failed to upload %s artifact: %w", file, err)
|
||||
}
|
||||
|
||||
log.Info().Msgf("successfully uploaded %s artifact", file)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -11,7 +11,7 @@ import (
|
|||
"hash/crc32"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/crypto/blake2b"
|
||||
"golang.org/x/crypto/blake2s"
|
||||
|
@ -19,7 +19,9 @@ import (
|
|||
|
||||
var ErrHashMethodNotSupported = errors.New("hash method not supported")
|
||||
|
||||
func checksum(r io.Reader, method string) (string, error) {
|
||||
// Checksum calculates the checksum of the given io.Reader using the specified hash method.
|
||||
// Supported hash methods are: "md5", "sha1", "sha256", "sha512", "adler32", "crc32", "blake2b", "blake2s".
|
||||
func Checksum(r io.Reader, method string) (string, error) {
|
||||
b, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -37,9 +39,9 @@ func checksum(r io.Reader, method string) (string, error) {
|
|||
case "sha512":
|
||||
return fmt.Sprintf("%x", sha512.Sum512(b)), nil
|
||||
case "adler32":
|
||||
return strconv.FormatUint(uint64(adler32.Checksum(b)), 10), nil
|
||||
return fmt.Sprintf("%08x", adler32.Checksum(b)), nil
|
||||
case "crc32":
|
||||
return strconv.FormatUint(uint64(crc32.ChecksumIEEE(b)), 10), nil
|
||||
return fmt.Sprintf("%08x", crc32.ChecksumIEEE(b)), nil
|
||||
case "blake2b":
|
||||
return fmt.Sprintf("%x", blake2b.Sum256(b)), nil
|
||||
case "blake2s":
|
||||
|
@ -49,44 +51,44 @@ func checksum(r io.Reader, method string) (string, error) {
|
|||
return "", fmt.Errorf("%w: %q", ErrHashMethodNotSupported, method)
|
||||
}
|
||||
|
||||
func writeChecksums(files, methods []string) ([]string, error) {
|
||||
checksums := make(map[string][]string)
|
||||
// WriteChecksums calculates the checksums for the given files using the specified hash methods,
|
||||
// and writes the checksums to files named after the hash methods (e.g. "md5sum.txt", "sha256sum.txt").
|
||||
func WriteChecksums(files, methods []string, outDir string) ([]string, error) {
|
||||
if len(files) == 0 {
|
||||
return files, nil
|
||||
}
|
||||
|
||||
checksumFiles := make([]string, 0)
|
||||
|
||||
for _, method := range methods {
|
||||
checksumFile := filepath.Join(outDir, method+"sum.txt")
|
||||
|
||||
f, err := os.Create(checksumFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
for _, file := range files {
|
||||
handle, err := os.Open(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read %q artifact: %w", file, err)
|
||||
}
|
||||
defer handle.Close()
|
||||
|
||||
hash, err := checksum(handle, method)
|
||||
hash, err := Checksum(handle, method)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not checksum %q file: %w", file, err)
|
||||
}
|
||||
|
||||
checksums[method] = append(checksums[method], hash, file)
|
||||
}
|
||||
}
|
||||
|
||||
for method, results := range checksums {
|
||||
filename := method + "sum.txt"
|
||||
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := 0; i < len(results); i += 2 {
|
||||
hash := results[i]
|
||||
file := results[i+1]
|
||||
|
||||
if _, err := f.WriteString(fmt.Sprintf("%s %s\n", hash, file)); err != nil {
|
||||
_, err = f.WriteString(fmt.Sprintf("%s %s\n", hash, file))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
files = append(files, filename)
|
||||
checksumFiles = append(checksumFiles, checksumFile)
|
||||
}
|
||||
|
||||
return files, nil
|
||||
return append(files, checksumFiles...), nil
|
||||
}
|
204
plugin/util_test.go
Normal file
204
plugin/util_test.go
Normal file
|
@ -0,0 +1,204 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
//nolint:lll
|
||||
func TestChecksum(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
method string
|
||||
input []byte
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "md5",
|
||||
method: "md5",
|
||||
input: []byte("hello"),
|
||||
want: "5d41402abc4b2a76b9719d911017c592",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "sha1",
|
||||
method: "sha1",
|
||||
input: []byte("hello"),
|
||||
want: "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "sha256",
|
||||
method: "sha256",
|
||||
input: []byte("hello"),
|
||||
want: "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "sha512",
|
||||
method: "sha512",
|
||||
input: []byte("hello"),
|
||||
want: "9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "adler32",
|
||||
method: "adler32",
|
||||
input: []byte("hello"),
|
||||
want: "062c0215",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "crc32",
|
||||
method: "crc32",
|
||||
input: []byte("hello"),
|
||||
want: "3610a686",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "blake2b",
|
||||
method: "blake2b",
|
||||
input: []byte("hello"),
|
||||
want: "324dcf027dd4a30a932c441f365a25e86b173defa4b8e58948253471b81b72cf",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "blake2s",
|
||||
method: "blake2s",
|
||||
input: []byte("hello"),
|
||||
want: "19213bacc58dee6dbde3ceb9a47cbb330b3d86f8cca8997eb00be456f140ca25",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "unsupported_method",
|
||||
method: "unsupported",
|
||||
input: []byte("hello"),
|
||||
want: "",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := bytes.NewReader(tt.input)
|
||||
|
||||
sum, err := Checksum(r, tt.method)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.want, sum)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkChecksum(b *testing.B) {
|
||||
input := []byte("hello")
|
||||
methods := []string{"md5", "sha1", "sha256", "sha512", "adler32", "crc32", "blake2b", "blake2s"}
|
||||
|
||||
for _, method := range methods {
|
||||
b.Run(method, func(b *testing.B) {
|
||||
r := bytes.NewReader(input)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = Checksum(r, method)
|
||||
_, _ = r.Seek(0, io.SeekStart)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteChecksums(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
files := []string{
|
||||
filepath.Join(tempDir, "file1.txt"),
|
||||
filepath.Join(tempDir, "file2.txt"),
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
err := os.WriteFile(file, []byte("hello"), 0o600)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
methods := []string{"md5", "sha256"}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
tempDir string
|
||||
files []string
|
||||
methods []string
|
||||
want []string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid input",
|
||||
tempDir: tempDir,
|
||||
files: files,
|
||||
methods: methods,
|
||||
want: []string{
|
||||
files[0],
|
||||
files[1],
|
||||
filepath.Join(tempDir, "md5sum.txt"),
|
||||
filepath.Join(tempDir, "sha256sum.txt"),
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "empty files",
|
||||
tempDir: tempDir,
|
||||
files: []string{},
|
||||
methods: methods,
|
||||
want: []string{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "empty methods",
|
||||
tempDir: tempDir,
|
||||
files: files,
|
||||
methods: []string{},
|
||||
want: []string{
|
||||
files[0],
|
||||
files[1],
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "non existent file",
|
||||
tempDir: tempDir,
|
||||
files: append(files, filepath.Join(tempDir, "non_existent.txt")),
|
||||
methods: methods,
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := WriteChecksums(tt.files, tt.methods, tt.tempDir)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
sort.Strings(result)
|
||||
sort.Strings(tt.want)
|
||||
assert.Equal(t, tt.want, result)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user