From dee5772e79a85bcf85a644d1b128604c15a95e4c Mon Sep 17 00:00:00 2001 From: meisZWFLZ Date: Tue, 17 Dec 2024 20:55:04 -0700 Subject: [PATCH] feat: :mute: params to mute warnings --- action.yml | 9 +++++ dist/index.js | 103 +++++++++++++++++++++++++++++++++++++++++++------- src/json.ts | 16 +++++++- src/main.ts | 9 ++++- src/types.ts | 9 +++++ src/update.ts | 13 +++++-- 6 files changed, 139 insertions(+), 20 deletions(-) diff --git a/action.yml b/action.yml index 6d36420..39105e8 100644 --- a/action.yml +++ b/action.yml @@ -66,6 +66,15 @@ inputs: description: 'The commit message that will be used when updating the depot' required: false + quiet: + description: 'When true, all warnings will be silenced. Errors should still be logged.' + required: false + default: false + + ignore-non-template-assets: + description: 'When true, silences warnings regarding assets that do not contain a template.pros file.' + required: false + default: false runs: using: node20 main: dist/index.js diff --git a/dist/index.js b/dist/index.js index d699246..5cffc2d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -35139,10 +35139,16 @@ const semver_1 = __importDefault(__nccwpck_require__(1383)); function validateTemplateDetails(details) { return (details !== null && typeof details === 'object' && + // for every key in the details object, check if it exists and is a string + // TODO: add a compile time check to ensure these are all the keys of TemplateDetails ['name', 'supported_kernels', 'target', 'version', 'url'].every(key => key in details && typeof details[key] === 'string')); } -async function retrieveTemplateDetails({ repo, owner, asset_id, asset_url }, client) { +/** Downloads asset from GitHub, unzips it, and then parses the `template.pros` file to get its {@linkcode TemplateDetails} */ +async function retrieveTemplateDetails(input, client) { + const { repo, owner, asset_id, asset_url } = input; + // Download the asset zip file + // NOTE: this could be done without github's api for more extensibility, but this works for now const rawAsset = await client.repos.getReleaseAsset({ repo, owner, @@ -35152,7 +35158,24 @@ async function retrieveTemplateDetails({ repo, owner, asset_id, asset_url }, cli const data = rawAsset.data; if (data instanceof ArrayBuffer) { const zip = new adm_zip_1.default(Buffer.from(data)); - const templateJson = JSON.parse(zip.readAsText('template.pros')); + const templateJsonEntry = zip.getEntry('template.pros'); + // If we can't find the template.pros file, then return an error + if (templateJsonEntry == null) { + const entries = zip.getEntries().map(entry => entry.entryName); + const errorBody = { ...input, zipContents: entries }; + // Checks if the asset is a project for debugging purposes + if (zip.getEntry('project.pros') == null) + return { + error: 'unknown asset type (no template.pros or project.pros file)', + body: errorBody + }; + return { + error: 'asset is a project, not a template (no template.pros file, but there is a project.pros file)', + body: errorBody + }; + } + // Parse the template.pros file + const templateJson = JSON.parse(zip.readAsText(templateJsonEntry)); const templateInfo = templateJson['py/state']; const details = { name: templateInfo.name, @@ -35161,11 +35184,25 @@ async function retrieveTemplateDetails({ repo, owner, asset_id, asset_url }, cli version: templateInfo.version, url: asset_url }; + // Ensure that the template details are valid if (validateTemplateDetails(details)) return details; + // If they aren't, return an error + else + return { + error: 'failed to validate template details', + body: { ...input, details } + }; } - return null; + return { + error: 'failed to download the asset zip file', + body: { ...input } + }; } +/** + * Converts a {@linkcode TemplateDetails} object into a {@linkcode DepotEntry} + * such that it can be parsed by the {@linkcode https://github.com/purduesigbots/pros-cli pros-cli} + */ function createDepotEntry({ name, supported_kernels, target, version, url }) { return { metadata: { @@ -35187,22 +35224,47 @@ function stringifyDepot(depot, readable) { * @param client The client to use for GitHub API requests. * @param readable Whether to format the JSON string for human readability. * @param unified Whether the beta and stable versions should be contained in a single depot. - * @returns + * @param quietWarnings prevents warnings regarding {@link retrieveTemplateDetails.Error} from being logged + * @param ignoreNonTemplates Prevents warnings about non templates being found in assets + * @returns A map of depot JSON strings, with keys 'stable' and 'beta'. If unified is true, only 'stable' is present. */ -async function createDepotJsonsFromGithub(repoId, client = new rest_1.Octokit(), readable = true, unified = false) { - const rawReleases = await client.repos.listReleases(repoId); - const templatePromises = rawReleases.data.map(release => retrieveTemplateDetails({ +async function createDepotJsonsFromGithub({ repoId, client = new rest_1.Octokit(), readable = true, unified = false, quietWarnings = false, ignoreNonTemplates = false }) { + // get releases from github + const rawReleases = (await client.repos.listReleases(repoId)).data; + // Get all of the assets of all the releases and attempt to retrieve their template details. + const templatePromises = rawReleases.flatMap(release => release.assets.map(asset => retrieveTemplateDetails({ ...repoId, - asset_id: release.assets[0].id, - asset_url: release.assets[0].browser_download_url - }, client)); - const templates = (await Promise.all(templatePromises)).filter((t) => t !== null); + asset_id: asset.id, + asset_url: asset.browser_download_url + }, client))); + // NOTE: What happens if there is a lot of assets (thousands)? + // Does NodeJS properly handle the cpu load, + // or do we need extra logic in order to prevent overloading the cpu? + // Wait until all the template details are retrieved and filter out any errors. + const templates = (await Promise.all(templatePromises)).filter((t) => { + if ('error' in t) { + // log errors + if (!quietWarnings && + ((t.error != + 'asset is a project, not a template (no template.pros file, but there is a project.pros file)' && + t.error != + 'unknown asset type (no template.pros or project.pros file)') || + !ignoreNonTemplates)) + console.warn(t); // TODO: implement logger + return false; + } + else + return true; + }); const depotEntries = templates.map(createDepotEntry); + // if we want all versions in a single depot, we can just return the depot with all entries if (unified) return { stable: stringifyDepot(depotEntries, readable) }; + // otherwise we need to separate the entries into stable and beta versions const stableEntries = []; const betaEntries = []; for (const entry of depotEntries) { + // if the version has a prerelease tag, then it is a beta version if (semver_1.default.parse(entry.version)?.prerelease.length ?? 0 > 0) { betaEntries.push(entry); } @@ -35300,6 +35362,8 @@ async function run() { const repos = getRepositoryIdentifiers(); const routes = getDepotLocations(); const readableFlag = core.getInput('readable-json') === 'true'; + const quietWarningsFlag = core.getInput('quiet') === 'true'; + const ignoreNonTemplateAssetsFlag = core.getInput('ignore-non-template-assets') === 'true'; const ghToken = core.getInput('token'); let message = core.getInput('message') || undefined; (0, update_1.updateDepots)({ @@ -35307,7 +35371,11 @@ async function run() { routes, readableJson: readableFlag, token: ghToken, - message + message, + logConfig: { + quietWarnings: quietWarningsFlag, + ignoreNonTemplateAssets: ignoreNonTemplateAssetsFlag + } }); } catch (error) { @@ -35480,12 +35548,19 @@ async function getOldFile(location, client) { throw e; } } -async function updateDepots({ srcRepo, destRepo, token, routes, readableJson, message: messageInput }) { +async function updateDepots({ srcRepo, destRepo, token, routes, readableJson, message: messageInput, logConfig: { ignoreNonTemplateAssets, quietWarnings } }) { const client = new rest_1.Octokit({ auth: token }); const unified = routes.beta.branch === routes.stable.branch && routes.beta.path === routes.stable.path; // create depot jsons - const jsons = await (0, json_1.createDepotJsonsFromGithub)(srcRepo, client, readableJson, unified); + const jsons = await (0, json_1.createDepotJsonsFromGithub)({ + repoId: srcRepo, + client, + readable: readableJson, + unified, + quietWarnings: quietWarnings, + ignoreNonTemplates: ignoreNonTemplateAssets + }); // remove beta depot if it's route has an undefined part const depots = filterDepots(destRepo, routes, jsons); for (const depot of depots) { diff --git a/src/json.ts b/src/json.ts index 88c1561..1c40d02 100644 --- a/src/json.ts +++ b/src/json.ts @@ -147,6 +147,7 @@ function stringifyDepot(depot: Depot, readable: boolean): string { * @param readable Whether to format the JSON string for human readability. * @param unified Whether the beta and stable versions should be contained in a single depot. * @param quietWarnings prevents warnings regarding {@link retrieveTemplateDetails.Error} from being logged + * @param ignoreNonTemplates Prevents warnings about non templates being found in assets * @returns A map of depot JSON strings, with keys 'stable' and 'beta'. If unified is true, only 'stable' is present. */ export async function createDepotJsonsFromGithub({ @@ -154,7 +155,8 @@ export async function createDepotJsonsFromGithub({ client = new Octokit(), readable = true, unified = false, - quietWarnings = false + quietWarnings = false, + ignoreNonTemplates = false }: { repoId: { owner: string @@ -164,6 +166,7 @@ export async function createDepotJsonsFromGithub({ readable?: boolean unified?: boolean quietWarnings?: boolean + ignoreNonTemplates?: boolean }): Promise { // get releases from github const rawReleases = (await client.repos.listReleases(repoId)).data @@ -192,7 +195,16 @@ export async function createDepotJsonsFromGithub({ (t): t is TemplateDetails => { if ('error' in t) { // log errors - if (quietWarnings !== true) console.warn(t) // TODO: implement logger + if ( + !quietWarnings && + ((t.error != + 'asset is a project, not a template (no template.pros file, but there is a project.pros file)' && + t.error != + 'unknown asset type (no template.pros or project.pros file)') || + !ignoreNonTemplates) + ) + console.warn(t) // TODO: implement logger + return false } else return true } diff --git a/src/main.ts b/src/main.ts index 92a5de1..919c1ff 100644 --- a/src/main.ts +++ b/src/main.ts @@ -56,6 +56,9 @@ export async function run(): Promise { const routes = getDepotLocations() const readableFlag = core.getInput('readable-json') === 'true' + const quietWarningsFlag = core.getInput('quiet') === 'true' + const ignoreNonTemplateAssetsFlag = + core.getInput('ignore-non-template-assets') === 'true' const ghToken = core.getInput('token') let message: string | undefined = core.getInput('message') || undefined updateDepots({ @@ -63,7 +66,11 @@ export async function run(): Promise { routes, readableJson: readableFlag, token: ghToken, - message + message, + logConfig: { + quietWarnings: quietWarningsFlag, + ignoreNonTemplateAssets: ignoreNonTemplateAssetsFlag + } }) } catch (error) { // Fail the workflow run if an error occurs diff --git a/src/types.ts b/src/types.ts index a7a9fa7..3a8c0d2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -31,6 +31,14 @@ export type DepotRouteMap = Record, DepotRoute> & export type DepotJsonMap = Record<'stable', string> & Partial> +/** Describes what should and shouldn't be logged */ +export interface LogConfig { + /** Silences all warnings */ + quietWarnings: boolean + /** Prevents logging of warnings for assets that are not templates */ + ignoreNonTemplateAssets: boolean +} + export interface Inputs { /** repo from which to parse releases */ srcRepo: RepositoryIdentifier @@ -40,4 +48,5 @@ export interface Inputs { routes: DepotRouteMap readableJson: boolean message?: string + logConfig: LogConfig } diff --git a/src/update.ts b/src/update.ts index da3a2e3..e8726ec 100644 --- a/src/update.ts +++ b/src/update.ts @@ -89,7 +89,8 @@ export async function updateDepots({ token, routes, readableJson, - message: messageInput + message: messageInput, + logConfig: { ignoreNonTemplateAssets, quietWarnings } }: Inputs) { const client = new Octokit({ auth: token }) @@ -97,8 +98,14 @@ export async function updateDepots({ routes.beta.branch === routes.stable.branch && routes.beta.path === routes.stable.path // create depot jsons - const jsons = await createDepotJsonsFromGithub( - { repoId: srcRepo, client, readable: readableJson, unified } ) + const jsons = await createDepotJsonsFromGithub({ + repoId: srcRepo, + client, + readable: readableJson, + unified, + quietWarnings: quietWarnings, + ignoreNonTemplates: ignoreNonTemplateAssets + }) // remove beta depot if it's route has an undefined part const depots = filterDepots(destRepo, routes, jsons)