From 496599720c9505dbe886d2caaad4e99e8ebaff68 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 19 Jun 2026 11:16:46 +0000 Subject: [PATCH] feat: release 3 new skills: configuring-code-analyzer, managing-cdc-enablement, using-salesforce-archive @W-23038011@ --- skills/building-sf-integrations/SKILL.md | 2 +- skills/configuring-code-analyzer/SKILL.md | 482 +++++++++++++ .../examples/apex-project-config.yml | 41 ++ .../examples/ci-github-actions.yml | 96 +++ .../examples/fullstack-project-config.yml | 46 ++ .../examples/lwc-project-config.yml | 26 + .../references/ci-cd-templates.md | 648 ++++++++++++++++++ .../references/config-schema.md | 257 +++++++ .../references/diagnostic-flow.md | 70 ++ .../references/engine-prerequisites.md | 276 ++++++++ .../references/rule-name-resolution.md | 67 ++ .../references/troubleshooting.md | 298 ++++++++ .../scripts/check-prerequisites.sh | 189 +++++ .../scripts/generate-config.sh | 143 ++++ .../scripts/validate-config.sh | 153 +++++ skills/managing-cdc-enablement/SKILL.md | 164 +++++ .../assets/PlatformEventChannel-template.xml | 5 + .../PlatformEventChannelMember-template.xml | 11 + .../references/deploy-troubleshooting.md | 73 ++ .../references/filter-expressions.md | 93 +++ skills/running-code-analyzer/SKILL.md | 549 ++++++++------- .../references/post-scan-workflows.md | 286 ++++++++ .../scripts/describe-rule.js | 382 +++++++++++ .../scripts/list-rules.js | 260 +++++++ .../scripts/query-results.js | 230 +++++++ skills/using-salesforce-archive/SKILL.md | 121 ++++ .../examples/monitor-failed-jobs.md | 47 ++ .../references/archive-activity-entity.md | 59 ++ .../references/connect-api-operations.md | 157 +++++ 29 files changed, 4954 insertions(+), 277 deletions(-) create mode 100644 skills/configuring-code-analyzer/SKILL.md create mode 100644 skills/configuring-code-analyzer/examples/apex-project-config.yml create mode 100644 skills/configuring-code-analyzer/examples/ci-github-actions.yml create mode 100644 skills/configuring-code-analyzer/examples/fullstack-project-config.yml create mode 100644 skills/configuring-code-analyzer/examples/lwc-project-config.yml create mode 100644 skills/configuring-code-analyzer/references/ci-cd-templates.md create mode 100644 skills/configuring-code-analyzer/references/config-schema.md create mode 100644 skills/configuring-code-analyzer/references/diagnostic-flow.md create mode 100644 skills/configuring-code-analyzer/references/engine-prerequisites.md create mode 100644 skills/configuring-code-analyzer/references/rule-name-resolution.md create mode 100644 skills/configuring-code-analyzer/references/troubleshooting.md create mode 100755 skills/configuring-code-analyzer/scripts/check-prerequisites.sh create mode 100755 skills/configuring-code-analyzer/scripts/generate-config.sh create mode 100755 skills/configuring-code-analyzer/scripts/validate-config.sh create mode 100644 skills/managing-cdc-enablement/SKILL.md create mode 100644 skills/managing-cdc-enablement/assets/PlatformEventChannel-template.xml create mode 100644 skills/managing-cdc-enablement/assets/PlatformEventChannelMember-template.xml create mode 100644 skills/managing-cdc-enablement/references/deploy-troubleshooting.md create mode 100644 skills/managing-cdc-enablement/references/filter-expressions.md create mode 100644 skills/running-code-analyzer/references/post-scan-workflows.md create mode 100644 skills/running-code-analyzer/scripts/describe-rule.js create mode 100644 skills/running-code-analyzer/scripts/list-rules.js create mode 100644 skills/running-code-analyzer/scripts/query-results.js create mode 100644 skills/using-salesforce-archive/SKILL.md create mode 100644 skills/using-salesforce-archive/examples/monitor-failed-jobs.md create mode 100644 skills/using-salesforce-archive/references/archive-activity-entity.md create mode 100644 skills/using-salesforce-archive/references/connect-api-operations.md diff --git a/skills/building-sf-integrations/SKILL.md b/skills/building-sf-integrations/SKILL.md index 8635e9c..fbf35ff 100644 --- a/skills/building-sf-integrations/SKILL.md +++ b/skills/building-sf-integrations/SKILL.md @@ -1,6 +1,6 @@ --- name: building-sf-integrations -description: "Salesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data)." +description: "Salesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), data import/export (use handling-sf-data), or CDC channel-membership metadata such as PlatformEventChannel, PlatformEventChannelMember, or EnrichedField (use managing-cdc-enablement)." metadata: version: "1.1" --- diff --git a/skills/configuring-code-analyzer/SKILL.md b/skills/configuring-code-analyzer/SKILL.md new file mode 100644 index 0000000..db0d47f --- /dev/null +++ b/skills/configuring-code-analyzer/SKILL.md @@ -0,0 +1,482 @@ +--- +name: configuring-code-analyzer +description: "Set up, configure, and troubleshoot Salesforce Code Analyzer for any project. Handles installation, prerequisite checks, diagnosing broken setups, creating and editing code-analyzer.yml overrides, engine-specific settings, ignore patterns, severity overrides, and CI/CD pipeline setup. TRIGGER when: user says 'set up code analyzer', 'configure code analyzer', 'install code analyzer', 'code analyzer not working', 'fix my setup', 'scan is failing', 'check my setup', 'is code analyzer installed', 'enable/disable engine', 'exclude files', 'change severity', 'set up GitHub Actions', 'set up CI/CD', 'add code analyzer to pipeline', 'make pipeline fail', 'update my workflow', 'quality gate', 'fail on violations', 'scan changed files only', 'add SARIF', 'code-analyzer.yml', 'ESLint config', 'increase SFGE memory', or reports errors running Code Analyzer. DO NOT TRIGGER when: user wants to run a scan (use running-code-analyzer), fix violations, explain rules, create custom rules, or suppress violations." +metadata: + version: "1.0" + argument-hint: "[--check-prerequisites] [--generate-config] [--engine pmd|eslint|cpd|retire-js|regex|flow|sfge|apexguru] [--ci github-actions|jenkins]" +--- + +# Configuring Code Analyzer Skill + +## Overview + +This skill manages the `code-analyzer.yml` configuration file — the single source of truth for how Code Analyzer behaves in a project. All customization (engines, rules, ignores, suppressions) is done by creating or editing this file. If the file doesn't exist, this skill creates it in the current working directory. + +--- + +## Scope + +**In scope:** +- Checking prerequisites (sf CLI, Java, Node.js, Python, org auth) +- Installing/updating the Code Analyzer plugin +- Creating `code-analyzer.yml` if it doesn't exist +- Editing `code-analyzer.yml` for all configuration changes +- Engine settings, rule overrides, ignore patterns, suppressions +- CI/CD pipeline setup (GitHub Actions, Jenkins, etc.) +- Environment validation and troubleshooting + +**Out of scope:** +- Running scans (use `running-code-analyzer` skill) +- Fixing violations, explaining rules, creating custom rules, suppression management + +--- + +## Tool Usage Rules + +**Allowed:** Bash (sf, java, node, python3, git, npm), Read, Write, Edit +**Forbidden:** MCP tools, Agent tool, Web tools, other skills, `which`, `find`, `locate`, searching for binaries + +--- + +## Core Principle: YAML Only When Customizing + +Code Analyzer works out of the box with NO config file — all defaults are built into the tool. The `code-analyzer.yml` file is ONLY created when the user explicitly requests a customization. + +**Rules:** +- **Do NOT create `code-analyzer.yml` proactively** — only when user asks to change something +- **Do NOT duplicate built-in defaults** — only write entries that intentionally override behavior +- **Always place at project root** — where `sfdx-project.json` or `sf-project.json` lives +- **The CLI auto-discovers it** — `sf code-analyzer run` from project root automatically picks up `code-analyzer.yml` in that directory. No `--config-file` flag needed. +- User says "configure code analyzer" with no specifics? → **Ask what they want to customize**. Don't create an empty or boilerplate file. + +**Workflow:** +1. User requests a customization (e.g., "disable PMD", "ignore test files", "increase SFGE memory") +2. Check if `code-analyzer.yml` exists at project root +3. If NO → create it at project root with ONLY the requested override +4. If YES → read it, then edit in the requested change +5. Validate with `sf code-analyzer config` + +--- + +## Step 1: Understand Intent and Map to Config Sections + +The user can request ANY combination of configuration changes in natural language. Your job is to: + +1. **Parse what they want** — may be one thing or many things combined +2. **Map each request to the correct section(s) of `code-analyzer.yml`** +3. **Create the file if it doesn't exist, then apply all changes** + +### The `code-analyzer.yml` Structure (what you can write/edit) + +```yaml +config_root: . # Root for relative path resolution +log_folder: # Where logs are written +log_level: <1-5> # 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Fine + +ignores: # Files/folders excluded from scanning + files: [] + +engines: # Per-engine settings + : + disable_engine: + : ... + +rules: # Per-rule overrides + : + : + severity: <1-5> + tags: [] + disabled: + +suppressions: # Bulk suppression configuration + disable_suppressions: + "": + - rule_selector: "" + max_suppressed_violations: + reason: "" +``` + +### Mapping Principle + +Any user request maps to one or more sections above. Parse the intent and edit the right section(s): + +| Intent Category | Maps To | Examples of What User Might Say | +|----------------|---------|-------------------------------| +| Setup / Install | Step 2 (prerequisites + install) | "set up", "install", "get started", "new laptop", "from scratch" | +| **Diagnose / Fix** | **Step 2A (systematic debug)** | **"not working", "broken", "fix my setup", "scan fails", "getting errors"** | +| Engine control | `engines..disable_engine` | "disable X", "turn off Y", "only use Z", "enable all" | +| Engine tuning | `engines..` | "increase memory", "change heap", "use my eslint config", "set tokens to 50" | +| File exclusions | `ignores.files` | "exclude", "ignore", "skip", "don't scan X" | +| Rule severity | `rules...severity` | "make X critical", "promote", "demote", "change severity" | +| Rule disable | `rules...disabled` | "disable rule X", "turn off Y rule", "remove Z" | +| Rule tags | `rules...tags` | "tag X as security", "add recommended tag" | +| Suppressions | `suppressions` section | "suppress X in folder Y", "allow N violations" | +| CI/CD | Generate pipeline file (separate from config) | "github actions", "CI", "quality gate" | +| View/inspect | Read file + `sf code-analyzer config` | "show config", "what's configured", "current settings" | + +### File Existence Decision + +**BEFORE editing anything**, check if `code-analyzer.yml` exists at project root: + +```bash +ls code-analyzer.yml code-analyzer.yaml 2>/dev/null +``` + +- **File does NOT exist** → Create it at project root with ONLY the user's requested override(s) +- **File exists** → Read it, then Edit to add/modify the requested section(s) + +The CLI auto-discovers `code-analyzer.yml` in the current directory. Since scans run from project root, the file must live there. + +### ⚠️ Rule Name Resolution — ALWAYS Before Writing YAML + +When a user references rules by partial, descriptive, or approximate names (e.g., "the doc rule", "CRUD violation", "console rule", "hardcoded values"), you MUST resolve to exact rule names using the lookup in **Step 6.1** BEFORE writing any YAML. The `code-analyzer.yml` file silently ignores rule names that don't exactly match — there is no error, the override just won't apply. + +**Examples of fuzzy → exact resolution needed:** +- "Disable the ApexDoc rule" → lookup confirms `ApexDoc` (engine: `pmd`) +- "Demote no-console to low" → lookup confirms `no-console` (engine: `eslint`) +- "Make CRUD violations critical" → lookup confirms `ApexCRUDViolation` (engine: `pmd`) +- "Turn off the hardcoded values check" → lookup finds `@salesforce-ux/slds/no-hardcoded-values-slds2` (engine: `eslint`) +- "Disable the injection rule" → multiple matches possible → ask user which one + +**Only skip the lookup** when the user provides an unambiguous, exact, well-known name (e.g., "ApexDoc", "no-console", "no-unused-vars"). + +### Handling Combined/Complex Requests + +Users will often combine multiple changes in one request. Handle ALL of them in a single edit: + +- "Disable PMD's ApexDoc rule and make CRUD violations critical" → edit two entries under `rules.pmd` +- "Exclude test files and vendor code, and increase SFGE memory" → edit `ignores.files` + `engines.sfge.java_max_heap_size` +- "Set up code analyzer with only ESLint and PMD, ignore node_modules" → create file with `engines` (disable others) + `ignores` +- "Make all security rules severity 1" → look up rules via `sf code-analyzer rules --rule-selector Security`, then override each +- "Configure code analyzer" (no specifics) → ask user what they want to customize before creating any file + +### Quick Reference: Common Requests → Config Output + +| User Says | Resulting YAML | +|-----------|---------------| +| "configure code analyzer" | Ask user what to customize — don't create file until there's an actual override | +| "disable the ApexDoc rule" | `rules: pmd: ApexDoc: disabled: true` | +| "only scan Apex, no JavaScript" | `engines: eslint: disable_engine: true` + `engines: retire-js: disable_engine: true` | +| "ignore all test files" | `ignores: files: ["**/test/**", "**/__tests__/**", "**/*.test.js"]` | +| "make security rules critical" | Look up rules, then `rules: : : severity: 1` for each | +| "increase SFGE memory to 8g" | `engines: sfge: java_max_heap_size: "8g"` | +| "use my project's ESLint config" | `engines: eslint: auto_discover_eslint_config: true` | +| "suppress CRUD violations in legacy folder" | `suppressions: "force-app/legacy/": [{rule_selector: "pmd:ApexCRUDViolation", reason: "..."}]` | + +**The AI must understand the YAML schema and write valid config for ANY request, not just the examples above.** + +--- + +## Step 2: Check Prerequisites and Install + +Run `bash "/scripts/check-prerequisites.sh"` or check manually: + +```bash +sf --version 2>&1 # sf CLI +sf plugins --core 2>&1 | grep -i "code-analyzer" # Plugin +java -version 2>&1 # Java 11+ (PMD, CPD, SFGE) +node --version 2>&1 # Node 18+ (ESLint, RetireJS) +python3 --version 2>&1 # Python 3 (Flow engine) +``` + +If anything is missing, install it (**always ask user first**): + +```bash +npm install -g @salesforce/cli # sf CLI +sf plugins install @salesforce/plugin-code-analyzer # Code Analyzer plugin +``` + +For Java/Node/Python installs, read `/references/engine-prerequisites.md`. +If install fails, read `/references/troubleshooting.md`. + +--- + +## Step 2A: Diagnose and Fix a Broken Setup + +**TRIGGER:** User says "not working", "broken", "getting errors", "scan fails", "help me fix", etc. + +**Read `/references/diagnostic-flow.md`** for the complete layered diagnostic procedure, fix table, and anti-patterns. + +**Key principles (always apply):** +- Never search for binaries (`which`, `find`, `ls /opt/homebrew/bin/`) +- Never use `sfdx` as a workaround — only `sf` +- Fix layer by layer: CLI → Plugin → Engine deps → verify scan +- Give user ONE command at a time, wait for confirmation before continuing +- After fix succeeds, proceed to run the full scan automatically + +--- + +## Step 3: Create or Edit `code-analyzer.yml` + +**Only triggered when user requests a customization.** Never create proactively. + +### Creating (file doesn't exist) + +Choose **one** of the two approaches below — do not run both: + +**Option A — Auto-generate from project type (recommended for first-time setup):** + +Run `bash "/scripts/generate-config.sh"`. This detects Apex, LWC, and Flow markers and produces a minimal `code-analyzer.yml` suited to the project. Skip to the "After any create/edit, validate" section. + +> Note: The script exits with an error if `code-analyzer.yml` already exists. Delete the existing file first if you need to regenerate. + +**Option B — Write manually (when the user has specific customizations in mind):** + +Read the appropriate example config as a reference for structure: +- For Apex-only projects, read `/examples/apex-project-config.yml` +- For LWC-only projects, read `/examples/lwc-project-config.yml` +- For full-stack (Apex + LWC + Flows), read `/examples/fullstack-project-config.yml` + +Write the file at project root using the Write tool. Include ONLY the user's requested changes: + +```bash +# Example: user said "ignore test files and increase SFGE memory" +# → Write to project root (where sfdx-project.json lives): +``` + +```yaml +ignores: + files: + - "**/test/**" + - "**/__tests__/**" + +engines: + sfge: + java_max_heap_size: "4g" +``` + +Do NOT add `config_root`, `log_folder`, or any other field the user didn't ask for. + +### Editing (file already exists) + +Read the file, then use the Edit tool to add/modify only the relevant section. Preserve everything else. + +### After any create/edit, validate: + +Run `bash "/scripts/validate-config.sh"` to validate YAML syntax and schema correctness, or use the CLI directly: + +```bash +sf code-analyzer config +``` + +(No `--config-file` needed — the CLI auto-discovers `code-analyzer.yml` in CWD.) + +### If user says "configure code analyzer" with no specifics + +Ask: "What would you like to customize? For example: ignore certain files, change rule severities, tune engine settings, or disable engines you don't need." + +--- + +## Step 4: Enable/Disable Engines + +Edit the `engines` section in `code-analyzer.yml`: + +```yaml +engines: + pmd: + disable_engine: true # Disable PMD + eslint: + disable_engine: false # Enable ESLint (default) +``` + +Valid engine names: `pmd`, `cpd`, `eslint`, `regex`, `retire-js`, `flow`, `sfge`, `apexguru` + +**Always validate after editing:** +```bash +sf code-analyzer config --config-file code-analyzer.yml +``` + +--- + +## Step 5: Ignore Patterns + +Edit the `ignores` section in `code-analyzer.yml`: + +```yaml +ignores: + files: + - "**/node_modules/**" + - "**/.sfdx/**" + - "**/.sf/**" + - "**/vendor/**" + - "**/*.min.js" +``` + +Common patterns: + +| Pattern | Excludes | +|---------|----------| +| `**/node_modules/**` | npm dependencies | +| `**/.sfdx/**`, `**/.sf/**` | SF CLI internals | +| `**/test/**`, `**/__tests__/**` | Test directories | +| `**/*.test.js`, `**/*.spec.js` | Test files | +| `**/jest-mocks/**` | Jest mocks | +| `**/vendor/**`, `**/*.min.js` | Third-party/minified | +| `**/staticresources/**` | Static resources | + +--- + +## Step 6: Rule Overrides + +Edit the `rules` section in `code-analyzer.yml`. Each rule can have `severity`, `tags`, and `disabled` overrides: + +```yaml +rules: + pmd: + ApexCRUDViolation: + severity: 1 # Promote to Critical + AvoidGlobalModifier: + disabled: true # Turn off entirely + ApexDoc: + severity: 5 # Demote to Info + tags: ["Documentation"] + eslint: + no-console: + severity: 4 # Demote to Low + no-unused-vars: + severity: 2 # Promote to High +``` + +**Severity values:** `1`/Critical, `2`/High, `3`/Moderate, `4`/Low, `5`/Info + +### 6.1 Rule Name Resolution (Fuzzy Matching) + +**⚠️ CRITICAL:** A misspelled or partial rule name in `code-analyzer.yml` is SILENTLY IGNORED — no error, the override just won't apply. + +**When users reference rules by approximate names** (e.g., "the doc rule", "CRUD violation", "hardcoded values"), resolve to exact names BEFORE writing YAML: + +```bash +sf code-analyzer rules --rule-selector all 2>&1 | grep -i "" +``` + +- **1 match** → use that exact name + its engine for the YAML path +- **Multiple matches** → ask user which one they meant +- **0 matches** → try broader keywords or inform user + +**Skip the lookup only** when the name is unambiguous and exact (e.g., "ApexDoc", "no-console", "no-unused-vars"). + +**For detailed matching strategies, common fuzzy→exact mappings, and engine identification:** Read `/references/rule-name-resolution.md`. + +--- + +## Step 7: Engine-Specific Settings + +Edit the `engines` section. Most common overrides: + +```yaml +engines: + sfge: + java_max_heap_size: "4g" # <200 classes→"2g", 200-500→"4g", 500+→"6g"/"8g" + java_thread_count: 4 + java_thread_timeout: 900000 + eslint: + auto_discover_eslint_config: true # Use project's own ESLint config + eslint_config_file: "./eslint.config.mjs" + pmd: + custom_rulesets: ["./config/custom-pmd-rules.xml"] + java_classpath_entries: ["./lib/custom-rules.jar"] + cpd: + minimum_tokens: { apex: 100, javascript: 100 } + apexguru: + target_org: "my-org-alias" + flow: + python_command: "python3" + regex: + custom_rules: + NoHardcodedIds: + regex: "/[a-zA-Z0-9]{15,18}/" + file_extensions: [".cls", ".trigger"] + description: "Detects hardcoded Salesforce record IDs" + severity: 2 + tags: ["Security"] +``` + +For full property list per engine, read `/references/config-schema.md`. + +--- + +## Step 8: CI/CD Pipeline Setup + +Detect CI system from workspace (`.github/workflows/` → GitHub Actions, `Jenkinsfile` → Jenkins, etc.). Read `/references/ci-cd-templates.md` for templates. Use `/examples/ci-github-actions.yml` as GitHub Actions base. Key flags: `--severity-threshold 2` (gate), `--output-file results.sarif` (GitHub scanning), `--config-file code-analyzer.yml`. + +--- + +## Step 9: View Current Configuration + +```bash +sf code-analyzer config # Show effective config +sf code-analyzer config --rule-selector pmd:Security # Specific rules +sf code-analyzer config --include-unmodified-rules # All defaults +``` + +--- + +## Cross-Skill Integration + +This skill works together with `running-code-analyzer`. The AI agent should seamlessly hand off between them: + +### When `running-code-analyzer` delegates HERE: + +If a user says "scan my code" / "run code analyzer" but it fails (CLI missing, plugin not installed, or scan errors out), `running-code-analyzer` delegates to this skill. In that case: + +1. Run the **diagnose and fix** flow (Step 2A) — find what's broken, fix it +2. After everything works, **automatically proceed to run the scan** — do not stop and ask. The user's original intent was to scan. +3. Hand execution back to `running-code-analyzer` behavior (build command, execute, parse results). + +### When THIS skill hands off to `running-code-analyzer`: + +After any successful configuration action, offer to run a scan (e.g., "Setup complete! Want me to run a scan?", "Config updated — want to scan and verify?"). If user says yes, proceed with `running-code-analyzer` behavior. + +### When user intent spans BOTH skills: + +Handle end-to-end: "not working" → Diagnose → Fix → Scan. "Set up and scan" → Install → Scan. "Disable ESLint and scan Apex" → Edit config → Run with `--rule-selector pmd`. Always follow through to the user's final intent. + +--- + +## Rules / Constraints + +| Constraint | Rationale | +|-----------|-----------| +| Only create YAML when user requests a customization | Defaults work without any file — don't create boilerplate | +| Place YAML at project root only | CLI auto-discovers `code-analyzer.yml` from CWD | +| Write only overrides, never duplicate defaults | Keep file minimal and intentional | +| Use Write tool to create, Edit tool to modify | Preserves existing settings | +| Validate after every change | `sf code-analyzer config` catches YAML errors | +| Ask before installing prerequisites | Never auto-install without consent | +| Never delete existing config without asking | User may have custom settings | +| After setup, offer to scan | Close the loop — config without scan is incomplete | + +--- + +## Gotchas + +| Issue | Solution | +|-------|----------| +| Config not picked up | Must be `code-analyzer.yml` in CWD or use `--config-file` | +| YAML validation fails | Spaces only (no tabs), check colon spacing | +| SFGE out of memory | Increase `java_max_heap_size` in engines section | +| ESLint rules missing | Set `auto_discover_eslint_config: true` | + +For full troubleshooting, read `/references/troubleshooting.md`. + +--- + +## Reference File Index + +`` is the absolute path to the directory containing this SKILL.md file. + +| File | Purpose | +|------|---------| +| `/scripts/check-prerequisites.sh` | Environment check | +| `/scripts/generate-config.sh` | Auto-detect project type and generate config | +| `/scripts/validate-config.sh` | Validate YAML after changes | +| `/references/config-schema.md` | Full YAML schema documentation | +| `/references/diagnostic-flow.md` | Step 2A: layered diagnostic procedure and fix table | +| `/references/rule-name-resolution.md` | Step 6.1: fuzzy rule name lookup strategies and mappings | +| `/references/engine-prerequisites.md` | Install instructions per engine | +| `/references/ci-cd-templates.md` | CI/CD pipeline templates | +| `/references/troubleshooting.md` | Common setup issues and fixes | +| `/examples/apex-project-config.yml` | Config for Apex-only project | +| `/examples/lwc-project-config.yml` | Config for LWC-only project | +| `/examples/fullstack-project-config.yml` | Config for Apex + LWC + Flows | +| `/examples/ci-github-actions.yml` | GitHub Actions workflow | diff --git a/skills/configuring-code-analyzer/examples/apex-project-config.yml b/skills/configuring-code-analyzer/examples/apex-project-config.yml new file mode 100644 index 0000000..4714145 --- /dev/null +++ b/skills/configuring-code-analyzer/examples/apex-project-config.yml @@ -0,0 +1,41 @@ +# Code Analyzer Configuration — Apex-focused Project +# +# This file contains ONLY overrides. Code Analyzer's built-in defaults +# handle everything else. Only add entries here that intentionally +# change behavior for your project. +# +# Usage: +# Place at project root as code-analyzer.yml +# Validate: sf code-analyzer config --config-file code-analyzer.yml + +# Exclude non-project files (these would otherwise be scanned) +ignores: + files: + - "**/node_modules/**" + - "**/.sfdx/**" + - "**/.sf/**" + - "**/staticresources/**" + +# Engine tuning — only override what differs from defaults +engines: + # SFGE: increase heap for projects with 200+ Apex classes + # (built-in default is dynamic/1g, too small for large projects) + sfge: + java_max_heap_size: "4g" + +# Rule overrides — promote security, demote noise +rules: + pmd: + # Promote security rules to Critical (default is High) + ApexCRUDViolation: + severity: 1 + ApexSOQLInjection: + severity: 1 + ApexSharingViolations: + severity: 1 + # Demote noisy rules (default is Moderate) + ApexDoc: + severity: 5 + # Disable rules that conflict with project conventions + AvoidGlobalModifier: + disabled: true diff --git a/skills/configuring-code-analyzer/examples/ci-github-actions.yml b/skills/configuring-code-analyzer/examples/ci-github-actions.yml new file mode 100644 index 0000000..e1408f0 --- /dev/null +++ b/skills/configuring-code-analyzer/examples/ci-github-actions.yml @@ -0,0 +1,96 @@ +# GitHub Actions Workflow — Code Analyzer Quality Gate +# +# Scans pull requests for code quality and security violations. +# Uploads SARIF results to GitHub Code Scanning for inline annotations. +# +# Usage: +# Copy to .github/workflows/code-analyzer.yml in your repository +# Customize severity-threshold and rule-selector as needed +# +# Prerequisites: +# - Repository must have GitHub Advanced Security enabled for SARIF upload +# - Or remove the "Upload SARIF" step for basic artifact-only workflow + +name: Code Analyzer + +on: + pull_request: + branches: [main, develop] + paths: + - 'force-app/**' + - 'src/**' + - '**/*.cls' + - '**/*.trigger' + - '**/*.js' + - '**/*.ts' + - '**/*.html' + - '**/*.flow-meta.xml' + - 'code-analyzer.yml' + push: + branches: [main, develop] + +permissions: + contents: read + security-events: write # Required for SARIF upload + +jobs: + code-analysis: + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Full history for diff-based scans + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '11' + + - name: Install Salesforce CLI + run: npm install -g @salesforce/cli + + - name: Install Code Analyzer + run: sf plugins install @salesforce/plugin-code-analyzer + + - name: Verify installation + run: | + sf --version + sf plugins --core | grep code-analyzer + java -version + node --version + + - name: Run Code Analyzer + run: | + sf code-analyzer run \ + --rule-selector Recommended \ + --severity-threshold 2 \ + --output-file results.sarif \ + --output-file results.json \ + --output-file results.html \ + --config-file code-analyzer.yml + + - name: Upload SARIF to GitHub Security + if: always() + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif + category: code-analyzer + + - name: Upload Results as Artifact + if: always() + uses: actions/upload-artifact@v4 + with: + name: code-analyzer-results + path: | + results.json + results.html + retention-days: 30 diff --git a/skills/configuring-code-analyzer/examples/fullstack-project-config.yml b/skills/configuring-code-analyzer/examples/fullstack-project-config.yml new file mode 100644 index 0000000..0be248b --- /dev/null +++ b/skills/configuring-code-analyzer/examples/fullstack-project-config.yml @@ -0,0 +1,46 @@ +# Code Analyzer Configuration — Full-Stack Salesforce Project +# +# This file contains ONLY overrides. Code Analyzer's built-in defaults +# handle everything else. Only add entries here that intentionally +# change behavior for your project. +# +# Usage: +# Place at project root as code-analyzer.yml +# Validate: sf code-analyzer config --config-file code-analyzer.yml + +# Exclude non-project files +ignores: + files: + - "**/node_modules/**" + - "**/.sfdx/**" + - "**/.sf/**" + - "**/jest-mocks/**" + - "**/__tests__/**" + - "**/*.min.js" + - "**/staticresources/jquery*" + - "**/staticresources/bootstrap*" + +# Engine tuning — only what differs from defaults +engines: + # SFGE: increase heap for large Apex codebases + sfge: + java_max_heap_size: "4g" + # ESLint: use project's existing config for LWC-specific rules + eslint: + auto_discover_eslint_config: true + +# Rule overrides — only rules that need project-specific treatment +rules: + pmd: + # Promote security rules (default is High → make Critical) + ApexCRUDViolation: + severity: 1 + ApexSOQLInjection: + severity: 1 + # Demote noisy documentation rule + ApexDoc: + severity: 5 + eslint: + # Demote console warnings in development (default is Moderate) + no-console: + severity: 4 diff --git a/skills/configuring-code-analyzer/examples/lwc-project-config.yml b/skills/configuring-code-analyzer/examples/lwc-project-config.yml new file mode 100644 index 0000000..4646f40 --- /dev/null +++ b/skills/configuring-code-analyzer/examples/lwc-project-config.yml @@ -0,0 +1,26 @@ +# Code Analyzer Configuration — LWC-focused Project +# +# This file contains ONLY overrides. Code Analyzer's built-in defaults +# handle everything else. Only add entries here that intentionally +# change behavior for your project. +# +# Usage: +# Place at project root as code-analyzer.yml +# Validate: sf code-analyzer config --config-file code-analyzer.yml + +# Exclude non-project files and test infrastructure +ignores: + files: + - "**/node_modules/**" + - "**/.sfdx/**" + - "**/.sf/**" + - "**/jest-mocks/**" + - "**/__tests__/**" + - "**/*.min.js" + +# Engine tuning +engines: + # ESLint: opt-in to use project's existing ESLint config + # (built-in default is false — Code Analyzer uses its own base configs) + eslint: + auto_discover_eslint_config: true diff --git a/skills/configuring-code-analyzer/references/ci-cd-templates.md b/skills/configuring-code-analyzer/references/ci-cd-templates.md new file mode 100644 index 0000000..5e86baf --- /dev/null +++ b/skills/configuring-code-analyzer/references/ci-cd-templates.md @@ -0,0 +1,648 @@ +# CI/CD Pipeline Templates + +Ready-to-use templates for integrating Code Analyzer into CI/CD pipelines. + +## GitHub Actions (using `forcedotcom/run-code-analyzer@v2`) + +The official GitHub Action for Code Analyzer. It orchestrates the run, uploads artifacts, and optionally creates PR reviews with violation counts. + +**Action inputs:** +| Input | Required | Default | Description | +|-------|----------|---------|-------------| +| `run-arguments` | No | `--view detail --output-file sfca_results.json` | Flags passed to `sf code-analyzer run` | +| `results-artifact-name` | No | `salesforce-code-analyzer-results` | Name of the uploaded ZIP artifact | +| `github-token` | No | *(none)* | Enables PR reviews + `*-in-changed-files` outputs | + +**Action outputs:** `exit-code`, `num-violations`, `num-sev1-violations` through `num-sev5-violations`, `num-violations-in-changed-files`, `num-sev1-violations-in-changed-files` through `num-sev5-violations-in-changed-files`, `review-id`. + +**Key `run-arguments` flags:** +| Flag | Purpose | Example | +|------|---------|---------| +| `--workspace` | Root directory to scan (scans all eligible files recursively) | `--workspace .` | +| `--target` | Specific files/directories to scan (comma-separated or repeated) | `--target force-app/main/default/classes --target force-app/main/default/triggers` | +| `--rule-selector` | Which rules to run | `Recommended`, `all`, `all:Security`, `pmd`, `"Severity:1,2"` | +| `--config-file` | Path to `code-analyzer.yml` — **omit if no config file exists** | `--config-file code-analyzer.yml` | +| `--output-file` | Output file (repeat for multiple formats; format inferred from extension) | `--output-file results.html --output-file results.sarif` | +| `--view` | Output verbosity | `detail` or `summary` | + +**`--workspace` vs `--target`:** +- Use `--workspace .` for full-repo scans (most common in CI) +- Use `--target ` to scan specific directories (monorepos, scoped scans, faster feedback) +- If both are omitted, defaults to current working directory +- You can combine: `--workspace . --target force-app/` scans only `force-app/` within the workspace + +### Basic Quality Gate (Pull Requests) + +```yaml +# .github/workflows/code-analyzer.yml +name: Salesforce Code Analyzer + +on: + pull_request: + branches: [main, develop] + +jobs: + code-analysis: + permissions: + pull-requests: write + contents: read + actions: read + runs-on: ubuntu-latest + steps: + - name: Check out files + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '>=20.9.0' + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '>=11' + + - name: Install Salesforce CLI + run: npm install -g @salesforce/cli@latest + + - name: Install Code Analyzer Plugin + run: sf plugins install code-analyzer@latest + + - name: Run Salesforce Code Analyzer + id: run-code-analyzer + uses: forcedotcom/run-code-analyzer@v2 + with: + run-arguments: --workspace . --rule-selector Recommended --output-file sfca_results.html --output-file sfca_results.json + results-artifact-name: code-analyzer-results + github-token: ${{ github.token }} + + - name: Check Quality Gate (Changed Files Only) + if: | + steps.run-code-analyzer.outputs.num-sev1-violations-in-changed-files > 0 || + steps.run-code-analyzer.outputs.num-sev2-violations-in-changed-files > 0 + run: | + echo "Critical/High violations found in changed files!" + echo "Sev1: ${{ steps.run-code-analyzer.outputs.num-sev1-violations-in-changed-files }}" + echo "Sev2: ${{ steps.run-code-analyzer.outputs.num-sev2-violations-in-changed-files }}" + exit 1 +``` + +### Strict Quality Gate (All Files + SARIF) + +```yaml +# .github/workflows/code-analyzer-strict.yml +name: Salesforce Code Analyzer (Strict) + +on: + pull_request: + branches: [main] + push: + branches: [main] + +jobs: + code-analysis: + permissions: + pull-requests: write + contents: read + actions: read + security-events: write + runs-on: ubuntu-latest + steps: + - name: Check out files + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '>=20.9.0' + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '>=11' + + - name: Install Salesforce CLI + run: npm install -g @salesforce/cli@latest + + - name: Install Code Analyzer Plugin + run: sf plugins install code-analyzer@latest + + - name: Run Salesforce Code Analyzer + id: run-code-analyzer + uses: forcedotcom/run-code-analyzer@v2 + with: + run-arguments: --workspace . --rule-selector Recommended --output-file sfca_results.html --output-file sfca_results.json --output-file sfca_results.sarif --config-file code-analyzer.yml + results-artifact-name: code-analyzer-results + github-token: ${{ github.token }} + + - name: Upload SARIF to GitHub Security + if: always() + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: sfca_results.sarif + + - name: Check Quality Gate (All Files) + if: | + steps.run-code-analyzer.outputs.exit-code > 0 || + steps.run-code-analyzer.outputs.num-sev1-violations > 0 || + steps.run-code-analyzer.outputs.num-sev2-violations > 0 + run: | + echo "Quality gate failed!" + echo "Total violations: ${{ steps.run-code-analyzer.outputs.num-violations }}" + echo "Sev1: ${{ steps.run-code-analyzer.outputs.num-sev1-violations }}" + echo "Sev2: ${{ steps.run-code-analyzer.outputs.num-sev2-violations }}" + exit 1 +``` + +### Security-Focused (AppExchange Prep) + +```yaml +# .github/workflows/code-analyzer-security.yml +name: Security Analysis + +on: + pull_request: + branches: [main] + schedule: + - cron: '0 6 * * 1' # Weekly Monday 6am + +jobs: + security-scan: + permissions: + pull-requests: write + contents: read + actions: read + security-events: write + runs-on: ubuntu-latest + steps: + - name: Check out files + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '>=20.9.0' + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '>=11' + + - name: Install Salesforce CLI + run: npm install -g @salesforce/cli@latest + + - name: Install Code Analyzer Plugin + run: sf plugins install code-analyzer@latest + + - name: Run Security Scan + id: run-code-analyzer + uses: forcedotcom/run-code-analyzer@v2 + with: + run-arguments: --workspace . --rule-selector "all:Security" --output-file security-results.html --output-file security-results.json --output-file security-results.sarif --config-file code-analyzer.yml + results-artifact-name: security-scan-results + github-token: ${{ github.token }} + + - name: Upload SARIF to GitHub Security + if: always() + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: security-results.sarif + + - name: Fail on Any Security Violations + if: steps.run-code-analyzer.outputs.num-sev1-violations > 0 + run: | + echo "Critical security violations found!" + echo "Sev1: ${{ steps.run-code-analyzer.outputs.num-sev1-violations }}" + exit 1 +``` + +### With Path Filters (Faster Feedback) + +Use `paths:` to only trigger the workflow when relevant source files change. This avoids running scans on README edits, CI config changes, etc. + +```yaml +# .github/workflows/code-analyzer-filtered.yml +name: Salesforce Code Analyzer + +on: + pull_request: + branches: [main, develop] + paths: + - 'force-app/**' + - '**/*.cls' + - '**/*.trigger' + - '**/*.js' + - '**/*.ts' + - '**/*.html' + - '**/*.flow-meta.xml' + - 'code-analyzer.yml' + +jobs: + code-analysis: + permissions: + pull-requests: write + contents: read + actions: read + runs-on: ubuntu-latest + steps: + - name: Check out files + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '>=20.9.0' + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '>=11' + + - name: Install Salesforce CLI + run: npm install -g @salesforce/cli@latest + + - name: Install Code Analyzer Plugin + run: sf plugins install code-analyzer@latest + + - name: Run Salesforce Code Analyzer + id: run-code-analyzer + uses: forcedotcom/run-code-analyzer@v2 + with: + run-arguments: --workspace . --rule-selector Recommended --output-file sfca_results.html --output-file sfca_results.json + results-artifact-name: code-analyzer-results + github-token: ${{ github.token }} + + - name: Check Quality Gate + if: | + steps.run-code-analyzer.outputs.num-sev1-violations-in-changed-files > 0 || + steps.run-code-analyzer.outputs.num-sev2-violations-in-changed-files > 0 + run: exit 1 +``` + +**When to use path filters:** +- Use when the repo contains non-Salesforce code (docs, scripts, infra) that shouldn't trigger scans +- Always include `code-analyzer.yml` in paths so config changes trigger a validation run +- Don't use if you want every PR to get a scan regardless of what changed + +### With Flow Engine (Python Required) + +If the project contains `.flow-meta.xml` files and uses the Flow engine, Python 3.10+ must be installed: + +```yaml + - name: Setup Python (required for Flow engine) + uses: actions/setup-python@v5 + with: + python-version: '>=3.10' +``` + +Insert this step after the Java setup step. If you're unsure whether the project uses Flows, include it — it adds ~5s and avoids a partial scan failure. + +### Monorepo / Scoped Scan + +For monorepos where Salesforce code lives in a subdirectory, use `--target` to scope the scan: + +```yaml + - name: Run Salesforce Code Analyzer + id: run-code-analyzer + uses: forcedotcom/run-code-analyzer@v2 + with: + run-arguments: --workspace . --target packages/salesforce-app/force-app --rule-selector Recommended --output-file sfca_results.html --output-file sfca_results.json + results-artifact-name: code-analyzer-results + github-token: ${{ github.token }} +``` + +For multiple packages: +``` +--target packages/app-a/force-app --target packages/app-b/force-app +``` + +### Without a Config File + +If the project has no `code-analyzer.yml`, simply omit `--config-file`. Code Analyzer uses built-in defaults: + +```yaml + - name: Run Salesforce Code Analyzer + id: run-code-analyzer + uses: forcedotcom/run-code-analyzer@v2 + with: + run-arguments: --workspace . --rule-selector Recommended --output-file sfca_results.html --output-file sfca_results.json + results-artifact-name: code-analyzer-results + github-token: ${{ github.token }} +``` + +To conditionally use a config file if it exists: +```yaml + - name: Check for config file + id: config-check + run: | + if [ -f "code-analyzer.yml" ]; then + echo "config-flag=--config-file code-analyzer.yml" >> $GITHUB_OUTPUT + else + echo "config-flag=" >> $GITHUB_OUTPUT + fi + + - name: Run Salesforce Code Analyzer + id: run-code-analyzer + uses: forcedotcom/run-code-analyzer@v2 + with: + run-arguments: --workspace . --rule-selector Recommended --output-file sfca_results.html --output-file sfca_results.json ${{ steps.config-check.outputs.config-flag }} + results-artifact-name: code-analyzer-results + github-token: ${{ github.token }} +``` + +## Jenkins + +### Jenkinsfile (Declarative Pipeline) + +```groovy +// Jenkinsfile +pipeline { + agent { + docker { + image 'node:20' + args '-v /usr/local/share/java:/usr/local/share/java' + } + } + + environment { + JAVA_HOME = '/usr/lib/jvm/java-11-openjdk-amd64' + } + + stages { + stage('Setup') { + steps { + sh 'npm install -g @salesforce/cli@latest' + sh 'sf plugins install code-analyzer@latest' + } + } + + stage('Code Analysis') { + steps { + sh ''' + sf code-analyzer run \ + --workspace . \ + --rule-selector Recommended \ + --output-file results.json \ + --output-file results.html \ + --config-file code-analyzer.yml 2>&1 | tee sfca_output.txt + ''' + script { + def output = readFile('results.json') + def json = new groovy.json.JsonSlurper().parseText(output) + def sev1Count = json.violations?.count { it.severity == 1 } ?: 0 + def sev2Count = json.violations?.count { it.severity == 2 } ?: 0 + if (sev1Count > 0 || sev2Count > 0) { + error("Quality gate failed! Sev1: ${sev1Count}, Sev2: ${sev2Count}") + } + } + } + post { + always { + archiveArtifacts artifacts: 'results.*', allowEmptyArchive: true + publishHTML(target: [ + reportName: 'Code Analyzer Report', + reportDir: '.', + reportFiles: 'results.html' + ]) + } + } + } + } + + post { + failure { + echo 'Code analysis found violations above severity threshold!' + } + } +} +``` + +## GitLab CI + +```yaml +# .gitlab-ci.yml +code-analysis: + image: node:20 + stage: test + before_script: + - apt-get update && apt-get install -y openjdk-11-jdk python3 + - export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 + - npm install -g @salesforce/cli@latest + - sf plugins install code-analyzer@latest + script: + - | + sf code-analyzer run \ + --workspace . \ + --rule-selector Recommended \ + --output-file results.json \ + --output-file results.html \ + --config-file code-analyzer.yml + - | + # Quality gate: fail on sev1 or sev2 violations + SEV1=$(cat results.json | python3 -c "import sys,json; d=json.load(sys.stdin); print(sum(1 for v in d.get('violations',[]) if v.get('severity')==1))") + SEV2=$(cat results.json | python3 -c "import sys,json; d=json.load(sys.stdin); print(sum(1 for v in d.get('violations',[]) if v.get('severity')==2))") + echo "Sev1: $SEV1, Sev2: $SEV2" + if [ "$SEV1" -gt 0 ] || [ "$SEV2" -gt 0 ]; then + echo "Quality gate failed!" + exit 1 + fi + artifacts: + paths: + - results.json + - results.html + reports: + codequality: results.json + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + - if: '$CI_COMMIT_BRANCH == "main"' +``` + +## Bitbucket Pipelines + +```yaml +# bitbucket-pipelines.yml +pipelines: + pull-requests: + '**': + - step: + name: Code Analysis + image: node:20 + script: + - apt-get update && apt-get install -y openjdk-11-jdk + - export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 + - npm install -g @salesforce/cli@latest + - sf plugins install code-analyzer@latest + - | + sf code-analyzer run \ + --workspace . \ + --rule-selector Recommended \ + --output-file results.json \ + --output-file results.html \ + --config-file code-analyzer.yml + - | + # Quality gate: fail on sev1 or sev2 violations + SEV1=$(cat results.json | python3 -c "import sys,json; d=json.load(sys.stdin); print(sum(1 for v in d.get('violations',[]) if v.get('severity')==1))") + SEV2=$(cat results.json | python3 -c "import sys,json; d=json.load(sys.stdin); print(sum(1 for v in d.get('violations',[]) if v.get('severity')==2))") + echo "Sev1: $SEV1, Sev2: $SEV2" + if [ "$SEV1" -gt 0 ] || [ "$SEV2" -gt 0 ]; then + echo "Quality gate failed!" + exit 1 + fi + artifacts: + - results.json + - results.html +``` + +## Configuration Tips for CI/CD + +### Quality Gating Strategy + +With `forcedotcom/run-code-analyzer@v2`, you control the quality gate via output checks: + +| Strategy | Condition | Use Case | +|----------|-----------|----------| +| Block on critical only | `num-sev1-violations > 0` | Permissive — only block security vulnerabilities | +| Block on critical + high | `num-sev1-violations > 0 \|\| num-sev2-violations > 0` | Recommended for most teams | +| Block on changed files only | `num-sev1-violations-in-changed-files > 0` | Great for legacy codebases — don't block on pre-existing issues | +| Block on total count | `num-violations > 10` | Budget approach — allow some violations but cap total | +| Zero tolerance | `num-violations > 0` | For new greenfield projects only | + +For non-GitHub platforms (Jenkins, GitLab, Bitbucket), parse `results.json` to count violations by severity and fail the pipeline accordingly. + +### Output Formats + +Specify multiple `--output-file` flags (format inferred from extension): + +| Format | Extension | Best For | +|--------|-----------|----------| +| SARIF | `.sarif` | GitHub Code Scanning integration (requires `security-events: write` permission) | +| JSON | `.json` | Programmatic processing, quality gating scripts, custom dashboards | +| HTML | `.html` | Human-readable reports in artifacts | +| CSV | `.csv` | Spreadsheet analysis | +| XML | `.xml` | Legacy tool integration | + +### Caching for Faster CI + +```yaml +# GitHub Actions: Cache sf CLI plugins +- uses: actions/cache@v4 + with: + path: ~/.local/share/sf/ + key: sf-plugins-${{ hashFiles('**/code-analyzer.yml') }} + restore-keys: sf-plugins- +``` + +### Recommended PR Workflow + +1. **On PR open/update:** Use `forcedotcom/run-code-analyzer@v2` with `github-token` — gate on changed-files outputs only +2. **On merge to main:** Full scan with SARIF upload to GitHub Security tab +3. **Weekly schedule:** Full security scan with `--rule-selector "all:Security"` + +### When to Use Path Filters + +| Situation | Use Path Filters? | Reasoning | +|-----------|-------------------|-----------| +| Mixed repo (docs, infra, salesforce code) | Yes | Avoid wasting CI minutes on non-code PRs | +| Pure Salesforce project | Optional | Every PR likely touches scannable files anyway | +| You want config changes to trigger a scan | Yes, include `code-analyzer.yml` | Validates config changes don't break the scan | +| Monorepo with multiple apps | Yes, scope to your package path | Avoid scanning unrelated packages | + +--- + +## Composition & Adaptation Guide + +When a user's request doesn't match an existing template exactly, compose from these building blocks: + +### Prerequisites Block (always required) +```yaml +# ALWAYS include these — the action does NOT install them +- uses: actions/setup-node@v4 # Node >= 20.9.0 +- uses: actions/setup-java@v4 # Java >= 11 (for PMD/CPD/SFGE) +- uses: actions/setup-python@v5 # Python >= 3.10 (ONLY if project has .flow-meta.xml files) +- run: npm install -g @salesforce/cli@latest +- run: sf plugins install code-analyzer@latest +``` + +### Composing `run-arguments` + +Build the `run-arguments` string by combining these independent flags as needed: + +| Need | Flag to Add | +|------|-------------| +| Scan the whole repo | `--workspace .` | +| Scan specific directory | `--target force-app/main/default/classes` | +| Scan multiple directories | `--target dir1 --target dir2` | +| Use recommended rules | `--rule-selector Recommended` | +| Use all rules | `--rule-selector all` | +| Security rules only | `--rule-selector "all:Security"` | +| Specific engine only | `--rule-selector pmd` or `--rule-selector eslint` | +| Specific severity filter | `--rule-selector "Severity:1,2"` | +| Combined selector | `--rule-selector "pmd:Security:(1,2)"` | +| Apply custom config | `--config-file code-analyzer.yml` | +| No custom config | *(omit --config-file entirely)* | +| HTML output | `--output-file results.html` | +| JSON output | `--output-file results.json` | +| SARIF for GitHub Security | `--output-file results.sarif` | +| Multiple formats | `--output-file a.html --output-file b.json --output-file c.sarif` | +| Verbose output | `--view detail` | + +### Permissions Required + +| Feature | Permission Needed | +|---------|------------------| +| PR review comments | `pull-requests: write` | +| Upload SARIF to Security tab | `security-events: write` | +| Private repo checkout | `contents: read` | +| Download artifacts | `actions: read` | + +### Decision Tree for Template Selection + +``` +User wants CI/CD for Code Analyzer +├── Platform? +│ ├── GitHub Actions → Use forcedotcom/run-code-analyzer@v2 +│ ├── Jenkins → Use Jenkinsfile template + JSON parsing for quality gate +│ ├── GitLab → Use .gitlab-ci.yml template + script-based quality gate +│ └── Bitbucket → Use bitbucket-pipelines.yml template + script-based quality gate +├── Scope? +│ ├── Full repo → --workspace . +│ ├── Specific folder → --target +│ └── Changed files only → use github-token + *-in-changed-files outputs (GitHub only) +├── Strictness? +│ ├── Block on sev1 only → check num-sev1-violations +│ ├── Block on sev1+sev2 → check both (RECOMMENDED DEFAULT) +│ ├── Block on any violation → check num-violations +│ └── Legacy codebase → use *-in-changed-files to avoid blocking on old debt +├── Has config file? +│ ├── Yes → add --config-file code-analyzer.yml +│ ├── No → omit --config-file +│ └── Maybe → use conditional check pattern +├── Has Flow files? +│ ├── Yes → add actions/setup-python step +│ └── No → skip Python setup +└── Wants GitHub Security integration? + ├── Yes → add --output-file *.sarif + upload-sarif step + security-events: write + └── No → skip SARIF +``` + +### Common User Requests → Modifications + +| User Says | What to Change | +|-----------|---------------| +| "Only scan on PRs to main" | Set `on: pull_request: branches: [main]` | +| "Scan only Apex classes" | Use `--target force-app/main/default/classes` | +| "Don't fail on existing violations" | Use `*-in-changed-files` outputs for quality gate | +| "I want to see results in GitHub Security tab" | Add `--output-file *.sarif` + `upload-sarif` step + `security-events: write` | +| "Run a nightly full scan" | Add `schedule: - cron: '0 0 * * *'` trigger | +| "We don't have a config file" | Remove `--config-file` from run-arguments | +| "Only run security rules" | Change `--rule-selector` to `"all:Security"` | +| "Block the PR if there are more than 5 violations" | Change gate to `num-violations > 5` | +| "We have Flows in our project" | Add `actions/setup-python@v5` with `python-version: '>=3.10'` | +| "Scan only what changed in the PR" | Use `github-token` + gate on `*-in-changed-files` outputs | +| "We're a monorepo" | Use `--target packages/my-sf-app/force-app` instead of `--workspace .` | +| "Cache the plugin install" | Add `actions/cache@v4` step for `~/.local/share/sf/` | +| "I want an HTML report I can download" | Add `--output-file results.html` (auto-uploaded via results-artifact-name) | diff --git a/skills/configuring-code-analyzer/references/config-schema.md b/skills/configuring-code-analyzer/references/config-schema.md new file mode 100644 index 0000000..0b68a69 --- /dev/null +++ b/skills/configuring-code-analyzer/references/config-schema.md @@ -0,0 +1,257 @@ +# Code Analyzer Configuration Schema + +Full reference for the `code-analyzer.yml` configuration file. + +## Top-Level Fields + +```yaml +# code-analyzer.yml + +# Root directory for resolving relative paths in the config +config_root: . + +# Directory where Code Analyzer writes log files +log_folder: /tmp + +# Log verbosity: 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Fine +log_level: 3 + +# File patterns to exclude from analysis +ignores: + files: [] + +# Rule severity, tag, and disable overrides +rules: {} + +# Engine-specific configuration +engines: {} + +# Bulk suppression rules +suppressions: + disable_suppressions: false +``` + +## Ignores Section + +```yaml +ignores: + files: + - "**/node_modules/**" # npm dependencies + - "**/.sfdx/**" # Salesforce DX internal + - "**/.sf/**" # Salesforce CLI internal + - "**/test/**" # Test directories + - "**/*.test.js" # Test files + - "**/*.min.js" # Minified files + - "**/staticresources/**" # Static resources (often vendor) +``` + +**Pattern syntax:** Glob patterns using `*` (any filename chars), `**` (any path segment), `?` (single char). + +## Rules Section + +Override severity, tags, or disable rules per engine: + +```yaml +rules: + : + : + severity: <1-5 or "Critical"|"High"|"Moderate"|"Low"|"Info"> + tags: ["Tag1", "Tag2"] # Override rule tags + disabled: true|false # Disable/enable rule +``` + +### Severity Values + +| Number | Name | Meaning | +|--------|------|---------| +| 1 | Critical | Security vulnerabilities, must fix before release | +| 2 | High | Significant issues, should fix | +| 3 | Moderate | Recommended improvements | +| 4 | Low | Minor suggestions | +| 5 | Info | Informational, no action required | + +### Example Rule Overrides + +```yaml +rules: + pmd: + ApexCRUDViolation: + severity: 1 # Promote to Critical + AvoidGlobalModifier: + disabled: true # Disable entirely + ApexDoc: + severity: 5 # Demote to Info + tags: ["Documentation"] + eslint: + no-console: + severity: 4 # Demote to Low + no-unused-vars: + severity: 2 # Promote to High +``` + +## Engines Section + +### PMD Engine + +```yaml +engines: + pmd: + disable_engine: false + java_command: "java" # Path to Java executable + custom_rulesets: # Additional ruleset XML files + - "./config/custom-pmd-rules.xml" + java_classpath_entries: # JARs for custom Java rules + - "./lib/my-custom-rules.jar" + file_extensions: # Override scanned file types + apex: [".cls", ".trigger"] + visualforce: [".page", ".component"] +``` + +### ESLint Engine + +```yaml +engines: + eslint: + disable_engine: false + auto_discover_eslint_config: true # Use project's eslint config files + eslint_config_file: "./eslint.config.mjs" # Explicit config file path + disable_javascript_base_config: false # Disable built-in JS rules + disable_typescript_base_config: false # Disable built-in TS rules + disable_lwc_base_config: false # Disable built-in LWC rules + disable_flow_base_config: false # Disable built-in Flow rules +``` + +**Note:** `auto_discover_eslint_config` requires a `--workspace` flag on the run command. + +### CPD Engine (Copy-Paste Detector) + +```yaml +engines: + cpd: + disable_engine: false + minimum_tokens: # Min tokens for duplicate detection + apex: 100 # Lower = more sensitive + html: 100 + javascript: 100 + visualforce: 100 + xml: 100 + skip_duplicate_files: false # Skip files with identical content +``` + +### SFGE Engine (Salesforce Graph Engine) + +```yaml +engines: + sfge: + disable_engine: false + java_max_heap_size: "4g" # JVM heap (increase for large projects) + java_thread_count: 4 # Parallel threads + java_thread_timeout: 900000 # Per-thread timeout in ms +``` + +**Warning:** SFGE is resource-intensive. For projects with 500+ Apex classes, use 4g+ heap. Analysis can take 10-30 minutes. + +### ApexGuru Engine + +```yaml +engines: + apexguru: + disable_engine: false + target_org: "my-org-alias" # Authenticated org alias or username + api_timeout_ms: 300000 # API timeout in ms (default 5min) +``` + +**Requires:** Authenticated Salesforce org (`sf org login web`). + +### Flow Engine + +```yaml +engines: + flow: + disable_engine: false + python_command: "python3" # Path to Python 3 executable +``` + +**Requires:** Python 3 installed. + +### Regex Engine + +```yaml +engines: + regex: + disable_engine: false + custom_rules: + : + regex: "//" # JavaScript regex syntax + regex_ignore: "//" # Optional: false positive filter + file_extensions: [".cls", ".trigger"] + description: "What this rule checks" + violation_message: "Message shown to developer" + severity: 3 + tags: ["Recommended", "Security"] +``` + +### RetireJS Engine + +```yaml +engines: + retire-js: + disable_engine: false +``` + +**Note:** RetireJS scans JavaScript dependencies for known CVEs. No additional configuration needed beyond enable/disable. + +## Suppressions Section + +```yaml +suppressions: + disable_suppressions: false # Set true to ignore ALL suppressions + + # Bulk suppressions by file/folder path + "src/legacy/": + - rule_selector: "pmd:ApexDoc" + max_suppressed_violations: 50 # Quota (null = unlimited) + reason: "Legacy code, documentation not required" + + "src/utils/Logger.cls": + - rule_selector: "eslint:no-console" + max_suppressed_violations: 10 + reason: "Logger intentionally uses console" +``` + +### Inline Suppression Markers + +In addition to bulk config suppressions, violations can be suppressed inline: + +```java +// Apex: PMD suppression +// NOPMD - reason here +@SuppressWarnings('PMD.ApexCRUDViolation') + +// Any engine: universal marker +// code-analyzer-suppress(pmd:ApexCRUDViolation) - reason +// code-analyzer-suppress(eslint:no-console) - reason +``` + +## Config File Discovery + +Code Analyzer automatically looks for configuration in this order: +1. File specified via `--config-file` flag +2. `code-analyzer.yml` in current working directory +3. `code-analyzer.yaml` in current working directory +4. No config (use defaults) + +## Validating Configuration + +Always validate after making changes: + +```bash +# Validate config and show effective settings +sf code-analyzer config --config-file code-analyzer.yml + +# Show config for specific rules +sf code-analyzer config --rule-selector pmd:Security + +# Show all rule defaults (verbose) +sf code-analyzer config --include-unmodified-rules --rule-selector all +``` diff --git a/skills/configuring-code-analyzer/references/diagnostic-flow.md b/skills/configuring-code-analyzer/references/diagnostic-flow.md new file mode 100644 index 0000000..9107876 --- /dev/null +++ b/skills/configuring-code-analyzer/references/diagnostic-flow.md @@ -0,0 +1,70 @@ +# Diagnostic Flow: Fix a Broken Setup + +**TRIGGER:** User says "not working", "broken", "getting errors", "scan fails", "help me fix", etc. + +## NEVER DO THESE (anti-patterns that waste time) + +- ❌ NEVER run `which sfdx`, `which sf`, `find`, `ls /opt/homebrew/bin/` or search for binaries +- ❌ NEVER use an old `sfdx` binary as a workaround — it is NOT a substitute for `sf` +- ❌ NEVER create symlinks (`ln -s`) to work around missing commands +- ❌ NEVER check PATH, inspect Cellar directories, or search for alternative installations +- ❌ NEVER proceed to Layer 2 if Layer 1 failed — fix Layer 1 first +- ❌ NEVER give the user a list of manual steps — fix it yourself or give ONE command + +## Diagnostic Flow (follow this EXACTLY, no deviation) + +Run **ONLY** this one command first: +```bash +sf --version 2>&1 +``` + +**If output contains "command not found":** +→ STOP. Do not run any other commands. Tell user: "sf CLI is not installed. I'll install it now." +→ Ask user for permission, then run: `npm install -g @salesforce/cli` +→ After install, re-run `sf --version 2>&1` to verify. If it works, continue to next layer. + +**If sf works**, run ONLY: +```bash +sf plugins --core 2>&1 | grep -i "code-analyzer" +``` + +**If output is empty or shows "JIT" but not a real version:** +→ STOP. Run: `sf plugins install @salesforce/plugin-code-analyzer` +→ After install, re-check. If it works, continue to next layer. + +**If plugin is installed**, check engine deps: +```bash +java -version 2>&1 +node --version 2>&1 +``` + +**If all pass**, verify with a scan: +```bash +sf code-analyzer run --rule-selector Recommended 2>&1 | tail -20 +``` + +## Fix Table + +| Error Pattern | The ONE Fix | +|--------------|-------------| +| `sf: command not found` | `npm install -g @salesforce/cli` | +| Plugin missing / JIT error | `sf plugins install @salesforce/plugin-code-analyzer` | +| `Cannot find module` | `sf plugins uninstall @salesforce/plugin-code-analyzer && sf plugins install @salesforce/plugin-code-analyzer` | +| `java: command not found` | Install Java 11+ (see `/references/engine-prerequisites.md`) | +| `OutOfMemoryError` (SFGE) | Add `engines.sfge.java_max_heap_size: "4g"` to `code-analyzer.yml` | +| `YAMLException` | Read the config file, fix YAML syntax | +| `EPERM` / npm permission error | Tell user to run: `sudo chown -R $(whoami) ~/.npm` — then wait for them to confirm, then retry the SAME install command that failed. Do NOT dump next steps. | + +## After Fix: Verify and Hand Off + +Re-run the check for the fixed layer. Once a scan succeeds, tell the user what was fixed and **proceed to run the full scan**. + +## When a fix requires user action (sudo, manual step) + +Tell the user ONLY the ONE command they need to run and WHY. Then STOP and WAIT for them to confirm it's done. Do NOT: +- ❌ List the remaining steps ("after that, do X, then Y, then Z") +- ❌ Tell them what to run next after the manual step +- ❌ Provide a multi-step recovery plan +- ❌ Ask "would you like me to attempt with sudo or do it yourself" + +Just say: "Run this command: ``. It fixes [reason]. Let me know when it's done and I'll continue." diff --git a/skills/configuring-code-analyzer/references/engine-prerequisites.md b/skills/configuring-code-analyzer/references/engine-prerequisites.md new file mode 100644 index 0000000..dd2ba30 --- /dev/null +++ b/skills/configuring-code-analyzer/references/engine-prerequisites.md @@ -0,0 +1,276 @@ +# Engine Prerequisites + +Detailed installation instructions for each Code Analyzer engine's dependencies. + +## Summary Table + +| Engine | Required Dependencies | Optional | +|--------|----------------------|----------| +| PMD | Java 11+ | Custom ruleset JARs | +| CPD | Java 11+ | — | +| ESLint | Node.js 18+ | Project ESLint config | +| RetireJS | Node.js 18+ | — | +| Regex | None (built-in) | — | +| Flow | Python 3 | — | +| SFGE | Java 11+ (4g+ heap recommended) | — | +| ApexGuru | Authenticated Salesforce org | — | + +## Core: Salesforce CLI + +**Required for ALL engines.** + +### macOS + +```bash +# Via Homebrew (recommended) +brew install sf + +# Or via npm +npm install -g @salesforce/cli +``` + +### Windows + +```bash +# Via npm +npm install -g @salesforce/cli + +# Or download installer from: +# https://developer.salesforce.com/tools/salesforcecli +``` + +### Linux + +```bash +# Via npm +npm install -g @salesforce/cli + +# Or via tarball: +# https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_install_cli.htm +``` + +### Verify + +```bash +sf --version +# Expected: @salesforce/cli/2.x.x ... +``` + +## Code Analyzer Plugin + +**Required: sf CLI must be installed first.** + +```bash +# Install +sf plugins install @salesforce/plugin-code-analyzer + +# Verify +sf code-analyzer --help + +# Update to latest +sf plugins install @salesforce/plugin-code-analyzer@latest + +# Check version +sf plugins --core | grep code-analyzer +``` + +## Java 11+ (for PMD, CPD, SFGE) + +### macOS + +```bash +# Via Homebrew +brew install openjdk@11 + +# Add to PATH (add to ~/.zshrc or ~/.bash_profile) +export PATH="/opt/homebrew/opt/openjdk@11/bin:$PATH" +export JAVA_HOME="/opt/homebrew/opt/openjdk@11" + +# Or via SDKMAN (manages multiple Java versions) +curl -s "https://get.sdkman.io" | bash +sdk install java 11.0.21-tem +``` + +### Windows + +```bash +# Via winget +winget install EclipseAdoptium.Temurin.11.JDK + +# Or via Chocolatey +choco install temurin11 +``` + +### Linux + +```bash +# Ubuntu/Debian +sudo apt install openjdk-11-jdk + +# RHEL/CentOS/Fedora +sudo dnf install java-11-openjdk-devel + +# Via SDKMAN (any Linux) +curl -s "https://get.sdkman.io" | bash +sdk install java 11.0.21-tem +``` + +### Verify + +```bash +java -version +# Expected: openjdk version "11.x.x" or higher + +echo $JAVA_HOME +# Should point to JDK installation +``` + +### Troubleshooting Java + +| Issue | Solution | +|-------|----------| +| `java: command not found` | Add Java bin dir to PATH | +| Wrong Java version | Set JAVA_HOME explicitly | +| Multiple Java versions | Use `sdk use java 11.x.x` or update PATH order | +| SFGE heap errors | Increase `java_max_heap_size` in config | + +## Node.js 18+ (for ESLint, RetireJS) + +### macOS + +```bash +# Via Homebrew +brew install node@20 + +# Or via nvm (recommended for version management) +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash +nvm install 20 +nvm use 20 +``` + +### Windows + +```bash +# Via winget +winget install OpenJS.NodeJS.LTS + +# Or via nvm-windows +# Download from: https://github.com/coreybutler/nvm-windows/releases +nvm install 20 +nvm use 20 +``` + +### Linux + +```bash +# Via nvm (recommended) +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash +nvm install 20 +nvm use 20 + +# Or via package manager (may be outdated) +# Ubuntu/Debian (use NodeSource for latest): +curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - +sudo apt install -y nodejs +``` + +### Verify + +```bash +node --version +# Expected: v20.x.x or v18.x.x (minimum v18) + +npm --version +# Expected: 9.x.x or 10.x.x +``` + +## Python 3 (for Flow Engine) + +**Only needed if you scan Flow files (*.flow-meta.xml).** + +### macOS + +```bash +# Via Homebrew +brew install python3 + +# macOS may already have python3 via Xcode Command Line Tools +xcode-select --install +``` + +### Windows + +```bash +# Via winget +winget install Python.Python.3.12 + +# Or from python.org +# https://www.python.org/downloads/windows/ +``` + +### Linux + +```bash +# Usually pre-installed. If not: +# Ubuntu/Debian +sudo apt install python3 + +# RHEL/CentOS/Fedora +sudo dnf install python3 +``` + +### Verify + +```bash +python3 --version +# Expected: Python 3.x.x +``` + +## Authenticated Org (for ApexGuru) + +**Only needed for ApexGuru performance analysis.** + +```bash +# Login to a Salesforce org +sf org login web --alias my-org + +# Or login with JWT (CI/CD) +sf org login jwt --client-id --jwt-key-file --username --alias my-org + +# Verify +sf org display --target-org my-org +``` + +### Troubleshooting ApexGuru Auth + +| Issue | Solution | +|-------|----------| +| `No default org` | Set default: `sf config set target-org my-org` | +| `Session expired` | Re-login: `sf org login web --alias my-org` | +| `Insufficient permissions` | Org needs API access enabled | + +## Quick Setup Script + +For a complete setup on macOS with Homebrew: + +```bash +# Install all prerequisites +brew install node@20 openjdk@11 python3 + +# Set Java environment +export JAVA_HOME="/opt/homebrew/opt/openjdk@11" +export PATH="/opt/homebrew/opt/openjdk@11/bin:$PATH" + +# Install Salesforce CLI +npm install -g @salesforce/cli + +# Install Code Analyzer +sf plugins install @salesforce/plugin-code-analyzer + +# Verify everything +sf --version +sf plugins --core | grep code-analyzer +java -version +node --version +python3 --version +``` diff --git a/skills/configuring-code-analyzer/references/rule-name-resolution.md b/skills/configuring-code-analyzer/references/rule-name-resolution.md new file mode 100644 index 0000000..b528b5b --- /dev/null +++ b/skills/configuring-code-analyzer/references/rule-name-resolution.md @@ -0,0 +1,67 @@ +# Rule Name Resolution (Fuzzy Matching) + +**⚠️ CRITICAL:** The `rules` section in `code-analyzer.yml` requires the EXACT full rule name as it appears in Code Analyzer's rule registry. A misspelled or partial name will be silently ignored — the override won't apply, and no error is shown. + +## Why This Matters + +Unlike `--rule-selector` (which returns 0 results on mismatch), a wrong name in `code-analyzer.yml` is SILENTLY ignored. The config validates fine, but the override simply doesn't apply. This makes typos and partial names dangerous. + +## Common Fuzzy → Exact Mappings + +Users will often refer to rules by approximate, partial, or descriptive names: + +| User Says | Exact Rule Name | Engine | +|-----------|----------------|--------| +| "the ApexDoc rule" | `ApexDoc` | `pmd` | +| "no-console" | `no-console` | `eslint` | +| "CRUD violation" | `ApexCRUDViolation` | `pmd` | +| "hardcoded values" | `@salesforce-ux/slds/no-hardcoded-values-slds2` | `eslint` | +| "unused variables" | `no-unused-vars` | `eslint` | +| "soql injection" | `ApexSOQLInjection` | `pmd` | +| "global modifier" | `AvoidGlobalModifier` | `pmd` | +| "empty catch" | `EmptyCatchBlock` | `pmd` | + +## Lookup Procedure + +When you are NOT 100% certain of the exact full rule name: + +1. **Do NOT guess** — a wrong name silently fails (the override is ignored with no error) +2. **Look up the rule first** using the `sf code-analyzer rules` command with grep: + ```bash + sf code-analyzer rules --rule-selector all 2>&1 | grep -i "" + ``` +3. **If grep returns exactly one match** → use that exact rule name in the YAML +4. **If grep returns multiple matches** → present them to the user and ask which one they meant +5. **If grep returns 0 matches** → try broader keywords or tell the user no rule matched + +## When You CAN Skip the Lookup + +Skip only when confident in the exact name: +- User provides the full exact name (e.g., "ApexCRUDViolation", "no-unused-vars") +- The rule is extremely common AND unambiguous (e.g., "ApexDoc", "no-console") + +## Matching Strategies for Ambiguous Input + +| User Says | Grep Command | Notes | +|-----------|-------------|-------| +| "the doc rule" | `grep -i "doc"` | May match ApexDoc, JSDoc, etc. — ask user if multiple | +| "CRUD" | `grep -i "crud"` | Likely matches ApexCRUDViolation | +| "hardcoded" | `grep -i "hardcoded"` | May match multiple SLDS/custom rules | +| "console" | `grep -i "console"` | Likely matches no-console | +| "security rules" | Use `--rule-selector all:Security` | Category-based, not name-based | +| "the injection rule" | `grep -i "injection"` | May match ApexSOQLInjection, ApexXSSFromURLParam, etc. | +| "unused" | `grep -i "unused"` | May match no-unused-vars, UnusedLocalVariable, etc. | + +## Identifying the Engine + +The YAML structure requires nesting under the correct engine. Always extract BOTH the engine and rule name from the `sf code-analyzer rules` output: + +```yaml +rules: + : # ← must match the engine that owns the rule + : + severity: ... + disabled: ... +``` + +The output of `sf code-analyzer rules` shows engine name alongside each rule. Use that to determine the correct YAML path. diff --git a/skills/configuring-code-analyzer/references/troubleshooting.md b/skills/configuring-code-analyzer/references/troubleshooting.md new file mode 100644 index 0000000..de4e192 --- /dev/null +++ b/skills/configuring-code-analyzer/references/troubleshooting.md @@ -0,0 +1,298 @@ +# Troubleshooting Code Analyzer Setup + +Common issues and solutions during installation and configuration. + +## Installation Issues + +### sf CLI Not Found + +**Symptom:** `sf: command not found` or `'sf' is not recognized` + +**Solutions:** +1. Check if installed: `which sf` or `where sf` +2. If not installed: `npm install -g @salesforce/cli` +3. If installed but not in PATH: + - macOS/Linux: Add `export PATH="$(npm prefix -g)/bin:$PATH"` to `~/.zshrc` or `~/.bashrc` + - Windows: Add npm global bin to System PATH +4. Restart terminal after PATH changes + +### Plugin Install Fails + +**Symptom:** `Error: EACCES permission denied` or timeout errors + +**Solutions:** +| Error | Fix | +|-------|-----| +| Permission denied | `sudo sf plugins install @salesforce/plugin-code-analyzer` or fix npm permissions | +| Network timeout | Check proxy: `npm config set proxy http://proxy:port` | +| Node version error | Upgrade Node.js to 18+: `nvm install 20 && nvm use 20` | +| Corrupt install | `sf plugins uninstall @salesforce/plugin-code-analyzer && sf plugins install @salesforce/plugin-code-analyzer` | + +### Plugin Version Mismatch + +**Symptom:** Command flags don't work, unexpected behavior + +**Check version:** +```bash +sf plugins --core | grep code-analyzer +``` + +**Expected:** `@salesforce/plugin-code-analyzer` v5.x+ + +**If on v3/v4 (legacy):** +```bash +sf plugins uninstall @salesforce/sfdx-scanner # Remove legacy v3 +sf plugins install @salesforce/plugin-code-analyzer # Install v5+ +``` + +## Java Issues + +### Java Not Found + +**Symptom:** PMD/CPD/SFGE fails with `java: command not found` or `JAVA_HOME not set` + +**Fix:** +```bash +# macOS +brew install openjdk@11 +export JAVA_HOME="/opt/homebrew/opt/openjdk@11" +export PATH="$JAVA_HOME/bin:$PATH" + +# Add to ~/.zshrc for persistence +echo 'export JAVA_HOME="/opt/homebrew/opt/openjdk@11"' >> ~/.zshrc +echo 'export PATH="$JAVA_HOME/bin:$PATH"' >> ~/.zshrc +``` + +### Wrong Java Version + +**Symptom:** `UnsupportedClassVersionError` or `class file version X.Y` + +**Fix:** Code Analyzer needs Java 11+. Check and switch: +```bash +java -version # Check current + +# If using SDKMAN: +sdk install java 11.0.21-tem +sdk use java 11.0.21-tem + +# If using Homebrew: +brew install openjdk@11 +export JAVA_HOME="/opt/homebrew/opt/openjdk@11" +``` + +### SFGE Out of Memory + +**Symptom:** `java.lang.OutOfMemoryError: Java heap space` + +**Fix:** Increase heap in `code-analyzer.yml`: +```yaml +engines: + sfge: + java_max_heap_size: "4g" # Default is 1g, increase for large projects +``` + +**Guidelines:** +| Project Size | Heap Recommendation | +|-------------|-------------------| +| < 200 Apex classes | 2g | +| 200-500 Apex classes | 4g | +| 500-1000 Apex classes | 6g | +| 1000+ Apex classes | 8g | + +## Node.js Issues + +### Node.js Version Too Old + +**Symptom:** `Error: Node.js v16 is not supported` or ESLint failures + +**Fix:** +```bash +# Check version +node --version + +# Upgrade via nvm +nvm install 20 +nvm use 20 +nvm alias default 20 + +# Or via Homebrew +brew upgrade node +``` + +### ESLint Config Conflicts + +**Symptom:** ESLint rules not loading, or unexpected rules appearing + +**Possible causes:** +1. Project has its own `.eslintrc.*` conflicting with Code Analyzer's built-in config +2. `auto_discover_eslint_config` is enabled but project config is incompatible + +**Fix options:** +```yaml +# Option A: Disable auto-discovery (use only Code Analyzer's built-in rules) +engines: + eslint: + auto_discover_eslint_config: false + +# Option B: Use project's config exclusively +engines: + eslint: + auto_discover_eslint_config: true + disable_javascript_base_config: true + disable_typescript_base_config: true + disable_lwc_base_config: true +``` + +## Configuration Issues + +### Config File Not Picked Up + +**Symptom:** Custom settings not applied, default behavior persists + +**Checklist:** +1. File must be named exactly `code-analyzer.yml` or `code-analyzer.yaml` +2. File must be in the current working directory when running commands +3. Or specify explicitly: `--config-file ./path/to/code-analyzer.yml` +4. Check for YAML syntax errors: `sf code-analyzer config --config-file code-analyzer.yml` + +### YAML Syntax Errors + +**Symptom:** `YAMLException: bad indentation` or `unexpected token` + +**Common YAML mistakes:** +```yaml +# WRONG - tabs instead of spaces +engines: + pmd: # TAB character - YAML requires spaces! + +# CORRECT - spaces only +engines: + pmd: # 2 spaces + +# WRONG - missing quotes around special values +rules: + pmd: + MyRule: + severity: High # String values need quotes or use numbers + +# CORRECT +rules: + pmd: + MyRule: + severity: 2 # Use numbers 1-5 + # OR + severity: "High" # Or quoted strings +``` + +### Unknown Engine or Rule Name + +**Symptom:** Rule selector returns 0 results + +**Fix:** Verify the engine/rule name: +```bash +# List all available engines +sf code-analyzer rules --rule-selector all 2>&1 | head -50 + +# Search for a specific rule +sf code-analyzer rules --rule-selector all 2>&1 | grep -i "CRUD" + +# List rules for specific engine +sf code-analyzer rules --rule-selector pmd 2>&1 | head -50 +``` + +## Engine-Specific Issues + +### PMD: Custom Rules Not Loading + +**Symptom:** Custom PMD rules don't appear in `sf code-analyzer rules` + +**Checklist:** +1. Ruleset XML must be valid PMD format +2. Path in `custom_rulesets` must be relative to `config_root` +3. For Java rules: JAR must be in `java_classpath_entries` +4. Validate: `sf code-analyzer rules --rule-selector pmd:` + +### RetireJS: False Positives on Test Files + +**Symptom:** RetireJS flags test fixtures or mock data + +**Fix:** Add test paths to ignores: +```yaml +ignores: + files: + - "**/test/**" + - "**/__tests__/**" + - "**/jest-mocks/**" + - "**/*.test.js" + - "**/*.spec.js" +``` + +### Flow Engine: Python Not Found + +**Symptom:** `python3: command not found` when scanning Flows + +**Fix:** +```bash +# Install Python 3 +brew install python3 # macOS +# OR +sudo apt install python3 # Linux + +# If python3 is at non-standard path: +# code-analyzer.yml +engines: + flow: + python_command: "/usr/local/bin/python3" +``` + +### ApexGuru: Authentication Error + +**Symptom:** `No authenticated org found` or `Session expired` + +**Fix:** +```bash +# Login to org +sf org login web --alias my-org + +# Set as default +sf config set target-org my-org + +# Configure in code-analyzer.yml +engines: + apexguru: + target_org: "my-org" +``` + +## Performance Issues + +### Scan Takes Too Long + +**Possible causes and solutions:** + +| Cause | Solution | +|-------|----------| +| SFGE on large project | Increase heap, reduce thread timeout, or disable SFGE for routine scans | +| Scanning node_modules | Add `**/node_modules/**` to ignores | +| Too many engines enabled | Use `--rule-selector Recommended` instead of `all` for routine scans | +| Large static resources | Add `**/staticresources/**` to ignores | +| Many Flow files | Flow engine can be slow; scan Flows separately | + +### Reduce Scan Time in CI + +```yaml +# Fast CI scan: recommended rules only, severity gate at High +sf code-analyzer run \ + --rule-selector Recommended \ + --severity-threshold 2 \ + --target force-app/main/default \ + --output-file results.json +``` + +## Getting Help + +If none of the above solves your issue: + +1. **Check logs:** Look in the log folder (default: `/tmp` or configured `log_folder`) +2. **Increase log level:** Set `log_level: 5` in config for maximum detail +3. **Run with debug:** `SF_LOG_LEVEL=debug sf code-analyzer run ...` +4. **File an issue:** https://github.com/forcedotcom/code-analyzer-core/issues diff --git a/skills/configuring-code-analyzer/scripts/check-prerequisites.sh b/skills/configuring-code-analyzer/scripts/check-prerequisites.sh new file mode 100755 index 0000000..a9a9d02 --- /dev/null +++ b/skills/configuring-code-analyzer/scripts/check-prerequisites.sh @@ -0,0 +1,189 @@ +#!/bin/bash +# check-prerequisites.sh +# Checks all Code Analyzer prerequisites and reports status. +# Usage: bash /scripts/check-prerequisites.sh +# +# Exit codes: +# 0 = All prerequisites met +# 1 = Some prerequisites missing (details in output) + +set -euo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +PASS="${GREEN}PASS${NC}" +FAIL="${RED}FAIL${NC}" +WARN="${YELLOW}WARN${NC}" + +MISSING_COUNT=0 +WARNINGS_COUNT=0 + +echo "=========================================" +echo " Code Analyzer Prerequisites Check" +echo "=========================================" +echo "" + +# --- Check sf CLI --- +echo -n "Salesforce CLI (sf): " +if command -v sf &> /dev/null; then + SF_VERSION=$(sf --version 2>&1 | head -1) + echo -e "${PASS} - ${SF_VERSION}" +else + echo -e "${FAIL} - Not installed" + echo " Install: npm install -g @salesforce/cli" + MISSING_COUNT=$((MISSING_COUNT + 1)) +fi + +# --- Check Code Analyzer Plugin --- +echo -n "Code Analyzer plugin: " +if command -v sf &> /dev/null; then + CA_VERSION=$(sf plugins --core 2>&1 | grep -i "code-analyzer" | head -1) + if [ -n "$CA_VERSION" ]; then + echo -e "${PASS} - ${CA_VERSION}" + else + echo -e "${FAIL} - Not installed" + echo " Install: sf plugins install @salesforce/plugin-code-analyzer" + MISSING_COUNT=$((MISSING_COUNT + 1)) + fi +else + echo -e "${FAIL} - Cannot check (sf CLI missing)" + MISSING_COUNT=$((MISSING_COUNT + 1)) +fi + +# --- Check Java --- +echo -n "Java 11+ (PMD, CPD, SFGE): " +if command -v java &> /dev/null; then + JAVA_VERSION=$(java -version 2>&1 | head -1) + # Extract major version number + JAVA_MAJOR=$(java -version 2>&1 | head -1 | sed -E 's/.*"([0-9]+)\..*/\1/') + if [ "$JAVA_MAJOR" -ge 11 ] 2>/dev/null; then + echo -e "${PASS} - ${JAVA_VERSION}" + else + echo -e "${WARN} - ${JAVA_VERSION} (need 11+)" + echo " Upgrade: brew install openjdk@11" + WARNINGS_COUNT=$((WARNINGS_COUNT + 1)) + fi +else + echo -e "${FAIL} - Not installed" + echo " Install: brew install openjdk@11 (macOS) / sdk install java 11.0.x-tem" + echo " Needed for: PMD, CPD, SFGE engines" + MISSING_COUNT=$((MISSING_COUNT + 1)) +fi + +# --- Check JAVA_HOME --- +echo -n "JAVA_HOME: " +if [ -n "${JAVA_HOME:-}" ]; then + echo -e "${PASS} - ${JAVA_HOME}" +else + echo -e "${WARN} - Not set (may cause issues with some Java installations)" + WARNINGS_COUNT=$((WARNINGS_COUNT + 1)) +fi + +# --- Check Node.js --- +echo -n "Node.js 18+ (ESLint, RetireJS): " +if command -v node &> /dev/null; then + NODE_VERSION=$(node --version 2>&1) + NODE_MAJOR=$(echo "$NODE_VERSION" | sed -E 's/v([0-9]+)\..*/\1/') + if [ "$NODE_MAJOR" -ge 18 ] 2>/dev/null; then + echo -e "${PASS} - ${NODE_VERSION}" + else + echo -e "${WARN} - ${NODE_VERSION} (need 18+)" + echo " Upgrade: nvm install 20 && nvm use 20" + WARNINGS_COUNT=$((WARNINGS_COUNT + 1)) + fi +else + echo -e "${FAIL} - Not installed" + echo " Install: brew install node@20 (macOS) / nvm install 20" + echo " Needed for: ESLint, RetireJS engines" + MISSING_COUNT=$((MISSING_COUNT + 1)) +fi + +# --- Check Python 3 --- +echo -n "Python 3 (Flow engine): " +if command -v python3 &> /dev/null; then + PY_VERSION=$(python3 --version 2>&1) + echo -e "${PASS} - ${PY_VERSION}" +else + echo -e "${WARN} - Not installed (only needed for Flow scanning)" + echo " Install: brew install python3 (macOS) / apt install python3 (Linux)" + echo " Needed for: Flow engine only" + WARNINGS_COUNT=$((WARNINGS_COUNT + 1)) +fi + +# --- Check authenticated org --- +echo -n "Authenticated Org (ApexGuru): " +if command -v sf &> /dev/null; then + ORG_INFO=$(sf org display 2>&1) + if echo "$ORG_INFO" | grep -qi "username\|access token"; then + ORG_USER=$(echo "$ORG_INFO" | grep -i "username" | head -1 | awk '{print $NF}') + echo -e "${PASS} - ${ORG_USER}" + else + echo -e "${WARN} - No default org (only needed for ApexGuru)" + echo " Login: sf org login web --alias my-org" + WARNINGS_COUNT=$((WARNINGS_COUNT + 1)) + fi +else + echo -e "${WARN} - Cannot check (sf CLI missing)" + WARNINGS_COUNT=$((WARNINGS_COUNT + 1)) +fi + +# --- Check for existing config --- +echo "" +echo "-----------------------------------------" +echo -n "Config file (code-analyzer.yml): " +if [ -f "code-analyzer.yml" ] || [ -f "code-analyzer.yaml" ]; then + CONFIG_FILE=$(ls code-analyzer.yml code-analyzer.yaml 2>/dev/null | head -1) + echo -e "${PASS} - Found: ${CONFIG_FILE}" +else + echo -e "${WARN} - Not found (will use defaults)" + echo " Generate: sf code-analyzer config --output-file code-analyzer.yml" +fi + +# --- Check project type --- +echo -n "Project type: " +HAS_APEX=false +HAS_LWC=false +HAS_FLOWS=false +HAS_SFDX=false + +[ -d "force-app" ] && HAS_APEX=true +find . -maxdepth 4 -path "*/lwc/*" -name "*.js" 2>/dev/null | head -1 | grep -q . && HAS_LWC=true +find . -maxdepth 4 -name "*.flow-meta.xml" 2>/dev/null | head -1 | grep -q . && HAS_FLOWS=true +[ -f "sfdx-project.json" ] || [ -f "sf-project.json" ] && HAS_SFDX=true + +PROJECT_TYPES="" +[ "$HAS_APEX" = true ] && PROJECT_TYPES="${PROJECT_TYPES}Apex, " +[ "$HAS_LWC" = true ] && PROJECT_TYPES="${PROJECT_TYPES}LWC, " +[ "$HAS_FLOWS" = true ] && PROJECT_TYPES="${PROJECT_TYPES}Flows, " +[ "$HAS_SFDX" = true ] && PROJECT_TYPES="${PROJECT_TYPES}SFDX Project, " + +if [ -n "$PROJECT_TYPES" ]; then + echo "${PROJECT_TYPES%, }" +else + echo "Unknown (no Salesforce project markers found)" +fi + +# --- Summary --- +echo "" +echo "=========================================" +echo " Summary" +echo "=========================================" + +if [ $MISSING_COUNT -eq 0 ] && [ $WARNINGS_COUNT -eq 0 ]; then + echo -e "${GREEN}All prerequisites met! Ready to scan.${NC}" + exit 0 +elif [ $MISSING_COUNT -eq 0 ]; then + echo -e "${YELLOW}${WARNINGS_COUNT} warning(s) - some engines may not work.${NC}" + echo "Core functionality (PMD, ESLint) should work if Java and Node.js are available." + exit 0 +else + echo -e "${RED}${MISSING_COUNT} required prerequisite(s) missing.${NC}" + [ $WARNINGS_COUNT -gt 0 ] && echo -e "${YELLOW}${WARNINGS_COUNT} additional warning(s).${NC}" + echo "" + echo "Install missing prerequisites before running Code Analyzer." + exit 1 +fi diff --git a/skills/configuring-code-analyzer/scripts/generate-config.sh b/skills/configuring-code-analyzer/scripts/generate-config.sh new file mode 100755 index 0000000..7c2fdf4 --- /dev/null +++ b/skills/configuring-code-analyzer/scripts/generate-config.sh @@ -0,0 +1,143 @@ +#!/bin/bash +# generate-config.sh +# Generates a code-analyzer.yml with ONLY project-specific overrides. +# Does NOT duplicate built-in defaults — only writes what intentionally differs. +# +# Usage: bash /scripts/generate-config.sh [--type apex|lwc|fullstack] +# +# If --type is not specified, auto-detects from workspace contents. + +set -euo pipefail + +CONFIG_FILE="code-analyzer.yml" +PROJECT_TYPE="${1:-auto}" + +# Remove --type prefix if present +PROJECT_TYPE="${PROJECT_TYPE#--type=}" +PROJECT_TYPE="${PROJECT_TYPE#--type }" + +# Auto-detect project type +if [ "$PROJECT_TYPE" = "auto" ] || [ "$PROJECT_TYPE" = "" ]; then + HAS_APEX=false + HAS_LWC=false + HAS_NODE_MODULES=false + + [ -d "force-app" ] && HAS_APEX=true + find . -maxdepth 4 -path "*/lwc/*" -name "*.js" 2>/dev/null | head -1 | grep -q . && HAS_LWC=true + [ -d "node_modules" ] && HAS_NODE_MODULES=true + + if [ "$HAS_APEX" = true ] && [ "$HAS_LWC" = true ]; then + PROJECT_TYPE="fullstack" + elif [ "$HAS_LWC" = true ]; then + PROJECT_TYPE="lwc" + elif [ "$HAS_APEX" = true ]; then + PROJECT_TYPE="apex" + else + PROJECT_TYPE="minimal" + fi + + echo "Auto-detected project type: ${PROJECT_TYPE}" + + # Warn if no Salesforce project markers found + if [ "$PROJECT_TYPE" = "minimal" ] && [ ! -f "sfdx-project.json" ] && [ ! -f "sf-project.json" ]; then + echo "WARNING: No Salesforce project markers found (no sfdx-project.json, sf-project.json, or force-app/)." + echo " Are you running this from your project root?" + echo " Generating minimal config — re-run from project root for better detection." + fi +fi + +# Check if config already exists +if [ -f "$CONFIG_FILE" ] || [ -f "code-analyzer.yaml" ]; then + echo "WARNING: Config file already exists." + echo "Edit the existing file instead of regenerating." + exit 1 +fi + +echo "Generating ${CONFIG_FILE} (overrides only)..." + +case "$PROJECT_TYPE" in + apex) + cat > "$CONFIG_FILE" << 'YAML' +# Code Analyzer overrides for Apex project +# Only entries that differ from built-in defaults + +ignores: + files: + - "**/node_modules/**" + - "**/.sfdx/**" + - "**/.sf/**" + +engines: + sfge: + java_max_heap_size: "4g" +YAML + ;; + + lwc) + cat > "$CONFIG_FILE" << 'YAML' +# Code Analyzer overrides for LWC project +# Only entries that differ from built-in defaults + +ignores: + files: + - "**/node_modules/**" + - "**/.sfdx/**" + - "**/.sf/**" + - "**/jest-mocks/**" + - "**/__tests__/**" + +engines: + eslint: + auto_discover_eslint_config: true +YAML + ;; + + fullstack) + cat > "$CONFIG_FILE" << 'YAML' +# Code Analyzer overrides for full-stack Salesforce project +# Only entries that differ from built-in defaults + +ignores: + files: + - "**/node_modules/**" + - "**/.sfdx/**" + - "**/.sf/**" + - "**/jest-mocks/**" + - "**/__tests__/**" + +engines: + sfge: + java_max_heap_size: "4g" + eslint: + auto_discover_eslint_config: true +YAML + ;; + + minimal) + cat > "$CONFIG_FILE" << 'YAML' +# Code Analyzer overrides +# Only entries that differ from built-in defaults + +ignores: + files: + - "**/node_modules/**" +YAML + ;; + + *) + echo "ERROR: Unknown project type: ${PROJECT_TYPE}" + echo "Valid types: apex, lwc, fullstack, minimal" + exit 1 + ;; +esac + +echo "" +echo "Created: ${CONFIG_FILE}" +echo "" +echo "This file contains ONLY overrides — built-in defaults still apply for" +echo "everything not listed here. Add more overrides as needed." +echo "" +echo "Next steps:" +echo " 1. Review: cat ${CONFIG_FILE}" +echo " 2. Validate: sf code-analyzer config --config-file ${CONFIG_FILE}" +echo " 3. Run scan: sf code-analyzer run --output-file results.json --include-fixes" diff --git a/skills/configuring-code-analyzer/scripts/validate-config.sh b/skills/configuring-code-analyzer/scripts/validate-config.sh new file mode 100755 index 0000000..202495b --- /dev/null +++ b/skills/configuring-code-analyzer/scripts/validate-config.sh @@ -0,0 +1,153 @@ +#!/bin/bash +# validate-config.sh +# Validates a code-analyzer.yml configuration file. +# Usage: bash /scripts/validate-config.sh [config-file] +# +# If no config file is specified, looks for code-analyzer.yml in current directory. +# +# Exit codes: +# 0 = Config is valid +# 1 = Config has errors + +set -euo pipefail + +CONFIG_FILE="${1:-code-analyzer.yml}" + +echo "Validating: ${CONFIG_FILE}" +echo "" + +# Check file exists +if [ ! -f "$CONFIG_FILE" ]; then + echo "ERROR: Config file not found: ${CONFIG_FILE}" + echo "" + echo "Expected locations:" + echo " - ./code-analyzer.yml" + echo " - ./code-analyzer.yaml" + echo "" + echo "Generate one with: sf code-analyzer config --output-file code-analyzer.yml" + exit 1 +fi + +# Check file is not empty +if [ ! -s "$CONFIG_FILE" ]; then + echo "ERROR: Config file is empty: ${CONFIG_FILE}" + exit 1 +fi + +# Basic YAML syntax check (if python3 available) +if command -v python3 &> /dev/null; then + echo "Checking YAML syntax..." + if python3 -c " +import yaml, sys +try: + with open('${CONFIG_FILE}', 'r') as f: + config = yaml.safe_load(f) + if config is None: + print('WARNING: Config file is empty or contains only comments') + sys.exit(0) + if not isinstance(config, dict): + print('ERROR: Config must be a YAML mapping (dictionary)') + sys.exit(1) + print('YAML syntax: OK') +except yaml.YAMLError as e: + print(f'ERROR: Invalid YAML syntax') + print(f' {e}') + sys.exit(1) +" 2>&1; then + echo "" + else + echo "" + echo "Fix YAML syntax errors before proceeding." + exit 1 + fi +else + echo "WARNING: python3 not found — skipping YAML syntax validation (install python3 for full checks)" +fi + +# Validate known field names +echo "Checking known fields..." +VALID_TOP_LEVEL="config_root log_folder log_level rules engines ignores suppressions" + +if command -v python3 &> /dev/null; then + python3 -c " +import yaml, sys + +with open('${CONFIG_FILE}', 'r') as f: + config = yaml.safe_load(f) + +if config is None: + sys.exit(0) + +valid_fields = set('${VALID_TOP_LEVEL}'.split()) +unknown = set(config.keys()) - valid_fields +if unknown: + print(f'WARNING: Unknown top-level fields: {sorted(unknown)}') + print(' Valid fields: config_root, log_folder, log_level, rules, engines, ignores, suppressions') +else: + print('Known fields: OK') + +# Check engines section +if 'engines' in config and config['engines']: + valid_engines = {'pmd', 'cpd', 'eslint', 'regex', 'retire-js', 'flow', 'sfge', 'apexguru'} + configured_engines = set(config['engines'].keys()) + unknown_engines = configured_engines - valid_engines + if unknown_engines: + print(f'WARNING: Unknown engine names: {sorted(unknown_engines)}') + print(f' Valid engines: {sorted(valid_engines)}') + else: + print(f'Engines configured: {sorted(configured_engines)}') + +# Check ignores section +if 'ignores' in config and config['ignores']: + if 'files' not in config['ignores']: + print('WARNING: ignores section should contain a \"files\" list') + elif not isinstance(config['ignores']['files'], list): + print('ERROR: ignores.files must be a list of glob patterns') + sys.exit(1) + else: + print(f'Ignore patterns: {len(config[\"ignores\"][\"files\"])} patterns configured') + +# Check rules section +if 'rules' in config and config['rules']: + for engine, rules in config['rules'].items(): + if rules and isinstance(rules, dict): + for rule_name, overrides in rules.items(): + if overrides and isinstance(overrides, dict): + if 'severity' in overrides: + sev = overrides['severity'] + valid_sevs = [1, 2, 3, 4, 5, 'Critical', 'High', 'Moderate', 'Low', 'Info'] + if sev not in valid_sevs: + print(f'WARNING: rules.{engine}.{rule_name}.severity = {sev} (expected 1-5 or Critical/High/Moderate/Low/Info)') + +print('') +" 2>&1 +fi + +# Run sf code-analyzer config validation (if sf CLI available) +echo "" +echo "Running Code Analyzer validation..." +if command -v sf &> /dev/null; then + if sf plugins --core 2>&1 | grep -qi "code-analyzer"; then + if sf code-analyzer config --config-file "$CONFIG_FILE" > /dev/null 2>&1; then + echo "Code Analyzer validation: PASSED" + echo "" + echo "Configuration is valid and ready to use." + exit 0 + else + echo "Code Analyzer validation: FAILED" + echo "" + echo "Running with verbose output:" + sf code-analyzer config --config-file "$CONFIG_FILE" 2>&1 || true + exit 1 + fi + else + echo "SKIP: Code Analyzer plugin not installed (cannot run full validation)" + echo "Install: sf plugins install @salesforce/plugin-code-analyzer" + fi +else + echo "SKIP: sf CLI not available (cannot run full validation)" +fi + +echo "" +echo "Basic validation passed. Install Code Analyzer for full validation." +exit 0 diff --git a/skills/managing-cdc-enablement/SKILL.md b/skills/managing-cdc-enablement/SKILL.md new file mode 100644 index 0000000..d6cdc6a --- /dev/null +++ b/skills/managing-cdc-enablement/SKILL.md @@ -0,0 +1,164 @@ +--- +name: managing-cdc-enablement +description: "Use to enable Salesforce Change Data Capture (CDC) on a standard or custom object, configure a custom event channel, set a filter expression, or add enrichment fields. TRIGGER broadly on any of: 'enable CDC', 'enable Change Data Capture', 'turn on CDC', 'subscribe X to change events', 'only emit events for', 'filter change events', 'enrich change events', 'create a custom event channel'; or any mention of CDC, change events, PlatformEventChannel, PlatformEventChannelMember, EnrichedField, ChangeEvents channel, enrichment fields, change event filter; or when the user wants a downstream system to receive Salesforce data changes; or when the user touches .platformEventChannelMember-meta.xml / .platformEventChannel-meta.xml files. SKIP when publishing platform events, Pub/Sub API or REST/SOAP (use building-sf-integrations), or ManagedEventSubscription (out of scope for CDC). Always use this skill for CDC channel-membership metadata." +metadata: + version: "1.0" +--- + +# Managing Change Data Capture Enablement + +Generate the metadata that subscribes Salesforce objects to Change Data Capture: `PlatformEventChannelMember` files for the default `ChangeEvents` channel or a custom channel, and `PlatformEventChannel` files for new custom channels. Covers enrichment fields, filter expressions, and the canonical naming and value formats that the Metadata API actually accepts (which differ from values that appear in many internal test fixtures and code-search hits). + +## Scope + +- **In scope**: Generating `PlatformEventChannelMember` and `PlatformEventChannel` metadata for CDC. Subscribing standard objects, custom objects, or both. Configuring enrichment fields. Configuring filter expressions. Defining custom data channels. +- **Out of scope**: Publishing custom platform events (PE) — that's a different metadata type (`PlatformEvent`). Pub/Sub API or external Kafka/Bayeux configuration. Pricing/limits guidance — refer the user to the [CDC Developer Guide](https://developer.salesforce.com/docs/atlas.en-us.change_data_capture.meta/change_data_capture/). Programmatic event-bus subscribers in Apex. + +--- + +## Clarifying Questions + +Before generating, confirm with the user if not already clear: + +- Which entity (or entities) need CDC enablement? Standard, custom, or both? +- Default channel (`ChangeEvents`) or a custom channel? If custom, what's the channel label? +- Any enrichment fields needed? (Lookup IDs that the consumer needs even when they didn't change.) +- Any filter expression needed? (A SOQL-WHERE-clause body that gates which change events emit.) + +--- + +## Required Inputs + +Gather or infer before proceeding: + +- **Source entity API name(s)** — e.g. `Account`, `Lead`, `Order__c`. The skill internally translates this to the **ChangeEvent entity name** (see Workflow step 2). +- **Channel** — either `ChangeEvents` (default) or the developer name of a custom channel ending in `__chn`. +- **Enrichment fields (optional)** — list of field API names on the source object whose values should be included in every change event. +- **Filter expression (optional)** — a predicate over fields on the change event payload (e.g. `Status__c != null`). + +Defaults unless specified: +- Channel: `ChangeEvents` (the default CDC channel — no path prefix). +- Enrichment fields: none. +- Filter expression: none. + +If the user provides a clear, complete request, generate immediately without unnecessary back-and-forth. + +--- + +## Workflow + +All steps are sequential. Do not skip or reorder. + +**Before generating anything, know the only valid CDC metadata types:** CDC is expressed entirely through `PlatformEventChannelMember` (one per subscribed entity) and `PlatformEventChannel` (only for custom channels). Do NOT use ``, `.changeDataCapture-meta.xml`, `changeDataCapture/` directories, `EnableChangeDataCapture`, or `ManagedEventSubscription` — these are not in scope for CDC. If you find yourself writing any of them, stop and use a `PlatformEventChannelMember` file instead. + +1. **Identify the channel** — if the user names a custom channel, you'll generate a `PlatformEventChannel` file (see step 4). Otherwise use the literal value `ChangeEvents` for the default channel. + +2. **Translate source entity to ChangeEvent entity name** — `` is the **ChangeEvent** type, NOT the source object: + + | Source object | `` value | + |---|---| + | `Account` | `AccountChangeEvent` | + | `Lead` | `LeadChangeEvent` | + | `Contact` | `ContactChangeEvent` | + | `Order__c` (custom) | `Order__ChangeEvent` | + | `MyThing__c` (custom) | `MyThing__ChangeEvent` | + + For standard objects: append `ChangeEvent`. For custom objects: replace the trailing `__c` with `__ChangeEvent` (the double-underscore is preserved). + +3. **Generate the channel-member file** — one file per `(entity, channel)` pair. **The filename and fullName always use a SINGLE underscore between the entity stem and `ChangeEvent`** — this is independent of how `selectedEntity` is formatted in the XML body. For custom objects, drop the `__c` from the source name when forming the filename: + + | Source object | Filename (and fullName) | `` (in XML) | + |---|---|---| + | `Account` | `Account_ChangeEvent.platformEventChannelMember-meta.xml` | `AccountChangeEvent` | + | `Lead` | `Lead_ChangeEvent.platformEventChannelMember-meta.xml` | `LeadChangeEvent` | + | `Order__c` | `Order_ChangeEvent.platformEventChannelMember-meta.xml` (NOT `Order__ChangeEvent`) | `Order__ChangeEvent` | + | `MyThing__c` | `MyThing_ChangeEvent.platformEventChannelMember-meta.xml` (NOT `MyThing__ChangeEvent`) | `MyThing__ChangeEvent` | + + The custom-object case is the easiest place to slip — the filename uses single underscore, the `selectedEntity` keeps its double underscore. Read `assets/PlatformEventChannelMember-template.xml` as the structural template. + +4. **For a custom channel**, generate a `PlatformEventChannel` file — required if any member references a non-default channel. Derive a DeveloperName from the user's label: strip spaces and non-alphanumeric characters, convert to CamelCase, then **always** append the literal suffix `__chn`. The filename and the channel's `` reference must use this exact form, otherwise the deploy fails with `Invalid channel name`: + + | User says | DeveloperName | Filename | + |---|---|---| + | `Partner Sync` | `PartnerSync__chn` | `PartnerSync__chn.platformEventChannel-meta.xml` (NOT `Partner_Sync...` or `PartnerSync...`) | + | `Order Updates` | `OrderUpdates__chn` | `OrderUpdates__chn.platformEventChannel-meta.xml` | + | `data sync` | `DataSync__chn` | `DataSync__chn.platformEventChannel-meta.xml` | + + Members on this channel reference it by the same DeveloperName: `PartnerSync__chn`. Read `assets/PlatformEventChannel-template.xml`. + +5. **Add enrichment fields** if requested — repeat the `FIELD_API_NAME` block for each field. The name must be a **single-hop API name on the source entity** — verified working with: standard lookup IDs (`OwnerId`, `ParentId`), custom lookup fields (`MyLookup__c`), and custom non-relationship fields (`Region__c`, `Status__c`). Relationship traversals like `Owner.Name` or `Parent.Account.Industry` are rejected by deploy with "The selected field, X.Y, isn't valid". + +6. **Add a filter expression** if requested — wrap the predicate in `...`. The body is a WHERE-clause body without the `WHERE` keyword (e.g. `Status__c != null`, not `WHERE Status__c != null`). For supported operators, field types, and pitfalls, read `references/filter-expressions.md`. + +--- + +## Rules / Constraints + +| Constraint | Rationale | +|---|---| +| `` is the ChangeEvent type name, not the source object name | The Metadata API binds the member to a ChangeEvent entity — passing `Account` directly fails with "invalid event in selectedEntity". | +| Member fullName uses **single** underscore: `Account_ChangeEvent` | The double-underscore form (`Account__ChangeEvent`) is parsed as `__` and rejected: "Cannot create a new component with the namespace: Account". | +| Default channel value is exactly `ChangeEvents` — no path prefix | Older fixtures and some docs show `data/ChangeEvents`; the deploy returns "Unable to find the specified channel" for that value. | +| Enrichment field names are single-hop API names on the source entity | Standard (`OwnerId`), custom lookup (`MyLookup__c`), and custom non-relationship (`Region__c`) all validate. Traversals like `Owner.Name` are rejected: "The selected field, X.Y, isn't valid". | +| `` body has no `WHERE` keyword | Deploy returns "filter expression has syntax errors: unexpected token: 'WHERE'". | +| Filter cannot reference `IsDeleted` or do relationship traversal (`Owner.Username`) | Deploy rejects with "field is invalid". | +| DateTime fields support **only equality** in filters (`=`, `!=`) — not `<` / `>` | Deploy returns "Only equality operators are supported for this field type or value". Use a named date literal: `LastModifiedDate = TODAY`. | +| Filter RHS must be a literal — no field-to-field comparison | `BillingCity = ShippingCity` returns "unexpected token: 'ShippingCity'". | +| Compound fields (e.g. `BillingAddress`) require dotted component access in filter | `BillingAddress.City = 'X'` deploys; flat `BillingCity` is rejected as "field is invalid"; raw `BillingAddress` is rejected as "has to be used with a component field". Note this is the OPPOSITE of ``, which uses flat names. | +| Custom channel filename ends with `__chn` before the meta-xml suffix | Salesforce's MDAPI naming convention; mismatch causes deploy ambiguity. | +| Custom channel XML must include `data` | Without `data`, the channel is rejected for CDC (other types exist for streaming/event channels). | +| Source custom objects must already exist (or be deployed in the same transaction) | The ChangeEvent entity for `Foo__c` doesn't exist until `Foo__c` does; member deploy fails otherwise. | +| Never generate a `PlatformEventChannel` file for the default `ChangeEvents` channel | The default channel is system-provided. Reference it via `ChangeEvents` on members, but only custom (`__chn`) channels need a channel-meta file. | +| `PlatformEventChannelMember` accepts ONLY four elements: ``, ``, ``, `` | Adding ``, ``, ``, or any other element fails XML schema validation: "Element {...} invalid at this location". Stick to the four documented elements. | +| `PlatformEventChannel` accepts ONLY two elements: `` and `