Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use when managing credentials in KubeSphere DevOps, including repository credentials, kubeconfig, and API tokens
.claude/skills/kubesphere-kubesphere-devops-credentials/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 374% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 177% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 384% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 527% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 423% | 0% |
Credentials in KubeSphere DevOps are Kubernetes Secrets with specific labels and annotations. They are synced to Jenkins for use in pipelines. Supported types include SSH keys, username/password, and secret tokens.
| Type | Use Case | Secret Key | |------|----------|------------| | SSH | Git repositories | username, privatekey | | Basic | Username/password | username, password | | Secret | API tokens, secrets | secret | | Kubeconfig | Kubernetes clusters | kubeconfig (v1.1.x only) | | SSH Username/Pass | Git with user/pass | username, password | | String | Generic text/tokens | secret |
Credentials are stored as Kubernetes Secrets with DevOps labels:
yamlapiVersion: v1 kind: Secret metadata: name: my-credential namespace: project-xxx # DevOps project namespace labels: devops.kubesphere.io/credential: "true" annotations: credential.devops.kubesphere.io/syncstatus: successful credential.devops.kubesphere.io/type: ssh|basic-auth|secret-text stringData: username: git-user privatekey: | -----BEGIN OPENSSH PRIVATE KEY----- ... -----END OPENSSH PRIVATE KEY----- type: credential.devops.kubesphere.io/ssh # CRITICAL: Must use credential.devops.kubesphere.io/* type, NOT Opaque!
⚠️ CRITICAL: Secret Type Must Be credential.devops.kubesphere.io/*
The type field must be one of:
credential.devops.kubesphere.io/basic-authcredential.devops.kubesphere.io/ssh-authcredential.devops.kubesphere.io/secret-textcredential.devops.kubesphere.io/kubeconfigUsing type: Opaque will result in:
Controller Logic: The credential controller only watches secrets with types starting with credential.devops.kubesphere.io/ (see devopscredential_controller.go line 102). Secrets with type: Opaque are completely ignored.
| Operation | Method | Endpoint | |-----------|--------|----------| | List Credentials | GET | /kapis/devops.kubesphere.io/v1alpha3/namespaces/{devops}/credentials | | Create Credential | POST | /kapis/devops.kubesphere.io/v1alpha3/namespaces/{devops}/credentials | | Get Credential | GET | /kapis/devops.kubesphere.io/v1alpha3/namespaces/{devops}/credentials/{credential} | | Update Credential | PUT | /kapis/devops.kubesphere.io/v1alpha3/namespaces/{devops}/credentials/{credential} | | Delete Credential | DELETE | /kapis/devops.kubesphere.io/v1alpha3/namespaces/{devops}/credentials/{credential} | | Get Usage | GET | /kapis/devops.kubesphere.io/v1alpha2/namespaces/{devops}/credentials/{credential}/usage |
bashcurl "https://kubesphere-api/kapis/devops.kubesphere.io/v1alpha3/namespaces/{devops}/credentials" \ -H "Authorization: Bearer $TOKEN"
bashcurl -X POST "https://kubesphere-api/kapis/devops.kubesphere.io/v1alpha3/namespaces/{devops}/credentials" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "apiVersion": "v1", "kind": "Secret", "metadata": { "name": "github-ssh-key", "annotations": { "credential.devops.kubesphere.io/type": "ssh" } }, "stringData": { "username": "git", "privatekey": "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----" }, "type": "credential.devops.kubesphere.io/ssh-auth" }'
bashcurl -X POST "https://kubesphere-api/kapis/devops.kubesphere.io/v1alpha3/namespaces/{devops}/credentials" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "apiVersion": "v1", "kind": "Secret", "metadata": { "name": "docker-registry", "annotations": { "credential.devops.kubesphere.io/type": "basic-auth" } }, "stringData": { "username": "docker-user", "password": "docker-password" }, "type": "credential.devops.kubesphere.io/basic-auth" }'
Best Practice: Use basic-auth type for Git access tokens:
bash# For GitHub/GitLab access tokens curl -X POST "https://kubesphere-api/kapis/devops.kubesphere.io/v1alpha3/namespaces/{devops}/credentials" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "apiVersion": "v1", "kind": "Secret", "metadata": { "name": "github-token", "annotations": { "credential.devops.kubesphere.io/type": "basic-auth" } }, "stringData": { "username": "git", # Can be any value for token auth "password": "ghp_xxxxxxxxxx" # Your GitHub/GitLab access token }, "type": "credential.devops.kubesphere.io/basic-auth" }'
Why basic-auth for tokens?
Supported Git Providers:
ghp_xxxxxxxxxxxxglpat-xxxxxxxxxxbashcurl -X POST "https://kubesphere-api/kapis/devops.kubesphere.io/v1alpha3/namespaces/{devops}/credentials" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "apiVersion": "v1", "kind": "Secret", "metadata": { "name": "api-token", "annotations": { "credential.devops.kubesphere.io/type": "secret-text" } }, "stringData": { "secret": "my-api-token-value" }, "type": "credential.devops.kubesphere.io/secret-text" }'
groovypipeline { agent any stages { stage('Checkout') { steps { git credentialsId: 'github-ssh-key', url: 'git@github.com:org/repo.git' } } } }
groovypipeline { agent any stages { stage('Deploy') { steps { withCredentials([ usernamePassword( credentialsId: 'docker-registry', usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS' ) ]) { sh 'echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin' } } } } }
groovypipeline { agent any stages { stage('Deploy to K8s') { steps { withCredentials([string(credentialsId: 'my-kubeconfig', variable: 'KUBECONFIG_DATA')]) { sh ''' printf "%s" "$KUBECONFIG_DATA" > kubeconfig kubectl --kubeconfig=kubeconfig apply -f deployment.yaml ''' } } } } }
GitRepository connects a Git repository with a credential for use in pipelines and ArgoCD applications.
yamlapiVersion: devops.kubesphere.io/v1alpha3 kind: GitRepository metadata: name: my-repo namespace: demo-project spec: url: https://github.com/example/repo.git provider: github # Git provider: github, gitlab, bitbucket, etc. secret: # Reference to credential secret name: github-token namespace: demo-project description: "Main application repository"
Required Fields:
spec.url: Repository URLspec.provider: Git provider type (github, gitlab, bitbucket, gitea, etc.)spec.secret.name: Name of the credential secretspec.secret.namespace: Namespace of the credential secretVia API:
bashcurl -X POST "https://kubesphere-api/kapis/devops.kubesphere.io/v1alpha3/namespaces/{devops}/gitrepositories" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "apiVersion": "devops.kubesphere.io/v1alpha3", "kind": "GitRepository", "metadata": { "name": "demo-jenkinsfiles", "namespace": "demo-project" }, "spec": { "url": "https://github.com/stoneshi-yunify/argocd-example-apps.git", "provider": "github", "secret": { "name": "github-token", "namespace": "demo-project" }, "description": "Demo repository with examples" } }'
Via kubectl:
bashcat <<EOF | kubectl apply -f - apiVersion: devops.kubesphere.io/v1alpha3 kind: GitRepository metadata: name: my-application-repo namespace: demo-project spec: url: https://github.com/example/my-app.git provider: github secret: name: github-token namespace: demo-project description: "Application source code" EOF
bash# Via API curl "https://kubesphere-api/kapis/devops.kubesphere.io/v1alpha3/namespaces/{devops}/gitrepositories" \ -H "Authorization: Bearer $TOKEN" | jq '.items[].metadata.name' # Via kubectl kubectl get gitrepositories -n demo-project
Complete workflow for private Git repository:
yaml# Step 1: Create credential for Git access apiVersion: v1 kind: Secret metadata: name: github-token namespace: demo-project annotations: credential.devops.kubesphere.io/type: basic-auth stringData: username: "git" password: "ghp_xxxxxxxxxxxxxxxxxxxx" type: credential.devops.kubesphere.io/basic-auth --- # Step 2: Create GitRepository linking repo + credential apiVersion: devops.kubesphere.io/v1alpha3 kind: GitRepository metadata: name: my-app-repo namespace: demo-project spec: url: https://github.com/org/my-app.git provider: github secret: name: github-token namespace: demo-project description: "Application source code" --- # Step 3: Create multi-branch pipeline using GitRepository apiVersion: devops.kubesphere.io/v1alpha3 kind: Pipeline metadata: name: my-multibranch-pipeline namespace: demo-project spec: type: multi-branch-pipeline multi_branch_pipeline: name: my-multibranch-pipeline source_type: git git_source: url: https://github.com/org/my-app.git credential_id: github-token # Reference credential directly discover_branches: true script_path: Jenkinsfile
yaml# Step 1: Create credential (basic-auth for token) apiVersion: v1 kind: Secret metadata: name: github-token namespace: demo-project annotations: credential.devops.kubesphere.io/type: basic-auth stringData: username: "git" password: "ghp_xxxxxxxxxxxxxxxxxxxx" --- # Step 2: Create GitRepository apiVersion: devops.kubesphere.io/v1alpha3 kind: GitRepository metadata: name: argo-manifests namespace: demo-project spec: url: https://github.com/org/k8s-manifests.git credentialId: github-token --- # Step 3: Create ArgoCD Application referencing the repository apiVersion: gitops.kubesphere.io/v1alpha1 kind: Application metadata: name: my-app namespace: demo-project spec: argoApp: spec: source: repoURL: https://github.com/org/k8s-manifests.git targetRevision: HEAD path: overlays/production destination: server: https://kubernetes.default.svc namespace: demo-project syncPolicy: automated: prune: true selfHeal: true
bash#!/bin/bash set -e export KUBESPHERE_API="https://kubesphere-api.example.com" export API_TOKEN="<tenant-token>" export DEVOPS_PROJECT="demo-project" # 1. Create credential for GitHub access echo "Creating GitHub credential..." curl -s -X POST "${KUBESPHERE_API}/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/credentials" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "apiVersion": "v1", "kind": "Secret", "metadata": { "name": "github-token", "annotations": { "credential.devops.kubesphere.io/type": "basic-auth" } }, "stringData": { "username": "git", "password": "'${GITHUB_TOKEN}'" }, "type": "credential.devops.kubesphere.io/basic-auth" }' | jq -r '.metadata.name' # 2. Create GitRepository echo "Creating GitRepository..." curl -s -X POST "${KUBESPHERE_API}/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/gitrepositories" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "apiVersion": "devops.kubesphere.io/v1alpha3", "kind": "GitRepository", "metadata": { "name": "my-repo", "namespace": "'${DEVOPS_PROJECT}'" }, "spec": { "url": "https://github.com/'${GITHUB_OWNER}'/'${GITHUB_REPO}'.git", "credentialId": "github-token", "description": "Application repository" } }' | jq -r '.metadata.name' # 3. Create multi-branch pipeline using the repository echo "Creating multi-branch pipeline..." curl -s -X POST "${KUBESPHERE_API}/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelines" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "apiVersion": "devops.kubesphere.io/v1alpha3", "kind": "Pipeline", "metadata": { "name": "my-app-pipeline", "namespace": "'${DEVOPS_PROJECT}'" }, "spec": { "type": "multi-branch-pipeline", "multi_branch_pipeline": { "name": "my-app-pipeline", "source_type": "git", "git_source": { "url": "https://github.com/'${GITHUB_OWNER}'/'${GITHUB_REPO}'.git", "credential_id": "github-token", "discover_branches": true, "discover_tags": false }, "script_path": "Jenkinsfile" } } }' | jq -r '.metadata.name' echo "Setup complete!"
Credentials are synced from Kubernetes to Jenkins. Check sync status:
bash# Check credential annotation kubectl -n <devops-project> get secret <credential-name> -o jsonpath='{.metadata.annotations.credential\.devops\.kubesphere\.io/syncstatus}' # Force resync all credentials kubectl get namespaces -l kubesphere.io/devopsproject,devops.kubesphere.io/managed=true --no-headers | \ awk '{print $1}' | \ xargs -I{} kubectl annotate secrets credential.devops.kubesphere.io/syncstatus- --all -n {}
| Mistake | Fix | |---------|-----| | kubeconfigContent not working (v1.2+) | Use string type with withCredentials | | Credential not appearing in Jenkins | Check syncstatus annotation | | SSH auth fails | Ensure username is correct (usually "git") | | Secret not found in pipeline | Verify credentialsId matches exactly |
| Using wrong credential type for Git tokens | Use basic-auth not secret-text for Git access tokens | | GitRepository credential not found | Ensure credential exists before creating GitRepository | | Git clone fails with 401/403 | Check token hasn't expired and has required permissions | | ArgoCD cannot access private repo | Verify GitRepository credentialId matches credential name | | Cannot delete credential | Check if used by pipelines (use /usage endpoint) |
Removed: kubernetes-cd plugin and kubeconfigContent credential type
Migration:
groovy// v1.1.x (OLD) withCredentials([kubeconfigContent(credentialsId: 'my-kubeconfig', variable: 'KUBECONFIG_DATA')]) { sh 'kubectl --kubeconfig=kubeconfig get node' } // v1.2.x (NEW) withCredentials([string(credentialsId: 'my-kubeconfig', variable: 'KUBECONFIG_DATA')]) { sh 'printf "%s" "$KUBECONFIG_DATA" > kubeconfig && kubectl --kubeconfig=kubeconfig get node' }
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 6,353 | 4,968 | -22% | 1 | 1 | 0% | 1,264 | 5,995 | +374% | 0 | 0 | — |
case-02 | fail→pass | 12,988 | 9,616 | -26% | 1 | 1 | 0% | 2,462 | 6,826 | +177% | 0 | 0 | — |
case-03 | fail→pass | 5,897 | 4,757 | -19% | 1 | 1 | 0% | 1,241 | 6,010 | +384% | 0 | 0 | — |
case-04 | fail→pass | 5,114 | 4,756 | -7% | 1 | 1 | 0% | 938 | 5,882 | +527% | 0 | 0 | — |
case-05 | fail→pass | 16,228 | 4,912 | -70% | 1 | 1 | 0% | 1,145 | 5,983 | +423% | 0 | 0 | — |
case-06 | fail→pass | 8,009 | 5,339 | -33% | 1 | 1 | 0% | 1,368 | 6,125 | +348% | 0 | 0 | — |
case-07 | fail→pass | 9,807 | 4,710 | -52% | 1 | 1 | 0% | 1,893 | 5,872 | +210% | 0 | 0 | — |
case-08 | pass→pass | 4,175 | 2,707 | -35% | 1 | 1 | 0% | 832 | 5,576 | +570% | 0 | 0 | — |
case-09 | pass→pass | 35,199 | 17,546 | -50% | 1 | 1 | 0% | 1,394 | 5,456 | +291% | 0 | 0 | — |
case-10 | pass→pass | 9,552 | 3,259 | -66% | 1 | 1 | 0% | 2,046 | 5,590 | +173% | 0 | 0 | — |
case-11 | fail→pass | 22,233 | 5,780 | -74% | 1 | 1 | 0% | 2,473 | 5,605 | +127% | 0 | 0 | — |
case-12 | pass→pass | 10,045 | 8,038 | -20% | 1 | 1 | 0% | 1,799 | 5,853 | +225% | 0 | 0 | — |
case-13 | fail→pass | 12,412 | 8,157 | -34% | 1 | 1 | 0% | 1,862 | 6,461 | +247% | 0 | 0 | — |
case-14 | fail→pass | 7,831 | 4,277 | -45% | 1 | 1 | 0% | 1,484 | 5,859 | +295% | 0 | 0 | — |
case-15 | pass→pass | 7,358 | 4,621 | -37% | 1 | 1 | 0% | 1,206 | 5,839 | +384% | 0 | 0 | — |
case-16 | pass→pass | 5,148 | 4,248 | -17% | 1 | 1 | 0% | 900 | 5,730 | +537% | 0 | 0 | — |
case-17 | fail→pass | 9,698 | 6,207 | -36% | 1 | 1 | 0% | 1,706 | 6,162 | +261% | 0 | 0 | — |
case-18 | fail→pass | 6,811 | 3,852 | -43% | 1 | 1 | 0% | 1,213 | 5,850 | +382% | 0 | 0 | — |
case-19 | fail→pass | 10,418 | 5,460 | -48% | 1 | 1 | 0% | 1,635 | 5,287 | +223% | 0 | 0 | — |
case-20 | pass→pass | 4,304 | 4,286 | -0% | 1 | 1 | 0% | 861 | 5,717 | +564% | 0 | 0 | — |
case-21 | pass→pass | 10,299 | 13,069 | +27% | 1 | 1 | 0% | 1,757 | 6,773 | +285% | 0 | 0 | — |
case-22 | pass→pass | 4,816 | 4,094 | -15% | 1 | 1 | 0% | 875 | 5,786 | +561% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +59 percentage points is the difference between those two pass rates over the 21 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.