Skip to content

junit5

junit5 #3631

# Licensed to the Apache Software Foundation (ASF) under one or more

Check warning on line 1 in .github/workflows/check-protected-classes.yml

View workflow run for this annotation

GitHub Actions / Protected Classes

Workflow execution policy warning (evaluate mode)

On November 2, 2026, GitHub will restrict `pull_request_target` on public repositories by default. To continue allowing the event trigger, configure an Actions policy. Learn more: https://gh.io/securely-using-pull_request_target#default-policy-for-pull_request_target
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: Protected Classes
# pull_request_target for the write token needed to comment/label the PR. The fork's changes are
# read through the API as data and never checked out or executed, so no allow-unsafe-pr-checkout and
# no working copy of the PR code on the runner. Do not add a step that builds or runs the PR code.
on: pull_request_target
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
check-protected-classes:
runs-on: ubuntu-latest
name: Rolling Upgrade check
permissions:
issues: write
pull-requests: write
checks: write
steps:
- name: Detect protected class changes and report
uses: actions/github-script@v8
with:
script: |
const ORDER_USE = '@Order(';
const ORDER_IMPORT = 'import org.apache.ignite.internal.Order;';
const ORDER_PKG = 'package org.apache.ignite.internal;';
const MARKER = '<!-- ignite-rolling-upgrade-check -->';
const { owner, repo } = context.repo;
const pr = context.payload.pull_request;
const baseSha = pr.base.sha;
const headSha = pr.head.sha;
// Changed files are read from the API as data; the fork's code is never checked out or executed.
const files = await github.paginate(github.rest.pulls.listFiles, {
owner, repo, pull_number: pr.number, per_page: 100,
});
// Reproduce `git diff --no-renames --diff-filter=ADM`: a rename is a delete(old)+add(new) pair.
// A protected class carries the @Order annotation; an added file is defined by the head revision,
// a deleted/modified one by the base.
const revisions = [];
for (const f of files) {
if (f.status === 'added' || f.status === 'copied')
revisions.push([f.filename, headSha]);
else if (f.status === 'removed' || f.status === 'modified' || f.status === 'changed')
revisions.push([f.filename, baseSha]);
else if (f.status === 'renamed') {
revisions.push([f.previous_filename, baseSha]);
revisions.push([f.filename, headSha]);
}
}
const isProtected = async (path, ref) => {
let meta;
try {
({ data: meta } = await github.rest.repos.getContent({ owner, repo, path, ref }));
} catch (e) {
if (e.status === 404) return false;
throw e;
}
if (Array.isArray(meta) || meta.type !== 'file') return false;
let content = meta.content ? Buffer.from(meta.content, 'base64').toString('utf8') : '';
if (!content && meta.sha) {
const { data: blob } = await github.rest.git.getBlob({ owner, repo, file_sha: meta.sha });
content = Buffer.from(blob.content, blob.encoding).toString('utf8');
}
const lines = content.split(/\r?\n/);
return content.includes(ORDER_USE)
&& (lines.includes(ORDER_IMPORT) || lines.includes(ORDER_PKG));
};
const hits = [];
for (const [path, ref] of revisions) {
if (path.endsWith('.java')
&& !path.startsWith('modules/core/src/test/resources/codegen/')
&& await isProtected(path, ref)) hits.push(path);
}
if (hits.length === 0) return;
// File names come from the fork; render them as inert inline code so they cannot inject
// markdown (backticks, @mentions, links) into content posted under the bot's write token.
const safe = f => '`' + String(f).replace(/[`\r\n]/g, '') + '`';
const list = hits.map(f => '- ' + safe(f)).join('\n');
const summary = [
'This PR modifies protected classes (with **Order** annotation).',
'Changes to these classes can break rolling upgrade compatibility.',
'',
'**Affected files:**',
list,
].join('\n');
const body = MARKER + '\n## Possible compatibility issues. Please, check rolling upgrade cases\n\n' + summary + '\n';
const comments = await github.paginate(github.rest.issues.listComments, {
owner, repo, issue_number: pr.number,
});
const existing = comments.find(c => c.body.includes(MARKER));
if (existing) {
await github.rest.issues.deleteComment({ owner, repo, comment_id: existing.id });
}
await github.rest.issues.createComment({ owner, repo, issue_number: pr.number, body });
await github.rest.issues.addLabels({
owner, repo, issue_number: pr.number, labels: ['compatibility'],
});
await github.rest.checks.create({
owner, repo,
name: 'Rolling upgrade compatibility',
head_sha: headSha,
status: 'completed',
conclusion: 'neutral',
output: { title: 'Possible compatibility issues', summary },
});