Skip to content

Add labeler-dependabot.yml to auto-label Dependabot PRs by update type#81

Open
Copilot wants to merge 4 commits intomainfrom
copilot/add-labeler-dependabot-workflow
Open

Add labeler-dependabot.yml to auto-label Dependabot PRs by update type#81
Copilot wants to merge 4 commits intomainfrom
copilot/add-labeler-dependabot-workflow

Conversation

Copy link
Copy Markdown

Copilot AI commented Mar 24, 2026

dependabot.yml cannot apply different labels to security update PRs vs. version update PRs within the same ecosystem block. This workflow fills that gap without requiring a PAT.

Changes

  • .github/workflows/labeler-dependabot.yml — new workflow that:
    • Fires on pull_request (all event types), scoped to dependabot[bot] only
    • Short-circuits at the job level if either Dependabot label is already present on the PR
    • Optionally ensures the custom labels exist via an ENSURE_LABELS_EXIST env var toggle (defaults to 'true'; set to 'false' when labels are managed centrally)
    • Detects security vs. version update PRs via two signals in the PR body — security update PRs always contain [Security Alerts page] or You can disable automated security fix PRs; version update PRs never contain either phrase
    • Applies dependabot-security-update for security update PRs, dependabot-version-update for version update PRs using the GitHub API

Permissions

Scope Reason
pull-requests: write Apply labels to PRs and create labels via the Issues API

Detection approach

dependabot/fetch-metadata's alert-lookup feature (which populates ghsa-id) requires a fine-grained PAT — it does not work with GITHUB_TOKEN, so ghsa-id is always empty with the standard token. Security vs. version update detection instead relies on phrases present in all Dependabot security update PR bodies:

  • Security update PR body always contains: [Security Alerts page] or You can disable automated security fix PRs
  • Version update PR body never contains either phrase

This was verified against real PRs: a security update PR and two version update PRs.

Original prompt

Create a pull request in repository advanced-security/reusable-workflows that adds a new GitHub Actions workflow named .github/workflows/labeler-dependabot.yml to automatically label Dependabot pull requests by type.

Requirements:

  • Follow the guidance from GitHub Docs for customizing Dependabot PRs and labeling them with custom labels, specifically the workflow pattern using Dependabot metadata: dependabot/fetch-metadata plus label application logic.
  • Use the filename labeler-dependabot.yml because the repository already has a different labeler for another paradigm.
  • Add logic to distinguish between:
    • Dependabot security update PRs -> apply label dependabot-security-update
    • Dependabot version update PRs -> apply label dependabot-version-update
  • Ensure the workflow only operates on Dependabot pull requests.
  • Prefer a clear, maintainable implementation aligned with official GitHub guidance.

Please also address the case where the labels do not yet exist on the repository:

  • Implement the workflow so it behaves sensibly if labels are missing.
  • If necessary, create the labels automatically in the workflow before applying them, or otherwise make the behavior explicit and robust.
  • Include any permissions needed for labeling and, if applicable, label creation.

Expected deliverables:

  • The new workflow file committed in the PR.
  • If additional supporting changes are needed for correctness or maintainability, include them.
  • In the PR description or changes, make clear how missing labels are handled.

The following is the prior conversation context from the user's chat exploration (may be truncated):

User: how to distinguish between a dependabot security pr vs a dependabot version update pr?

can you do a custom label per type via dependabot.yml ?

Assistant: Yes:

  1. Security PRs are created in response to a Dependabot alert/security advisory.
  2. Version update PRs are created on the schedule in dependabot.yml (daily/weekly/monthly). About Dependabot pull requests

And for your second question: not directly per PR type with separate labels keys in the same update block. The labels: setting on an updates: entry can apply to both version updates and security updates for that ecosystem, rather than letting you define one label set for security PRs and a different label set for version PRs in that same block. Customizing pull requests for Dependabot security updates About Dependabot pull requests

How to distinguish them in practice

  • Security update PR
    • Triggered by a known vulnerability / Dependabot alert.
    • Targets the default branch only.
    • Not driven by your configured schedule. About Dependabot pull requests
  • Version update PR
    • Triggered by the scheduled update checks in dependabot.yml.
    • Follows settings like schedule, open-pull-requests-limit, and target-branch where configured. About Dependabot pull requests

