Files
blackroad-operating-system/sop/integrations/salesforce-to-github.md
Claude 7cde897040 Add complete automation SOP system for BlackRoad ERP
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.
2025-11-17 08:17:51 +00:00

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

Benefits:

  • More secure (short-lived tokens)
  • Better rate limits
  • Granular permissions
  • Audit trail

Setup Steps:

  1. Create GitHub App:

  2. Permissions:

    • Repository permissions:
      • Administration: Read & Write
      • Contents: Read & Write
      • Metadata: Read-only
      • Secrets: Read & Write
      • Workflows: Read & Write
    • Organization permissions:
      • Members: Read-only
  3. Install App:

    • Install app on organization: blackboxprogramming
    • Select: All repositories (or specific repos)
  4. Generate Private Key:

    • Download private key (.pem file)
    • Store securely in Salesforce
  5. Get App Details:

    • App ID: 123456
    • Installation ID: 789012

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:

  1. Create PAT:

  2. 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:

  1. Store workflow YAML templates in Salesforce Static Resources:

    • ci_yml
    • deploy_yml
    • safety_yml
  2. 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

  1. Create test Project record
  2. Run flow: GitHub_Repo_Setup with test Project ID
  3. Verify:
    • Repos created in GitHub
    • Labels applied
    • Branch protection enabled
    • Workflows present
    • Secrets added
  4. 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


Changelog

Date Version Change Author
2025-11-17 1.0 Initial specification Cece (Claude)