Skip to content

Latest commit

 

History

History
275 lines (211 loc) · 7.47 KB

File metadata and controls

275 lines (211 loc) · 7.47 KB

github_app

A Rust library for managing GitHub App integrations with GitOps support.

Features

  • GitHub App Authentication: JWT creation and installation access token management with automatic caching
  • Webhook Verification: HMAC-SHA256 signature verification for GitHub webhooks
  • GitOps: Git-backed manifest loading from GitHub repositories
  • Type-safe: Strongly typed webhook events and manifest parsing

Architecture

Module Responsibility
config Configuration for GitHub App, repository, and manifest settings
error Unified error handling across all operations
app_auth GitHub App JWT creation and installation token management with caching
webhook Webhook signature verification and event parsing
gitops Git operations for cloning, syncing, and loading manifests

Usage

1. Setup Configuration

use github_app::GitHubAppConfig;
use std::path::PathBuf;

let config = GitHubAppConfig::new(
    12345,                                    // app_id
    67890,                                    // installation_id
    std::fs::read_to_string("private-key.pem").unwrap(), // private_key_pem
    "webhook_secret".to_string(),             // webhook_secret
    "owner/repo".to_string(),                 // repo
    "main".to_string(),                       // branch
    PathBuf::from("/tmp/repo"),               // git_clone_path
    "manifests/**/*.yaml".to_string(),        // manifest_glob
);

config.validate()?;

2. Token Provider

use github_app::GitHubTokenProvider;

let token_provider = GitHubTokenProvider::new(config.clone());

let token = token_provider.get_token().await?;

The token provider automatically:

  • Creates JWTs signed with your private key
  • Fetches installation access tokens from GitHub
  • Caches tokens and refreshes them before expiry

3. Webhook Handler

use github_app::{WebhookVerifier, WebhookEvent};

let verifier = WebhookVerifier::new(&config);

async fn handle_webhook(
    event_type: &str,
    signature: &str,
    body: &[u8],
) -> Result<(), github_app::GitHubError> {
    let event = verifier.parse_event(event_type, signature, body)?;
    
    match event {
        WebhookEvent::Push(push_event) => {
            println!("Push to: {}", push_event.git_ref);
            if let Some(branch) = push_event.branch() {
                println!("Branch: {}", branch);
            }
            if let Some(sha) = push_event.commit_sha() {
                println!("Commit: {}", sha);
            }
        }
        WebhookEvent::Ping => {
            println!("Ping event received");
        }
        WebhookEvent::Unknown(event_type) => {
            println!("Unknown event: {}", event_type);
        }
    }
    
    Ok(())
}

HTTP Integration Example

// Extract headers and body from your HTTP framework
let event_type = request.headers().get("X-GitHub-Event").unwrap();
let signature = request.headers().get("X-Hub-Signature-256").unwrap();
let body = request.body().as_bytes();

// Verify and parse
match verifier.parse_event(event_type, signature, body) {
    Ok(event) => {
        // Handle event
        Ok(())
    }
    Err(github_app::GitHubError::InvalidSignature) => {
        // Return 401 Unauthorized
        Err("Invalid signature")
    }
    Err(e) => {
        // Return 500 Internal Server Error
        Err(format!("Error processing webhook: {}", e))
    }
}

4. GitOps Operations

use github_app::GitHubGitOps;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct DeploymentManifest {
    name: String,
    version: String,
}

let gitops = GitHubGitOps::new(config.clone(), token_provider);

gitops.initialize().await?;

gitops.sync().await?;

let manifests: Vec<DeploymentManifest> = gitops.load_all_manifests()?;

for manifest in manifests {
    println!("Found manifest: {} v{}", manifest.name, manifest.version);
}

5. Complete Broker Integration

use github_app::{
    GitHubAppConfig, GitHubTokenProvider, WebhookVerifier, 
    GitHubGitOps, WebhookEvent
};
use std::sync::Arc;

struct Broker {
    config: GitHubAppConfig,
    token_provider: Arc<GitHubTokenProvider>,
    webhook_verifier: WebhookVerifier,
    gitops: Arc<GitHubGitOps>,
}

impl Broker {
    async fn new(config: GitHubAppConfig) -> Result<Self, github_app::GitHubError> {
        config.validate()?;
        
        let token_provider = Arc::new(GitHubTokenProvider::new(config.clone()));
        let webhook_verifier = WebhookVerifier::new(&config);
        let gitops = Arc::new(GitHubGitOps::new(
            config.clone(),
            GitHubTokenProvider::new(config.clone())
        ));
        
        gitops.initialize().await?;
        
        Ok(Self {
            config,
            token_provider,
            webhook_verifier,
            gitops,
        })
    }
    
    async fn handle_webhook(
        &self,
        event_type: &str,
        signature: &str,
        body: &[u8],
    ) -> Result<(), github_app::GitHubError> {
        let event = self.webhook_verifier.parse_event(event_type, signature, body)?;
        
        match event {
            WebhookEvent::Push(push_event) => {
                if push_event.branch() == Some(self.config.branch.clone()) {
                    self.gitops.sync().await?;
                    self.reconcile().await?;
                }
            }
            _ => {}
        }
        
        Ok(())
    }
    
    async fn reconcile(&self) -> Result<(), github_app::GitHubError> {
        let manifests: Vec<DeploymentManifest> = self.gitops.load_all_manifests()?;
        
        Ok(())
    }
}

API Reference

GitHubAppConfig

Configuration for GitHub App integration.

Methods:

  • new(...) - Create new configuration
  • validate() - Validate all required fields are present and valid

GitHubTokenProvider

Manages GitHub App authentication and installation tokens.

Methods:

  • new(config: GitHubAppConfig) - Create new token provider
  • async get_token() -> Result<String, GitHubError> - Get valid installation token (cached)

WebhookVerifier

Verifies and parses GitHub webhook events.

Methods:

  • new(config: &GitHubAppConfig) - Create new verifier
  • verify_signature(signature: &str, body: &[u8]) -> Result<(), GitHubError> - Verify HMAC signature
  • parse_event(event_type: &str, signature: &str, body: &[u8]) -> Result<WebhookEvent, GitHubError> - Verify and parse event

GitHubGitOps

Git operations for manifest management.

Methods:

  • new(config: GitHubAppConfig, token_provider: GitHubTokenProvider) - Create new GitOps manager
  • async initialize() -> Result<(), GitHubError> - Clone repository if not exists
  • async sync() -> Result<(), GitHubError> - Fetch and fast-forward to latest commit
  • load_all_manifests<T: DeserializeOwned>() -> Result<Vec<T>, GitHubError> - Load and parse all manifests
  • repo_path() -> &PathBuf - Get local repository path

Error Handling

All public APIs return Result<T, GitHubError> with variants for:

  • Http - HTTP/network errors
  • Jwt - JWT creation errors
  • Git - Git operation errors
  • Io - File system errors
  • Yaml - YAML parsing errors
  • Json - JSON parsing errors
  • InvalidSignature - Webhook signature verification failed
  • UnknownEventType - Unknown webhook event type
  • Config - Configuration validation errors
  • TokenExpired - Token expired or unavailable
  • Pattern - Glob pattern errors
  • Other - Other errors

Testing

Run tests with:

cargo test

License

MIT