All skills
Skillintermediate

Deployment and DevOps

``` my-salesforce-project/ ├── .forceignore # Files to ignore in deployments ├── .gitignore # Git ignore patterns ├── sfdx-project.json # Project configuration ├── config/ │ └── project-scratch-def.json # Scratch org definition ├── force-app/ │ └── main/ │ └── default/ │ ├── classes/ # Apex classes │ ├── triggers/ # Apex triggers │ ├── lwc/ # Lightning Web Components │ ├── aura/ # Aura components

Claude Code Knowledge Pack7/10/2026

Overview

Deployment and DevOps


Salesforce DX Project Setup

Project Structure

my-salesforce-project/
├── .forceignore              # Files to ignore in deployments
├── .gitignore                # Git ignore patterns
├── sfdx-project.json         # Project configuration
├── config/
│   └── project-scratch-def.json  # Scratch org definition
├── force-app/
│   └── main/
│       └── default/
│           ├── classes/      # Apex classes
│           ├── triggers/     # Apex triggers
│           ├── lwc/          # Lightning Web Components
│           ├── aura/         # Aura components
│           ├── objects/      # Custom objects
│           ├── layouts/      # Page layouts
│           ├── flows/        # Flows
│           ├── profiles/     # Profiles
│           ├── permissionsets/  # Permission sets
│           └── staticresources/ # Static resources
├── scripts/
│   └── apex/                 # Anonymous Apex scripts
├── data/                     # Sample data files
└── manifest/
    └── package.xml           # Deployment manifest

sfdx-project.json

{
  "packageDirectories": [
    {
      "path": "force-app",
      "default": true,
      "package": "MyPackage",
      "versionName": "ver 1.0",
      "versionNumber": "1.0.0.NEXT",
      "definitionFile": "config/project-scratch-def.json"
    },
    {
      "path": "unpackaged",
      "default": false
    }
  ],
  "name": "my-salesforce-project",
  "namespace": "",
  "sfdcLoginUrl": "https://login.salesforce.com",
  "sourceApiVersion": "59.0",
  "plugins": {
    "salesforcedx-templates": {
      "minApiVersion": "55.0"
    }
  }
}

Scratch Org Definition

{
  "orgName": "My Company Dev Org",
  "edition": "Developer",
  "features": [
    "EnableSetPasswordInApi",
    "Communities",
    "ServiceCloud",
    "SalesCloud",
    "MultiCurrency"
  ],
  "settings": {
    "lightningExperienceSettings": {
      "enableS1DesktopEnabled": true
    },
    "mobileSettings": {
      "enableS1EncryptedStoragePref2": false
    },
    "securitySettings": {
      "passwordPolicies": {
        "enableSetPasswordInApi": true
      }
    },
    "communitiesSettings": {
      "enableNetworksEnabled": true
    },
    "languageSettings": {
      "enableTranslationWorkbench": true
    }
  },
  "objectSettings": {
    "opportunity": {
      "sharingModel": "private"
    },
    "account": {
      "sharingModel": "readWrite"
    }
  }
}

.forceignore

# Profiles (use Permission Sets instead)
**/profiles/**

# User-specific settings
**/settings/**

# Package installation
**/*-meta.xml.bak

# IDE files
.sfdx/
.sf/
.idea/
*.log

# OS files
.DS_Store
Thumbs.db

# Test data
**/test-data/**

# Ignore standard objects
**/objects/Account/fields/IsDeleted.field-meta.xml
**/objects/Account/fields/IsPersonAccount.field-meta.xml

SF CLI Commands

Authentication

# Login to Dev Hub (web browser)
sf org login web --set-default-dev-hub --alias mydevhub

# Login to production/sandbox
sf org login web --alias myprod --instance-url https://login.salesforce.com
sf org login web --alias mysandbox --instance-url https://test.salesforce.com

# Login with JWT (CI/CD)
sf org login jwt \\
  --client-id YOUR_CONNECTED_APP_CLIENT_ID \\
  --jwt-key-file server.key \\
  --username admin@mycompany.com \\
  --set-default-dev-hub \\
  --alias mydevhub

# List authenticated orgs
sf org list

# Logout
sf org logout --target-org myalias

Scratch Org Management

# Create scratch org (30-day expiration)
sf org create scratch \\
  --definition-file config/project-scratch-def.json \\
  --alias myscratch \\
  --set-default \\
  --duration-days 30

# Open scratch org in browser
sf org open --target-org myscratch

# Push source to scratch org
sf project deploy start --target-org myscratch

# Pull changes from scratch org
sf project retrieve start --target-org myscratch

# Delete scratch org
sf org delete scratch --target-org myscratch --no-prompt

# List scratch orgs
sf org list --all

Source Deployment

# Deploy to target org
sf project deploy start --target-org myprod

# Deploy specific metadata
sf project deploy start \\
  --target-org myprod \\
  --source-dir force-app/main/default/classes

# Deploy with tests
sf project deploy start \\
  --target-org myprod \\
  --test-level RunLocalTests

# Deploy using manifest
sf project deploy start \\
  --target-org myprod \\
  --manifest manifest/package.xml

# Quick deploy (after validation)
sf project deploy quick --job-id 0Af...

# Check deploy status
sf project deploy report --job-id 0Af...

# Cancel deployment
sf project deploy cancel --job-id 0Af...

Retrieve Metadata

# Retrieve all metadata
sf project retrieve start --target-org myprod

# Retrieve specific components
sf project retrieve start \\
  --target-org myprod \\
  --metadata ApexClass:AccountService

# Retrieve using package.xml
sf project retrieve start \\
  --target-org myprod \\
  --manifest manifest/package.xml

# Generate package.xml from org
sf project generate manifest \\
  --from-org myprod \\
  --output-dir manifest

Running Tests

# Run all tests
sf apex run test --target-org myscratch --code-coverage --result-format human

# Run specific test classes
sf apex run test \\
  --target-org myscratch \\
  --class-names AccountServiceTest,ContactServiceTest \\
  --code-coverage

# Run tests with output to file
sf apex run test \\
  --target-org myscratch \\
  --test-level RunLocalTests \\
  --output-dir test-results \\
  --wait 10

# Run async tests and check later
sf apex run test --target-org myscratch --synchronous false
sf apex get test --target-org myscratch --test-run-id 707...

Data Operations

# Export data using SOQL
sf data query \\
  --target-org myprod \\
  --query "SELECT Id, Name FROM Account LIMIT 10" \\
  --result-format csv \\
  > accounts.csv

# Import data
sf data import tree \\
  --target-org myscratch \\
  --plan data/sample-data-plan.json

# Export data for reimport
sf data export tree \\
  --target-org myprod \\
  --query "SELECT Id, Name, (SELECT Id, Name FROM Contacts) FROM Account LIMIT 10" \\
  --output-dir data

# Bulk upsert
sf data upsert bulk \\
  --target-org myprod \\
  --sobject Account \\
  --file accounts.csv \\
  --external-id External_Id__c

Package.xml Manifest

Complete Example

<?xml version="1.0" encoding="UTF-8"?>

    <types>
        <members>AccountService</members>
        <members>AccountTriggerHandler</members>
        <members>HttpCalloutService</members>
        <name>ApexClass</name>
    </types>
    <types>
        <members>AccountServiceTest</members>
        <name>ApexClass</name>
    </types>
    <types>
        <members>AccountTrigger</members>
        <name>ApexTrigger</name>
    </types>
    <types>
        <members>accountCard</members>
        <members>accountList</members>
        <name>LightningComponentBundle</name>
    </types>
    <types>
        <members>Account.External_System_Id__c</members>
        <members>Account.IsActive__c</members>
        <name>CustomField</name>
    </types>
    <types>
        <members>Integration_Log__c</members>
        <members>Account_Audit__c</members>
        <name>CustomObject</name>
    </types>
    <types>
        <members>Account_Manager</members>
        <members>Sales_Rep</members>
        <name>PermissionSet</name>
    </types>
    <types>
        <members>Order_Event__e</members>
        <name>PlatformEventChannel</name>
    </types>
    <types>
        <members>Account_Update_Flow</members>
        <name>Flow</name>
    </types>
    <types>
        <members>External_System</members>
        <name>NamedCredential</name>
    </types>
    <types>
        <members>CustomLabels</members>
        <name>CustomLabels</name>
    </types>
    <version>59.0</version>

Wildcard Retrieval

<?xml version="1.0" encoding="UTF-8"?>

    <types>
        <members>*</members>
        <name>ApexClass</name>
    </types>
    <types>
        <members>*</members>
        <name>ApexTrigger</name>
    </types>
    <types>
        <members>*</members>
        <name>LightningComponentBundle</name>
    </types>
    <types>
        <members>*</members>
        <name>CustomObject</name>
    </types>
    <types>
        <members>*</members>
        <name>Flow</name>
    </types>
    <version>59.0</version>


CI/CD Pipeline

GitHub Actions Workflow

# .github/workflows/salesforce-ci.yml
name: Salesforce CI/CD

on:
  push:
    branches: [main, develop]
    paths:
      - 'force-app/**'
      - 'manifest/**'
  pull_request:
    branches: [main, develop]

env:
  SFDX_CLI_URL: https://developer.salesforce.com/media/salesforce-cli/sf/channels/stable/sf-linux-x64.tar.xz

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Install Salesforce CLI
        run: |
          wget $SFDX_CLI_URL
          mkdir sfdx
          tar xJf sf-linux-x64.tar.xz -C sfdx --strip-components 1
          ./sfdx/bin/sf version

      - name: Authenticate to Dev Hub
        run: |
          echo "${{ secrets.SFDX_AUTH_URL }}" > auth.txt
          ./sfdx/bin/sf org login sfdx-url --sfdx-url-file auth.txt --alias devhub --set-default-dev-hub

      - name: Create Scratch Org
        run: |
          ./sfdx/bin/sf org create scratch \\
            --definition-file config/project-scratch-def.json \\
            --alias ci-scratch \\
            --set-default \\
            --duration-days 1

      - name: Push Source
        run: ./sfdx/bin/sf project deploy start --target-org ci-scratch

      - name: Run Apex Tests
        run: |
          ./sfdx/bin/sf apex run test \\
            --target-org ci-scratch \\
            --code-coverage \\
            --result-format human \\
            --wait 20 \\
            --test-level RunLocalTests

      - name: Check Code Coverage
        run: |
          COVERAGE=$(./sfdx/bin/sf apex get test --target-org ci-scratch --code-coverage --json | jq '.result.summary.orgWideCoverage' | tr -d '"' | tr -d '%')
          echo "Code coverage: $COVERAGE%"
          if [ "$COVERAGE" -lt "75" ]; then
            echo "Code coverage is below 75%"
            exit 1
          fi

      - name: Delete Scratch Org
        if: always()
        run: ./sfdx/bin/sf org delete scratch --target-org ci-scratch --no-prompt

  deploy-staging:
    needs: validate
    if: github.ref == 'refs/heads/develop'
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Install Salesforce CLI
        run: |
          wget $SFDX_CLI_URL
          mkdir sfdx
          tar xJf sf-linux-x64.tar.xz -C sfdx --strip-components 1

      - name: Authenticate to Staging
        run: |
          echo "${{ secrets.STAGING_AUTH_URL }}" > auth.txt
          ./sfdx/bin/sf org login sfdx-url --sfdx-url-file auth.txt --alias staging

      - name: Deploy to Staging
        run: |
          ./sfdx/bin/sf project deploy start \\
            --target-org staging \\
            --test-level RunLocalTests \\
            --wait 30

  deploy-production:
    needs: validate
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Install Salesforce CLI
        run: |
          wget $SFDX_CLI_URL
          mkdir sfdx
          tar xJf sf-linux-x64.tar.xz -C sfdx --strip-components 1

      - name: Authenticate to Production
        run: |
          echo "${{ secrets.PROD_AUTH_URL }}" > auth.txt
          ./sfdx/bin/sf org login sfdx-url --sfdx-url-file auth.txt --alias prod

      - name: Validate Deployment
        run: |
          ./sfdx/bin/sf project deploy validate \\
            --target-org prod \\
            --test-level RunLocalTests \\
            --wait 30

      - name: Quick Deploy
        run: |
          JOB_ID=$(./sfdx/bin/sf project deploy report --json | jq -r '.result.id')
          ./sfdx/bin/sf project deploy quick --job-id $JOB_ID --target-org prod

GitLab CI Pipeline

# .gitlab-ci.yml
stages:
  - validate
  - deploy

variables:
  SF_CLI_VERSION: "2.20.7"

.sf_base:
  image: salesforce/cli:${SF_CLI_VERSION}-full
  before_script:
    - sf --version

validate:
  extends: .sf_base
  stage: validate
  script:
    - echo $SFDX_AUTH_URL > auth.txt
    - sf org login sfdx-url --sfdx-url-file auth.txt --alias devhub --set-default-dev-hub
    - sf org create scratch --definition-file config/project-scratch-def.json --alias ci-scratch --set-default --duration-days 1
    - sf project deploy start --target-org ci-scratch
    - sf apex run test --target-org ci-scratch --code-coverage --result-format junit --output-dir test-results --wait 20
  after_script:
    - sf org delete scratch --target-org ci-scratch --no-prompt || true
  artifacts:
    reports:
      junit: test-results/*.xml
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

deploy_staging:
  extends: .sf_base
  stage: deploy
  environment:
    name: staging
  script:
    - echo $STAGING_AUTH_URL > auth.txt
    - sf org login sfdx-url --sfdx-url-file auth.txt --alias staging
    - sf project deploy start --target-org staging --test-level RunLocalTests --wait 30
  rules:
    - if: $CI_COMMIT_BRANCH == "develop"

deploy_production:
  extends: .sf_base
  stage: deploy
  environment:
    name: production
  script:
    - echo $PROD_AUTH_URL > auth.txt
    - sf org login sfdx-url --sfdx-url-file auth.txt --alias prod
    - sf project deploy start --target-org prod --test-level RunLocalTests --wait 30
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
  when: manual

Deployment Scripts

Pre-Deployment Validation

#!/bin/bash
# scripts/validate-deployment.sh

set -e

TARGET_ORG=${1:-"myscratch"}
TEST_LEVEL=${2:-"RunLocalTests"}

echo "=== Validating deployment to $TARGET_ORG ==="

# Check for uncommitted changes
if [[ $(git status --porcelain) ]]; then
    echo "Warning: Uncommitted changes detected"
fi

# Validate package.xml
if [ ! -f "manifest/package.xml" ]; then
    echo "Error: manifest/package.xml not found"
    exit 1
fi

# Run validation
echo "Starting validation deploy..."
sf project deploy validate \\
    --target-org "$TARGET_ORG" \\
    --manifest manifest/package.xml \\
    --test-level "$TEST_LEVEL" \\
    --wait 60

echo "=== Validation complete ==="

Post-Deployment Script

#!/bin/bash
# scripts/post-deployment.sh

set -e

TARGET_ORG=${1:-"myprod"}

echo "=== Running post-deployment tasks ==="

# Run data migration scripts
echo "Running data migration..."
sf apex run \\
    --target-org "$TARGET_ORG" \\
    --file scripts/apex/data-migration.apex

# Assign permission sets
echo "Ass