Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ability to ignore tags when deleting packages #104

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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

Expand All @@ -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 while 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)


Expand Down Expand Up @@ -405,6 +414,44 @@ This action deletes versions of a package from [GitHub Packages](https://github.
token: ${{ secrets.PAT }}
```

<br>

### 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)
Expand Down
87 changes: 86 additions & 1 deletion __tests__/delete.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions __tests__/version/get-version.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
18 changes: 13 additions & 5 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -95,7 +99,8 @@ const defaultParams = {
ignoreVersions: new RegExp(''),
deletePreReleaseVersions: '',
token: '',
deleteUntaggedVersions: ''
deleteUntaggedVersions: '',
includeTags: ''
};
class Input {
constructor(params) {
Expand All @@ -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 &&
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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() {
Expand Down
7 changes: 7 additions & 0 deletions src/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ export function finalIds(input: Input): Observable<string[]> {
*/
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)
}
Expand Down
7 changes: 6 additions & 1 deletion src/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface InputParams {
token?: string
deletePreReleaseVersions?: string
deleteUntaggedVersions?: string
includeTags?: string
}

const defaultParams = {
Expand All @@ -21,7 +22,8 @@ const defaultParams = {
ignoreVersions: new RegExp(''),
deletePreReleaseVersions: '',
token: '',
deleteUntaggedVersions: ''
deleteUntaggedVersions: '',
includeTags: ''
}

export class Input {
Expand All @@ -36,6 +38,7 @@ export class Input {
token: string
numDeleted: number
deleteUntaggedVersions: string
includeTags: string

constructor(params?: InputParams) {
const validatedParams: Required<InputParams> = {...defaultParams, ...params}
Expand All @@ -51,6 +54,7 @@ export class Input {
this.token = validatedParams.token
this.numDeleted = 0
this.deleteUntaggedVersions = validatedParams.deleteUntaggedVersions
this.includeTags = validatedParams.includeTags
}

hasOldestVersionQueryInfo(): boolean {
Expand All @@ -65,6 +69,7 @@ export class Input {
checkInput(): boolean {
if (this.packageType.toLowerCase() !== 'container') {
this.deleteUntaggedVersions = 'false'
this.includeTags = 'false'
}

if (
Expand Down
3 changes: 2 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})
}

Expand Down
9 changes: 6 additions & 3 deletions src/version/get-versions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface RestVersionInfo {
version: string
created_at: string
tagged: boolean
tags: string[]
}

export interface RestQueryInfo {
Expand Down Expand Up @@ -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,
Expand Down