· tutorials · 14 min read

Deploy Salesforce Code with Github Actions - Salesforce DevOps Tutorial

Learn how you can use GitHub Actions to manually deploy changes from git source control to Salesforce.

Learn how you can use GitHub Actions to manually deploy changes from git source control to Salesforce.

Developers spend countless hours deploying their code to production. They either need to use archaic change sets, or pay an arm and a leg to use “Industry Standard” tools. This leaves small teams with few practical options.

Enter GitHub. The world’s most popular place to track and store code. We can track changes by committing work with git. Additionally, we can deploy this code to our Salesforce environment with GitHub Actions.

Today we’ll explore how you can use GitHub Actions to deploy Salesforce code.

What Does DevOps Look Like In Salesforce?

DevOps is the practice of simplifying code management and change deployment. Companies implement these processes to make their code bases easier to understand, and easier to deploy.

When implementing new change management processes, a crawl, walk, run approach is best. We want users to adopt the process, which means simplifying their workflow.

End users will take the path of least resistance. If a tool or process has too much friction, the end user will not use the tool. This is the case with the latest tool, DevOps Center. You spend more time fighting the tool than deploying your changes.

We want to start with a tool that performs the following:

  • Lives outside Salesforce.
  • Automatically tracks changes.
  • Deploys to many environments.

To start our process will involve:

  1. Pulling source code out of Salesforce
  2. Storing code in GitHub.
  3. Manually deploying to target orgs.

Why is DevOps Different in Salesforce?

Salesforce environments are almost never compiled from scratch. Even tools like scratch orgs contain a subset of a Salesforce environment’s customizations. Additionally, many companies use third-party apps to improve their workflow. These apps are often difficult to maintain in sandbox environments.

Many changes in Salesforce are completed through declarative interfaces. Fields, objects, and flows are all created through the Salesforce UI. Some Salesforce components are maintained in production. Many companies agree to maintain the following in production:

  • Profiles
  • Reports
  • Page Layouts

With this in mind, traditional views on DevOps from other industries need to be tailored to fit the needs of Salesforce environments.

Why Not Use Existing DevOps Tools?

DevOps tools have always been in a weird place. Change sets have existed to deploy changes between environments, but have many issues including:

  • Not automatically tracking changes
  • Overwriting the target environment
  • Need to be recreated to deploy between environments.

While Salesforce has released tools to address this issue, they do not solve all needs. Dev Ops Center Review is one of these tools. And while it’s easy to configure, most of the time is spent fighting against the tool, rather than using it.

How Do I Get My Source Code Out of Salesforce?

Most developers in 2023 are using VS Code and SFDX to manage Salesforce development. This is Salesforce’s recommended developer environment. VS Code is one of the most powerful IDEs, and integrates seamlessly with SFDX. SFDX is a command line tool that gives a convenient interface to perform developer tasks. This includes

  • pushing code written to Salesforce.
  • Running code with Execute Anonymous.
  • Retrieving field definitions from Salesforce.

If you need to setup VS Code or SFDX, check out this tutorial here:

Creating An SFDX Project From Your Salesforce Instance

Most Salesforce orgs have existing code and customizations inside of Salesforce. To ensure that these are brought out of Salesforce and into your source code, perform the following to setup the project:

  1. Inside VS Code, open the command pallet Ctrl + Shift + P and run SFDX: Create Project with Manifest
  2. Choose the Empty template.
  3. Give the project a name, we will use SFDX-Project
  4. Choose the folder location on your computer.

VS Code will redirect you to the newly created project. This project created does not have any source code from your Salesforce instance. To retrieve this, run the following:

  1. Open the command pallet
  2. Run SFDX: Authorize an Org.
  3. Choose the type of org, Production.
  4. Enter an alias to call your org - prod.
  5. A new tab will be opened in your browser. Login using your credentials.

After logging in, you will be able to perform tasks in VS Code using SFDX. Opening the command pallet will yield many extra options after authentication.

Now that you are authenticated, it’s time to grab the source code. In the folder explorer on the left, open the folder manifest and right-click the file package.xml. Run the command SFDX: Retrieve Source in Manifest from Org.

