This implements the "Automate The Company" initiative with comprehensive
Standard Operating Procedures for GitHub + Salesforce + Asana integration.
New directory: sop/
├── workflows/ - End-to-end process documentation
│ ├── new-client-kickoff.md - Flagship workflow from deal → repos → Asana
│ └── release-pipeline.md - Deploy → update Salesforce + Asana
├── playbooks/ - Human-friendly checklists
│ └── brenda-new-client-checklist.md - Non-technical operator guide
├── salesforce/ - Salesforce automation specifications
│ ├── flows/opp-automation-onstagechange.md - Trigger on Closed Won
│ └── orchestrations/new-client-kickoff-orchestration.md - Multi-stage process
├── integrations/ - API integration specifications
│ ├── salesforce-to-github.md - Create repos from Salesforce
│ ├── github-to-salesforce.md - Update Salesforce after deploy
│ └── salesforce-to-asana.md - Create Asana projects from Salesforce
└── templates/ - Reusable templates
├── github-actions/ - CI/CD workflows (ci.yml, deploy.yml, safety.yml)
└── repo-template/ - Standard repo config (PR template, labels, branch protection)
Key Features:
- Event-driven automation (Closed Won → repos + Asana creation)
- GitHub Actions templates for CI/CD baseline
- Salesforce Flow & Orchestration specs
- Complete API integration documentation
- Operator-friendly playbooks
- Two-view approach (operator + engineer)
- No manual status syncing across systems
This provides the complete backbone for next-gen ERP automation.
13 KiB
Integration: Salesforce → GitHub
Purpose: Enable Salesforce to create and configure GitHub repositories automatically Direction: Salesforce calls GitHub REST API Authentication: GitHub App or Personal Access Token Status: Active Last Updated: 2025-11-17
Overview
This integration allows Salesforce Flows and Orchestrations to:
- Create new repositories
- Configure repository settings
- Add labels, branch protection, workflows
- Manage repository secrets
Key Use Case: Automatic repo creation when Opportunity moves to "Closed Won"
Authentication Setup
Option A: GitHub App (Recommended for Production)
Benefits:
- More secure (short-lived tokens)
- Better rate limits
- Granular permissions
- Audit trail
Setup Steps:
-
Create GitHub App:
- Go to: https://github.com/organizations/blackboxprogramming/settings/apps
- Click "New GitHub App"
- Name:
BlackRoad Salesforce Integration - Homepage URL:
https://blackroad.app - Webhook URL: (leave blank for now, covered in github-to-salesforce.md)
-
Permissions:
- Repository permissions:
- Administration: Read & Write
- Contents: Read & Write
- Metadata: Read-only
- Secrets: Read & Write
- Workflows: Read & Write
- Organization permissions:
- Members: Read-only
- Repository permissions:
-
Install App:
- Install app on organization:
blackboxprogramming - Select: All repositories (or specific repos)
- Install app on organization:
-
Generate Private Key:
- Download private key (
.pemfile) - Store securely in Salesforce
- Download private key (
-
Get App Details:
- App ID:
123456 - Installation ID:
789012
- App ID:
Salesforce Named Credential:
- Name:
GitHub_API - URL:
https://api.github.com - Identity Type: Named Principal
- Authentication Protocol: Custom
- Custom Authentication: Use Apex class to generate JWT → exchange for installation access token
Apex Class for Token Generation:
public class GitHubAppTokenProvider {
private static final String GITHUB_APP_ID = '123456';
private static final String GITHUB_APP_PRIVATE_KEY = 'YOUR_PRIVATE_KEY_HERE'; // Store in Protected Custom Setting
public static String getInstallationToken() {
// 1. Generate JWT
String jwt = generateJWT();
// 2. Exchange JWT for installation access token
HttpRequest req = new HttpRequest();
req.setEndpoint('https://api.github.com/app/installations/789012/access_tokens');
req.setMethod('POST');
req.setHeader('Authorization', 'Bearer ' + jwt);
req.setHeader('Accept', 'application/vnd.github+json');
Http http = new Http();
HttpResponse res = http.send(req);
if (res.getStatusCode() == 201) {
Map<String, Object> result = (Map<String, Object>) JSON.deserializeUntyped(res.getBody());
return (String) result.get('token');
}
throw new CalloutException('Failed to get GitHub access token: ' + res.getBody());
}
private static String generateJWT() {
// Use JWT library or implement JWT generation
// See: https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-json-web-token-jwt-for-a-github-app
}
}
Option B: Personal Access Token (Quick Start / Testing)
Setup Steps:
-
Create PAT:
- Go to: https://github.com/settings/tokens
- Click "Generate new token (classic)"
- Scopes:
repo(full control)workflow(update workflows)admin:org(read org)
-
Store in Salesforce:
- Setup → Named Credentials → New
- Name:
GitHub_API - URL:
https://api.github.com - Identity Type: Named Principal
- Authentication Protocol: Password Authentication
- Username: (your GitHub username)
- Password: (paste PAT)
⚠️ Security Note: PAT never expires unless you set an expiration. For production, use GitHub App.
API Endpoints & Payloads
1. Create Repository
Endpoint:
POST https://api.github.com/orgs/blackboxprogramming/repos
Headers:
Authorization: Bearer {GITHUB_TOKEN}
Accept: application/vnd.github+json
X-GitHub-Api-Version: 2022-11-28
Content-Type: application/json
Payload:
{
"name": "blackroad-ACME-1042-backend",
"description": "Backend for Acme Corp (ACME-1042)",
"private": true,
"auto_init": true,
"gitignore_template": "Python",
"license_template": "mit"
}
Response (201 Created):
{
"id": 123456789,
"name": "blackroad-ACME-1042-backend",
"full_name": "blackboxprogramming/blackroad-ACME-1042-backend",
"html_url": "https://github.com/blackboxprogramming/blackroad-ACME-1042-backend",
"clone_url": "https://github.com/blackboxprogramming/blackroad-ACME-1042-backend.git",
"default_branch": "main",
...
}
Salesforce Flow Implementation:
Element: HTTP Callout
Method: POST
Endpoint: {!$Credential.GitHub_API}/orgs/blackboxprogramming/repos
Headers:
- Authorization: Bearer {!$Credential.GitHub_API.AccessToken}
- Accept: application/vnd.github+json
- Content-Type: application/json
Body: (JSON from above, with merge fields)
Store Response In: varRepoResponse
Parse:
- varRepoURL = {!varRepoResponse.html_url}
- varRepoName = {!varRepoResponse.name}
2. Create Labels
Endpoint:
POST https://api.github.com/repos/blackboxprogramming/{REPO_NAME}/labels
Payload (repeat for each label):
{
"name": "type:feature",
"color": "0E8A16",
"description": "New feature or enhancement"
}
Salesforce Implementation:
Use a loop to create multiple labels from a JSON dataset:
Element: Loop
Collection: Parse JSON from sop/templates/repo-template/.github/labels.json
Current Item: varLabel
Inside Loop:
- HTTP Callout
- Endpoint: {!$Credential.GitHub_API}/repos/blackboxprogramming/{!varRepoName}/labels
- Body: {!varLabel}
3. Apply Branch Protection
Endpoint:
PUT https://api.github.com/repos/blackboxprogramming/{REPO_NAME}/branches/main/protection
Payload:
{
"required_status_checks": {
"strict": true,
"contexts": ["test (3.11)", "test (3.12)", "lint", "build"]
},
"enforce_admins": true,
"required_pull_request_reviews": {
"dismiss_stale_reviews": true,
"require_code_owner_reviews": true,
"required_approving_review_count": 1
},
"restrictions": null,
"required_linear_history": true,
"allow_force_pushes": false,
"allow_deletions": false
}
Response (200 OK):
{
"url": "https://api.github.com/repos/blackboxprogramming/blackroad-ACME-1042-backend/branches/main/protection",
...
}
4. Create Workflow Files
Endpoint:
PUT https://api.github.com/repos/blackboxprogramming/{REPO_NAME}/contents/.github/workflows/ci.yml
Payload:
{
"message": "Add CI workflow",
"content": "{BASE64_ENCODED_CI_YML}",
"branch": "main"
}
Salesforce Implementation:
-
Store workflow YAML templates in Salesforce Static Resources:
ci_ymldeploy_ymlsafety_yml
-
For each workflow:
- Load static resource
- Base64 encode content (use Apex)
- PUT to GitHub
Apex Helper:
public static String base64EncodeStaticResource(String resourceName) {
StaticResource sr = [SELECT Body FROM StaticResource WHERE Name = :resourceName LIMIT 1];
return EncodingUtil.base64Encode(sr.Body);
}
5. Add Repository Secrets
Endpoint:
PUT https://api.github.com/repos/blackboxprogramming/{REPO_NAME}/actions/secrets/{SECRET_NAME}
Payload:
{
"encrypted_value": "{ENCRYPTED_SECRET}",
"key_id": "{PUBLIC_KEY_ID}"
}
Pre-requisite: Get repository public key
GET https://api.github.com/repos/blackboxprogramming/{REPO_NAME}/actions/secrets/public-key
Response:
{
"key_id": "012345678912345678",
"key": "BASE64_PUBLIC_KEY"
}
Encryption:
Use libsodium sealed boxes (NaCl) to encrypt secrets.
Apex Implementation: Use external service or pre-encrypted values.
Secrets to Add:
| Secret Name | Value Source |
|---|---|
PROJECT_KEY |
From Project__c.Project_Key__c |
SALESFORCE_INSTANCE_URL |
From Salesforce |
SALESFORCE_ACCESS_TOKEN |
Generate Connected App token |
RAILWAY_TOKEN |
From Salesforce Custom Setting |
CLOUDFLARE_API_TOKEN |
From Salesforce Custom Setting |
ASANA_PAT |
From Salesforce Custom Setting |
Complete Flow Example
Salesforce Flow: Create GitHub Repos for Project
Flow Name: GitHub_Repo_Setup
Input Variables:
- ProjectRecordId (Text)
Steps:
1. Get Project Record
- Object: Project__c
- Filter: Id = {!ProjectRecordId}
- Store: varProject
2. Loop: For Each Repo Type
- Collection: ["backend", "frontend", "ops"]
- Current Item: varRepoType
2.1: Create Repo
- HTTP Callout (as documented above)
- Store Response: varRepoResponse
2.2: Create Labels
- Loop through labels.json
- HTTP Callout for each label
2.3: Apply Branch Protection
- HTTP Callout (as documented)
2.4: Create Workflow Files
- For each: ci.yml, deploy.yml, safety.yml
- HTTP Callout to create file
2.5: Add Secrets
- Get public key
- Encrypt secrets
- PUT each secret
2.6: Update Project Record
- Assignment:
- Backend_Repo_URL__c = {!varRepoResponse.html_url} (if backend)
- Frontend_Repo_URL__c = {!varRepoResponse.html_url} (if frontend)
- Ops_Repo_URL__c = {!varRepoResponse.html_url} (if ops)
3. Update Project Record with all URLs
- Update Record: Project__c
4. Send Success Notification
- Post to Chatter or send email
Error Handling
Common Errors
| Status Code | Error | Cause | Solution |
|---|---|---|---|
| 401 | Unauthorized | Invalid token | Refresh GitHub App token or regenerate PAT |
| 403 | Forbidden | Insufficient permissions | Check GitHub App/PAT scopes |
| 422 | Unprocessable Entity | Repo name already exists | Check for existing repo first |
| 422 | Validation Failed | Branch protection: required check doesn't exist | Create workflow first, then apply protection |
| 404 | Not Found | Repo or resource doesn't exist | Verify repo was created successfully |
Salesforce Fault Path
Fault Path:
- Element: Create Case
Subject: "GitHub Integration Error: {!$Flow.FaultMessage}"
Description: "Failed to create GitHub repo for Project: {!varProject.Name}\n\nError: {!$Flow.FaultMessage}"
Priority: High
OwnerId: {!DevOpsQueueId}
- Element: Send Email
To: devops@blackroad.com
Subject: "GitHub Automation Failed"
Body: (include error details)
Testing
Manual Test (Postman / curl)
# 1. Create repo
curl -X POST \
https://api.github.com/orgs/blackboxprogramming/repos \
-H "Authorization: token YOUR_PAT" \
-H "Accept: application/vnd.github+json" \
-d '{
"name": "test-repo-delete-me",
"private": true,
"auto_init": true
}'
# 2. Create label
curl -X POST \
https://api.github.com/repos/blackboxprogramming/test-repo-delete-me/labels \
-H "Authorization: token YOUR_PAT" \
-d '{
"name": "type:test",
"color": "BADA55"
}'
# 3. Clean up
curl -X DELETE \
https://api.github.com/repos/blackboxprogramming/test-repo-delete-me \
-H "Authorization: token YOUR_PAT"
Salesforce Sandbox Test
- Create test Project record
- Run flow:
GitHub_Repo_Setupwith test Project ID - Verify:
- Repos created in GitHub
- Labels applied
- Branch protection enabled
- Workflows present
- Secrets added
- Clean up test repos
Rate Limits
GitHub API Rate Limits:
- PAT: 5,000 requests/hour
- GitHub App: 15,000 requests/hour
Per Repository Creation:
- Approximately 50-100 API calls (1 repo + labels + protection + workflows + secrets)
- Can create ~50-300 repos per hour
Best Practices:
- Use GitHub App for better limits
- Implement exponential backoff on 403 (rate limit exceeded)
- Cache public keys for secret encryption
- Batch operations where possible
Monitoring
Track These Metrics:
| Metric | Target | Alert Threshold |
|---|---|---|
| API Success Rate | > 98% | < 95% |
| Avg Response Time | < 2s | > 5s |
| Rate Limit Usage | < 50% | > 80% |
| Failed Repo Creations | < 2% | > 5% |
Salesforce Custom Object: GitHub_API_Log__c
Fields:
- Operation__c (Create Repo, Add Label, etc.)
- Project__c (Lookup)
- Status__c (Success, Failed)
- Status_Code__c (200, 201, 422, etc.)
- Error_Message__c
- Response_Time__c
- Timestamp__c
Related Documentation
- GitHub API Docs: Repositories
- GitHub API Docs: Branch Protection
- GitHub API Docs: Secrets
- Salesforce Orchestration: New Client Kickoff
- Workflow: New Client Kickoff
Changelog
| Date | Version | Change | Author |
|---|---|---|---|
| 2025-11-17 | 1.0 | Initial specification | Cece (Claude) |