Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve mavenLicenseCheck workflow #372

Closed
Closed
Show file tree
Hide file tree
Changes from all 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
20 changes: 11 additions & 9 deletions .github/actions/maven-license-check-action/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,22 @@ runs:
shell: bash {0} # do not fail-fast
run: |
mvnArgs="-U -B -ntp org.eclipse.dash:license-tool-plugin:license-check -Ddash.fail=true -Dtycho.target.eager=true --settings $GITHUB_ACTION_PATH/licenseCheckMavenSettings.xml"
if [[ ${{ inputs.project-id }} != "" ]]; then
mvnArgs+=" -Ddash.repo=https://github.com/${{ github.repository }}"

if [[ "${{ inputs.project-id }}" != "" ]]; then
mvnArgs+=" -Ddash.projectId=${{ inputs.project-id }}"
fi
if [ ${{ inputs.request-review }} ]; then
if [ "${{ inputs.request-review }}" != "" ]; then
mvn ${mvnArgs} -Ddash.iplab.token=$GITLAB_API_TOKEN
if [[ $? == 0 ]]; then # All licenses are vetted
echo "build-succeeded=1" >> $GITHUB_OUTPUT
else
echo "build-succeeded=0" >> $GITHUB_OUTPUT
fi
else
mvn ${mvnArgs}
if [[ $? != 0 ]]; then
fi

if [[ $? == 0 ]]; then # All licenses are vetted
echo "build-succeeded=1" >> $GITHUB_OUTPUT
else
if [[ "${{ inputs.request-review }}" = "" ]]; then
echo "Committers can request a review by commenting '/request-license-review'"
exit 1
fi
echo "build-succeeded=0" >> $GITHUB_OUTPUT
fi
53 changes: 53 additions & 0 deletions .github/workflows/addLicenseCheckCommentForForks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Add license check comment for PRs coming from a fork

on:
workflow_call:

jobs:
add-pr-comment-for-forks:
runs-on: ubuntu-latest
permissions:
pull-requests: write
if: |
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.head_repository.full_name != github.repository
steps:
- name: 'Download artifact'
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
var artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{github.event.workflow_run.id }},
});
var matchArtifact = artifacts.data.artifacts.filter((artifact) => {
return artifact.name == "pr-comment"
})[0];
var download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
var fs = require('fs');
fs.writeFileSync('${{github.workspace}}/pr-comment.zip', Buffer.from(download.data));

- run: unzip pr-comment.zip

- name: 'Comment on PR'
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
var fs = require('fs');

const issue_number = Number(fs.readFileSync('./pr.txt'));
const body = fs.readFileSync('./comment.txt', { encoding: 'utf8', flag: 'r' });

await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue_number,
body: body
});
234 changes: 169 additions & 65 deletions .github/workflows/mavenLicenseCheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,29 +50,43 @@ on:
required: false

jobs:
check-licenses:
if: github.event_name != 'issue_comment' || ( github.event.issue.pull_request != '' && (github.event.comment.body == '/request-license-review') )
check-request:
# Run on all non-comment events specified by the calling workflow and for comments on PRs that have a corresponding body.
if: >
github.event_name != 'issue_comment' ||
(github.event.issue.pull_request &&
(github.event.comment.body == '/request-license-review' ||
github.event.comment.body == '/license-check'))
runs-on: ubuntu-latest
permissions:
pull-requests: write
outputs:
request-review: ${{ steps.request-review.outputs.request-review }}
license-check: ${{ steps.license-check.outputs.license-check }}
steps:

- name: Check dependabot PR
run: echo "isDependabotPR=1" >> $GITHUB_ENV
- name: Check dependabot PR
if: >
github.event_name == 'pull_request' && (github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.action == 'reopened')
github.event_name == 'pull_request'
&& (github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.action == 'reopened')
&& github.actor == 'dependabot[bot]' && github.actor_id == '49699333'
# For 'issue_comment'-events this job only runs if a comment was added to a PR with body specified above
run: echo "isDependabotPR=1" >> $GITHUB_ENV

# For 'issue_comment'-events this job only runs if a comment was added to a PR with body specified above
- name: Set review request
run: echo "request-review=1" >> $GITHUB_ENV
if: github.event_name == 'issue_comment' || env.isDependabotPR
# For 'issue_comment'-events this job only runs if a comment was added to a PR with body specified above
id: request-review
if: (github.event_name == 'issue_comment' && contains(github.event.comment.body, '/request-license-review')) || env.isDependabotPR
run: |
echo "request-review=1" >> "$GITHUB_OUTPUT"

- name: Set license check
id: license-check
if: (github.event_name == 'issue_comment' && contains(github.event.comment.body, '/license-check')) || env.isDependabotPR
run: |
echo "license-check=1" >> "$GITHUB_OUTPUT"

