Files
blackroad-tools/branch-cleanup/ghql.ts
Alexa Louise 79ff3dbb74 Initial extraction from blackroad-prism-console
BlackRoad Tools - ERP, CRM, manifest profiler, and DevOps utilities:

Core Tools:
- manifest_profile.py (937 lines) - Kubernetes manifest analysis
- pully.py (735 lines) - Pull request automation
- build_cluster_manifests.py (515 lines) - K8s cluster builders
- erp.py (478 lines) - Enterprise resource planning
- crm.py (405 lines) - Customer relationship management
- build_metaverse_roster.py (331 lines) - Agent roster management
- storage.py (305 lines) - Storage abstractions
- agent_test_pipeline.py (304 lines) - Test automation
- holo_cli.py (259 lines) - Holographic display CLI

DevOps Scripts:
- orin_bootstrap.sh - Jetson Orin setup
- install_self_heal_pack.sh - Self-healing infrastructure
- deploy_openwebui.sh - OpenWebUI deployment
- triton_setup.sh - NVIDIA Triton setup

Subdirectories:
- branch-cleanup/ - Git branch management
- kpis/ - KPI dashboards
- metrics/ - Metrics collection
- miners/ - Data mining tools
- pulse/ - Health monitoring
- tools-adapter/ - Integration adapters

11,351 lines of Python across 116 code files.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-30 08:39:17 -06:00

132 lines
3.4 KiB
TypeScript

import { Octokit } from '@octokit/core';
import { paginateGraphQL } from '@octokit/plugin-paginate-graphql';
const OctokitWithPlugins = Octokit.plugin(paginateGraphQL);
export interface MergedPR {
number: number;
mergedAt: string;
headRefName: string;
headRefOid: string;
baseRefName: string;
}
export interface BranchInfo {
name: string;
protected: boolean;
exists: boolean;
}
export interface CompareResult {
status: 'ahead' | 'behind' | 'identical' | 'diverged';
ahead_by: number;
behind_by: number;
}
export class GitHubQLClient {
private octokit: InstanceType<typeof OctokitWithPlugins>;
constructor(token: string) {
this.octokit = new OctokitWithPlugins({ auth: token });
}
async getDefaultBranch(owner: string, repo: string): Promise<string> {
const { data } = await this.octokit.request('GET /repos/{owner}/{repo}', {
owner,
repo,
});
return data.default_branch;
}
async getMergedPRs(owner: string, repo: string): Promise<MergedPR[]> {
const query = `
query($owner: String!, $repo: String!, $cursor: String) {
repository(owner: $owner, name: $repo) {
pullRequests(first: 100, after: $cursor, states: MERGED, orderBy: {field: UPDATED_AT, direction: DESC}) {
pageInfo {
hasNextPage
endCursor
}
nodes {
number
mergedAt
headRefName
headRefOid
baseRefName
}
}
}
}
`;
const results: MergedPR[] = [];
const iterator = this.octokit.graphql.paginate.iterator(query, {
owner,
repo,
});
for await (const response of iterator) {
const prs = response.repository.pullRequests.nodes;
results.push(...prs);
}
return results;
}
async checkBranch(owner: string, repo: string, branch: string): Promise<BranchInfo> {
try {
const { data } = await this.octokit.request('GET /repos/{owner}/{repo}/branches/{branch}', {
owner,
repo,
branch,
});
return {
name: branch,
protected: data.protected,
exists: true,
};
} catch (error: any) {
if (error.status === 404) {
return { name: branch, protected: false, exists: false };
}
throw error;
}
}
async compareCommits(owner: string, repo: string, base: string, head: string): Promise<CompareResult> {
try {
const { data } = await this.octokit.request('GET /repos/{owner}/{repo}/compare/{basehead}', {
owner,
repo,
basehead: `${base}...${head}`,
});
return {
status: data.status as CompareResult['status'],
ahead_by: data.ahead_by,
behind_by: data.behind_by,
};
} catch (error: any) {
if (error.status === 404) {
// Branch might be deleted already
return { status: 'diverged', ahead_by: 0, behind_by: 0 };
}
throw error;
}
}
async isBranchReachable(owner: string, repo: string, base: string, head: string): Promise<boolean> {
const result = await this.compareCommits(owner, repo, base, head);
// Safe to delete if:
// - behind (head is behind base, so fully contained)
// - identical (head === base)
// NOT safe if ahead (has commits not in base)
return result.status === 'behind' || result.status === 'identical';
}
getOctokit() {
return this.octokit;
}
}