From 1745eab77b671de30fb4f3a15710504e60714328 Mon Sep 17 00:00:00 2001 From: HenryNguyen5 <6404866+HenryNguyen5@users.noreply.github.com> Date: Wed, 13 Sep 2023 22:27:26 -0400 Subject: [PATCH] Remove deployments for the integration environment (#10594) --- .github/actions/delete-deployments/action.yml | 59 +++ .github/actions/delete-deployments/index.ts | 232 ++++++++++++ .../actions/delete-deployments/package.json | 25 ++ .../actions/delete-deployments/pnpm-lock.yaml | 350 ++++++++++++++++++ .github/actions/delete-deployments/test.sh | 9 + .../actions/delete-deployments/tsconfig.json | 104 ++++++ .github/workflows/delete-deployments.yml | 32 ++ .github/workflows/integration-tests.yml | 41 +- 8 files changed, 845 insertions(+), 7 deletions(-) create mode 100644 .github/actions/delete-deployments/action.yml create mode 100644 .github/actions/delete-deployments/index.ts create mode 100644 .github/actions/delete-deployments/package.json create mode 100644 .github/actions/delete-deployments/pnpm-lock.yaml create mode 100755 .github/actions/delete-deployments/test.sh create mode 100644 .github/actions/delete-deployments/tsconfig.json create mode 100644 .github/workflows/delete-deployments.yml diff --git a/.github/actions/delete-deployments/action.yml b/.github/actions/delete-deployments/action.yml new file mode 100644 index 00000000000..20b7d0eefe5 --- /dev/null +++ b/.github/actions/delete-deployments/action.yml @@ -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" diff --git a/.github/actions/delete-deployments/index.ts b/.github/actions/delete-deployments/index.ts new file mode 100644 index 00000000000..e38f1957d21 --- /dev/null +++ b/.github/actions/delete-deployments/index.ts @@ -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; + 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 + >["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; + } +} diff --git a/.github/actions/delete-deployments/package.json b/.github/actions/delete-deployments/package.json new file mode 100644 index 00000000000..7045cb35797 --- /dev/null +++ b/.github/actions/delete-deployments/package.json @@ -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" + } +} diff --git a/.github/actions/delete-deployments/pnpm-lock.yaml b/.github/actions/delete-deployments/pnpm-lock.yaml new file mode 100644 index 00000000000..a5553eb3dee --- /dev/null +++ b/.github/actions/delete-deployments/pnpm-lock.yaml @@ -0,0 +1,350 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@actions/core': + specifier: ^1.10.1 + version: 1.10.1 + '@octokit/action': + specifier: ^6.0.5 + version: 6.0.5 + '@octokit/plugin-retry': + specifier: ^6.0.0 + version: 6.0.0(@octokit/core@5.0.0) + '@octokit/plugin-throttling': + specifier: ^7.0.0 + version: 7.0.0(@octokit/core@5.0.0) + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@18.17.15)(typescript@5.2.2) + +devDependencies: + '@octokit/types': + specifier: ^11.1.0 + version: 11.1.0 + '@types/node': + specifier: ^18 + version: 18.17.15 + typescript: + specifier: ^5.2.2 + version: 5.2.2 + +packages: + + /@actions/core@1.10.1: + resolution: {integrity: sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==} + dependencies: + '@actions/http-client': 2.1.1 + uuid: 8.3.2 + dev: false + + /@actions/http-client@2.1.1: + resolution: {integrity: sha512-qhrkRMB40bbbLo7gF+0vu+X+UawOvQQqNAA/5Unx774RS8poaOhThDOG6BGmxvAnxhQnDp2BG/ZUm65xZILTpw==} + dependencies: + tunnel: 0.0.6 + dev: false + + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: false + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: false + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: false + + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: false + + /@octokit/action@6.0.5: + resolution: {integrity: sha512-jcCZb+jR4nzHgj86wlUvbTv92hiZ4OWpI9dIoWRilbtT4HuVVNFZvQih8X/YE2GMVrLCVbBD0xkjeq+1m8Rcpw==} + engines: {node: '>= 18'} + dependencies: + '@octokit/auth-action': 4.0.0 + '@octokit/core': 5.0.0 + '@octokit/plugin-paginate-rest': 8.0.0(@octokit/core@5.0.0) + '@octokit/plugin-rest-endpoint-methods': 9.0.0(@octokit/core@5.0.0) + '@octokit/types': 11.1.0 + undici: 5.24.0 + dev: false + + /@octokit/auth-action@4.0.0: + resolution: {integrity: sha512-sMm9lWZdiX6e89YFaLrgE9EFs94k58BwIkvjOtozNWUqyTmsrnWFr/M5LolaRzZ7Kmb5FbhF9hi7FEeE274SoQ==} + engines: {node: '>= 18'} + dependencies: + '@octokit/auth-token': 4.0.0 + '@octokit/types': 11.1.0 + dev: false + + /@octokit/auth-token@4.0.0: + resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==} + engines: {node: '>= 18'} + dev: false + + /@octokit/core@5.0.0: + resolution: {integrity: sha512-YbAtMWIrbZ9FCXbLwT9wWB8TyLjq9mxpKdgB3dUNxQcIVTf9hJ70gRPwAcqGZdY6WdJPZ0I7jLaaNDCiloGN2A==} + engines: {node: '>= 18'} + dependencies: + '@octokit/auth-token': 4.0.0 + '@octokit/graphql': 7.0.1 + '@octokit/request': 8.1.1 + '@octokit/request-error': 5.0.0 + '@octokit/types': 11.1.0 + before-after-hook: 2.2.3 + universal-user-agent: 6.0.0 + dev: false + + /@octokit/endpoint@9.0.0: + resolution: {integrity: sha512-szrQhiqJ88gghWY2Htt8MqUDO6++E/EIXqJ2ZEp5ma3uGS46o7LZAzSLt49myB7rT+Hfw5Y6gO3LmOxGzHijAQ==} + engines: {node: '>= 18'} + dependencies: + '@octokit/types': 11.1.0 + is-plain-object: 5.0.0 + universal-user-agent: 6.0.0 + dev: false + + /@octokit/graphql@7.0.1: + resolution: {integrity: sha512-T5S3oZ1JOE58gom6MIcrgwZXzTaxRnxBso58xhozxHpOqSTgDS6YNeEUvZ/kRvXgPrRz/KHnZhtb7jUMRi9E6w==} + engines: {node: '>= 18'} + dependencies: + '@octokit/request': 8.1.1 + '@octokit/types': 11.1.0 + universal-user-agent: 6.0.0 + dev: false + + /@octokit/openapi-types@18.0.0: + resolution: {integrity: sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw==} + + /@octokit/plugin-paginate-rest@8.0.0(@octokit/core@5.0.0): + resolution: {integrity: sha512-2xZ+baZWUg+qudVXnnvXz7qfrTmDeYPCzangBVq/1gXxii/OiS//4shJp9dnCCvj1x+JAm9ji1Egwm1BA47lPQ==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=5' + dependencies: + '@octokit/core': 5.0.0 + '@octokit/types': 11.1.0 + dev: false + + /@octokit/plugin-rest-endpoint-methods@9.0.0(@octokit/core@5.0.0): + resolution: {integrity: sha512-KquMF/VB1IkKNiVnzJKspY5mFgGyLd7HzdJfVEGTJFzqu9BRFNWt+nwTCMuUiWc72gLQhRWYubTwOkQj+w/1PA==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=5' + dependencies: + '@octokit/core': 5.0.0 + '@octokit/types': 11.1.0 + dev: false + + /@octokit/plugin-retry@6.0.0(@octokit/core@5.0.0): + resolution: {integrity: sha512-a1/A4A+PB1QoAHQfLJxGHhLfSAT03bR1jJz3GgQJZvty2ozawFWs93MiBQXO7SL2YbO7CIq0Goj4qLOBj8JeMQ==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=5' + dependencies: + '@octokit/core': 5.0.0 + '@octokit/request-error': 5.0.0 + '@octokit/types': 11.1.0 + bottleneck: 2.19.5 + dev: false + + /@octokit/plugin-throttling@7.0.0(@octokit/core@5.0.0): + resolution: {integrity: sha512-KL2k/d0uANc8XqP5S64YcNFCudR3F5AaKO39XWdUtlJIjT9Ni79ekWJ6Kj5xvAw87udkOMEPcVf9xEge2+ahew==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': ^5.0.0 + dependencies: + '@octokit/core': 5.0.0 + '@octokit/types': 11.1.0 + bottleneck: 2.19.5 + dev: false + + /@octokit/request-error@5.0.0: + resolution: {integrity: sha512-1ue0DH0Lif5iEqT52+Rf/hf0RmGO9NWFjrzmrkArpG9trFfDM/efx00BJHdLGuro4BR/gECxCU2Twf5OKrRFsQ==} + engines: {node: '>= 18'} + dependencies: + '@octokit/types': 11.1.0 + deprecation: 2.3.1 + once: 1.4.0 + dev: false + + /@octokit/request@8.1.1: + resolution: {integrity: sha512-8N+tdUz4aCqQmXl8FpHYfKG9GelDFd7XGVzyN8rc6WxVlYcfpHECnuRkgquzz+WzvHTK62co5di8gSXnzASZPQ==} + engines: {node: '>= 18'} + dependencies: + '@octokit/endpoint': 9.0.0 + '@octokit/request-error': 5.0.0 + '@octokit/types': 11.1.0 + is-plain-object: 5.0.0 + universal-user-agent: 6.0.0 + dev: false + + /@octokit/types@11.1.0: + resolution: {integrity: sha512-Fz0+7GyLm/bHt8fwEqgvRBWwIV1S6wRRyq+V6exRKLVWaKGsuy6H9QFYeBVDV7rK6fO3XwHgQOPxv+cLj2zpXQ==} + dependencies: + '@octokit/openapi-types': 18.0.0 + + /@tsconfig/node10@1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: false + + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: false + + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: false + + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: false + + /@types/node@18.17.15: + resolution: {integrity: sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==} + + /acorn-walk@8.2.0: + resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + engines: {node: '>=0.4.0'} + dev: false + + /acorn@8.10.0: + resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: false + + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: false + + /before-after-hook@2.2.3: + resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} + dev: false + + /bottleneck@2.19.5: + resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} + dev: false + + /busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + dependencies: + streamsearch: 1.1.0 + dev: false + + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: false + + /deprecation@2.3.1: + resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} + dev: false + + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: false + + /is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + dev: false + + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: false + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: false + + /streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + dev: false + + /ts-node@10.9.1(@types/node@18.17.15)(typescript@5.2.2): + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 18.17.15 + acorn: 8.10.0 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.2.2 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: false + + /tunnel@0.0.6: + resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} + engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} + dev: false + + /typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + + /undici@5.24.0: + resolution: {integrity: sha512-OKlckxBjFl0oXxcj9FU6oB8fDAaiRUq+D8jrFWGmOfI/gIyjk/IeS75LMzgYKUaeHzLUcYvf9bbJGSrUwTfwwQ==} + engines: {node: '>=14.0'} + dependencies: + busboy: 1.6.0 + dev: false + + /universal-user-agent@6.0.0: + resolution: {integrity: sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==} + dev: false + + /uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + dev: false + + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: false + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: false + + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: false diff --git a/.github/actions/delete-deployments/test.sh b/.github/actions/delete-deployments/test.sh new file mode 100755 index 00000000000..18b77260886 --- /dev/null +++ b/.github/actions/delete-deployments/test.sh @@ -0,0 +1,9 @@ +#!/bin/sh +export NUM_OF_PAGES=all +export ENVIRONMENT=integration +export DRY_RUN=false +export REPOSITORY=smartcontractkit/chainlink +export REF=fix/golint +export GITHUB_ACTION=true + +pnpm start diff --git a/.github/actions/delete-deployments/tsconfig.json b/.github/actions/delete-deployments/tsconfig.json new file mode 100644 index 00000000000..4b36d4a178c --- /dev/null +++ b/.github/actions/delete-deployments/tsconfig.json @@ -0,0 +1,104 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "NodeNext" /* Specify what module code is generated. */, + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "NodeNext" /* Specify how TypeScript looks up a file from a given module specifier. */, + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + "noEmit": true /* Disable emitting files from a compilation. */, + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + + /* Type Checking */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": false /* Skip type checking all .d.ts files. */ + }, + "include": ["src", "test"] +} diff --git a/.github/workflows/delete-deployments.yml b/.github/workflows/delete-deployments.yml new file mode 100644 index 00000000000..87cba36c085 --- /dev/null +++ b/.github/workflows/delete-deployments.yml @@ -0,0 +1,32 @@ +name: Deployments +on: + workflow_dispatch: + schedule: + # every 10 mins + - cron: "*/10 * * * *" + +jobs: + cleanup: + name: Clean up integration environment deployments + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: 🧼 Clean up Environment + uses: ./.github/actions/delete-deployments + with: + environment: integration + # Delete 300 deployments at a time + num-of-pages: 3 + # We start with page 2 because usually the first 200 deployments are still active, so we cannot delete them + starting-page: 2 + + - name: Collect Metrics + id: collect-gha-metrics + uses: smartcontractkit/push-gha-metrics-action@d2c2b7bdc9012651230b2608a1bcb0c48538b6ec + with: + basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} + this-job-name: Clean up integration environment deployments + continue-on-error: true diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 937d74a11db..9fdb42cbbeb 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -221,7 +221,7 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} go_mod_path: ./integration-tests/go.mod cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} - cache_restore_only: 'true' + cache_restore_only: "true" QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} @@ -328,7 +328,7 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} go_mod_path: ./integration-tests/go.mod cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} - cache_restore_only: 'true' + cache_restore_only: "true" QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} @@ -340,7 +340,7 @@ jobs: test_download_vendor_packages_command: cd ./integration-tests && go mod download go_mod_path: ./integration-tests/go.mod cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} - cache_restore_only: 'true' + cache_restore_only: "true" QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} @@ -384,6 +384,33 @@ jobs: this-job-name: ETH Smoke Tests continue-on-error: true + cleanup: + name: Clean up integration environment deployments + if: always() + needs: [eth-smoke-tests] + runs-on: ubuntu-latest + steps: + - name: Checkout repo + if: ${{ github.event_name == 'pull_request' }} + uses: actions/checkout@v4 + + - name: 🧼 Clean up Environment + if: ${{ github.event_name == 'pull_request' }} + uses: ./.github/actions/delete-deployments + with: + environment: integration + ref: ${{ github.head_ref }} # See https://github.com/github/docs/issues/15319#issuecomment-1476705663 + + - name: Collect Metrics + if: ${{ github.event_name == 'pull_request' }} + id: collect-gha-metrics + uses: smartcontractkit/push-gha-metrics-action@d2c2b7bdc9012651230b2608a1bcb0c48538b6ec + with: + basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} + this-job-name: Clean up integration environment deployments + continue-on-error: true + # Run the setup if the matrix finishes but this time save the cache if we have a cache hit miss # this will also only run if both of the matrix jobs pass eth-smoke-go-mod-cache: @@ -406,7 +433,7 @@ jobs: go test -run=NonExistentTest ./smoke/... || echo "ignore expected test failure" go_mod_path: ./integration-tests/go.mod cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} - cache_restore_only: 'false' + cache_restore_only: "false" ### Migration tests node-migration-tests: @@ -456,7 +483,7 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} go_mod_path: ./integration-tests/go.mod cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} - cache_restore_only: 'true' + cache_restore_only: "true" QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} @@ -766,7 +793,7 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} go_mod_path: ./integration-tests/go.mod cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} - cache_restore_only: 'true' + cache_restore_only: "true" QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} @@ -904,4 +931,4 @@ jobs: env: SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} - ### End Live Testnet Section \ No newline at end of file + ### End Live Testnet Section