diff --git a/action.yml b/action.yml index e231a48..bcc7e65 100644 --- a/action.yml +++ b/action.yml @@ -16,7 +16,12 @@ inputs: names: description: >- Names of the packages. - required: true + required: false + + name-pattern: + description: >- + Names of the packages. + required: false semver-pattern: description: >- diff --git a/src/action.ts b/src/action.ts index 658acdb..80df3d8 100644 --- a/src/action.ts +++ b/src/action.ts @@ -8,6 +8,16 @@ export async function executeAction(input: Input, queryStrategy: QueryStrategy, warning("No package versions will be actually deleted") } + if (input.namePattern) { + info("Fetching package names") + + input.names = await queryStrategy.queryPackageNames(input) + + input.names.forEach((it) => { + info(`${it}`) + }) + } + info("Fetching packages") const packages = await queryStrategy.queryPackages(input) diff --git a/src/input.ts b/src/input.ts index a498010..e2900c7 100644 --- a/src/input.ts +++ b/src/input.ts @@ -5,7 +5,7 @@ import { Input, PackageType } from "./types" const DEFAULT_KEEP = 2 function getRegExpInput(name: string): RegExp | undefined { - const input = getInput("version-pattern") + const input = getInput(name) if (input !== "") { try { @@ -50,6 +50,7 @@ function getTypeInput(name: string): PackageType { export function getActionInput(): Input { return { names: getMultilineInput("names"), + namePattern: getRegExpInput("name-pattern"), versionPattern: getRegExpInput("version-pattern"), semverPattern: genSemVerInput("semver-pattern"), keep: Number(getInput("keep") || DEFAULT_KEEP), @@ -62,7 +63,11 @@ export function getActionInput(): Input { } export function validateInput(input: Input): Input { - if (input.names.length <= 0) { + if (input.namePattern && input.names && input.names.length > 0) { + throw new Error("Only one of name-pattern and names can be specified") + } + + if (!input.namePattern && (!input.names || input.names.length <= 0)) { throw new Error("names cannot be empty") } diff --git a/src/query/strategies/organization.query.strategy.ts b/src/query/strategies/organization.query.strategy.ts index 06c3a77..abc5cf4 100644 --- a/src/query/strategies/organization.query.strategy.ts +++ b/src/query/strategies/organization.query.strategy.ts @@ -6,6 +6,9 @@ export default class OrganizationQueryStrategy implements QueryStrategy { constructor(private readonly octokit: InstanceType) {} async queryPackages(input: RestInput): Promise { + if (!input.names) { + throw new Error("No names specified") + } return await Promise.all( input.names.map(async (name) => { const response = await this.queryPackage(input, name) @@ -15,6 +18,26 @@ export default class OrganizationQueryStrategy implements QueryStrategy { ) } + async queryPackageNames(input: RestInput) { + if (!input.namePattern) { + throw new Error("No name-pattern specified") + } + const namePattern = input.namePattern + try { + const params = { + package_type: input.type, + org: input.organization, + per_page: 100, + } + + const packages = await this.octokit.paginate(this.octokit.rest.packages.listPackagesForOrganization, params) + + return packages.map((p) => p.name).filter((n) => namePattern.test(n)) + } catch (error) { + throw new Error(`Failed to query package name pattern ${input.namePattern}: ${error}`) + } + } + private async queryPackage(input: RestInput, name: string) { try { const params = { diff --git a/src/query/strategies/user.query.strategy.ts b/src/query/strategies/user.query.strategy.ts index e8ee388..af325f7 100644 --- a/src/query/strategies/user.query.strategy.ts +++ b/src/query/strategies/user.query.strategy.ts @@ -6,6 +6,9 @@ export default class UserQueryStrategy implements QueryStrategy { constructor(private readonly octokit: InstanceType) {} async queryPackages(input: RestInput): Promise { + if (!input.names) { + throw new Error("No names specified") + } return await Promise.all( input.names.map(async (name) => { const response = await this.queryPackage(input, name) @@ -15,6 +18,26 @@ export default class UserQueryStrategy implements QueryStrategy { ) } + async queryPackageNames(input: RestInput) { + if (!input.namePattern) { + throw new Error("No name-pattern specified") + } + const namePattern = input.namePattern + try { + const params = { + package_type: input.type, + username: input.user, + per_page: 100, + } + + const packages = await this.octokit.paginate(this.octokit.rest.packages.listPackagesForUser, params) + + return packages.map((p) => p.name).filter((n) => namePattern.test(n)) + } catch (error) { + throw new Error(`Failed to query package name pattern ${input.namePattern}: ${error}`) + } + } + private async queryPackage(input: RestInput, name: string) { try { const params = { diff --git a/src/types.ts b/src/types.ts index 195ec8f..d8338e4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -10,7 +10,8 @@ export enum PackageType { } export type Input = { - names: string[] + names?: string[] + namePattern?: RegExp versionPattern?: RegExp semverPattern?: Range keep: number @@ -38,6 +39,7 @@ export type PackageVersion = { export interface QueryStrategy { queryPackages(input: Input): Promise + queryPackageNames(input: Input): Promise } export interface DeleteStrategy {