- name: Process license-vetting request
if: env.request-review && (!env.isDependabotPR)
uses: actions/github-script@v7
if: |
(steps.request-review.outputs.request-review || steps.license-check.outputs.license-check)
&& (!env.isDependabotPR)
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const payload = await github.rest.repos.getCollaboratorPermissionLevel({
Expand All @@ -90,27 +104,41 @@ jobs:
...context.repo, comment_id: context.payload?.comment?.id, content: reaction
});

# By default the git-ref checked out for events triggered by comments to PRs is 'refs/heads/master'

check-licenses:
needs: check-request
if: ${{needs.check-request.outputs.license-check == ''}}
runs-on: ubuntu-latest
permissions:
pull-requests: write
env:
request-review: ${{ needs.check-request.outputs.request-review }}
license-check: ${{ needs.check-request.outputs.license-check }}
comment-header: '<!-- tag-license-comment -->'
steps:
# By default, the git-ref checked out for events triggered by comments to PRs is 'refs/heads/master'
# and for events triggered by PR creation/updates the ref is 'refs/pull/<PR-number>/merge'.
# So by default only the master-branch would be considered when requesting license-reviews, but we want the PR's state.
# Unless the PR is closed, then we want the master-branch, which allows subsequent license review requests.
- uses: actions/checkout@v4
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
# use default ref 'refs/pull/<PR-number>/merge' for PR-events and 'refs/heads/master' for comments if the PR is closed
if: github.event.issue.pull_request == '' || github.event.issue.state != 'open'
with:
submodules: ${{ inputs.submodules }}
- uses: actions/checkout@v4

- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
if: github.event.issue.pull_request != '' && github.event.issue.state == 'open'
with:
ref: 'refs/pull/${{ github.event.issue.number }}/merge'
submodules: ${{ inputs.submodules }}
if: github.event.issue.pull_request != '' && github.event.issue.state == 'open'

- uses: actions/setup-java@v4
- uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # v4.3.0
with:
java-version: ${{ inputs.javaVersion }}
distribution: 'temurin'

- name: Cache local Maven repository
uses: actions/cache@v4
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
with:
path: ~/.m2/repository
# re-cache on changes in the pom and target files
Expand All @@ -124,75 +152,151 @@ jobs:
maven-version: ${{ inputs.mavenVersion }}

- name: Prepare for license check
run: ${{ inputs.setupScript }}
if: inputs.setupScript !=''
run: ${{ inputs.setupScript }}

- name: Check license vetting status (and ask for review if requested)
id: check-license-vetting
uses: eclipse-dash/dash-licenses/.github/actions/maven-license-check-action@master
uses: eclipse-dash/dash-licenses/.github/actions/maven-license-check-action@07a2f0e44c1948918f6ba3e5a0aa1b52b1413847 # master
with:
request-review: ${{ env.request-review }}
project-id: ${{ inputs.projectId }}
env:
GITLAB_API_TOKEN: ${{ secrets.gitlabAPIToken }}

- name: Process license check results
if: env.request-review
uses: actions/github-script@v7
id: process-results
if: always()
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
result-encoding: string
script: |
const fs = require('fs')

const licenesVetted = ${{ steps.check-license-vetting.outputs.licenses-vetted }}
let commentBody = ''
// if context.payload.comment is empty, this is an explicit review-request through a comment, if not an automated one, e.g. for dependabot PRs
if ( context.payload.comment ) {
commentBody += '> ' + context.payload.comment.body + '\n\n'
} else if ( licenesVetted ){
core.info('License review request made automatically but all licenses are already vetted.')
return; // Don't create a comment in this case, the checks in the UI indicate the state already.
} else {
// This run encountered pending reviews, which have been requested automatically, e.g. for dependabot PRs
core.setFailed("Some dependencies must be vetted and their review was requested. Rerun this check once these reviews succeeded.")
}

