Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

| head -1)\n\n if echo \"$first_meaningful\" | grep -q '^\\s*pipeline\\s*{'; then\n echo \"declarative\"\n elif echo \"$first_meaningful\" | grep -qE '^\\s*node\\s*(\\(|{)'; then\n echo \"scripted\"\n elif grep -q '^\\s*pipeline\\s*{' \"$file\"; then\n echo \"declarative\"\n elif grep -qE '^\\s*node\\s*(\\(|{)' \"$file\"; then\n echo \"scripted\"\n # 'stages {' is exclusive to Declarative syntax — catch malformed declarative\n # pipelines that are missing the outer 'pipeline {}' wrapper.\n elif grep -qE '^\\s*stages\\s*\\{' \"$file\"; then\n echo \"declarative\"\n else\n echo \"unknown\"\n fi\n}\n\n# Check for hardcoded credentials\ncheck_credentials() {\n local file=$1\n local line_num=0\n\n echo -e \"${BLUE}=== Checking for Hardcoded Credentials ===${NC}\"\n\n while IFS= read -r line; do\n ((++line_num))\n\n # Skip comments\n if echo \"$line\" | grep -qE '^\\s*//'; then\n continue\n fi\n\n # Skip lines that are just comment markers in multi-line comments\n if echo \"$line\" | grep -qE '^\\s*\\*'; then\n continue\n fi\n\n # ============================================\n # PASSWORD DETECTION\n # ============================================\n\n # Check for password variable assignments: password = \"value\"\n if echo \"$line\" | grep -qiE '(password|passwd|pwd)\\s*=\\s*[\"\\047]' && ! echo \"$line\" | grep -qi 'credentials'; then\n log_error \"$line_num\" \"Potential hardcoded password detected\"\n log_error \"$line_num\" \" → Use credentials() or withCredentials instead\"\n fi\n\n # Check for command-line password flags: -p password, --password=secret\n # Look for -p followed by a non-variable value (not starting with $)\n # Exclude common false positives: mkdir -p, cp -p, tar -p, install -p, etc.\n if echo \"$line\" | grep -qiE '\\s-p\\s+[a-zA-Z0-9_]+' && ! echo \"$line\" | grep -qE '\\s-p\\s+\\

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

; then\n # Skip common commands that use -p for other purposes\n if ! echo \"$line\" | grep -qiE '(mkdir|cp|tar|install|rsync|scp|chmod)\\s+-p'; then\n log_error \"$line_num\" \"Password passed as command-line argument (-p)\"\n log_error \"$line_num\" \" → Use withCredentials to inject secrets securely\"\n fi\n fi\n\n # Check for --password flag with value\n if echo \"$line\" | grep -qiE -e '--password[=\\s]+[a-zA-Z0-9_]+' && ! echo \"$line\" | grep -qE '\\

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

; then\n log_error \"$line_num\" \"Password passed as command-line argument (--password)\"\n log_error \"$line_num\" \" → Use withCredentials to inject secrets securely\"\n fi\n\n # ============================================\n # API KEY / TOKEN DETECTION\n # ============================================\n\n # Check for API key variable assignments\n if echo \"$line\" | grep -qiE '(api[_-]?key|apikey)\\s*=\\s*[\"\\047]' && ! echo \"$line\" | grep -qi 'credentials'; then\n log_error \"$line_num\" \"Potential hardcoded API key detected\"\n log_error \"$line_num\" \" → Use credentials() or withCredentials instead\"\n fi\n\n # Check for token/secret variable assignments with long values.\n # Variable name group includes api[_-]?token to catch 'apiToken', 'api_token'.\n # Value char class [a-zA-Z0-9_-] covers hyphen-delimited tokens such as\n # sk-..., ghp-..., stripe-style keys that previously evaded [a-zA-Z0-9]{20,}.\n #

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

...' ANSI quoting is used so that \\047 (octal for ') is expanded by bash\n # into a literal single-quote before being passed to grep; plain single-quoted\n # shell strings pass \\047 as literal chars and BSD grep does not treat them as\n # an octal escape in ERE, meaning single-quoted token values would be missed.\n if echo \"$line\" | grep -qiE

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

(api[_-]?token|token|secret)\\\\s*=\\\\s*[\"\\047][a-zA-Z0-9_-]{20,}'; then\n log_warning \"$line_num\" \"Potential hardcoded token/secret detected\"\n log_warning \"$line_num\" \" → Use Jenkins Credentials Manager\"\n fi\n\n # ============================================\n # BEARER TOKEN / AUTH HEADER DETECTION\n # ============================================\n\n # Check for Bearer tokens in Authorization headers\n if echo \"$line\" | grep -qiE 'Bearer\\s+[a-zA-Z0-9_.-]{10,}' && ! echo \"$line\" | grep -qE '\\

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

; then\n log_error \"$line_num\" \"Hardcoded Bearer token detected\"\n log_error \"$line_num\" \" → Use withCredentials for token management\"\n fi\n\n # Check for Basic auth with inline credentials (base64 or plain)\n if echo \"$line\" | grep -qiE 'Basic\\s+[a-zA-Z0-9+/=]{10,}' && ! echo \"$line\" | grep -qE '\\

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

; then\n log_error \"$line_num\" \"Hardcoded Basic auth credentials detected\"\n log_error \"$line_num\" \" → Use withCredentials([usernamePassword(...)]) instead\"\n fi\n\n # Check for X-API-Key, X-Auth-Token, and similar headers with inline values\n if echo \"$line\" | grep -qiE '(X-API-Key|X-Auth-Token|X-Access-Token|Authorization):\\s*[a-zA-Z0-9_.-]{8,}' && ! echo \"$line\" | grep -qE '\\

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

; then\n log_warning \"$line_num\" \"Hardcoded auth header value detected\"\n log_warning \"$line_num\" \" → Use withCredentials for secure header injection\"\n fi\n\n # ============================================\n # CLOUD PROVIDER CREDENTIALS\n # ============================================\n\n # Check for AWS Access Key IDs\n if echo \"$line\" | grep -qE 'AKIA[0-9A-Z]{16}'; then\n log_error \"$line_num\" \"AWS Access Key ID detected - NEVER hardcode AWS credentials!\"\n log_error \"$line_num\" \" → Use withCredentials or AWS credentials plugin\"\n fi\n\n # Check for AWS Secret Access Keys (40 char base64-like string after aws_secret)\n if echo \"$line\" | grep -qiE 'aws_secret[_-]?(access[_-]?key)?\\s*=\\s*[\"\\047][A-Za-z0-9/+=]{40}'; then\n log_error \"$line_num\" \"AWS Secret Access Key detected\"\n log_error \"$line_num\" \" → Use withCredentials or AWS credentials plugin\"\n fi\n\n # Check for Azure credentials patterns\n if echo \"$line\" | grep -qiE '(azure[_-]?client[_-]?secret|azure[_-]?tenant)\\s*=\\s*[\"\\047][a-zA-Z0-9-]{20,}'; then\n log_error \"$line_num\" \"Azure credential detected\"\n log_error \"$line_num\" \" → Use Azure Credentials plugin with withCredentials\"\n fi\n\n # Check for GCP service account key indicators\n if echo \"$line\" | grep -qE '\"type\"\\s*:\\s*\"service_account\"'; then\n log_error \"$line_num\" \"GCP service account key detected\"\n log_error \"$line_num\" \" → Use GCP Credentials plugin or secret file credentials\"\n fi\n\n # ============================================\n # VERSION CONTROL TOKENS\n # ============================================\n\n # Check for GitHub tokens (ghp_, gho_, ghu_, ghs_, ghr_)\n if echo \"$line\" | grep -qE 'gh[pousr]_[a-zA-Z0-9]{36,}'; then\n log_error \"$line_num\" \"GitHub token detected\"\n log_error \"$line_num\" \" → Use credentials() with secret text\"\n fi\n\n # Check for GitLab tokens (glpat-)\n if echo \"$line\" | grep -qE 'glpat-[a-zA-Z0-9_-]{20,}'; then\n log_error \"$line_num\" \"GitLab personal access token detected\"\n log_error \"$line_num\" \" → Use credentials() with secret text\"\n fi\n\n # Check for Bitbucket app passwords\n if echo \"$line\" | grep -qiE 'bitbucket.*[\"\\047][a-zA-Z0-9]{20,}[\"\\047]'; then\n log_warning \"$line_num\" \"Potential Bitbucket credential detected\"\n log_warning \"$line_num\" \" → Use credentials() with username/password\"\n fi\n\n # ============================================\n # DOCKER / REGISTRY CREDENTIALS\n # ============================================\n\n # Check for docker login with inline password\n if echo \"$line\" | grep -qiE 'docker\\s+login.*-p\\s+[^\\$]' && ! echo \"$line\" | grep -qE '\\

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

; then\n log_error \"$line_num\" \"Docker login with hardcoded password\"\n log_error \"$line_num\" \" → Use withCredentials([usernamePassword(credentialsId: 'docker-creds', ...)])\"\n fi\n\n # Check for docker login with --password\n if echo \"$line\" | grep -qiE 'docker\\s+login.*--password[=\\s]+[a-zA-Z0-9]' && ! echo \"$line\" | grep -qE '\\

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

; then\n log_error \"$line_num\" \"Docker login with hardcoded password\"\n log_error \"$line_num\" \" → Use docker.withRegistry() with credentials\"\n fi\n\n # ============================================\n # SSH / PRIVATE KEY DETECTION\n # ============================================\n\n # Check for private key content (use -e to avoid -- being interpreted as option)\n if echo \"$line\" | grep -qE -e 'BEGIN.*(RSA\\s+)?PRIVATE\\s+KEY'; then\n log_error \"$line_num\" \"Private key content detected in pipeline\"\n log_error \"$line_num\" \" → Use SSH credentials in Jenkins Credentials Manager\"\n fi\n\n # Check for SSH key file paths with hardcoded values\n if echo \"$line\" | grep -qiE 'ssh.*-i\\s+[\"\\047]?(/[^\\$]+|~[^\\$]+)[\"\\047]?' && ! echo \"$line\" | grep -qE '\\

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

; then\n log_warning \"$line_num\" \"Hardcoded SSH key path detected\"\n log_warning \"$line_num\" \" → Use sshUserPrivateKey credentials\"\n fi\n\n # ============================================\n # DATABASE CREDENTIALS\n # ============================================\n\n # Check for database connection strings with credentials\n if echo \"$line\" | grep -qiE '(mysql|postgresql|postgres|mongodb|redis|jdbc)://[a-zA-Z0-9_]+:[a-zA-Z0-9_]+@'; then\n log_error \"$line_num\" \"Database connection string with embedded credentials\"\n log_error \"$line_num\" \" → Use withCredentials to inject database credentials\"\n fi\n\n # Check for database password parameters\n if echo \"$line\" | grep -qiE '(db[_-]?pass|database[_-]?password|mysql[_-]?pwd)\\s*=\\s*[\"\\047][a-zA-Z0-9]'; then\n log_error \"$line_num\" \"Hardcoded database password\"\n log_error \"$line_num\" \" → Use credentials() or withCredentials\"\n fi\n\n # ============================================\n # GENERIC PATTERNS\n # ============================================\n\n # Check for common credential variable names with suspicious values\n if echo \"$line\" | grep -qiE '(username|user)\\s*=\\s*[\"\\047](admin|root|sa|administrator)'; then\n log_warning \"$line_num\" \"Hardcoded username detected - consider using credentials\"\n fi\n\n # Check for slack/webhook tokens\n if echo \"$line\" | grep -qE 'xox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24}'; then\n log_error \"$line_num\" \"Slack token detected\"\n log_error \"$line_num\" \" → Use credentials() with secret text\"\n fi\n\n # Check for generic webhook URLs with tokens\n if echo \"$line\" | grep -qiE 'hooks\\.(slack|discord)\\.com/services/[A-Z0-9/]+'; then\n log_warning \"$line_num\" \"Webhook URL with token detected\"\n log_warning \"$line_num\" \" → Store webhook URL in credentials\"\n fi\n\n # Check for npm tokens\n if echo \"$line\" | grep -qE 'npm_[a-zA-Z0-9]{36}'; then\n log_error \"$line_num\" \"NPM token detected\"\n log_error \"$line_num\" \" → Use credentials() with secret text\"\n fi\n\n # Check for PyPI tokens\n if echo \"$line\" | grep -qE 'pypi-[a-zA-Z0-9_-]{50,}'; then\n log_error \"$line_num\" \"PyPI token detected\"\n log_error \"$line_num\" \" → Use credentials() with secret text\"\n fi\n\n # Check for base64 encoded credentials (common mistake) - long base64 strings\n if echo \"$line\" | grep -qE '[\"\\047][A-Za-z0-9+/]{40,}={0,2}[\"\\047]'; then\n log_info \"$line_num\" \"Potential base64 encoded value - ensure this is not a credential\"\n fi\n\n # Check for hex-encoded secrets (32+ hex chars often indicate secrets)\n if echo \"$line\" | grep -qE '[\"\\047][a-fA-F0-9]{32,}[\"\\047]' && ! echo \"$line\" | grep -qiE '(commit|sha|hash|checksum|md5|sha1|sha256)'; then\n log_info \"$line_num\" \"Long hex string detected - verify this is not a secret\"\n fi\n\n done \u003c \"$file\"\n\n echo \"\"\n}\n\n# Check variable usage\ncheck_variable_usage() {\n local file=$1\n local line_num=0\n\n echo -e \"${BLUE}=== Checking Variable Usage ===${NC}\"\n\n declare -A defined_vars\n declare -A used_vars\n\n while IFS= read -r line; do\n ((++line_num))\n\n # Skip comments\n if echo \"$line\" | grep -qE '^\\s*//'; then\n continue\n fi\n\n # Track variable definitions\n if echo \"$line\" | grep -qE '(def|String|Integer|Boolean)\\s+[a-zA-Z_][a-zA-Z0-9_]*\\s*='; then\n local var_name=$(echo \"$line\" | grep -oE '[a-zA-Z_][a-zA-Z0-9_]*\\s*=' | sed 's/\\s*=//')\n defined_vars[\"$var_name\"]=$line_num\n fi\n\n # Track variable usage (simple heuristic)\n if echo \"$line\" | grep -qE '\\$\\{?[a-zA-Z_][a-zA-Z0-9_]*\\}?'; then\n local vars=$(echo \"$line\" | grep -oE '\\$\\{?[a-zA-Z_][a-zA-Z0-9_]*\\}?' | sed 's/[\\${}]//g')\n for var in $vars; do\n used_vars[\"$var\"]=$line_num\n done\n fi\n\n # Check for undefined variables being used\n if echo \"$line\" | grep -qE '\\$[a-zA-Z_][a-zA-Z0-9_]*' && ! echo \"$line\" | grep -qE '(def|String|params\\.|env\\.)'; then\n local var=$(echo \"$line\" | grep -oE '\\$[a-zA-Z_][a-zA-Z0-9_]*' | sed 's/\\$//' | head -1)\n if [[ ! -v defined_vars[\"$var\"] ]] && [[ \"$var\" != \"WORKSPACE\" ]] && [[ \"$var\" != \"BUILD_NUMBER\" ]] && [[ \"$var\" != \"JOB_NAME\" ]]; then\n log_info \"$line_num\" \"Variable '\\$var' used but not explicitly defined - ensure it's set by environment\"\n fi\n fi\n done \u003c \"$file\"\n\n echo \"\"\n}\n\n# Detect plugin usage\ndetect_plugins() {\n local file=$1\n\n echo -e \"${BLUE}=== Detecting Plugin Usage ===${NC}\"\n\n declare -A plugins\n\n # Common plugin steps\n local plugin_patterns=(\n \"docker\\.\"\n \"kubernetes\\.\"\n \"withCredentials\"\n \"git \"\n \"checkout\"\n \"junit\"\n \"archiveArtifacts\"\n \"publishHTML\"\n \"mail\"\n \"emailext\"\n \"slackSend\"\n \"build job:\"\n \"input \"\n \"timeout\"\n \"retry\"\n \"script\"\n )\n\n for pattern in \"${plugin_patterns[@]}\"; do\n if grep -q \"$pattern\" \"$file\"; then\n local plugin_name=$(echo \"$pattern\" | sed 's/[\\.\\\\].*$//' | sed 's/ $//')\n plugins[\"$plugin_name\"]=1\n fi\n done\n\n if [ ${#plugins[@]} -gt 0 ]; then\n echo \"Plugins detected:\"\n for plugin in \"${!plugins[@]}\"; do\n echo \" - $plugin\"\n done\n else\n echo \"No common plugins detected\"\n fi\n\n echo \"\"\n}\n\n# Check for multiple consecutive sh/bat steps\ncheck_multiple_sh_steps() {\n local file=$1\n local line_num=0\n local consecutive_sh=0\n local first_sh_line=0\n\n echo -e \"${BLUE}=== Checking for Multiple Shell Steps ===${NC}\"\n\n while IFS= read -r line; do\n ((++line_num))\n\n # Check for sh/bat steps\n if echo \"$line\" | grep -qE '^\\s*(sh|bat)\\s+[\"\\047]' || echo \"$line\" | grep -qE '^\\s*(sh|bat)\\s*\\('; then\n if [ $consecutive_sh -eq 0 ]; then\n first_sh_line=$line_num\n fi\n ((consecutive_sh++))\n else\n # Not a sh/bat line\n if [ $consecutive_sh -gt 2 ]; then\n log_warning \"$first_sh_line\" \"Found $consecutive_sh consecutive sh/bat steps\"\n log_warning \"$first_sh_line\" \" → Combine into single sh step with triple-quoted string (sh '''...''')\"\n log_warning \"$first_sh_line\" \" → See: references/best_practices.md#combine-shell-commands\"\n fi\n consecutive_sh=0\n fi\n done \u003c \"$file\"\n\n # Check at end of file\n if [ $consecutive_sh -gt 2 ]; then\n log_warning \"$first_sh_line\" \"Found $consecutive_sh consecutive sh/bat steps\"\n log_warning \"$first_sh_line\" \" → Combine into single sh step with triple-quoted string\"\n fi\n\n echo \"\"\n}\n\n# Check for timeout usage\ncheck_timeout_usage() {\n local file=$1\n\n echo -e \"${BLUE}=== Checking Timeout Configuration ===${NC}\"\n\n if ! grep -qE '(timeout|Timeout)' \"$file\"; then\n log_info 1 \"No timeout configuration found - consider adding timeout to prevent hung builds\"\n log_info 1 \" → Declarative: options { timeout(time: 1, unit: 'HOURS') }\"\n log_info 1 \" → Scripted: timeout(time: 1, unit: 'HOURS') { ... }\"\n fi\n\n echo \"\"\n}\n\n# Check for proper workspace cleanup\ncheck_workspace_cleanup() {\n local file=$1\n\n echo -e \"${BLUE}=== Checking Workspace Cleanup ===${NC}\"\n\n if ! grep -qE '(cleanWs|deleteDir|ws)' \"$file\"; then\n log_info 1 \"No workspace cleanup detected - consider cleaning workspace for reproducible builds\"\n log_info 1 \" → Add cleanWs() in post section or use deleteDir()\"\n fi\n\n echo \"\"\n}\n\n# Print validation results\nprint_results() {\n echo -e \"${BLUE}=== Validation Results ===${NC}\"\n echo \"\"\n\n if [ ${#ERROR_MESSAGES[@]} -gt 0 ]; then\n echo -e \"${RED}ERRORS (${ERRORS}):${NC}\"\n for msg in \"${ERROR_MESSAGES[@]}\"; do\n echo -e \"${RED}$msg${NC}\"\n done\n echo \"\"\n fi\n\n if [ ${#WARNING_MESSAGES[@]} -gt 0 ]; then\n echo -e \"${YELLOW}WARNINGS (${WARNINGS}):${NC}\"\n for msg in \"${WARNING_MESSAGES[@]}\"; do\n echo -e \"${YELLOW}$msg${NC}\"\n done\n echo \"\"\n fi\n\n if [ ${#INFO_MESSAGES[@]} -gt 0 ]; then\n echo -e \"${BLUE}INFO (${INFO}):${NC}\"\n for msg in \"${INFO_MESSAGES[@]}\"; do\n echo -e \"${BLUE}$msg${NC}\"\n done\n echo \"\"\n fi\n\n # Summary\n echo -e \"${BLUE}=== Summary ===${NC}\"\n if [ $ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then\n echo -e \"${GREEN}✓ Validation passed with no errors or warnings${NC}\"\n return 0\n elif [ $ERRORS -eq 0 ]; then\n echo -e \"${YELLOW}✓ Validation passed with $WARNINGS warning(s)${NC}\"\n return 0\n else\n echo -e \"${RED}✗ Validation failed with $ERRORS error(s) and $WARNINGS warning(s)${NC}\"\n return 1\n fi\n}\n\n# Main function - run all common checks\nrun_common_checks() {\n local file=$1\n\n echo -e \"${BLUE}=== Running Common Validation Checks ===${NC}\"\n echo \"File: $file\"\n echo \"\"\n\n check_credentials \"$file\" || true\n check_variable_usage \"$file\" || true\n detect_plugins \"$file\" || true\n check_multiple_sh_steps \"$file\" || true\n check_timeout_usage \"$file\" || true\n check_workspace_cleanup \"$file\" || true\n\n print_results\n}\n\n# If script is executed directly\nif [ \"${BASH_SOURCE[0]}\" == \"${0}\" ]; then\n if [ $# -lt 2 ]; then\n echo \"Usage: $0 \u003ccommand> \u003cjenkinsfile>\"\n echo \"Commands:\"\n echo \" detect_type - Detect pipeline type (declarative/scripted)\"\n echo \" check_credentials - Check for hardcoded credentials\"\n echo \" check_all - Run all common validation checks\"\n exit 1\n fi\n\n COMMAND=$1\n JENKINSFILE=$2\n\n if [ ! -f \"$JENKINSFILE\" ]; then\n echo -e \"${RED}Error: File '$JENKINSFILE' not found${NC}\"\n exit 1\n fi\n\n case \"$COMMAND\" in\n detect_type)\n TYPE=$(detect_pipeline_type \"$JENKINSFILE\")\n echo \"Pipeline type: $TYPE\"\n ;;\n check_credentials)\n check_credentials \"$JENKINSFILE\"\n print_results\n ;;\n check_all)\n run_common_checks \"$JENKINSFILE\"\n ;;\n *)\n echo \"Unknown command: $COMMAND\"\n exit 1\n ;;\n esac\nfi\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":21202,"content_sha256":"ec69895de8bd763334a522002a7f92b722369384b954c49fa47cb5f095265683"},{"filename":"scripts/validate_declarative.sh","content":"#!/bin/bash\n\n# Declarative Pipeline Validator\n# Validates Jenkins Declarative Pipeline syntax and structure\n\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\n\n# Colors for output\nRED='\\033[0;31m'\nYELLOW='\\033[1;33m'\nGREEN='\\033[0;32m'\nBLUE='\\033[0;34m'\nNC='\\033[0m' # No Color\n\n# Counters\nERRORS=0\nWARNINGS=0\nINFO=0\n\n# Validation result arrays\ndeclare -a ERROR_MESSAGES=()\ndeclare -a WARNING_MESSAGES=()\ndeclare -a INFO_MESSAGES=()\n\nusage() {\n echo \"Usage: $0 \u003cjenkinsfile>\"\n echo \"Validates a Declarative Jenkins Pipeline\"\n exit 1\n}\n\nlog_error() {\n local line=$1\n local message=$2\n ERROR_MESSAGES+=(\"ERROR [Line $line]: $message\")\n ((ERRORS++))\n}\n\nlog_warning() {\n local line=$1\n local message=$2\n WARNING_MESSAGES+=(\"WARNING [Line $line]: $message\")\n ((WARNINGS++))\n}\n\nlog_info() {\n local line=$1\n local message=$2\n INFO_MESSAGES+=(\"INFO [Line $line]: $message\")\n ((INFO++))\n}\n\n# Check if file starts with 'pipeline {'\nvalidate_pipeline_block() {\n local file=$1\n\n # Remove comments and empty lines for checking\n local first_meaningful=$(grep -v '^\\s*//' \"$file\" | grep -v '^\\s*

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

| head -1)\n\n if ! echo \"$first_meaningful\" | grep -q '^\\s*pipeline\\s*{'; then\n log_error 1 \"Declarative pipeline must start with 'pipeline {' block\"\n return 1\n fi\n\n return 0\n}\n\n# Check for required top-level sections\nvalidate_required_sections() {\n local file=$1\n\n # Check for agent section\n if ! grep -q '^\\s*agent\\s' \"$file\"; then\n local line=$(grep -n 'pipeline\\s*{' \"$file\" | head -1 | cut -d: -f1)\n # Default to line 1 if pipeline block not found\n line=${line:-1}\n log_error \"$line\" \"Missing required section 'agent' - must be defined at pipeline or stage level\"\n log_error \"$line\" \" → Add 'agent any' or specific agent configuration\"\n fi\n\n # Check for stages section\n if ! grep -q '^\\s*stages\\s*{' \"$file\"; then\n local line=$(grep -n 'pipeline\\s*{' \"$file\" | head -1 | cut -d: -f1)\n # Default to line 1 if pipeline block not found\n line=${line:-1}\n log_error \"$line\" \"Missing required section 'stages'\"\n log_error \"$line\" \" → Add 'stages { ... }' block containing your pipeline stages\"\n fi\n\n # If stages exists, check for at least one stage\n if grep -q '^\\s*stages\\s*{' \"$file\"; then\n if ! grep -q '^\\s*stage(' \"$file\"; then\n local line=$(grep -n 'stages\\s*{' \"$file\" | head -1 | cut -d: -f1)\n line=${line:-1}\n log_error \"$line\" \"stages block must contain at least one stage\"\n log_error \"$line\" \" → Add 'stage('name') { ... }' inside stages block\"\n fi\n fi\n}\n\n# Validate stage structure\nvalidate_stages() {\n local file=$1\n local line_num=0\n\n while IFS= read -r line; do\n ((line_num++))\n\n # Check for stage definitions (but skip nested stages inside parallel blocks)\n if echo \"$line\" | grep -q '^\\s*stage('; then\n # Check if stage has a name\n if ! echo \"$line\" | grep -q \"stage(['\\\"]\"; then\n log_error \"$line_num\" \"Stage must have a name in quotes\"\n log_error \"$line_num\" \" → Use: stage('Stage Name') { ... }\"\n fi\n\n # Check for steps block (need to look ahead)\n local stage_start=$line_num\n local has_valid_body=false\n local has_agent=false\n local check_line=$line_num\n\n # Initialize brace depth - account for opening brace on stage line itself\n # e.g., stage('Build') { has one opening brace\n local stage_open_braces=$(echo \"$line\" | grep -o '{' | wc -l)\n local stage_close_braces=$(echo \"$line\" | grep -o '}' | wc -l)\n local brace_depth=$((stage_open_braces - stage_close_braces))\n\n # Look for steps, parallel, or script blocks within this stage\n # Use brace depth tracking to find the stage boundary correctly\n for ((i=0; i\u003c100; i++)); do\n ((check_line++))\n local next_line=$(sed -n \"${check_line}p\" \"$file\")\n\n # Skip empty lines and comments\n if echo \"$next_line\" | grep -qE '^\\s*(//.*)?

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