About custom labels per type

Typical workaround
Use a GitHub Actions workflow that watches Dependabot PRs and applies labels based on whether the PR is a security update or a version update.

If you want, I can draft a GitHub Actions workflow that auto-labels:

  • security PRs with dependencies-security
  • version PRs with dependencies-version

User: ok is there an action that does this workaround already?

Assistant: Yes — the main e...

This pull request was created from Copilot chat.


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

Copilot AI changed the title [WIP] Add GitHub Actions workflow to label Dependabot PRs Add labeler-dependabot.yml to auto-label Dependabot PRs by update type Mar 24, 2026
Copilot AI requested a review from felickz March 24, 2026 15:38
@felickz
Copy link
Copy Markdown
Collaborator

felickz commented Mar 24, 2026

@copilot i enabled https://api.github.com/repos/dependabot/fetch-metadata/ in your firewall

I need you to test this against a version PR: #80 and advanced-security/component-detection-dependency-submission-action#184

and a security pr: advanced-security/component-detection-dependency-submission-action#174

metadata looks like this which doesnt appear to help with security vs version:

Run dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a
Parsing Dependabot metadata
Outputting metadata for 1 updated dependency
  outputs.dependency-names: lodash
  outputs.dependency-type: direct:production
  outputs.update-type: version-update:semver-minor
  outputs.directory: /
  outputs.package-ecosystem: npm_and_yarn
  outputs.target-branch: main
  outputs.previous-version: 4.0.1
  outputs.new-version: 4.17.23
  outputs.compatibility-score: 0
  outputs.maintainer-changes: false
  outputs.dependency-group: 
  outputs.alert-state: 
  outputs.ghsa-id: 
  outputs.cvss: 0

There is a comment in the PR that might distinguish though:

  • version like: (unless you unignore this specific dependency's minor version or upgrade to it yourself)
  • security like: You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/advanced-security/component-detection-dependency-submission-action/network/alerts).

Copy link
Copy Markdown
Author

Copilot AI commented Mar 24, 2026

@copilot i enabled https://api.github.com/repos/dependabot/fetch-metadata/ in your firewall

I need you to test this against a version PR: https://github.com/advanced-security/reu...

Good catch — alert-lookup: true requires a fine-grained PAT, so ghsa-id is always empty when using GITHUB_TOKEN. That's why the approach didn't work.

Fixed in f71a111. The workflow now detects update type via PR body text:

Changes made:

  • Removed alert-lookup: true and security-events: read permission
  • Detection now uses contains(github.event.pull_request.body, 'You can disable automated security fix PRs') as the if condition on both label steps

@felickz
Copy link
Copy Markdown
Collaborator

felickz commented Mar 24, 2026

@copilot - how does this version look? any paradigms we can borrow for our script? (ex the check on existing labels first to short circuit?)

on:
  pull_request:

permissions:
  contents: read
  pull-requests: write


env:
  ENSURE_LABELS_EXIST: 'true' # Set to 'false' to skip label creation   

