Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
cli:
- changed-files:
- any-glob-to-any-file:
- 'Sources/CLI/**/*'
- 'Sources/ContainerCommands/**/*'
63 changes: 63 additions & 0 deletions .github/workflows/color-labels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Color Labels
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we need this. We can make them by hand for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

should i remvoe the colors file

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes


on:
push:
branches:
- main
paths:
- '.github/workflows/color-labels.yml'
workflow_dispatch:

permissions:
issues: write
Copy link
Contributor

Choose a reason for hiding this comment

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

See #781, the top level permissions should always be the least permissive possible and any additional permissions should be set at the job level.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I changed the permissiion to read only


jobs:
create-labels:
name: Create labels with colors
runs-on: ubuntu-latest

steps:
- name: Create or update labels
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const labels = [
{ name: 'cli', color: '0E8A16', description: 'Changes to CLI components' },
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of trying to encode the different label categories into the workflow job itself, maybe instead we could use the PR number parameter to actions/labeler in the label-apply workflow like this:

    - uses: actions/labeler@v6
      with:        
        pr-number: |
          1
          <PR number we got from the artifact download>

We should double check that the labeler doesn't try to read the untrusted code. But my understanding is that the action does not checkout the PR code, just analyzes based on the files that have been changed.

Note: I think it would be unsafe to read the labeler's configuration file from the PR, we might have to settle for using the configuration in main. But we should look into that as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestion, Katie! I've updated the workflows to use actions/labeler@v5 with the PR number from the artifact as you recommended.

];
for (const label of labels) {
try {
await github.rest.issues.getLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label.name
});
await github.rest.issues.updateLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label.name,
color: label.color,
description: label.description
});
console.log(`✅ Updated label: ${label.name}`);
} catch (error) {
if (error.status === 404) {
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label.name,
color: label.color,
description: label.description
});
console.log(`✅ Created label: ${label.name}`);
} else {
throw error;
}
}
}
console.log('🎉 All labels created/updated successfully!');
50 changes: 50 additions & 0 deletions .github/workflows/pr-label-analysis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: PR Label Analysis

on:
pull_request:
types: [opened]

permissions:
contents: read

jobs:
analyze:
name: Analyze PR for labeling
runs-on: ubuntu-latest

steps:
- name: Checkout PR branch
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0

- name: Get changed files and build labels
id: build-labels
run: |
git fetch origin ${{ github.event.pull_request.base.ref }}

CHANGED_FILES=$(git diff --name-only origin/${{ github.event.pull_request.base.ref }}...HEAD)

echo "Changed files:"
echo "$CHANGED_FILES"

LABELS=""

if echo "$CHANGED_FILES" | grep -qE '^Sources/CLI/|^Sources/ContainerCommands/'; then
LABELS="${LABELS}cli,"
fi

- name: Save PR metadata
run: |
mkdir -p ./pr-metadata
echo "${{ github.event.pull_request.number }}" > ./pr-metadata/pr-number.txt
echo "${{ steps.build-labels.outputs.labels }}" > ./pr-metadata/labels.txt
echo "${{ github.event.pull_request.head.sha }}" > ./pr-metadata/pr-sha.txt

- name: Upload PR metadata as artifact
uses: actions/upload-artifact@v4
with:
name: pr-metadata-${{ github.event.pull_request.number }}
path: pr-metadata/
retention-days: 1
129 changes: 129 additions & 0 deletions .github/workflows/pr-label-apply.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
name: PR Label Apply

on:
workflow_run:
workflows: ["PR Label Analysis"]
types:
- completed

permissions:
contents: read
pull-requests: write
issues: write

jobs:
apply-labels:
name: Apply labels to PR
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}

steps:
- name: Download PR metadata artifact
uses: actions/download-artifact@v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
pattern: pr-metadata-*
merge-multiple: false
continue-on-error: true
id: download-artifact

- name: Check if artifact exists
id: check-artifact
run: |
METADATA_DIR=$(find . -type d -name "pr-metadata-*" 2>/dev/null | head -n 1)

if [ -z "$METADATA_DIR" ]; then
echo "artifact-exists=false" >> $GITHUB_OUTPUT
exit 0
fi

echo "artifact-exists=true" >> $GITHUB_OUTPUT
echo "metadata-dir=${METADATA_DIR}" >> $GITHUB_OUTPUT

- name: Read PR metadata
if: steps.check-artifact.outputs.artifact-exists == 'true'
id: pr-metadata
run: |
METADATA_DIR="${{ steps.check-artifact.outputs.metadata-dir }}"

PR_NUMBER=$(cat "${METADATA_DIR}/pr-number.txt")
LABELS=$(cat "${METADATA_DIR}/labels.txt" || echo "")
PR_SHA=$(cat "${METADATA_DIR}/pr-sha.txt" || echo "")

echo "pr-number=${PR_NUMBER}" >> $GITHUB_OUTPUT
echo "labels=${LABELS}" >> $GITHUB_OUTPUT
echo "pr-sha=${PR_SHA}" >> $GITHUB_OUTPUT

- name: Apply labels
if: steps.check-artifact.outputs.artifact-exists == 'true' && steps.pr-metadata.outputs.labels != ''
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const prNumber = parseInt('${{ steps.pr-metadata.outputs.pr-number }}');
const labelsString = '${{ steps.pr-metadata.outputs.labels }}';

if (!labelsString) return;

const labels = labelsString.split(',').map(l => l.trim()).filter(l => l !== '');
if (labels.length === 0) return;

try {
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});

const currentLabels = pr.labels.map(l => l.name);
const newLabels = labels.filter(l => !currentLabels.includes(l));

if (newLabels.length === 0) {
console.log('All labels already applied');
return;
}

await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
labels: newLabels
});

core.setOutput('labels-applied', newLabels.join(','));
core.setOutput('should-comment', 'true');

} catch (error) {
if (error.status === 404) return;
throw error;
}
id: apply-labels

- name: Post comment
if: steps.apply-labels.outputs.should-comment == 'true'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const prNumber = parseInt('${{ steps.pr-metadata.outputs.pr-number }}');
const labelsString = '${{ steps.apply-labels.outputs.labels-applied }}';

if (!labelsString) return;

const appliedLabels = labelsString.split(',').map(l => l.trim()).filter(l => l !== '');
if (appliedLabels.length === 0) return;

const labelBadges = appliedLabels.map(l => `\`${l}\``).join(', ');
const comment = `🏷️ **Auto-labeler** has applied the following labels: ${labelBadges}`;

try {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: comment
});
} catch (error) {
console.error('Failed to post comment:', error.message);
}