core
core copied to clipboard
Investigate Title Updates Based on Labels
This is only required if we find the solution above requires the developer to remember or know the available list of actions and scopes.
Tasks:
- Research existing solutions for updating PR titles based on labels
- If no suitable solution exists, outline a custom GitHub Action to achieve this. Make use of AI to help.
- Test the chosen or proposed solution in our test branch
Possible Custom Action Steps
- Define labels for conventional commits actions and scopes and a "breaking" label
- When title is created or updated pass it to a github actions workflow
- Extract any existing conventional commit prefix from the message using a regular expression. If there is no prefix then just use the message as is. Be aware of the posibility of colon within the message.
feat(api)!: send an email to the customer when a product is shipped
- create the conventional commit prefix based on the labels
- Recreate the title by appending the conventional commit prefix to the message.
- Be aware of the possibility that auto-updating the title could trigger the same action to be triggered. An unvalidated AI example of this task is provided below
name: Update PR Title Based on Labels
on:
pull_request:
types: [opened, edited, labeled, unlabeled]
jobs:
update-title:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/github-script@v6
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const core = require('@actions/core');
const github = require('@actions/github');
async function run() {
try {
const { pull_request } = github.context.payload;
// Get current labels
const labelsResponse = await github.rest.issues.listLabelsOnIssue({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
issue_number: pull_request.number,
});
const currentLabels = labelsResponse.data.map(label => label.name);
// Extract type and scope from labels
const typeLabel = currentLabels.find(label => label.startsWith('type:'));
const scopeLabel = currentLabels.find(label => label.startsWith('scope:'));
const isBreaking = currentLabels.includes('breaking');
const type = typeLabel ? typeLabel.split(':')[1].trim() : null;
const scope = scopeLabel ? scopeLabel.split(':')[1].trim() : null;
// Extract existing title components
const titleRegex = /^(?:(\w+)(?:\((\w+)\))?(?:!)?:)?\s*(.+)$/;
const [, existingType, existingScope, message] = pull_request.title.match(titleRegex);
// Prepare new title components
const newType = type || existingType || 'chore';
const newScope = scope || existingScope || '';
const breakingChange = isBreaking ? '!' : '';
// Construct new title
const newTitle = `${newType}${newScope ? `(${newScope})` : ''}${breakingChange}: ${message}`;
// Update PR title if it has changed
if (newTitle !== pull_request.title) {
await github.rest.pulls.update({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
pull_number: pull_request.number,
title: newTitle
});
console.log(`Updated PR title to: ${newTitle}`);
} else {
console.log('PR title is already up to date.');
}
} catch (error) {
core.setFailed(error.message);
}
}
run();
Key Considerations:
- How well does this integrate with conventional commit enforcement?
- Does it provide a user-friendly way for developers to set types and scopes?
- Can it automatically update the PR title when labels change?