117 Commits

Author SHA1 Message Date
Anupam
0d39a63126 Add constraint on delete-only-untagged-versions 2023-03-09 15:37:07 +05:30
Anupam
8c3a64de32 Improve delete-only-untagged-versions flag and minor bug fix (#97)
Improve `delete-only-untagged-versions` flag and minor bug fix
2023-03-09 15:31:35 +05:30
Anupam
564c9d0297 Not currently supported in GHES (#95) 2023-03-06 13:11:48 +05:30
Anupam
48cd5a6793 Nit: Change dotcom to GitHub.com in README 2023-03-03 17:02:20 +05:30
Anupam
029d95066b add delete-only-untagged-versions param (#94)
Add a new boolean parameter to action `delete-only-untagged-versions`. When true, only versions that do not have any tags will be deleted.
2023-03-03 16:42:34 +05:30
Anupam
b9ed39f728 Configure baseUrl in octokit initialisation (#91)
Make the action work for GHES by passing `GITHUB_API_URL` environment variable to octokit initialisation.
2023-03-02 19:56:14 +05:30
Nishtha Gupta
ba4d22f251 Merge pull request #86 from actions/nishthaGupta-patch-1
Update version in readme
2023-01-05 17:20:20 +05:30
Nishtha Gupta
22f216e584 Update version in readme 2023-01-05 17:18:58 +05:30
Nishtha Gupta
9a08425079 Merge pull request #85 from actions/dependabot/npm_and_yarn/json5-1.0.2
Bump json5 from 1.0.1 to 1.0.2
2023-01-05 16:25:51 +05:30
dependabot[bot]
ac5977570e Bump json5 from 1.0.1 to 1.0.2
Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-05 10:40:51 +00:00
Anupam
fe2a95a0cb Merge pull request #83 from actions/s-anupam-rest-api
Add support for deleting packages on new GitHub Packages architecture
2023-01-05 16:10:25 +05:30
Anupam
46653ba3cb restrict to RATE_LIMIT with package-version-ids 2023-01-05 10:25:21 +00:00
Anupam
79269eb4bf Update README 2023-01-05 10:08:06 +00:00
Nishtha Gupta
4833470995 Update README.md 2023-01-05 14:05:04 +05:30
Anupam
2de4236b17 Address review coments 2023-01-03 16:35:40 +00:00
Anupam
23073eb4a2 add more UTs, delete ununsed files 2022-12-30 20:03:11 +00:00
Anupam
faf614822d Update test.yml 2022-12-30 19:35:17 +05:30
Anupam
bde81b096d Add packages permission to workflow 2022-12-30 19:30:08 +05:30
Anupam
f1783122af Update test.yml 2022-12-30 19:15:21 +05:30
Nishtha Gupta
404c7849e4 Update rest.dep.yml 2022-12-30 18:49:00 +05:30
Anupam
0b99e5f60d Fix prettier errors 2022-12-30 13:10:17 +00:00
Nishtha Gupta
40030c0eb0 Add mandatory package-type param 2022-12-30 18:36:05 +05:30
Anupam
5e7113f543 update dev dependencies 2022-12-30 12:55:09 +00:00
Anupam
90c8dc6d0b better UT for deletePackageVersions 2022-12-30 08:13:23 +00:00
Anupam
98c1e7eab7 add api UTs 2022-12-30 05:12:10 +00:00
Nishtha Gupta
eaa1f7c20f Update action.yml 2022-12-29 17:43:25 +05:30
Nishtha Gupta
ce1d329288 Update readme: 2022-12-29 15:59:15 +05:30
Nishtha Gupta
9dbafb83da Remove repo param support & logs 2022-12-29 15:51:31 +05:30
Nishtha Gupta
64305dab94 Optimisation 2022-12-29 15:35:31 +05:30
Nishtha Gupta
e95cd87648 Fetch all versions at once 2022-12-29 14:43:48 +05:30
Anupam
7f4037254e add some logs 2022-12-28 18:32:12 +00:00
Anupam
e521d9e753 get versions using rest 2022-12-28 14:43:50 +00:00
Anupam
cc7196aa1d try to fix npm run pack? 2022-12-28 14:39:47 +00:00
Nishtha Gupta
6ce7577c5e Adding logs 2022-12-28 16:56:22 +05:30
Nishtha Gupta
92bd8a3cd0 First commit with delete version API 2022-12-28 16:33:42 +05:30
Anupam
280935cb1a add package-type param to action.yml 2022-12-27 15:11:20 +00:00
Anupam
272ad859dc add package type param 2022-12-27 14:35:56 +00:00
Shyam Grover
c5e64443ea Merge pull request #79 from actions/dependabot/npm_and_yarn/actions/core-1.9.1
Bump @actions/core from 1.6.0 to 1.9.1
2022-11-01 11:53:52 +05:30
Shyam Grover
9b1e76ea45 Update core.dep.yml 2022-11-01 11:51:12 +05:30
Shyam Grover
37b7aca7eb Update core.dep.yml 2022-11-01 11:50:38 +05:30
Shyam Grover
f87b4a0b81 Update README.md 2022-11-01 11:48:29 +05:30
Shyam Grover
6961202f36 Merge pull request #70 from actions/dependabot/npm_and_yarn/minimist-1.2.6
Bump minimist from 1.2.5 to 1.2.6
2022-11-01 11:44:17 +05:30
Naveen Desu
4eab40be13 Merge pull request #82 from gaelgoth/fix_readme_typo
fix typo in readme
2022-09-15 12:02:36 +05:30
gael.gothuey
8701e46f41 fix typo in readme 2022-09-14 22:07:47 +02:00
dependabot[bot]
565ec2459a Bump @actions/core from 1.6.0 to 1.9.1
Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 1.6.0 to 1.9.1.
- [Release notes](https://github.com/actions/toolkit/releases)
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

---
updated-dependencies:
- dependency-name: "@actions/core"
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-18 19:22:50 +00:00
Tina Heidinger
ec4518c513 Merge pull request #75 from actions/74-add-support-for-deletion-of-container-and-npm-packages
Indicate missing support for GHCR and soon also npm in readme
2022-07-01 11:37:47 +02:00
tinaheidinger
33300d7916 Indicate missing support for GHCR and soon also npm in readme 2022-07-01 11:34:51 +02:00
dependabot[bot]
e048663cdf Bump minimist from 1.2.5 to 1.2.6
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-26 06:17:25 +00:00
Namrata Jha
0e86ee6891 Update README.md 2022-03-02 13:36:45 +05:30
Namrata Jha
a31a43afa6 Merge pull request #69 from actions/Upgrade-to-node16
Upgrade to node16
2022-03-02 13:28:44 +05:30
Namrata Jha
56849b848a Upgrade to node16 2022-03-02 07:40:19 +00:00
Namrata Jha
000223ef3e Merge pull request #68 from actions/v2-patch-release
change node version for v2 patch release
2022-03-02 12:32:41 +05:30
Namrata Jha
df897cea27 change node version for v2 patch release 2022-03-02 06:11:51 +00:00
Namrata Jha
b77daeff53 Merge pull request #67 from ralfstuckert/main
#66 Type mismatch on variable $packageVersionId and argument packageV…
2022-03-02 11:26:17 +05:30
Ralf Stuckert
7515f7a4f9 #66 Type mismatch on variable $packageVersionId and argument packageVersionId (String! / ID!) 2022-03-01 07:44:01 +01:00
Namrata Jha
3e14cd73d4 Merge pull request #62 from actions/thboop/updatetonode16
Update default runtime to node16
2022-02-16 14:45:08 +05:30
Namrata Jha
5e4787da1c Merge pull request #64 from actions/fix-dependabot-alerts
Fix dependabot alerts
2022-02-16 14:43:28 +05:30
Namrata Jha
355299fa7d fix licensed 2022-02-15 15:21:28 +00:00
Namrata Jha
5263253279 upgrade eslint and fix breaking changes 2022-02-15 13:52:11 +00:00
Namrata Jha
d6bf5f6a97 update node-fetch and ws 2022-02-15 07:10:56 +00:00
Namrata Jha
4afcc8f713 Merge pull request #63 from actions/dependabot/npm_and_yarn/ajv-6.12.6
Bump ajv from 6.10.2 to 6.12.6
2022-02-15 12:23:29 +05:30
dependabot[bot]
1472d90599 Bump ajv from 6.10.2 to 6.12.6
Bumps [ajv](https://github.com/ajv-validator/ajv) from 6.10.2 to 6.12.6.
- [Release notes](https://github.com/ajv-validator/ajv/releases)
- [Commits](https://github.com/ajv-validator/ajv/compare/v6.10.2...v6.12.6)

---
updated-dependencies:
- dependency-name: ajv
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-13 02:52:00 +00:00
Thomas Boop
09f7b84634 Update default runtime to node16
Update default runtime to node16
2022-02-07 13:58:41 -05:00
Namrata Jha
5aa567b6f7 Merge pull request #58 from actions/improve-existing-action
Allow deletion beyond last 100 packages
2021-12-24 15:55:58 +05:30
Namrata Jha
1979f24cbb Merge branch 'improve-existing-action' of https://github.com/actions/delete-package-versions into improve-existing-action 2021-12-24 10:21:12 +00:00
Namrata Jha
6a5e08c48d fix typo 2021-12-24 10:21:02 +00:00
Namrata Jha
f804442fe4 Update README.md 2021-12-24 14:10:14 +05:30
Namrata Jha
5282013b71 Update README.md 2021-12-24 12:35:55 +05:30
Namrata Jha
4d762a4bc3 Merge branch 'improve-existing-action' of https://github.com/actions/delete-package-versions into improve-existing-action 2021-12-23 18:53:34 +00:00
Namrata Jha
aac34a9f33 update tests 2021-12-23 18:53:16 +00:00
Namrata Jha
406ce0281d Update README.md 2021-12-24 00:20:21 +05:30
Namrata Jha
72cc4b27b0 update tests 2021-12-23 17:41:07 +00:00
Namrata Jha
7d188d1776 RATE_LIMIT = 99 2021-12-23 13:02:25 +00:00
Namrata Jha
81676df338 add comments 2021-12-23 13:01:35 +00:00
Namrata Jha
cfbae0ac37 remove excess logs 2021-12-23 09:46:46 +00:00
Namrata Jha
f556b07dce check error 2021-12-23 08:41:06 +00:00
Namrata Jha
8283350471 fix totalCount 2021-12-22 21:39:57 +00:00
Namrata Jha
1a7a0291b8 RATE_LIMIT=100 2021-12-22 19:21:20 +00:00
Namrata Jha
38d4003d57 Merge branch 'improve-existing-action' of https://github.com/actions/delete-package-versions into improve-existing-action 2021-12-22 19:18:26 +00:00
Namrata Jha
bf6c02443a run with RATE_LIMIT=100 2021-12-22 19:09:58 +00:00
Namrata Jha
e9faf3233c check rate limit 2021-12-22 16:17:44 +00:00
Namrata Jha
9ff1e31776 use promise for rate limit 2021-12-22 16:05:18 +00:00
Namrata Jha
0739cce1e8 use tap 2021-12-22 15:49:48 +00:00
Namrata Jha
ef5d9ac398 add logs 2021-12-22 15:45:39 +00:00
Namrata Jha
1369f0ca05 fix ratelimit query 2021-12-22 15:33:41 +00:00
Namrata Jha
372546727d add error handling 2021-12-22 15:30:35 +00:00
Namrata Jha
324039ba0a logs to check rate limit 2021-12-22 15:22:21 +00:00
Namrata Jha
508f6970a5 bypass rate limit 2021-12-22 09:59:18 +00:00
Namrata Jha
e6a9d9c802 set RATE_LIMIT=99 2021-12-21 18:52:10 +00:00
Namrata Jha
21a6c99c5d rate throttling at 100 2021-12-21 18:23:06 +00:00
Namrata Jha
db4d66086a test delete mutation 2021-12-21 18:14:06 +00:00
Namrata Jha
c8087bcc70 fix failed delete mutations 2021-12-21 17:34:02 +00:00
Namrata Jha
75db2288ca intermitent failed delete mutations logs 2021-12-21 16:19:51 +00:00
Namrata Jha
cbfc7d9ca3 Merge branch 'improve-existing-action' of https://github.com/actions/delete-package-versions into improve-existing-action 2021-12-21 09:29:52 +00:00
Namrata Jha
d846be5128 add unit tests 2021-12-21 09:29:12 +00:00
Namrata Jha
15db109d58 Merge branch 'main' into improve-existing-action 2021-12-20 16:19:59 +05:30
Namrata Jha
94d1202726 Update README.md 2021-12-20 16:17:20 +05:30
Namrata Jha
8e78cd925d Update README.md 2021-12-20 16:16:11 +05:30
Namrata Jha
c6b1d83a14 Update README.md 2021-12-20 15:55:01 +05:30
Namrata Jha
b7877514c6 Update README.md 2021-12-20 15:34:15 +05:30
Namrata Jha
370a350353 update ReadME 2021-12-20 10:00:12 +00:00
Namrata Jha
ba58e0e966 update ReadME 2021-12-20 09:45:54 +00:00
Namrata Jha
106b04e076 fix logs 2021-12-20 09:31:36 +00:00
Namrata Jha
04947a51f3 fix logs 2021-12-20 09:21:35 +00:00
Namrata Jha
7d96e1e541 fix delete rate throttling 2021-12-20 09:05:08 +00:00
Namrata Jha
5003595d8e fix delete rate throttling 2021-12-20 08:54:55 +00:00
Namrata Jha
263c5d855a logs to check rate limiting 2021-12-20 08:37:14 +00:00
Namrata Jha
36894fd813 add logs 2021-12-16 08:54:07 +00:00
Namrata Jha
a22ff2a7c5 add delete limit of 100 2021-12-16 07:02:21 +00:00
Namrata Jha
8c24449b56 fix delete all pre release versions 2021-12-14 16:15:58 +00:00
Namrata Jha
0994e6c0d2 fix delete count update condition 2021-12-14 16:06:05 +00:00
Namrata Jha
334f20f7fa fix checks 2021-12-14 15:57:21 +00:00
Namrata Jha
93d3863084 add paging 2021-12-14 15:51:31 +00:00
Namrata Jha
e7ecd94635 change default values 2021-12-14 12:39:22 +00:00
Namrata Jha
c253b9d27a Check input combinations 2021-12-14 12:34:03 +00:00
Namrata Jha
248e3651e3 Check input combinations 2021-12-14 12:22:48 +00:00
Namrata Jha
fd4e8f971f Update README.md 2021-11-25 11:25:32 +05:30
22 changed files with 24521 additions and 16437 deletions

View File

@@ -1,6 +1,6 @@
{
"plugins": ["jest", "@typescript-eslint"],
"extends": ["plugin:github/es6"],
"extends": ["plugin:github/recommended","plugin:@typescript-eslint/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 9,
@@ -17,13 +17,29 @@
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/array-type": "error",
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/ban-ts-ignore": "error",
"@typescript-eslint/ban-ts-comment": "error",
"camelcase": "off",
"@typescript-eslint/camelcase": "error",
"@typescript-eslint/class-name-casing": "error",
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "class",
"format": ["PascalCase"]
},
{
"selector": "interface",
"format": ["PascalCase"]
},
{
"selector": "typeProperty",
"format": [],
"custom": {
"regex": "^[A-Z][A-Za-z]*$",
"match": false
}
}
],
"@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}],
"@typescript-eslint/func-call-spacing": ["error", "never"],
"@typescript-eslint/generic-type-naming": ["error", "^[A-Z][A-Za-z]*$"],
"@typescript-eslint/no-array-constructor": "error",
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-explicit-any": "error",
@@ -33,7 +49,7 @@
"@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/no-object-literal-type-assertion": "error",
"@typescript-eslint/consistent-type-assertions": "error",
"@typescript-eslint/no-unnecessary-qualifier": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-useless-constructor": "error",
@@ -41,7 +57,7 @@
"@typescript-eslint/prefer-for-of": "warn",
"@typescript-eslint/prefer-function-type": "warn",
"@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-interface": "error",
"@typescript-eslint/consistent-type-definitions": "error",
"@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/promise-function-async": "error",
"@typescript-eslint/require-array-sort-compare": "error",
@@ -50,7 +66,10 @@
"@typescript-eslint/semi": ["error", "never"],
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/unbound-method": "error",
"no-console": "off"
"no-console": "off",
"import/no-unresolved": "off",
"sort-imports": "off",
"filenames/match-regex": "off"
},
"env": {
"node": true,

View File

@@ -24,10 +24,10 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Set Node.js 12.x
- name: Set Node.js 16.x
uses: actions/setup-node@v1
with:
node-version: 12.x
node-version: 16.x
- name: Install dependencies
run: npm ci

View File

@@ -7,6 +7,9 @@ on: # rebuild any PRs and main branch changes
- main
- 'releases/*'
permissions:
packages: write
jobs:
package: # make sure build/ci work properly
@@ -15,6 +18,9 @@ jobs:
steps:
- uses: actions/checkout@v2
name: Checkout Delete Package Versions Repo
- uses: actions/setup-node@v3
with:
node-version: 16
- run: npm install
name: NPM Install
- run: npm run pack
@@ -26,9 +32,12 @@ jobs:
steps:
- uses: actions/checkout@v2
name: Checkout Delete Package Versions Repo
- uses: actions/setup-node@v3
with:
node-version: 16
- uses: ./
name: Smoke Test Delete Package Versions Action
with:
package-name: 'com.github.actions.test-package'
package-name: 'action-ci-test-package'
package-type: 'npm'
num-old-versions-to-delete: 0
min-versions-to-keep: 0

View File

@@ -1,6 +1,6 @@
---
name: "@actions/core"
version: 1.6.0
version: 1.9.1
type: npm
summary: Actions core lib
homepage: https://github.com/actions/toolkit/tree/master/packages/core

View File

@@ -1,6 +1,6 @@
---
name: "@octokit/rest"
version: 16.43.1
version: 19.0.5
type: npm
summary: GitHub REST API client for Node.js
homepage: https://github.com/octokit/rest.js#readme

View File

@@ -1,6 +1,6 @@
---
name: rxjs
version: 6.5.4
version: 6.6.7
type: npm
summary: Reactive Extensions for modern JavaScript
homepage: https://github.com/ReactiveX/RxJS

253
README.md
View File

@@ -1,117 +1,142 @@
# Delete Package Versions
This action deletes versions of a package from [GitHub Packages](https://github.com/features/packages).
This action deletes versions of a package from [GitHub Packages](https://github.com/features/packages). This action will only delete a maximum of 100 versions in one run.
### What It Can Do
* Create a retention policy (delete all except n most recent pre-release versions)
* Delete all package versions except n most recent versions.
* Delete all package versions except n most recent versions
* Delete oldest version(s)
* Ignore version(s) from deletion through regex.
* Delete version(s) of a package that is hosted in the same repo that is executing the workflow
* Delete version(s) of a package that is hosted in a different repo than the one executing the workflow
* Ignore version(s) from deletion through regex
* Delete version(s) of a package that is hosted from a repo having access to package
* Delete version(s) of a package that is hosted from a repo not having access to package
* Delete a single version
* Delete multiple versions
* Delete specific version(s)
# Usage
```yaml
- uses: actions/delete-package-versions@v2
- uses: actions/delete-package-versions@v4
with:
# Can be a single package version id, or a comma separated list of package version ids.
# Defaults to an empty string.
package-version-ids:
# Owner of the repo hosting the package.
# Owner of the package.
# Defaults to the owner of the repo executing the workflow.
# Required if deleting a version from a package hosted in a different repo than the one executing the workflow.
# Required if deleting a version from a package hosted in a different org than the one executing the workflow.
owner:
# Repo hosting the package.
# Defaults to the repo executing the workflow.
# Required if deleting a version from a package hosted in a different repo than the one executing the workflow.
repo:
# Name of the package.
# Defaults to an empty string.
# Required if `package-version-ids` input is not given.
# Required
package-name:
# Type of the package. Can be one of container, maven, npm, nuget, or rubygems.
# Required
package-type:
# The number of old versions to delete starting from the oldest version.
# Defaults to 1.
# Cannot be more than 100.
num-old-versions-to-delete:
# The number of latest versions to not delete.
# Defaults to 0.
# When this is set greater than 0 it will delete all deletable package versions except the specified no.
# `num-old-versions-to-delete` will not be taken into account with this option.
# Cannot be more than 100.
# The number of latest versions to keep.
# This cannot be specified with `num-old-versions-to-delete`. By default, `min-versions-to-keep` takes precedence over `num-old-versions-to-delete`.
# When set to 0, all deletable versions will be deleted.
# When set greater than 0, all deletable package versions except the specified number will be deleted.
min-versions-to-keep:
# The package versions to exclude from deletion.
# Takes regex for the version name as input.
# By default nothing is ignored.
# By default nothing is ignored. This is ignored when `delete-only-pre-release-versions` is true
ignore-versions:
# If true it will delete only the pre-release versions.
# The number of pre-release versions to keep can be set by using `min-versions-to-keep` value with this.
# When `min-versions-to-keep` is 0, all pre-release versions get deleted.
# Defaults to false.
# Cannot be used with `num-old-versions-to-delete` and `ignore-versions`.
delete-only-pre-release-versions:
# If true it will delete only the untagged versions in case of container package.
# Does not work for other package types and will be ignored.
# The number of untagged versions to keep can be set by using `min-versions-to-keep` value with this.
# When `min-versions-to-keep` is 0, all untagged versions get deleted.
# Defaults to false.
# Cannot be used with `num-old-versions-to-delete`.
delete-only-untagged-versions:
# The token used to authenticate with GitHub Packages.
# Defaults to github.token.
# Required if deleting a version from a package hosted in a different repo than the one executing the workflow.
# Required if the repo running the workflow does not have access to delete the package.
# For rubygems and maven package, repo has access if package is hosted in the same repo as the workflow.
# For container, npm and nuget package, repo has access if assigned **Admin** role under Package Settings > Manage Actions Access.
# If `package-version-ids` is given the token only needs the delete packages scope.
# If `package-version-ids` is not given the token needs the delete packages scope and the read packages scope
token:
```
# Valid Input Combinations
`owner`, `package-name`, `package-type` and `token` can be used with the following combinations in a workflow -
- `num-old-versions-to-delete`
- `min-versions-to-keep`
- `delete-only-pre-release-versions`
- `ignore-versions`
- `num-old-versions-to-delete` + `ignore-versions`
- `min-versions-to-keep` + `ignore-versions`
- `min-versions-to-keep` + `delete-only-pre-release-versions`
- `delete-only-untagged-versions`
- `min-versions-to-keep` + `delete-only-untagged-versions`
# Scenarios
- [Delete all pre-release versions except y latest pre-release package versions](#delete-all-pre-release-versions-except-y-latest-pre-release-package-versions)
- [Delete all except y latest versions while ignoring particular package versions](#delete-all-except-y-latest-versions-while-ignoring-particular-package-versions)
- [Delete oldest x number of versions while ignoring particular package versions](#delete-oldest-x-number-of-versions-while-ignoring-particular-package-versions)
- [Delete all except y latest versions of a package](#delete-all-except-y-latest-versions-of-a-package)
- [Delete oldest x number of versions of a package](#delete-oldest-x-number-of-versions-of-a-package)
- [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 Package Versions](#delete-package-versions)
- [What It Can Do](#what-it-can-do)
- [Usage](#usage)
- [Valid Input Combinations](#valid-input-combinations)
- [Scenarios](#scenarios)
- [Delete all pre-release versions except y latest pre-release package versions](#delete-all-pre-release-versions-except-y-latest-pre-release-package-versions)
- [Delete all untagged container versions except y latest untagged versions](#delete-all-untagged-container-versions-except-y-latest-untagged-versions)
- [Delete all except y latest versions while ignoring particular package versions](#delete-all-except-y-latest-versions-while-ignoring-particular-package-versions)
- [Delete oldest x number of versions while ignoring particular package versions](#delete-oldest-x-number-of-versions-while-ignoring-particular-package-versions)
- [Delete all except y latest versions of a package](#delete-all-except-y-latest-versions-of-a-package)
- [Delete oldest x number of versions of a package](#delete-oldest-x-number-of-versions-of-a-package)
- [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)
- [License](#license)
### Delete all pre-release versions except y latest pre-release package versions
To delete all pre release versions except y latest pre-release package versions in the same repo as the workflow the __package-name__, __min-versions-to-keep__ and __delete-only-pre-release-versions__ inputs are required.
To delete all pre release versions except y latest pre-release package versions, the __package-name__, __min-versions-to-keep__ and __delete-only-pre-release-versions__ inputs are required.
__Example__
Delete all pre-release package versions except latest 10 in the same repo as the workflow
Delete all pre-release package versions except latest 10
```yaml
- uses: actions/delete-package-versions@v2
- uses: actions/delete-package-versions@v4
with:
package-name: 'test-package'
package-type: 'npm'
min-versions-to-keep: 10
delete-only-pre-release-versions: "true"
```
To delete all pre release versions except y latest pre-release package versions in a different repo than the workflow the __owner__, __repo__, __package-name__, __token__, __min-versions-to-keep__ and __delete-only-pre-release-versions__ inputs are required.
The [token][token] needs the delete packages and read packages scope. It is recommended [to store the token as a secret][secret]. In this example the [token][token] was stored as a secret named __GITHUB_PAT__.
To delete all pre release versions except y latest pre-release package versions from a repo not having access to package, the __owner__, __package-name__, __token__, __min-versions-to-keep__ and __delete-only-pre-release-versions__ inputs are required.
__Example__
Delete all pre-release package versions except latest 10 in a different repo than the workflow
Delete all pre-release package versions except latest 10 from a repo not having access to package
```yaml
- uses: actions/delete-package-versions@v2
- uses: actions/delete-package-versions@v4
with:
owner: 'github'
repo: 'packages'
package-name: 'test-package'
package-type: 'npm'
token: ${{ secrets.GITHUB_PAT }}
min-versions-to-keep: 10
delete-only-pre-release-versions: "true"
@@ -119,36 +144,56 @@ This action deletes versions of a package from [GitHub Packages](https://github.
<br>
### Delete all except y latest versions while ignoring particular package versions
### Delete all untagged container versions except y latest untagged versions
To delete all except y latest versions while ignoring particular package versions in the same repo as the workflow the __package-name__, __min-versions-to-keep__ and __ignore-versions__ inputs are required.
To delete all untagged versions of a container package except y latest untagged versions, the __package-name__, __package-type__, __min-versions-to-keep__ and __delete-only-untagged-versions__ inputs are required. __package-type__ must be container for this scenario.
__Example__
Delete all except latest 3 package versions excluding major versions as per semver in the same repo as the workflow
Delete all untagged versions except latest 10
```yaml
- uses: actions/delete-package-versions@v2
- uses: actions/delete-package-versions@v4
with:
package-name: 'test-packae'
package-name: 'test-package'
package-type: 'container'
min-versions-to-keep: 10
delete-only-untagged-versions: 'true'
```
<br>
### Delete all except y latest versions while ignoring particular package versions
To delete all except y latest versions while ignoring particular package versions, the __package-name__, __min-versions-to-keep__ and __ignore-versions__ inputs are required.
__Example__
Delete all except latest 3 package versions excluding major versions as per semver
```yaml
- uses: actions/delete-package-versions@v4
with:
package-name: 'test-package'
package-type: 'npm'
min-versions-to-keep: 3
ignore-versions: '^(0|[1-9]\\d*)\\.0\\.0$'
```
To delete all except y latest versions while ignoring particular package versions in a different repo than the workflow the __owner__, __repo__, __package-name__, __token__, __min-versions-to-keep__ and __ignore-versions__ inputs are required.
To delete all except y latest versions while ignoring particular package versions from a repo not having access to package, the __owner__, __package-name__, __token__, __min-versions-to-keep__ and __ignore-versions__ inputs are required.
The [token][token] needs the delete packages and read packages scope. It is recommended [to store the token as a secret][secret]. In this example the [token][token] was stored as a secret named __GITHUB_PAT__.
__Example__
Delete all except latest 3 package versions excluding major versions as per semver in a different repo than the workflow
Delete all except latest 3 package versions excluding major versions as per semver from a repo not having access to package
```yaml
- uses: actions/delete-package-versions@v2
- uses: actions/delete-package-versions@v4
with:
owner: 'github'
repo: 'packages'
package-name: 'test-packae'
package-name: 'test-package'
package-type: 'npm'
token: ${{ secrets.GITHUB_PAT }}
min-versions-to-keep: 3
ignore-versions: '^(0|[1-9]\\d*)\\.0\\.0$'
@@ -158,38 +203,37 @@ This action deletes versions of a package from [GitHub Packages](https://github.
### Delete oldest x number of versions while ignoring particular package versions
To delete oldest x number of versions while ignoring all the major package versions in the same repo as the workflow the __package-name__, __num-oldest-versions-to-delete__ and __ignore-versions__ inputs are required.
To delete oldest x number of versions while ignoring all the major package versions, the __package-name__, __num-oldest-versions-to-delete__ and __ignore-versions__ inputs are required.
There is a possibility if the oldest x number of versions contain ignored package versions, actual package versions to get deleted will be less than x.
__Example__
Delete 3 oldest versions excluding major versions as per semver is the same repo as the workflow
Delete 3 oldest versions excluding major versions as per semver
```yaml
- uses: actions/delete-package-versions@v2
- uses: actions/delete-package-versions@v4
with:
package-name: 'test-packae'
package-name: 'test-package'
package-type: 'npm'
num-old-versions-to-delete: 3
ignore-versions: '^(0|[1-9]\\d*)\\.0\\.0$'
```
To delete oldest x number of versions while ignoring all the major package versions in a different repo than the workflow the __owner__, __repo__, __package-name__, __token__, __num-oldest-versions-to-delete__ and __ignore-versions__ inputs are required.
To delete oldest x number of versions while ignoring all the major package versions from a repo not having access to package, the __owner__, __package-name__, __token__, __num-oldest-versions-to-delete__ and __ignore-versions__ inputs are required.
There is a possibility if the oldest x number of versions contain ignored package versions, actual package versions to get deleted will be less than x.
The [token][token] needs the delete packages and read packages scope. It is recommended [to store the token as a secret][secret]. In this example the [token][token] was stored as a secret named __GITHUB_PAT__.
__Example__
Delete 3 oldest versions excluding major versions as per semver is a differernt repo than the workflow
Delete 3 oldest versions excluding major versions as per semver from a repo not having access to package
```yaml
- uses: actions/delete-package-versions@v2
- uses: actions/delete-package-versions@v4
with:
owner: 'github'
repo: 'packages'
package-name: 'test-packae'
package-name: 'test-package'
package-type: 'npm'
token: ${{ secrets.PAT }}
num-old-versions-to-delete: 3
ignore-versions: '^(0|[1-9]\\d*)\\.0\\.0$'
@@ -199,33 +243,34 @@ This action deletes versions of a package from [GitHub Packages](https://github.
### Delete all except y latest versions of a package
To delete all except y latest versions of a package hosted in the same repo as the workflow the __package-name__ and __min-versions-to-keep__ inputs are required.
To delete all except y latest versions of a package hosted, the __package-name__ and __min-versions-to-keep__ inputs are required.
__Example__
Delete all except latest 2 versions of a package hosted in the same repo as the workflow
Delete all except latest 2 versions of a package hosted
```yaml
- uses: actions/delete-package-versions@v2
- uses: actions/delete-package-versions@v4
with:
package-name: 'test-package'
package-type: 'npm'
min-versions-to-keep: 2
```
To delete all except y latest versions of a package hosted in a repo other than the workflow the __owner__, __repo__, __package-name__, __token__ and __min-versions-to-keep__ inputs are required.
To delete all except y latest versions of a package hosted from a repo not having access to package, the __owner__, __package-name__, __token__ and __min-versions-to-keep__ inputs are required.
The [token][token] needs the delete packages and read packages scope. It is recommended [to store the token as a secret][secret]. In this example the [token][token] was stored as a secret named __GITHUB_PAT__.
__Example__
Delete all except latest 2 versions of a package hosted in a repo other than the workflow
Delete all except latest 2 versions of a package hosted from a repo not having access to package
```yaml
- uses: actions/delete-package-versions@v2
- uses: actions/delete-package-versions@v4
with:
owner: 'github'
repo: 'packages'
package-name: 'test-package'
package-type: 'npm'
token: ${{ secrets.PAT }}
min-versions-to-keep: 2
```
@@ -234,33 +279,34 @@ This action deletes versions of a package from [GitHub Packages](https://github.
### Delete oldest x number of versions of a package
To delete the oldest x number of versions of a package hosted in the same repo as the workflow the __package-name__, and __num-old-versions-to-delete__ inputs are required.
To delete the oldest x number of versions of a package hosted, the __package-name__, and __num-old-versions-to-delete__ inputs are required.
__Example__
Delete the oldest 3 version of a package hosted in the same repo as the workflow
Delete the oldest 3 version of a package hosted
```yaml
- uses: actions/delete-package-versions@v2
- uses: actions/delete-package-versions@v4
with:
package-name: 'test-package'
package-type: 'npm'
num-old-versions-to-delete: 3
```
To delete the oldest x number of versions of a package hosted in a different repo than the workflow the __owner__, __repo__, __package-name__, __token__ and __num-old-versions-to-delete__ inputs are required.
To delete the oldest x number of versions of a package hosted from a repo not having access to package, the __owner__, __package-name__, __token__ and __num-old-versions-to-delete__ inputs are required.
The [token][token] needs the delete packages and read packages scope. It is recommended [to store the token as a secret][secret]. In this example the [token][token] was stored as a secret named __GITHUB_PAT__.
__Example__
Delete the oldest 3 version of a package hosted in a different repo than the one executing the workflow
Delete the oldest 3 version of a package hosted from a repo not having access to package
```yaml
- uses: actions/delete-package-versions@v2
- uses: actions/delete-package-versions@v4
with:
owner: 'github'
repo: 'packages'
package-name: 'test-package'
package-type: 'npm'
num-old-versions-to-delete: 3
token: ${{ secrets.GITHUB_PAT }}
```
@@ -269,28 +315,27 @@ This action deletes versions of a package from [GitHub Packages](https://github.
### Delete oldest version of a package
To delete the oldest version of a package that is hosted in the same repo as the workflow the __package-name__ input is required.
To delete the oldest version of a package that is hosted, the __package-name__ input is required.
__Example__
```yaml
- uses: actions/delete-package-versions@v2
- uses: actions/delete-package-versions@v4
with:
package-name: 'test-package'
package-type: 'npm'
```
To delete the oldest version of a package that is hosted in a different repo than the workflow the __owner__, __repo__, __package-name__, __token__ inputs are required.
The [token][token] needs the delete packages and read packages scope. It is recommended [to store the token as a secret][secret]. In this example the [token][token] was stored as a secret named __GITHUB_PAT__.
To delete the oldest version of a package that is hosted from a repo not having access to package, the __owner__, __package-name__, __token__ inputs are required.
__Example__
```yaml
- uses: actions/delete-package-versions@v2
- uses: actions/delete-package-versions@v4
with:
owner: 'github'
repo: 'packages'
package-name: 'test-package'
package-type: 'npm'
token: ${{ secrets.PAT }}
```
@@ -298,30 +343,32 @@ This action deletes versions of a package from [GitHub Packages](https://github.
### Delete a specific version of a package
To delete a specific version of a package that is hosted in the same repo as the workflow the __package-version-ids__ input is required.
To delete a specific version of a package that is hosted, the __package-version-ids__ input is required.
Package version ids can be retrieved via the [GitHub GraphQL API][api]
Package version ids can be retrieved via the [GitHub REST API][api]
__Example__
```yaml
- uses: actions/delete-package-versions@v2
- uses: actions/delete-package-versions@v4
with:
package-version-ids: 'MDE0OlBhY2thZ2VWZXJzaW9uOTcyMDY3'
package-name: 'test-package'
package-type: 'npm'
```
To delete a specific version of a package that is hosted in a different repo than the workflow the __package-version-ids__ and __token__ inputs are required.
To delete a specific version of a package that is hosted from a repo not having access to package, the __package-version-ids__ and __token__ inputs are required.
The [token][token] needs the delete packages and read packages scope. It is recommended [to store the token as a secret][secret]. In this example the [token][token] was stored as a secret named __GITHUB_PAT__.
Package version ids can be retrieved via the [GitHub GraphQL API][api]
Package version ids can be retrieved via the [GitHub REST API][api]
__Example__
```yaml
- uses: actions/delete-package-versions@v2
- uses: actions/delete-package-versions@v4
with:
package-version-ids: 'MDE0OlBhY2thZ2VWZXJzaW9uOTcyMDY3'
package-name: 'test-package'
package-type: 'npm'
token: ${{ secrets.PAT }}
```
@@ -329,30 +376,32 @@ This action deletes versions of a package from [GitHub Packages](https://github.
### Delete multiple specific versions of a package
To delete multiple specifc versions of a package that is hosted in the same repo as the workflow the __package-version-ids__ input is required.
To delete multiple specific versions of a package that is hosted, the __package-version-ids__ input is required.
The __package-version-ids__ input should be a comma separated string of package version ids. Package version ids can be retrieved via the [GitHub GraphQL API][api].
The __package-version-ids__ input should be a comma separated string of package version ids. Package version ids can be retrieved via the [GitHub REST API][api].
__Example__
```yaml
- uses: actions/delete-package-versions@v2
- uses: actions/delete-package-versions@v4
with:
package-version-ids: 'MDE0OlBhY2thZ2VWZXJzaW9uOTcyMDY3, MDE0OlBhY2thZ2VWZXJzaW9uOTcyMzQ5, MDE0OlBhY2thZ2VWZXJzaW9uOTcyMzUw'
package-name: 'test-package'
package-type: 'npm'
```
To delete multiple specifc versions of a package that is hosted in a repo other than the workflow the __package-version-ids__, __token__ inputs are required.
To delete multiple specific versions of a package that is hosted from a repo not having access to package, the __package-version-ids__, __token__ inputs are required.
The __package-version-ids__ input should be a comma separated string of package version ids. Package version ids can be retrieved via the [GitHub GraphQL API][api].
The [token][token] needs the delete packages and read packages scope. It is recommended [to store the token as a secret][secret]. In this example the [token][token] was stored as a secret named __GITHUB_PAT__.
The __package-version-ids__ input should be a comma separated string of package version ids. Package version ids can be retrieved via the [GitHub REST API][api].
__Example__
```yaml
- uses: actions/delete-package-versions@v2
- uses: actions/delete-package-versions@v4
with:
package-version-ids: 'MDE0OlBhY2thZ2VWZXJzaW9uOTcyMDY3, MDE0OlBhY2thZ2VWZXJzaW9uOTcyMzQ5, MDE0OlBhY2thZ2VWZXJzaW9uOTcyMzUw'
package-name: 'test-package'
package-type: 'npm'
token: ${{ secrets.PAT }}
```
@@ -360,6 +409,6 @@ This action deletes versions of a package from [GitHub Packages](https://github.
The scripts and documentation in this project are released under the [MIT License](https://github.com/actions/delete-package-versions/blob/main/LICENSE)
[api]: https://developer.github.com/v4/previews/#github-packages
[api]: https://docs.github.com/en/rest/packages
[token]: https://help.github.com/en/packages/publishing-and-managing-packages/about-github-packages#about-tokens
[secret]: https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets

View File

@@ -1,43 +1,347 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import {rest} from 'msw'
import {setupServer} from 'msw/node'
import {Input, InputParams} from '../src/input'
import {deleteVersions, getVersionIds} from '../src/delete'
import {deleteVersions, finalIds, RATE_LIMIT} from '../src/delete'
import {getMockedVersionsResponse} from './version/rest.mock'
import {RestEndpointMethodTypes} from '@octokit/plugin-rest-endpoint-methods/dist-types/generated/parameters-and-response-types'
describe.skip('index tests -- call graphql', () => {
it('getVersionIds test -- get oldest version', done => {
const numVersions = 1
type GetVersionsResponseData =
RestEndpointMethodTypes['packages']['getAllPackageVersionsForPackageOwnedByUser']['response']['data']
getVersionIds(getInput({numOldVersionsToDelete: numVersions})).subscribe(
ids => {
expect(ids.length).toBeLessThanOrEqual(numVersions)
done()
}
)
describe('index tests -- call rest', () => {
let server = setupServer()
beforeEach(() => {
server = setupServer()
server.listen()
})
it('getVersionIds test -- get oldest 3 versions', done => {
const numVersions = 3
getVersionIds(getInput({numOldVersionsToDelete: numVersions})).subscribe(
ids => {
expect(ids.length).toBeLessThanOrEqual(numVersions)
done()
}
)
afterEach(() => {
server.close()
})
it('getVersionIds test -- supplied package version id', done => {
const suppliedIds = [
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB',
'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC'
]
getVersionIds(getInput({packageVersionIds: suppliedIds})).subscribe(ids => {
expect(ids).toBe(suppliedIds)
it('finalIds test - supplied package version id', done => {
const suppliedIds = ['123', '456', '789']
finalIds(getInput({packageVersionIds: suppliedIds})).subscribe(ids => {
expect(ids).toStrictEqual(suppliedIds)
done()
})
})
it('deleteVersions test -- missing token', done => {
it('finalIDs test - success', done => {
const numVersions = 10
let apiCalled = 0
const versions = getMockedVersionsResponse(numVersions)
server.use(
rest.get(
'https://api.github.com/users/test-owner/packages/npm/test-package/versions',
(req, res, ctx) => {
apiCalled++
return res(ctx.status(200), ctx.json(versions))
}
)
)
finalIds(getInput()).subscribe(ids => {
expect(apiCalled).toBe(1)
expect(ids.length).toBe(numVersions)
for (let i = 0; i < numVersions; i++) {
expect(ids[i]).toBe(versions[i].id.toString())
}
done()
})
})
it('finalIDs test - success - GHES', done => {
process.env.GITHUB_API_URL = 'https://github.someghesinstance.com/api/v3'
const numVersions = 10
let apiCalled = 0
const versions = getMockedVersionsResponse(numVersions)
server.use(
rest.get(
'https://github.someghesinstance.com/api/v3/users/test-owner/packages/npm/test-package/versions',
(req, res, ctx) => {
apiCalled++
return res(ctx.status(200), ctx.json(versions))
}
)
)
finalIds(getInput()).subscribe(ids => {
expect(apiCalled).toBe(1)
expect(ids.length).toBe(numVersions)
for (let i = 0; i < numVersions; i++) {
expect(ids[i]).toBe(versions[i].id.toString())
}
delete process.env.GITHUB_API_URL
done()
})
})
it('finalIDs test - success - pagination', done => {
const numVersions = RATE_LIMIT * 2
let apiCalled = 0
const versions = getMockedVersionsResponse(numVersions)
const firstPage = versions.slice(0, RATE_LIMIT)
const secondPage = versions.slice(RATE_LIMIT)
server.use(
rest.get(
'https://api.github.com/users/test-owner/packages/npm/test-package/versions',
(req, res, ctx) => {
apiCalled++
const page = req.url.searchParams.get('page')
if (page === '1') {
return res(ctx.status(200), ctx.json(firstPage))
} else if (page === '2') {
return res(ctx.status(200), ctx.json(secondPage))
} else {
return res(ctx.status(200), ctx.json([]))
}
}
)
)
finalIds(getInput()).subscribe(ids => {
expect(apiCalled).toBe(3) // 2 full pages + 1 empty page
// never returns more than RATE_LIMIT versions
expect(ids.length).toBe(RATE_LIMIT)
for (let i = 0; i < RATE_LIMIT; i++) {
expect(ids[i]).toBe(versions[i].id.toString())
}
done()
})
})
it('finalIDs test - success - sorting accross pages', done => {
const numVersions = RATE_LIMIT * 2
let apiCalled = 0
// versions is in ascending order of created_at
const versions = getMockedVersionsResponse(numVersions)
// return newer versions on first page to test sorting
const firstPage = versions.slice(RATE_LIMIT).reverse()
const secondPage = versions.slice(0, RATE_LIMIT).reverse()
server.use(
rest.get(
'https://api.github.com/users/test-owner/packages/npm/test-package/versions',
(req, res, ctx) => {
apiCalled++
const page = req.url.searchParams.get('page')
if (page === '1') {
return res(ctx.status(200), ctx.json(firstPage))
} else if (page === '2') {
return res(ctx.status(200), ctx.json(secondPage))
} else {
return res(ctx.status(200), ctx.json([]))
}
}
)
)
finalIds(getInput()).subscribe(ids => {
expect(apiCalled).toBe(3) // 2 full pages + 1 empty page
expect(ids.length).toBe(RATE_LIMIT)
for (let i = 0; i < RATE_LIMIT; i++) {
expect(ids[i]).toBe(versions[i].id.toString())
}
done()
})
})
it('finalIds test - do not delete more than numOldVersionsToDelete', done => {
const numVersions = 50
let apiCalled = 0
const versions = getMockedVersionsResponse(numVersions)
server.use(
rest.get(
'https://api.github.com/users/test-owner/packages/npm/test-package/versions',
(req, res, ctx) => {
apiCalled++
return res(ctx.status(200), ctx.json(versions))
}
)
)
const numOldVersionsToDelete = 10
finalIds(getInput({numOldVersionsToDelete})).subscribe(ids => {
expect(apiCalled).toBe(1)
expect(ids.length).toBe(numOldVersionsToDelete)
for (let i = 0; i < numOldVersionsToDelete; i++) {
expect(ids[i]).toBe(versions[i].id.toString())
}
done()
})
})
it('finalIds test - keep minVersionsToKeep', done => {
const numVersions = 50
let apiCalled = 0
const versions = getMockedVersionsResponse(numVersions)
server.use(
rest.get(
'https://api.github.com/users/test-owner/packages/npm/test-package/versions',
(req, res, ctx) => {
apiCalled++
return res(ctx.status(200), ctx.json(versions))
}
)
)
const minVersionsToKeep = 10
finalIds(getInput({minVersionsToKeep})).subscribe(ids => {
expect(apiCalled).toBe(1)
expect(ids.length).toBe(numVersions - minVersionsToKeep)
for (let i = 0; i < numVersions - minVersionsToKeep; i++) {
expect(ids[i]).toBe(versions[i].id.toString())
}
done()
})
})
it('finalIds test - delete only prerelease versions with minVersionsToKeep', done => {
const numVersions = 50
let apiCalled = 0
const versions = getMockedVersionsResponse(numVersions)
// make half versions prerelease
for (let i = 0; i < numVersions; i++) {
if (i % 2 === 0) {
versions[i].name += '-alpha'
}
}
server.use(
rest.get(
'https://api.github.com/users/test-owner/packages/npm/test-package/versions',
(req, res, ctx) => {
apiCalled++
return res(ctx.status(200), ctx.json(versions))
}
)
)
const toDelete = numVersions / 2 - 10
finalIds(
getInput({
ignoreVersions: RegExp('^(0|[1-9]\\d*)((\\.(0|[1-9]\\d*))*)$'),
minVersionsToKeep: 10
})
).subscribe(ids => {
expect(apiCalled).toBe(1)
expect(ids.length).toBe(toDelete)
for (let i = 0; i < toDelete; i++) {
expect(ids[i]).toBe(versions[i * 2].id.toString())
}
done()
})
})
it('finalIds test - delete only untagged versions with minVersionsToKeep', done => {
const numVersions = 50
const numTaggedVersions = 20
const numUntaggedVersions = numVersions - numTaggedVersions
const taggedVersions = getMockedVersionsResponse(
numTaggedVersions,
0,
'container',
true
)
const untaggedVersions = getMockedVersionsResponse(
numUntaggedVersions,
numTaggedVersions,
'container',
false
)
const versions = taggedVersions.concat(untaggedVersions)
let apiCalled = 0
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: 10,
deleteUntaggedVersions: 'true',
packageType: 'container'
})
).subscribe(ids => {
expect(apiCalled).toBe(1)
expect(ids.length).toBe(numUntaggedVersions - 10)
for (let i = 0; i < numUntaggedVersions - 10; i++) {
expect(ids[i]).toBe(untaggedVersions[i].id.toString())
}
done()
})
})
it('finalIds test - no versions deleted if API error even once', done => {
const numVersions = RATE_LIMIT * 2
let apiCalled = 0
const versions = getMockedVersionsResponse(numVersions)
const firstPage = versions.slice(0, RATE_LIMIT)
const secondPage = versions.slice(RATE_LIMIT)
server.use(
rest.get(
'https://api.github.com/users/test-owner/packages/npm/test-package/versions',
(req, res, ctx) => {
apiCalled++
const page = req.url.searchParams.get('page')
if (page === '1') {
return res(ctx.status(200), ctx.json(firstPage))
} else if (page === '2') {
return res(ctx.status(500), ctx.json([]))
} else {
return res(ctx.status(200), ctx.json([]))
}
}
)
)
finalIds(getInput()).subscribe(
() => {
done.fail('should not complete')
},
err => {
expect(apiCalled).toBe(2) // 1 full page + 1 error page
expect(err).toBeTruthy()
expect(err).toContain('get versions API failed.')
done()
}
)
})
it('deleteVersions test - missing token', done => {
deleteVersions(getInput({token: ''})).subscribe({
error: err => {
expect(err).toBeTruthy()
@@ -47,7 +351,7 @@ describe.skip('index tests -- call graphql', () => {
})
})
it('deleteVersions test -- missing packageName', done => {
it('deleteVersions test - missing packageName', done => {
deleteVersions(getInput({packageName: ''})).subscribe({
error: err => {
expect(err).toBeTruthy()
@@ -57,40 +361,123 @@ describe.skip('index tests -- call graphql', () => {
})
})
it.skip('deleteVersions test -- delete oldest version', done => {
deleteVersions(
getInput({numOldVersionsToDelete: 2, minVersionsToKeep: 1})
).subscribe(isSuccess => {
expect(isSuccess).toBe(true)
it('deleteVersions test - missing packageType', done => {
deleteVersions(getInput({packageType: ''})).subscribe({
error: err => {
expect(err).toBeTruthy()
done()
},
complete: async () => done.fail('no error thrown')
})
})
it('deleteVersions test - zero numOldVersionsToDelete', done => {
deleteVersions(getInput({numOldVersionsToDelete: 0})).subscribe(result => {
expect(result).toBe(true)
done()
})
})
it.skip('deleteVersions test -- delete 3 oldest versions', done => {
deleteVersions(
getInput({numOldVersionsToDelete: 3, minVersionsToKeep: 1})
).subscribe(isSuccess => {
expect(isSuccess).toBe(true)
done()
})
it('deleteVersions test - success complete flow', done => {
const numVersions = 10
let getApiCalled = 0
let deleteApiCalled = 0
const versions = getMockedVersionsResponse(numVersions)
const versionsDeleted: string[] = []
server.use(
rest.get(
'https://api.github.com/users/test-owner/packages/npm/test-package/versions',
(req, res, ctx) => {
getApiCalled++
return res(ctx.status(200), ctx.json(versions))
}
)
)
server.use(
rest.delete(
'https://api.github.com/users/test-owner/packages/npm/test-package/versions/:versionId',
(req, res, ctx) => {
deleteApiCalled++
versionsDeleted.push(req.params.versionId as string)
return res(ctx.status(204))
}
)
)
deleteVersions(getInput())
.subscribe(result => {
expect(result).toBe(true)
})
.add(() => {
expect(getApiCalled).toBe(1)
expect(deleteApiCalled).toBe(numVersions)
for (let i = 0; i < numVersions; i++) {
expect(versionsDeleted[i]).toBe(versions[i].id.toString())
}
done()
})
})
it('deleteVersions test -- keep 5 versions', done => {
deleteVersions(getInput({minVersionsToKeep: 5})).subscribe(isSuccess => {
expect(isSuccess).toBe(true)
done()
})
it('deleteVersions test - success complete flow - GHES', done => {
process.env.GITHUB_API_URL = 'https://github.someghesinstance.com/api/v3'
const numVersions = 10
let getApiCalled = 0
let deleteApiCalled = 0
const versions = getMockedVersionsResponse(numVersions)
const versionsDeleted: string[] = []
server.use(
rest.get(
'https://github.someghesinstance.com/api/v3/users/test-owner/packages/npm/test-package/versions',
(req, res, ctx) => {
getApiCalled++
return res(ctx.status(200), ctx.json(versions))
}
)
)
server.use(
rest.delete(
'https://github.someghesinstance.com/api/v3/users/test-owner/packages/npm/test-package/versions/:versionId',
(req, res, ctx) => {
deleteApiCalled++
versionsDeleted.push(req.params.versionId as string)
return res(ctx.status(204))
}
)
)
deleteVersions(getInput())
.subscribe(result => {
expect(result).toBe(true)
})
.add(() => {
expect(getApiCalled).toBe(1)
expect(deleteApiCalled).toBe(numVersions)
for (let i = 0; i < numVersions; i++) {
expect(versionsDeleted[i]).toBe(versions[i].id.toString())
}
delete process.env.GITHUB_API_URL
done()
})
})
})
const defaultInput: InputParams = {
packageVersionIds: [],
owner: 'namratajha',
repo: 'only-pkg',
packageName: 'onlypkg.maven',
numOldVersionsToDelete: 1,
minVersionsToKeep: 1,
token: process.env.GITHUB_TOKEN as string
owner: 'test-owner',
packageName: 'test-package',
packageType: 'npm',
numOldVersionsToDelete: RATE_LIMIT,
minVersionsToKeep: -1,
ignoreVersions: RegExp('^$'),
token: 'test-token'
}
function getInput(params?: InputParams): Input {

View File

@@ -1,25 +1,179 @@
import {rest} from 'msw'
import {setupServer} from 'msw/node'
import {deletePackageVersion, deletePackageVersions} from '../../src/version'
const githubToken = process.env.GITHUB_TOKEN as string
describe('delete tests - mock rest', () => {
let server = setupServer()
describe.skip('delete tests', () => {
it('deletePackageVersion', async () => {
const response = await deletePackageVersion(
'MDE0OlBhY2thZ2VWZXJzaW9uNjg5OTU1',
githubToken
).toPromise()
expect(response).toBe(true)
beforeEach(() => {
server = setupServer()
server.listen()
})
it('deletePackageVersions', async () => {
const response = await deletePackageVersions(
[
'MDE0OlBhY2thZ2VWZXJzaW9uNjk4Mjc0',
'MDE0OlBhY2thZ2VWZXJzaW9uNjk4Mjcx',
'MDE0OlBhY2thZ2VWZXJzaW9uNjk4MjY3'
],
githubToken
).toPromise()
expect(response).toBe(true)
afterEach(() => {
server.close()
})
it('deletePackageVersion', done => {
server.use(
rest.delete(
'https://api.github.com/users/test-owner/packages/npm/test-package/versions/123',
(req, res, ctx) => {
return res(ctx.status(204))
}
)
)
deletePackageVersion(
'123',
'test-owner',
'test-package',
'npm',
'test-token'
).subscribe(result => {
expect(result).toBe(true)
done()
})
})
it('deletePackageVersions', done => {
let success = 0
server.use(
rest.delete(
'https://api.github.com/users/test-owner/packages/npm/test-package/versions/*',
(req, res, ctx) => {
return res(ctx.status(204))
}
)
)
deletePackageVersions(
['123', '456', '789'],
'test-owner',
'test-package',
'npm',
'test-token'
)
.subscribe(
result => {
expect(result).toBe(true)
success++
},
err => {
// should not get here
done.fail(err)
}
)
.add(() => {
expect(success).toBe(3)
done()
})
})
it('deletePackageVersions - GHES', done => {
process.env.GITHUB_API_URL = 'https://github.someghesinstance.com/api/v3'
let success = 0
server.use(
rest.delete(
'https://github.someghesinstance.com/api/v3/users/test-owner/packages/npm/test-package/versions/*',
(req, res, ctx) => {
return res(ctx.status(204))
}
)
)
deletePackageVersions(
['123', '456', '789'],
'test-owner',
'test-package',
'npm',
'test-token'
)
.subscribe(
result => {
expect(result).toBe(true)
success++
},
err => {
// should not get here
done.fail(err)
}
)
.add(() => {
expect(success).toBe(3)
delete process.env.GITHUB_API_URL
done()
})
})
it('deletePackageVersion - API error', done => {
server.use(
rest.delete(
'https://api.github.com/users/test-owner/packages/npm/test-package/versions/123',
(req, res, ctx) => {
return res(ctx.status(500))
}
)
)
deletePackageVersion(
'123',
'test-owner',
'test-package',
'npm',
'test-token'
).subscribe(
() => {
done.fail('should not get here.')
},
err => {
expect(err).toContain('delete version API failed.')
done()
}
)
})
it('deletePackageVersions - API error for some versions', done => {
let success = 0
let failed = 0
server.use(
rest.delete(
'https://api.github.com/users/test-owner/packages/npm/test-package/versions/:versionId',
(req, res, ctx) => {
if (req.params.versionId === '456') {
return res(ctx.status(500))
}
return res(ctx.status(204))
}
)
)
deletePackageVersions(
['123', '456', '789'],
'test-owner',
'test-package',
'npm',
'test-token'
)
.subscribe(
result => {
expect(result).toBe(true)
success++
},
err => {
expect(err).toContain('delete version API failed.')
failed++
}
)
.add(() => {
expect(success).toBe(2)
expect(failed).toBe(1)
done()
})
})
})

View File

@@ -1,68 +1,200 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
import {mockOldestQueryResponse} from './graphql.mock'
import {rest} from 'msw'
import {setupServer} from 'msw/node'
import {
getOldestVersions as _getOldestVersions,
VersionInfo
RestQueryInfo
} from '../../src/version'
import {Observable} from 'rxjs'
import {getMockedVersionsResponse} from './rest.mock'
import {RATE_LIMIT} from '../../src/delete'
describe.skip('get versions tests -- call graphql', () => {
it('getOldestVersions -- succeeds', done => {
const numVersions = 1
describe('get versions tests -- mock rest', () => {
let server = setupServer()
getOldestVersions({numVersions}).subscribe(versions => {
expect(versions.length).toBe(numVersions)
done()
})
beforeEach(() => {
server = setupServer()
server.listen()
})
it('getOldestVersions -- fails for invalid repo', done => {
getOldestVersions({repo: 'actions-testin'}).subscribe({
error: err => {
expect(err).toBeTruthy()
done()
},
complete: async () => done.fail('no error thrown')
})
afterEach(() => {
server.close()
})
})
describe('get versions tests -- mock graphql', () => {
it('getOldestVersions -- success', done => {
const numVersions = 5
mockOldestQueryResponse(numVersions)
const numVersions = RATE_LIMIT
const resp = getMockedVersionsResponse(numVersions)
getOldestVersions({numVersions}).subscribe(versions => {
expect(versions.length).toBe(numVersions)
server.use(
rest.get(
'https://api.github.com/users/test-owner/packages/npm/test-package/versions',
(req, res, ctx) => {
return res(ctx.status(200), ctx.json(resp))
}
)
)
getOldestVersions({numVersions}).subscribe(result => {
expect(result.versions.length).toBe(numVersions)
for (let i = 0; i < numVersions; i++) {
expect(result.versions[i].id).toBe(resp[i].id)
expect(result.versions[i].version).toBe(resp[i].name)
expect(result.versions[i].created_at).toBe(resp[i].created_at)
}
expect(result.paginate).toBe(true)
expect(result.totalCount).toBe(numVersions)
done()
})
})
it('getOldestVersions -- success - GHES', done => {
const numVersions = RATE_LIMIT
const resp = getMockedVersionsResponse(numVersions)
// set GITHUB_API_URL to a different base url
process.env.GITHUB_API_URL = 'https://github.someghesinstance.com/api/v3'
server.use(
rest.get(
'https://github.someghesinstance.com/api/v3/users/test-owner/packages/npm/test-package/versions',
(req, res, ctx) => {
return res(ctx.status(200), ctx.json(resp))
}
)
)
getOldestVersions({numVersions}).subscribe(result => {
expect(result.versions.length).toBe(numVersions)
for (let i = 0; i < numVersions; i++) {
expect(result.versions[i].id).toBe(resp[i].id)
expect(result.versions[i].version).toBe(resp[i].name)
expect(result.versions[i].created_at).toBe(resp[i].created_at)
}
expect(result.paginate).toBe(true)
expect(result.totalCount).toBe(numVersions)
delete process.env.GITHUB_API_URL
done()
})
})
it('getOldestVersions -- success - container tagged versions', done => {
const numVersions = 6
const numTaggedVersions = 3
const numUntaggedVersions = numVersions - numTaggedVersions
const respTagged = getMockedVersionsResponse(
numTaggedVersions,
0,
'container',
true
)
const respUntagged = getMockedVersionsResponse(
numUntaggedVersions,
numTaggedVersions,
'container',
false
)
const resp = respTagged.concat(respUntagged)
server.use(
rest.get(
'https://api.github.com/users/test-owner/packages/container/test-package/versions',
(req, res, ctx) => {
return res(ctx.status(200), ctx.json(resp))
}
)
)
getOldestVersions({numVersions, packageType: 'container'}).subscribe(
result => {
expect(result.versions.length).toBe(numVersions)
for (let i = 0; i < numVersions; i++) {
expect(result.versions[i].id).toBe(resp[i].id)
expect(result.versions[i].version).toBe(resp[i].name)
expect(result.versions[i].created_at).toBe(resp[i].created_at)
if (i < numTaggedVersions) {
expect(result.versions[i].tagged).toBe(true)
} else {
expect(result.versions[i].tagged).toBe(false)
}
}
expect(result.paginate).toBe(true)
expect(result.totalCount).toBe(numVersions)
done()
}
)
})
it('getOldestVersions -- paginate is false when fetched versions is less than page size', done => {
const numVersions = 5
server.use(
rest.get(
'https://api.github.com/users/test-owner/packages/npm/test-package/versions',
(req, res, ctx) => {
return res(
ctx.status(200),
ctx.json(getMockedVersionsResponse(numVersions))
)
}
)
)
// In the call numVersions is set to RATE_LIMIT, but the response has only 5 versions.
getOldestVersions().subscribe(result => {
expect(result.paginate).toBe(false)
done()
})
})
it('getOldestVersions -- API error', done => {
server.use(
rest.get(
'https://api.github.com/users/test-owner/packages/npm/test-package/versions',
(req, res, ctx) => {
return res(ctx.status(500))
}
)
)
getOldestVersions().subscribe(
() => {
done.fail('should not get here.')
},
err => {
expect(err).toContain('get versions API failed.')
done()
}
)
})
})
interface Params {
owner?: string
repo?: string
packageName?: string
packageType?: string
numVersions?: number
page?: number
token?: string
}
const defaultParams = {
owner: 'namratajha',
repo: 'only-pkg',
packageName: 'onlypkg.maven',
numVersions: 3,
token: process.env.GITHUB_TOKEN as string
owner: 'test-owner',
packageName: 'test-package',
packageType: 'npm',
numVersions: RATE_LIMIT,
page: 1,
token: 'test-token'
}
function getOldestVersions(params?: Params): Observable<VersionInfo[]> {
function getOldestVersions(params?: Params): Observable<RestQueryInfo> {
const p: Required<Params> = {...defaultParams, ...params}
return _getOldestVersions(
p.owner,
p.repo,
p.packageName,
p.packageType,
p.numVersions,
p.page,
p.token
)
}

View File

@@ -1,49 +0,0 @@
import {
GraphQlQueryResponseData,
RequestParameters
} from '@octokit/graphql/dist-types/types'
import * as Graphql from '../../src/version/graphql'
import {GetVersionsQueryResponse} from '../../src/version'
export function getMockedOldestQueryResponse(
numVersions: number
): GetVersionsQueryResponse {
const versions = []
for (let i = 1; i <= numVersions; ++i) {
versions.push({
node: {
id: i.toString(),
version: `${i}.0.0`
}
})
}
return {
repository: {
packages: {
edges: [
{
node: {
name: 'test',
versions: {
edges: versions.reverse()
}
}
}
]
}
}
}
}
export function mockOldestQueryResponse(
numVersions: number
) {
const response = new Promise((resolve) => {
resolve(getMockedOldestQueryResponse(numVersions))
}) as Promise<GraphQlQueryResponseData>
jest.spyOn(Graphql, 'graphql').mockImplementation(
(token: string, query: string, parameters: RequestParameters) => response)
}

View File

@@ -0,0 +1,45 @@
import {RestEndpointMethodTypes} from '@octokit/plugin-rest-endpoint-methods/dist-types/generated/parameters-and-response-types'
type GetVersionsResponseData =
RestEndpointMethodTypes['packages']['getAllPackageVersionsForPackageOwnedByUser']['response']['data']
type PackageType =
RestEndpointMethodTypes['packages']['getAllPackageVersionsForPackageOwnedByUser']['parameters']['package_type']
export function getMockedVersionsResponse(
numVersions: number,
offset = 0,
packageType = 'npm',
tagged = false
): GetVersionsResponseData {
const versions: GetVersionsResponseData = []
for (let i = 1 + offset; i <= numVersions + offset; ++i) {
const created_at = new Date()
created_at.setUTCFullYear(2000 + Number(i), 1, 1)
let version = {
id: i,
name: `${i}.0.0`,
url: '',
created_at: created_at.toUTCString(),
package_html_url: '',
updated_at: '',
metadata: {
package_type: packageType as PackageType
}
} as GetVersionsResponseData[0]
if (packageType === 'container' && tagged) {
version = {
...version,
metadata: {
package_type: packageType as PackageType,
container: {
tags: [`latest${i}`] as string[]
}
}
}
}
versions.push(version)
}
return versions
}

View File

@@ -15,17 +15,15 @@ inputs:
Defaults to the owner of the repo running the action.
required: false
repo:
description: >
Repo containing the package version to delete.
Defaults to the repo running the action.
required: false
package-name:
description: >
Name of the package containing the version to delete.
Required if dynamically deleting oldest versions.
required: false
required: true
package-type:
description: >
Type of package. Can be one of container, maven, npm, nuget, or rubygems.
required: true
num-old-versions-to-delete:
description: >
@@ -37,9 +35,10 @@ inputs:
min-versions-to-keep:
description: >
Number of versions to keep starting with the latest version
Defaults to 0.
By default keeps no version.
To delete all versions set this as 0.
required: false
default: "0"
default: "-1"
ignore-versions:
description: >
@@ -50,12 +49,21 @@ inputs:
delete-only-pre-release-versions:
description: >
Deletes only pre-release versions upto. The number of pre-release versions to keep can be specified by min-versions-to-keep.
Deletes only pre-release versions. The number of pre-release versions to keep can be specified by min-versions-to-keep.
When this is set num-old-versions-to-delete and ignore-versions will not be taken into account.
By default this is set to false
required: false
default: "false"
delete-only-untagged-versions:
description: >
Deletes only untagged versions in case of a container package. Does not work for other package types.
The number of untagged versions to keep can be specified by min-versions-to-keep.
When this is set num-old-versions-to-delete will not be taken into account.
By default this is set to false
required: false
default: "false"
token:
description: >
Token with the necessary scopes to delete package versions.
@@ -66,9 +74,9 @@ inputs:
default: ${{ github.token }}
runs:
using: node12
using: node16
main: dist/index.js
branding:
icon: package
color: blue
color: blue

19264
dist/index.js vendored

File diff suppressed because one or more lines are too long

19890
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,9 +7,10 @@
"scripts": {
"format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts",
"lint": "eslint src/**/*.ts",
"lint": "eslint src/**/*.ts --fix",
"lint-check": "eslint src/**/*.ts",
"test": "jest",
"build": "npm run format-check && npm run lint && npm run test && tsc",
"build": "npm run format-check && npm run lint-check && npm run test && tsc",
"pack": "rm -rf ./lib ./dist && npm run build && ncc build"
},
"repository": {
@@ -24,23 +25,24 @@
"author": "YourNameOrOrganization",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.6.0",
"@actions/core": "^1.9.1",
"@actions/github": "^2.1.1",
"@octokit/rest": "^19.0.5",
"rxjs": "^6.5.4"
},
"devDependencies": {
"@types/jest": "^24.0.23",
"@types/node": "^12.7.12",
"@typescript-eslint/parser": "^2.8.0",
"@vercel/ncc": "^0.31.1",
"eslint": "^5.16.0",
"eslint-plugin-github": "^2.0.0",
"eslint-plugin-jest": "^22.21.0",
"jest": "^27.3.1",
"jest-circus": "^27.3.1",
"js-yaml": "^3.13.1",
"prettier": "^1.19.1",
"ts-jest": "^27.0.7",
"typescript": "^3.6.4"
"@types/jest": "^27.5.2",
"@types/node": "^18.11.18",
"@typescript-eslint/parser": "^5.47.1",
"@vercel/ncc": "^0.36.0",
"eslint": "^8.30.0",
"eslint-plugin-github": "^4.6.0",
"eslint-plugin-jest": "^27.1.7",
"jest": "^29.3.1",
"jest-circus": "^29.3.1",
"js-yaml": "^4.1.0",
"msw": "^0.49.2",
"prettier": "^2.8.1",
"ts-jest": "^29.0.3"
}
}

View File

@@ -1,46 +1,104 @@
/* eslint-disable i18n-text/no-en */
import {Input} from './input'
import {Observable, of, throwError} from 'rxjs'
import {deletePackageVersions, getOldestVersions} from './version'
import {concatMap, map} from 'rxjs/operators'
import {EMPTY, Observable, of, throwError} from 'rxjs'
import {reduce, concatMap, map, expand, tap} from 'rxjs/operators'
import {
deletePackageVersions,
getOldestVersions,
RestVersionInfo
} from './version'
export function getVersionIds(input: Input): Observable<string[]> {
export const RATE_LIMIT = 100
let totalCount = 0
export function getVersionIds(
owner: string,
packageName: string,
packageType: string,
numVersions: number,
page: number,
token: string
): Observable<RestVersionInfo[]> {
return getOldestVersions(
owner,
packageName,
packageType,
numVersions,
page,
token
).pipe(
expand(value =>
value.paginate
? getOldestVersions(
owner,
packageName,
packageType,
numVersions,
value.page + 1,
token
)
: EMPTY
),
tap(value => (totalCount = totalCount + value.totalCount)),
reduce((acc, value) => acc.concat(value.versions), [] as RestVersionInfo[])
)
}
export function finalIds(input: Input): Observable<string[]> {
if (input.packageVersionIds.length > 0) {
return of(input.packageVersionIds)
const toDelete = Math.min(input.packageVersionIds.length, RATE_LIMIT)
return of(input.packageVersionIds.slice(0, toDelete))
}
if (input.hasOldestVersionQueryInfo()) {
return getOldestVersions(
return getVersionIds(
input.owner,
input.repo,
input.packageName,
input.numOldVersionsToDelete + input.minVersionsToKeep,
input.packageType,
RATE_LIMIT,
1,
input.token
).pipe(
map(versionInfo => {
const numberVersionsToDelete =
versionInfo.length - input.minVersionsToKeep
// This code block executes on all versions of a package starting from oldest
map(value => {
// we need to delete oldest versions first
value.sort((a, b) => {
if (a.created_at === b.created_at) {
return a.id - b.id
}
return (
new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
)
})
/*
Here first filter out the versions that are to be ignored.
Then compute number of versions to delete (toDelete) based on the inputs.
*/
value = value.filter(info => !input.ignoreVersions.test(info.version))
if (input.minVersionsToKeep > 0) {
return numberVersionsToDelete <= 0
? []
: versionInfo
.filter(info => !input.ignoreVersions.test(info.version))
.map(info => info.id)
.slice(0, -input.minVersionsToKeep)
} else {
return numberVersionsToDelete <= 0
? []
: versionInfo
.filter(info => !input.ignoreVersions.test(info.version))
.map(info => info.id)
.slice(0, numberVersionsToDelete)
if (input.deleteUntaggedVersions === 'true') {
value = value.filter(info => !info.tagged)
}
let toDelete = 0
if (input.minVersionsToKeep < 0) {
toDelete = Math.min(
value.length,
Math.min(input.numOldVersionsToDelete, RATE_LIMIT)
)
} else {
toDelete = Math.min(
value.length - input.minVersionsToKeep,
RATE_LIMIT
)
}
if (toDelete < 0) return []
return value.map(info => info.id.toString()).slice(0, toDelete)
})
)
}
return throwError(
"Could not get packageVersionIds. Explicitly specify using the 'package-version-ids' input or provide the 'package-name' and 'num-old-versions-to-delete' inputs to dynamically retrieve oldest versions"
"Could not get packageVersionIds. Explicitly specify using the 'package-version-ids' input"
)
}
@@ -49,14 +107,28 @@ export function deleteVersions(input: Input): Observable<boolean> {
return throwError('No token found')
}
if (input.numOldVersionsToDelete <= 0) {
if (!input.checkInput()) {
return throwError('Invalid input combination')
}
if (input.numOldVersionsToDelete <= 0 && input.minVersionsToKeep < 0) {
console.log(
'Number of old versions to delete input is 0 or less, no versions will be deleted'
)
return of(true)
}
return getVersionIds(input).pipe(
concatMap(ids => deletePackageVersions(ids, input.token))
const result = finalIds(input)
return result.pipe(
concatMap(ids =>
deletePackageVersions(
ids,
input.owner,
input.packageName,
input.packageType,
input.token
)
)
)
}

View File

@@ -1,69 +1,100 @@
export interface InputParams {
packageVersionIds?: string[]
owner?: string
repo?: string
packageName?: string
packageType?: string
numOldVersionsToDelete?: number
minVersionsToKeep?: number
ignoreVersions?: RegExp
token?: string
deletePreReleaseVersions?: string
deleteUntaggedVersions?: string
}
const defaultParams = {
packageVersionIds: [],
owner: '',
repo: '',
packageName: '',
packageType: '',
numOldVersionsToDelete: 0,
minVersionsToKeep: 0,
ignoreVersions: new RegExp(''),
deletePreReleaseVersions: '',
token: ''
token: '',
deleteUntaggedVersions: ''
}
export class Input {
packageVersionIds: string[]
owner: string
repo: string
packageName: string
packageType: string
numOldVersionsToDelete: number
minVersionsToKeep: number
ignoreVersions: RegExp
deletePreReleaseVersions: string
token: string
numDeleted: number
deleteUntaggedVersions: string
constructor(params?: InputParams) {
const validatedParams: Required<InputParams> = {...defaultParams, ...params}
this.packageVersionIds = validatedParams.packageVersionIds
this.owner = validatedParams.owner
this.repo = validatedParams.repo
this.packageName = validatedParams.packageName
this.packageType = validatedParams.packageType
this.numOldVersionsToDelete = validatedParams.numOldVersionsToDelete
this.minVersionsToKeep = validatedParams.minVersionsToKeep
this.ignoreVersions = validatedParams.ignoreVersions
this.deletePreReleaseVersions = validatedParams.deletePreReleaseVersions
this.token = validatedParams.token
if (this.minVersionsToKeep > 0) {
this.numOldVersionsToDelete = 100 - this.minVersionsToKeep
}
if (this.deletePreReleaseVersions == 'true') {
this.numOldVersionsToDelete = 100 - this.minVersionsToKeep
this.ignoreVersions = new RegExp('^(0|[1-9]\\d*)((\\.(0|[1-9]\\d*))*)$')
}
this.numDeleted = 0
this.deleteUntaggedVersions = validatedParams.deleteUntaggedVersions
}
hasOldestVersionQueryInfo(): boolean {
return !!(
this.owner &&
this.repo &&
this.packageName &&
this.numOldVersionsToDelete > 0 &&
this.minVersionsToKeep >= 0 &&
this.numOldVersionsToDelete >= 0 &&
this.token
)
}
checkInput(): boolean {
if (this.packageType.toLowerCase() !== 'container') {
this.deleteUntaggedVersions = 'false'
}
if (
this.numOldVersionsToDelete > 1 &&
(this.minVersionsToKeep >= 0 ||
this.deletePreReleaseVersions === 'true' ||
this.deleteUntaggedVersions === 'true')
) {
return false
}
if (this.packageType === '' || this.packageName === '') {
return false
}
if (this.deletePreReleaseVersions === 'true') {
this.minVersionsToKeep =
this.minVersionsToKeep > 0 ? this.minVersionsToKeep : 0
this.ignoreVersions = new RegExp('^(0|[1-9]\\d*)((\\.(0|[1-9]\\d*))*)$')
}
if (this.deleteUntaggedVersions === 'true') {
this.minVersionsToKeep =
this.minVersionsToKeep > 0 ? this.minVersionsToKeep : 0
}
if (this.minVersionsToKeep >= 0) {
this.numOldVersionsToDelete = 0
}
return true
}
}

View File

@@ -11,15 +11,18 @@ function getActionInput(): Input {
? getInput('package-version-ids').split(',')
: [],
owner: getInput('owner') ? getInput('owner') : context.repo.owner,
repo: getInput('repo') ? getInput('repo') : context.repo.repo,
packageName: getInput('package-name'),
packageType: getInput('package-type'),
numOldVersionsToDelete: Number(getInput('num-old-versions-to-delete')),
minVersionsToKeep: Number(getInput('min-versions-to-keep')),
ignoreVersions: RegExp(getInput('ignore-versions')),
deletePreReleaseVersions: getInput(
'delete-only-pre-release-versions'
).toLowerCase(),
token: getInput('token')
token: getInput('token'),
deleteUntaggedVersions: getInput(
'delete-only-untagged-versions'
).toLowerCase()
})
}
@@ -29,7 +32,10 @@ function run(): Observable<boolean> {
catchError(err => throwError(err))
)
} catch (error) {
return throwError(error.message)
if (error instanceof Error) {
return throwError(error.message)
}
return throwError(error)
}
}

View File

@@ -1,65 +1,67 @@
import {from, Observable, merge, throwError, of} from 'rxjs'
import {catchError, map, tap} from 'rxjs/operators'
import {GraphQlQueryResponse} from '@octokit/graphql/dist-types/types'
import {graphql} from './graphql'
import {Octokit} from '@octokit/rest'
import {RestEndpointMethodTypes} from '@octokit/plugin-rest-endpoint-methods/dist-types/generated/parameters-and-response-types'
export interface DeletePackageVersionMutationResponse {
deletePackageVersion: {
success: boolean
}
}
const mutation = `
mutation deletePackageVersion($packageVersionId: String!) {
deletePackageVersion(input: {packageVersionId: $packageVersionId}) {
success
}
}`
let deleted = 0
type PackageType =
RestEndpointMethodTypes['packages']['getAllPackageVersionsForPackageOwnedByUser']['parameters']['package_type']
export function deletePackageVersion(
packageVersionId: string,
owner: string,
packageName: string,
packageType: string,
token: string
): Observable<boolean> {
const octokit = new Octokit({
auth: token,
baseUrl: process.env.GITHUB_API_URL || 'https://api.github.com'
})
const package_version_id = +packageVersionId
const package_type: PackageType = packageType as PackageType
deleted += 1
return from(
graphql(token, mutation, {
packageVersionId,
headers: {
Accept: 'application/vnd.github.package-deletes-preview+json'
}
}) as Promise<DeletePackageVersionMutationResponse>
octokit.rest.packages.deletePackageVersionForUser({
package_type,
package_name: packageName,
username: owner,
package_version_id
})
).pipe(
catchError((err: GraphQlQueryResponse) => {
const msg = 'delete version mutation failed.'
catchError(err => {
const msg = 'delete version API failed.'
return throwError(
err.errors && err.errors.length > 0
? `${msg} ${err.errors[0].message}`
: `${msg} verify input parameters are correct`
: `${msg} ${err.message} \n${deleted - 1} versions deleted till now.`
)
}),
map(response => response.deletePackageVersion.success)
map(response => response.status === 204)
)
}
export function deletePackageVersions(
packageVersionIds: string[],
owner: string,
packageName: string,
packageType: string,
token: string
): Observable<boolean> {
if (packageVersionIds.length === 0) {
console.log('no package version ids found, no versions will be deleted')
return of(true)
}
const deletes = packageVersionIds.map(id =>
deletePackageVersion(id, token).pipe(
deletePackageVersion(id, owner, packageName, packageType, token).pipe(
tap(result => {
if (result) {
console.log(`version with id: ${id}, deleted`)
} else {
if (!result) {
console.log(`version with id: ${id}, not deleted`)
}
})
)
)
console.log(`Total versions deleted till now: ${deleted}`)
return merge(...deletes)
}

View File

@@ -1,111 +1,83 @@
import {GraphQlQueryResponse} from '@octokit/graphql/dist-types/types'
import {Observable, from, throwError} from 'rxjs'
/* eslint-disable @typescript-eslint/no-unused-vars */
import {from, Observable, merge, throwError, of} from 'rxjs'
import {catchError, map} from 'rxjs/operators'
import {graphql} from './graphql'
import {Octokit} from '@octokit/rest'
import {RestEndpointMethodTypes} from '@octokit/plugin-rest-endpoint-methods/dist-types/generated/parameters-and-response-types'
export interface VersionInfo {
id: string
export interface RestVersionInfo {
id: number
version: string
created_at: string
tagged: boolean
}
export interface GetVersionsQueryResponse {
repository: {
packages: {
edges: {
node: {
name: string
versions: {
edges: {node: VersionInfo}[]
}
}
}[]
}
}
export interface RestQueryInfo {
versions: RestVersionInfo[]
page: number
paginate: boolean
totalCount: number
}
const query = `
query getVersions($owner: String!, $repo: String!, $package: String!, $last: Int!) {
repository(owner: $owner, name: $repo) {
packages(first: 1, names: [$package]) {
edges {
node {
name
versions(last: $last) {
edges {
node {
id
version
}
}
}
}
}
}
}
}`
export function queryForOldestVersions(
owner: string,
repo: string,
packageName: string,
numVersions: number,
token: string
): Observable<GetVersionsQueryResponse> {
return from(
graphql(token, query, {
owner,
repo,
package: packageName,
last: numVersions,
headers: {
Accept: 'application/vnd.github.packages-preview+json'
}
}) as Promise<GetVersionsQueryResponse>
).pipe(
catchError((err: GraphQlQueryResponse) => {
const msg = 'query for oldest version failed.'
return throwError(
err.errors && err.errors.length > 0
? `${msg} ${err.errors[0].message}`
: `${msg} verify input parameters are correct`
)
})
)
}
type PackageType =
RestEndpointMethodTypes['packages']['getAllPackageVersionsForPackageOwnedByUser']['parameters']['package_type']
type GetVersionsResponse =
RestEndpointMethodTypes['packages']['getAllPackageVersionsForPackageOwnedByUser']['response']['data']
export function getOldestVersions(
owner: string,
repo: string,
packageName: string,
packageType: string,
numVersions: number,
page: number,
token: string
): Observable<VersionInfo[]> {
return queryForOldestVersions(
owner,
repo,
packageName,
numVersions,
token
): Observable<RestQueryInfo> {
const octokit = new Octokit({
auth: token,
baseUrl: process.env.GITHUB_API_URL || 'https://api.github.com'
})
const package_type: PackageType = packageType as PackageType
return from(
octokit.rest.packages.getAllPackageVersionsForPackageOwnedByUser({
package_type,
package_name: packageName,
username: owner,
per_page: numVersions,
page
})
).pipe(
map(result => {
if (result.repository.packages.edges.length < 1) {
console.log(
`package: ${packageName} not found for owner: ${owner} in repo: ${repo}`
)
return []
catchError(err => {
const msg = 'get versions API failed.'
return throwError(
err.errors && err.errors.length > 0
? `${msg} ${err.errors[0].message}`
: `${msg} ${err.message}`
)
}),
map(response => {
const resp = {
versions: response.data.map((version: GetVersionsResponse[0]) => {
let tagged = false
if (
package_type === 'container' &&
version.metadata &&
version.metadata.container
) {
tagged = version.metadata.container.tags.length > 0
}
return {
id: version.id,
version: version.name,
created_at: version.created_at,
tagged
}
}),
page,
paginate: response.data.length === numVersions,
totalCount: response.data.length
}
const versions = result.repository.packages.edges[0].node.versions.edges
if (versions.length !== numVersions) {
console.log(
`number of versions requested was: ${numVersions}, but found: ${versions.length}`
)
}
return versions
.map(value => ({id: value.node.id, version: value.node.version}))
.reverse()
return resp
})
)
}

View File

@@ -1,19 +0,0 @@
import {GitHub} from '@actions/github'
import {GraphQlQueryResponseData} from '@octokit/graphql/dist-types/types'
import {RequestParameters} from '@octokit/types/dist-types/RequestParameters'
/**
* Sends a GraphQL query request based on endpoint options
*
* @param {string} token Auth token
* @param {string} query GraphQL query. Example: `'query { viewer { login } }'`.
* @param {object} parameters URL, query or body parameters, as well as `headers`, `mediaType.{format|previews}`, `request`, or `baseUrl`.
*/
export async function graphql(
token: string,
query: string,
parameters: RequestParameters
): Promise<GraphQlQueryResponseData> {
const github = new GitHub(token)
return await github.graphql(query, parameters)
}