Skip to content

Commit

Permalink
feat(backend): save globalTriggers to db; update it;
Browse files Browse the repository at this point in the history
  • Loading branch information
dr460nf1r3 committed Nov 3, 2024
1 parent c5f6772 commit 7434a9a
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 40 deletions.
2 changes: 1 addition & 1 deletion backend/src/config/repo-manager.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { registerAs } from "@nestjs/config";

export default registerAs("repoMan", () => ({
alwaysRebuild: process.env.REPOMANAGER_ALWAYS_REBUILD ?? "[]",
gitAuthor: process.env.GIT_AUTHOR ?? "Chaotic Temeraire",
gitEmail: process.env.GIT_EMAIL ?? "[email protected]",
gitUsername: process.env.GIT_USERNAME ?? "git",
gitlabToken: process.env.CAUR_GITLAB_TOKEN,
globalTriggers: process.env.REPOMANAGER_ALWAYS_REBUILD ?? "[]",
schedulerInterval: process.env.REPOMANAGER_SCHEDULE ?? "0 * * * *",
}));
2 changes: 1 addition & 1 deletion backend/src/interfaces/repo-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export interface RepoSettings {
gitEmail: string;
gitUsername: string;
gitlabToken: string;
alwaysRebuild: string[];
globalTriggers: string[];
}

export interface RepoUpdateRunParams {
Expand Down
32 changes: 32 additions & 0 deletions backend/src/repo-manager/repo-manager.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export class RepoManagerSettings {
}

const packageMutex = new Mutex();
const settingsMutex = new Mutex();

/**
* Check if a package exists in the database, if not create a new entry
Expand Down Expand Up @@ -74,3 +75,34 @@ export async function archPkgExists(
}
});
}

/**
* Check if a setting exists in the database, if not create a new entry
* @param key The key to check
* @param connection The repository connection
*/
export function repoSettingsExists(
key: string,
connection: Repository<RepoManagerSettings>,
): Promise<RepoManagerSettings> {
return settingsMutex.runExclusive(async () => {
try {
const settings: RepoManagerSettings[] = await connection.find({ where: { key } });
let settingExists: Partial<RepoManagerSettings> = settings.find((oneSetting) => {
return oneSetting.key === key;
});

if (settingExists === undefined) {
Logger.log(`Setting ${key} not found in database, creating new entry`, "RepoManagerEntity");
settingExists = await connection.save({
key,
value: "",
});
}

return settingExists as RepoManagerSettings;
} catch (err: unknown) {
Logger.error(err, "RepoManagerEntity");
}
});
}
85 changes: 47 additions & 38 deletions backend/src/repo-manager/repo-manager.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
RepoUpdateRunParams,
RepoWorkDir,
} from "../interfaces/repo-manager";
import { ArchlinuxPackage, archPkgExists, RepoManagerSettings } from "./repo-manager.entity";
import { ArchlinuxPackage, archPkgExists, RepoManagerSettings, repoSettingsExists } from "./repo-manager.entity";
import * as os from "node:os";
import { InjectRepository } from "@nestjs/typeorm";
import { IsNull, Not, Repository } from "typeorm";
Expand All @@ -29,10 +29,10 @@ import { exec } from "node:child_process";

@Injectable()
export class RepoManagerService {
private readonly repoManager: RepoManager;
initialized = false;
private repoManager: RepoManager;
private repos: Repo[];
private tasks: CronJob[] = [];
initialized = false;

constructor(
private configService: ConfigService,
Expand All @@ -47,8 +47,6 @@ export class RepoManagerService {
private packageRepository: Repository<Package>,
) {
Logger.log("Initializing RepoManager service...", "RepoManager");

this.repoManager = this.createRepoManager();
void this.init();
}

Expand All @@ -66,69 +64,80 @@ export class RepoManagerService {
),
);

const globalTriggers: string[] = JSON.parse(this.configService.get<string>("repoMan.globalTriggers"));
if (globalTriggers && globalTriggers.length > 0) {
const existingSettings = await repoSettingsExists("globalTriggers", this.settingsRepository);
if (existingSettings) {
const existing: string[] = JSON.parse(existingSettings.value);

for (const key of existing) {
if (!globalTriggers.includes(key)) {
globalTriggers.push(key);
}
}

await this.settingsRepository.update(
{ key: "alwaysRebuild" },
{ value: JSON.stringify(globalTriggers) },
);
} else {
await this.settingsRepository.save({ key: "globalTriggers", value: JSON.stringify(globalTriggers) });
}
}

this.repoManager = this.createRepoManager(globalTriggers);
this.initialized = true;
Logger.log(`RepoManager service initialized with ${this.repos.length} repos`, "RepoManager");
Logger.log(`Global triggers: ${globalTriggers.length}`, "RepoManager");
}

/**
* Update the Chaotic-AUR database versions with current information
* and set any non-existing packages to inactive.
*/
async updateChaoticVersions(): Promise<void> {
let repoManager: RepoManager;
if (!this.repoManager) {
repoManager = this.createRepoManager();
} else {
repoManager = this.repoManager;
}
this.repos = await this.repoRepository.find({ where: { isActive: true, dbPath: Not(IsNull()) } });
await repoManager.updateChaoticDatabaseVersions(this.repos);
await this.repoManager.updateChaoticDatabaseVersions(this.repos);
}

