Compare commits

...

11 Commits

Author SHA1 Message Date
Bo-Yi Wu 2e4860b70c ci(docker): fail push when trivy finds CRITICAL/HIGH issues 2026-04-16 23:01:05 +08:00
Bo-Yi Wu c885f9c805 ci: enable check-latest in docker and goreleaser workflows 2026-04-16 22:42:47 +08:00
Bo-Yi Wu 764f7b6bf6 fix: skip integration tests without telegram secrets; apply modernize fix 2026-04-16 22:39:41 +08:00
Bo-Yi Wu c8d19e8231 ci: enable check-latest for setup-go to fetch newest patch 2026-04-16 21:15:40 +08:00
Bo-Yi Wu c25c40af3b ci: pin golangci-lint to v2.11 2026-04-16 21:11:16 +08:00
Bo-Yi Wu e0116d31de ci: bump GitHub Actions and add Go 1.25/1.26 to test matrix 2026-04-16 21:03:25 +08:00
Bo-Yi Wu c2d73374b4 chore: bump go directive to 1.25.9 2026-04-16 20:57:55 +08:00
Bo-Yi Wu c773b54f0e ci: standardize Trivy security scanning workflows
- Add Trivy image scan job to trivy.yml alongside existing repo scan
- Add Trivy image scan step in docker.yml before pushing Docker image
- Add security-events permission for SARIF upload
2026-04-16 18:10:08 +08:00
Bo-Yi Wu 5d50e1e745 ci(actions): upgrade GitHub Actions to latest versions
- bump actions/checkout to v6
- bump actions/setup-go to v6
- bump actions/cache to v5
- bump goreleaser/goreleaser-action to v7
- bump golangci/golangci-lint-action to v9
- bump github/codeql-action/* to v4
- bump codecov/codecov-action to v5
- bump docker/build-push-action to v7
- bump docker/login-action to v4
- bump docker/metadata-action to v6
- bump docker/setup-buildx-action to v4
- bump docker/setup-qemu-action to v4
- bump hadolint/hadolint-action to v3.3.0
- bump aquasecurity/trivy-action to v0.35.0
2026-04-16 12:06:52 +08:00
appleboy f2a83d3d6c docs: document Jenkins authentication and CSRF protection methods
- Clarify and expand Jenkins authentication documentation, outlining API token, remote trigger token, and combined authentication options
- Add detailed explanations and requirements for working with CSRF protection in Jenkins, including error guidance
- Modify authentication requirements and usage examples to emphasize API token over remote trigger token, especially for secure Jenkins setups
- Introduce a Troubleshooting section with solutions for common Jenkins authentication errors (403, 401, remote token issues)
- Add corresponding CSRF protection notices and troubleshooting guidance to Chinese (Simplified and Traditional) documentation

Signed-off-by: appleboy <appleboy.tw@gmail.com>
2025-12-27 11:56:15 +08:00
Bo-Yi Wu 351ac33e2d feat: refactor authentication logic and broaden test coverage (#50)
* feat: refactor authentication logic and broaden test coverage

- Improve authentication checks to only require username and token when both are provided
- Update validation logic to allow either (username and token) or remote-token for authentication
- Enhance test coverage for various authentication scenarios
- Refine error messages to indicate a generic authentication requirement instead of specifying missing username or token

Signed-off-by: appleboy <appleboy.tw@gmail.com>

* style: streamline authentication error handling in config validation

- Simplify authentication error message in config validation

Signed-off-by: appleboy <appleboy.tw@gmail.com>

---------

Signed-off-by: appleboy <appleboy.tw@gmail.com>
2025-12-27 11:25:36 +08:00
12 changed files with 335 additions and 63 deletions
+36 -5
View File
@@ -10,6 +10,11 @@ on:
branches: branches:
- "master" - "master"
permissions:
contents: read
packages: write
security-events: write
jobs: jobs:
build-docker: build-docker:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -18,6 +23,7 @@ jobs:
uses: actions/setup-go@v6 uses: actions/setup-go@v6
with: with:
go-version: "^1" go-version: "^1"
check-latest: true
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v6
with: with:
@@ -29,13 +35,13 @@ jobs:
make build_linux_arm64 make build_linux_arm64
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v4
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
@@ -43,7 +49,7 @@ jobs:
- name: Docker meta - name: Docker meta
id: docker-meta id: docker-meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v6
with: with:
images: | images: |
ghcr.io/${{ github.repository }} ghcr.io/${{ github.repository }}
@@ -53,8 +59,33 @@ jobs:
type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}} type=semver,pattern={{major}}
- name: Build image for scanning
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64
push: false
load: true
tags: drone-jenkins:scan
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@v0.35.0
with:
image-ref: "drone-jenkins:scan"
format: "sarif"
output: "trivy-image-results.sarif"
severity: "CRITICAL,HIGH"
exit-code: '1'
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v4
if: always()
with:
sarif_file: "trivy-image-results.sarif"
category: "trivy-docker-image"
- name: Build and push - name: Build and push
uses: docker/build-push-action@v6 uses: docker/build-push-action@v7
with: with:
context: . context: .
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
check-latest: true check-latest: true
- name: Run GoReleaser - name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6 uses: goreleaser/goreleaser-action@v7
with: with:
# either 'goreleaser' (default) or 'goreleaser-pro' # either 'goreleaser' (default) or 'goreleaser-pro'
distribution: goreleaser distribution: goreleaser
+4 -2
View File
@@ -12,13 +12,14 @@ jobs:
uses: actions/setup-go@v6 uses: actions/setup-go@v6
with: with:
go-version: "stable" go-version: "stable"
check-latest: true
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v6
- name: Setup golangci-lint - name: Setup golangci-lint
uses: golangci/golangci-lint-action@v9 uses: golangci/golangci-lint-action@v9
with: with:
version: v2.6 version: v2.11
args: --verbose args: --verbose
- uses: hadolint/hadolint-action@v3.3.0 - uses: hadolint/hadolint-action@v3.3.0
@@ -30,7 +31,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest] os: [ubuntu-latest]
go: ["1.25"] go: ["1.25", "1.26"]
include: include:
- os: ubuntu-latest - os: ubuntu-latest
go-build: ~/.cache/go-build go-build: ~/.cache/go-build
@@ -44,6 +45,7 @@ jobs:
uses: actions/setup-go@v6 uses: actions/setup-go@v6
with: with:
go-version: ${{ matrix.go }} go-version: ${{ matrix.go }}
check-latest: true
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v6 uses: actions/checkout@v6
+53 -24
View File
@@ -10,47 +10,76 @@ on:
schedule: schedule:
# Run daily at 00:00 UTC # Run daily at 00:00 UTC
- cron: "0 0 * * *" - cron: "0 0 * * *"
workflow_dispatch: # Allow manual trigger workflow_dispatch:
permissions: permissions:
contents: read contents: read
security-events: write # Required for uploading SARIF results security-events: write
jobs: jobs:
trivy-scan: trivy-repo-scan:
name: Trivy Security Scan name: Trivy Repository Scan
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Run Trivy vulnerability scanner (source code) - name: Run Trivy vulnerability scanner (repo)
uses: aquasecurity/trivy-action@0.33.1 uses: aquasecurity/trivy-action@v0.35.0
with: with:
scan-type: "fs" scan-type: "fs"
scan-ref: "." scan-ref: "."
scanners: "vuln,secret,misconfig"
format: "sarif" format: "sarif"
output: "trivy-results.sarif" output: "trivy-repo-results.sarif"
severity: "CRITICAL,HIGH,MEDIUM" severity: "CRITICAL,HIGH"
ignore-unfixed: true
- name: Upload Trivy results to GitHub Security tab - name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v4 uses: github/codeql-action/upload-sarif@v4
if: always() if: always()
with: with:
sarif_file: "trivy-results.sarif" sarif_file: "trivy-repo-results.sarif"
- name: Run Trivy scanner (table output for logs) trivy-image-scan:
uses: aquasecurity/trivy-action@0.33.1 name: Trivy Image Scan
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Setup go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
check-latest: true
- name: Build binary
run: |
make build_linux_amd64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build Docker image for scanning
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64
push: false
load: true
tags: drone-jenkins:scan
- name: Run Trivy vulnerability scanner (image)
uses: aquasecurity/trivy-action@v0.35.0
with:
image-ref: "drone-jenkins:scan"
format: "sarif"
output: "trivy-image-results.sarif"
severity: "CRITICAL,HIGH"
- name: Upload Trivy image scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v4
if: always() if: always()
with: with:
scan-type: "fs" sarif_file: "trivy-image-results.sarif"
scan-ref: "." category: "trivy-image"
scanners: "vuln,secret,misconfig"
format: "table"
severity: "CRITICAL,HIGH,MEDIUM"
ignore-unfixed: true
exit-code: "1"
+145 -6
View File
@@ -46,10 +46,16 @@ Whether you're managing a hybrid CI/CD environment or orchestrating complex mult
- [Configuration](#configuration) - [Configuration](#configuration)
- [Jenkins Server Setup](#jenkins-server-setup) - [Jenkins Server Setup](#jenkins-server-setup)
- [Authentication](#authentication) - [Authentication](#authentication)
- [Understanding Jenkins Authentication](#understanding-jenkins-authentication)
- [CSRF Protection Notice](#csrf-protection-notice)
- [Parameters Reference](#parameters-reference) - [Parameters Reference](#parameters-reference)
- [Usage](#usage) - [Usage](#usage)
- [Command Line](#command-line) - [Command Line](#command-line)
- [Docker](#docker) - [Docker](#docker)
- [Troubleshooting](#troubleshooting)
- [Error: 403 No valid crumb was included in the request](#error-403-no-valid-crumb-was-included-in-the-request)
- [Error: 401 Unauthorized](#error-401-unauthorized)
- [Remote Token Not Working](#remote-token-not-working)
- [Development](#development) - [Development](#development)
- [Building](#building) - [Building](#building)
- [Testing](#testing) - [Testing](#testing)
@@ -125,7 +131,20 @@ docker run -d -v jenkins_home:/var/jenkins_home -p 8080:8080 -p 50000:50000 --re
### Authentication ### Authentication
Jenkins API tokens are recommended for authentication. To create an API token: #### Understanding Jenkins Authentication
Jenkins supports multiple authentication methods for triggering builds. This tool supports two approaches:
**1. API Token Authentication (Recommended)**
Use Jenkins user credentials with an API token. This method:
- ✅ Works with all Jenkins configurations
- ✅ Supports CSRF protection (enabled by default in modern Jenkins)
- ✅ Supports wait mode to monitor build completion
- ✅ Provides full access to Jenkins API features
To create an API token:
1. Log into Jenkins 1. Log into Jenkins
2. Click on your username (top right) 2. Click on your username (top right)
@@ -136,7 +155,42 @@ Jenkins API tokens are recommended for authentication. To create an API token:
![personal token](./images/personal-token.png) ![personal token](./images/personal-token.png)
Alternatively, you can use a remote trigger token configured in your Jenkins job settings. **2. Remote Trigger Token Authentication**
Use a remote trigger token configured in your Jenkins job. **Important limitations**:
- ⚠️ **Does not work** with Jenkins CSRF protection enabled (default in modern Jenkins)
- ⚠️ Requires anonymous users to have read access to the job, OR
- ⚠️ Must be combined with API token authentication (see Combined Authentication below)
**3. Combined Authentication (Recommended for Remote Tokens)**
Use both API token and remote trigger token together:
- ✅ Works with CSRF protection enabled
- ✅ Provides double authentication security
- ✅ Supports all features including wait mode
```bash
drone-jenkins \
--host http://jenkins.example.com/ \
--user YOUR_USERNAME \
--token YOUR_API_TOKEN \
--remote-token YOUR_REMOTE_TOKEN \
--job my-jenkins-job
```
#### CSRF Protection Notice
Modern Jenkins installations have CSRF protection enabled by default. If you encounter errors like:
```txt
Error 403 No valid crumb was included in the request
```
This means your Jenkins has CSRF protection enabled. You **must** use API token authentication (option 1 or 3 above). Remote trigger token alone will not work.
For more information about Jenkins CSRF protection, see the [official Jenkins documentation](https://www.jenkins.io/doc/book/security/csrf-protection/).
### Parameters Reference ### Parameters Reference
@@ -155,10 +209,19 @@ Alternatively, you can use a remote trigger token configured in your Jenkins job
| Timeout | `--timeout` | `PLUGIN_TIMEOUT`, `JENKINS_TIMEOUT` | No | Maximum time to wait for job completion (default: 30m) | | Timeout | `--timeout` | `PLUGIN_TIMEOUT`, `JENKINS_TIMEOUT` | No | Maximum time to wait for job completion (default: 30m) |
| Debug | `--debug` | `PLUGIN_DEBUG`, `JENKINS_DEBUG` | No | Enable debug mode to show detailed parameter information (default: false) | | Debug | `--debug` | `PLUGIN_DEBUG`, `JENKINS_DEBUG` | No | Enable debug mode to show detailed parameter information (default: false) |
**Authentication Requirements**: You must provide either: **Authentication Requirements**:
- `user` + `token` (API token authentication), OR For Jenkins with **CSRF protection enabled** (default in modern Jenkins):
- `remote-token` (remote trigger token authentication)
- **Required**: `user` + `token` (API token authentication)
- **Optional**: `remote-token` (for additional security)
For Jenkins with **CSRF protection disabled** (not recommended):
- **Option 1**: `user` + `token` (API token authentication)
- **Option 2**: `remote-token` only (requires anonymous read access to job)
**Important**: If you encounter "403 No valid crumb" errors, you must use API token authentication (`user` + `token`).
**Parameters Format**: The `parameters` field accepts a multi-line string where each line contains one `key=value` pair: **Parameters Format**: The `parameters` field accepts a multi-line string where each line contains one `key=value` pair:
@@ -220,9 +283,22 @@ drone-jenkins \
--job my-jenkins-job --job my-jenkins-job
``` ```
**Using remote token authentication:** **Using combined authentication (API token + remote token - Recommended):**
```bash ```bash
drone-jenkins \
--host http://jenkins.example.com/ \
--user appleboy \
--token XXXXXXXX \
--remote-token REMOTE_TOKEN_HERE \
--job my-jenkins-job
```
**Using remote token only (only works without CSRF protection):**
```bash
# Note: This will fail if Jenkins has CSRF protection enabled
# You will get "403 No valid crumb" error
drone-jenkins \ drone-jenkins \
--host http://jenkins.example.com/ \ --host http://jenkins.example.com/ \
--remote-token REMOTE_TOKEN_HERE \ --remote-token REMOTE_TOKEN_HERE \
@@ -309,6 +385,18 @@ docker run --rm \
ghcr.io/appleboy/drone-jenkins ghcr.io/appleboy/drone-jenkins
``` ```
**With combined authentication (API token + remote token):**
```bash
docker run --rm \
-e JENKINS_URL=http://jenkins.example.com/ \
-e JENKINS_USER=appleboy \
-e JENKINS_TOKEN=xxxxxxx \
-e JENKINS_REMOTE_TOKEN=your_remote_token \
-e JENKINS_JOB=my-jenkins-job \
ghcr.io/appleboy/drone-jenkins
```
**Wait for job completion:** **Wait for job completion:**
```bash ```bash
@@ -360,6 +448,57 @@ docker run --rm \
For more detailed examples and advanced configurations, see [DOCS.md](DOCS.md). For more detailed examples and advanced configurations, see [DOCS.md](DOCS.md).
## Troubleshooting
### Error: 403 No valid crumb was included in the request
**Cause**: Your Jenkins server has CSRF protection enabled (this is the default in modern Jenkins). Learn more at the [Jenkins CSRF Protection documentation](https://www.jenkins.io/doc/book/security/csrf-protection/).
**Solution**: Use API token authentication instead of remote token only:
```bash
# ❌ This will fail with CSRF protection enabled
drone-jenkins \
--host http://jenkins.example.com/ \
--remote-token YOUR_REMOTE_TOKEN \
--job my-jenkins-job
# ✅ Use this instead
drone-jenkins \
--host http://jenkins.example.com/ \
--user YOUR_USERNAME \
--token YOUR_API_TOKEN \
--job my-jenkins-job
# ✅ Or combine both for additional security
drone-jenkins \
--host http://jenkins.example.com/ \
--user YOUR_USERNAME \
--token YOUR_API_TOKEN \
--remote-token YOUR_REMOTE_TOKEN \
--job my-jenkins-job
```
### Error: 401 Unauthorized
**Cause**: Invalid credentials or incorrect authentication method.
**Solutions**:
1. Verify your username and API token are correct
2. Ensure you're using an API token, not your Jenkins password
3. Check if you have permission to trigger the job
4. Make sure both `--user` and `--token` are provided together
### Remote Token Not Working
**Cause**: Remote trigger tokens alone only work in specific scenarios:
- Jenkins has CSRF protection disabled (not recommended), AND
- Anonymous users have read access to the job
**Solution**: Use combined authentication (API token + remote token) as shown in the examples above.
## Development ## Development
### Building ### Building
+13
View File
@@ -46,6 +46,7 @@
- [配置](#配置) - [配置](#配置)
- [Jenkins 服务器设置](#jenkins-服务器设置) - [Jenkins 服务器设置](#jenkins-服务器设置)
- [认证](#认证) - [认证](#认证)
- [CSRF 保护注意事项](#csrf-保护注意事项)
- [参数参考](#参数参考) - [参数参考](#参数参考)
- [使用方式](#使用方式) - [使用方式](#使用方式)
- [命令行](#命令行) - [命令行](#命令行)
@@ -138,6 +139,18 @@ docker run -d -v jenkins_home:/var/jenkins_home -p 8080:8080 -p 50000:50000 --re
或者,您可以使用在 Jenkins 任务设置中配置的远程触发令牌。 或者,您可以使用在 Jenkins 任务设置中配置的远程触发令牌。
#### CSRF 保护注意事项
现代 Jenkins 安装默认启用 CSRF 保护。如果您遇到以下错误:
```
Error 403 No valid crumb was included in the request
```
这表示您的 Jenkins 已启用 CSRF 保护。您**必须**使用 API 令牌认证(user + token)。单独使用远程触发令牌将无法工作。
如需更多关于 Jenkins CSRF 保护的信息,请参阅 [Jenkins 官方文档](https://www.jenkins.io/doc/book/security/csrf-protection/)。
### 参数参考 ### 参数参考
| 参数 | CLI 标志 | 环境变量 | 必需 | 说明 | | 参数 | CLI 标志 | 环境变量 | 必需 | 说明 |
+13
View File
@@ -46,6 +46,7 @@
- [設定](#設定) - [設定](#設定)
- [Jenkins 伺服器設定](#jenkins-伺服器設定) - [Jenkins 伺服器設定](#jenkins-伺服器設定)
- [認證](#認證) - [認證](#認證)
- [CSRF 保護注意事項](#csrf-保護注意事項)
- [參數參考](#參數參考) - [參數參考](#參數參考)
- [使用方式](#使用方式) - [使用方式](#使用方式)
- [命令列](#命令列) - [命令列](#命令列)
@@ -138,6 +139,18 @@ docker run -d -v jenkins_home:/var/jenkins_home -p 8080:8080 -p 50000:50000 --re
或者,您可以使用在 Jenkins 任務設定中配置的遠端觸發令牌。 或者,您可以使用在 Jenkins 任務設定中配置的遠端觸發令牌。
#### CSRF 保護注意事項
現代 Jenkins 安裝預設啟用 CSRF 保護。如果您遇到以下錯誤:
```
Error 403 No valid crumb was included in the request
```
這表示您的 Jenkins 已啟用 CSRF 保護。您**必須**使用 API 令牌認證(user + token)。單獨使用遠端觸發令牌將無法運作。
如需更多關於 Jenkins CSRF 保護的資訊,請參閱 [Jenkins 官方文件](https://www.jenkins.io/doc/book/security/csrf-protection/)。
### 參數參考 ### 參數參考
| 參數 | CLI 旗標 | 環境變數 | 必要 | 說明 | | 參數 | CLI 旗標 | 環境變數 | 必要 | 說明 |
+1 -1
View File
@@ -1,6 +1,6 @@
module github.com/appleboy/drone-jenkins module github.com/appleboy/drone-jenkins
go 1.24.0 go 1.25.9
require ( require (
github.com/appleboy/com v1.1.1 github.com/appleboy/com v1.1.1
+10 -6
View File
@@ -224,7 +224,7 @@ func (jenkins *Jenkins) sendRequest(
req *http.Request, req *http.Request,
crumb *CrumbResponse, crumb *CrumbResponse,
) (*http.Response, error) { ) (*http.Response, error) {
if jenkins.Auth != nil { if jenkins.Auth != nil && jenkins.Auth.Username != "" && jenkins.Auth.Token != "" {
req.SetBasicAuth(jenkins.Auth.Username, jenkins.Auth.Token) req.SetBasicAuth(jenkins.Auth.Username, jenkins.Auth.Token)
} }
@@ -233,7 +233,7 @@ func (jenkins *Jenkins) sendRequest(
req.Header.Set(crumb.CrumbRequestField, crumb.Crumb) req.Header.Set(crumb.CrumbRequestField, crumb.Crumb)
} }
return jenkins.Client.Do(req) return jenkins.Client.Do(req) //nolint:gosec // user-configured Jenkins URL
} }
func (jenkins *Jenkins) get( func (jenkins *Jenkins) get(
@@ -277,10 +277,14 @@ func (jenkins *Jenkins) postAndGetLocation(
path string, path string,
params url.Values, params url.Values,
) (int, error) { ) (int, error) {
// Fetch CSRF crumb before POST request // Fetch CSRF crumb before POST request (only if authenticated)
crumb, err := jenkins.getCrumb(ctx) var crumb *CrumbResponse
if err != nil { if jenkins.Auth != nil && jenkins.Auth.Username != "" && jenkins.Auth.Token != "" {
return 0, fmt.Errorf("failed to get crumb: %w", err) var err error
crumb, err = jenkins.getCrumb(ctx)
if err != nil {
return 0, fmt.Errorf("failed to get crumb: %w", err)
}
} }
requestURL := jenkins.buildURL(path, params) requestURL := jenkins.buildURL(path, params)
+10 -2
View File
@@ -347,7 +347,11 @@ func TestWaitForCompletion(t *testing.T) {
[]byte(`{"number":456,"building":true,"duration":0,"result":null}`), []byte(`{"number":456,"building":true,"duration":0,"result":null}`),
) )
} else { } else {
_, _ = w.Write([]byte(`{"number":456,"building":false,"duration":5000,"result":"SUCCESS"}`)) _, _ = w.Write(
[]byte(
`{"number":456,"building":false,"duration":5000,"result":"SUCCESS"}`,
),
)
} }
} }
})) }))
@@ -470,7 +474,11 @@ func TestWaitForCompletion(t *testing.T) {
[]byte(`{"number":456,"building":true,"duration":0,"result":null}`), []byte(`{"number":456,"building":true,"duration":0,"result":null}`),
) )
} else { } else {
_, _ = w.Write([]byte(`{"number":456,"building":false,"duration":3000,"result":"FAILURE"}`)) _, _ = w.Write(
[]byte(
`{"number":456,"building":false,"duration":3000,"result":"FAILURE"}`,
),
)
} }
} }
})) }))
+15 -9
View File
@@ -88,12 +88,15 @@ func (p Plugin) validateConfig() error {
if p.BaseURL == "" { if p.BaseURL == "" {
return errors.New("jenkins base URL is required") return errors.New("jenkins base URL is required")
} }
if p.Username == "" {
return errors.New("jenkins username is required") // Validate authentication: either (user + token) or remote-token must be provided
} hasUserAuth := p.Username != "" && p.Token != ""
if p.Token == "" { hasRemoteToken := p.RemoteToken != ""
return errors.New("jenkins API token is required")
if !hasUserAuth && !hasRemoteToken {
return errors.New("authentication required")
} }
return nil return nil
} }
@@ -113,10 +116,13 @@ func (p Plugin) Exec(ctx context.Context) error {
return errors.New("at least one Jenkins job name is required") return errors.New("at least one Jenkins job name is required")
} }
// Set up authentication // Set up authentication (only if username and token are provided)
auth := &Auth{ var auth *Auth
Username: p.Username, if p.Username != "" && p.Token != "" {
Token: p.Token, auth = &Auth{
Username: p.Username,
Token: p.Token,
}
} }
// Initialize Jenkins client // Initialize Jenkins client
+34 -7
View File
@@ -32,24 +32,33 @@ func TestValidateConfig(t *testing.T) {
errorMsg: "jenkins base URL is required", errorMsg: "jenkins base URL is required",
}, },
{ {
name: "missing username and token", name: "missing authentication",
plugin: Plugin{ plugin: Plugin{
BaseURL: "http://example.com", BaseURL: "http://example.com",
}, },
wantError: true, wantError: true,
errorMsg: "jenkins username is required", errorMsg: "authentication required",
}, },
{ {
name: "missing token", name: "missing token (only username)",
plugin: Plugin{ plugin: Plugin{
BaseURL: "http://example.com", BaseURL: "http://example.com",
Username: "foo", Username: "foo",
}, },
wantError: true, wantError: true,
errorMsg: "jenkins API token is required", errorMsg: "authentication required",
}, },
{ {
name: "all required config present", name: "missing username (only token)",
plugin: Plugin{
BaseURL: "http://example.com",
Token: "bar",
},
wantError: true,
errorMsg: "authentication required",
},
{
name: "user and token auth",
plugin: Plugin{ plugin: Plugin{
BaseURL: "http://example.com", BaseURL: "http://example.com",
Username: "foo", Username: "foo",
@@ -57,6 +66,24 @@ func TestValidateConfig(t *testing.T) {
}, },
wantError: false, wantError: false,
}, },
{
name: "remote token auth",
plugin: Plugin{
BaseURL: "http://example.com",
RemoteToken: "remote-token-123",
},
wantError: false,
},
{
name: "both auth methods",
plugin: Plugin{
BaseURL: "http://example.com",
Username: "foo",
Token: "bar",
RemoteToken: "remote-token-123",
},
wantError: false,
},
} }
for _, tt := range tests { for _, tt := range tests {
@@ -232,7 +259,7 @@ func TestExecMissingJenkinsUsername(t *testing.T) {
assert.Error(t, err) assert.Error(t, err)
assert.Contains(t, err.Error(), "configuration error") assert.Contains(t, err.Error(), "configuration error")
assert.Contains(t, err.Error(), "jenkins username is required") assert.Contains(t, err.Error(), "authentication required")
} }
// TestExecMissingJenkinsToken tests Exec with missing token // TestExecMissingJenkinsToken tests Exec with missing token
@@ -246,7 +273,7 @@ func TestExecMissingJenkinsToken(t *testing.T) {
assert.Error(t, err) assert.Error(t, err)
assert.Contains(t, err.Error(), "configuration error") assert.Contains(t, err.Error(), "configuration error")
assert.Contains(t, err.Error(), "jenkins API token is required") assert.Contains(t, err.Error(), "authentication required")
} }
// TestExecMissingJenkinsJob tests Exec with missing or empty job list // TestExecMissingJenkinsJob tests Exec with missing or empty job list