; then\n continue\n fi\n\n # Track brace depth\n local open_braces=$(echo \"$next_line\" | grep -o '{' | wc -l)\n local close_braces=$(echo \"$next_line\" | grep -o '}' | wc -l)\n brace_depth=$((brace_depth + open_braces - close_braces))\n\n # Check for valid stage body types at the stage level (brace_depth == 1)\n # steps, parallel, and matrix are all valid stage body types\n if echo \"$next_line\" | grep -qE '^\\s*steps\\s*\\{'; then\n has_valid_body=true\n fi\n\n if echo \"$next_line\" | grep -qE '^\\s*parallel\\s*\\{'; then\n has_valid_body=true\n fi\n\n if echo \"$next_line\" | grep -qE '^\\s*matrix\\s*\\{'; then\n has_valid_body=true\n fi\n\n # Check for stage-level agent\n if echo \"$next_line\" | grep -qE '^\\s*agent\\s'; then\n has_agent=true\n fi\n\n # Stop if we've exited the stage block (brace depth back to 0 or negative)\n if [ $brace_depth -le 0 ]; then\n break\n fi\n\n # Safety: Stop if we hit another top-level stage (at depth 0)\n if [ $brace_depth -eq 0 ] && echo \"$next_line\" | grep -q '^\\s*stage('; then\n break\n fi\n done\n\n # Only report error if no valid body found\n # Note: Stages inside parallel blocks don't need steps at their level\n # because the parallel block itself satisfies the requirement\n if [[ \"$has_valid_body\" == false ]] && [[ \"$has_agent\" == false ]]; then\n # Check if this might be a nested stage inside a parallel block\n # by looking backward for parallel {\n local is_nested_in_parallel=false\n local look_back=$line_num\n local back_depth=0\n\n for ((j=0; j\u003c50 && look_back>1; j++)); do\n ((look_back--))\n local prev_line=$(sed -n \"${look_back}p\" \"$file\")\n\n if echo \"$prev_line\" | grep -qE '^\\s*parallel\\s*\\{'; then\n is_nested_in_parallel=true\n break\n fi\n\n # Stop if we hit the stages block start\n if echo \"$prev_line\" | grep -qE '^\\s*stages\\s*\\{'; then\n break\n fi\n done\n\n # Don't error on nested parallel stages - they're checked separately\n if [[ \"$is_nested_in_parallel\" == false ]]; then\n log_error \"$stage_start\" \"Stage must contain 'steps', 'parallel', or 'matrix' block\"\n log_error \"$stage_start\" \" → Add 'steps { ... }' inside stage\"\n fi\n fi\n fi\n done \u003c \"$file\"\n}\n\n# Check for invalid directive placement\nvalidate_directive_placement() {\n local file=$1\n local line_num=0\n local in_steps=false\n local in_post=false\n\n while IFS= read -r line; do\n ((line_num++))\n\n # Track if we're inside steps block\n if echo \"$line\" | grep -q '^\\s*steps\\s*{'; then\n in_steps=true\n fi\n\n if echo \"$line\" | grep -q '^\\s*post\\s*{'; then\n in_post=true\n fi\n\n # Reset on closing brace (simple heuristic)\n if [[ \"$in_steps\" == true ]] && echo \"$line\" | grep -q '^\\s*}\\s*

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

; then\n in_steps=false\n fi\n\n if [[ \"$in_post\" == true ]] && echo \"$line\" | grep -q '^\\s*}\\s*

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

; then\n in_post=false\n fi\n\n # Check for directives that shouldn't be in steps\n if [[ \"$in_steps\" == true ]]; then\n if echo \"$line\" | grep -qE '^\\s*(environment|options|parameters|triggers|tools)\\s*{'; then\n log_error \"$line_num\" \"Directive '$(echo \"$line\" | grep -oE '(environment|options|parameters|triggers|tools)')' cannot be inside 'steps' block\"\n log_error \"$line_num\" \" → Move this directive to pipeline or stage level\"\n fi\n fi\n done \u003c \"$file\"\n}\n\n# Check for common syntax errors\nvalidate_syntax() {\n local file=$1\n local line_num=0\n\n while IFS= read -r line; do\n ((line_num++))\n\n # Skip comment lines\n if echo \"$line\" | grep -q '^\\s*//'; then\n continue\n fi\n\n # Check for semicolons at end of lines (not needed in Jenkins pipelines)\n if echo \"$line\" | grep -qE '[^\"\\047];\\s*

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

&& ! echo \"$line\" | grep -q '^\\s*//'; then\n log_warning \"$line_num\" \"Semicolon at end of line is unnecessary in Jenkins pipelines\"\n log_warning \"$line_num\" \" → Remove trailing semicolon\"\n fi\n\n # Check for unmatched quotes (basic check)\n # Skip lines that are part of multi-line strings (triple quotes or heredocs)\n if echo \"$line\" | grep -qE \"'''|\\\"\\\"\\\"\"; then\n continue\n fi\n\n # Skip lines inside multi-line sh blocks (contain just string content)\n if echo \"$line\" | grep -qE '^\\s*(echo|mkdir|make|cd|cmake|mvn|gradle|npm|yarn|kubectl|docker|git)\\s'; then\n continue\n fi\n\n # Remove escaped quotes before counting\n local clean_line=$(echo \"$line\" | sed \"s/\\\\\\\\'//g\" | sed 's/\\\\\"//g')\n local single_quotes=$(echo \"$clean_line\" | grep -o \"'\" | wc -l)\n local double_quotes=$(echo \"$clean_line\" | grep -o '\"' | wc -l)\n\n # Only flag truly unbalanced quotes (not in multi-line contexts)\n # Skip if line has shell command patterns that commonly span lines\n if (( single_quotes % 2 != 0 )); then\n # Only error if not a shell command continuation\n if ! echo \"$line\" | grep -qE \"sh\\s+'''|sh\\s*\\(|script:|'''\"; then\n log_error \"$line_num\" \"Unmatched single quote detected\"\n fi\n fi\n\n if (( double_quotes % 2 != 0 )); then\n # Only error if not a shell command continuation\n if ! echo \"$line\" | grep -qE 'sh\\s+\"\"\"|sh\\s*\\(|script:|\"\"\"'; then\n log_error \"$line_num\" \"Unmatched double quote detected\"\n fi\n fi\n\n # Check for common typos in section names (singular instead of plural)\n # Only flag when followed by { (not function calls like step([...]))\n # Note: 'stage' is valid as stage('name'), so we check specifically for 'stage {'\n if echo \"$line\" | grep -qE '^\\s*(option|parameter|trigger|tool)\\s*\\{'; then\n local typo=$(echo \"$line\" | grep -oE '(option|parameter|trigger|tool)')\n log_error \"$line_num\" \"Possible typo: '$typo' (did you mean '${typo}s'?)\"\n fi\n # Special check for 'step {' (not step( which is valid)\n if echo \"$line\" | grep -qE '^\\s*step\\s*\\{'; then\n log_error \"$line_num\" \"Possible typo: 'step' (did you mean 'steps'?)\"\n fi\n done \u003c \"$file\"\n}\n\n# Validate environment variable syntax\nvalidate_environment() {\n local file=$1\n local line_num=0\n local in_env=false\n\n while IFS= read -r line; do\n ((line_num++))\n\n if echo \"$line\" | grep -q '^\\s*environment\\s*{'; then\n in_env=true\n continue\n fi\n\n if [[ \"$in_env\" == true ]]; then\n if echo \"$line\" | grep -q '^\\s*}\\s*

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

; then\n in_env=false\n continue\n fi\n\n # Check for proper environment variable syntax\n if echo \"$line\" | grep -q '=' && ! echo \"$line\" | grep -q '^\\s*//' && ! echo \"$line\" | grep -qE '^\\s*[A-Z_][A-Z0-9_]*\\s*='; then\n log_warning \"$line_num\" \"Environment variable should be UPPER_CASE\"\n log_warning \"$line_num\" \" → Convention: MY_VAR = 'value'\"\n fi\n fi\n done \u003c \"$file\"\n}\n\n# Check for parallel stages usage\nvalidate_parallel() {\n local file=$1\n\n if grep -q '^\\s*parallel\\s*{' \"$file\"; then\n local line=$(grep -n 'parallel\\s*{' \"$file\" | head -1 | cut -d: -f1)\n\n # Check for parallelsAlwaysFailFast() in pipeline options (global setting)\n local has_global_failfast=false\n if grep -q 'parallelsAlwaysFailFast' \"$file\"; then\n has_global_failfast=true\n fi\n\n # If global setting is present, no need to check individual parallel blocks\n if [[ \"$has_global_failfast\" == true ]]; then\n return 0\n fi\n\n # Check if failFast is considered on individual parallel blocks\n # In Declarative pipelines, failFast appears BEFORE the parallel block (as a stage directive)\n # So we need to check both before (-B) and after (-A) the parallel block\n local has_failfast=false\n if grep -B 5 'parallel\\s*{' \"$file\" | grep -q 'failFast'; then\n has_failfast=true\n fi\n if grep -A 20 'parallel\\s*{' \"$file\" | grep -q 'failFast'; then\n has_failfast=true\n fi\n\n if [[ \"$has_failfast\" == false ]]; then\n log_info \"$line\" \"Consider adding 'failFast true' to parallel block to stop on first failure\"\n log_info \"$line\" \" → Or add 'parallelsAlwaysFailFast()' in pipeline options for global coverage\"\n fi\n fi\n}\n\n# Validate when conditions\nvalidate_when() {\n local file=$1\n local line_num=0\n\n while IFS= read -r line; do\n ((line_num++))\n\n if echo \"$line\" | grep -q '^\\s*when\\s*{'; then\n # Check for valid when conditions in next few lines\n local has_condition=false\n local check_line=$line_num\n\n for ((i=0; i\u003c10; i++)); do\n ((check_line++))\n local next_line=$(sed -n \"${check_line}p\" \"$file\")\n\n if echo \"$next_line\" | grep -qE '^\\s*(branch|environment|expression|tag|not|allOf|anyOf)'; then\n has_condition=true\n break\n fi\n\n if echo \"$next_line\" | grep -q '^\\s*}\\s*

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

; then\n break\n fi\n done\n\n if [[ \"$has_condition\" == false ]]; then\n log_warning \"$line_num\" \"when block appears empty or has no valid condition\"\n log_warning \"$line_num\" \" → Use: branch, environment, expression, tag, or boolean operators\"\n fi\n fi\n done \u003c \"$file\"\n}\n\n# Validate matrix builds (requires Jenkins 2.0+)\nvalidate_matrix() {\n local file=$1\n local line_num=0\n\n while IFS= read -r line; do\n ((line_num++))\n\n if echo \"$line\" | grep -q '^\\s*matrix\\s*{'; then\n local matrix_start=$line_num\n local has_axes=false\n local has_stages=false\n local check_line=$line_num\n local brace_count=1\n\n # Check matrix block structure\n for ((i=0; i\u003c50; i++)); do\n ((check_line++))\n local next_line=$(sed -n \"${check_line}p\" \"$file\")\n\n # Track braces\n local open=$(echo \"$next_line\" | grep -o '{' | wc -l)\n local close=$(echo \"$next_line\" | grep -o '}' | wc -l)\n brace_count=$((brace_count + open - close))\n\n if echo \"$next_line\" | grep -q '^\\s*axes\\s*{'; then\n has_axes=true\n fi\n\n if echo \"$next_line\" | grep -q '^\\s*stages\\s*{'; then\n has_stages=true\n fi\n\n # Exit when matrix block closes\n if [ $brace_count -le 0 ]; then\n break\n fi\n done\n\n # Validate matrix structure\n if [[ \"$has_axes\" == false ]]; then\n log_error \"$matrix_start\" \"Matrix block missing required 'axes' section\"\n log_error \"$matrix_start\" \" → Add 'axes { axis { name 'AXIS_NAME'; values 'val1', 'val2' } }'\"\n fi\n\n if [[ \"$has_stages\" == false ]]; then\n log_error \"$matrix_start\" \"Matrix block missing required 'stages' section\"\n log_error \"$matrix_start\" \" → Add 'stages { stage('Build') { steps { ... } } }'\"\n fi\n\n # Check for best practices\n if ! grep -A 30 \"^\\s*matrix\\s*{\" \"$file\" | grep -q 'failFast'; then\n log_info \"$matrix_start\" \"Consider adding 'failFast true' to matrix options for faster feedback\"\n log_info \"$matrix_start\" \" → options { failFast true }\"\n fi\n fi\n\n # Validate axis definitions\n if echo \"$line\" | grep -q '^\\s*axis\\s*{'; then\n local axis_start=$line_num\n local has_name=false\n local has_values=false\n local check_line=$line_num\n\n for ((i=0; i\u003c10; i++)); do\n ((check_line++))\n local next_line=$(sed -n \"${check_line}p\" \"$file\")\n\n if echo \"$next_line\" | grep -q '^\\s*name\\s'; then\n has_name=true\n fi\n\n if echo \"$next_line\" | grep -q '^\\s*values\\s'; then\n has_values=true\n fi\n\n if echo \"$next_line\" | grep -q '^\\s*}\\s*

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

; then\n break\n fi\n done\n\n if [[ \"$has_name\" == false ]]; then\n log_error \"$axis_start\" \"Axis missing required 'name' property\"\n log_error \"$axis_start\" \" → Add 'name \\\"AXIS_NAME\\\"'\"\n fi\n\n if [[ \"$has_values\" == false ]]; then\n log_error \"$axis_start\" \"Axis missing required 'values' property\"\n log_error \"$axis_start\" \" → Add 'values \\\"val1\\\", \\\"val2\\\"'\"\n fi\n fi\n\n # Validate excludes section (optional but must be valid if present)\n if echo \"$line\" | grep -q '^\\s*excludes\\s*{'; then\n local excludes_start=$line_num\n local check_line=$line_num\n local has_exclude=false\n\n for ((i=0; i\u003c20; i++)); do\n ((check_line++))\n local next_line=$(sed -n \"${check_line}p\" \"$file\")\n\n if echo \"$next_line\" | grep -q '^\\s*exclude\\s*{'; then\n has_exclude=true\n fi\n\n if echo \"$next_line\" | grep -q '^\\s*}\\s*

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

; then\n break\n fi\n done\n\n if [[ \"$has_exclude\" == false ]]; then\n log_warning \"$excludes_start\" \"excludes block is empty\"\n log_warning \"$excludes_start\" \" → Add 'exclude { axis { ... } }' or remove excludes block\"\n fi\n fi\n done \u003c \"$file\"\n}\n\n# Validate @Library shared library imports\nvalidate_shared_libraries() {\n local file=$1\n local line_num=0\n\n while IFS= read -r line; do\n ((line_num++))\n\n # Check for @Library annotations\n if echo \"$line\" | grep -q '@Library'; then\n # Check for proper syntax: @Library('name') _ or @Library(['lib1', 'lib2'])\n if ! echo \"$line\" | grep -qE \"@Library\\s*\\(\\s*['\\\"\\[]\"; then\n log_error \"$line_num\" \"Invalid @Library syntax\"\n log_error \"$line_num\" \" → Use: @Library('library-name') _ or @Library(['lib1', 'lib2']) _\"\n fi\n\n # Check if underscore is used for implicit import\n if echo \"$line\" | grep -qE \"@Library\\s*\\([^)]+\\)\\s*$\"; then\n log_warning \"$line_num\" \"Missing underscore after @Library - add '_' for implicit import\"\n log_warning \"$line_num\" \" → @Library('library-name') _\"\n fi\n\n # Check for version specification\n if ! echo \"$line\" | grep -qE \"@Library\\s*\\(['\\\"][^'\\\"]+@\"; then\n log_info \"$line_num\" \"Consider pinning library version for reproducible builds\"\n log_info \"$line_num\" \" → @Library('library-name@branch-or-tag') _\"\n fi\n fi\n\n # Check for library step usage\n if echo \"$line\" | grep -q '^\\s*library\\s*'; then\n if ! echo \"$line\" | grep -qE \"library\\s+(identifier:|['\\\"])\"; then\n log_warning \"$line_num\" \"Invalid library step syntax\"\n log_warning \"$line_num\" \" → Use: library identifier: 'name@version' or library 'name'\"\n fi\n fi\n done \u003c \"$file\"\n}\n\n# Main validation function\nvalidate_declarative() {\n local file=$1\n\n echo -e \"${BLUE}=== Validating Declarative Pipeline ===${NC}\"\n echo \"File: $file\"\n echo \"\"\n\n # Run all validation checks (|| true prevents early exit on validation failures)\n validate_pipeline_block \"$file\" || true\n validate_required_sections \"$file\" || true\n validate_stages \"$file\" || true\n validate_directive_placement \"$file\" || true\n validate_syntax \"$file\" || true\n validate_environment \"$file\" || true\n validate_parallel \"$file\" || true\n validate_when \"$file\" || true\n validate_matrix \"$file\" || true\n validate_shared_libraries \"$file\" || true\n\n # Print results\n echo -e \"${BLUE}=== Validation Results ===${NC}\"\n echo \"\"\n\n if [ ${#ERROR_MESSAGES[@]} -gt 0 ]; then\n echo -e \"${RED}ERRORS (${ERRORS}):${NC}\"\n for msg in \"${ERROR_MESSAGES[@]}\"; do\n echo -e \"${RED}$msg${NC}\"\n done\n echo \"\"\n fi\n\n if [ ${#WARNING_MESSAGES[@]} -gt 0 ]; then\n echo -e \"${YELLOW}WARNINGS (${WARNINGS}):${NC}\"\n for msg in \"${WARNING_MESSAGES[@]}\"; do\n echo -e \"${YELLOW}$msg${NC}\"\n done\n echo \"\"\n fi\n\n if [ ${#INFO_MESSAGES[@]} -gt 0 ]; then\n echo -e \"${BLUE}INFO (${INFO}):${NC}\"\n for msg in \"${INFO_MESSAGES[@]}\"; do\n echo -e \"${BLUE}$msg${NC}\"\n done\n echo \"\"\n fi\n\n # Summary\n echo -e \"${BLUE}=== Summary ===${NC}\"\n if [ $ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then\n echo -e \"${GREEN}✓ Validation passed with no errors or warnings${NC}\"\n return 0\n elif [ $ERRORS -eq 0 ]; then\n echo -e \"${YELLOW}✓ Validation passed with $WARNINGS warning(s)${NC}\"\n return 0\n else\n echo -e \"${RED}✗ Validation failed with $ERRORS error(s) and $WARNINGS warning(s)${NC}\"\n return 1\n fi\n}\n\n# Main execution\nif [ $# -ne 1 ]; then\n usage\nfi\n\nJENKINSFILE=\"$1\"\n\nif [ ! -f \"$JENKINSFILE\" ]; then\n echo -e \"${RED}Error: File '$JENKINSFILE' not found${NC}\"\n exit 1\nfi\n\nvalidate_declarative \"$JENKINSFILE\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":22710,"content_sha256":"f2cf5736d8f74e3cf3fcfa90d20f2b80e49062a1b14bffea0c5480c950ca9f4e"},{"filename":"scripts/validate_jenkinsfile.sh","content":"#!/bin/bash\n\n# Jenkinsfile Validator - Main Orchestrator Script\n# Runs all validators in sequence with unified output\n#\n# Usage: validate_jenkinsfile.sh [OPTIONS] \u003cjenkinsfile>\n#\n# Options:\n# --syntax-only Run only syntax validation\n# --security-only Run only security checks\n# --best-practices Run only best practices check\n# --no-security Skip security checks\n# --no-best-practices Skip best practices check\n# --assume-declarative Force declarative syntax mode for unknown files\n# --assume-scripted Force scripted syntax mode for unknown files\n# --strict-unknown Fail when pipeline type cannot be auto-detected (default)\n# --strict Fail on warnings\n# -h, --help Show this help message\n#\n# Exit codes:\n# 0 - Validation passed\n# 1 - Validation failed\n# 2 - Usage error\n\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\n\n# Colors for output\nRED='\\033[0;31m'\nYELLOW='\\033[1;33m'\nGREEN='\\033[0;32m'\nBLUE='\\033[0;34m'\nBOLD='\\033[1m'\nNC='\\033[0m' # No Color\n\n# Symbols\nPASS_SYMBOL=\"✓\"\nFAIL_SYMBOL=\"✗\"\nWARN_SYMBOL=\"⚠\"\nSKIP_SYMBOL=\"○\"\n\n# Default options\nRUN_SYNTAX=true\nRUN_SECURITY=true\nRUN_BEST_PRACTICES=true\nSTRICT_MODE=false\nSTRICT_UNKNOWN=true\nASSUME_TYPE=\"\"\n\n# Counters\nTOTAL_ERRORS=0\nTOTAL_WARNINGS=0\nTOTAL_INFO=0\n\nusage() {\n cat \u003c\u003c EOF\nUsage: $(basename \"$0\") [OPTIONS] \u003cjenkinsfile>\n\nValidates Jenkins pipeline files (Declarative and Scripted).\n\nOptions:\n --syntax-only Run only syntax validation\n --security-only Run only security checks\n --best-practices Run only best practices check\n --no-security Skip security checks\n --no-best-practices Skip best practices check\n --assume-declarative Force declarative syntax mode for unknown files\n --assume-scripted Force scripted syntax mode for unknown files\n --strict-unknown Fail when type cannot be auto-detected (default)\n --strict Fail on warnings (treat warnings as errors)\n -h, --help Show this help message\n\nExamples:\n $(basename \"$0\") Jenkinsfile # Full validation\n $(basename \"$0\") --syntax-only Jenkinsfile # Syntax only\n $(basename \"$0\") --assume-scripted file.groovy # Explicit override for unknown type\n $(basename \"$0\") --strict Jenkinsfile # Fail on warnings\n $(basename \"$0\") --no-security Jenkinsfile # Skip security scan\n\nExit codes:\n 0 - Validation passed\n 1 - Validation failed (errors found, or warnings in strict mode)\n 2 - Usage error\nEOF\n exit 2\n}\n\nprint_header() {\n echo \"\"\n echo -e \"${BLUE}${BOLD}════════════════════════════════════════════════════════════════${NC}\"\n echo -e \"${BLUE}${BOLD} Jenkinsfile Validator v1.2.1${NC}\"\n echo -e \"${BLUE}${BOLD}════════════════════════════════════════════════════════════════${NC}\"\n echo \"\"\n}\n\nprint_section() {\n local title=$1\n echo \"\"\n echo -e \"${BLUE}┌──────────────────────────────────────────────────────────────┐${NC}\"\n echo -e \"${BLUE}│ ${BOLD}$title${NC}\"\n echo -e \"${BLUE}└──────────────────────────────────────────────────────────────┘${NC}\"\n echo \"\"\n}\n\ncheck_prerequisites() {\n local required_tools=(\"bash\" \"grep\" \"sed\" \"awk\" \"head\" \"wc\")\n local missing_tools=()\n\n for tool in \"${required_tools[@]}\"; do\n if ! command -v \"$tool\" >/dev/null 2>&1; then\n missing_tools+=(\"$tool\")\n fi\n done\n\n if [ ${#missing_tools[@]} -gt 0 ]; then\n echo -e \"${RED}Error: Missing required tool(s): ${missing_tools[*]}${NC}\"\n echo \"Install missing tools and rerun validation.\"\n exit 2\n fi\n\n # Optional dependency used in recommendations and troubleshooting flows.\n if ! command -v jq >/dev/null 2>&1; then\n echo -e \"${YELLOW}Warning: jq not found (optional).${NC}\"\n echo \" jq is recommended for JSON-heavy pipeline debugging.\"\n fi\n}\n\nrun_validator_script() {\n local script_path=$1\n shift\n\n if [ ! -r \"$script_path\" ]; then\n echo -e \"${RED}ERROR [Runner]: Required script is missing or not readable: $(basename \"$script_path\")${NC}\"\n return 127\n fi\n\n if [ ! -x \"$script_path\" ]; then\n echo -e \"${YELLOW}WARNING [Runner]: $(basename \"$script_path\") is not executable. Using 'bash' fallback.${NC}\"\n fi\n\n local output=\"\"\n local status=0\n set +e\n output=$(bash \"$script_path\" \"$@\" 2>&1)\n status=$?\n set -e\n\n if [ -n \"$output\" ]; then\n echo \"$output\"\n fi\n\n if [ \"$status\" -gt 1 ]; then\n echo -e \"${RED}ERROR [Runner]: $(basename \"$script_path\") exited with unexpected code ${status}${NC}\"\n fi\n\n return \"$status\"\n}\n\ncount_error_warning_lines() {\n local output=$1\n local clean_output\n clean_output=$(printf '%s\\n' \"$output\" | sed 's/\\x1b\\[[0-9;]*m//g')\n local errors\n local warnings\n errors=$(printf '%s\\n' \"$clean_output\" | grep -E -c '^ERROR \\[' || true)\n warnings=$(printf '%s\\n' \"$clean_output\" | grep -E -c '^WARNING \\[' || true)\n echo \"${errors}:${warnings}\"\n}\n\ncount_runner_error_lines() {\n local output=$1\n local clean_output\n clean_output=$(printf '%s\\n' \"$output\" | sed 's/\\x1b\\[[0-9;]*m//g')\n printf '%s\\n' \"$clean_output\" | grep -E -c '^ERROR \\[Runner\\]:' || true\n}\n\nensure_runner_failure_is_counted() {\n local errors=$1\n local status=$2\n local output=$3\n\n if [ \"$status\" -gt 1 ]; then\n local runner_errors\n runner_errors=$(count_runner_error_lines \"$output\")\n if [ \"$runner_errors\" -eq 0 ]; then\n errors=$((errors + 1))\n fi\n fi\n\n echo \"$errors\"\n}\n\ncount_info_lines() {\n local output=$1\n local clean_output\n clean_output=$(printf '%s\\n' \"$output\" | sed 's/\\x1b\\[[0-9;]*m//g')\n printf '%s\\n' \"$clean_output\" | grep -E -c '^INFO \\[' || true\n}\n\n# Detect pipeline type (Declarative or Scripted)\ndetect_pipeline_type() {\n local file=$1\n\n # Remove comments and check for pipeline block\n local first_meaningful\n first_meaningful=$(grep -v '^\\s*//' \"$file\" | grep -v '^\\s*

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

| grep -v '^\\s*\\*' | grep -v '^\\s*/\\*' | head -20)\n\n if echo \"$first_meaningful\" | grep -q '^\\s*pipeline\\s*{'; then\n echo \"declarative\"\n elif echo \"$first_meaningful\" | grep -q '^\\s*node\\s*[({]'; then\n echo \"scripted\"\n elif grep -q 'pipeline\\s*{' \"$file\"; then\n echo \"declarative\"\n elif grep -q 'node\\s*[({]' \"$file\"; then\n echo \"scripted\"\n # Declarative-specific keywords that appear even in malformed pipelines missing\n # the outer 'pipeline {}' block (e.g. stages { ... }, agent, environment, post).\n # 'stages {' is exclusive to Declarative syntax - scripted pipelines never use it.\n elif grep -qE '^\\s*stages\\s*\\{' \"$file\"; then\n echo \"declarative\"\n else\n echo \"unknown\"\n fi\n}\n\n# Run syntax validation based on pipeline type\nrun_syntax_validation() {\n local file=$1\n local detected_type=$2\n local type=$detected_type\n local errors=0\n local warnings=0\n\n print_section \"1. Syntax Validation\"\n\n if [ \"$detected_type\" == \"unknown\" ] && [ -n \"$ASSUME_TYPE\" ]; then\n type=\"$ASSUME_TYPE\"\n echo -e \"${BLUE}Pipeline type could not be auto-detected.${NC}\"\n echo -e \"${BLUE}Using assumed pipeline type: ${BOLD}$ASSUME_TYPE${NC}\"\n echo \"\"\n fi\n\n if [ \"$type\" == \"declarative\" ]; then\n if [ \"$detected_type\" == \"unknown\" ] && [ -n \"$ASSUME_TYPE\" ]; then\n echo -e \"Pipeline type: ${GREEN}Declarative (assumed)${NC}\"\n else\n echo -e \"Pipeline type: ${GREEN}Declarative${NC}\"\n fi\n echo \"\"\n\n local output=\"\"\n local script_status=0\n set +e\n output=$(run_validator_script \"$SCRIPT_DIR/validate_declarative.sh\" \"$file\")\n script_status=$?\n set -e\n echo \"$output\"\n\n local counts\n counts=$(count_error_warning_lines \"$output\")\n errors=${counts%%:*}\n warnings=${counts##*:}\n errors=$(ensure_runner_failure_is_counted \"$errors\" \"$script_status\" \"$output\")\n elif [ \"$type\" == \"scripted\" ]; then\n if [ \"$detected_type\" == \"unknown\" ] && [ -n \"$ASSUME_TYPE\" ]; then\n echo -e \"Pipeline type: ${GREEN}Scripted (assumed)${NC}\"\n else\n echo -e \"Pipeline type: ${GREEN}Scripted${NC}\"\n fi\n echo \"\"\n\n local output=\"\"\n local script_status=0\n set +e\n output=$(run_validator_script \"$SCRIPT_DIR/validate_scripted.sh\" \"$file\")\n script_status=$?\n set -e\n echo \"$output\"\n\n local counts\n counts=$(count_error_warning_lines \"$output\")\n errors=${counts%%:*}\n warnings=${counts##*:}\n errors=$(ensure_runner_failure_is_counted \"$errors\" \"$script_status\" \"$output\")\n else\n if [ \"$STRICT_UNKNOWN\" == true ]; then\n echo -e \"${RED}ERROR [TypeDetection]: Unable to classify file as Declarative or Scripted pipeline.${NC}\"\n echo \"HINT: no pipeline markers detected.\"\n echo \"HINT: use --assume-declarative or --assume-scripted only when intentional.\"\n errors=1\n warnings=0\n else\n echo -e \"${RED}ERROR [TypeDetection]: Unknown pipeline type handling is disabled by policy.${NC}\"\n errors=1\n warnings=0\n fi\n fi\n\n TOTAL_ERRORS=$((TOTAL_ERRORS + errors))\n TOTAL_WARNINGS=$((TOTAL_WARNINGS + warnings))\n\n if [ \"$errors\" -eq 0 ]; then\n echo \"\"\n echo -e \"${GREEN}${PASS_SYMBOL} Syntax validation passed${NC}\"\n return 0\n else\n echo \"\"\n echo -e \"${RED}${FAIL_SYMBOL} Syntax validation failed with $errors error(s)${NC}\"\n return 1\n fi\n}\n\n# Run security scan\nrun_security_scan() {\n local file=$1\n local errors=0\n local warnings=0\n local info=0\n\n print_section \"2. Security Scan\"\n\n if [ -r \"$SCRIPT_DIR/common_validation.sh\" ]; then\n # Run credential check via script (not sourced, to get proper output)\n echo -e \"${BLUE}Scanning for hardcoded credentials...${NC}\"\n echo \"\"\n\n local output\n local script_status=0\n set +e\n output=$(run_validator_script \"$SCRIPT_DIR/common_validation.sh\" check_credentials \"$file\")\n script_status=$?\n set -e\n echo \"$output\"\n\n local counts\n counts=$(count_error_warning_lines \"$output\")\n errors=${counts%%:*}\n warnings=${counts##*:}\n info=$(count_info_lines \"$output\")\n errors=$(ensure_runner_failure_is_counted \"$errors\" \"$script_status\" \"$output\")\n else\n echo -e \"${YELLOW}Warning: common_validation.sh not found or not readable, skipping security scan${NC}\"\n fi\n\n TOTAL_ERRORS=$((TOTAL_ERRORS + errors))\n TOTAL_WARNINGS=$((TOTAL_WARNINGS + warnings))\n TOTAL_INFO=$((TOTAL_INFO + info))\n\n if [ \"$errors\" -eq 0 ] && [ \"$warnings\" -eq 0 ]; then\n echo \"\"\n echo -e \"${GREEN}${PASS_SYMBOL} Security scan passed - no credentials detected${NC}\"\n return 0\n elif [ \"$errors\" -eq 0 ]; then\n echo \"\"\n echo -e \"${YELLOW}${WARN_SYMBOL} Security scan completed with $warnings warning(s)${NC}\"\n return 0\n else\n echo \"\"\n echo -e \"${RED}${FAIL_SYMBOL} Security scan failed with $errors error(s)${NC}\"\n return 1\n fi\n}\n\n# Run best practices check\nrun_best_practices() {\n local file=$1\n local errors=0\n local warnings=0\n\n print_section \"3. Best Practices Check\"\n\n if [ -r \"$SCRIPT_DIR/best_practices.sh\" ]; then\n local output\n local script_status=0\n set +e\n output=$(run_validator_script \"$SCRIPT_DIR/best_practices.sh\" \"$file\")\n script_status=$?\n set -e\n echo \"$output\"\n\n local counts\n counts=$(count_error_warning_lines \"$output\")\n errors=${counts%%:*}\n warnings=${counts##*:}\n errors=$(ensure_runner_failure_is_counted \"$errors\" \"$script_status\" \"$output\")\n else\n echo -e \"${YELLOW}Warning: best_practices.sh not found or not readable, skipping best practices check${NC}\"\n fi\n\n # Don't add to totals - best practices has its own scoring\n\n if [ \"$errors\" -eq 0 ]; then\n return 0\n else\n return 1\n fi\n}\n\n# Print final summary\nprint_summary() {\n local file=$1\n local syntax_result=$2\n local security_result=$3\n local practices_result=$4\n\n echo \"\"\n echo -e \"${BLUE}${BOLD}════════════════════════════════════════════════════════════════${NC}\"\n echo -e \"${BLUE}${BOLD} Validation Summary${NC}\"\n echo -e \"${BLUE}${BOLD}════════════════════════════════════════════════════════════════${NC}\"\n echo \"\"\n echo -e \"File: ${BOLD}$file${NC}\"\n echo \"\"\n\n # Syntax result\n if [ \"$RUN_SYNTAX\" == true ]; then\n if [ \"$syntax_result\" == \"0\" ]; then\n echo -e \" ${GREEN}${PASS_SYMBOL}${NC} Syntax Validation : ${GREEN}PASSED${NC}\"\n else\n echo -e \" ${RED}${FAIL_SYMBOL}${NC} Syntax Validation : ${RED}FAILED${NC}\"\n fi\n else\n echo -e \" ${BLUE}${SKIP_SYMBOL}${NC} Syntax Validation : ${BLUE}SKIPPED${NC}\"\n fi\n\n # Security result\n if [ \"$RUN_SECURITY\" == true ]; then\n if [ \"$security_result\" == \"0\" ]; then\n echo -e \" ${GREEN}${PASS_SYMBOL}${NC} Security Scan : ${GREEN}PASSED${NC}\"\n else\n echo -e \" ${RED}${FAIL_SYMBOL}${NC} Security Scan : ${RED}FAILED${NC}\"\n fi\n else\n echo -e \" ${BLUE}${SKIP_SYMBOL}${NC} Security Scan : ${BLUE}SKIPPED${NC}\"\n fi\n\n # Best practices result\n if [ \"$RUN_BEST_PRACTICES\" == true ]; then\n if [ \"$practices_result\" == \"0\" ]; then\n echo -e \" ${GREEN}${PASS_SYMBOL}${NC} Best Practices : ${GREEN}PASSED${NC}\"\n else\n echo -e \" ${YELLOW}${WARN_SYMBOL}${NC} Best Practices : ${YELLOW}REVIEW NEEDED${NC}\"\n fi\n else\n echo -e \" ${BLUE}${SKIP_SYMBOL}${NC} Best Practices : ${BLUE}SKIPPED${NC}\"\n fi\n\n echo \"\"\n echo -e \"${BLUE}────────────────────────────────────────────────────────────────${NC}\"\n\n # Overall result\n local overall_pass=true\n\n if [ \"$RUN_SYNTAX\" == true ] && [ \"$syntax_result\" != \"0\" ]; then\n overall_pass=false\n fi\n\n if [ \"$RUN_SECURITY\" == true ] && [ \"$security_result\" != \"0\" ]; then\n overall_pass=false\n fi\n\n # In strict mode, warnings also cause failure\n if [ \"$STRICT_MODE\" == true ] && [ \"$TOTAL_WARNINGS\" -gt 0 ]; then\n overall_pass=false\n fi\n\n echo \"\"\n if [ \"$overall_pass\" == true ]; then\n echo -e \" ${GREEN}${BOLD}${PASS_SYMBOL} VALIDATION PASSED${NC}\"\n if [ \"$TOTAL_WARNINGS\" -gt 0 ]; then\n echo -e \" (with $TOTAL_WARNINGS warning(s) - review recommended)\"\n fi\n echo \"\"\n return 0\n else\n echo -e \" ${RED}${BOLD}${FAIL_SYMBOL} VALIDATION FAILED${NC}\"\n if [ \"$STRICT_MODE\" == true ] && [ \"$TOTAL_WARNINGS\" -gt 0 ]; then\n echo -e \" (strict mode: $TOTAL_WARNINGS warning(s) treated as errors)\"\n fi\n echo \"\"\n return 1\n fi\n}\n\n# Parse command line arguments\nparse_args() {\n while [[ $# -gt 0 ]]; do\n case $1 in\n --syntax-only)\n RUN_SECURITY=false\n RUN_BEST_PRACTICES=false\n shift\n ;;\n --security-only)\n RUN_SYNTAX=false\n RUN_BEST_PRACTICES=false\n shift\n ;;\n --best-practices)\n RUN_SYNTAX=false\n RUN_SECURITY=false\n shift\n ;;\n --no-security)\n RUN_SECURITY=false\n shift\n ;;\n --no-best-practices)\n RUN_BEST_PRACTICES=false\n shift\n ;;\n --strict)\n STRICT_MODE=true\n shift\n ;;\n --assume-declarative)\n ASSUME_TYPE=\"declarative\"\n shift\n ;;\n --assume-scripted)\n ASSUME_TYPE=\"scripted\"\n shift\n ;;\n --strict-unknown)\n STRICT_UNKNOWN=true\n shift\n ;;\n -h|--help)\n usage\n ;;\n -*)\n echo -e \"${RED}Error: Unknown option: $1${NC}\"\n usage\n ;;\n *)\n JENKINSFILE=\"$1\"\n shift\n ;;\n esac\n done\n}\n\n# Main execution\nmain() {\n parse_args \"$@\"\n check_prerequisites\n\n # Validate input\n if [ -z \"${JENKINSFILE:-}\" ]; then\n echo -e \"${RED}Error: No Jenkinsfile specified${NC}\"\n usage\n fi\n\n if [ ! -f \"$JENKINSFILE\" ]; then\n echo -e \"${RED}Error: File '$JENKINSFILE' not found${NC}\"\n exit 2\n fi\n\n print_header\n echo -e \"Validating: ${BOLD}$JENKINSFILE${NC}\"\n\n # Detect pipeline type\n local pipeline_type\n pipeline_type=$(detect_pipeline_type \"$JENKINSFILE\")\n\n # Track results\n local syntax_result=0\n local security_result=0\n local practices_result=0\n\n # Run validations based on options\n if [ \"$RUN_SYNTAX\" == true ]; then\n run_syntax_validation \"$JENKINSFILE\" \"$pipeline_type\" || syntax_result=$?\n fi\n\n if [ \"$RUN_SECURITY\" == true ]; then\n run_security_scan \"$JENKINSFILE\" || security_result=$?\n fi\n\n if [ \"$RUN_BEST_PRACTICES\" == true ]; then\n run_best_practices \"$JENKINSFILE\" || practices_result=$?\n fi\n\n # Print summary and exit with appropriate code\n print_summary \"$JENKINSFILE\" \"$syntax_result\" \"$security_result\" \"$practices_result\"\n}\n\nmain \"$@\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":18631,"content_sha256":"ff91e46128becba7139c7be6cd3fea1110640fc5ac765c3652bc5cfbf30e6c0b"},{"filename":"scripts/validate_scripted.sh","content":"#!/bin/bash\n\n# Scripted Pipeline Validator\n# Validates Jenkins Scripted Pipeline syntax and structure\n\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\n\n# Colors for output\nRED='\\033[0;31m'\nYELLOW='\\033[1;33m'\nGREEN='\\033[0;32m'\nBLUE='\\033[0;34m'\nNC='\\033[0m' # No Color\n\n# Counters\nERRORS=0\nWARNINGS=0\nINFO=0\n\n# Validation result arrays\ndeclare -a ERROR_MESSAGES=()\ndeclare -a WARNING_MESSAGES=()\ndeclare -a INFO_MESSAGES=()\n\nusage() {\n echo \"Usage: $0 \u003cjenkinsfile>\"\n echo \"Validates a Scripted Jenkins Pipeline\"\n exit 1\n}\n\nlog_error() {\n local line=$1\n local message=$2\n ERROR_MESSAGES+=(\"ERROR [Line $line]: $message\")\n ((ERRORS++))\n}\n\nlog_warning() {\n local line=$1\n local message=$2\n WARNING_MESSAGES+=(\"WARNING [Line $line]: $message\")\n ((WARNINGS++))\n}\n\nlog_info() {\n local line=$1\n local message=$2\n INFO_MESSAGES+=(\"INFO [Line $line]: $message\")\n ((INFO++))\n}\n\n# Basic Groovy syntax validation\nvalidate_groovy_syntax() {\n local file=$1\n local line_num=0\n\n while IFS= read -r line; do\n ((line_num++))\n\n # Skip comments and empty lines\n if echo \"$line\" | grep -qE '^\\s*(//|$)'; then\n continue\n fi\n\n # Check for unmatched braces (simple check)\n local open_braces=$(echo \"$line\" | grep -o '{' | wc -l)\n local close_braces=$(echo \"$line\" | grep -o '}' | wc -l)\n\n # Check for unmatched quotes\n # Skip lines that are part of multi-line strings (triple quotes or heredocs)\n if echo \"$line\" | grep -qE \"'''|\\\"\\\"\\\"\"; then\n continue\n fi\n\n # Skip lines inside multi-line sh blocks (contain just string content)\n if echo \"$line\" | grep -qE '^\\s*(echo|mkdir|make|cd|cmake|mvn|gradle|npm|yarn|kubectl|docker|git)\\s'; then\n continue\n fi\n\n # Remove escaped quotes before counting\n local clean_line=$(echo \"$line\" | sed \"s/\\\\\\\\'//g\" | sed 's/\\\\\"//g')\n local single_quotes=$(echo \"$clean_line\" | grep -o \"'\" | wc -l)\n local double_quotes=$(echo \"$clean_line\" | grep -o '\"' | wc -l)\n\n # Only flag truly unbalanced quotes (not in multi-line contexts)\n if (( single_quotes % 2 != 0 )); then\n # Only error if not a shell command continuation\n if ! echo \"$line\" | grep -qE \"sh\\s+'''|sh\\s*\\(|script:|'''\"; then\n log_error \"$line_num\" \"Unmatched single quote detected\"\n fi\n fi\n\n if (( double_quotes % 2 != 0 )); then\n # Only error if not a shell command continuation\n if ! echo \"$line\" | grep -qE 'sh\\s+\"\"\"|sh\\s*\\(|script:|\"\"\"'; then\n log_error \"$line_num\" \"Unmatched double quote detected\"\n fi\n fi\n\n # Note: Per-line parenthesis check removed to avoid false positives\n # Multi-line constructs like properties([...]) are valid Groovy\n done \u003c \"$file\"\n\n # Check overall brace balance\n local total_open=$(grep -o '{' \"$file\" | wc -l)\n local total_close=$(grep -o '}' \"$file\" | wc -l)\n\n if (( total_open != total_close )); then\n log_error \"EOF\" \"Unbalanced braces: $total_open opening, $total_close closing\"\n fi\n}\n\n# Validate node block usage\nvalidate_node_blocks() {\n local file=$1\n local line_num=0\n local has_node=false\n\n while IFS= read -r line; do\n ((line_num++))\n\n # Check for node blocks\n if echo \"$line\" | grep -qE '^\\s*node\\s*(\\(|{)'; then\n has_node=true\n\n # Check if node has label or is empty\n if echo \"$line\" | grep -qE '^\\s*node\\s*\\(\\s*\\)'; then\n log_warning \"$line_num\" \"Empty node() - consider using node('label') for specific agents\"\n log_warning \"$line_num\" \" → Use: node('docker') or node('linux') for specific agents\"\n fi\n fi\n\n # Check for stage definitions outside node blocks (potential issue)\n if echo \"$line\" | grep -qE '^\\s*stage\\s*\\(' && [[ \"$has_node\" == false ]]; then\n log_info \"$line_num\" \"Stage defined outside node block - ensure this is intentional\"\n fi\n done \u003c \"$file\"\n\n if [[ \"$has_node\" == false ]]; then\n log_warning 1 \"No node blocks found - scripted pipelines typically use node blocks for agent allocation\"\n log_warning 1 \" → Wrap your pipeline code in node { ... } block\"\n fi\n}\n\n# Check for error handling\nvalidate_error_handling() {\n local file=$1\n local line_num=0\n local has_try=false\n local has_catch=false\n local has_finally=false\n\n while IFS= read -r line; do\n ((line_num++))\n\n # Check for try-catch-finally blocks\n if echo \"$line\" | grep -qE '^\\s*try\\s*{'; then\n has_try=true\n local try_line=$line_num\n\n # Look for corresponding catch/finally in next 100 lines\n local check_line=$line_num\n local found_catch=false\n local found_finally=false\n\n for ((i=0; i\u003c100; i++)); do\n ((check_line++))\n local next_line=$(sed -n \"${check_line}p\" \"$file\")\n\n # Match catch with optional leading } for \"} catch (\" pattern\n if echo \"$next_line\" | grep -qE 'catch\\s*\\('; then\n found_catch=true\n fi\n\n # Match finally with optional leading } for \"} finally {\" pattern\n if echo \"$next_line\" | grep -qE 'finally\\s*\\{'; then\n found_finally=true\n break\n fi\n done\n\n if [[ \"$found_catch\" == false ]]; then\n log_warning \"$try_line\" \"try block without catch - consider adding error handling\"\n log_warning \"$try_line\" \" → Add: catch (Exception e) { ... }\"\n fi\n fi\n\n # Check for sh/bat commands without try-catch\n if echo \"$line\" | grep -qE '\\s+(sh|bat)\\s*[\"\\047(]' && ! echo \"$line\" | grep -q 'returnStatus'; then\n # This is a heuristic - check if we're in a try block by looking backward\n # Also need to check we're not in catch/finally (which would be inside try)\n local in_try=false\n local in_catch_finally=false\n local check_back=$line_num\n local brace_depth=0\n\n for ((i=0; i\u003c100 && check_back>0; i++)); do\n ((check_back--))\n local prev_line=$(sed -n \"${check_back}p\" \"$file\")\n\n # Track brace depth to understand scope\n local close_braces=$(echo \"$prev_line\" | grep -o '}' | wc -l)\n local open_braces=$(echo \"$prev_line\" | grep -o '{' | wc -l)\n brace_depth=$((brace_depth + close_braces - open_braces))\n\n # Check if we find a try block at the right depth\n if echo \"$prev_line\" | grep -qE '^\\s*try\\s*\\{'; then\n in_try=true\n break\n fi\n\n # If we're in catch or finally block, we're effectively in error handling\n if echo \"$prev_line\" | grep -qE 'catch\\s*\\(|finally\\s*\\{'; then\n in_catch_finally=true\n break\n fi\n\n # Stop if we exit the current block structure too far\n if [ $brace_depth -gt 3 ]; then\n break\n fi\n done\n\n if [[ \"$in_try\" == false ]] && [[ \"$in_catch_finally\" == false ]]; then\n log_info \"$line_num\" \"Consider wrapping shell commands in try-catch for better error handling\"\n fi\n fi\n done \u003c \"$file\"\n}\n\n# Validate @NonCPS usage\nvalidate_noncps() {\n local file=$1\n local line_num=0\n\n while IFS= read -r line; do\n ((line_num++))\n\n if echo \"$line\" | grep -q '@NonCPS'; then\n # Check if next lines contain problematic pipeline steps\n local check_line=$line_num\n local found_issue=false\n\n for ((i=0; i\u003c30; i++)); do\n ((check_line++))\n local next_line=$(sed -n \"${check_line}p\" \"$file\")\n\n # Check for async pipeline steps in @NonCPS method\n if echo \"$next_line\" | grep -qE '\\s+(sh|bat|sleep|timeout|node|stage)\\s*[\"\\047(]'; then\n log_error \"$check_line\" \"Cannot use pipeline steps (sh, sleep, etc.) in @NonCPS methods\"\n log_error \"$check_line\" \" → Remove @NonCPS or refactor to not use async pipeline steps\"\n found_issue=true\n break\n fi\n\n # Stop at method end\n if echo \"$next_line\" | grep -qE '^\\s*}\\s*

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

; then\n break\n fi\n done\n fi\n done \u003c \"$file\"\n}\n\n# Check for proper variable declarations\nvalidate_variables() {\n local file=$1\n local line_num=0\n\n # Track declared variables to avoid false positives on reassignments\n # Using a simple string list instead of associative array for bash 3.x compatibility\n local declared_vars=\"\"\n\n # First pass: collect all declared variables\n while IFS= read -r line; do\n ((line_num++))\n\n # Track variables declared with def, String, Integer, Boolean, Map, List\n if echo \"$line\" | grep -qE '^\\s*(def|String|Integer|Boolean|Map|List)\\s+([a-zA-Z_][a-zA-Z0-9_]*)'; then\n local var_name=$(echo \"$line\" | grep -oE '(def|String|Integer|Boolean|Map|List)\\s+[a-zA-Z_][a-zA-Z0-9_]*' | awk '{print $2}')\n if [[ -n \"$var_name\" ]]; then\n declared_vars=\"$declared_vars:$var_name:\"\n fi\n fi\n done \u003c \"$file\"\n\n # Second pass: check for issues\n line_num=0\n while IFS= read -r line; do\n ((line_num++))\n\n # Skip comments\n if echo \"$line\" | grep -qE '^\\s*//'; then\n continue\n fi\n\n # Check for variable assignments without def\n if echo \"$line\" | grep -qE '^\\s*[a-zA-Z_][a-zA-Z0-9_]*\\s*=' && ! echo \"$line\" | grep -qE '^\\s*(def|String|Integer|Boolean|Map|List)'; then\n # Check if it's not a property assignment (has dot notation)\n if ! echo \"$line\" | grep -q '\\.'; then\n # Extract variable name\n local var_name=$(echo \"$line\" | grep -oE '^\\s*[a-zA-Z_][a-zA-Z0-9_]*' | tr -d ' ')\n # Only warn if variable was not previously declared\n if [[ \"$declared_vars\" != *\":$var_name:\"* ]]; then\n log_info \"$line_num\" \"Consider using 'def' for variable declaration for better scoping\"\n log_info \"$line_num\" \" → Use: def myVar = ... instead of myVar = ...\"\n fi\n fi\n fi\n\n # Check for proper string interpolation\n if echo \"$line\" | grep -q '\\$[A-Z_][A-Z0-9_]*' && ! echo \"$line\" | grep -q '\"\\

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…

; then\n log_warning \"$line_num\" \"Variable interpolation should use double quotes, not single quotes\"\n log_warning \"$line_num\" \" → Use: \\\"text \\${VAR}\\\" instead of 'text \\$VAR'\"\n fi\n done \u003c \"$file\"\n}\n\n# Check for common anti-patterns\nvalidate_antipatterns() {\n local file=$1\n local line_num=0\n\n while IFS= read -r line; do\n ((line_num++))\n\n # Check for JsonSlurper on controller\n if echo \"$line\" | grep -q 'JsonSlurper'; then\n log_warning \"$line_num\" \"JsonSlurper runs on controller - consider using jq on agent instead\"\n log_warning \"$line_num\" \" → Use: sh(script: 'jq ...', returnStdout: true)\"\n fi\n\n # Check for HttpRequest without proper delegation\n if echo \"$line\" | grep -qE 'HttpRequest|httpRequest' && ! echo \"$line\" | grep -q 'sh.*curl'; then\n log_warning \"$line_num\" \"Consider using curl/wget on agent instead of HTTP libraries on controller\"\n log_warning \"$line_num\" \" → Use: sh 'curl -s http://...' for better performance\"\n fi\n\n # Check for readFile without size checks\n if echo \"$line\" | grep -q 'readFile' && ! echo \"$line\" | grep -q 'encoding'; then\n log_info \"$line_num\" \"Consider checking file size before readFile to avoid memory issues\"\n fi\n\n # Check for writeFile\n if echo \"$line\" | grep -q 'writeFile'; then\n log_info \"$line_num\" \"Ensure writeFile is writing to workspace, not arbitrary locations\"\n fi\n done \u003c \"$file\"\n}\n\n# Check for parallel execution opportunities\nvalidate_parallel_usage() {\n local file=$1\n local line_num=0\n local stage_count=0\n\n while IFS= read -r line; do\n ((line_num++))\n\n if echo \"$line\" | grep -qE '^\\s*stage\\s*\\('; then\n ((stage_count++))\n fi\n done \u003c \"$file\"\n\n if [ $stage_count -gt 3 ] && ! grep -q 'parallel' \"$file\"; then\n log_info 1 \"Consider using parallel execution for independent stages to improve build time\"\n log_info 1 \" → See: references/scripted_syntax.md#parallel-execution\"\n fi\n}\n\n# Check for timestamps and other useful wrappers\nvalidate_wrappers() {\n local file=$1\n\n if ! grep -q 'timestamps' \"$file\"; then\n log_info 1 \"Consider adding timestamps() wrapper for better log readability\"\n log_info 1 \" → Wrap pipeline: timestamps { node { ... } }\"\n fi\n\n if ! grep -q 'ansiColor' \"$file\" && ! grep -q 'xterm' \"$file\"; then\n log_info 1 \"Consider adding ansiColor wrapper for colorized output\"\n log_info 1 \" → Wrap pipeline: ansiColor('xterm') { ... }\"\n fi\n}\n\n# Main validation function\nvalidate_scripted() {\n local file=$1\n\n echo -e \"${BLUE}=== Validating Scripted Pipeline ===${NC}\"\n echo \"File: $file\"\n echo \"\"\n\n # Run all validation checks (|| true prevents early exit on validation failures)\n validate_groovy_syntax \"$file\" || true\n validate_node_blocks \"$file\" || true\n validate_error_handling \"$file\" || true\n validate_noncps \"$file\" || true\n validate_variables \"$file\" || true\n validate_antipatterns \"$file\" || true\n validate_parallel_usage \"$file\" || true\n validate_wrappers \"$file\" || true\n\n # Print results\n echo -e \"${BLUE}=== Validation Results ===${NC}\"\n echo \"\"\n\n if [ ${#ERROR_MESSAGES[@]} -gt 0 ]; then\n echo -e \"${RED}ERRORS (${ERRORS}):${NC}\"\n for msg in \"${ERROR_MESSAGES[@]}\"; do\n echo -e \"${RED}$msg${NC}\"\n done\n echo \"\"\n fi\n\n if [ ${#WARNING_MESSAGES[@]} -gt 0 ]; then\n echo -e \"${YELLOW}WARNINGS (${WARNINGS}):${NC}\"\n for msg in \"${WARNING_MESSAGES[@]}\"; do\n echo -e \"${YELLOW}$msg${NC}\"\n done\n echo \"\"\n fi\n\n if [ ${#INFO_MESSAGES[@]} -gt 0 ]; then\n echo -e \"${BLUE}INFO (${INFO}):${NC}\"\n for msg in \"${INFO_MESSAGES[@]}\"; do\n echo -e \"${BLUE}$msg${NC}\"\n done\n echo \"\"\n fi\n\n # Summary\n echo -e \"${BLUE}=== Summary ===${NC}\"\n if [ $ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then\n echo -e \"${GREEN}✓ Validation passed with no errors or warnings${NC}\"\n return 0\n elif [ $ERRORS -eq 0 ]; then\n echo -e \"${YELLOW}✓ Validation passed with $WARNINGS warning(s)${NC}\"\n return 0\n else\n echo -e \"${RED}✗ Validation failed with $ERRORS error(s) and $WARNINGS warning(s)${NC}\"\n return 1\n fi\n}\n\n# Main execution\nif [ $# -ne 1 ]; then\n usage\nfi\n\nJENKINSFILE=\"$1\"\n\nif [ ! -f \"$JENKINSFILE\" ]; then\n echo -e \"${RED}Error: File '$JENKINSFILE' not found${NC}\"\n exit 1\nfi\n\nvalidate_scripted \"$JENKINSFILE\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":15505,"content_sha256":"e51307756c764c4d04a91e25efd4f55296cd2c807cc894dba4782c8fe8d1ebae"},{"filename":"scripts/validate_shared_library.sh","content":"#!/bin/bash\n\n# Shared Library Validator\n# Validates Jenkins Shared Library files (vars/*.groovy, src/**/*.groovy)\n\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\n\n# Colors for output\nRED='\\033[0;31m'\nYELLOW='\\033[1;33m'\nGREEN='\\033[0;32m'\nBLUE='\\033[0;34m'\nCYAN='\\033[0;36m'\nNC='\\033[0m' # No Color\nBOLD='\\033[1m'\n\n# Counters\nERRORS=0\nWARNINGS=0\nINFO=0\n\n# Validation result arrays\ndeclare -a ERROR_MESSAGES=()\ndeclare -a WARNING_MESSAGES=()\ndeclare -a INFO_MESSAGES=()\n\nusage() {\n echo -e \"${BOLD}Usage:${NC} $0 \u003cfile_or_directory>\"\n echo \"\"\n echo \"Validates Jenkins Shared Library Groovy files.\"\n echo \"\"\n echo -e \"${BOLD}Examples:${NC}\"\n echo \" $0 vars/myStep.groovy # Validate single global variable file\"\n echo \" $0 vars/ # Validate all vars/*.groovy files\"\n echo \" $0 src/ # Validate src/**/*.groovy files\"\n echo \" $0 . # Validate entire shared library\"\n echo \"\"\n echo -e \"${BOLD}File Types:${NC}\"\n echo \" vars/*.groovy - Global variable definitions (callable steps)\"\n echo \" src/**/*.groovy - Groovy source classes\"\n echo \"\"\n exit 1\n}\n\nlog_error() {\n local line=$1\n local message=$2\n ERROR_MESSAGES+=(\"ERROR [Line $line]: $message\")\n ERRORS=$((ERRORS + 1))\n}\n\nlog_warning() {\n local line=$1\n local message=$2\n WARNING_MESSAGES+=(\"WARNING [Line $line]: $message\")\n WARNINGS=$((WARNINGS + 1))\n}\n\nlog_info() {\n local line=$1\n local message=$2\n INFO_MESSAGES+=(\"INFO [Line $line]: $message\")\n INFO=$((INFO + 1))\n}\n\n# Detect file type within shared library\ndetect_library_file_type() {\n local file=$1\n local filepath=$(realpath \"$file\" 2>/dev/null || echo \"$file\")\n\n if [[ \"$filepath\" == */vars/*.groovy ]]; then\n echo \"vars\"\n elif [[ \"$filepath\" == */src/*.groovy ]] || [[ \"$filepath\" == */src/**/*.groovy ]]; then\n echo \"src\"\n elif [[ \"$file\" == *.groovy ]]; then\n # Check content to determine type\n if grep -qE '^\\s*def\\s+call\\s*\\(' \"$file\" 2>/dev/null; then\n echo \"vars\"\n elif grep -qE '^\\s*(class|interface|enum|trait)\\s+' \"$file\" 2>/dev/null; then\n echo \"src\"\n else\n echo \"unknown\"\n fi\n else\n echo \"unknown\"\n fi\n}\n\n# Validate vars/*.groovy global variable files\nvalidate_vars_file() {\n local file=$1\n local filename=$(basename \"$file\" .groovy)\n local line_num=0\n local has_call_method=false\n local has_doc_comment=false\n local in_call_method=false\n local brace_count=0\n\n echo -e \"${BLUE}=== Validating Global Variable: ${BOLD}$filename${NC}${BLUE} ===${NC}\"\n echo \"File: $file\"\n echo \"\"\n\n # Check for call() method\n if grep -qE '^\\s*def\\s+call\\s*\\(' \"$file\"; then\n has_call_method=true\n fi\n\n # Check for documentation comment\n if grep -qE '^\\s*/\\*\\*' \"$file\"; then\n has_doc_comment=true\n fi\n\n # Line by line validation\n while IFS= read -r line || [[ -n \"$line\" ]]; do\n line_num=$((line_num + 1))\n\n # Skip empty lines and comments for some checks\n if [[ -z \"${line// }\" ]] || [[ \"$line\" =~ ^[[:space:]]*//.* ]] || [[ \"$line\" =~ ^[[:space:]]*\\*.* ]]; then\n continue\n fi\n\n # Check for hardcoded credentials\n if echo \"$line\" | grep -qiE '(password|secret|token|api[_-]?key)\\s*=\\s*[\"\\047][a-zA-Z0-9]'; then\n log_error \"$line_num\" \"Potential hardcoded credential detected\"\n log_error \"$line_num\" \" → Use credentials() or parameters instead\"\n fi\n\n # Check for @NonCPS annotation usage\n if echo \"$line\" | grep -q '@NonCPS'; then\n # Check next non-empty line for pipeline steps\n local next_lines=$(tail -n +$((line_num + 1)) \"$file\" | head -20)\n if echo \"$next_lines\" | grep -qE '^\\s*(sh|bat|echo|checkout|archiveArtifacts|junit|input|build)\\s'; then\n log_error \"$line_num\" \"@NonCPS method contains pipeline steps (sh, echo, etc.)\"\n log_error \"$line_num\" \" → Pipeline steps cannot be used in @NonCPS methods\"\n log_error \"$line_num\" \" → Move pipeline steps outside the @NonCPS method\"\n fi\n fi\n\n # Check for proper error handling in pipeline steps\n if echo \"$line\" | grep -qE '^\\s*sh\\s+[\"\\047]'; then\n # Check if inside try block\n local context=$(head -n $line_num \"$file\" | tail -20)\n if ! echo \"$context\" | grep -q 'try\\s*{'; then\n log_info \"$line_num\" \"Consider wrapping sh step in try-catch for error handling\"\n fi\n fi\n\n # Check for return type documentation\n if echo \"$line\" | grep -qE '^\\s*def\\s+call\\s*\\(' && ! echo \"$line\" | grep -qE '^\\s*(String|boolean|int|def|void|Map|List)'; then\n log_info \"$line_num\" \"Consider specifying return type for call() method\"\n fi\n\n # Check for deprecated methods\n if echo \"$line\" | grep -qE 'new\\s+File\\s*\\('; then\n log_warning \"$line_num\" \"Using 'new File()' - prefer readFile/writeFile for pipeline compatibility\"\n log_warning \"$line_num\" \" → Use readFile('path') instead of new File('path').text\"\n fi\n\n # Check for URL/HTTP on controller\n if echo \"$line\" | grep -qE 'new\\s+URL\\s*\\(' || echo \"$line\" | grep -q 'HttpURLConnection'; then\n log_warning \"$line_num\" \"HTTP operations run on controller - use httpRequest step or curl on agent\"\n log_warning \"$line_num\" \" → Use: sh 'curl ...' or httpRequest plugin\"\n fi\n\n # Check for JsonSlurper on controller\n if echo \"$line\" | grep -q 'JsonSlurper'; then\n log_warning \"$line_num\" \"JsonSlurper runs on controller - consider using jq on agent for large files\"\n log_warning \"$line_num\" \" → For small JSON: JsonSlurperClassic is safer\"\n log_warning \"$line_num\" \" → For large JSON: sh 'jq ... file.json'\"\n fi\n\n # Check for proper CPS handling with closures\n if echo \"$line\" | grep -qE '\\.(each|collect|find|findAll|any|every)\\s*\\{'; then\n # Check if marked @NonCPS\n local prev_lines=$(head -n $line_num \"$file\" | tail -10)\n if ! echo \"$prev_lines\" | grep -q '@NonCPS'; then\n log_info \"$line_num\" \"Groovy closure detected - ensure method is @NonCPS if not using pipeline steps\"\n log_info \"$line_num\" \" → Closures like .each{}, .collect{} require @NonCPS annotation\"\n fi\n fi\n\n # Check for environment variable access\n if echo \"$line\" | grep -qE 'System\\.getenv\\s*\\('; then\n log_warning \"$line_num\" \"Using System.getenv() - prefer env.VAR_NAME for pipeline compatibility\"\n fi\n\n # Check for sleep usage\n if echo \"$line\" | grep -qE 'Thread\\.sleep\\s*\\('; then\n log_warning \"$line_num\" \"Using Thread.sleep() - prefer sleep() step for pipeline compatibility\"\n fi\n\n done \u003c \"$file\"\n\n # Post-file checks\n if [ \"$has_call_method\" = false ]; then\n log_warning \"1\" \"No call() method found - file may not be callable as a step\"\n log_warning \"1\" \" → Add: def call(Map config = [:]) { ... }\"\n fi\n\n if [ \"$has_doc_comment\" = false ]; then\n log_info \"1\" \"No documentation comment found - consider adding /** ... */ block\"\n log_info \"1\" \" → Documents step usage for Pipeline Syntax generator\"\n fi\n\n # Check file naming convention\n if [[ ! \"$filename\" =~ ^[a-z][a-zA-Z0-9]*$ ]]; then\n log_warning \"1\" \"Filename '$filename' should be camelCase starting with lowercase\"\n log_warning \"1\" \" → Convention: myStepName.groovy\"\n fi\n}\n\n# Validate src/**/*.groovy class files\nvalidate_src_file() {\n local file=$1\n local filename=$(basename \"$file\" .groovy)\n local line_num=0\n local has_package=false\n local has_class=false\n local has_serializable=false\n\n echo -e \"${BLUE}=== Validating Source Class: ${BOLD}$filename${NC}${BLUE} ===${NC}\"\n echo \"File: $file\"\n echo \"\"\n\n # Check for package declaration\n if grep -qE '^\\s*package\\s+' \"$file\"; then\n has_package=true\n fi\n\n # Check for class/interface/enum\n if grep -qE '^\\s*(class|interface|enum|trait)\\s+' \"$file\"; then\n has_class=true\n fi\n\n # Check for Serializable\n if grep -q 'implements.*Serializable' \"$file\"; then\n has_serializable=true\n fi\n\n while IFS= read -r line || [[ -n \"$line\" ]]; do\n line_num=$((line_num + 1))\n\n # Skip empty lines and comments\n if [[ -z \"${line// }\" ]] || [[ \"$line\" =~ ^[[:space:]]*//.* ]]; then\n continue\n fi\n\n # Check for hardcoded credentials\n if echo \"$line\" | grep -qiE '(password|secret|token|api[_-]?key)\\s*=\\s*[\"\\047][a-zA-Z0-9]'; then\n log_error \"$line_num\" \"Potential hardcoded credential detected\"\n fi\n\n # Check for proper import usage\n if echo \"$line\" | grep -qE '^\\s*import\\s+' && echo \"$line\" | grep -q '\\*'; then\n log_info \"$line_num\" \"Wildcard import detected - prefer explicit imports\"\n fi\n\n # Check for static method usage in CPS context\n if echo \"$line\" | grep -qE 'static.*def\\s+' && ! grep -q '@NonCPS' \"$file\"; then\n log_info \"$line_num\" \"Static method detected - ensure CPS compatibility or add @NonCPS\"\n fi\n\n done \u003c \"$file\"\n\n # Post-file checks\n if [ \"$has_class\" = true ] && [ \"$has_serializable\" = false ]; then\n log_warning \"1\" \"Class does not implement Serializable - may cause CPS issues\"\n log_warning \"1\" \" → Add: implements Serializable\"\n fi\n\n if [ \"$has_class\" = true ] && [ \"$has_package\" = false ]; then\n log_warning \"1\" \"No package declaration - add package statement\"\n log_warning \"1\" \" → Add: package com.example.jenkins\"\n fi\n\n # Check class naming matches filename\n local class_name=$(grep -oE '^\\s*(class|interface|enum|trait)\\s+([A-Z][a-zA-Z0-9]*)' \"$file\" | head -1 | awk '{print $2}')\n if [ -n \"$class_name\" ] && [ \"$class_name\" != \"$filename\" ]; then\n log_error \"1\" \"Class name '$class_name' does not match filename '$filename'\"\n fi\n}\n\n# Print validation results\nprint_results() {\n echo \"\"\n echo -e \"${BLUE}=== Validation Results ===${NC}\"\n echo \"\"\n\n if [ ${#ERROR_MESSAGES[@]} -gt 0 ]; then\n echo -e \"${RED}ERRORS (${ERRORS}):${NC}\"\n for msg in \"${ERROR_MESSAGES[@]}\"; do\n echo -e \"${RED}$msg${NC}\"\n done\n echo \"\"\n fi\n\n if [ ${#WARNING_MESSAGES[@]} -gt 0 ]; then\n echo -e \"${YELLOW}WARNINGS (${WARNINGS}):${NC}\"\n for msg in \"${WARNING_MESSAGES[@]}\"; do\n echo -e \"${YELLOW}$msg${NC}\"\n done\n echo \"\"\n fi\n\n if [ ${#INFO_MESSAGES[@]} -gt 0 ]; then\n echo -e \"${BLUE}INFO (${INFO}):${NC}\"\n for msg in \"${INFO_MESSAGES[@]}\"; do\n echo -e \"${BLUE}$msg${NC}\"\n done\n echo \"\"\n fi\n\n echo -e \"${BLUE}=== Summary ===${NC}\"\n if [ $ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then\n echo -e \"${GREEN}✓ Validation passed with no errors or warnings${NC}\"\n elif [ $ERRORS -eq 0 ]; then\n echo -e \"${YELLOW}⚠ Validation passed with ${WARNINGS} warning(s)${NC}\"\n else\n echo -e \"${RED}✗ Validation failed with ${ERRORS} error(s) and ${WARNINGS} warning(s)${NC}\"\n fi\n}\n\n# Validate a single file\nvalidate_file() {\n local file=$1\n local file_type=$(detect_library_file_type \"$file\")\n\n case \"$file_type\" in\n vars)\n validate_vars_file \"$file\"\n ;;\n src)\n validate_src_file \"$file\"\n ;;\n unknown)\n echo -e \"${YELLOW}Warning: Could not determine file type for $file${NC}\"\n echo \"Treating as vars file...\"\n validate_vars_file \"$file\"\n ;;\n esac\n}\n\n# Validate directory of files\nvalidate_directory() {\n local dir=$1\n local file_count=0\n\n echo -e \"${CYAN}════════════════════════════════════════════════════════════════════════════════${NC}\"\n echo -e \"${CYAN} ${BOLD}Jenkins Shared Library Validator${NC}\"\n echo -e \"${CYAN}════════════════════════════════════════════════════════════════════════════════${NC}\"\n echo \"\"\n echo -e \"Directory: ${BOLD}$dir${NC}\"\n echo \"\"\n\n # Find and validate vars files\n if [ -d \"$dir/vars\" ]; then\n echo -e \"${BLUE}--- Validating vars/ directory ---${NC}\"\n for file in \"$dir/vars\"/*.groovy; do\n if [ -f \"$file\" ]; then\n file_count=$((file_count + 1))\n validate_file \"$file\"\n echo \"\"\n fi\n done\n fi\n\n # Find and validate src files\n if [ -d \"$dir/src\" ]; then\n echo -e \"${BLUE}--- Validating src/ directory ---${NC}\"\n while IFS= read -r file; do\n if [ -f \"$file\" ]; then\n file_count=$((file_count + 1))\n validate_file \"$file\"\n echo \"\"\n fi\n done \u003c \u003c(find \"$dir/src\" -name \"*.groovy\" -type f 2>/dev/null)\n fi\n\n # If no vars or src, validate all groovy files\n if [ $file_count -eq 0 ]; then\n echo -e \"${YELLOW}No vars/ or src/ directory found. Validating all .groovy files...${NC}\"\n while IFS= read -r file; do\n if [ -f \"$file\" ]; then\n file_count=$((file_count + 1))\n validate_file \"$file\"\n echo \"\"\n fi\n done \u003c \u003c(find \"$dir\" -name \"*.groovy\" -type f 2>/dev/null)\n fi\n\n if [ $file_count -eq 0 ]; then\n echo -e \"${YELLOW}No Groovy files found in $dir${NC}\"\n exit 0\n fi\n\n echo -e \"${CYAN}════════════════════════════════════════════════════════════════════════════════${NC}\"\n echo -e \" ${BOLD}Validated ${file_count} file(s)${NC}\"\n echo -e \"${CYAN}════════════════════════════════════════════════════════════════════════════════${NC}\"\n}\n\n# Main execution\nif [ $# -ne 1 ]; then\n usage\nfi\n\nTARGET=\"$1\"\n\nif [ -f \"$TARGET\" ]; then\n echo -e \"${CYAN}════════════════════════════════════════════════════════════════════════════════${NC}\"\n echo -e \"${CYAN} ${BOLD}Jenkins Shared Library Validator${NC}\"\n echo -e \"${CYAN}════════════════════════════════════════════════════════════════════════════════${NC}\"\n echo \"\"\n validate_file \"$TARGET\"\n print_results\nelif [ -d \"$TARGET\" ]; then\n validate_directory \"$TARGET\"\n print_results\nelse\n echo -e \"${RED}Error: '$TARGET' is not a valid file or directory${NC}\"\n exit 2\nfi\n\n# Exit with appropriate code\nif [ $ERRORS -gt 0 ]; then\n exit 1\nfi\nexit 0","content_type":"application/x-sh; charset=utf-8","language":"bash","size":15638,"content_sha256":"1bb9ef424fdd44c61d99e0ee89477b1440ba4c615d8a8ea95de30832f5a75855"},{"filename":"tests/run_local_ci.sh","content":"#!/usr/bin/env bash\n\n# Local CI entrypoint for jenkinsfile-validator regressions.\n\nset -euo pipefail\n\nTEST_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nSKILL_DIR=\"$(cd \"$TEST_DIR/..\" && pwd)\"\nREGRESSION_TEST=\"$SKILL_DIR/tests/test_validate_jenkinsfile.sh\"\n\nshell_files=()\nfor file in \"$SKILL_DIR\"/scripts/*.sh \"$SKILL_DIR\"/tests/*.sh; do\n if [ -f \"$file\" ]; then\n shell_files+=(\"$file\")\n fi\ndone\n\nif [ \"${#shell_files[@]}\" -eq 0 ]; then\n echo \"ERROR: no shell files found for syntax checks\"\n exit 2\nfi\n\necho \"Running shell syntax checks...\"\nbash -n \"${shell_files[@]}\"\n\necho \"Running regression suite...\"\nbash \"$REGRESSION_TEST\"\n\necho \"PASS: jenkinsfile-validator local CI checks\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":707,"content_sha256":"2b0f209d1a567c572b354aeaf4671d61ecebe29eefcad2aa9e5ffff52b69929f"},{"filename":"tests/test_validate_jenkinsfile.sh","content":"#!/usr/bin/env bash\n\n# Regression tests for scripts/validate_jenkinsfile.sh\n\nset -euo pipefail\n\nTEST_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nSKILL_DIR=\"$(cd \"$TEST_DIR/..\" && pwd)\"\nVALIDATOR_SOURCE=\"$SKILL_DIR/scripts/validate_jenkinsfile.sh\"\n\nSANDBOX_ROOT=\"$(mktemp -d)\"\ntrap 'rm -rf \"$SANDBOX_ROOT\"' EXIT\n\nPASS_COUNT=0\nFAIL_COUNT=0\nTEST_OUTPUT=\"\"\nTEST_STATUS=0\n\nprepare_sandbox() {\n local name=$1\n local sandbox=\"$SANDBOX_ROOT/$name\"\n\n mkdir -p \"$sandbox/skill/scripts\" \"$sandbox/work\"\n cp \"$SKILL_DIR/scripts/\"*.sh \"$sandbox/skill/scripts/\"\n chmod +x \"$sandbox/skill/scripts/\"*.sh\n\n echo \"$sandbox\"\n}\n\nstrip_ansi() {\n printf '%s\\n' \"$1\" | sed 's/\\x1b\\[[0-9;]*m//g'\n}\n\nrun_validator() {\n local sandbox=$1\n shift\n local args=(\"$@\")\n\n set +e\n TEST_OUTPUT=$(bash \"$sandbox/skill/scripts/validate_jenkinsfile.sh\" \"${args[@]}\" 2>&1)\n TEST_STATUS=$?\n set -e\n}\n\npass() {\n local message=$1\n echo \"PASS: $message\"\n PASS_COUNT=$((PASS_COUNT + 1))\n}\n\nfail() {\n local message=$1\n echo \"FAIL: $message\"\n FAIL_COUNT=$((FAIL_COUNT + 1))\n}\n\nassert_exit() {\n local message=$1\n local expected=$2\n if [ \"$TEST_STATUS\" -eq \"$expected\" ]; then\n pass \"$message\"\n else\n fail \"$message (expected exit $expected, got $TEST_STATUS)\"\n fi\n}\n\nassert_contains() {\n local message=$1\n local pattern=$2\n if strip_ansi \"$TEST_OUTPUT\" | grep -Eq \"$pattern\"; then\n pass \"$message\"\n else\n fail \"$message (pattern not found: $pattern)\"\n fi\n}\n\nassert_not_contains() {\n local message=$1\n local pattern=$2\n if strip_ansi \"$TEST_OUTPUT\" | grep -Eq \"$pattern\"; then\n fail \"$message (unexpected pattern found: $pattern)\"\n else\n pass \"$message\"\n fi\n}\n\nassert_equals() {\n local message=$1\n local expected=$2\n local actual=$3\n if [ \"$expected\" = \"$actual\" ]; then\n pass \"$message\"\n else\n fail \"$message (expected: $expected, actual: $actual)\"\n fi\n}\n\nif [ ! -f \"$VALIDATOR_SOURCE\" ]; then\n echo \"ERROR: validator script not found at $VALIDATOR_SOURCE\"\n exit 1\nfi\n\necho \"Running validate_jenkinsfile.sh regression tests...\"\n\n# Test 1: Unknown/non-pipeline files fail closed by default.\nsandbox=\"$(prepare_sandbox \"unknown_fail_closed\")\"\ncat > \"$sandbox/work/not-a-jenkinsfile.groovy\" \u003c\u003c'EOF'\nprintln \"hello from groovy\"\ndef x = 1 + 2\nEOF\n\nrun_validator \"$sandbox\" --syntax-only \"$sandbox/work/not-a-jenkinsfile.groovy\"\nassert_exit \"unknown file exits with validation failure\" 1\nassert_contains \"reports type detection failure\" \"ERROR \\\\[TypeDetection\\\\]: Unable to classify file as Declarative or Scripted pipeline\\\\.\"\nassert_contains \"prints unknown marker guidance\" \"HINT: no pipeline markers detected\\\\.\"\nassert_not_contains \"does not report validation passed\" \"VALIDATION PASSED\"\n\n# Test 2: Unknown type override is explicit and bypasses type-detection failure.\nrun_validator \"$sandbox\" --syntax-only --assume-scripted \"$sandbox/work/not-a-jenkinsfile.groovy\"\nassert_contains \"assume flag marks scripted mode as assumed\" \"Pipeline type: Scripted \\\\(assumed\\\\)\"\nassert_not_contains \"assume flag avoids type-detection error\" \"ERROR \\\\[TypeDetection\\\\]\"\n\n# Test 3: Missing child validator reports exactly one runner error (no double count).\nsandbox=\"$(prepare_sandbox \"runner_error_count\")\"\ncat > \"$sandbox/work/declarative.Jenkinsfile\" \u003c\u003c'EOF'\npipeline {\n agent any\n stages {\n stage('Build') {\n steps {\n echo 'Hello'\n }\n }\n }\n}\nEOF\nrm -f \"$sandbox/skill/scripts/validate_declarative.sh\"\n\nrun_validator \"$sandbox\" --syntax-only \"$sandbox/work/declarative.Jenkinsfile\"\nassert_exit \"missing declarative validator exits with validation failure\" 1\nassert_contains \"runner error line is emitted\" \"^ERROR \\\\[Runner\\\\]: Required script is missing or not readable: validate_declarative\\\\.sh$\"\nassert_contains \"syntax summary reports single error\" \"Syntax validation failed with 1 error\\\\(s\\\\)\"\nrunner_error_count=\"$(strip_ansi \"$TEST_OUTPUT\" | grep -E -c '^ERROR \\[Runner\\]:' || true)\"\nassert_equals \"runner error appears once in output\" \"1\" \"$runner_error_count\"\n\necho \"\"\necho \"Test summary: $PASS_COUNT passed, $FAIL_COUNT failed\"\n\nif [ \"$FAIL_COUNT\" -ne 0 ]; then\n exit 1\nfi\n\necho \"PASS: validate_jenkinsfile.sh regression tests\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":4305,"content_sha256":"3cacec81f0fd59b77a73368abaeaf7fee4b8a335aa455b80fe26c9671aa343d6"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Jenkinsfile Validator Skill","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Trigger Phrases","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this skill when requests look like:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Validate this Jenkinsfile\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Check this pipeline for security issues\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Lint my Declarative/Scripted pipeline\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Why is this Jenkins pipeline failing syntax checks?\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Validate vars/","type":"text"},{"text":".groovy or src/**/","type":"text","marks":[{"type":"em"}]},{"text":".groovy shared library files\"","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Scope","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill validates:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Declarative pipelines (","type":"text"},{"text":"pipeline { ... }","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Scripted pipelines (","type":"text"},{"text":"node { ... }","type":"text","marks":[{"type":"code_inline"}]},{"text":" and Groovy-style pipelines)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Shared library files (","type":"text"},{"text":"vars/*.groovy","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"src/**/*.groovy","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Hardcoded credential patterns","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pipeline best practices and maintainability signals","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Prerequisites","type":"text"}]},{"type":"paragraph","content":[{"text":"Run commands from repository root unless noted.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Required tools","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"bash","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"grep","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"sed","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"awk","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"head","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"wc","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"find","type":"text","marks":[{"type":"code_inline"}]},{"text":" (needed for shared-library directory scans)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Recommended tools","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"jq","type":"text","marks":[{"type":"code_inline"}]},{"text":" (optional; improves JSON-heavy troubleshooting workflows)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Script prerequisites","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Scripts live in ","type":"text"},{"text":"devops-skills-plugin/skills/jenkinsfile-validator/scripts/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Main orchestrator can run child scripts even if ","type":"text"},{"text":"+x","type":"text","marks":[{"type":"code_inline"}]},{"text":" is missing (it uses ","type":"text"},{"text":"bash","type":"text","marks":[{"type":"code_inline"}]},{"text":" fallback)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If you want direct execution (","type":"text"},{"text":"./script.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":"), make scripts executable:","type":"text"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"chmod +x devops-skills-plugin/skills/jenkinsfile-validator/scripts/*.sh","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Preflight check (recommended)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"SKILL_DIR=\"devops-skills-plugin/skills/jenkinsfile-validator\"\n\ncommand -v bash grep sed awk head wc find >/dev/null && echo \"required tools: ok\" || echo \"required tools: missing\"\ncommand -v jq >/dev/null && echo \"jq: installed (optional)\" || echo \"jq: missing (optional)\"\n\n[ -d \"$SKILL_DIR/scripts\" ] && echo \"scripts dir: ok\" || echo \"scripts dir: missing\"\n[ -f \"$SKILL_DIR/scripts/validate_jenkinsfile.sh\" ] && echo \"main validator: ok\" || echo \"main validator: missing\"","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Start (Normalized Paths)","type":"text"}]},{"type":"paragraph","content":[{"text":"Use a single base path variable to avoid path ambiguity.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"SKILL_DIR=\"devops-skills-plugin/skills/jenkinsfile-validator\"\nTARGET_JENKINSFILE=\"Jenkinsfile\"\n\n# Full validation (recommended)\nbash \"$SKILL_DIR/scripts/validate_jenkinsfile.sh\" \"$TARGET_JENKINSFILE\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Common options","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"SKILL_DIR=\"devops-skills-plugin/skills/jenkinsfile-validator\"\nTARGET_JENKINSFILE=\"Jenkinsfile\"\n\nbash \"$SKILL_DIR/scripts/validate_jenkinsfile.sh\" --syntax-only \"$TARGET_JENKINSFILE\"\nbash \"$SKILL_DIR/scripts/validate_jenkinsfile.sh\" --security-only \"$TARGET_JENKINSFILE\"\nbash \"$SKILL_DIR/scripts/validate_jenkinsfile.sh\" --best-practices \"$TARGET_JENKINSFILE\"\nbash \"$SKILL_DIR/scripts/validate_jenkinsfile.sh\" --no-security \"$TARGET_JENKINSFILE\"\nbash \"$SKILL_DIR/scripts/validate_jenkinsfile.sh\" --no-best-practices \"$TARGET_JENKINSFILE\"\nbash \"$SKILL_DIR/scripts/validate_jenkinsfile.sh\" --strict \"$TARGET_JENKINSFILE\"\nbash \"$SKILL_DIR/scripts/validate_jenkinsfile.sh\" --assume-declarative \"$TARGET_JENKINSFILE\"\nbash \"$SKILL_DIR/scripts/validate_jenkinsfile.sh\" --assume-scripted \"$TARGET_JENKINSFILE\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Shared library validation","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"SKILL_DIR=\"devops-skills-plugin/skills/jenkinsfile-validator\"\n\nbash \"$SKILL_DIR/scripts/validate_shared_library.sh\" vars/myStep.groovy\nbash \"$SKILL_DIR/scripts/validate_shared_library.sh\" vars/\nbash \"$SKILL_DIR/scripts/validate_shared_library.sh\" src/\nbash \"$SKILL_DIR/scripts/validate_shared_library.sh\" /path/to/shared-library","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Regression and local CI checks","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"SKILL_DIR=\"devops-skills-plugin/skills/jenkinsfile-validator\"\nbash \"$SKILL_DIR/tests/run_local_ci.sh\"","type":"text"}]},{"type":"paragraph","content":[{"text":"run_local_ci.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" is the supported local/CI entrypoint for regression coverage. It runs:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"bash -n","type":"text","marks":[{"type":"code_inline"}]},{"text":" syntax checks for all ","type":"text"},{"text":"scripts/*.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"tests/*.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" files","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"tests/test_validate_jenkinsfile.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" regression scenarios","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Deterministic Validation Flow","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1) Detect pipeline type","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"pipeline {","type":"text","marks":[{"type":"code_inline"}]},{"text":" => Declarative validator","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"node (...)","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"node {","type":"text","marks":[{"type":"code_inline"}]},{"text":" => Scripted validator","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Unknown => fails closed by default (","type":"text"},{"text":"ERROR [TypeDetection]","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Override intentionally ambiguous files with ","type":"text"},{"text":"--assume-declarative","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"--assume-scripted","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2) Run syntax validation","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Declarative: ","type":"text"},{"text":"validate_declarative.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Scripted: ","type":"text"},{"text":"validate_scripted.sh","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3) Run security scan","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"common_validation.sh check_credentials","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"4) Run best practices check","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"best_practices.sh","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"5) Aggregate and return final status","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Unified summary with pass/fail per phase and final exit code","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"6) Run regression suite after script changes","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"bash tests/run_local_ci.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Intended for both local pre-commit checks and CI job wiring","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Individual Script Commands (Advanced)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"SKILL_DIR=\"devops-skills-plugin/skills/jenkinsfile-validator\"\nTARGET_JENKINSFILE=\"Jenkinsfile\"\n\n# Type detection\nbash \"$SKILL_DIR/scripts/common_validation.sh\" detect_type \"$TARGET_JENKINSFILE\"\n\n# Syntax-only by type\nbash \"$SKILL_DIR/scripts/validate_declarative.sh\" \"$TARGET_JENKINSFILE\"\nbash \"$SKILL_DIR/scripts/validate_scripted.sh\" \"$TARGET_JENKINSFILE\"\n\n# Security-only\nbash \"$SKILL_DIR/scripts/common_validation.sh\" check_credentials \"$TARGET_JENKINSFILE\"\n\n# Best-practices-only\nbash \"$SKILL_DIR/scripts/best_practices.sh\" \"$TARGET_JENKINSFILE\"","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Exit Code and Log Interpretation","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Main orchestrator: ","type":"text"},{"text":"validate_jenkinsfile.sh","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"0","type":"text","marks":[{"type":"code_inline"}]},{"text":": Validation passed","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"1","type":"text","marks":[{"type":"code_inline"}]},{"text":": Validation failed (syntax/security errors, or warnings in ","type":"text"},{"text":"--strict","type":"text","marks":[{"type":"code_inline"}]},{"text":" mode)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"2","type":"text","marks":[{"type":"code_inline"}]},{"text":": Usage or environment error (bad args, missing file, missing required tools)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Sub-scripts","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"validate_declarative.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":": ","type":"text"},{"text":"0","type":"text","marks":[{"type":"code_inline"}]},{"text":" pass (errors=0), ","type":"text"},{"text":"1","type":"text","marks":[{"type":"code_inline"}]},{"text":" usage/file/validation failure","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"validate_scripted.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":": ","type":"text"},{"text":"0","type":"text","marks":[{"type":"code_inline"}]},{"text":" pass (errors=0), ","type":"text"},{"text":"1","type":"text","marks":[{"type":"code_inline"}]},{"text":" usage/file/validation failure","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"common_validation.sh check_credentials","type":"text","marks":[{"type":"code_inline"}]},{"text":": ","type":"text"},{"text":"0","type":"text","marks":[{"type":"code_inline"}]},{"text":" no credential errors, ","type":"text"},{"text":"1","type":"text","marks":[{"type":"code_inline"}]},{"text":" credential issues found","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"validate_shared_library.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":": ","type":"text"},{"text":"0","type":"text","marks":[{"type":"code_inline"}]},{"text":" pass, ","type":"text"},{"text":"1","type":"text","marks":[{"type":"code_inline"}]},{"text":" validation errors found, ","type":"text"},{"text":"2","type":"text","marks":[{"type":"code_inline"}]},{"text":" invalid input target","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"best_practices.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":": ","type":"text"},{"text":"1","type":"text","marks":[{"type":"code_inline"}]},{"text":" only for usage/file errors; content findings are reported in logs and score output","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Log severity patterns","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ERROR [Line N]: ...","type":"text","marks":[{"type":"code_inline"}]},{"text":" => must fix","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"WARNING [Line N]: ...","type":"text","marks":[{"type":"code_inline"}]},{"text":" => should review","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"INFO [Line N]: ...","type":"text","marks":[{"type":"code_inline"}]},{"text":" => optional improvement","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Summary banners (","type":"text"},{"text":"VALIDATION PASSED/FAILED","type":"text","marks":[{"type":"code_inline"}]},{"text":") determine final interpretation quickly","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Practical interpretation rules","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For CI gating, rely on main orchestrator exit code.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"--strict","type":"text","marks":[{"type":"code_inline"}]},{"text":" when warnings should fail pipelines.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"When ","type":"text"},{"text":"best_practices.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" is run standalone, read report sections (","type":"text"},{"text":"CRITICAL ISSUES","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"IMPROVEMENTS RECOMMENDED","type":"text","marks":[{"type":"code_inline"}]},{"text":", score); do not rely only on exit code.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Fallback Behavior","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Missing optional tools","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"jq","type":"text","marks":[{"type":"code_inline"}]},{"text":" is missing, continue validation; treat as non-blocking.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Non-executable child scripts","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Main orchestrator warns and falls back to ","type":"text"},{"text":"bash \u003cscript>","type":"text","marks":[{"type":"code_inline"}]},{"text":" execution.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Missing child scripts","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Main orchestrator reports runner error and returns failure for that phase.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Unknown plugin steps","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this order:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check local reference: ","type":"text"},{"text":"devops-skills-plugin/skills/jenkinsfile-validator/references/common_plugins.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Context7 lookup:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"mcp__context7__resolve-library-id","type":"text","marks":[{"type":"code_inline"}]},{"text":" with query like ","type":"text"},{"text":"jenkinsci \u003cplugin-name>-plugin","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"mcp__context7__query-docs","type":"text","marks":[{"type":"code_inline"}]},{"text":" for usage and parameters","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Web fallback: ","type":"text"},{"text":"plugins.jenkins.io","type":"text","marks":[{"type":"link","attrs":{"href":"https://plugins.jenkins.io/","title":null}}]},{"text":" and official Jenkins docs","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Offline/air-gapped mode","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run all local validators.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If plugin docs cannot be fetched, report: \"Plugin docs lookup skipped due to environment constraints; local validation only.\"","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Plugin Documentation Lookup Workflow","type":"text"}]},{"type":"paragraph","content":[{"text":"When plugin-specific validation is requested:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Identify unknown steps from Jenkinsfile or validator logs.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check ","type":"text"},{"text":"references/common_plugins.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" first.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If missing, use Context7 (","type":"text"},{"text":"resolve-library-id","type":"text","marks":[{"type":"code_inline"}]},{"text":" then ","type":"text"},{"text":"query-docs","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If still missing, use web search against official plugin index/docs.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Return required parameters, optional parameters, version-sensitive notes, and security guidance.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"References","type":"text"}]},{"type":"paragraph","content":[{"text":"Local references:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"devops-skills-plugin/skills/jenkinsfile-validator/references/declarative_syntax.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"devops-skills-plugin/skills/jenkinsfile-validator/references/scripted_syntax.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"devops-skills-plugin/skills/jenkinsfile-validator/references/best_practices.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"devops-skills-plugin/skills/jenkinsfile-validator/references/common_plugins.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"External references:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Jenkins Pipeline Syntax","type":"text","marks":[{"type":"link","attrs":{"href":"https://www.jenkins.io/doc/book/pipeline/syntax/","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pipeline Development Tools","type":"text","marks":[{"type":"link","attrs":{"href":"https://www.jenkins.io/doc/book/pipeline/development/","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pipeline Best Practices","type":"text","marks":[{"type":"link","attrs":{"href":"https://www.jenkins.io/doc/book/pipeline/pipeline-best-practices/","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Jenkins Plugins","type":"text","marks":[{"type":"link","attrs":{"href":"https://plugins.jenkins.io/","title":null}}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Reporting Template","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this structure in validation responses:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"text"},"content":[{"text":"Validation Target: \u003cpath>\nPipeline Type: \u003cDeclarative|Scripted|Shared Library|Unknown>\n\nFindings:\n- ERROR [Line X]: \u003cissue>\n- WARNING [Line Y]: \u003cissue>\n- INFO [Line Z]: \u003csuggestion>\n\nPhase Results:\n- Syntax: \u003cPASSED|FAILED|SKIPPED>\n- Security: \u003cPASSED|FAILED|SKIPPED>\n- Best Practices: \u003cPASSED|REVIEW NEEDED|SKIPPED>\n\nExit Code: \u003c0|1|2>\nNext Actions:\n1. \u003chighest-priority fix>\n2. \u003csecond fix>","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Example Flows","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Example 1: Full Jenkinsfile validation","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"SKILL_DIR=\"devops-skills-plugin/skills/jenkinsfile-validator\"\nbash \"$SKILL_DIR/scripts/validate_jenkinsfile.sh\" Jenkinsfile","type":"text"}]},{"type":"paragraph","content":[{"text":"Expected behavior:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Runs syntax + security + best practices","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prints per-phase results and unified summary","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Returns ","type":"text"},{"text":"0/1/2","type":"text","marks":[{"type":"code_inline"}]},{"text":" per orchestrator rules","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Example 2: Shared library directory validation","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"SKILL_DIR=\"devops-skills-plugin/skills/jenkinsfile-validator\"\nbash \"$SKILL_DIR/scripts/validate_shared_library.sh\" examples/shared-library","type":"text"}]},{"type":"paragraph","content":[{"text":"Expected behavior:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validates both ","type":"text"},{"text":"vars/","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"src/","type":"text","marks":[{"type":"code_inline"}]},{"text":" files","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Aggregates issues with line references","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Returns ","type":"text"},{"text":"1","type":"text","marks":[{"type":"code_inline"}]},{"text":" when errors are present","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Example 3: Unknown plugin step follow-up","type":"text"}]},{"type":"paragraph","content":[{"text":"Input step:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"groovy"},"content":[{"text":"nexusArtifactUploader artifacts: [[...]], nexusUrl: 'https://nexus.example.com'","type":"text"}]},{"type":"paragraph","content":[{"text":"Flow:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validate locally first.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If step behavior is unclear, resolve docs via Context7.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If unavailable, use plugin site docs.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Report usage guidance and security-safe parameter patterns.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Done Criteria","type":"text"}]},{"type":"paragraph","content":[{"text":"The skill usage is complete when all are true:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Commands use normalized paths (","type":"text"},{"text":"$SKILL_DIR/scripts/...","type":"text","marks":[{"type":"code_inline"}]},{"text":") with no cwd ambiguity.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prerequisites and optional dependencies are explicit.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Exit-code semantics and log-severity interpretation are documented.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Fallback behavior is defined for missing tools/docs and constrained environments.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"At least one runnable example exists for full validation and shared-library validation.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reporting format is deterministic and actionable.","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"jenkinsfile-validator","author":"@skillopedia","source":{"stars":224,"repo_name":"cc-devops-skills","origin_url":"https://github.com/akin-ozer/cc-devops-skills/blob/HEAD/devops-skills-plugin/skills/jenkinsfile-validator/SKILL.md","repo_owner":"akin-ozer","body_sha256":"f64b1f5a93e7d84f624086e4eca5423d71ecdeb8cee37541cbd7010d7df7b9fa","cluster_key":"06ef6ff525068dcebf1bb1f97330ea571b29026c5827b31da52fd8e5c52c235f","clean_bundle":{"format":"clean-skill-bundle-v1","source":"akin-ozer/cc-devops-skills/devops-skills-plugin/skills/jenkinsfile-validator/SKILL.md","attachments":[{"id":"8e5d8433-651a-5429-8bc1-6dadd2d5ab77","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8e5d8433-651a-5429-8bc1-6dadd2d5ab77/attachment.jenkinsfile","path":"examples/bad-declarative-pipeline.Jenkinsfile","size":2627,"sha256":"e5b0925d0a394754f7475b495d73608dab17639640492e735cd185b0ff6f7707","contentType":"text/plain; charset=utf-8"},{"id":"a4698ece-18e3-58c9-887a-4e988248c2d5","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a4698ece-18e3-58c9-887a-4e988248c2d5/attachment.jenkinsfile","path":"examples/bad-scripted-pipeline.Jenkinsfile","size":5496,"sha256":"9f17d01ec42bfdd62221a3af1d1bc9034d0122ca5c592a42f84752effa298f2a","contentType":"text/plain; charset=utf-8"},{"id":"92bc3c53-34f0-5f64-9cb8-a0a23f9e1a90","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/92bc3c53-34f0-5f64-9cb8-a0a23f9e1a90/attachment.jenkinsfile","path":"examples/declarative-docker.Jenkinsfile","size":3466,"sha256":"dca5801e6afafc92ad8284705dae94f6a287060661181447622e8cd78e1de48d","contentType":"text/plain; charset=utf-8"},{"id":"d976ecd6-7135-58fb-a9c9-9863ad169028","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d976ecd6-7135-58fb-a9c9-9863ad169028/attachment.jenkinsfile","path":"examples/declarative-kubernetes.Jenkinsfile","size":4023,"sha256":"ecd5eed39cf1810ead1122d681e826a33d5727144442098b418e32995ff631b2","contentType":"text/plain; charset=utf-8"},{"id":"b4bda1b3-100f-5d96-94c8-f4f436ab21f3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b4bda1b3-100f-5d96-94c8-f4f436ab21f3/attachment.jenkinsfile","path":"examples/declarative-parallel.Jenkinsfile","size":3327,"sha256":"07a21690402dd4344d039e5800130467664be65ac3fdc0d052ea1b4b135d8c64","contentType":"text/plain; charset=utf-8"},{"id":"854d448d-c8ed-52f0-85c1-e0c94a59db73","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/854d448d-c8ed-52f0-85c1-e0c94a59db73/attachment.jenkinsfile","path":"examples/declarative-unknown-plugins.Jenkinsfile","size":5628,"sha256":"d6a82a911a788518b07c09ebf0862223103a52cd3220aaa1ce65611c7f933819","contentType":"text/plain; charset=utf-8"},{"id":"5390642f-f5c9-5c51-bf1a-b07327309acb","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/5390642f-f5c9-5c51-bf1a-b07327309acb/attachment.jenkinsfile","path":"examples/scripted-basic.Jenkinsfile","size":2708,"sha256":"6f7e1b9efdd369b948f3a27e1ab4f15dc3f6c1990a0eb83a05efa066a89b43f2","contentType":"text/plain; charset=utf-8"},{"id":"34af0b3a-7246-5761-bbe0-86960fb166eb","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/34af0b3a-7246-5761-bbe0-86960fb166eb/attachment.jenkinsfile","path":"examples/scripted-conditional.Jenkinsfile","size":5772,"sha256":"b10f98eeed45f48468566d07c42bcd4100c4d54429fba654752e6f7441751d37","contentType":"text/plain; charset=utf-8"},{"id":"6b00275c-6b8b-5af2-9181-305a2aefd81f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6b00275c-6b8b-5af2-9181-305a2aefd81f/attachment.jenkinsfile","path":"examples/scripted-docker.Jenkinsfile","size":4123,"sha256":"36d6952f54176bd8747bd4a81d4041926d399580fb02e11cea803686ab1fac1a","contentType":"text/plain; charset=utf-8"},{"id":"e8cc691c-9860-5b2d-b00f-3a6defb243da","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e8cc691c-9860-5b2d-b00f-3a6defb243da/attachment.groovy","path":"examples/shared-library/src/com/example/BuildConfig.groovy","size":1252,"sha256":"7a575e2e68d6e616f3ad8ee7510282832f290539327f637404e01bb2f68721b7","contentType":"text/plain; charset=utf-8"},{"id":"0d5149ca-dfc8-58e0-bc13-25e2cdec5522","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/0d5149ca-dfc8-58e0-bc13-25e2cdec5522/attachment.groovy","path":"examples/shared-library/vars/BadStep.groovy","size":1551,"sha256":"5e5f777ad98f19e0933f3e1f01e801e410eb8e2df9d3b723f7f8b44bb3a79158","contentType":"text/plain; charset=utf-8"},{"id":"e5760078-aab9-5137-9eef-6f14d8a28154","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e5760078-aab9-5137-9eef-6f14d8a28154/attachment.groovy","path":"examples/shared-library/vars/buildApp.groovy","size":3163,"sha256":"208850b1df056c9e703a3ba9dbc016f143b7e9ebacccc3fe3a389d8727c9ee21","contentType":"text/plain; charset=utf-8"},{"id":"aecee942-d407-5ca8-b43e-8da6d62db918","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/aecee942-d407-5ca8-b43e-8da6d62db918/attachment.md","path":"references/best_practices.md","size":14509,"sha256":"150eaee98607b62834f2a35f9bd7ebbd03151dedd4692f416bb689f7ccc1e1e8","contentType":"text/markdown; charset=utf-8"},{"id":"d42f56cc-fb64-5679-b7d6-61e09ae8102c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d42f56cc-fb64-5679-b7d6-61e09ae8102c/attachment.md","path":"references/common_plugins.md","size":14309,"sha256":"0cfe90816117278d5056c4ffa286284d5dfdd3a697b2da855308663a764aaa28","contentType":"text/markdown; charset=utf-8"},{"id":"d3c1d3b6-bb3f-52f1-858a-ffba61590cc5","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d3c1d3b6-bb3f-52f1-858a-ffba61590cc5/attachment.md","path":"references/declarative_syntax.md","size":14807,"sha256":"6f8c76ef5c770f3ce67de6218473ae7169a2b20968b80795c2b938ce4565e16e","contentType":"text/markdown; charset=utf-8"},{"id":"f2234007-9acc-5e2e-865f-4cc9e4acf279","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f2234007-9acc-5e2e-865f-4cc9e4acf279/attachment.md","path":"references/scripted_syntax.md","size":18105,"sha256":"01376aa84cb224705b9006648d25982d154fee85323229fa5c8d83603ace0062","contentType":"text/markdown; charset=utf-8"},{"id":"31c7721d-9735-5107-b49e-7ba2871b4d2a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/31c7721d-9735-5107-b49e-7ba2871b4d2a/attachment.sh","path":"scripts/best_practices.sh","size":18109,"sha256":"929909c4f02575fd4d62392a3aea3109ba03c7989b7373fd377be0de3f49f2f0","contentType":"application/x-sh; charset=utf-8"},{"id":"3f04da72-afbe-5fc6-947d-77b77d7cb826","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3f04da72-afbe-5fc6-947d-77b77d7cb826/attachment.sh","path":"scripts/common_validation.sh","size":21202,"sha256":"ec69895de8bd763334a522002a7f92b722369384b954c49fa47cb5f095265683","contentType":"application/x-sh; charset=utf-8"},{"id":"de7d3734-fbb4-51ff-8e68-3ad2b1edabba","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/de7d3734-fbb4-51ff-8e68-3ad2b1edabba/attachment.sh","path":"scripts/validate_declarative.sh","size":22710,"sha256":"f2cf5736d8f74e3cf3fcfa90d20f2b80e49062a1b14bffea0c5480c950ca9f4e","contentType":"application/x-sh; charset=utf-8"},{"id":"018fa911-3ffe-5ac4-8582-736be12da67f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/018fa911-3ffe-5ac4-8582-736be12da67f/attachment.sh","path":"scripts/validate_jenkinsfile.sh","size":18631,"sha256":"ff91e46128becba7139c7be6cd3fea1110640fc5ac765c3652bc5cfbf30e6c0b","contentType":"application/x-sh; charset=utf-8"},{"id":"13c8f4e3-ccdf-5d77-a0e8-1e1e66b95079","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/13c8f4e3-ccdf-5d77-a0e8-1e1e66b95079/attachment.sh","path":"scripts/validate_scripted.sh","size":15505,"sha256":"e51307756c764c4d04a91e25efd4f55296cd2c807cc894dba4782c8fe8d1ebae","contentType":"application/x-sh; charset=utf-8"},{"id":"20f1df18-76ed-5e17-80f1-a98bd65aea9e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/20f1df18-76ed-5e17-80f1-a98bd65aea9e/attachment.sh","path":"scripts/validate_shared_library.sh","size":15638,"sha256":"1bb9ef424fdd44c61d99e0ee89477b1440ba4c615d8a8ea95de30832f5a75855","contentType":"application/x-sh; charset=utf-8"},{"id":"a9ca5b81-97dc-5fd4-877f-00f635eccaf9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a9ca5b81-97dc-5fd4-877f-00f635eccaf9/attachment.sh","path":"tests/run_local_ci.sh","size":707,"sha256":"2b0f209d1a567c572b354aeaf4671d61ecebe29eefcad2aa9e5ffff52b69929f","contentType":"application/x-sh; charset=utf-8"},{"id":"1ca30f36-305c-5a9b-99ae-86c2cc3bc251","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1ca30f36-305c-5a9b-99ae-86c2cc3bc251/attachment.sh","path":"tests/test_validate_jenkinsfile.sh","size":4305,"sha256":"3cacec81f0fd59b77a73368abaeaf7fee4b8a335aa455b80fe26c9671aa343d6","contentType":"application/x-sh; charset=utf-8"}],"bundle_sha256":"1a30e39c179d438c9f538b597149f8f82d09dd7479763cbffffc4b068429cdaf","attachment_count":24,"text_attachments":12,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":12,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"devops-skills-plugin/skills/jenkinsfile-validator/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"security","category_label":"Security"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"security","import_tag":"clean-skills-v1","description":"Validate, lint, audit, or check Jenkinsfiles and shared libraries."}},"renderedAt":1782980781424}

Jenkinsfile Validator Skill Use this skill to validate Jenkins pipelines and shared libraries with local scripts first, then optionally enrich findings with plugin documentation. Trigger Phrases Use this skill when requests look like: - "Validate this Jenkinsfile" - "Check this pipeline for security issues" - "Lint my Declarative/Scripted pipeline" - "Why is this Jenkins pipeline failing syntax checks?" - "Validate vars/ .groovy or src/ / .groovy shared library files" Scope This skill validates: - Declarative pipelines ( ) - Scripted pipelines ( and Groovy-style pipelines) - Shared library fi…