/**
* Run the RepoManager service, checking for new packages to bump.
*/
async run(): Promise<void> {
let repoManager: RepoManager;
if (!this.repoManager) {
repoManager = this.createRepoManager();
} else {
repoManager = this.repoManager;
}

if (repoManager.status === RepoStatus.ACTIVE) {
if (this.repoManager.status === RepoStatus.ACTIVE) {
Logger.warn("RepoManager is already active, skipping run", "RepoManager");
return;
}

Logger.log("Starting checking packages for needed rebuilds...", "RepoManager");
await this.repoManager.pullArchlinuxPackages();

if (!repoManager.changedArchPackages || repoManager.changedArchPackages.length === 0) {
if (!this.repoManager.changedArchPackages || this.repoManager.changedArchPackages.length === 0) {
Logger.log("No packages changed in Arch repos, skipping run", "RepoManager");
return;
}

const results: BumpResult[] = [];
for (const repo of this.repos) {
const result: BumpResult = await repoManager.startRun(repo);
const result: BumpResult = await this.repoManager.startRun(repo);
results.push(result);
}

this.summarizeChanges(results, repoManager);
this.summarizeChanges(results, this.repoManager);
}

/**
* Create a new RepoManager instance.
* @returns A new RepoManager instance
*/
createRepoManager(): RepoManager {
createRepoManager(globalTriggers?: string[]): RepoManager {
const repoSettings: RepoSettings = {
gitAuthor: this.configService.getOrThrow<string>("repoMan.gitAuthor"),
gitEmail: this.configService.getOrThrow<string>("repoMan.gitEmail"),
gitUsername: this.configService.getOrThrow<string>("repoMan.gitUsername"),
gitlabToken: this.configService.getOrThrow<string>("repoMan.gitlabToken"),
alwaysRebuild: JSON.parse(this.configService.getOrThrow<string>("repoMan.alwaysRebuild")),
globalTriggers:
globalTriggers ?? JSON.parse(this.configService.getOrThrow<string>("repoMan.globalTriggers")),
};

return new RepoManager(
Expand Down Expand Up @@ -170,19 +179,20 @@ export class RepoManagerService {
class RepoManager {
changedArchPackages?: ArchlinuxPackage[];
status: RepoStatus = RepoStatus.INACTIVE;
private readonly httpService: HttpService;

private readonly archlinuxRepos: string[] = ["core", "extra"];
private readonly archlinuxRepoUrl = (name: string) =>
`https://arch.mirror.constant.com/${name}/os/x86_64/${name}.db.tar.gz`;

private readonly dbConnections: {
archPkg: Repository<ArchlinuxPackage>;
packages: Repository<Package>;
repo: Repository<any>;
settings: Repository<RepoManagerSettings>;
};
private repoManagerSettings: RepoSettings;
private readonly httpService: HttpService;

private repoDirs: string[] = [];
private repoManagerSettings: RepoSettings;

constructor(
httpService: HttpService,
Expand All @@ -204,6 +214,7 @@ class RepoManager {
/**
* Clone a repository and check for packages that need to be rebuilt.
* @param repo The repository to clone
* @returns An object containing the bumped packages as a map and the repository name
*/
async startRun(repo: Repo): Promise<{ bumped: Map<string, string>; repo: string }> {
Logger.log(`Checking repo ${repo.name} for rebuild triggers...`, "RepoManager");
Expand All @@ -223,8 +234,6 @@ class RepoManager {
this.status = RepoStatus.INACTIVE;
return { repo: repo.name, bumped: undefined };
}

// Now is a good time to check for updated Chaotic-AUR versions. To be triggered with each build success soon
const bumpedPackages: Map<string, string> = await this.bumpPackages(needsRebuild, repoDir);

Logger.log(`Pushing changes to ${repo.name}`, "RepoManager");
Expand Down Expand Up @@ -266,7 +275,7 @@ class RepoManager {

if (archRebuildPkg.length === 0) {
// If no trigger was found in explicit triggers, let's check for global triggers
for (const globalTrigger of this.repoManagerSettings.alwaysRebuild) {
for (const globalTrigger of this.repoManagerSettings.globalTriggers) {
if (metadata?.deps?.includes(globalTrigger)) {
dbObject = await this.dbConnections.archPkg.findOne({
where: { pkgname: globalTrigger },
Expand All @@ -286,7 +295,10 @@ class RepoManager {
dbObject = await this.dbConnections.archPkg.findOne({
where: { pkgname: archRebuildPkg[0].pkgname },
});
Logger.debug(`Rebuilding ${pkgbaseDir} because of explicit trigger ${dbObject.pkgname}`, "RepoManager");
Logger.debug(
`Rebuilding ${pkgbaseDir} because of explicit trigger ${dbObject.pkgname}`,
"RepoManager",
);
needsRebuild.push({
archPkg: dbObject,
configs: pkgConfig.configs,
Expand All @@ -306,10 +318,7 @@ class RepoManager {
* @param repoDir The directory of the repository
* @returns A map of the packages that were bumped
*/
async bumpPackages(
needsRebuild: RepoUpdateRunParams[],
repoDir: string,
): Promise<Map<string, string>> {
async bumpPackages(needsRebuild: RepoUpdateRunParams[], repoDir: string): Promise<Map<string, string>> {
const alreadyBumped = new Map<string, string>();

Logger.debug(needsRebuild, "RepoManager");
Expand Down Expand Up @@ -703,7 +712,7 @@ class RepoManager {
pkg: Package;
}[],
repo: Repo,
) {
): Promise<void> {
Logger.log("Committing changes and pushing back to repo...", "RepoManager");
for (const param of needsRebuild) {
try {
Expand Down Expand Up @@ -801,7 +810,7 @@ class RepoManager {
* @private
*/
private async createRepoDir(repo: Repo): Promise<string> {
const repoDir = fs.mkdtempSync(path.join(os.tmpdir(), repo.name));
const repoDir: string = fs.mkdtempSync(path.join(os.tmpdir(), repo.name));
try {
if (fs.existsSync(repoDir)) {
if (!isValidUrl(repo.repoUrl)) {
Expand Down

0 comments on commit 7434a9a

Please sign in to comment.