jobs:
  label-dependabot-pr:
    if: >-
      github.actor == 'dependabot[bot]' &&
      !contains(github.event.pull_request.labels.*.name, 'dependabot-security-update') &&
      !contains(github.event.pull_request.labels.*.name, 'dependabot-version-update')
    runs-on: ubuntu-latest
    steps:
      - name: Ensure labels exist
        if: env.ENSURE_LABELS_EXIST == 'true'
        uses: actions/github-script@v7
        with:
          script: |
            const labels = ['dependabot-security-update', 'dependabot-version-update'];
            const existing = await github.rest.issues.listLabelsForRepo({
              owner: context.repo.owner,
              repo: context.repo.repo,
              per_page: 100
            });
            const existingNames = existing.data.map(l => l.name);

            for (const label of labels) {
              if (!existingNames.includes(label)) {
                await github.rest.issues.createLabel({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  name: label
                });
                core.info(`Created label: ${label}`);
              }
            }

      - name: Check if PR is from Dependabot alert
        id: check-alert
        uses: actions/github-script@v7
        with:
          script: |
            const prBody = context.payload.pull_request.body || '';
            const isSecurityUpdate = prBody.includes('[Security Alerts page]') ||
              prBody.includes('You can disable automated security fix PRs');

            core.info(`PR #${context.payload.pull_request.number} is ${isSecurityUpdate ? '' : 'not '}a security update`);
            core.setOutput('is-dependabot-security-update', isSecurityUpdate ? 'true' : 'false');

      - name: Label PR based on Dependabot alert type
        uses: actions/github-script@v7
        with:
          script: |
            const isSecurityUpdate = '${{ steps.check-alert.outputs.is-dependabot-security-update }}' === 'true';
            const label = isSecurityUpdate ? 'dependabot-security-update' : 'dependabot-version-update';

            core.info(`Adding label: ${label} to PR #${context.payload.pull_request.number}`);

            await github.rest.issues.addLabels({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.payload.pull_request.number,
              labels: [label]
            });

Copy link
Copy Markdown
Author

Copilot AI commented Mar 24, 2026

@copilot - how does this version look? any paradigms we can borrow for our script? (ex the check on existing labels first to short circuit?)

...

Adopted all the paradigms from your snippet in 90f9e3e:

  • Job-level short-circuit: if on the job now skips entirely if either Dependabot label is already on the PR
  • ENSURE_LABELS_EXIST toggle: env var at the top, defaults to 'true'; set to 'false' to skip label creation when labels are managed centrally
  • actions/github-script@v7 for both label creation and label application (no more gh CLI)
  • Two-signal security detection: prBody.includes('[Security Alerts page]') || prBody.includes('You can disable automated security fix PRs')
  • Collapsed the two separate label steps into one combined detect+apply step
  • Removed the now-unused dependabot/fetch-metadata step entirely
  • Dropped issues: write (label creation/application via the Issues REST API requires only pull-requests: write in this context)

@felickz felickz marked this pull request as ready for review March 24, 2026 20:45
@felickz felickz requested a review from a team as a code owner March 24, 2026 20:45
@felickz felickz requested review from adrienpessu and Copilot March 24, 2026 20:45
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 24, 2026

Dependency Review

The following issues were found:
  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 1 package(s) with unknown licenses.
See the Details below.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 90f9e3e.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

License Issues

.github/workflows/labeler-dependabot.yml

PackageVersionLicenseIssue Type
actions/github-script7.*.*NullUnknown License

OpenSSF Scorecard

PackageVersionScoreDetails
actions/actions/github-script 7.*.* 🟢 7.7
Details
CheckScoreReason
Code-Review🟢 10all changesets reviewed
Maintained🟢 1013 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 10
Packaging⚠️ -1packaging workflow not detected
Binary-Artifacts🟢 10no binaries found in the repo
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Token-Permissions🟢 9detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Pinned-Dependencies⚠️ 1dependency not pinned by hash detected -- score normalized to 1
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Security-Policy🟢 9security policy file detected
Branch-Protection🟢 5branch protection is not maximal on development and all release branches
SAST🟢 10SAST tool is run on all commits

Scanned Files

  • .github/workflows/labeler-dependabot.yml

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a GitHub Actions workflow to automatically label Dependabot pull requests as either security updates or version updates, enabling label differentiation that isn’t possible within a single dependabot.yml ecosystem block.

Changes:

  • Introduces .github/workflows/labeler-dependabot.yml to detect Dependabot PR type and apply dependabot-security-update vs dependabot-version-update.
  • Adds optional label auto-creation controlled by ENSURE_LABELS_EXIST.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


permissions:
contents: read
pull-requests: write
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

github.rest.issues.* endpoints (label creation + applying labels) require the issues: write permission on GITHUB_TOKEN. With only pull-requests: write, these API calls can fail with 403 in repos where issues write isn’t implicitly granted. Add issues: write (and keep/remove pull-requests as needed).

Suggested change
pull-requests: write
pull-requests: write
issues: write