This command is what pulls your source code from Salesforce into your local machine. You can see all the source code pulled over under the folder force-app. The files are organized based on the metadata type.

How to Use Git (GitHub)

Part of source tracking involves using the popular software, Git. This is a widely used version control system (VCS) that allows you to track changes in your codebase. Additionally, the software streamlines collaboration and managing your project’s history.

We can initialize the repository by opening the terminal with Ctrl + `and running the following at the root of the project:

git init

This will allow changes to be tracked within Git. We also need to commit the source code. We can run:

git add .
git commit -m "Initial commit"

From here we need to push the source code to GitHub. Inside GitHub, we can create an empty repository with the following:

  1. Log in to your GitHub account.
  2. Click on the ”+” sign in the top-right corner and select “New repository.”
  3. Give your repository a name.
  4. Add an optional description
  5. Choose the repository visibility of private
  6. Click “Create repository.”

Additionally, ensure no extra files are created during the repository creation in GitHub. Do not add a License or a README.md during this step.

From here, we need to add the remote to the GitHub repository on your local machine. You will need to configure SSH authentication. Then, run the following:

git remote add origin [email protected]:username/repository.git

All that’s left is pushing the source code from your local machine to GitHub is:

git push -u origin master

And that’s everything that’s needed to track source code and push to GitHub.

Track Source Code with Package.xml

Now that we understand how to work with GitHub, let’s dive deeper into how we can pull source code out of Salesforce.

In a previous step, we ran SFDX: Retrieve Source in Manifest from Org on the file package.xml. This file, package.xml is the bread and butter of component management in Salesforce.

This file is used in the following actions:

  • Retrieving metadata from Salesforce
  • Deploying metadata to Salesforce
  • Deleting metadata in Salesforce

The package.xml file will need to be continuously updated to support your Salesforce development. This is because other dependencies that are required in the Salesforce ecosystem will need to be tracked within the source code. This includes, but is not limited to:

  • Flows
  • Custom Fields
  • Custom Objects
  • Remote Site Settings

It is important that after components are created in the Salesforce UI, that they are brought into the source code to be tracked. Fields and objects will need to be tracked in the source code to be deployed.

Defining the package.xml

We can define components in the package.xml. The base file created does not include every metadata item that is available to track within Salesforce. Additional metadata can be added to track within source control. For example, the metadata item RemoteSiteSetting can be added by appending the following code after </types>

    <types>
        <members>*</members>
        <name>RemoteSiteSetting</name>
    </types>

This process can be done for every metadata item. To see a full list of what can be tracked within Salesforce, check out the documentation here.

Wildcard vs. Declarative

The wildcard operator seen above is a great way to track every metadata member for a type. But if it’s so great, why decide to declare metadata members manually? The truth is, it’s required. Not all metadata types support wildcard tracking. The main example is standard objects. Let’s say you want to pull the metadata from the standard object, Account (You probably do). The metadata type CustomObject would look like the following:

<types>
	<members>*</members>
	<members>Account</members>
	<name>CustomObject</name>
</types>

Notice how the account object is manually declared in this package.xml. Any additional objects that are being customized would need to be added to this list.

Because Salesforce instances are large in nature, it can become messy and cumbersome to track all objects in your source code. With a recent project of mine, the target org dropped their Support Cloud licenses. When running a blind wildcard operator, the following error occurred:

This user does not have permission to manage Salesforce Knowledge. CustomObject named: FAQ__kav

This means that the wildcard operator cannot be used. Any object that needs to be tracked must be declared by hand.

Generating a package.xml

Thankfully this is a widely understood problem that people have built tools to assist in building the package.xml. This website by benedwards44 is a great starting point to generate the package.xml. It is as simple as:

  1. Log in to your Salesforce Org
  2. Select the component option Exclude Managed
  3. Press generate
  4. Download the file and save it to your project.

This is a great way of getting all the components into the package.xml. From here, I still recommend trimming the file down, to reduce the size of what is being tracked.

What Should We Track in Our Source Code?

It isn’t required to track all changes made inside of Salesforce in your source code. In fact, you shouldn’t track everything in your source code. This is a case where less is more.

Only adding components that are being managed through VS code will reduce headaches downstream.

Most environments have developers and admins working together. If all source code is tracked in GitHub, and an admin makes a change to a field in production, then the change has the potential to be overwritten.

Automate Tasks Using Github Actions

GitHub Actions is an integrated CI/CD (Continuous Integration/Continuous Deployment) tool. It allows developers to automate repetitive tasks. These tasks can be run on:

  • Code pushes.
  • Pull requests.
  • Issue updates.
  • Etc.

GitHub Actions can help developers automate their workflow. Tasks like:

  • Running test classes,
  • Deploying to environments,
  • Running code scanning,
  • And more, are candidates to be automated with GitHub Actions.

The core building blocks of GitHub Actions are workflow files. These files define the series of steps to be executed, along with their dependencies and conditions. They are written in YAML (YAML Ain’t Markup Language). This makes it easy for developers to create and maintain workflows.

Import Syntax of GitHub Actions

Let’s expand on the structure of a workflow file. A workflow consists of one or more jobs, and each job contains one or more steps. A step represents a single task that should be performed, like:

  • Running a script
  • Checking out code
  • Deploying an application.

All GitHub Actions are stored under the .github directory. The actions can further be categorized as workflows or actions.

Sample GitHub Action: Run Test Classes

Now, let’s create a simple GitHub Action that automatically runs the test classes within production.

SFDX Authentication For DevOps

Earlier, we logged into our production environment. This saved a token offline that can be used for authentication. We can use this token to generate a login URL for CI/CD platforms.

Within SFDX, there is an easy way to authenticate for CI/CD platforms. Inside your terminal, run the following command:

sfdx org display --verbose --json -o <MY_TARGET_ORG_ALIAS>

We will need the value of sfdxAuthUrl for later.

We can use this value as a secret inside of GitHub. To add a secret, perform the following inside of GitHub:

  1. Navigate to Settings
  2. Under Security open Secrets and variables and click on Actions
  3. Press New Repository Secret
  4. Enter the name as SFDX_AUTH_URL_PRODUCTION
  5. Enter the secret as the sfdxAuthUrl copied above.

This URL can be used in any GitHub action, and cannot be seen by other people who have access to the repository.

I also recommend naming the URL based on the environment. A sample repository may have the following secret URLs:

  • SFDX_AUTH_URL_PRODUCTION
  • SFDX_AUTH_URL_FULL_SB
  • SFDX_AUTH_URL_PARTIAL_SB
  • SFDX_AUTH_URL_DEVELOPER_SB

Run Test Classes on Commit

Now that authentication is solved, let’s create the following workflow, .github/worflows/test.yml

name: Run Test Classes

on:
  push:
    paths:
      - force-app/**

jobs:
  run-test-classes:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - name: Install SFDX
        run: |
          npm install --global sfdx-cli
          sfdx --version
      - name: Login To Org
        run: |
          sfdx org login sfdx-url --set-default --sfdx-url-file <(echo "${{ secrets.SFDX_AUTH_URL_PRODUCTION }}")
      - name: Run Test Classes
        run: |
          sfdx apex run test -l RunLocalTests -w 15

This workflow will run test classes on the target production environment. The workflow will perform as follows:

  1. Run when any change inside the force-app folder is made.
  2. Checkout the code for the workflow.
  3. Install SFDX
  4. Authenticate to Salesforce with sfdx org login
  5. Run test classes. Note the -w 15 parameter that will wait 15 minutes to perform the test classes. This waiting period can be extended as needed.

When the test class runs are complete, you can see the results inside of GitHub.

Sample GitHub Action - Run SFDX Scanner

SFDX Scanner is a great tool that increases code quality. We can use this tool to automate code scanning in our repository. I lay out how to use this tool, and how to add it to your GitHub action in a recent blog post.

Manual Deploy with Github Action

We can create a GitHub action to deploy the source code to a target org. We want to be able to select the following:

  • Target environment.
  • Validation vs Real Deployment.

Building Reusable Workflows Using Actions

We can reuse deployment code to reduce the amount of code overlapping with actions. Actions are reusable components that can be added to any workflow.

This custom action will build a deployment package and deploy via SFDX. We can create the action under .github/actions/deploy-environment/action.yml with the following content:

name: Deploy To Environment
description: Deploys to a specified environment

inputs:
  validateOnly:
    description: 'Run the deployment as validation only'
    type: boolean
  environmentName:
    description: 'The name of the environment you are deploying to'
    type: string
    required: true
  sfdx_auth_url:
    description: 'The auth url tied to your deployment environment'
    required: true

runs:
  using: 'composite'
  steps:
    - name: Install sfdx
      shell: bash
      run: |
        npm install --global sfdx-cli
        sfdx --version
    - name: Login to ${{ inputs.environmentName }}
      shell: bash
      run: |
        sfdx org login sfdx-url --set-default --sfdx-url-file <(echo "${{ inputs.sfdx_auth_url }}")
    - name: Generate package.xml
      shell: bash
      run: |
        sfdx project generate manifest --source-dir force-app --output-dir manifest
    - name: Deploy to ${{ inputs.environmentName }}
      shell: bash
      run: |
        deployFlags=(
            --manifest manifest/package.xml
            --wait 30
            --test-level RunLocalTests
            --verbose
          )
        if [ "${{ inputs.validateOnly }}" = "true" ]; then
          deployFlags+=( --dry-run )
        fi
        sfdx project deploy start "${deployFlags[@]}"

This action uses the following inputs:

  • validateOnly
  • environmentName
  • sfdx_auth_url

A workflow can pass various values per environment with these values. This action will then:

  1. Install SFDX.
  2. Authenticate to the target org.
  3. Generate a package.xml to deploy to an environment using the metadata API.
  4. Deploy to the target environment.

Additionally, the validateOnly flag can be used to validate changes before deploying. This is recommended before pushing to ensure that unexpected changes aren’t being pushed to production.

Creating UI to manually deploy

Now that the action is created, it’s time to add wrapper code that allows us to select the environment to deploy to. Add the following content to the .github/workflows/deploy.yml file:

name: Manual Deploy

on:
  workflow_dispatch:
    inputs:
      environment:
        type: choice
        description: Which environment to deploy to
        options:
          - partial-sandbox
          - production
        default: 'partial-sandbox'
        required: true
      validateOnly:
        description: 'Run the deployment as validation only'
        type: boolean
        default: false

jobs:
  deploy-partial-sandbox:
    runs-on: ubuntu-latest
    if: ${{ github.event.inputs.environment == 'partial-sandbox' }}
    steps:
      - uses: actions/checkout@v3
      - name: Deploy to partial sandbox
        uses: ./.github/actions/deploy-environment
        with:
          sfdx_auth_url: ${{ secrets.SFDX_AUTH_URL_PARTIALSB }}
          environmentName: ${{ github.event.inputs.environment }}
          validateOnly: ${{ github.event.inputs.validateOnly }}

  deploy-prod:
    runs-on: ubuntu-latest
    if: ${{ github.event.inputs.environment == 'production' }}
    steps:
      - uses: actions/checkout@v3
      - name: Deploy to production
        uses: ./.github/actions/deploy-environment
        with:
          sfdx_auth_url: ${{ secrets.SFDX_AUTH_URL_PRODUCTION }}
          environmentName: ${{ github.event.inputs.environment }}
          validateOnly: ${{ github.event.inputs.validateOnly }}

This allows us to select the target environment to deploy to inside of GitHub. Any additional environments can be added here.

After this workflow is committed to GitHub, you can run this by performing:

  1. Go to Actions on your repository.
  2. Select the action Manual Deploy.
  3. On the top right, press Run workflow.

And that’s everything! You can see the results run inside of Github.

A few tips for running this successfully:

  1. All code that is retrieved through the package.xml will be deployed between environments.
  2. Use the validateOnly option to confirm changes being deployed to the target environment.
  3. Search for changes being made with changed and created.

Conclusion

GitHub actions are a powerful tool to automate your Salesforce deployment workflow. Workflows can be built to deploy code to different Salesforce environments. Additionally, customizing the package.xml file will allow different metadata items to be tracked in your source code.

Need Our Help To Get Your Data Into Salesforce?

Join dozens of other companies by learning how you can get all your company's data in one place.

Back to Blog