What's trending
UPCOMING EVENTS
How to Use Salesforce LWC to Trigger GitHub Actions Workflows
Salesforce development teams are increasingly adopting DevOps practices to improve code quality, accelerate releases, and automate repetitive tasks. GitHub Actions has emerged as the go-to tool for CI/CD pipelines, enabling teams to automate deployments, run tests, and manage releases efficiently.
Taking automation a step further, initiating these workflows directly from Salesforce, where business logic and user actions originate, unlocks a new level of automation and pushes the boundaries of DevOps enablement.
By integrating Salesforce Lightning Web Components (LWC) with GitHub pipelines, organizations can achieve true DevOps democratization, bridging the gap between development and business teams. This integration typically operates within the production environment, where admins can design controlled interfaces that enable operational users to perform secure, governed administrative workflows without requiring elevated admin privileges.
It provides a custom alternative to comprehensive DevOps platforms. The same framework architecture can be tested and validated in full-copy sandboxes during testing phases, ensuring flexibility and governance across environments.
Welcome to the game-changing world of GitHub Pipeline orchestration through Lightning Web Components.
The Benefits at a Glance
This framing highlights the strategic advantages of incorporating GitHub pipeline orchestration directly within Salesforce:
- Empower Admins: Give non-developers the power to trigger complex workflows, reducing dependency on development teams.
- Streamline Workflows: Minimize context-switching by centralizing process triggers within Salesforce.
- Boost Automation: Bring CI/CD into the CRM ecosystem for faster delivery.
Step-by-Step for Triggering Workflows Using LWC
This article explores how to trigger GitHub Actions workflows directly from Salesforce using LWC, enabling seamless DevOps automation for Salesforce projects.
Let’s deep dive into the architecture of this solution and break down its components.
Step 1: Set Up the GitHub Connection
To communicate with GitHub, you need a Personal Access Token (PAT) with the necessary permissions (repo and workflow scopes).
Disclaimer: If you are experimenting in a personal GitHub account, you can simply generate a PAT under your user settings. For organizations, PATs are still tied to a particular user account, but admins may prefer alternatives such as GitHub Apps or fine-grained PATs to enforce security and governance. While this guide demonstrates the process using a personal PAT for simplicity, you should adapt the approach according to your organization’s security and compliance policies.
- Log in to your GitHub account.
- Click your profile picture in the upper-right corner and select Settings:

- In the left sidebar, scroll down and click Developer settings:

- Under Developer settings, select Personal access tokens.

- Click Generate new token (you may see options for “classic” or “fine-grained” tokens; choose the one that fits your needs). In this case, the classic token is selected.

- Provide a descriptive name for the token, set an expiration date, and select the appropriate permissions or scopes (e.g., repo for full repository access, or more limited scopes for specific needs):
Select Generate New Token (classic):
- Maintain the generated token for later use.
Step 2: Define Your GitHub Workflow
For the sake of simplicity, we will consider the GitHub flow developed in a previous article.
Step 3: Create a Custom Metadata Type to Store Pipeline Default Parameters
To avoid entering the pipeline parameters manually every time it is triggered, create a custom metadata type to store these default settings.
Specifications for the Custom Metadata Type:
| Name = GitHub Settings | ||
|---|---|---|
| Custom Fields | ||
| Field Label | API Name | Data Type |
| GitHub Branch | Github_Branch__c | Text(255) |
| GitHub Workflow | GitHub_Workflow__c | Text(255) |
| GitHub Owner | Github_Owner__c | Text(255) |
| GitHub Repo | Github_Repo__c | Text(255) |
| GitHub PAT | Github_PAT__c | Text(255) |
Once the custom metadata type is created, add a record and fill in the parameters accordingly:

Step 4: Building an APEX Middleware
The GitHubActionService Apex class acts as the middleware, handling authentication, error management, and business logic.
Key Features of the Class:
- The method
getDefaultGitHubSettingsfetches GitHub-related configuration values from a Custom Metadata Type (Github_Settings__mdt) based on the provided flow name (githubFlow). - The method
triggerWorkflowsends a POST HTTP request to the GitHub API to trigger a GitHub Actions workflow. It requires parameters including the GitHub owner, repository, workflow filename, target branch, personal access token (PAT) for authentication, and an org alias (passed as an input to the workflow). The method returns a success or error message depending on the API response.
public class GitHubActionService {
@AuraEnabled(cacheable=true)
public static Map<String, String> getDefaultGitHubSettings(String githubFlow) {
Map<String, String> settings = new Map<String, String>();
try
{
// Use getInstance to get the Custom Metadata Type record by DeveloperName
Github_Settings__mdt config = Github_Settings__mdt.getInstance(githubFlow);
if (config != null) {
settings.put('githubOwner', config.Github_Owner__c);
settings.put('githubRepo', config.Github_Repo__c);
settings.put('githubWorkflow', config.Github_Workflow__c);
settings.put('githubBranch', config.Github_Branch__c);
settings.put('githubPAT', config.Github_PAT__c);
} else {
System.debug('No GitHub settings found for flow: ' + githubFlow);
}
} catch (Exception e) {
System.debug('Error retrieving GitHub settings: ' + e.getMessage());
throw new AuraHandledException('Error retrieving GitHub settings: ' +
e.getMessage());
}
return settings;
}
@AuraEnabled
public static String triggerWorkflow(
String githubOwner,
String githubRepo,
String githubWorkflow,
String githubBranch,
String githubPat,
String orgAlias
) {
try {
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('https://api.github.com/repos/' + githubOwner
+ '/' + githubRepo
+ '/actions/workflows/' + githubWorkflow
+ '/dispatches');
request.setMethod('POST');
request.setHeader('Accept', 'application/vnd.github+json');
request.setHeader('X-GitHub-Api-Version', '2022-11-28');
request.setHeader('Authorization', 'Bearer ' + githubPat);
request.setHeader('Content-Type', 'application/json');
Map<String, Object> payload = new Map<String, Object>();
payload.put('ref', githubBranch);
Map<String, String> inputs = new Map<String, String>();
inputs.put('ORG_ALIAS', orgAlias);
payload.put('inputs', inputs);
String body = JSON.serialize(payload);
request.setBody(body);
HttpResponse response = http.send(request);
if (response.getStatusCode() == 204) {
return 'SUCCESS: GitHub workflow triggered successfully.';
} else {
return 'ERROR: ' + response.getStatus() + ' - ' + response.getBody();
}
} catch (Exception e) {
return 'ERROR: ' + e.getMessage();
}
}
}
Step 5: Design Your LWC Admin Panel
Our reusable LWC component provides a secure bridge between Salesforce and GitHub Actions, with robust state management and error handling.
Key features include:
- Loads the default GitHub settings from Salesforce Custom Metadata.
- Validates user inputs.
- Triggers GitHub Actions workflows via Apex.
- Handles loading states and error messaging.
LWC Files Breakdown:
gitHubWorkflowTrigger.js
import { LightningElement, track, api, wire } from 'lwc';
import triggerWorkflow from '@salesforce/apex/GitHubActionService.triggerWorkflow';
import getDefaultGitHubSettings from '@salesforce/apex/GitHubActionService.getDefaultGitHubSettings';
export default class GitHubWorkflowTrigger extends LightningElement {
@track githubOwner = '';
@track githubRepo = '';
@track githubWorkflow = '';
@track githubBranch = '';
@track githubPat = '';
@track resultMessage = '';
@track isLoading = false;
@track settingsLoaded = false;
@api orgAlias = 'orgAlias';
@api githubFlow = 'Run_LWC_Flow';
// Load default settings from Custom Metadata
@wire(getDefaultGitHubSettings , { githubFlow: '$githubFlow' })
wiredSettings({ error, data }) {
if (data) {
this.githubOwner = data.githubOwner || '';
this.githubRepo = data.githubRepo || '';
this.githubWorkflow = data.githubWorkflow || '';
this.githubBranch = data.githubBranch || '';
this.githubPat = data.githubPAT || '';
this.settingsLoaded = true;
console.log('Default settings loaded from Custom Metadata:', data);
} else if (error) {
console.error('Error loading default settings from Custom Metadata:', error);
this.resultMessage = 'Error loading default settings: ' + (error.body?.message || error.message);
}
}
handleInputChange(event) {
const field = event.target.name;
this[field] = event.target.value;
}
async handleTriggerClick() {
if (this.isLoading || !this.settingsLoaded) return;
// Basic validation
if (!this.githubOwner || !this.githubRepo || !this.githubWorkflow || !this.githubBranch || !this.githubPat) {
this.resultMessage = 'ERROR: Please fill in all required GitHub fields';
return;
}
this.isLoading = true;
this.resultMessage = '';
try {
// Call Apex method to trigger the GitHub workflow
const result = await triggerWorkflow({
githubOwner: this.githubOwner
,githubRepo: this.githubRepo
,githubWorkflow: this.githubWorkflow
,githubBranch: this.githubBranch
,githubPat: this.githubPat
,orgAlias: this.orgAlias
});
this.resultMessage = result;
} catch (error) {
this.resultMessage = 'ERROR: ' + error.body.message;
} finally {
this.isLoading = false;
}
}
}
gitHubWorkflowTrigger.html
<template>
<lightning-card title="GitHub Action Trigger" icon-name="custom:custom63">
<div class="slds-p-around_medium">
<lightning-input
name="githubOwner"
label="GitHub Owner"
onchange={handleInputChange}
value={githubOwner}
>
</lightning-input>
<lightning-input
name="githubRepo"
label="GitHub Repository"
onchange={handleInputChange}
value={githubRepo}
>
</lightning-input>
<lightning-input
name="githubWorkflow"
label="Workflow Filename or ID"
onchange={handleInputChange}
value={githubWorkflow}
>
</lightning-input>
<lightning-input
name="githubBranch"
label="GitHub Branch"
onchange={handleInputChange}
value={githubBranch}
>
</lightning-input>
<lightning-input
type="password"
name="githubPat"
label="GitHub PAT"
onchange={handleInputChange}
value={githubPat}
>
</lightning-input>
<lightning-input
name="orgAlias"
label="Org Alias"
onchange={handleInputChange}
value={orgAlias}
>
</lightning-input>
<div class="slds-m-top_medium">
<lightning-button
label="Trigger Workflow"
onclick={handleTriggerClick}
variant="brand">
</lightning-button>
</div>
<template if:true={resultMessage}>
<div class="slds-m-top_medium">
{resultMessage}
</div>
</template>
<template if:true={isLoading}>
<div class="slds-m-top_medium">
<lightning-spinner alternative-text="Loading..." size="small"></lightning-spinner>
</div>
</template>
</div>
</lightning-card>
</template>
gitHubWorkflowTrigger.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>63.0</apiVersion>
<isExposed>true</isExposed>
<masterLabel>Trigger Github Workflow</masterLabel>
<description>Trigger Github Workflow</description>
<targets>
<target>lightning__AppPage</target>
<target>lightning__RecordPage</target>
<target>lightning__HomePage</target>
<target>lightning__FlowScreen</target>
<target>lightningCommunity__Page</target>
<target>lightningCommunity__Default</target>
</targets>
<targetConfigs>
<targetConfig targets="lightningCommunity__Default,lightning__AppPage,lightning__RecordPage,lightning__HomePage,lightning__FlowScreen">
<property name="orgAlias" type="String" label="Org Alias" />
<property name="githubFlow" type="String" label="GitHub Flow"/>
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
Disclaimer: The code provided here is intended as a conceptual example or proof of concept. Organizations should thoroughly test and refine this code to ensure it meets their operational and compliance requirements before deploying it in a production environment.
Integrating and Testing the Lightning Web Component
Step 1: Component Integration
- Embed the LWC component “GithubActionsTrigger” in an Experience Builder site. This gives you more flexibility and reduces licensing requirements, as it can be accessed by external users without needing full Salesforce licenses.
- Configure its related parameters: Github Flow & Org Alias.

Step 2: Component Execution
- Publish and access the Experience Builder site.
- Trigger the component functionality.

- Verification:
- Connect to GitHub.
- Navigate to the Actions tab.
- Confirm the latest “Run LWC Tests” workflow instance shows as triggered:

Summary
This approach bridges the gap between business processes and technical automation, demonstrating how Salesforce can serve as a true DevOps orchestrator in today’s modern enterprise.
By centralizing controls, integrating with external systems, and providing real-time feedback, admins gain unprecedented agility, oversight, and efficiency directly within the Salesforce platform.



