378 lines
8.9 KiB
Go
378 lines
8.9 KiB
Go
// 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/v3/server/forge"
|
|
"go.woodpecker-ci.org/woodpecker/v3/server/forge/types"
|
|
"go.woodpecker-ci.org/woodpecker/v3/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)
|
|
}
|