if( licenesVetted ) {
commentBody += ':heavy_check_mark: All licenses already successfully vetted.\n'
const reviewRequested = ${{ env.request-review || false }}
const updateRequested = reviewRequested || ${{ env.license-check || false }}
const licensesVetted = ${{ steps.check-license-vetting.outputs.licenses-vetted }}

let commentBody = "### License summary\n"
if (licensesVetted) {
if (updateRequested) {
commentBody += ':heavy_check_mark: All licenses already successfully vetted.\n'
} else {
// Do not comment if all licenses are vetted and no update was requested
core.info('All licenses are already vetted.')
return;
}
} else {

const reviewSummaryFile = process.env.GITHUB_WORKSPACE + '/target/dash/review-summary'
core.info("Read review summary at " + reviewSummaryFile)
// Print dependency info
const dependencySummaryFile = 'target/dash/summary'
core.info("Read dependency summary at " + dependencySummaryFile)
let content = "";
if ( fs.existsSync( reviewSummaryFile )) {
content = fs.readFileSync( reviewSummaryFile, {encoding: 'utf8'}).trim();
if (fs.existsSync(dependencySummaryFile)) {
content = fs.readFileSync(dependencySummaryFile, { encoding: 'utf8' }).trim();
}

if ( content ) { // not empty
commentBody += 'License review requests:\n'

if (content) { // not empty
commentBody += ":x: Not yet vetted dependencies:\n"
commentBody += "| Dependency | License | Status | Ticket |\n"
commentBody += "|------------|---------|--------|--------|\n"
const lines = content.split('\n')
for(var line = 0; line < lines.length; line++){
commentBody += ('- ' + lines[line] + '\n')
let notVettedDependencies = 0
for (const line of lines) {
if (line.includes('restricted')) {
depLine = `| ${line.split(", ").join(" | ")} |\n`
commentBody += depLine.replace(/#([0-9]+)/gm, "[#\$1](https://gitlab.eclipse.org/eclipsefdn/emo-team/iplab/-/issues/\$1)")
notVettedDependencies++
}
}
core.setFailed(`${notVettedDependencies} dependencies are not vetted yet.`)
} else {
commentBody += ':warning: Failed to process summary.\n'
core.setFailed('Failed to process dash summary')
}

if (reviewRequested) {
const reviewSummaryFile = "target/dash/review-summary"
let reviews = "";
if (fs.existsSync(reviewSummaryFile)) {
reviews = fs.readFileSync(reviewSummaryFile, { encoding: 'utf8' }).trim();
}
if (reviews) { // not empty
commentBody += "\n### :rocket: Requested reviews:\n"
const lines = reviews.split('\n')
for (const line of lines) {
commentBody += `- ${line}\n`
}
} else {
core.setFailed("License vetting build failed, but no reviews are created")
commentBody += ':warning: Failed to request review of not vetted licenses.\n'
}
commentBody += '\n'
commentBody += 'After all reviews have concluded, re-run the license-vetting check from the Github Actions web-interface to update its status.\n'

} else {
core.setFailed("License vetting build failed, but no reviews are created")
commentBody += ':warning: Failed to request review of not vetted licenses.\n'
commentBody += '\n\n- Committers can request a license review via by commenting `/request-license-review`.\n- After all reviews have concluded, Committers can re-run the license-vetting check by commenting `/license-check`\n'
}
}
commentBody += '\n'
commentBody += 'Workflow run (with attached summary files):\n'
commentBody += context.serverUrl + "/" + process.env.GITHUB_REPOSITORY + "/actions/runs/" + context.runId

github.rest.issues.createComment({
issue_number: context.issue.number, ...context.repo, body: commentBody
})

- uses: actions/upload-artifact@v4
if: always() && env.request-review

commentBody += `\nWorkflow run (with attached summary files):\n${context.serverUrl}/${process.env.GITHUB_REPOSITORY}/actions/runs/${context.runId}`
return commentBody

- name: Adding comment to job summary
if: always()
run: |
echo '${{steps.process-results.outputs.result}}' >> $GITHUB_STEP_SUMMARY

- name: Determine comment header
if: ${{env.request-review}}
run: echo "comment-header='<!-- tag-review-request-comment -->'" >> "$GITHUB_ENV"

# Add the process result as comment to the PR if an update has been requested
# or if the PR is not coming from a fork (in which case we don't have write tokens)
- name: Adding comment to PR
uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 # v2.9.0
if: |
always()
&& (github.event_name == 'issue_comment' || github.event.pull_request.head.repo.full_name == github.repository)
with:
header: ${{env.comment-header}}
hide_and_recreate: true
hide_classify: "OUTDATED"
number: ${{github.event.issue.number}}
message: |
${{steps.process-results.outputs.result}}

- name: Store PR comment and PR number
if: always()
env:
PR: ${{github.event.issue.number || github.event.pull_request.number}}
run: |
mkdir -p ./pr-comment
echo ${PR} > ./pr-comment/pr.txt
echo '${{steps.process-results.outputs.result}}' > ./pr-comment/comment.txt

- uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
if: always()
with:
name: '${{ inputs.projectId }}-license-vetting-summary'
path: |
target/dash/review-summary
target/dash/summary

- uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
if: always()
with:
name: 'pr-comment'
path: |
pr-comment/pr.txt
pr-comment/comment.txt

rerun-check:
needs: check-request
if: ${{needs.check-request.outputs.license-check == '1'}}
runs-on: ubuntu-latest
permissions:
actions: write
steps:
- name: Rerun license check
run: |
HEAD_SHA=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }} | jq -r '.head | .sha')
RUNID=$(gh api repos/${{ github.repository }}/commits/${HEAD_SHA}/check-runs | jq -r '.check_runs[] | select(.name | endswith("check-licenses")) | .html_url | capture("/runs/(?<number>[0-9]+)/job") | .number' | sed 's/"//g' | head -n 1)
gh run rerun ${RUNID} --repo ${{ github.repository }}
env:
GH_TOKEN: ${{ github.token }}
Loading
Loading