From 4b65b59a57a77ad099d4722624c4017bb2e545ee Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Thu, 5 Feb 2026 18:39:37 +0100 Subject: [PATCH 1/2] ci: remove workflow-based PR automation --- .github/scripts/download_pr_artifact.js | 28 ----- .github/scripts/label_pr_based_on_title.js | 44 ------- .github/scripts/label_related_issue.js | 107 ---------------- .github/scripts/save_pr_details.js | 13 -- .github/workflows/on_merged_pr.yml | 64 ---------- .github/workflows/record_pr.yml | 60 --------- .../workflows/reusable_export_pr_details.yml | 114 ------------------ 7 files changed, 430 deletions(-) delete mode 100644 .github/scripts/download_pr_artifact.js delete mode 100644 .github/scripts/label_pr_based_on_title.js delete mode 100644 .github/scripts/label_related_issue.js delete mode 100644 .github/scripts/save_pr_details.js delete mode 100644 .github/workflows/on_merged_pr.yml delete mode 100644 .github/workflows/record_pr.yml delete mode 100644 .github/workflows/reusable_export_pr_details.yml diff --git a/.github/scripts/download_pr_artifact.js b/.github/scripts/download_pr_artifact.js deleted file mode 100644 index dc02a88dff..0000000000 --- a/.github/scripts/download_pr_artifact.js +++ /dev/null @@ -1,28 +0,0 @@ -const fs = require('node:fs'); - -module.exports = async ({ github, context, core }) => { - const workflowRunId = process.env.WORKFLOW_ID; - core.info(`Listing artifacts for workflow run ${workflowRunId}`); - - const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: workflowRunId, - }); - - const matchArtifact = artifacts.data.artifacts.filter( - (artifact) => artifact.name === 'pr' - )[0]; - - core.info(`Downloading artifacts for workflow run ${workflowRunId}`); - const artifact = await github.rest.actions.downloadArtifact({ - owner: context.repo.owner, - repo: context.repo.repo, - artifact_id: matchArtifact.id, - archive_format: 'zip', - }); - - core.info('Saving artifact found', artifact); - - fs.writeFileSync('pr.zip', Buffer.from(artifact.data)); -}; diff --git a/.github/scripts/label_pr_based_on_title.js b/.github/scripts/label_pr_based_on_title.js deleted file mode 100644 index 45a1932599..0000000000 --- a/.github/scripts/label_pr_based_on_title.js +++ /dev/null @@ -1,44 +0,0 @@ -const { PR_NUMBER, PR_TITLE } = require('./constants'); - -module.exports = async ({ github, context, core }) => { - const BUG_REGEX = /(fix|bug)(\((.+)\))?(:.+)/; - const ENHANCEMENT_REGEX = /(refactor|improv)(\((.+)\))?(:.+)/; - const FEAT_REFACTOR_REGEX = /(feat)(\((.+)\))?(:.+)/; - const DEPRECATED_REGEX = /(deprecated)(\((.+)\))?(:.+)/; - - const labels = { - feature: FEAT_REFACTOR_REGEX, - bug: BUG_REGEX, - deprecation: DEPRECATED_REGEX, - enhancement: ENHANCEMENT_REGEX, - }; - - // Maintenance: We should keep track of modified PRs in case their titles change - let miss = 0; - try { - for (const label in labels) { - const matcher = new RegExp(labels[label]); - const matches = matcher.exec(PR_TITLE); - if (matches != null) { - core.info(`Auto-labeling PR ${PR_NUMBER} with ${label}`); - - await github.rest.issues.addLabels({ - issue_number: PR_NUMBER, - owner: context.repo.owner, - repo: context.repo.repo, - labels: [label], - }); - - return; - } - core.debug(`'${PR_TITLE}' didn't match '${label}' semantic.`); - miss += 1; - } - } finally { - if (miss === Object.keys(labels).length) { - core.notice( - `PR ${PR_NUMBER} title '${PR_TITLE}' contain any of the release-related types; skipping...` - ); - } - } -}; diff --git a/.github/scripts/label_related_issue.js b/.github/scripts/label_related_issue.js deleted file mode 100644 index 8b7db0052f..0000000000 --- a/.github/scripts/label_related_issue.js +++ /dev/null @@ -1,107 +0,0 @@ -const { - PR_AUTHOR, - PR_BODY, - PR_NUMBER, - IGNORE_AUTHORS, - LABEL_PENDING_RELEASE, - HANDLE_MAINTAINERS_TEAM, - PR_IS_MERGED, - RELATED_ISSUE_REGEX, - LABEL_RELEASED, -} = require('./constants'); - -module.exports = async ({ github, context, core }) => { - if (IGNORE_AUTHORS.includes(PR_AUTHOR)) { - return core.notice('Author in IGNORE_AUTHORS list; skipping...'); - } - - if (PR_IS_MERGED === 'false') { - return core.notice('Only merged PRs to avoid spam; skipping'); - } - - const isMatch = RELATED_ISSUE_REGEX.exec(PR_BODY); - try { - if (!isMatch) { - core.setFailed( - `Unable to find related issue for PR number ${PR_NUMBER}.\n\n Body details: ${PR_BODY}` - ); - return await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - body: `${HANDLE_MAINTAINERS_TEAM} No related issues found. Please ensure '${LABEL_PENDING_RELEASE}' label is applied before releasing.`, - issue_number: PR_NUMBER, - }); - } - } catch (error) { - core.setFailed( - `Unable to create comment on PR number ${PR_NUMBER}.\n\n Error details: ${error}` - ); - throw new Error(error); - } - - const { - groups: { issue }, - } = isMatch; - - let currentLabels = []; - try { - core.info(`Getting labels for issue ${issue}`); - currentLabels = await github.rest.issues.listLabelsOnIssue({ - issue_number: issue, - owner: context.repo.owner, - repo: context.repo.repo, - }); - } catch (error) { - core.setFailed( - `Unable to get labels for issue ${issue}.\n\n Error details: ${error}` - ); - throw new Error(error); - } - - /** - * Keep all labels except any of the `status` ones, or 'need-' or equal to 'help-wanted' - * as those are contextual to issues still in progress. - * - * If the issue was already marked with the 'completed' label, then we'll keep that, otherwise - * we'll add the 'pending-release' label. - */ - let hasCompletedLabel = false; - const newLabels = currentLabels.data - .filter((label) => { - if (label.name === LABEL_RELEASED) { - hasCompletedLabel = true; - } - return ( - ![ - 'blocked', - 'confirmed', - 'discussing', - 'on-hold', - 'completed', - 'rejected', - 'pending-release', - 'pending-close-response-required', - ].includes(label.name) && - !label.name.startsWith('need-') && - label.name !== 'help-wanted' - ); - }) - .map((label) => label.name); - // Add the pending-release or completed label - newLabels.push(hasCompletedLabel ? LABEL_RELEASED : LABEL_PENDING_RELEASE); - - try { - core.info(`Auto-labeling related issue ${issue} completed`); - return await github.rest.issues.setLabels({ - issue_number: issue, - owner: context.repo.owner, - repo: context.repo.repo, - labels: newLabels, - }); - } catch (error) { - core.setFailed( - `Is this issue number (${issue}) valid? Perhaps a discussion?` - ); - throw new Error(error); - } -}; diff --git a/.github/scripts/save_pr_details.js b/.github/scripts/save_pr_details.js deleted file mode 100644 index 9c92ff00fc..0000000000 --- a/.github/scripts/save_pr_details.js +++ /dev/null @@ -1,13 +0,0 @@ -const fs = require('node:fs'); -module.exports = async ({ context, core }) => { - const filename = 'pr.txt'; - - try { - fs.writeFileSync(`./${filename}`, JSON.stringify(context.payload)); - - return `PR successfully saved ${filename}`; - } catch (err) { - core.setFailed('Failed to save PR details'); - console.error(err); - } -}; diff --git a/.github/workflows/on_merged_pr.yml b/.github/workflows/on_merged_pr.yml deleted file mode 100644 index 55a61442fc..0000000000 --- a/.github/workflows/on_merged_pr.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: On PR merge - -# PROCESS -# -# 1. Fetch PR details previously saved from untrusted location -# 2. Parse details for safety -# 3. Add `pending-release` label for related issue -# 4. Make a comment in PR if related issue is invalid or can't be labeled - -# USAGE -# -# NOTE: meant to be used with ./.github/workflows/record_pr.yml -# -# Security Note: -# -# This workflow depends on "Record PR" workflow that runs in an untrusted location (forks) instead of `pull_request_target`. -# This enforces zero trust where "Record PR" workflow always runs on fork with zero permissions on GH_TOKEN. -# When "Record PR" completes, this workflow runs in our repository with the appropriate permissions and sanitize inputs. -# -# Coupled with "Approve GitHub Action to run on forks", we have confidence no privilege can be escalated, -# since any malicious change would need to be approved, and upon social engineering, it'll have zero permissions. - -on: - workflow_run: - workflows: ["Record PR details"] - types: - - completed - -permissions: - contents: read - -jobs: - get_pr_details: - if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' - permissions: - actions: read # download PR artifact - contents: read # checkout code - uses: ./.github/workflows/reusable_export_pr_details.yml - with: - record_pr_workflow_id: ${{ github.event.workflow_run.id }} - workflow_origin: ${{ github.event.repository.full_name }} - secrets: - token: ${{ secrets.GITHUB_TOKEN }} - release_label_on_merge: - needs: get_pr_details - runs-on: ubuntu-latest - permissions: - pull-requests: write # make a comment in PR if unable to find related issue - issues: write # label issue with pending-release - if: needs.get_pr_details.outputs.prIsMerged == 'true' - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: "Label PR related issue for release" - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - PR_NUMBER: ${{ needs.get_pr_details.outputs.prNumber }} - PR_BODY: ${{ needs.get_pr_details.outputs.prBody }} - PR_IS_MERGED: ${{ needs.get_pr_details.outputs.prIsMerged }} - PR_AUTHOR: ${{ needs.get_pr_details.outputs.prAuthor }} - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const script = require('.github/scripts/label_related_issue.js') - await script({github, context, core}) diff --git a/.github/workflows/record_pr.yml b/.github/workflows/record_pr.yml deleted file mode 100644 index 6626cadc24..0000000000 --- a/.github/workflows/record_pr.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Record PR details - -# PROCESS -# -# 1. Runs in fork location upon PR creation or changes -# 2. Saves GitHub Pull Request Webhook payload -# 3. Uploads as a temporary GitHub Action Artifact with shortest retention - -# USAGE -# -# see .github/workflows/on_merged_pr.yml and related for full example. -# -# on: -# workflow_run: -# workflows: ["Record PR details"] -# types: -# - completed -# -# Security Note: -# -# For security, this is intended to be a 2-step process: (1) collect PR, (2) act on PR. -# Do not ever use `pull_request_target` to "simplify", as it sends a write-token to the fork. Our linter should catch it. -# -# The first step runs in untrusted location (fork), therefore we limit permissions to only check out code. -# -# The second step will be workflows that want to act on a given PR, this time with intended permissions, and -# it runs on its base location (this repo!). -# -# This enforces zero trust where this workflow always runs on fork with zero permissions on GH_TOKEN. -# When this workflow completes, X workflows run in our repository with the appropriate permissions and sanitize inputs. -# -# Coupled with "Approve GitHub Action to run on forks", we have confidence no privilege can be escalated, -# since any malicious change would need to be approved, and upon social engineering, it'll have zero permissions. - - -on: - pull_request: - types: [opened, edited, closed, labeled] - -permissions: - contents: read - -jobs: - record_pr: - runs-on: ubuntu-latest - permissions: - contents: read # NOTE: treat as untrusted location - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: "Extract PR details" - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const script = require('.github/scripts/save_pr_details.js') - await script({github, context, core}) - - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 - with: - name: pr - path: pr.txt - retention-days: 1 \ No newline at end of file diff --git a/.github/workflows/reusable_export_pr_details.yml b/.github/workflows/reusable_export_pr_details.yml deleted file mode 100644 index 17984620d7..0000000000 --- a/.github/workflows/reusable_export_pr_details.yml +++ /dev/null @@ -1,114 +0,0 @@ -name: Export previously recorded PR - -# PROCESS -# -# 1. Fetch PR details previously saved from untrusted location -# 2. Parse details for safety -# 3. Export only what's needed for automation, e.g., PR number, title, body, author, action, whether is merged - -# USAGE -# -# see .github/workflows/on_merged_pr.yml and related for full example. -# -# NOTE: meant to be used with workflows that react to a given PR state (labeling, new, merged, etc.) -# done separately to isolate security practices and make it reusable. - -on: - workflow_call: - inputs: - record_pr_workflow_id: - description: "Record PR workflow execution ID to download PR details" - required: true - type: number - workflow_origin: # see https://github.com/aws-powertools/powertools-lambda-python/issues/1349 - description: "Repository full name for runner integrity" - required: true - type: string - secrets: - token: - description: "GitHub Actions temporary and scoped token" - required: true - # Map the workflow outputs to job outputs - outputs: - prNumber: - description: "PR Number" - value: ${{ jobs.export_pr_details.outputs.prNumber }} - prTitle: - description: "PR Title" - value: ${{ jobs.export_pr_details.outputs.prTitle }} - prBody: - description: "PR Body as string" - value: ${{ jobs.export_pr_details.outputs.prBody }} - prAuthor: - description: "PR author username" - value: ${{ jobs.export_pr_details.outputs.prAuthor }} - prAction: - description: "PR event action" - value: ${{ jobs.export_pr_details.outputs.prAction }} - prIsMerged: - description: "Whether PR is merged" - value: ${{ jobs.export_pr_details.outputs.prIsMerged }} - prLabels: - description: "PR Labels" - value: ${{ jobs.export_pr_details.outputs.prLabels }} - -permissions: - contents: read - -jobs: - export_pr_details: - permissions: - actions: read # download PR artifact - # see https://github.com/aws-powertools/powertools-lambda-python/issues/1349 - if: inputs.workflow_origin == 'aws-powertools/powertools-lambda-typescript' - runs-on: ubuntu-latest - env: - FILENAME: pr.txt - # Map the job outputs to step outputs - outputs: - prNumber: ${{ steps.prNumber.outputs.prNumber }} - prTitle: ${{ steps.prTitle.outputs.prTitle }} - prBody: ${{ steps.prBody.outputs.prBody }} - prAuthor: ${{ steps.prAuthor.outputs.prAuthor }} - prAction: ${{ steps.prAction.outputs.prAction }} - prIsMerged: ${{ steps.prIsMerged.outputs.prIsMerged }} - prLabels: ${{ steps.prLabels.outputs.prLabels }} - steps: - - name: Checkout repository # in case caller workflow doesn't checkout thus failing with file not found - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: "Download previously saved PR" - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - WORKFLOW_ID: ${{ inputs.record_pr_workflow_id }} - # For security, we only download artifacts tied to the successful PR recording workflow - with: - github-token: ${{ secrets.token }} - script: | - const script = require('.github/scripts/download_pr_artifact.js') - await script({github, context, core}) - # NodeJS standard library doesn't provide ZIP capabilities; use system `unzip` command instead - - name: "Unzip PR artifact" - run: unzip pr.zip - # NOTE: We need separate steps for each mapped output and respective IDs - # otherwise the parent caller won't see them regardless on how outputs are set. - - name: "Export Pull Request Number" - id: prNumber - run: echo prNumber="$(jq -c '.number' "${FILENAME}")" >> "$GITHUB_OUTPUT" - - name: "Export Pull Request Title" - id: prTitle - run: echo prTitle="$(jq -c '.pull_request.title' "${FILENAME}")" >> "$GITHUB_OUTPUT" - - name: "Export Pull Request Body" - id: prBody - run: echo prBody="$(jq -c '.pull_request.body' "${FILENAME}")" >> "$GITHUB_OUTPUT" - - name: "Export Pull Request Author" - id: prAuthor - run: echo prAuthor="$(jq -c '.pull_request.user.login' "${FILENAME}")" >> "$GITHUB_OUTPUT" - - name: "Export Pull Request Action" - id: prAction - run: echo prAction="$(jq -c '.action' "${FILENAME}")" >> "$GITHUB_OUTPUT" - - name: "Export Pull Request Merged status" - id: prIsMerged - run: echo prIsMerged="$(jq -c '.pull_request.merged' "${FILENAME}")" >> "$GITHUB_OUTPUT" - - name: "Export Pull Request labels" - id: prLabels - run: echo prLabels="$(jq -c '.labels' "${FILENAME}")" >> "$GITHUB_OUTPUT" \ No newline at end of file From 62bdef49ba688cd4335ac063ddab5058f9a781e3 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Thu, 5 Feb 2026 18:44:37 +0100 Subject: [PATCH 2/2] chore: remove boring cyborg automation for PRs --- .github/boring-cyborg.yml | 134 -------------------------------------- 1 file changed, 134 deletions(-) diff --git a/.github/boring-cyborg.yml b/.github/boring-cyborg.yml index bfc5c7b972..c51d6a0598 100644 --- a/.github/boring-cyborg.yml +++ b/.github/boring-cyborg.yml @@ -1,137 +1,3 @@ -##### Labeler ########################################################################################################## -labelPRBasedOnFilePath: - logger: - - packages/logger/src/* - - packages/logger/src/**/* - tracer: - - packages/tracer/src/* - - packages/tracer/src/**/* - metrics: - - packages/metrics/src/* - - packages/metrics/src/**/* - idempotency: - - packages/idempotency/src/* - - packages/idempotency/src/**/* - parameters: - - packages/parameters/src/* - - packages/parameters/src/**/* - parser: - - packages/parser/src/* - - packages/parser/src/**/* - event-handler: - - packages/event-handler/src/* - - packages/event-handler/src/**/* - validator: - - packages/validator/src/* - - packages/validator/src/**/* - batch: - - packages/batch/src/* - - packages/batch/src/**/* - commons: - - packages/commons/src/* - - packages/commons/src/**/* - - layers: - - layers/src/* - - layers/src/**/* - - layers/bin/* - - layers/cdk.json - - tests: - - packages/logger/tests/* - - packages/logger/tests/**/* - - packages/tracer/tests/* - - packages/tracer/tests/**/* - - packages/metrics/tests/* - - packages/metrics/tests/**/* - - packages/idempotency/tests/* - - packages/idempotency/tests/**/* - - packages/parameters/tests/* - - packages/parameters/tests/**/* - - packages/parser/tests/* - - packages/parser/tests/**/* - - packages/event-handler/tests/* - - packages/event-handler/tests/**/* - - packages/validator/tests/* - - packages/validator/tests/**/* - - packages/batch/tests/* - - packages/batch/tests/**/* - - packages/commons/tests/* - - packages/commons/tests/**/* - - layers/tests/* - - layers/tests/**/* - - documentation: - - docs/* - - docs/**/* - - mkdocs.yml - - examples/app/* - - automation: - - .github/scripts/* - - .github/scripts/**/* - - .github/workflows/* - - .github/workflows/**/* - - .github/actions/* - - .github/actions/**/* - - .github/release-drafter.yml - - .github/boring-cyborg.yml - - .github/semantic.yml - - internal: - - .github/CODEOWNERS - - .github/PULL_REQUEST_TEMPLATE.md - - .github/ISSUE_TEMPLATE/* - - CONTRIBUTING.md - - MAINTAINERS.md - - CODE_OF_CONDUCT.md - - SECURITY.md - - LICENSE - - LICENSE-THIRD-PARTY - - .nvmrc - - .biome.json - - .npmignore - - .gitpod.yml - - .husky/* - - .devcontainer/* - - packages/logger/tsconfig*.json - - packages/logger/README.md - - packages/metrics/tsconfig*.json - - packages/metrics/README.md - - packages/tracer/tsconfig*.json - - packages/tracer/README.md - - packages/parser/tsconfig*.json - - packages/parser/README.md - - packages/event-handler/tsconfig*.json - - packages/event-handler/README.md - - packages/idempotency/tsconfig*.json - - packages/idempotency/README.md - - packages/batch/tsconfig*.json - - packages/batch/README.md - - packages/commons/tsconfig*.json - - packages/commons/README.md - - packages/validator/tsconfig*.json - - packages/validator/README.md - - layers/tsconfig*.json - - layers/README.md - - examples/app/tsconfig*.json - - dependencies: - - package.json - - package-lock.json - - packages/tracer/package.json - - packages/metrics/package.json - - packages/logger/package.json - - packages/commons/package.json - - packages/parameters/package.json - - packages/idempotency/package.json - - packages/parser/package.json - - packages/event-handler/package.json - - packages/validator/package.json - - packages/batch/package.json - - layers/package.json - - examples/app/package.json - ##### Greetings ######################################################################################################## firstPRWelcomeComment: > Thanks a lot for your first contribution! Please check out our [contributing guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md) and don't hesitate to ask whatever you need.