Copilot uses AI. Check for mistakes.
Comment on lines +39 to +43
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label
});
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issues.createLabel is missing required fields (notably color). As written, label creation will fail when the labels don’t already exist. Provide a color (and optionally description) when creating the labels.

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +44
const existing = await github.rest.issues.listLabelsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
per_page: 100
});
const existingNames = existing.data.map(l => l.name);

for (const label of labels) {
if (!existingNames.includes(label)) {
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label
});
core.info(`Created label: ${label}`);
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

listLabelsForRepo is limited to per_page: 100 with no pagination. If the repo has >100 labels, an existing label may not be found, leading to a failing createLabel call. Consider using issues.getLabel per label, paginate, or catch/ignore the “already exists” error (422).

Suggested change
const existing = await github.rest.issues.listLabelsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
per_page: 100
});
const existingNames = existing.data.map(l => l.name);
for (const label of labels) {
if (!existingNames.includes(label)) {
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label
});
core.info(`Created label: ${label}`);
// Fetch all labels with pagination to avoid the 100-label per-page limit.
const existingLabels = await github.paginate(
github.rest.issues.listLabelsForRepo,
{
owner: context.repo.owner,
repo: context.repo.repo,
per_page: 100
}
);
const existingNames = existingLabels.map(l => l.name);
for (const label of labels) {
if (!existingNames.includes(label)) {
try {
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label
});
core.info(`Created label: ${label}`);
} catch (error) {
// Ignore "already exists" errors (HTTP 422), rethrow others.
if (error.status === 422) {
core.info(`Label already exists, skipping creation: ${label}`);
} else {
throw error;
}
}

Copilot uses AI. Check for mistakes.
runs-on: ubuntu-latest
# Only run for Dependabot PRs that have not already been labeled
if: >-
github.actor == 'dependabot[bot]' &&
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The job filter uses github.actor == 'dependabot[bot]'. That ties execution to who triggered the event, not who authored the PR, so Dependabot PRs may not get labeled on event types initiated by humans (e.g., edited) or when re-running the workflow. Prefer checking github.event.pull_request.user.login == 'dependabot[bot]' (and/or the PR author association) to ensure it always targets Dependabot PRs.

Suggested change
github.actor == 'dependabot[bot]' &&
github.event.pull_request.user.login == 'dependabot[bot]' &&

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +66
core.info(`PR #${context.payload.pull_request.number} is ${isSecurityUpdate ? '' : 'not '}a security update — adding label: ${label}`);

await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
labels: [label]
});
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When ENSURE_LABELS_EXIST is set to 'false' and the labels don’t exist, issues.addLabels will fail with a generic API error. Consider handling that case explicitly (e.g., check label existence before labeling or catch the error and emit a clear message explaining how to create the missing labels).

Suggested change
core.info(`PR #${context.payload.pull_request.number} is ${isSecurityUpdate ? '' : 'not '}a security update — adding label: ${label}`);
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
labels: [label]
});
const prNumber = context.payload.pull_request.number;
core.info(`PR #${prNumber} is ${isSecurityUpdate ? '' : 'not '}a security update — adding label: ${label}`);
try {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
labels: [label]
});
} catch (error) {
const ensureLabels = process.env.ENSURE_LABELS_EXIST;
const hint = ensureLabels === 'false'
? "The label may not exist because ENSURE_LABELS_EXIST is set to 'false'. Create the label in the repository's Labels settings, or set ENSURE_LABELS_EXIST to 'true' so this workflow can create it automatically."
: "Ensure the label exists in the repository's Labels settings, or set ENSURE_LABELS_EXIST to 'true' so this workflow can create it automatically.";
core.setFailed(`Failed to add label '${label}' to PR #${prNumber}. ${hint} Original error: ${error.message}`);
}

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +5
on:
pull_request:

Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description states the workflow (a) runs on pull_request “all event types” and (b) follows the documented dependabot/fetch-metadata pattern, but the workflow currently uses default pull_request event types and doesn’t call dependabot/fetch-metadata. Either update the workflow to match (e.g., specify types: and/or add fetch-metadata) or adjust the PR description to reflect the implemented approach.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants