feat: initial prototype attempt
Node.js CI / CI (push) Failing after 7s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 50s

This commit is contained in:
2026-02-03 17:13:57 -08:00
parent 729bd4b472
commit 5bc2cfbe43
26 changed files with 7982 additions and 19 deletions
+147
View File
@@ -0,0 +1,147 @@
/**
* @copyright NHCarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import axios, { isAxiosError, type AxiosInstance } from "axios";
import { config } from "../config.js";
import type {
GiteaFile,
GiteaPullRequest,
GiteaRepository,
} from "../types/gitea.types.js";
interface CreatePullRequestOptions {
base: string;
body: string;
head: string;
owner: string;
repo: string;
title: string;
}
interface GetFileContentOptions {
owner: string;
path: string;
reference?: string;
repo: string;
}
/**
* Service for interacting with the Gitea API.
*/
class GiteaService {
private readonly client: AxiosInstance;
/**
* Creates a new GiteaService instance.
* @throws Error if GITEA_TOKEN environment variable is not set.
*/
public constructor() {
const token = process.env.GITEA_TOKEN;
if (token === undefined || token === "") {
throw new Error("GITEA_TOKEN environment variable is required");
}
this.client = axios.create({
baseURL: `${config.giteaUrl}/api/v1`,
/* eslint-disable @typescript-eslint/naming-convention -- HTTP headers use PascalCase by convention */
headers: {
"Authorization": `token ${token}`,
"Content-Type": "application/json",
},
/* eslint-enable @typescript-eslint/naming-convention -- End HTTP headers */
});
}
/**
* Creates a new pull request in a repository.
* @param options - The PR creation options.
* @returns The created pull request.
*/
public async createPullRequest(
options: CreatePullRequestOptions,
): Promise<GiteaPullRequest> {
const { base, body, head, owner, repo, title } = options;
const { data } = await this.client.post<GiteaPullRequest>(
`/repos/${owner}/${repo}/pulls`,
{ base, body, head, title },
);
return data;
}
/**
* Gets the content of a file in a repository.
* @param options - Configuration specifying the file path and repository.
* @returns The file content or null if not found.
*/
public async getFileContent(
options: GetFileContentOptions,
): Promise<GiteaFile | null> {
const { owner, path, reference, repo } = options;
try {
const { data } = await this.client.get<GiteaFile>(
`/repos/${owner}/${repo}/contents/${path}`,
{ params: { ref: reference } },
);
return data;
} catch (error) {
if (isAxiosError(error) && error.response?.status === 404) {
return null;
}
throw error;
}
}
/**
* Lists all repositories in the configured organisation.
* @returns Array of non-archived, non-disabled, non-mirror repositories.
*/
public async listOrgRepositories(): Promise<Array<GiteaRepository>> {
const repositories: Array<GiteaRepository> = [];
let page = 1;
const limit = 100;
let hasMore = true;
while (hasMore) {
// eslint-disable-next-line no-await-in-loop -- Sequential pagination is required here
const { data } = await this.client.get<Array<GiteaRepository>>(
`/orgs/${config.giteaOrg}/repos`,
{ params: { limit, page } },
);
if (data.length === 0) {
hasMore = false;
} else {
repositories.push(...data);
page = page + 1;
}
}
return repositories.filter((repo) => {
return !repo.archived && !repo.disabled && !repo.mirror;
});
}
/**
* Lists pull requests in a repository.
* @param owner - The repository owner.
* @param repo - The repository name.
* @param state - The PR state filter.
* @returns Array of pull requests.
*/
public async listPullRequests(
owner: string,
repo: string,
state: "all" | "closed" | "open" = "open",
): Promise<Array<GiteaPullRequest>> {
const { data } = await this.client.get<Array<GiteaPullRequest>>(
`/repos/${owner}/${repo}/pulls`,
{ params: { state } },
);
return data;
}
}
export { GiteaService };