Skip to content

[maven-release-plugin] prepare release v7.0.0 #623

[maven-release-plugin] prepare release v7.0.0

[maven-release-plugin] prepare release v7.0.0 #623

Workflow file for this run

on:
push:
branches:
- main
- release/**
- develop
- feature/**
pull_request:
types: [opened, synchronize, reopened]
branches:
- main
- release/**
- develop
- feature/**
merge_group:
workflow_call:
inputs:
ref:
description: 'Git ref to checkout (branch, tag, or SHA)'
required: false
type: string
skip_code_scans:
description: 'Skip CodeQL and Trivy security scans'
required: false
default: false
type: boolean
skip_linkcheck:
description: 'Skip website link checker'
required: false
default: false
type: boolean
linkcheck_fail_on_error:
description: 'a boolean flag that determines if bad links found by the link checker fail fast and stop a complete build'
required: false
default: true
type: boolean
linkcheck_create_issue:
description: 'create new GitHub issue if broken links found'
required: false
default: false
type: boolean
workflow_dispatch:
inputs:
ref:
description: 'Git ref to checkout (branch, tag, or SHA)'
required: false
type: string
skip_code_scans:
description: 'Skip CodeQL and Trivy security scans'
required: false
default: false
type: boolean
skip_linkcheck:
description: 'Skip website link checker'
required: false
default: false
type: boolean
linkcheck_fail_on_error:
description: 'a boolean flag that determines if bad links found by the link checker fail fast and stop a complete build'
required: false
default: true
type: boolean
linkcheck_create_issue:
description: 'create new GitHub issue if broken links found'
required: false
default: false
type: boolean
name: Build and Test
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
INPUT_FAIL_ON_ERROR: ${{ inputs.linkcheck_fail_on_error || 'true' }}
INPUT_ISSUE_ON_ERROR: ${{ inputs.linkcheck_create_issue || 'false' }}
# Effective ref for link checker remap: use inputs.ref if provided, else base_ref (PRs) or ref_name (pushes), fallback to develop
EFFECTIVE_REF: ${{ inputs.ref || github.base_ref || github.ref_name || 'develop' }}
MAVEN_VERSION: 3.9.8
JAVA_DISTRO: 'temurin'
JAVA_VERSION_FILE: .java-version
# Post Maven artifacts to the artifact repo if the branch is 'develop' or 'release/*'. This avoids publishing artifacts for pull requests
COMMIT_MAVEN_ARTIFACTS: ${{ (github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/release/')) && github.repository_owner == 'metaschema-framework' }}
# Upload security scan SARIF results for main, develop, release/* branches, or PRs targeting these branches.
UPLOAD_SCAN_SARIF: ${{ (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/release/')) || (github.event_name == 'pull_request' && (github.base_ref == 'main' || github.base_ref == 'develop' || startsWith(github.base_ref, 'release/'))) }}
jobs:
build-code:
name: Code
runs-on: ubuntu-24.04
permissions:
actions: read
contents: read
security-events: write
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
with:
ref: ${{ inputs.ref || github.ref }}
submodules: recursive
filter: tree:0
- name: Checkout maven2 branch
if: env.COMMIT_MAVEN_ARTIFACTS == 'true'
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
with:
path: maven2
ref: main
repository: metaschema-framework/maven2
token: ${{ secrets.ACCESS_TOKEN }}
fetch-depth: 2
persist-credentials: true
# -------------------------
# Java Environment Setup
# -------------------------
- name: Set up Maven
uses: stCarolas/setup-maven@d6af6abeda15e98926a57b5aa970a96bb37f97d1
with:
maven-version: ${{ env.MAVEN_VERSION }}
- name: Set up JDK
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e
with:
java-version-file: ${{ env.JAVA_VERSION_FILE }}
distribution: ${{ env.JAVA_DISTRO }}
cache: 'maven'
- name: Initialize CodeQL
if: ${{ !inputs.skip_code_scans }}
uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7
with:
languages: java
config-file: .github/codeql-config.yml
# -------------------------
# Maven Build
# -------------------------
- name: Build and Test Code
run: |
mvn -B -e -Prelease -Psnapshots -DaltDeploymentRepository=repo-snapshot::file://${GITHUB_WORKSPACE}/maven2/ -DaltSnapshotDeploymentRepository=repo-snapshot::file://${GITHUB_WORKSPACE}/maven2/ -DrepositoryId=repo-snapshot deploy
- name: Deploy Artifacts
if: env.COMMIT_MAVEN_ARTIFACTS == 'true'
run: |
MVN_COORDS=$(echo '${project.groupId}:${project.artifactId}:${project.version}' | mvn -N -q -DforceStdout help:evaluate)
cd maven2
echo "Configuring git identity"
git config user.name "GitHub Action"
git config user.email "action@github.com"
echo "Comitting artifacts"
git add -A
git commit -m "[CI SKIP] Deploying artifacts for $MVN_COORDS."
echo "Syncing with latest"
git pull -r -s ours
echo "Pushing changes"
git push --force-with-lease
- name: Perform CodeQL Analysis
if: ${{ !inputs.skip_code_scans }}
uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7
with:
upload: 'never'
output: codeql-results
- name: CodeQL Summary
if: ${{ !inputs.skip_code_scans }}
run: |
echo "## CodeQL Security Scan Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ -d "codeql-results" ]; then
for sarif in codeql-results/*.sarif; do
if [ -f "$sarif" ]; then
FILENAME=$(basename "$sarif" .sarif)
RESULTS=$(jq -r '.runs[0].results | length' "$sarif" 2>/dev/null || echo "0")
# Count rules from driver and all extensions
DRIVER_RULES=$(jq -r '.runs[0].tool.driver.rules // [] | length' "$sarif" 2>/dev/null || echo "0")
EXT_RULES=$(jq -r '[.runs[0].tool.extensions[]?.rules // [] | length] | add // 0' "$sarif" 2>/dev/null || echo "0")
RULES=$((DRIVER_RULES + EXT_RULES))
echo "**Language:** $FILENAME" >> $GITHUB_STEP_SUMMARY
echo "- Results found: $RESULTS" >> $GITHUB_STEP_SUMMARY
echo "- Rules checked: $RULES" >> $GITHUB_STEP_SUMMARY
# Show details if there are findings
if [ "$RESULTS" -gt 0 ]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "<details>" >> $GITHUB_STEP_SUMMARY
echo "<summary>View $RESULTS finding(s)</summary>" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Severity | Sec-Sev | Rule | Location | Message |" >> $GITHUB_STEP_SUMMARY
echo "|----------|---------|------|----------|---------|" >> $GITHUB_STEP_SUMMARY
# Join results with rules to get security-severity (which is on rule definitions, not results)
jq -r '
(.runs[0].tool.driver.rules // []) as $driver_rules |
([.runs[0].tool.extensions[]?.rules // []] | add // []) as $ext_rules |
($driver_rules + $ext_rules | map({(.id): (.properties["security-severity"] // null)}) | add // {}) as $severities |
.runs[0].results[] |
"| \(if .level == "error" then "Error" elif .level == "warning" then "Warning" elif .level == "note" then "Note" else .level end) | \($severities[.ruleId] // "N/A") | \(.ruleId // "unknown") | `\(.locations[0].physicalLocation.artifactLocation.uri // "unknown"):\(.locations[0].physicalLocation.region.startLine // "?")` | \(.message.text | gsub("\n"; " ") | gsub("\\|"; "\\\\|") | .[0:120]) |"
' "$sarif" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "</details>" >> $GITHUB_STEP_SUMMARY
fi
fi
done
else
echo "No CodeQL results directory found." >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ env.UPLOAD_SCAN_SARIF }}" == "true" ]; then
echo ":white_check_mark: Results uploaded to GitHub Security tab" >> $GITHUB_STEP_SUMMARY
else
echo ":information_source: Results not uploaded (branch/PR not targeting main, develop, or release)" >> $GITHUB_STEP_SUMMARY
fi
# -------------------------
# Trivy Security Scan
# -------------------------
- name: Run Trivy security scanner
if: ${{ !inputs.skip_code_scans }}
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8
with:
scan-type: 'fs'
scan-ref: '.'
scanners: 'vuln,secret,misconfig'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH,MEDIUM,LOW,UNKNOWN'
# Exclude submodule (has its own security scanning in upstream repo)
skip-dirs: 'oscal'
- name: Trivy Summary
if: ${{ !inputs.skip_code_scans }}
run: |
echo "## Trivy Security Scan Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ -f "trivy-results.sarif" ]; then
TOTAL=$(jq -r '.runs[0].results | length' trivy-results.sarif 2>/dev/null || echo "0")
# Trivy SARIF level mapping (from trivy source): CRITICAL+HIGH -> error, MEDIUM -> warning, LOW+UNKNOWN -> note
CRITICAL_HIGH=$(jq -r '[.runs[0].results[] | select(.level == "error")] | length' trivy-results.sarif 2>/dev/null || echo "0")
MEDIUM=$(jq -r '[.runs[0].results[] | select(.level == "warning")] | length' trivy-results.sarif 2>/dev/null || echo "0")
LOW=$(jq -r '[.runs[0].results[] | select(.level == "note")] | length' trivy-results.sarif 2>/dev/null || echo "0")
echo "| Severity | Count |" >> $GITHUB_STEP_SUMMARY
echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| :red_circle: Critical/High | $CRITICAL_HIGH |" >> $GITHUB_STEP_SUMMARY
echo "| :orange_circle: Medium | $MEDIUM |" >> $GITHUB_STEP_SUMMARY
echo "| :yellow_circle: Low | $LOW |" >> $GITHUB_STEP_SUMMARY
echo "| **Total** | **$TOTAL** |" >> $GITHUB_STEP_SUMMARY
# Show details if there are findings
if [ "$TOTAL" -gt 0 ]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "<details>" >> $GITHUB_STEP_SUMMARY
echo "<summary>View $TOTAL finding(s)</summary>" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Severity | Rule | Location | Message |" >> $GITHUB_STEP_SUMMARY
echo "|----------|------|----------|---------|" >> $GITHUB_STEP_SUMMARY
jq -r '.runs[0].results[] | "| \(if .level == "error" then "Critical/High" elif .level == "warning" then "Medium" elif .level == "note" then "Low" else .level end) | \(.ruleId // "unknown") | `\(.locations[0].physicalLocation.artifactLocation.uri // "unknown"):\(.locations[0].physicalLocation.region.startLine // "?")` | \(.message.text | gsub("\n"; " ") | gsub("\\|"; "\\\\|") | .[0:120]) |"' trivy-results.sarif >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "</details>" >> $GITHUB_STEP_SUMMARY
fi
else
echo "No Trivy results file found." >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ env.UPLOAD_SCAN_SARIF }}" == "true" ]; then
echo ":white_check_mark: Results uploaded to GitHub Security tab" >> $GITHUB_STEP_SUMMARY
else
echo ":information_source: Results not uploaded (branch/PR not targeting main, develop, or release)" >> $GITHUB_STEP_SUMMARY
fi
- name: Upload CodeQL scan results to GitHub Security tab
if: ${{ !inputs.skip_code_scans && env.UPLOAD_SCAN_SARIF == 'true' }}
uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7
with:
sarif_file: codeql-results
category: 'codeql'
- name: Upload Trivy scan results to GitHub Security tab
if: ${{ !inputs.skip_code_scans && env.UPLOAD_SCAN_SARIF == 'true' }}
uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7
with:
sarif_file: 'trivy-results.sarif'
category: 'trivy'
- name: Fail on critical/high security findings
if: ${{ !inputs.skip_code_scans }}
run: |
FAILED=false
# Check CodeQL for error level (critical/high) findings
if [ -d "codeql-results" ]; then
for sarif in codeql-results/*.sarif; do
if [ -f "$sarif" ]; then
# Check for error level OR security-severity >= 7.0 (high/critical)
# Note: security-severity is on rule definitions, not results, so we join via ruleId
CODEQL_CRITICAL=$(jq -r '
# Collect security-severity from driver and extension rules
(.runs[0].tool.driver.rules // []) as $driver_rules |
([.runs[0].tool.extensions[]?.rules // []] | add // []) as $ext_rules |
($driver_rules + $ext_rules | map({(.id): (.properties["security-severity"] // "0")}) | add // {}) as $severities |
[.runs[0].results[] | select(
.level == "error" or
(($severities[.ruleId] // "0") | tonumber >= 7.0)
)] | length
' "$sarif" 2>/dev/null || echo "0")
if [ "$CODEQL_CRITICAL" -gt 0 ]; then
echo "::error::CodeQL found $CODEQL_CRITICAL critical/high severity issue(s)"
FAILED=true
fi
fi
done
fi
# Check Trivy for critical/high (error) and medium (warning) findings
# Note: Trivy SARIF mapping: CRITICAL+HIGH -> error, MEDIUM -> warning, LOW -> note
if [ -f "trivy-results.sarif" ]; then
TRIVY_CRITICAL_HIGH=$(jq -r '[.runs[0].results[] | select(.level == "error")] | length' trivy-results.sarif 2>/dev/null || echo "0")
TRIVY_MEDIUM=$(jq -r '[.runs[0].results[] | select(.level == "warning")] | length' trivy-results.sarif 2>/dev/null || echo "0")
if [ "$TRIVY_CRITICAL_HIGH" -gt 0 ]; then
echo "::error::Trivy found $TRIVY_CRITICAL_HIGH critical/high severity issue(s)"
FAILED=true
fi
if [ "$TRIVY_MEDIUM" -gt 0 ]; then
echo "::warning::Trivy found $TRIVY_MEDIUM medium severity issue(s)"
fi
fi
if [ "$FAILED" = true ]; then
echo "::error::Build failed due to critical/high security findings. See summaries above for details."
exit 1
fi
build-website:
name: Website
runs-on: ubuntu-24.04
permissions:
actions: read
contents: write
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
with:
ref: ${{ inputs.ref || github.ref }}
submodules: recursive
filter: tree:0
# -------------------------
# Java Environment Setup
# -------------------------
- name: Set up Maven
uses: stCarolas/setup-maven@d6af6abeda15e98926a57b5aa970a96bb37f97d1
with:
maven-version: ${{ env.MAVEN_VERSION }}
- name: Set up JDK
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e
with:
java-version-file: ${{ env.JAVA_VERSION_FILE }}
distribution: ${{ env.JAVA_DISTRO }}
cache: 'maven'
# -------------------------
# Maven Build
# -------------------------
- name: Build and Test Website
run: |
mvn -B -e -PCI -Prelease install site site:stage -Dmaven.test.skip=true
- name: Zip Artifacts for Upload
run: |
zip ${{ runner.temp }}/website.zip -r target/staging
- name: Upload generated site
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
with:
name: website
path: |
${{ runner.temp }}/website.zip
retention-days: 5
- id: linkchecker
name: Link Checker
if: ${{ !inputs.skip_linkcheck }}
uses: lycheeverse/lychee-action@a8c4c7cb88f0c7386610c35eb25108e448569cb0
with:
args: --verbose --no-progress --accept 200,206,429,503 './target/staging/**/*.html' --remap "https://github.com/metaschema-framework/liboscal-java/tree/${{ env.EFFECTIVE_REF }}/ file://${GITHUB_WORKSPACE}/" --remap "https://liboscal-java.metaschema.dev/ file://${GITHUB_WORKSPACE}/target/staging/"
format: markdown
output: html-link-report.md
debug: true
fail: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
- name: Link Checker Summary
if: ${{ !inputs.skip_linkcheck && always() }}
run: |
echo "## Link Checker Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ -f "html-link-report.md" ]; then
# Extract summary stats from the report
# Lychee uses [ERROR], [4xx], [5xx] for broken links and [TIMEOUT] for timeouts
# Note: grep -c exits 1 when no matches, so we capture output first then handle exit code
ERRORS=$(grep -cE "^\[ERROR\]|^\[[45][0-9]{2}\]" html-link-report.md 2>/dev/null) || ERRORS=0
TIMEOUTS=$(grep -c "^\[TIMEOUT\]" html-link-report.md 2>/dev/null) || TIMEOUTS=0
if [ "$ERRORS" -gt 0 ]; then
echo ":x: **Found $ERRORS broken link(s)**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "<details>" >> $GITHUB_STEP_SUMMARY
echo "<summary>View broken links</summary>" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
grep -E "^\[ERROR\]|^\[[45][0-9]{2}\]" html-link-report.md >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "</details>" >> $GITHUB_STEP_SUMMARY
elif [ "$TIMEOUTS" -gt 0 ]; then
echo ":warning: **$TIMEOUTS link(s) timed out** (external sites may be slow)" >> $GITHUB_STEP_SUMMARY
else
echo ":white_check_mark: **All links valid**" >> $GITHUB_STEP_SUMMARY
fi
else
echo ":warning: No link check report found." >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
- name: Upload link check report
if: ${{ !inputs.skip_linkcheck }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
with:
name: html-link-report
path: html-link-report.md
retention-days: 5
- name: Create issue if bad links detected
# Only create issues for actual broken links (exit code 2), not timeouts (exit code 1)
if: ${{ !inputs.skip_linkcheck && !cancelled() && steps.linkchecker.outputs.exit_code == '2' && env.INPUT_ISSUE_ON_ERROR == 'true' }}
uses: peter-evans/create-issue-from-file@fca9117c27cdc29c6c4db3b86c48e4115a786710
with:
title: Scheduled Check of Website Content Found Bad Hyperlinks
content-filepath: html-link-report.md
labels: |
bug
documentation
- name: Fail on link check error
# Check report for actual broken links (ERROR, 4xx, 5xx), not timeouts
if: ${{ !inputs.skip_linkcheck && !cancelled() && env.INPUT_FAIL_ON_ERROR == 'true' }}
run: |
if [ -f "html-link-report.md" ]; then
ERRORS=$(grep -cE "^\[ERROR\]|^\[[45][0-9]{2}\]" html-link-report.md 2>/dev/null) || ERRORS=0
if [ "$ERRORS" -gt 0 ]; then
echo "::error::Link checker found $ERRORS broken link(s). See report for details."
exit 1
fi
fi