mirror of
https://github.com/slsdetectorgroup/slsDetectorPackage.git
synced 2026-09-03 00:00:45 +02:00
Dev/pr automation part 1 (#1540)
Build and Deploy on local RHEL9 / build (push) Successful in 2m6s
Build on RHEL9 docker image / build (push) Successful in 3m50s
Build on RHEL8 docker image / build (push) Successful in 5m8s
Build and Deploy on local RHEL8 / build (push) Successful in 5m4s
Run Simulator Tests on local RHEL9 / build (push) Successful in 20m4s
Run Simulator Tests on local RHEL8 / build (push) Successful in 23m28s
Build and Deploy on local RHEL9 / build (push) Successful in 2m6s
Build on RHEL9 docker image / build (push) Successful in 3m50s
Build on RHEL8 docker image / build (push) Successful in 5m8s
Build and Deploy on local RHEL8 / build (push) Successful in 5m4s
Run Simulator Tests on local RHEL9 / build (push) Successful in 20m4s
Run Simulator Tests on local RHEL8 / build (push) Successful in 23m28s
* added pr validation * redundnat * validate type and add label * breaking api label also in pr validation * milestone workflow triggered when changing labels and it sets the milestone from the release targets yaml and labels * milestone workflow triggered when changing labels and it sets the milestone from the release targets yaml and labels * workflow changing label cannot trigger another workflow, so directly invoking it or if a user changes label for pr milestone workflow * added trigger workflow for label change by user, added check for infrastructure and breaking api in pr milestone workflow * typo * release targets * milestone workflow should also ensure only 1 primary labels * it doesnt need to be rerun for commits * label and unlabel do the checks as well of primary type and brekaing api, it does not need to be in milestone * pr validation now when primar type and breaking api labels are there and shouldnt be instead of removing them. the the label workflow nowjust ensures labesl and description are the same * node change * complain if labels dont match descriotion and retrigger validation workflow for better errors. If none labels, this is okay.. if not, it will give approperiate errors, mainly checking mismatch of labels and descriotion is labels trigger, logic will have been checked in validation anyway and failed if so * label trigger to run milestone only needs detect-mistmatch and does not need pr validation * getting rid of checkout for just reading a file from the base branch of PR, if main, give success for milestone * issues.update is deprecated for prs * removed label trigger workflow, it will not retrigger itself * made pr validation not a reusable workflow. it treiggers also on labeled and unlabeled, but ignores unrelated labels, too much resource to check if the checkboxes are edited * update from pulls back to issues when updating milestone as its not supported yet for pulls * setting a github rest api version of 2026, else it uses the old one of 2022 * didnt work. removing github api specs for now * more prints for miestone workflow and infrastructure is now bug fix milestone
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
major: "11.0.0"
|
||||
minor: "10.2.0"
|
||||
bug_fix: "10.1.1"
|
||||
@@ -0,0 +1,164 @@
|
||||
# This reusable workflow sets the milestone based on the
|
||||
# current PR labels.
|
||||
|
||||
name: PR Milestone
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
pr_number:
|
||||
required: true
|
||||
type: number
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
set-milestone:
|
||||
name: Set PR milestone
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Set milestone
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const prNumber = ${{ inputs.pr_number }};
|
||||
|
||||
/*
|
||||
* Get the base branch of the PR.
|
||||
*/
|
||||
const { data: pullRequest } =
|
||||
await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber
|
||||
});
|
||||
const baseBranch = pullRequest.base.ref;
|
||||
|
||||
/*
|
||||
* Return early if base is main branch. Milestone is already
|
||||
* managed and probably done when merging to main.
|
||||
*/
|
||||
if (baseBranch === 'main') {
|
||||
core.info('PR targets main. Milestone is managed separately.');
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read release targets through the GitHub API
|
||||
* from the base branch of PR.
|
||||
*/
|
||||
const { data: file } =
|
||||
await github.rest.repos.getContent({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
path: '.github/release-targets.yaml',
|
||||
ref: baseBranch
|
||||
});
|
||||
|
||||
const releaseTargetsFile =
|
||||
Buffer.from(file.content, 'base64').toString('utf8');
|
||||
|
||||
const getTarget = (name) => {
|
||||
const match = releaseTargetsFile.match(
|
||||
new RegExp(`^${name}:\\s*["']?([^"'\\s]+)["']?`, 'm')
|
||||
);
|
||||
|
||||
if (!match) {
|
||||
core.setFailed(
|
||||
`Release target "${name}" was not found.`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
return match[1];
|
||||
};
|
||||
|
||||
const major = getTarget('major');
|
||||
const minor = getTarget('minor');
|
||||
const bugFix = getTarget('bug_fix');
|
||||
|
||||
if (!major || !minor || !bugFix) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get current PR labels.
|
||||
*/
|
||||
const { data: labels } =
|
||||
await github.rest.issues.listLabelsOnIssue({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber
|
||||
});
|
||||
|
||||
const labelNames =
|
||||
labels.map(label => label.name);
|
||||
|
||||
/*
|
||||
* Determine the milestone.
|
||||
*
|
||||
* Breaking API has highest priority.
|
||||
*/
|
||||
let targetMilestone;
|
||||
|
||||
if (labelNames.includes('Breaking API')) {
|
||||
targetMilestone = major;
|
||||
} else if (labelNames.includes('Feature')) {
|
||||
targetMilestone = minor;
|
||||
} else if (
|
||||
labelNames.includes('Bug Fix') ||
|
||||
labelNames.includes('Infrastructure')
|
||||
) {
|
||||
targetMilestone = bugFix;
|
||||
} else {
|
||||
core.info(
|
||||
'No milestone-related labels found.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
core.info(
|
||||
`Target milestone: ${targetMilestone}`
|
||||
);
|
||||
|
||||
/*
|
||||
* Find the milestone.
|
||||
*/
|
||||
const { data: milestones } =
|
||||
await github.rest.issues.listMilestones({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open'
|
||||
});
|
||||
|
||||
const milestone = milestones.find(
|
||||
milestone => milestone.title === targetMilestone
|
||||
);
|
||||
|
||||
if (!milestone) {
|
||||
core.setFailed(
|
||||
`Milestone "${targetMilestone}" was not found.`
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
core.info(
|
||||
`Found milestone "${targetMilestone}".`
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the milestone.
|
||||
*/
|
||||
await github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
milestone: milestone.number
|
||||
});
|
||||
|
||||
core.info(
|
||||
`Milestone set to "${targetMilestone}".`
|
||||
);
|
||||
@@ -0,0 +1,218 @@
|
||||
# This workflow is triggered when opening/reopening PR or editing the Pr description or changing labels. It ignores unrelated label changes. It validates the PR type and Breaking API, then sets the labels. It also runs the PR milestone workflow to set the milestone based on the PR type label.
|
||||
name: PR Validation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- edited
|
||||
- labeled
|
||||
- unlabeled
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
validate-pr:
|
||||
name: Validate PR
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
set_milestone: ${{ steps.validate.outputs.set_milestone }}
|
||||
|
||||
steps:
|
||||
- name: Validate PR type and Breaking API
|
||||
id: validate
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
/*
|
||||
* Set it to run milestone at the end by default
|
||||
*/
|
||||
core.setOutput('set_milestone', 'true');
|
||||
|
||||
/*
|
||||
* If triggered by label change, ignore unrelated labels.
|
||||
*/
|
||||
const action = context.payload.action;
|
||||
if (action === 'labeled' || action === 'unlabeled') {
|
||||
const changedLabel = context.payload.label?.name;
|
||||
|
||||
const relevantLabels = [
|
||||
'Feature',
|
||||
'Bug Fix',
|
||||
'Infrastructure',
|
||||
'Breaking API'
|
||||
];
|
||||
|
||||
if (!relevantLabels.includes(changedLabel)) {
|
||||
core.info(
|
||||
`Ignoring unrelated label change: ${changedLabel}`
|
||||
);
|
||||
core.setOutput('set_milestone', 'false');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the body contents of PR
|
||||
*/
|
||||
const prNumber = context.payload.pull_request?.number;
|
||||
|
||||
if (!prNumber) {
|
||||
core.setFailed('Unable to determine PR number.');
|
||||
return;
|
||||
}
|
||||
|
||||
const { data: pr } =
|
||||
await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber
|
||||
});
|
||||
|
||||
const body = pr.body || '';
|
||||
|
||||
/*
|
||||
* Check which primary PR type is selected.
|
||||
*/
|
||||
const feature =
|
||||
/-\s*\[[xX]\]\s*Feature\b/.test(body);
|
||||
|
||||
const bugFix =
|
||||
/-\s*\[[xX]\]\s*Bug Fix\b/.test(body);
|
||||
|
||||
const infrastructure =
|
||||
/-\s*\[[xX]\]\s*Infrastructure\b/.test(body);
|
||||
|
||||
const primaryTypes = [
|
||||
feature && 'Feature',
|
||||
bugFix && 'Bug Fix',
|
||||
infrastructure && 'Infrastructure'
|
||||
].filter(Boolean);
|
||||
|
||||
/*
|
||||
* Exactly one primary PR type must be selected.
|
||||
*/
|
||||
if (primaryTypes.length !== 1) {
|
||||
core.setFailed(
|
||||
'Exactly one primary PR type must be selected. Select exactly one of: Feature, Bug Fix, Infrastructure in the description.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const primaryType = primaryTypes[0];
|
||||
|
||||
/*
|
||||
* Infrastructure PRs cannot be Breaking API.
|
||||
*/
|
||||
const breakingApi =
|
||||
/-\s*\[[xX]\]\s*This is a Breaking API change\b/.test(body);
|
||||
|
||||
if (infrastructure && breakingApi) {
|
||||
core.setFailed(
|
||||
'Infrastructure PRs cannot be Breaking API changes. Deselect Breaking API or change the PR type to Feature or Bug Fix in the description.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
core.info(`Primary PR type: ${primaryType}`);
|
||||
core.info(
|
||||
`Breaking API: ${breakingApi ? 'yes' : 'no'}`
|
||||
);
|
||||
|
||||
/*
|
||||
* Get the labels currently on the PR.
|
||||
*/
|
||||
const { data: currentLabels } =
|
||||
await github.rest.issues.listLabelsOnIssue({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber
|
||||
});
|
||||
|
||||
const currentLabelNames =
|
||||
currentLabels.map(label => label.name);
|
||||
|
||||
const primaryLabels = [
|
||||
'Feature',
|
||||
'Bug Fix',
|
||||
'Infrastructure'
|
||||
];
|
||||
|
||||
const selectedPrimary =
|
||||
primaryLabels.filter(label =>
|
||||
currentLabelNames.includes(label)
|
||||
);
|
||||
|
||||
if (selectedPrimary.length > 1) {
|
||||
core.setFailed(
|
||||
`Invalid PR label state: multiple primary labels are present: ${selectedPrimary.join(', ')}. Remove these primary type labels 'Feature, Bug Fix, Infrastructure' and let the workflow set the correct label based on the PR description.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedPrimary.length === 1 && selectedPrimary[0] !== primaryType) {
|
||||
core.setFailed(
|
||||
`Invalid PR label state: the PR body selects ${primaryType}, but the existing label is ${selectedPrimary[0]}. Remove the existing label and let the workflow set the correct label based on the PR description.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
currentLabelNames.includes('Breaking API') &&
|
||||
infrastructure
|
||||
) {
|
||||
core.setFailed(
|
||||
'Infrastructure PRs cannot have the Breaking API label.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!breakingApi &&
|
||||
currentLabelNames.includes('Breaking API')
|
||||
) {
|
||||
core.setFailed(
|
||||
'Breaking API label is present but the PR body does not mark the checkbox. Remove the label or check the checkbox in the PR body.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
breakingApi &&
|
||||
!currentLabelNames.includes('Breaking API')
|
||||
) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
labels: ['Breaking API']
|
||||
});
|
||||
|
||||
core.info('Added label: Breaking API');
|
||||
}
|
||||
|
||||
if (!currentLabelNames.includes(primaryType)) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
labels: [primaryType]
|
||||
});
|
||||
|
||||
core.info(`Added label: ${primaryType}`);
|
||||
}
|
||||
|
||||
core.info('PR validation passed.');
|
||||
|
||||
milestone:
|
||||
needs: validate-pr
|
||||
if: ${{ needs.validate-pr.outputs.set_milestone == 'true' }}
|
||||
uses: ./.github/workflows/pr_milestone.yaml
|
||||
with:
|
||||
pr_number: ${{ github.event.pull_request.number }}
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
Reference in New Issue
Block a user