mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-24 08:02:18 +02:00
Rework addons (use rpc) (#3268)
Co-authored-by: Anbraten <6918444+anbraten@users.noreply.github.com>
This commit is contained in:
parent
b177d82064
commit
00f0fcd416
@ -114,7 +114,7 @@ func lintFile(_ *cli.Context, file string) error {
|
||||
hasErrors = true
|
||||
}
|
||||
|
||||
if data := err.GetLinterData(); data != nil {
|
||||
if data := pipeline_errors.GetLinterData(err); data != nil {
|
||||
line = fmt.Sprintf("%s %s\t%s", line, output.String(data.Field).Bold(), err.Message)
|
||||
} else {
|
||||
line = fmt.Sprintf("%s %s", line, err.Message)
|
||||
|
@ -3969,7 +3969,7 @@ const docTemplate = `{
|
||||
"errors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/errors.PipelineError"
|
||||
"$ref": "#/definitions/types.PipelineError"
|
||||
}
|
||||
},
|
||||
"event": {
|
||||
@ -4455,45 +4455,6 @@ const docTemplate = `{
|
||||
"EventManual"
|
||||
]
|
||||
},
|
||||
"errors.PipelineError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {},
|
||||
"is_warning": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"$ref": "#/definitions/errors.PipelineErrorType"
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors.PipelineErrorType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"linter",
|
||||
"deprecation",
|
||||
"compiler",
|
||||
"generic",
|
||||
"bad_habit"
|
||||
],
|
||||
"x-enum-comments": {
|
||||
"PipelineErrorTypeBadHabit": "some bad-habit error",
|
||||
"PipelineErrorTypeCompiler": "some error with the config semantics",
|
||||
"PipelineErrorTypeDeprecation": "using some deprecated feature",
|
||||
"PipelineErrorTypeGeneric": "some generic error",
|
||||
"PipelineErrorTypeLinter": "some error with the config syntax"
|
||||
},
|
||||
"x-enum-varnames": [
|
||||
"PipelineErrorTypeLinter",
|
||||
"PipelineErrorTypeDeprecation",
|
||||
"PipelineErrorTypeCompiler",
|
||||
"PipelineErrorTypeGeneric",
|
||||
"PipelineErrorTypeBadHabit"
|
||||
]
|
||||
},
|
||||
"model.Workflow": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -4540,6 +4501,45 @@ const docTemplate = `{
|
||||
"$ref": "#/definitions/StatusValue"
|
||||
}
|
||||
}
|
||||
},
|
||||
"types.PipelineError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {},
|
||||
"is_warning": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"$ref": "#/definitions/types.PipelineErrorType"
|
||||
}
|
||||
}
|
||||
},
|
||||
"types.PipelineErrorType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"linter",
|
||||
"deprecation",
|
||||
"compiler",
|
||||
"generic",
|
||||
"bad_habit"
|
||||
],
|
||||
"x-enum-comments": {
|
||||
"PipelineErrorTypeBadHabit": "some bad-habit error",
|
||||
"PipelineErrorTypeCompiler": "some error with the config semantics",
|
||||
"PipelineErrorTypeDeprecation": "using some deprecated feature",
|
||||
"PipelineErrorTypeGeneric": "some generic error",
|
||||
"PipelineErrorTypeLinter": "some error with the config syntax"
|
||||
},
|
||||
"x-enum-varnames": [
|
||||
"PipelineErrorTypeLinter",
|
||||
"PipelineErrorTypeDeprecation",
|
||||
"PipelineErrorTypeCompiler",
|
||||
"PipelineErrorTypeGeneric",
|
||||
"PipelineErrorTypeBadHabit"
|
||||
]
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
@ -246,10 +246,10 @@ var flags = append([]cli.Flag{
|
||||
Usage: "Disable version check in admin web ui.",
|
||||
Name: "skip-version-check",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
EnvVars: []string{"WOODPECKER_ADDONS"},
|
||||
Name: "addons",
|
||||
Usage: "list of addon files",
|
||||
&cli.StringFlag{
|
||||
EnvVars: []string{"WOODPECKER_ADDON_FORGE"},
|
||||
Name: "addon-forge",
|
||||
Usage: "forge addon",
|
||||
},
|
||||
//
|
||||
// backend options for pipeline compiler
|
||||
|
@ -32,6 +32,7 @@ import (
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/cache"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge/addon"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge/bitbucket"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge/bitbucketdatacenter"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge/gitea"
|
||||
@ -40,8 +41,6 @@ import (
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/queue"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/store"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/store/datastore"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/shared/addon"
|
||||
addonTypes "go.woodpecker-ci.org/woodpecker/v2/shared/addon/types"
|
||||
)
|
||||
|
||||
func setupStore(c *cli.Context) (store.Store, error) {
|
||||
@ -107,15 +106,9 @@ func setupMembershipService(_ *cli.Context, r forge.Forge) cache.MembershipServi
|
||||
|
||||
// setupForge helper function to set up the forge from the CLI arguments.
|
||||
func setupForge(c *cli.Context) (forge.Forge, error) {
|
||||
addonForge, err := addon.Load[forge.Forge](c.StringSlice("addons"), addonTypes.TypeForge)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addonForge != nil {
|
||||
return addonForge.Value, nil
|
||||
}
|
||||
|
||||
switch {
|
||||
case c.String("addon-forge") != "":
|
||||
return addon.Load(c.String("addon-forge"))
|
||||
case c.Bool("github"):
|
||||
return setupGitHub(c)
|
||||
case c.Bool("gitlab"):
|
||||
|
@ -135,5 +135,5 @@ docker run --rm \
|
||||
These should also be built for different OS/architectures.
|
||||
- Use [built-in env vars](../50-environment.md#built-in-environment-variables) where possible.
|
||||
- Do not use any configuration except settings (and internal env vars). This means: Don't require using [`environment`](../50-environment.md) and don't require specific secret names.
|
||||
- Add a `docs.md` file, listing all your settings and plugin metadata ([example](https://codeberg.org/woodpecker-plugins/plugin-docker-buildx/src/branch/main/docs.md)).
|
||||
- Add your plugin to the [plugin index](/plugins) using your `docs.md` ([the example above in the index](https://woodpecker-ci.org/plugins/Docker%20Buildx)).
|
||||
- Add a `docs.md` file, listing all your settings and plugin metadata ([example](https://github.com/woodpecker-ci/plugin-git/blob/main/docs.md)).
|
||||
- Add your plugin to the [plugin index](/plugins) using your `docs.md` ([the example above in the index](https://woodpecker-ci.org/plugins/Git%20Clone)).
|
||||
|
@ -473,12 +473,6 @@ Supported variables:
|
||||
- `owner`: the repo's owner
|
||||
- `repo`: the repo's name
|
||||
|
||||
### `WOODPECKER_ADDONS`
|
||||
|
||||
> Default: empty
|
||||
|
||||
List of addon files. See [addons](./75-addons/75-overview.md).
|
||||
|
||||
---
|
||||
|
||||
### `WOODPECKER_LIMIT_MEM_SWAP`
|
||||
@ -559,4 +553,8 @@ See [Bitbucket configuration](./11-forges/50-bitbucket.md#configuration)
|
||||
|
||||
### `WOODPECKER_GITLAB_...`
|
||||
|
||||
See [Gitlab configuration](./11-forges/40-gitlab.md#configuration)
|
||||
See [GitLab configuration](./11-forges/40-gitlab.md#configuration)
|
||||
|
||||
### `WOODPECKER_ADDON_FORGE`
|
||||
|
||||
See [addon forges](./11-forges/100-addon.md).
|
||||
|
68
docs/docs/30-administration/11-forges/100-addon.md
Normal file
68
docs/docs/30-administration/11-forges/100-addon.md
Normal file
@ -0,0 +1,68 @@
|
||||
# Addon forges
|
||||
|
||||
If the forge you're using does not comply with [Woodpecker's requirements](../../92-development/02-core-ideas.md#forge) or your setup is too specific to be added to Woodpecker's core, you can write your own forge using an addon forge.
|
||||
|
||||
:::warning
|
||||
Addon forges are still experimental. Their implementation can change and break at any time.
|
||||
:::
|
||||
|
||||
:::danger
|
||||
You need to trust the author of the addon forge you use. It can access authentication codes and other possibly sensitive information.
|
||||
:::
|
||||
|
||||
## Usage
|
||||
|
||||
To use an addon forge, download the correct addon version. Then, you can add the following to your configuration:
|
||||
|
||||
```ini
|
||||
WOODPECKER_ADDON_FORGE=/path/to/your/addon/forge/file
|
||||
```
|
||||
|
||||
In case you run Woodpecker as container, you probably want to mount the addon binary to `/opt/addons/`.
|
||||
|
||||
### Bug reports
|
||||
|
||||
If you experience bugs, please check which component has the issue. If it's the addon, **do not raise an issue in the main repository**, but rather use the separate addon repositories. To check which component is responsible for the bug, look at the logs. Logs from addons are marked with a special field `addon` containing their addon file name.
|
||||
|
||||
## List of addon forges
|
||||
|
||||
If you wrote or found an addon forge, please add it here so others can find it!
|
||||
|
||||
_Be the first one to add your addon forge!_
|
||||
|
||||
## Creating addon forges
|
||||
|
||||
Addons use RPC to communicate to the server and are implemented using the [`go-plugin` library](https://github.com/hashicorp/go-plugin).
|
||||
|
||||
### Writing your code
|
||||
|
||||
This example will use the Go language.
|
||||
|
||||
Directly import Woodpecker's Go packages (`go.woodpecker-ci.org/woodpecker/woodpecker/v2`) and use the interfaces and types defined there.
|
||||
|
||||
In the `main` function, just call `"go.woodpecker-ci.org/woodpecker/v2/server/forge/addon".Serve` with a `"go.woodpecker-ci.org/woodpecker/v2/server/forge".Forge` as argument.
|
||||
This will take care of connecting the addon forge to the server.
|
||||
|
||||
### Example structure
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge/addon"
|
||||
forgeTypes "go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||
)
|
||||
|
||||
func main() {
|
||||
addon.Serve(config{})
|
||||
}
|
||||
|
||||
type config struct {
|
||||
}
|
||||
|
||||
// `config` must implement `"go.woodpecker-ci.org/woodpecker/v2/server/forge".Forge`. You must directly use Woodpecker's packages - see imports above.
|
||||
```
|
@ -1,97 +0,0 @@
|
||||
# Creating addons
|
||||
|
||||
Addons are written in Go.
|
||||
|
||||
## Writing your code
|
||||
|
||||
An addon consists of two variables/functions in Go.
|
||||
|
||||
1. The `Type` variable. Specifies the type of the addon and must be directly accessed from `shared/addons/types/types.go`.
|
||||
2. The `Addon` function which is the main point of your addon.
|
||||
This function takes the `zerolog` logger you should use to log errors, warnings, etc. as argument.
|
||||
|
||||
It returns two values:
|
||||
|
||||
1. The actual addon. For type reference see [table below](#return-types).
|
||||
2. An error. If this error is not `nil`, Woodpecker exits.
|
||||
|
||||
Directly import Woodpecker's Go package (`go.woodpecker-ci.org/woodpecker/woodpecker/v2`) and use the interfaces and types defined there.
|
||||
|
||||
### Return types
|
||||
|
||||
| Addon type | Return type |
|
||||
| ---------- | -------------------------------------------------------------------- |
|
||||
| `Forge` | `"go.woodpecker-ci.org/woodpecker/woodpecker/v2/server/forge".Forge` |
|
||||
|
||||
### Using configurations
|
||||
|
||||
If you write a plugin for the server (`Forge` and the services), you can access the server config.
|
||||
|
||||
Therefore, use the `"go.woodpecker-ci.org/woodpecker/v2/server".Config` variable.
|
||||
|
||||
:::warning
|
||||
The config is not available when your addon is initialized, i.e., the `Addon` function is called.
|
||||
Only use the config in the interface methods.
|
||||
:::
|
||||
|
||||
## Compiling
|
||||
|
||||
After you write your addon code, compile your addon:
|
||||
|
||||
```sh
|
||||
go build -buildmode plugin
|
||||
```
|
||||
|
||||
The output file is your addon that is now ready to be used.
|
||||
|
||||
## Restrictions
|
||||
|
||||
Addons must directly depend on Woodpecker's core (`go.woodpecker-ci.org/woodpecker/woodpecker/v2`).
|
||||
The addon must have been built with **exactly the same code** as the Woodpecker instance you'd like to use it on. This means: If you build your addon with a specific commit from Woodpecker `next`, you can likely only use it with the Woodpecker version compiled from this commit.
|
||||
Also, if you change something inside Woodpecker without committing, it might fail because you need to recompile your addon with this code first.
|
||||
|
||||
In addition to this, addons are only supported on Linux, FreeBSD, and macOS.
|
||||
|
||||
:::info
|
||||
It is recommended to at least support the latest version of Woodpecker.
|
||||
:::
|
||||
|
||||
### Compile for different versions
|
||||
|
||||
As long as there are no changes to Woodpecker's interfaces,
|
||||
or they are backwards-compatible, you can compile the addon for multiple versions
|
||||
by changing the version of `go.woodpecker-ci.org/woodpecker/woodpecker/v2` using `go get` before compiling.
|
||||
|
||||
## Logging
|
||||
|
||||
The entrypoint receives a `zerolog.Logger` as input. **Do not use any other logging solution.** This logger follows the configuration of the Woodpecker instance and adds a special field `addon` to the log entries which allows users to find out which component is writing the log messages.
|
||||
|
||||
## Example structure
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
||||
forge_types "go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||
addon_types "go.woodpecker-ci.org/woodpecker/v2/shared/addon/types"
|
||||
)
|
||||
|
||||
var Type = addon_types.TypeForge
|
||||
|
||||
func Addon(logger zerolog.Logger) (forge.Forge, error) {
|
||||
logger.Info().Msg("hello world from addon")
|
||||
return &config{l: logger}, nil
|
||||
}
|
||||
|
||||
type config struct {
|
||||
l zerolog.Logger
|
||||
}
|
||||
|
||||
// In this case, `config` must implement `forge.Forge`. You must directly use Woodpecker's packages - see imports above.
|
||||
```
|
@ -1,40 +0,0 @@
|
||||
# Addons
|
||||
|
||||
:::warning
|
||||
Addons are still experimental. Their implementation can change and break at any time.
|
||||
:::
|
||||
|
||||
:::danger
|
||||
You need to trust the author of the addons you use. Depending on their type, addons can access forge authentication codes, your secrets or other sensitive information.
|
||||
:::
|
||||
|
||||
To adapt Woodpecker to your needs beyond the [configuration](../10-server-config.md), Woodpecker has its own **addon** system, built ontop of [Go's internal plugin system](https://go.dev/pkg/plugin).
|
||||
|
||||
Addons can be used for:
|
||||
|
||||
- Forges
|
||||
|
||||
## Restrictions
|
||||
|
||||
Addons are restricted by how Go plugins work. This includes the following restrictions:
|
||||
|
||||
- only supported on Linux, FreeBSD, and macOS
|
||||
- addons must have been built for the correct Woodpecker version. If an addon is not provided specifically for this version, you likely won't be able to use it.
|
||||
|
||||
## Usage
|
||||
|
||||
To use an addon, download the addon version built for your Woodpecker version. Then, you can add the following to your configuration:
|
||||
|
||||
```ini
|
||||
WOODPECKER_ADDONS=/path/to/your/addon/file.so
|
||||
```
|
||||
|
||||
In case you run Woodpecker as container, you probably want to mount the addon binaries to `/opt/addons/`.
|
||||
|
||||
You can list multiple addons, Woodpecker will automatically determine their type. If you specify multiple addons with the same type, only the first one will be used.
|
||||
|
||||
Using an addon always overwrites Woodpecker's internal setup. This means, that a forge addon will be used if specified, no matter what's configured for the forges natively supported by Woodpecker.
|
||||
|
||||
### Bug reports
|
||||
|
||||
If you experience bugs, please check which component has the issue. If it's the addon, **do not raise an issue in the main repository**, but rather use the separate addon repositories. To check which component is responsible for the bug, look at the logs. Logs from addons are marked with a special field `addon` containing their addon file name.
|
@ -1,6 +0,0 @@
|
||||
label: 'Addons'
|
||||
collapsible: true
|
||||
collapsed: true
|
||||
link:
|
||||
type: 'doc'
|
||||
id: 'overview'
|
@ -8,7 +8,7 @@
|
||||
## Addons and extensions
|
||||
|
||||
If you are wondering whether your contribution will be accepted to be merged in the Woodpecker core, or whether it's better to write an
|
||||
[addon](../30-administration/75-addons/75-overview.md), [extension](../30-administration/100-external-configuration-api.md) or an
|
||||
[addon forge](../30-administration/11-forges/100-addon.md), [extension](../30-administration/100-external-configuration-api.md) or an
|
||||
[external custom backend](../30-administration/22-backends/50-custom-backends.md), please check these points:
|
||||
|
||||
- Is your change very specific to your setup and unlikely to be used by anyone else?
|
||||
|
6
go.mod
6
go.mod
@ -30,6 +30,8 @@ require (
|
||||
github.com/google/go-github/v61 v61.0.0
|
||||
github.com/google/tink/go v1.7.0
|
||||
github.com/gorilla/securecookie v1.1.2
|
||||
github.com/hashicorp/go-hclog v1.2.0
|
||||
github.com/hashicorp/go-plugin v1.4.3
|
||||
github.com/jellydator/ttlcache/v3 v3.2.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/kinbiko/jsonassert v1.1.1
|
||||
@ -119,9 +121,9 @@ require (
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-hclog v1.2.0 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
|
||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
@ -137,6 +139,7 @@ require (
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mholt/acmez v1.2.0 // indirect
|
||||
github.com/miekg/dns v1.1.57 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
@ -144,6 +147,7 @@ require (
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/oklog/run v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||
|
48
go.sum
48
go.sum
@ -1,3 +1,4 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
code.gitea.io/sdk/gitea v0.17.1 h1:3jCPOG2ojbl8AcfaUCRYLT5MUcBMFwS0OSK2mA5Zok8=
|
||||
code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM=
|
||||
codeberg.org/6543/go-yaml2json v1.0.0 h1:heGqo9VEi7gY2yNqjj7X4ADs5nzlFIbGsJtgYDLrnig=
|
||||
@ -44,6 +45,7 @@ github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA=
|
||||
github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
|
||||
@ -66,6 +68,7 @@ github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLI
|
||||
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
|
||||
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
|
||||
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
@ -108,6 +111,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/expr-lang/expr v1.16.3 h1:NLldf786GffptcXNxxJx5dQ+FzeWDKChBDqOOwyK8to=
|
||||
github.com/expr-lang/expr v1.16.3/go.mod h1:uCkhfG+x7fcZ5A5sXHKuQ07jGZRl6J0FCAaf2k4PtVQ=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
@ -173,7 +178,12 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0kt
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
@ -211,12 +221,17 @@ github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pw
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
|
||||
github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM=
|
||||
github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||
@ -263,6 +278,8 @@ github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv
|
||||
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jellydator/ttlcache/v3 v3.2.0 h1:6lqVJ8X3ZaUwvzENqPAobDsXNExfUJd61u++uW8a3LE=
|
||||
github.com/jellydator/ttlcache/v3 v3.2.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4=
|
||||
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
|
||||
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
@ -337,6 +354,9 @@ github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
|
||||
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
|
||||
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
|
||||
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/moby v24.0.9+incompatible h1:Z/hFbZJqC5Fmuf6jesMLdHU71CMAgdiSJ1ZYey+bFmg=
|
||||
@ -363,6 +383,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/neticdk/go-bitbucket v1.0.0 h1:FPvHEgPHoDwD2VHbpyu2R2gnoWQ867RxZd2FivS4wSw=
|
||||
github.com/neticdk/go-bitbucket v1.0.0/go.mod h1:IrHeWO1CrNi0DlOvfhAA9bGRSeNSUB6/SAfzmwbA5aU=
|
||||
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
|
||||
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@ -388,6 +410,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||
@ -519,6 +542,10 @@ golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
@ -528,7 +555,11 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
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=
|
||||
@ -544,9 +575,11 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
|
||||
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -554,6 +587,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -609,8 +643,11 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@ -631,10 +668,19 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
|
||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
@ -664,6 +710,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
||||
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw=
|
||||
k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80=
|
||||
|
@ -2,28 +2,12 @@ package errors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"go.uber.org/multierr"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors/types"
|
||||
)
|
||||
|
||||
type PipelineErrorType string
|
||||
|
||||
const (
|
||||
PipelineErrorTypeLinter PipelineErrorType = "linter" // some error with the config syntax
|
||||
PipelineErrorTypeDeprecation PipelineErrorType = "deprecation" // using some deprecated feature
|
||||
PipelineErrorTypeCompiler PipelineErrorType = "compiler" // some error with the config semantics
|
||||
PipelineErrorTypeGeneric PipelineErrorType = "generic" // some generic error
|
||||
PipelineErrorTypeBadHabit PipelineErrorType = "bad_habit" // some bad-habit error
|
||||
)
|
||||
|
||||
type PipelineError struct {
|
||||
Type PipelineErrorType `json:"type"`
|
||||
Message string `json:"message"`
|
||||
IsWarning bool `json:"is_warning"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
type LinterErrorData struct {
|
||||
File string `json:"file"`
|
||||
Field string `json:"field"`
|
||||
@ -35,12 +19,8 @@ type DeprecationErrorData struct {
|
||||
Docs string `json:"docs"`
|
||||
}
|
||||
|
||||
func (e *PipelineError) Error() string {
|
||||
return fmt.Sprintf("[%s] %s", e.Type, e.Message)
|
||||
}
|
||||
|
||||
func (e *PipelineError) GetLinterData() *LinterErrorData {
|
||||
if e.Type != PipelineErrorTypeLinter {
|
||||
func GetLinterData(e *types.PipelineError) *LinterErrorData {
|
||||
if e.Type != types.PipelineErrorTypeLinter {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -51,16 +31,16 @@ func (e *PipelineError) GetLinterData() *LinterErrorData {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetPipelineErrors(err error) []*PipelineError {
|
||||
var pipelineErrors []*PipelineError
|
||||
func GetPipelineErrors(err error) []*types.PipelineError {
|
||||
var pipelineErrors []*types.PipelineError
|
||||
for _, _err := range multierr.Errors(err) {
|
||||
var err *PipelineError
|
||||
var err *types.PipelineError
|
||||
if errors.As(_err, &err) {
|
||||
pipelineErrors = append(pipelineErrors, err)
|
||||
} else {
|
||||
pipelineErrors = append(pipelineErrors, &PipelineError{
|
||||
pipelineErrors = append(pipelineErrors, &types.PipelineError{
|
||||
Message: _err.Error(),
|
||||
Type: PipelineErrorTypeGeneric,
|
||||
Type: types.PipelineErrorTypeGeneric,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"go.uber.org/multierr"
|
||||
|
||||
pipeline_errors "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors/types"
|
||||
)
|
||||
|
||||
func TestGetPipelineErrors(t *testing.T) {
|
||||
@ -16,7 +17,7 @@ func TestGetPipelineErrors(t *testing.T) {
|
||||
tests := []struct {
|
||||
title string
|
||||
err error
|
||||
expected []*pipeline_errors.PipelineError
|
||||
expected []*types.PipelineError
|
||||
}{
|
||||
{
|
||||
title: "nil error",
|
||||
@ -25,10 +26,10 @@ func TestGetPipelineErrors(t *testing.T) {
|
||||
},
|
||||
{
|
||||
title: "warning",
|
||||
err: &pipeline_errors.PipelineError{
|
||||
err: &types.PipelineError{
|
||||
IsWarning: true,
|
||||
},
|
||||
expected: []*pipeline_errors.PipelineError{
|
||||
expected: []*types.PipelineError{
|
||||
{
|
||||
IsWarning: true,
|
||||
},
|
||||
@ -36,10 +37,10 @@ func TestGetPipelineErrors(t *testing.T) {
|
||||
},
|
||||
{
|
||||
title: "pipeline error",
|
||||
err: &pipeline_errors.PipelineError{
|
||||
err: &types.PipelineError{
|
||||
IsWarning: false,
|
||||
},
|
||||
expected: []*pipeline_errors.PipelineError{
|
||||
expected: []*types.PipelineError{
|
||||
{
|
||||
IsWarning: false,
|
||||
},
|
||||
@ -48,14 +49,14 @@ func TestGetPipelineErrors(t *testing.T) {
|
||||
{
|
||||
title: "multiple warnings",
|
||||
err: multierr.Combine(
|
||||
&pipeline_errors.PipelineError{
|
||||
&types.PipelineError{
|
||||
IsWarning: true,
|
||||
},
|
||||
&pipeline_errors.PipelineError{
|
||||
&types.PipelineError{
|
||||
IsWarning: true,
|
||||
},
|
||||
),
|
||||
expected: []*pipeline_errors.PipelineError{
|
||||
expected: []*types.PipelineError{
|
||||
{
|
||||
IsWarning: true,
|
||||
},
|
||||
@ -67,15 +68,15 @@ func TestGetPipelineErrors(t *testing.T) {
|
||||
{
|
||||
title: "multiple errors and warnings",
|
||||
err: multierr.Combine(
|
||||
&pipeline_errors.PipelineError{
|
||||
&types.PipelineError{
|
||||
IsWarning: true,
|
||||
},
|
||||
&pipeline_errors.PipelineError{
|
||||
&types.PipelineError{
|
||||
IsWarning: false,
|
||||
},
|
||||
errors.New("some error"),
|
||||
),
|
||||
expected: []*pipeline_errors.PipelineError{
|
||||
expected: []*types.PipelineError{
|
||||
{
|
||||
IsWarning: true,
|
||||
},
|
||||
@ -83,7 +84,7 @@ func TestGetPipelineErrors(t *testing.T) {
|
||||
IsWarning: false,
|
||||
},
|
||||
{
|
||||
Type: pipeline_errors.PipelineErrorTypeGeneric,
|
||||
Type: types.PipelineErrorTypeGeneric,
|
||||
IsWarning: false,
|
||||
Message: "some error",
|
||||
},
|
||||
@ -111,14 +112,14 @@ func TestHasBlockingErrors(t *testing.T) {
|
||||
},
|
||||
{
|
||||
title: "warning",
|
||||
err: &pipeline_errors.PipelineError{
|
||||
err: &types.PipelineError{
|
||||
IsWarning: true,
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
title: "pipeline error",
|
||||
err: &pipeline_errors.PipelineError{
|
||||
err: &types.PipelineError{
|
||||
IsWarning: false,
|
||||
},
|
||||
expected: true,
|
||||
@ -126,10 +127,10 @@ func TestHasBlockingErrors(t *testing.T) {
|
||||
{
|
||||
title: "multiple warnings",
|
||||
err: multierr.Combine(
|
||||
&pipeline_errors.PipelineError{
|
||||
&types.PipelineError{
|
||||
IsWarning: true,
|
||||
},
|
||||
&pipeline_errors.PipelineError{
|
||||
&types.PipelineError{
|
||||
IsWarning: true,
|
||||
},
|
||||
),
|
||||
@ -138,10 +139,10 @@ func TestHasBlockingErrors(t *testing.T) {
|
||||
{
|
||||
title: "multiple errors and warnings",
|
||||
err: multierr.Combine(
|
||||
&pipeline_errors.PipelineError{
|
||||
&types.PipelineError{
|
||||
IsWarning: true,
|
||||
},
|
||||
&pipeline_errors.PipelineError{
|
||||
&types.PipelineError{
|
||||
IsWarning: false,
|
||||
},
|
||||
errors.New("some error"),
|
||||
|
24
pipeline/errors/types/errors.go
Normal file
24
pipeline/errors/types/errors.go
Normal file
@ -0,0 +1,24 @@
|
||||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
type PipelineErrorType string
|
||||
|
||||
const (
|
||||
PipelineErrorTypeLinter PipelineErrorType = "linter" // some error with the config syntax
|
||||
PipelineErrorTypeDeprecation PipelineErrorType = "deprecation" // using some deprecated feature
|
||||
PipelineErrorTypeCompiler PipelineErrorType = "compiler" // some error with the config semantics
|
||||
PipelineErrorTypeGeneric PipelineErrorType = "generic" // some generic error
|
||||
PipelineErrorTypeBadHabit PipelineErrorType = "bad_habit" // some bad-habit error
|
||||
)
|
||||
|
||||
type PipelineError struct {
|
||||
Type PipelineErrorType `json:"type"`
|
||||
Message string `json:"message"`
|
||||
IsWarning bool `json:"is_warning"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
func (e *PipelineError) Error() string {
|
||||
return fmt.Sprintf("[%s] %s", e.Type, e.Message)
|
||||
}
|
@ -16,11 +16,12 @@ package linter
|
||||
|
||||
import (
|
||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
||||
errorTypes "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors/types"
|
||||
)
|
||||
|
||||
func newLinterError(message, file, field string, isWarning bool) *errors.PipelineError {
|
||||
return &errors.PipelineError{
|
||||
Type: errors.PipelineErrorTypeLinter,
|
||||
func newLinterError(message, file, field string, isWarning bool) *errorTypes.PipelineError {
|
||||
return &errorTypes.PipelineError{
|
||||
Type: errorTypes.PipelineErrorTypeLinter,
|
||||
Message: message,
|
||||
Data: &errors.LinterErrorData{File: file, Field: field},
|
||||
IsWarning: isWarning,
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"go.uber.org/multierr"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
||||
errorTypes "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors/types"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/linter/schema"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/types"
|
||||
)
|
||||
@ -210,8 +211,8 @@ func (l *Linter) lintDeprecations(config *WorkflowConfig) (err error) {
|
||||
}
|
||||
|
||||
if parsed.PipelineDoNotUseIt.ContainerList != nil {
|
||||
err = multierr.Append(err, &errors.PipelineError{
|
||||
Type: errors.PipelineErrorTypeDeprecation,
|
||||
err = multierr.Append(err, &errorTypes.PipelineError{
|
||||
Type: errorTypes.PipelineErrorTypeDeprecation,
|
||||
Message: "Please use 'steps:' instead of deprecated 'pipeline:' list",
|
||||
Data: errors.DeprecationErrorData{
|
||||
File: config.File,
|
||||
@ -223,8 +224,8 @@ func (l *Linter) lintDeprecations(config *WorkflowConfig) (err error) {
|
||||
}
|
||||
|
||||
if parsed.PlatformDoNotUseIt != "" {
|
||||
err = multierr.Append(err, &errors.PipelineError{
|
||||
Type: errors.PipelineErrorTypeDeprecation,
|
||||
err = multierr.Append(err, &errorTypes.PipelineError{
|
||||
Type: errorTypes.PipelineErrorTypeDeprecation,
|
||||
Message: "Please use labels instead of deprecated 'platform' filters",
|
||||
Data: errors.DeprecationErrorData{
|
||||
File: config.File,
|
||||
@ -236,8 +237,8 @@ func (l *Linter) lintDeprecations(config *WorkflowConfig) (err error) {
|
||||
}
|
||||
|
||||
if parsed.BranchesDoNotUseIt != nil {
|
||||
err = multierr.Append(err, &errors.PipelineError{
|
||||
Type: errors.PipelineErrorTypeDeprecation,
|
||||
err = multierr.Append(err, &errorTypes.PipelineError{
|
||||
Type: errorTypes.PipelineErrorTypeDeprecation,
|
||||
Message: "Please use global when instead of deprecated 'branches' filter",
|
||||
Data: errors.DeprecationErrorData{
|
||||
File: config.File,
|
||||
@ -250,8 +251,8 @@ func (l *Linter) lintDeprecations(config *WorkflowConfig) (err error) {
|
||||
|
||||
for _, step := range parsed.Steps.ContainerList {
|
||||
if step.Group != "" {
|
||||
err = multierr.Append(err, &errors.PipelineError{
|
||||
Type: errors.PipelineErrorTypeDeprecation,
|
||||
err = multierr.Append(err, &errorTypes.PipelineError{
|
||||
Type: errorTypes.PipelineErrorTypeDeprecation,
|
||||
Message: "Please use depends_on instead of deprecated 'group' setting",
|
||||
Data: errors.DeprecationErrorData{
|
||||
File: config.File,
|
||||
@ -265,8 +266,8 @@ func (l *Linter) lintDeprecations(config *WorkflowConfig) (err error) {
|
||||
|
||||
for i, c := range parsed.When.Constraints {
|
||||
if len(c.Event.Exclude) != 0 {
|
||||
err = multierr.Append(err, &errors.PipelineError{
|
||||
Type: errors.PipelineErrorTypeDeprecation,
|
||||
err = multierr.Append(err, &errorTypes.PipelineError{
|
||||
Type: errorTypes.PipelineErrorTypeDeprecation,
|
||||
Message: "Please only use allow lists for events",
|
||||
Data: errors.DeprecationErrorData{
|
||||
File: config.File,
|
||||
@ -281,8 +282,8 @@ func (l *Linter) lintDeprecations(config *WorkflowConfig) (err error) {
|
||||
for _, step := range parsed.Steps.ContainerList {
|
||||
for i, c := range step.When.Constraints {
|
||||
if len(c.Event.Exclude) != 0 {
|
||||
err = multierr.Append(err, &errors.PipelineError{
|
||||
Type: errors.PipelineErrorTypeDeprecation,
|
||||
err = multierr.Append(err, &errorTypes.PipelineError{
|
||||
Type: errorTypes.PipelineErrorTypeDeprecation,
|
||||
Message: "Please only use allow lists for events",
|
||||
Data: errors.DeprecationErrorData{
|
||||
File: config.File,
|
||||
@ -298,8 +299,8 @@ func (l *Linter) lintDeprecations(config *WorkflowConfig) (err error) {
|
||||
for _, step := range parsed.Steps.ContainerList {
|
||||
for i, c := range step.Secrets.Secrets {
|
||||
if c.Source != c.Target {
|
||||
err = multierr.Append(err, &errors.PipelineError{
|
||||
Type: errors.PipelineErrorTypeDeprecation,
|
||||
err = multierr.Append(err, &errorTypes.PipelineError{
|
||||
Type: errorTypes.PipelineErrorTypeDeprecation,
|
||||
Message: "Secrets alternative names are deprecated, use environment with from_secret",
|
||||
Data: errors.DeprecationErrorData{
|
||||
File: config.File,
|
||||
@ -348,8 +349,8 @@ func (l *Linter) lintBadHabits(config *WorkflowConfig) (err error) {
|
||||
}
|
||||
}
|
||||
if field != "" {
|
||||
err = multierr.Append(err, &errors.PipelineError{
|
||||
Type: errors.PipelineErrorTypeBadHabit,
|
||||
err = multierr.Append(err, &errorTypes.PipelineError{
|
||||
Type: errorTypes.PipelineErrorTypeBadHabit,
|
||||
Message: "Please set an event filter on all when branches",
|
||||
Data: errors.LinterErrorData{
|
||||
File: config.File,
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
|
||||
"codeberg.org/6543/xyaml"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
||||
errorTypes "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -116,7 +116,7 @@ func parse(raw []byte) (Matrix, error) {
|
||||
Matrix map[string][]string
|
||||
}{}
|
||||
if err := xyaml.Unmarshal(raw, &data); err != nil {
|
||||
return nil, &errors.PipelineError{Message: err.Error(), Type: errors.PipelineErrorTypeCompiler}
|
||||
return nil, &errorTypes.PipelineError{Message: err.Error(), Type: errorTypes.PipelineErrorTypeCompiler}
|
||||
}
|
||||
return data.Matrix, nil
|
||||
}
|
||||
@ -129,7 +129,7 @@ func parseList(raw []byte) ([]Axis, error) {
|
||||
}{}
|
||||
|
||||
if err := xyaml.Unmarshal(raw, &data); err != nil {
|
||||
return nil, &errors.PipelineError{Message: err.Error(), Type: errors.PipelineErrorTypeCompiler}
|
||||
return nil, &errorTypes.PipelineError{Message: err.Error(), Type: errorTypes.PipelineErrorTypeCompiler}
|
||||
}
|
||||
return data.Matrix.Include, nil
|
||||
}
|
||||
|
154
server/forge/addon/args.go
Normal file
154
server/forge/addon/args.go
Normal file
@ -0,0 +1,154 @@
|
||||
// Copyright 2024 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package addon
|
||||
|
||||
import (
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||
)
|
||||
|
||||
type argumentsAuth struct {
|
||||
Token string `json:"token"`
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
type argumentsRepo struct {
|
||||
U *modelUser `json:"u"`
|
||||
RemoteID model.ForgeRemoteID `json:"remote_id"`
|
||||
Owner string `json:"owner"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type argumentsFileDir struct {
|
||||
U *modelUser `json:"u"`
|
||||
R *modelRepo `json:"r"`
|
||||
B *model.Pipeline `json:"b"`
|
||||
F string `json:"f"`
|
||||
}
|
||||
|
||||
type argumentsStatus struct {
|
||||
U *modelUser `json:"u"`
|
||||
R *modelRepo `json:"r"`
|
||||
B *model.Pipeline `json:"b"`
|
||||
P *model.Workflow `json:"p"`
|
||||
}
|
||||
|
||||
type argumentsNetrc struct {
|
||||
U *modelUser `json:"u"`
|
||||
R *modelRepo `json:"r"`
|
||||
}
|
||||
|
||||
type argumentsActivateDeactivate struct {
|
||||
U *modelUser `json:"u"`
|
||||
R *modelRepo `json:"r"`
|
||||
Link string `json:"link"`
|
||||
}
|
||||
|
||||
type argumentsBranchesPullRequests struct {
|
||||
U *modelUser `json:"u"`
|
||||
R *modelRepo `json:"r"`
|
||||
P *model.ListOptions `json:"p"`
|
||||
}
|
||||
|
||||
type argumentsBranchHead struct {
|
||||
U *modelUser `json:"u"`
|
||||
R *modelRepo `json:"r"`
|
||||
Branch string `json:"branch"`
|
||||
}
|
||||
|
||||
type argumentsOrgMembershipOrg struct {
|
||||
U *modelUser `json:"u"`
|
||||
Org string `json:"org"`
|
||||
}
|
||||
|
||||
type responseHook struct {
|
||||
Repo *modelRepo `json:"repo"`
|
||||
Pipeline *model.Pipeline `json:"pipeline"`
|
||||
}
|
||||
|
||||
type responseLogin struct {
|
||||
User *modelUser `json:"user"`
|
||||
RedirectURL string `json:"redirect_url"`
|
||||
}
|
||||
|
||||
type httpRequest struct {
|
||||
Method string `json:"method"`
|
||||
URL string `json:"url"`
|
||||
Header map[string][]string `json:"header"`
|
||||
Form map[string][]string `json:"form"`
|
||||
Body []byte `json:"body"`
|
||||
}
|
||||
|
||||
// modelUser is an extension of model.User to marshal all fields to JSON
|
||||
type modelUser struct {
|
||||
User *model.User `json:"user"`
|
||||
|
||||
ForgeRemoteID model.ForgeRemoteID `json:"forge_remote_id"`
|
||||
|
||||
// Token is the oauth2 token.
|
||||
Token string `json:"token"`
|
||||
|
||||
// Secret is the oauth2 token secret.
|
||||
Secret string `json:"secret"`
|
||||
|
||||
// Expiry is the token and secret expiration timestamp.
|
||||
Expiry int64 `json:"expiry"`
|
||||
|
||||
// Hash is a unique token used to sign tokens.
|
||||
Hash string `json:"hash"`
|
||||
}
|
||||
|
||||
func (m *modelUser) asModel() *model.User {
|
||||
m.User.ForgeRemoteID = m.ForgeRemoteID
|
||||
m.User.Token = m.Token
|
||||
m.User.Secret = m.Secret
|
||||
m.User.Expiry = m.Expiry
|
||||
m.User.Hash = m.Hash
|
||||
return m.User
|
||||
}
|
||||
|
||||
func modelUserFromModel(u *model.User) *modelUser {
|
||||
return &modelUser{
|
||||
User: u,
|
||||
ForgeRemoteID: u.ForgeRemoteID,
|
||||
Token: u.Token,
|
||||
Secret: u.Secret,
|
||||
Expiry: u.Expiry,
|
||||
Hash: u.Hash,
|
||||
}
|
||||
}
|
||||
|
||||
// modelRepo is an extension of model.Repo to marshal all fields to JSON
|
||||
type modelRepo struct {
|
||||
Repo *model.Repo `json:"repo"`
|
||||
UserID int64 `json:"user_id"`
|
||||
Hash string `json:"hash"`
|
||||
Perm *model.Perm `json:"perm"`
|
||||
}
|
||||
|
||||
func (m *modelRepo) asModel() *model.Repo {
|
||||
m.Repo.UserID = m.UserID
|
||||
m.Repo.Hash = m.Hash
|
||||
m.Repo.Perm = m.Perm
|
||||
return m.Repo
|
||||
}
|
||||
|
||||
func modelRepoFromModel(r *model.Repo) *modelRepo {
|
||||
return &modelRepo{
|
||||
Repo: r,
|
||||
UserID: r.UserID,
|
||||
Hash: r.Hash,
|
||||
Perm: r.Perm,
|
||||
}
|
||||
}
|
377
server/forge/addon/client.go
Normal file
377
server/forge/addon/client.go
Normal file
@ -0,0 +1,377 @@
|
||||
// Copyright 2024 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package addon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/rpc"
|
||||
"os/exec"
|
||||
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||
)
|
||||
|
||||
// make sure RPC implements forge.Forge
|
||||
var _ forge.Forge = new(RPC)
|
||||
|
||||
func Load(file string) (forge.Forge, error) {
|
||||
client := plugin.NewClient(&plugin.ClientConfig{
|
||||
HandshakeConfig: HandshakeConfig,
|
||||
Plugins: map[string]plugin.Plugin{
|
||||
pluginKey: &Plugin{},
|
||||
},
|
||||
Cmd: exec.Command(file),
|
||||
Logger: &clientLogger{
|
||||
logger: log.With().Str("addon", file).Logger(),
|
||||
},
|
||||
})
|
||||
// TODO defer client.Kill()
|
||||
|
||||
rpcClient, err := client.Client()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
raw, err := rpcClient.Dispense(pluginKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extension, _ := raw.(forge.Forge)
|
||||
return extension, nil
|
||||
}
|
||||
|
||||
type RPC struct {
|
||||
client *rpc.Client
|
||||
}
|
||||
|
||||
func (g *RPC) Name() string {
|
||||
var resp string
|
||||
_ = g.client.Call("Plugin.Name", nil, &resp)
|
||||
return resp
|
||||
}
|
||||
|
||||
func (g *RPC) URL() string {
|
||||
var resp string
|
||||
_ = g.client.Call("Plugin.URL", nil, &resp)
|
||||
return resp
|
||||
}
|
||||
|
||||
func (g *RPC) Login(_ context.Context, r *types.OAuthRequest) (*model.User, string, error) {
|
||||
args, err := json.Marshal(r)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
var jsonResp []byte
|
||||
err = g.client.Call("Plugin.Login", args, &jsonResp)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var resp responseLogin
|
||||
err = json.Unmarshal(jsonResp, &resp)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return resp.User.asModel(), resp.RedirectURL, nil
|
||||
}
|
||||
|
||||
func (g *RPC) Auth(_ context.Context, token, secret string) (string, error) {
|
||||
args, err := json.Marshal(&argumentsAuth{
|
||||
Token: token,
|
||||
Secret: secret,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var resp string
|
||||
return resp, g.client.Call("Plugin.Auth", args, &resp)
|
||||
}
|
||||
|
||||
func (g *RPC) Teams(_ context.Context, u *model.User) ([]*model.Team, error) {
|
||||
args, err := json.Marshal(modelUserFromModel(u))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var jsonResp []byte
|
||||
err = g.client.Call("Plugin.Teams", args, &jsonResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp []*model.Team
|
||||
return resp, json.Unmarshal(jsonResp, &resp)
|
||||
}
|
||||
|
||||
func (g *RPC) Repo(_ context.Context, u *model.User, remoteID model.ForgeRemoteID, owner, name string) (*model.Repo, error) {
|
||||
args, err := json.Marshal(&argumentsRepo{
|
||||
U: modelUserFromModel(u),
|
||||
RemoteID: remoteID,
|
||||
Owner: owner,
|
||||
Name: name,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var jsonResp []byte
|
||||
err = g.client.Call("Plugin.Repo", args, &jsonResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp *modelRepo
|
||||
err = json.Unmarshal(jsonResp, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.asModel(), nil
|
||||
}
|
||||
|
||||
func (g *RPC) Repos(_ context.Context, u *model.User) ([]*model.Repo, error) {
|
||||
args, err := json.Marshal(modelUserFromModel(u))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var jsonResp []byte
|
||||
err = g.client.Call("Plugin.Repos", args, &jsonResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp []*modelRepo
|
||||
err = json.Unmarshal(jsonResp, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var modelRepos []*model.Repo
|
||||
for _, repo := range resp {
|
||||
modelRepos = append(modelRepos, repo.asModel())
|
||||
}
|
||||
return modelRepos, nil
|
||||
}
|
||||
|
||||
func (g *RPC) File(_ context.Context, u *model.User, r *model.Repo, b *model.Pipeline, f string) ([]byte, error) {
|
||||
args, err := json.Marshal(&argumentsFileDir{
|
||||
U: modelUserFromModel(u),
|
||||
R: modelRepoFromModel(r),
|
||||
B: b,
|
||||
F: f,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var resp []byte
|
||||
return resp, g.client.Call("Plugin.File", args, &resp)
|
||||
}
|
||||
|
||||
func (g *RPC) Dir(_ context.Context, u *model.User, r *model.Repo, b *model.Pipeline, f string) ([]*types.FileMeta, error) {
|
||||
args, err := json.Marshal(&argumentsFileDir{
|
||||
U: modelUserFromModel(u),
|
||||
R: modelRepoFromModel(r),
|
||||
B: b,
|
||||
F: f,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var jsonResp []byte
|
||||
err = g.client.Call("Plugin.Dir", args, &jsonResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var resp []*types.FileMeta
|
||||
return resp, json.Unmarshal(jsonResp, &resp)
|
||||
}
|
||||
|
||||
func (g *RPC) Status(_ context.Context, u *model.User, r *model.Repo, b *model.Pipeline, p *model.Workflow) error {
|
||||
args, err := json.Marshal(&argumentsStatus{
|
||||
U: modelUserFromModel(u),
|
||||
R: modelRepoFromModel(r),
|
||||
B: b,
|
||||
P: p,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var jsonResp []byte
|
||||
return g.client.Call("Plugin.Status", args, &jsonResp)
|
||||
}
|
||||
|
||||
func (g *RPC) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
|
||||
args, err := json.Marshal(&argumentsNetrc{
|
||||
U: modelUserFromModel(u),
|
||||
R: modelRepoFromModel(r),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var jsonResp []byte
|
||||
err = g.client.Call("Plugin.Netrc", args, &jsonResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var resp *model.Netrc
|
||||
return resp, json.Unmarshal(jsonResp, &resp)
|
||||
}
|
||||
|
||||
func (g *RPC) Activate(_ context.Context, u *model.User, r *model.Repo, link string) error {
|
||||
args, err := json.Marshal(&argumentsActivateDeactivate{
|
||||
U: modelUserFromModel(u),
|
||||
R: modelRepoFromModel(r),
|
||||
Link: link,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var jsonResp []byte
|
||||
return g.client.Call("Plugin.Activate", args, &jsonResp)
|
||||
}
|
||||
|
||||
func (g *RPC) Deactivate(_ context.Context, u *model.User, r *model.Repo, link string) error {
|
||||
args, err := json.Marshal(&argumentsActivateDeactivate{
|
||||
U: modelUserFromModel(u),
|
||||
R: modelRepoFromModel(r),
|
||||
Link: link,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var jsonResp []byte
|
||||
return g.client.Call("Plugin.Deactivate", args, &jsonResp)
|
||||
}
|
||||
|
||||
func (g *RPC) Branches(_ context.Context, u *model.User, r *model.Repo, p *model.ListOptions) ([]string, error) {
|
||||
args, err := json.Marshal(&argumentsBranchesPullRequests{
|
||||
U: modelUserFromModel(u),
|
||||
R: modelRepoFromModel(r),
|
||||
P: p,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var jsonResp []byte
|
||||
err = g.client.Call("Plugin.Branches", args, &jsonResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var resp []string
|
||||
return resp, json.Unmarshal(jsonResp, &resp)
|
||||
}
|
||||
|
||||
func (g *RPC) BranchHead(_ context.Context, u *model.User, r *model.Repo, branch string) (*model.Commit, error) {
|
||||
args, err := json.Marshal(&argumentsBranchHead{
|
||||
U: modelUserFromModel(u),
|
||||
R: modelRepoFromModel(r),
|
||||
Branch: branch,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var jsonResp []byte
|
||||
err = g.client.Call("Plugin.BranchHead", args, &jsonResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var resp *model.Commit
|
||||
return resp, json.Unmarshal(jsonResp, &resp)
|
||||
}
|
||||
|
||||
func (g *RPC) PullRequests(_ context.Context, u *model.User, r *model.Repo, p *model.ListOptions) ([]*model.PullRequest, error) {
|
||||
args, err := json.Marshal(&argumentsBranchesPullRequests{
|
||||
U: modelUserFromModel(u),
|
||||
R: modelRepoFromModel(r),
|
||||
P: p,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var jsonResp []byte
|
||||
err = g.client.Call("Plugin.PullRequests", args, &jsonResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var resp []*model.PullRequest
|
||||
return resp, json.Unmarshal(jsonResp, &resp)
|
||||
}
|
||||
|
||||
func (g *RPC) Hook(_ context.Context, r *http.Request) (*model.Repo, *model.Pipeline, error) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
args, err := json.Marshal(&httpRequest{
|
||||
Method: r.Method,
|
||||
URL: r.URL.String(),
|
||||
Header: r.Header,
|
||||
Form: r.Form,
|
||||
Body: body,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var jsonResp []byte
|
||||
err = g.client.Call("Plugin.Hook", args, &jsonResp)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var resp responseHook
|
||||
err = json.Unmarshal(jsonResp, &resp)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return resp.Repo.asModel(), resp.Pipeline, nil
|
||||
}
|
||||
|
||||
func (g *RPC) OrgMembership(_ context.Context, u *model.User, org string) (*model.OrgPerm, error) {
|
||||
args, err := json.Marshal(&argumentsOrgMembershipOrg{
|
||||
U: modelUserFromModel(u),
|
||||
Org: org,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var jsonResp []byte
|
||||
err = g.client.Call("Plugin.OrgMembership", args, &jsonResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var resp *model.OrgPerm
|
||||
return resp, json.Unmarshal(jsonResp, &resp)
|
||||
}
|
||||
|
||||
func (g *RPC) Org(_ context.Context, u *model.User, org string) (*model.Org, error) {
|
||||
args, err := json.Marshal(&argumentsOrgMembershipOrg{
|
||||
U: modelUserFromModel(u),
|
||||
Org: org,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var jsonResp []byte
|
||||
err = g.client.Call("Plugin.Org", args, &jsonResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var resp *model.Org
|
||||
return resp, json.Unmarshal(jsonResp, &resp)
|
||||
}
|
165
server/forge/addon/logger.go
Normal file
165
server/forge/addon/logger.go
Normal file
@ -0,0 +1,165 @@
|
||||
// Copyright 2024 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package addon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
stdlog "log"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type clientLogger struct {
|
||||
logger zerolog.Logger
|
||||
name string
|
||||
withArgs []any
|
||||
}
|
||||
|
||||
func convertLvl(level hclog.Level) zerolog.Level {
|
||||
switch level {
|
||||
case hclog.Error:
|
||||
return zerolog.ErrorLevel
|
||||
case hclog.Warn:
|
||||
return zerolog.WarnLevel
|
||||
case hclog.Info:
|
||||
return zerolog.InfoLevel
|
||||
case hclog.Debug:
|
||||
return zerolog.DebugLevel
|
||||
case hclog.Trace:
|
||||
return zerolog.TraceLevel
|
||||
}
|
||||
return zerolog.NoLevel
|
||||
}
|
||||
|
||||
func (c *clientLogger) applyArgs(args []any) *zerolog.Logger {
|
||||
var key string
|
||||
logger := c.logger.With()
|
||||
args = append(args, c.withArgs)
|
||||
for i, arg := range args {
|
||||
switch {
|
||||
case key != "":
|
||||
logger.Any(key, arg)
|
||||
key = ""
|
||||
case i == len(args)-1:
|
||||
logger.Any(hclog.MissingKey, arg)
|
||||
default:
|
||||
|
||||
key, _ = arg.(string)
|
||||
}
|
||||
}
|
||||
l := logger.Logger()
|
||||
return &l
|
||||
}
|
||||
|
||||
func (c *clientLogger) Log(level hclog.Level, msg string, args ...any) {
|
||||
c.applyArgs(args).WithLevel(convertLvl(level)).Msg(msg)
|
||||
}
|
||||
|
||||
func (c *clientLogger) Trace(msg string, args ...any) {
|
||||
c.applyArgs(args).Trace().Msg(msg)
|
||||
}
|
||||
|
||||
func (c *clientLogger) Debug(msg string, args ...any) {
|
||||
c.applyArgs(args).Debug().Msg(msg)
|
||||
}
|
||||
|
||||
func (c *clientLogger) Info(msg string, args ...any) {
|
||||
c.applyArgs(args).Info().Msg(msg)
|
||||
}
|
||||
|
||||
func (c *clientLogger) Warn(msg string, args ...any) {
|
||||
c.applyArgs(args).Warn().Msg(msg)
|
||||
}
|
||||
|
||||
func (c *clientLogger) Error(msg string, args ...any) {
|
||||
c.applyArgs(args).Error().Msg(msg)
|
||||
}
|
||||
|
||||
func (c *clientLogger) IsTrace() bool {
|
||||
return log.Logger.GetLevel() >= zerolog.TraceLevel
|
||||
}
|
||||
|
||||
func (c *clientLogger) IsDebug() bool {
|
||||
return log.Logger.GetLevel() >= zerolog.DebugLevel
|
||||
}
|
||||
|
||||
func (c *clientLogger) IsInfo() bool {
|
||||
return log.Logger.GetLevel() >= zerolog.InfoLevel
|
||||
}
|
||||
|
||||
func (c *clientLogger) IsWarn() bool {
|
||||
return log.Logger.GetLevel() >= zerolog.WarnLevel
|
||||
}
|
||||
|
||||
func (c *clientLogger) IsError() bool {
|
||||
return log.Logger.GetLevel() >= zerolog.ErrorLevel
|
||||
}
|
||||
|
||||
func (c *clientLogger) ImpliedArgs() []any {
|
||||
return c.withArgs
|
||||
}
|
||||
|
||||
func (c *clientLogger) With(args ...any) hclog.Logger {
|
||||
return &clientLogger{
|
||||
logger: c.logger,
|
||||
name: c.name,
|
||||
withArgs: args,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *clientLogger) Name() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
func (c *clientLogger) Named(name string) hclog.Logger {
|
||||
curr := c.name
|
||||
if curr != "" {
|
||||
curr = c.name + "."
|
||||
}
|
||||
return c.ResetNamed(curr + name)
|
||||
}
|
||||
|
||||
func (c *clientLogger) ResetNamed(name string) hclog.Logger {
|
||||
return &clientLogger{
|
||||
logger: c.logger,
|
||||
name: name,
|
||||
withArgs: c.withArgs,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *clientLogger) SetLevel(level hclog.Level) {
|
||||
c.logger = c.logger.Level(convertLvl(level))
|
||||
}
|
||||
|
||||
func (c *clientLogger) StandardLogger(opts *hclog.StandardLoggerOptions) *stdlog.Logger {
|
||||
return stdlog.New(c.StandardWriter(opts), "", 0)
|
||||
}
|
||||
|
||||
func (c *clientLogger) StandardWriter(*hclog.StandardLoggerOptions) io.Writer {
|
||||
return ioAdapter{logger: c.logger}
|
||||
}
|
||||
|
||||
type ioAdapter struct {
|
||||
logger zerolog.Logger
|
||||
}
|
||||
|
||||
func (i ioAdapter) Write(p []byte) (n int, err error) {
|
||||
str := string(bytes.TrimRight(p, " \t\n"))
|
||||
i.logger.Log().Msg(str)
|
||||
return len(p), nil
|
||||
}
|
43
server/forge/addon/plugin.go
Normal file
43
server/forge/addon/plugin.go
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2024 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package addon
|
||||
|
||||
import (
|
||||
"net/rpc"
|
||||
|
||||
"github.com/hashicorp/go-plugin"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
||||
)
|
||||
|
||||
const pluginKey = "forge"
|
||||
|
||||
var HandshakeConfig = plugin.HandshakeConfig{
|
||||
ProtocolVersion: 1,
|
||||
MagicCookieKey: "WOODPECKER_FORGE_ADDON_PLUGIN",
|
||||
MagicCookieValue: "woodpecker-plugin-magic-cookie-value",
|
||||
}
|
||||
|
||||
type Plugin struct {
|
||||
Impl forge.Forge
|
||||
}
|
||||
|
||||
func (p *Plugin) Server(*plugin.MuxBroker) (any, error) {
|
||||
return &RPCServer{Impl: p.Impl}, nil
|
||||
}
|
||||
|
||||
func (*Plugin) Client(_ *plugin.MuxBroker, c *rpc.Client) (any, error) {
|
||||
return &RPC{client: c}, nil
|
||||
}
|
278
server/forge/addon/server.go
Normal file
278
server/forge/addon/server.go
Normal file
@ -0,0 +1,278 @@
|
||||
// Copyright 2024 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package addon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/go-plugin"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
||||
)
|
||||
|
||||
func Serve(impl forge.Forge) {
|
||||
plugin.Serve(&plugin.ServeConfig{
|
||||
HandshakeConfig: HandshakeConfig,
|
||||
Plugins: map[string]plugin.Plugin{
|
||||
pluginKey: &Plugin{Impl: impl},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func mkCtx() context.Context {
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
type RPCServer struct {
|
||||
Impl forge.Forge
|
||||
}
|
||||
|
||||
func (s *RPCServer) Name(_ []byte, resp *string) error {
|
||||
*resp = s.Impl.Name()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) URL(_ []byte, resp *string) error {
|
||||
*resp = s.Impl.URL()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) Teams(args []byte, resp *[]byte) error {
|
||||
var a *modelUser
|
||||
err := json.Unmarshal(args, a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
teams, err := s.Impl.Teams(mkCtx(), a.asModel())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp, err = json.Marshal(teams)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *RPCServer) Repo(args []byte, resp *[]byte) error {
|
||||
var a argumentsRepo
|
||||
err := json.Unmarshal(args, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repos, err := s.Impl.Repo(mkCtx(), a.U.asModel(), a.RemoteID, a.Owner, a.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp, err = json.Marshal(modelRepoFromModel(repos))
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *RPCServer) Repos(args []byte, resp *[]byte) error {
|
||||
var a *modelUser
|
||||
err := json.Unmarshal(args, a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repos, err := s.Impl.Repos(mkCtx(), a.asModel())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var modelRepos []*modelRepo
|
||||
for _, repo := range repos {
|
||||
modelRepos = append(modelRepos, modelRepoFromModel(repo))
|
||||
}
|
||||
*resp, err = json.Marshal(modelRepos)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *RPCServer) File(args []byte, resp *[]byte) error {
|
||||
var a argumentsFileDir
|
||||
err := json.Unmarshal(args, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp, err = s.Impl.File(mkCtx(), a.U.asModel(), a.R.asModel(), a.B, a.F)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *RPCServer) Dir(args []byte, resp *[]byte) error {
|
||||
var a argumentsFileDir
|
||||
err := json.Unmarshal(args, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
meta, err := s.Impl.Dir(mkCtx(), a.U.asModel(), a.R.asModel(), a.B, a.F)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp, err = json.Marshal(meta)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *RPCServer) Status(args []byte, resp *[]byte) error {
|
||||
var a argumentsStatus
|
||||
err := json.Unmarshal(args, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp = []byte{}
|
||||
return s.Impl.Status(mkCtx(), a.U.asModel(), a.R.asModel(), a.B, a.P)
|
||||
}
|
||||
|
||||
func (s *RPCServer) Netrc(args []byte, resp *[]byte) error {
|
||||
var a argumentsNetrc
|
||||
err := json.Unmarshal(args, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
netrc, err := s.Impl.Netrc(a.U.asModel(), a.R.asModel())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp, err = json.Marshal(netrc)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *RPCServer) Activate(args []byte, resp *[]byte) error {
|
||||
var a argumentsActivateDeactivate
|
||||
err := json.Unmarshal(args, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp = []byte{}
|
||||
return s.Impl.Activate(mkCtx(), a.U.asModel(), a.R.asModel(), a.Link)
|
||||
}
|
||||
|
||||
func (s *RPCServer) Deactivate(args []byte, resp *[]byte) error {
|
||||
var a argumentsActivateDeactivate
|
||||
err := json.Unmarshal(args, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp = []byte{}
|
||||
return s.Impl.Deactivate(mkCtx(), a.U.asModel(), a.R.asModel(), a.Link)
|
||||
}
|
||||
|
||||
func (s *RPCServer) Branches(args []byte, resp *[]byte) error {
|
||||
var a argumentsBranchesPullRequests
|
||||
err := json.Unmarshal(args, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
branches, err := s.Impl.Branches(mkCtx(), a.U.asModel(), a.R.asModel(), a.P)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp, err = json.Marshal(branches)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *RPCServer) BranchHead(args []byte, resp *[]byte) error {
|
||||
var a argumentsBranchHead
|
||||
err := json.Unmarshal(args, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
commit, err := s.Impl.BranchHead(mkCtx(), a.U.asModel(), a.R.asModel(), a.Branch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp, err = json.Marshal(commit)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *RPCServer) PullRequests(args []byte, resp *[]byte) error {
|
||||
var a argumentsBranchesPullRequests
|
||||
err := json.Unmarshal(args, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prs, err := s.Impl.PullRequests(mkCtx(), a.U.asModel(), a.R.asModel(), a.P)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp, err = json.Marshal(prs)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *RPCServer) OrgMembership(args []byte, resp *[]byte) error {
|
||||
var a argumentsOrgMembershipOrg
|
||||
err := json.Unmarshal(args, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
org, err := s.Impl.OrgMembership(mkCtx(), a.U.asModel(), a.Org)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp, err = json.Marshal(org)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *RPCServer) Org(args []byte, resp *[]byte) error {
|
||||
var a argumentsOrgMembershipOrg
|
||||
err := json.Unmarshal(args, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
org, err := s.Impl.Org(mkCtx(), a.U.asModel(), a.Org)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp, err = json.Marshal(org)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *RPCServer) Hook(args []byte, resp *[]byte) error {
|
||||
var a httpRequest
|
||||
err := json.Unmarshal(args, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req, err := http.NewRequest(a.Method, a.URL, bytes.NewBuffer(a.Body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header = a.Header
|
||||
req.Form = a.Form
|
||||
repo, pipeline, err := s.Impl.Hook(mkCtx(), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp, err = json.Marshal(&responseHook{
|
||||
Repo: modelRepoFromModel(repo),
|
||||
Pipeline: pipeline,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *RPCServer) Login(args []byte, resp *[]byte) error {
|
||||
var a *types.OAuthRequest
|
||||
err := json.Unmarshal(args, a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user, red, err := s.Impl.Login(mkCtx(), a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp, err = json.Marshal(&responseLogin{
|
||||
User: modelUserFromModel(user),
|
||||
RedirectURL: red,
|
||||
})
|
||||
return err
|
||||
}
|
@ -16,41 +16,41 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors/types"
|
||||
)
|
||||
|
||||
type Pipeline struct {
|
||||
ID int64 `json:"id" xorm:"pk autoincr 'pipeline_id'"`
|
||||
RepoID int64 `json:"-" xorm:"UNIQUE(s) INDEX 'pipeline_repo_id'"`
|
||||
Number int64 `json:"number" xorm:"UNIQUE(s) 'pipeline_number'"`
|
||||
Author string `json:"author" xorm:"INDEX 'pipeline_author'"`
|
||||
Parent int64 `json:"parent" xorm:"pipeline_parent"`
|
||||
Event WebhookEvent `json:"event" xorm:"pipeline_event"`
|
||||
Status StatusValue `json:"status" xorm:"INDEX 'pipeline_status'"`
|
||||
Errors []*errors.PipelineError `json:"errors" xorm:"json 'pipeline_errors'"`
|
||||
Created int64 `json:"created_at" xorm:"pipeline_created"`
|
||||
Updated int64 `json:"updated_at" xorm:"updated NOT NULL DEFAULT 0 'updated'"`
|
||||
Started int64 `json:"started_at" xorm:"pipeline_started"`
|
||||
Finished int64 `json:"finished_at" xorm:"pipeline_finished"`
|
||||
Deploy string `json:"deploy_to" xorm:"pipeline_deploy"`
|
||||
Commit string `json:"commit" xorm:"pipeline_commit"`
|
||||
Branch string `json:"branch" xorm:"pipeline_branch"`
|
||||
Ref string `json:"ref" xorm:"pipeline_ref"`
|
||||
Refspec string `json:"refspec" xorm:"pipeline_refspec"`
|
||||
Title string `json:"title" xorm:"pipeline_title"`
|
||||
Message string `json:"message" xorm:"TEXT 'pipeline_message'"`
|
||||
Timestamp int64 `json:"timestamp" xorm:"pipeline_timestamp"`
|
||||
Sender string `json:"sender" xorm:"pipeline_sender"` // uses reported user for webhooks and name of cron for cron pipelines
|
||||
Avatar string `json:"author_avatar" xorm:"pipeline_avatar"`
|
||||
Email string `json:"author_email" xorm:"pipeline_email"`
|
||||
ForgeURL string `json:"forge_url" xorm:"pipeline_forge_url"`
|
||||
Reviewer string `json:"reviewed_by" xorm:"pipeline_reviewer"`
|
||||
Reviewed int64 `json:"reviewed_at" xorm:"pipeline_reviewed"`
|
||||
Workflows []*Workflow `json:"workflows,omitempty" xorm:"-"`
|
||||
ChangedFiles []string `json:"changed_files,omitempty" xorm:"LONGTEXT 'changed_files'"`
|
||||
AdditionalVariables map[string]string `json:"variables,omitempty" xorm:"json 'additional_variables'"`
|
||||
PullRequestLabels []string `json:"pr_labels,omitempty" xorm:"json 'pr_labels'"`
|
||||
IsPrerelease bool `json:"is_prerelease,omitempty" xorm:"is_prerelease"`
|
||||
ID int64 `json:"id" xorm:"pk autoincr 'pipeline_id'"`
|
||||
RepoID int64 `json:"-" xorm:"UNIQUE(s) INDEX 'pipeline_repo_id'"`
|
||||
Number int64 `json:"number" xorm:"UNIQUE(s) 'pipeline_number'"`
|
||||
Author string `json:"author" xorm:"INDEX 'pipeline_author'"`
|
||||
Parent int64 `json:"parent" xorm:"pipeline_parent"`
|
||||
Event WebhookEvent `json:"event" xorm:"pipeline_event"`
|
||||
Status StatusValue `json:"status" xorm:"INDEX 'pipeline_status'"`
|
||||
Errors []*types.PipelineError `json:"errors" xorm:"json 'pipeline_errors'"`
|
||||
Created int64 `json:"created_at" xorm:"pipeline_created"`
|
||||
Updated int64 `json:"updated_at" xorm:"updated NOT NULL DEFAULT 0 'updated'"`
|
||||
Started int64 `json:"started_at" xorm:"pipeline_started"`
|
||||
Finished int64 `json:"finished_at" xorm:"pipeline_finished"`
|
||||
Deploy string `json:"deploy_to" xorm:"pipeline_deploy"`
|
||||
Commit string `json:"commit" xorm:"pipeline_commit"`
|
||||
Branch string `json:"branch" xorm:"pipeline_branch"`
|
||||
Ref string `json:"ref" xorm:"pipeline_ref"`
|
||||
Refspec string `json:"refspec" xorm:"pipeline_refspec"`
|
||||
Title string `json:"title" xorm:"pipeline_title"`
|
||||
Message string `json:"message" xorm:"TEXT 'pipeline_message'"`
|
||||
Timestamp int64 `json:"timestamp" xorm:"pipeline_timestamp"`
|
||||
Sender string `json:"sender" xorm:"pipeline_sender"` // uses reported user for webhooks and name of cron for cron pipelines
|
||||
Avatar string `json:"author_avatar" xorm:"pipeline_avatar"`
|
||||
Email string `json:"author_email" xorm:"pipeline_email"`
|
||||
ForgeURL string `json:"forge_url" xorm:"pipeline_forge_url"`
|
||||
Reviewer string `json:"reviewed_by" xorm:"pipeline_reviewer"`
|
||||
Reviewed int64 `json:"reviewed_at" xorm:"pipeline_reviewed"`
|
||||
Workflows []*Workflow `json:"workflows,omitempty" xorm:"-"`
|
||||
ChangedFiles []string `json:"changed_files,omitempty" xorm:"LONGTEXT 'changed_files'"`
|
||||
AdditionalVariables map[string]string `json:"variables,omitempty" xorm:"json 'additional_variables'"`
|
||||
PullRequestLabels []string `json:"pr_labels,omitempty" xorm:"json 'pr_labels'"`
|
||||
IsPrerelease bool `json:"is_prerelease,omitempty" xorm:"is_prerelease"`
|
||||
} // @name Pipeline
|
||||
|
||||
// TableName return database table name for xorm
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
|
||||
backend_types "go.woodpecker-ci.org/woodpecker/v2/pipeline/backend/types"
|
||||
pipeline_errors "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
||||
errorTypes "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors/types"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/metadata"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/compiler"
|
||||
@ -135,7 +136,7 @@ func (b *StepBuilder) genItemForWorkflow(workflow *model.Workflow, axis matrix.A
|
||||
// parse yaml pipeline
|
||||
parsed, err := yaml.ParseString(substituted)
|
||||
if err != nil {
|
||||
return nil, &pipeline_errors.PipelineError{Message: err.Error(), Type: pipeline_errors.PipelineErrorTypeCompiler}
|
||||
return nil, &errorTypes.PipelineError{Message: err.Error(), Type: errorTypes.PipelineErrorTypeCompiler}
|
||||
}
|
||||
|
||||
// lint pipeline
|
||||
|
@ -18,16 +18,16 @@ import (
|
||||
"src.techknowlogick.com/xormigrate"
|
||||
"xorm.io/xorm"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
||||
errorTypes "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors/types"
|
||||
)
|
||||
|
||||
// perPage027 set the size of the slice to read per page
|
||||
var perPage027 = 100
|
||||
|
||||
type pipeline027 struct {
|
||||
ID int64 `json:"id" xorm:"pk autoincr 'pipeline_id'"`
|
||||
Error string `json:"error" xorm:"LONGTEXT 'pipeline_error'"` // old error format
|
||||
Errors []*errors.PipelineError `json:"errors" xorm:"json 'pipeline_errors'"` // new error format
|
||||
ID int64 `json:"id" xorm:"pk autoincr 'pipeline_id'"`
|
||||
Error string `json:"error" xorm:"LONGTEXT 'pipeline_error'"` // old error format
|
||||
Errors []*errorTypes.PipelineError `json:"errors" xorm:"json 'pipeline_errors'"` // new error format
|
||||
}
|
||||
|
||||
func (pipeline027) TableName() string {
|
||||
@ -64,7 +64,7 @@ var convertToNewPipelineErrorFormat = xormigrate.Migration{
|
||||
for _, oldPipeline := range oldPipelines {
|
||||
var newPipeline pipeline027
|
||||
newPipeline.ID = oldPipeline.ID
|
||||
newPipeline.Errors = []*errors.PipelineError{{
|
||||
newPipeline.Errors = []*errorTypes.PipelineError{{
|
||||
Type: "generic",
|
||||
Message: oldPipeline.Error,
|
||||
}}
|
||||
|
@ -1,62 +0,0 @@
|
||||
package addon
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"plugin"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/shared/addon/types"
|
||||
)
|
||||
|
||||
var pluginCache = map[string]*plugin.Plugin{}
|
||||
|
||||
type Addon[T any] struct {
|
||||
Type types.Type
|
||||
Value T
|
||||
}
|
||||
|
||||
func Load[T any](files []string, t types.Type) (*Addon[T], error) {
|
||||
for _, file := range files {
|
||||
if _, has := pluginCache[file]; !has {
|
||||
p, err := plugin.Open(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pluginCache[file] = p
|
||||
}
|
||||
|
||||
typeLookup, err := pluginCache[file].Lookup("Type")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addonType, is := typeLookup.(*types.Type); !is {
|
||||
return nil, errors.New("addon type is incorrect")
|
||||
} else if *addonType != t {
|
||||
continue
|
||||
}
|
||||
|
||||
mainLookup, err := pluginCache[file].Lookup("Addon")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
main, is := mainLookup.(func(zerolog.Logger) (T, error))
|
||||
if !is {
|
||||
return nil, errors.New("addon main function has incorrect type")
|
||||
}
|
||||
|
||||
logger := log.Logger.With().Str("addon", file).Logger()
|
||||
|
||||
mainOut, err := main(logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Addon[T]{
|
||||
Type: t,
|
||||
Value: mainOut,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package types
|
||||
|
||||
type Type string
|
||||
|
||||
const (
|
||||
TypeForge Type = "forge"
|
||||
)
|
Loading…
Reference in New Issue
Block a user