From bf25fb8df311fdcbeac67ba2e5153495d76415a8 Mon Sep 17 00:00:00 2001 From: Donovan Date: Tue, 18 Apr 2023 11:37:08 +0200 Subject: [PATCH] Added ability to ignore tags when deleting packages --- README.md | 47 +++++++++++++++ __tests__/delete.test.ts | 87 ++++++++++++++++++++++++++- __tests__/version/get-version.test.ts | 2 + action.yml | 7 +++ dist/index.js | 18 ++++-- src/delete.ts | 7 +++ src/input.ts | 7 ++- src/main.ts | 3 +- src/version/get-versions.ts | 9 ++- 9 files changed, 176 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 6d6f76fb..ecef237a 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,11 @@ This action deletes versions of a package from [GitHub Packages](https://github. # Cannot be used with `num-old-versions-to-delete`. delete-only-untagged-versions: + # If true it will also ignore tags matching the regex provided in `ignore-versions` + # Defaults to false. + # Can only be used with `ignore-versions`. + ignore-versions-include-tags: + # The token used to authenticate with GitHub Packages. # Defaults to github.token. # Required if the repo running the workflow does not have access to delete the package. @@ -89,6 +94,8 @@ This action deletes versions of a package from [GitHub Packages](https://github. - `min-versions-to-keep` + `delete-only-pre-release-versions` - `delete-only-untagged-versions` - `min-versions-to-keep` + `delete-only-untagged-versions` + - `ignore-versions-include-tags` + `ignore-versions` + - `ignore-versions-include-tags` + `ignore-versions` + `min-versions-to-keep` # Scenarios @@ -106,6 +113,8 @@ This action deletes versions of a package from [GitHub Packages](https://github. - [Delete oldest version of a package](#delete-oldest-version-of-a-package) - [Delete a specific version of a package](#delete-a-specific-version-of-a-package) - [Delete multiple specific versions of a package](#delete-multiple-specific-versions-of-a-package) + - [Delete all white ignoring particular tags](#delete-all-while-ignoring-particular-tags) + - [Delete all except y latest versions while ignoring particular tags](#delete-all-except-y-latest-versions-while-ignoring-particular-tags) - [License](#license) @@ -405,6 +414,44 @@ This action deletes versions of a package from [GitHub Packages](https://github. token: ${{ secrets.PAT }} ``` +
+ + ### Delete all while ignoring particular tags + + To delete all while ignoring particular tags, the __package-name__, __ignore-versions-include-tags__ and __ignore-versions__ inputs are required. + + __Example__ + + Deletes all versions of a package excluding package containing the `important-tag` tag. + + ```yaml + - uses: actions/delete-package-version@v4 + with: + package-name: 'test-package' + package-type: 'container' + ignore-versions: '^important-tag$' + ignore-versions-include-tags: 'true' + token: ${{ secrets.PAT }} + ``` + + ### Delete all except y latest versions while ignoring particular tags + + To delete all except y latest versions while ignoring particular tags, the __package-name__, __min-versions-to-keep__, __ignore-versions-include-tags__ and __ignore-versions__ inputs are required. + + __Example__ + + Delete all except latest 3 package versions excluding package containing the `important-tag` tag. + + ```yaml + - uses: actions/delete-package-versions@v4 + with: + package-name: 'test-package' + package-type: 'container' + min-versions-to-keep: 3 + ignore-versions: '^important-tag$' + ignore-versions-include-tags: 'true' + ``` + # License The scripts and documentation in this project are released under the [MIT License](https://github.com/actions/delete-package-versions/blob/main/LICENSE) diff --git a/__tests__/delete.test.ts b/__tests__/delete.test.ts index 96dda11a..d9ca0133 100644 --- a/__tests__/delete.test.ts +++ b/__tests__/delete.test.ts @@ -341,6 +341,90 @@ describe('index tests -- call rest', () => { ) }) + it('finalIds test - no versions deleted if tags match ignore version and include tags is enabled', done => { + const numVersions = 10 + let apiCalled = 0 + + const versions = getMockedVersionsResponse( + numVersions, + 0, + 'container', + true + ) + + server.use( + rest.get( + 'https://api.github.com/users/test-owner/packages/container/test-package/versions', + (req, res, ctx) => { + apiCalled++ + return res(ctx.status(200), ctx.json(versions)) + } + ) + ) + + finalIds( + getInput({ + minVersionsToKeep: 2, + packageType: 'container', + ignoreVersions: RegExp('^(latest[1-3]{1})$'), + includeTags: 'true' + }) + ).subscribe(ids => { + expect(apiCalled).toBe(1) + expect(ids).toStrictEqual(['4', '5', '6', '7', '8']) + done() + }) + }) + + it('', done => { + const numVersions = 10 + let apiCalled = 0 + + let date = new Date() + date.setUTCFullYear(2000, 1, 1) + + const version = { + id: 1, + name: '1.0.0', + url: '', + created_at: date.toUTCString(), + package_html_url: '', + updated_at: '', + metadata: { + package_type: 'container', + container: { + tags: [ + 'test', + '9970186500fd471320e7340b256229209899bde5', + 'my-first-pr' + ] as string[] + } + } + } + + server.use( + rest.get( + 'https://api.github.com/users/test-owner/packages/container/test-package/versions', + (req, res, ctx) => { + apiCalled++ + return res(ctx.status(200), ctx.json([version])) + } + ) + ) + + finalIds( + getInput({ + packageType: 'container', + ignoreVersions: RegExp('^production|acceptance|test$'), + includeTags: 'true' + }) + ).subscribe(ids => { + expect(apiCalled).toBe(1) + expect(ids.length).toBe(0) + done() + }) + }) + it('deleteVersions test - missing token', done => { deleteVersions(getInput({token: ''})).subscribe({ error: err => { @@ -477,7 +561,8 @@ const defaultInput: InputParams = { numOldVersionsToDelete: RATE_LIMIT, minVersionsToKeep: -1, ignoreVersions: RegExp('^$'), - token: 'test-token' + token: 'test-token', + includeTags: 'false' } function getInput(params?: InputParams): Input { diff --git a/__tests__/version/get-version.test.ts b/__tests__/version/get-version.test.ts index 313a93dc..8df47eec 100644 --- a/__tests__/version/get-version.test.ts +++ b/__tests__/version/get-version.test.ts @@ -114,8 +114,10 @@ describe('get versions tests -- mock rest', () => { expect(result.versions[i].created_at).toBe(resp[i].created_at) if (i < numTaggedVersions) { expect(result.versions[i].tagged).toBe(true) + expect(result.versions[i].tags.length).toBe(1) } else { expect(result.versions[i].tagged).toBe(false) + expect(result.versions[i].tags.length).toBe(0) } } expect(result.paginate).toBe(true) diff --git a/action.yml b/action.yml index 789a96e9..fd6e0834 100644 --- a/action.yml +++ b/action.yml @@ -64,6 +64,13 @@ inputs: required: false default: "false" + ignore-versions-include-tags: + description: > + Next to the version also ignore tags matching the ignore-versions regex. + By default this is set to false + required: false + default: "false" + token: description: > Token with the necessary scopes to delete package versions. diff --git a/dist/index.js b/dist/index.js index a2729f70..5bc91720 100644 --- a/dist/index.js +++ b/dist/index.js @@ -41,6 +41,10 @@ function finalIds(input) { Then compute number of versions to delete (toDelete) based on the inputs. */ value = value.filter(info => !input.ignoreVersions.test(info.version)); + // Filter out tags that are to be ignored only when including tags is enabled + if (input.includeTags === 'true') { + value = value.filter(info => !info.tags.some(tag => input.ignoreVersions.test(tag))); + } if (input.deleteUntaggedVersions === 'true') { value = value.filter(info => !info.tagged); } @@ -95,7 +99,8 @@ const defaultParams = { ignoreVersions: new RegExp(''), deletePreReleaseVersions: '', token: '', - deleteUntaggedVersions: '' + deleteUntaggedVersions: '', + includeTags: '' }; class Input { constructor(params) { @@ -111,6 +116,7 @@ class Input { this.token = validatedParams.token; this.numDeleted = 0; this.deleteUntaggedVersions = validatedParams.deleteUntaggedVersions; + this.includeTags = validatedParams.includeTags; } hasOldestVersionQueryInfo() { return !!(this.owner && @@ -231,17 +237,18 @@ function getOldestVersions(owner, packageName, packageType, numVersions, page, t }), (0, operators_1.map)(response => { const resp = { versions: response.data.map((version) => { - let tagged = false; + let tags = []; if (package_type === 'container' && version.metadata && version.metadata.container) { - tagged = version.metadata.container.tags.length > 0; + tags = version.metadata.container.tags; } return { id: version.id, version: version.name, created_at: version.created_at, - tagged + tagged: tags.length > 0, + tags }; }), page, @@ -43941,7 +43948,8 @@ function getActionInput() { ignoreVersions: RegExp((0, core_1.getInput)('ignore-versions')), deletePreReleaseVersions: (0, core_1.getInput)('delete-only-pre-release-versions').toLowerCase(), token: (0, core_1.getInput)('token'), - deleteUntaggedVersions: (0, core_1.getInput)('delete-only-untagged-versions').toLowerCase() + deleteUntaggedVersions: (0, core_1.getInput)('delete-only-untagged-versions').toLowerCase(), + includeTags: (0, core_1.getInput)('ignore-versions-include-tags').toLowerCase() }); } function run() { diff --git a/src/delete.ts b/src/delete.ts index 4eb953fe..37c9e334 100644 --- a/src/delete.ts +++ b/src/delete.ts @@ -76,6 +76,13 @@ export function finalIds(input: Input): Observable { */ value = value.filter(info => !input.ignoreVersions.test(info.version)) + // Filter out tags that are to be ignored only when including tags is enabled + if (input.includeTags === 'true') { + value = value.filter( + info => !info.tags.some(tag => input.ignoreVersions.test(tag)) + ) + } + if (input.deleteUntaggedVersions === 'true') { value = value.filter(info => !info.tagged) } diff --git a/src/input.ts b/src/input.ts index d05e0690..ef671d48 100644 --- a/src/input.ts +++ b/src/input.ts @@ -9,6 +9,7 @@ export interface InputParams { token?: string deletePreReleaseVersions?: string deleteUntaggedVersions?: string + includeTags?: string } const defaultParams = { @@ -21,7 +22,8 @@ const defaultParams = { ignoreVersions: new RegExp(''), deletePreReleaseVersions: '', token: '', - deleteUntaggedVersions: '' + deleteUntaggedVersions: '', + includeTags: '' } export class Input { @@ -36,6 +38,7 @@ export class Input { token: string numDeleted: number deleteUntaggedVersions: string + includeTags: string constructor(params?: InputParams) { const validatedParams: Required = {...defaultParams, ...params} @@ -51,6 +54,7 @@ export class Input { this.token = validatedParams.token this.numDeleted = 0 this.deleteUntaggedVersions = validatedParams.deleteUntaggedVersions + this.includeTags = validatedParams.includeTags } hasOldestVersionQueryInfo(): boolean { @@ -65,6 +69,7 @@ export class Input { checkInput(): boolean { if (this.packageType.toLowerCase() !== 'container') { this.deleteUntaggedVersions = 'false' + this.includeTags = 'false' } if ( diff --git a/src/main.ts b/src/main.ts index 2e3df1b2..d948dd01 100644 --- a/src/main.ts +++ b/src/main.ts @@ -22,7 +22,8 @@ function getActionInput(): Input { token: getInput('token'), deleteUntaggedVersions: getInput( 'delete-only-untagged-versions' - ).toLowerCase() + ).toLowerCase(), + includeTags: getInput('ignore-versions-include-tags').toLowerCase() }) } diff --git a/src/version/get-versions.ts b/src/version/get-versions.ts index 342cad2b..5ee97f51 100644 --- a/src/version/get-versions.ts +++ b/src/version/get-versions.ts @@ -9,6 +9,7 @@ export interface RestVersionInfo { version: string created_at: string tagged: boolean + tags: string[] } export interface RestQueryInfo { @@ -57,20 +58,22 @@ export function getOldestVersions( map(response => { const resp = { versions: response.data.map((version: GetVersionsResponse[0]) => { - let tagged = false + let tags: string[] = [] + if ( package_type === 'container' && version.metadata && version.metadata.container ) { - tagged = version.metadata.container.tags.length > 0 + tags = version.metadata.container.tags } return { id: version.id, version: version.name, created_at: version.created_at, - tagged + tagged: tags.length > 0, + tags } }), page,