feat: add remote trigger token support for Jenkins job execution

- Add support for passing a remote trigger token to Jenkins jobs
- Update the Jenkins constructor to accept a token parameter
- Ensure the token is included as a query parameter when triggering jobs
- Improve error reporting by including response body in error messages
- Remove unnecessary logging and refactor build parameter logic
- Update tests to use the new Jenkins constructor and token handling
- Add CLI option for specifying a remote trigger token
- Extend plugin configuration to support remote token injection

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
This commit is contained in:
Bo-Yi Wu
2025-12-01 17:16:39 +08:00
parent a5469c939e
commit 3dd86f956c
4 changed files with 55 additions and 30 deletions
+30 -12
View File
@@ -6,7 +6,6 @@ import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"strings"
@@ -23,12 +22,13 @@ type (
Jenkins struct {
Auth *Auth
BaseURL string
Token string // Remote trigger token
Client *http.Client
}
)
// NewJenkins is initial Jenkins object
func NewJenkins(auth *Auth, url string, insecure bool) *Jenkins {
func NewJenkins(auth *Auth, url string, token string, insecure bool) *Jenkins {
url = strings.TrimRight(url, "/")
client := http.DefaultClient
@@ -44,6 +44,7 @@ func NewJenkins(auth *Auth, url string, insecure bool) *Jenkins {
return &Jenkins{
Auth: auth,
BaseURL: url,
Token: token,
Client: client,
}
}
@@ -70,12 +71,12 @@ func (jenkins *Jenkins) sendRequest(req *http.Request) (*http.Response, error) {
func (jenkins *Jenkins) parseResponse(resp *http.Response, body interface{}) (err error) {
defer resp.Body.Close()
if body == nil {
data, err := io.ReadAll(resp.Body)
if err != nil {
return
}
data, err := io.ReadAll(resp.Body)
if err != nil {
if body == nil {
return
}
@@ -84,6 +85,7 @@ func (jenkins *Jenkins) parseResponse(resp *http.Response, body interface{}) (er
func (jenkins *Jenkins) post(path string, params url.Values, body interface{}) (err error) {
requestURL := jenkins.buildURL(path, params)
req, err := http.NewRequestWithContext(context.Background(), "POST", requestURL, nil)
if err != nil {
return
@@ -95,7 +97,7 @@ func (jenkins *Jenkins) post(path string, params url.Values, body interface{}) (
}
if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
return fmt.Errorf("unexpected response code: %d", resp.StatusCode)
return fmt.Errorf("unexpected response code: %d, body: %s", resp.StatusCode, string(data))
}
return jenkins.parseResponse(resp, body)
@@ -119,14 +121,30 @@ func (jenkins *Jenkins) parseJobPath(job string) string {
}
func (jenkins *Jenkins) trigger(job string, params url.Values) error {
var urlPath string
if len(params) == 0 {
urlPath = jenkins.parseJobPath(job) + "/build"
} else {
urlPath = jenkins.parseJobPath(job) + "/buildWithParameters"
// Add remote trigger token to params
if jenkins.Token != "" {
if params == nil {
params = url.Values{}
}
params.Set("token", jenkins.Token)
}
log.Println(urlPath)
var urlPath string
// Check if params contains build parameters (excluding 'token')
hasBuildParams := false
for key := range params {
if key != "token" {
hasBuildParams = true
break
}
}
if hasBuildParams {
urlPath = jenkins.parseJobPath(job) + "/buildWithParameters"
} else {
urlPath = jenkins.parseJobPath(job) + "/build"
}
// All params (including token) are passed as query parameters
return jenkins.post(urlPath, params, nil)
}
+4 -4
View File
@@ -12,7 +12,7 @@ func TestParseJobPath(t *testing.T) {
Username: "appleboy",
Token: "1234",
}
jenkins := NewJenkins(auth, "http://example.com", false)
jenkins := NewJenkins(auth, "http://example.com", "", false)
assert.Equal(t, "/job/foo", jenkins.parseJobPath("/foo/"))
assert.Equal(t, "/job/foo", jenkins.parseJobPath("foo/"))
@@ -25,7 +25,7 @@ func TestUnSupportProtocol(t *testing.T) {
Username: "foo",
Token: "bar",
}
jenkins := NewJenkins(auth, "example.com", false)
jenkins := NewJenkins(auth, "example.com", "", false)
err := jenkins.trigger("drone-jenkins", nil)
assert.NotNil(t, err)
@@ -36,8 +36,8 @@ func TestTriggerBuild(t *testing.T) {
Username: "foo",
Token: "bar",
}
jenkins := NewJenkins(auth, "http://example.com", false)
jenkins := NewJenkins(auth, "http://example.com", "remote-token", false)
err := jenkins.trigger("drone-jenkins", url.Values{"token": []string{"bar"}})
err := jenkins.trigger("drone-jenkins", url.Values{"param": []string{"value"}})
assert.Nil(t, err)
}
+13 -7
View File
@@ -48,9 +48,14 @@ func main() {
&cli.StringFlag{
Name: "token",
Aliases: []string{"t"},
Usage: "jenkins token",
Usage: "jenkins API token for authentication",
EnvVars: []string{"PLUGIN_TOKEN", "JENKINS_TOKEN", "INPUT_TOKEN"},
},
&cli.StringFlag{
Name: "remote-token",
Usage: "jenkins remote trigger token",
EnvVars: []string{"PLUGIN_REMOTE_TOKEN", "JENKINS_REMOTE_TOKEN", "INPUT_REMOTE_TOKEN"},
},
&cli.StringSliceFlag{
Name: "job",
Aliases: []string{"j"},
@@ -112,12 +117,13 @@ REPOSITORY:
func run(c *cli.Context) error {
plugin := Plugin{
BaseURL: c.String("host"),
Username: c.String("user"),
Token: c.String("token"),
Job: c.StringSlice("job"),
Insecure: c.Bool("insecure"),
Parameters: c.StringSlice("parameters"),
BaseURL: c.String("host"),
Username: c.String("user"),
Token: c.String("token"),
RemoteToken: c.String("remote-token"),
Job: c.StringSlice("job"),
Insecure: c.Bool("insecure"),
Parameters: c.StringSlice("parameters"),
}
return plugin.Exec()
+8 -7
View File
@@ -10,12 +10,13 @@ import (
type (
// Plugin values.
Plugin struct {
BaseURL string
Username string
Token string
Job []string
Insecure bool
Parameters []string
BaseURL string
Username string
Token string
RemoteToken string
Job []string
Insecure bool
Parameters []string
}
)
@@ -50,7 +51,7 @@ func (p Plugin) Exec() error {
Token: p.Token,
}
jenkins := NewJenkins(auth, p.BaseURL, p.Insecure)
jenkins := NewJenkins(auth, p.BaseURL, p.RemoteToken, p.Insecure)
params := url.Values{}
for _, v := range p.Parameters {