import exclusions from "./data/exclude.json" assert { type: "json" }; import * as fs from "fs"; import { exec } from "child_process"; import { parseString } from "xml2js"; import { SVNList, ExternalComponent, SVNProperties, DirType, InternalComponent, } from "./types.js"; import { parseExternals } from "./parser.js"; function execShellCommand(cmd: string) { return new Promise((resolve) => { exec(cmd, { maxBuffer: 1024 * 500 }, (error, stdout, stderr) => { if (error) { console.warn(error); } resolve(stdout || stderr); }); }); } // eslint-disable-next-line @typescript-eslint/no-explicit-any async function svnCmdWithResponse( cmd: string, baseUrl: string, url: string, returnRaw = false // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise { return new Promise((resolve, reject) => { const execCommand = `svn ${cmd} "${baseUrl}${url}" --xml`; execShellCommand(execCommand) .then((svnCmdResponse) => { parseString( svnCmdResponse as SVNList | SVNProperties, (err, result) => { if (err) { reject(err); return; } if (returnRaw) { resolve(result); } else { const json = JSON.stringify(result); const svnCmdResponseJson = JSON.parse(json); resolve(svnCmdResponseJson); } } ); }) .catch((error) => { reject(error); }); }); } export async function svnList( baseUrl: string, url: string ): Promise { try { const svnListResponse: InternalComponent[] = await svnCmdWithResponse( "list", baseUrl, url, false ); type arrayOfSVNList = { name: string[]; }; const internals = svnListResponse.lists.list[0].entry.map( ({ name }: arrayOfSVNList) => `${name[0]}` ); const excludeJson = fs.readFileSync("./dist/data/exclude.json", "utf-8"); const exclude = JSON.parse(excludeJson); // const parsedExclusions = exclusions; const filteredInternals = internals.filter( (internal: string) => !exclusions.includes(internal) ); // const internals = listResponse.lists.list[0].entry; return filteredInternals; } catch (error) { console.log(error); return []; } } async function svnCmdWithoutResponse( cmd: string, localPath: string // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise { return new Promise((resolve, reject) => { const execCommand = `svn ${cmd} "${localPath}"`; execShellCommand(execCommand) .then((svnCmdResponse) => { console.log(`Performed ${execCommand}`); resolve(svnCmdResponse); }) .catch((error) => { reject(error); }); }); } export async function svnUpdate(localPath: string): Promise { try { await svnCmdWithoutResponse("cleanup", localPath); } catch (error) { console.log(error); } try { await svnCmdWithoutResponse("update", localPath); } catch (error) { console.log(error); } } export async function svnPropGet( property: string, baseUrl: string, url: string ): Promise<[ExternalComponent]> { const propGetResponse = await svnCmdWithResponse( `propget ${property}`, baseUrl, url, true ); const rawExternals: string = propGetResponse.properties.target[0].property[0]._; return parseExternals(rawExternals); } export async function svnTagsBranchesList( dirType: DirType, baseUrl: string, url: string, // expects url that has tags and branches latestOnly = false ): Promise { try { url = url.replace(/trunk$/, ""); url = url.replace(/tags$/, ""); url = url.replace(/branches$/, ""); url = dirType === "tags" ? url.concat("/tags") : url.concat("/branches"); const svnListResponse: SVNList = await svnCmdWithResponse( "list", baseUrl, url ); const regex = /^[0-9.]+$/; // Regular expression for semantic version format type arrayOfSVNList = { name: string[]; }; if ( Object.prototype.hasOwnProperty.call( svnListResponse.lists.list[0], "entry" ) ) { // filter any tag or branch version which do not comply with the format 0.0.0 / 0.0.0.0 const filteredTags = svnListResponse.lists.list[0].entry.filter( (entry: arrayOfSVNList) => { return regex.test(entry.name[0]); } ); // only leave the name in the objects, get rid of the hyrachical structure let flattenedFilteredTags: string[] = filteredTags.map( ({ name }) => `${name[0]}` ); flattenedFilteredTags.sort((a, b) => { const partsA = a.split(".").map(Number); const partsB = b.split(".").map(Number); for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) { const partA = partsA[i] || 0; const partB = partsB[i] || 0; if (partA > partB) { return -1; } else if (partA < partB) { return 1; } } return 0; }); flattenedFilteredTags.sort((a, b) => b.localeCompare(a)); flattenedFilteredTags = flattenedFilteredTags.map( (element) => (element = `/${dirType}/${element}`) ); if (latestOnly) { return [flattenedFilteredTags[0]]; } else { return flattenedFilteredTags; } } } catch (error) { console.log(`${url} does not have a ${dirType}: ${error}`); return []; } } export async function svnGetLatestTag( baseUrl: string, url: string ): Promise { return svnTagsBranchesList(DirType.tags, baseUrl, url, true); } export async function svnGetLatestBranch( baseUrl: string, url: string ): Promise { return svnTagsBranchesList(DirType.branches, baseUrl, url, true); } export async function svnGetTagList( baseUrl: string, url: string ): Promise { return svnTagsBranchesList(DirType.tags, baseUrl, url, false); } export async function svnGetBranchesList( baseUrl: string, url: string ): Promise { return svnTagsBranchesList(DirType.branches, baseUrl, url, false); }