-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove deployments for the integration environment
- Loading branch information
1 parent
411c119
commit b35e694
Showing
8 changed files
with
845 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
name: Delete Deployments | ||
description: Delete deployments by env and ref | ||
inputs: | ||
environment: | ||
required: true | ||
description: The Github environment to filter deployments by | ||
ref: | ||
required: true | ||
description: The ref to filter deployments by | ||
dry-run: | ||
required: false | ||
description: Whether to actually delete deployments or not | ||
github-token: | ||
description: "The Github token to use for authentication" | ||
required: true | ||
default: ${{ github.token }} | ||
num-of-pages: | ||
required: false | ||
description: The number of pages (of 100 per page) to fetch deployments from, set to 'all' to fetch all deployments | ||
default: "all" | ||
starting-page: | ||
required: false | ||
description: The page to start fetching deployments from, only valid if num-of-pages is set to a number | ||
repository: | ||
required: false | ||
description: The owner and repository name to delete deployments from, defaults to the current repository, ex. 'smartcontractkit/chainlink' | ||
default: ${{ github.repository }} | ||
|
||
runs: | ||
using: composite | ||
steps: | ||
- uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd #v2.2.4 | ||
with: | ||
version: ^8.0.0 | ||
|
||
- uses: actions/setup-node@v3 | ||
with: | ||
node-version: "18" | ||
cache: "pnpm" | ||
cache-dependency-path: "./.github/actions/delete-deployments/pnpm-lock.yaml" | ||
|
||
- name: Install dependencies | ||
shell: bash | ||
run: pnpm i --prod | ||
working-directory: "./.github/actions/delete-deployments" | ||
|
||
- name: Run deployment deleter | ||
shell: bash | ||
run: pnpm start | ||
env: | ||
NUM_OF_PAGES: ${{ inputs.num-of-pages }} | ||
STARTING_PAGE: ${{ inputs.starting-page }} | ||
GITHUB_TOKEN: ${{ inputs.github-token }} | ||
ENVIRONMENT: ${{ inputs.environment }} | ||
REF: ${{ inputs.ref }} | ||
DRY_RUN: ${{ inputs.dry-run }} | ||
OWNER: ${{ inputs.owner }} | ||
REPOSITORY: ${{ inputs.repository }} | ||
working-directory: "./.github/actions/delete-deployments" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
import { Octokit } from "@octokit/action"; | ||
import { info, warning, isDebug } from "@actions/core"; | ||
import { throttling } from "@octokit/plugin-throttling"; | ||
import { retry } from "@octokit/plugin-retry"; | ||
|
||
async function main() { | ||
const { | ||
dryRun, | ||
environment, | ||
numOfPages, | ||
owner, | ||
ref, | ||
repo, | ||
debug, | ||
startingPage, | ||
} = getInputs(); | ||
const octokit = getOctokit(debug); | ||
|
||
const deployments = await getDeployments({ | ||
octokit, | ||
owner, | ||
repo, | ||
environment, | ||
ref, | ||
paginateOptions: { | ||
numOfPages, | ||
startingPage, | ||
}, | ||
}); | ||
const deploymentIds = deployments.map((d) => d.id); | ||
if (dryRun) { | ||
info(`Dry run: would delete deployments (${deploymentIds.length})`); | ||
return; | ||
} | ||
|
||
info(`Deleting deployments (${deploymentIds.length})`); | ||
const deleteDeployments = deploymentIds.map(async (id) => { | ||
const sharedArgs = { | ||
owner, | ||
repo, | ||
deployment_id: id, | ||
request: { | ||
retries: 0, | ||
}, | ||
}; | ||
|
||
const setStatus = await octokit.repos | ||
.createDeploymentStatus({ | ||
...sharedArgs, | ||
state: "inactive", | ||
}) | ||
.then(() => true) | ||
.catch((e) => { | ||
warning( | ||
`Marking deployment id ${id} to "inactive" failed: ${e.message}` | ||
); | ||
return false; | ||
}); | ||
if (!setStatus) return false; | ||
|
||
return octokit.repos | ||
.deleteDeployment({ | ||
...sharedArgs, | ||
}) | ||
.then(() => true) | ||
.catch((e) => { | ||
warning(`Deleting deployment id ${id} failed: ${e.message}`); | ||
return false; | ||
}); | ||
}); | ||
|
||
const processed = await Promise.all(deleteDeployments); | ||
const succeeded = processed.filter((p) => !!p); | ||
info( | ||
`Successfully deleted ${succeeded.length}/${processed.length} deployments` | ||
); | ||
} | ||
main(); | ||
|
||
function getInputs() { | ||
const debug = !!(process.env.DEBUG || isDebug()); | ||
|
||
const dryRun = process.env.DRY_RUN === "true"; | ||
|
||
const environment = process.env.ENVIRONMENT; | ||
if (!environment) throw new Error("ENVIRONMENT not set"); | ||
|
||
const ref = process.env.REF; | ||
|
||
const repository = process.env.REPOSITORY; | ||
if (!repository) throw new Error("REPOSITORY not set"); | ||
const [owner, repo] = repository.split("/"); | ||
|
||
const rawStartingPage = process.env.STARTING_PAGE; | ||
|
||
let startingPage: number | undefined; | ||
if (rawStartingPage) { | ||
startingPage = parseInt(rawStartingPage); | ||
if (isNaN(startingPage)) { | ||
throw new Error(`STARTING_PAGE is not a number: ${rawStartingPage}`); | ||
} | ||
if (startingPage < 0) { | ||
throw new Error( | ||
`STARTING_PAGE must be a positive integer or zero: ${rawStartingPage}` | ||
); | ||
} | ||
info(`Starting from page ${startingPage}`); | ||
} | ||
|
||
const rawNumOfPages = process.env.NUM_OF_PAGES; | ||
let numOfPages: "all" | number = "all"; | ||
if (rawNumOfPages === "all") { | ||
info("Fetching all pages of deployments"); | ||
} else { | ||
const parsedPages = parseInt(rawNumOfPages || ""); | ||
if (isNaN(parsedPages)) { | ||
throw new Error(`NUM_OF_PAGES is not a number: ${rawNumOfPages}`); | ||
} | ||
if (parsedPages < 1) { | ||
throw new Error(`NUM_OF_PAGES must be greater than 0: ${rawNumOfPages}`); | ||
} | ||
numOfPages = parsedPages; | ||
} | ||
|
||
if (numOfPages === "all" && startingPage) { | ||
throw new Error(`Cannot use STARTING_PAGE with NUM_OF_PAGES=all`); | ||
} | ||
|
||
const parsedInputs = { | ||
environment, | ||
ref, | ||
owner, | ||
repo, | ||
numOfPages, | ||
startingPage, | ||
dryRun, | ||
debug, | ||
}; | ||
info(`Configuration: ${JSON.stringify(parsedInputs)}`); | ||
return parsedInputs; | ||
} | ||
|
||
function getOctokit(debug: boolean) { | ||
const OctokitAPI = Octokit.plugin(throttling, retry); | ||
const octokit = new OctokitAPI({ | ||
log: debug ? console : undefined, | ||
throttle: { | ||
onRateLimit: (retryAfter, options, octokit, retryCount) => { | ||
octokit.log.warn( | ||
// Types are busted from octokit | ||
//@ts-expect-error | ||
`Request quota exhausted for request ${options.method} ${options.url}` | ||
); | ||
|
||
octokit.log.info(`Retrying after ${retryAfter} seconds!`); | ||
return true; | ||
}, | ||
onSecondaryRateLimit: (_retryAfter, options, octokit) => { | ||
octokit.log.warn( | ||
// Types are busted from octokit | ||
//@ts-expect-error | ||
`SecondaryRateLimit detected for request ${options.method} ${options.url}` | ||
); | ||
return true; | ||
}, | ||
}, | ||
}); | ||
|
||
return octokit; | ||
} | ||
|
||
async function getDeployments({ | ||
octokit, | ||
owner, | ||
repo, | ||
environment, | ||
ref, | ||
paginateOptions, | ||
}: { | ||
octokit: ReturnType<typeof getOctokit>; | ||
owner: string; | ||
repo: string; | ||
environment: string; | ||
ref?: string; | ||
paginateOptions: { | ||
numOfPages: number | "all"; | ||
startingPage?: number; | ||
}; | ||
}) { | ||
const listDeploymentsSharedArgs: Parameters< | ||
typeof octokit.repos.listDeployments | ||
>[0] = { | ||
owner, | ||
repo, | ||
environment, | ||
ref, | ||
per_page: 100, | ||
request: { | ||
retries: 20, | ||
}, | ||
}; | ||
|
||
if (paginateOptions.numOfPages === "all") { | ||
info(`Fetching all deployments`); | ||
const deployments = await octokit.paginate(octokit.repos.listDeployments, { | ||
...listDeploymentsSharedArgs, | ||
}); | ||
|
||
return deployments; | ||
} else { | ||
info( | ||
`Fetching ${ | ||
paginateOptions.numOfPages * listDeploymentsSharedArgs.per_page! | ||
} deployments` | ||
); | ||
const deployments: Awaited< | ||
ReturnType<typeof octokit.repos.listDeployments> | ||
>["data"] = []; | ||
|
||
const offset = paginateOptions.startingPage || 0; | ||
for (let i = offset; i < paginateOptions.numOfPages + offset; i++) { | ||
const deploymentPage = await octokit.repos.listDeployments({ | ||
...listDeploymentsSharedArgs, | ||
page: i, | ||
}); | ||
|
||
deployments.push(...deploymentPage.data); | ||
} | ||
|
||
return deployments; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"name": "delete-deployments", | ||
"version": "1.0.0", | ||
"description": "", | ||
"main": "index.ts", | ||
"scripts": { | ||
"start": "ts-node -T .", | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"keywords": [], | ||
"author": "", | ||
"license": "ISC", | ||
"dependencies": { | ||
"@actions/core": "^1.10.1", | ||
"@octokit/action": "^6.0.5", | ||
"@octokit/plugin-retry": "^6.0.0", | ||
"@octokit/plugin-throttling": "^7.0.0", | ||
"ts-node": "^10.9.1" | ||
}, | ||
"devDependencies": { | ||
"@octokit/types": "^11.1.0", | ||
"@types/node": "^18", | ||
"typescript": "^5.2.2" | ||
} | ||
} |
Oops, something went wrong.