Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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
4 changes: 3 additions & 1 deletion .github/actions/detect-file-changes/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ runs:
using: "node20"
main: "detect-file-changes.js"
outputs:
changes_detected:
changed:
description: "Returns `true` if any changed file matches the specified patterns, `false` otherwise."
changed_files:
description: "Returns a space-separated list of all changed files that match the specified patterns, or an empty list if none match."
14 changes: 11 additions & 3 deletions .github/actions/detect-file-changes/detect-file-changes.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,15 +232,23 @@ function fileMatches(file, compiled) {
changedFiles.forEach(f => console.log(`- ${f}`));

// Check if any file matches
const matched = changedFiles.some(f => fileMatches(f, compiled));
const matchedFiles = changedFiles.filter(f => fileMatches(f, compiled));
const matched = matchedFiles.length > 0;

// Write GitHub Action output
// Write GitHub Action outputs
fs.appendFileSync(
process.env.GITHUB_OUTPUT,
`changed=${matched ? 'true' : 'false'}\n`
);

console.log(`Files match filter → ${matched}`);
// Write the list of matched files as a multiline output
fs.appendFileSync(
process.env.GITHUB_OUTPUT,
`changed_files=${matchedFiles.join(' ')}\n`
);

console.log(`Files matching filter (${matchedFiles.length}):`);
matchedFiles.forEach(f => console.log(`- ${f}`));
} catch (err) {
console.log(`file-filter encountered an error: ${err?.message || err}`);
process.exit(0); // Non-fatal for CI
Expand Down
90 changes: 90 additions & 0 deletions .github/workflows/index-file-validation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: "Validate index.json files"

on:
pull_request:

jobs:
validate-json:
name: "📋 Validate changed JSON files"
runs-on: ubuntu-22.04
timeout-minutes: 10

steps:
- name: "☁️ Checkout repository"
uses: actions/checkout@v4

- name: "🔎 Detect relevant file changes"
id: filter
uses: ./.github/actions/detect-file-changes
with:
file-patterns: |
- 'metadata/index.json'
- 'metadata/*/*/index.json'
- 'tests/src/index.json'
- 'tests/src/*/*/*/index.json'
github_token: ${{ secrets.GITHUB_TOKEN }}

- name: "Setup Python"
if: steps.filter.outputs.changed == 'true'
uses: actions/setup-python@v4
with:
python-version: '3.10'

- name: "Install validation tools"
if: steps.filter.outputs.changed == 'true'
run: |
pip install jq check-jsonschema

- name: "Check that the changed index.json files conform to their schemas"
if: steps.filter.outputs.changed == 'true'
run: |
set -e

# Treat changed_files as a proper array
FILES=(${{ steps.filter.outputs.changed_files }})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should always strive to do this in gradle. That way I can also do it locally to verify everything is OK.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. I've removed this file-producing part of the GitHub action and created a generateChangedIndexFileMatrix gradle task that will do this in gradle (and is locally test-able). As these tasks require a ci.json entry, I've added it as only tested on java: latest-ea and os: ubuntu-latest as not to produce multiple jobs (because there shouldn't be any change with different jdks for this functionality as we don't run native-image).


# Track failures
FAILURES=()

for FILE in "${FILES[@]}"; do

# Determine schema for each file
if [[ "$FILE" == "metadata/index.json" ]]; then
SCHEMA="schemas/metadata-root-index-schema-v1.0.0.json"
elif [[ "$FILE" == metadata/*/*/index.json ]]; then
SCHEMA="schemas/metadata-library-index-schema-v1.0.0.json"
elif [[ "$FILE" == "tests/src/index.json" ]]; then
SCHEMA="schemas/tests-root-index-schema-v1.0.0.json"
elif [[ "$FILE" == tests/src/*/*/*/index.json ]]; then
SCHEMA="schemas/tests-project-index-schema-v1.0.0.json"
else
echo "ℹ️ Skipping: $FILE (no schema mapping)"
continue
fi

echo "🔍 Validating $FILE using $SCHEMA"

# Step 1: Check JSON well-formedness
if ! jq -e . "$FILE" >/dev/null; then
echo "❌ $FILE is not valid JSON"
FAILURES+=("$FILE (invalid JSON)")
continue
fi

# Step 2: Validate against schema
if ! check-jsonschema --schemafile "$SCHEMA" "$FILE"; then
FAILURES+=("$FILE (schema validation failed)")
continue
fi

echo "✅ $FILE validated successfully"
done

# Fail at the end if any errors occurred
if [ ${#FAILURES[@]} -ne 0 ]; then
echo "::error ::Some files failed validation:"
for f in "${FAILURES[@]}"; do
echo "::error :: $f"
done
exit 1
fi
5 changes: 3 additions & 2 deletions .github/workflows/library-and-framework-list-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ jobs:
uses: ./.github/actions/detect-file-changes
with:
file-patterns: |
- 'library-and-framework-list*.json'
- 'library-and-framework-list.json'
- 'schemas/library-and-framework-list-schema*.json'

- uses: actions/setup-python@v4
with:
Expand All @@ -43,4 +44,4 @@ jobs:
if: steps.filter.outputs.changed == 'true'
run: |
pip install check-jsonschema
check-jsonschema --schemafile library-and-framework-list-schema.json library-and-framework-list.json
check-jsonschema --schemafile schemas/library-and-framework-list-schema-v1.0.0.json library-and-framework-list.json
11 changes: 9 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,17 @@ tasks.register('package', Zip) { task ->
task.destinationDirectory = layout.buildDirectory

from(tck.metadataRoot)
// library-and-framework-list.json is used by Native Build Tools to provide additional
// information on library and framework support to the native image Build Report

from(project.rootDir) {
// library-and-framework-list.json is used by Native Build Tools to provide additional
// information on library and framework support to the native image Build Report
include("library-and-framework-list.json")

// Schemas are included in the ZIP root to allow Native Build Tools to verify
// structural compatibility with this release of GraalVM Reachability Metadata
include("schemas/metadata-root-index-schema-v1.0.0.json")
include("schemas/metadata-library-index-schema-v1.0.0.json")
include("schemas/library-and-framework-list-schema-v1.0.0.json")
}
}

Expand Down
2 changes: 1 addition & 1 deletion docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,4 +344,4 @@ Where:

**Note:** To pass format and style checks, please run `sorted="$(jq -s '.[] | sort_by(.artifact)' library-and-framework-list.json)" && echo -E "${sorted}" > library-and-framework-list.json` before submitting a PR.

**Note:** The entries you add will be validated against [library-and-framework-list-schema.json](https://github.com/oracle/graalvm-reachability-metadata/blob/master/library-and-framework-list-schema.json)
**Note:** The entries you add will be validated against [library-and-framework-list-schema-v1.0.0.json](https://github.com/oracle/graalvm-reachability-metadata/blob/master/schemas/library-and-framework-list-schema-v1.0.0.json)
100 changes: 100 additions & 0 deletions schemas/metadata-library-index-schema-v1.0.0.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
{
"$id": "https://github.com/oracle/graalvm-reachability-metadata/schemas/metadata-library-index.schema.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "Schema for metadata/<groupId>/<artifactId>/index.json. Each entry describes a metadata bundle for a range of library versions.",
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"module",
"metadata-version",
"tested-versions"
],
"properties": {
"module": {
"$ref": "#/$defs/moduleCoordinate",
"description": "Maven coordinates in the form '<groupId>:<artifactId>'."
},
"metadata-version": {
"type": "string",
"minLength": 1,
"description": "Subdirectory name where the metadata files for this entry reside, e.g. '7.1.0.Final'."
},
"tested-versions": {
"type": "array",
"description": "Explicitly tested upstream library versions that this metadata is known to support.",
"minItems": 1,
"uniqueItems": true,
"items": {
"type": "string",
"minLength": 1
}
},
"latest": {
"type": "boolean",
"description": "Marks this entry as the latest/default metadata for currently supported versions."
},
"default-for": {
"type": "string",
"description": "Java regular expression describing the version range for which this entry should be used by default (e.g. '7\\\\.1\\\\..*')."
},
"override": {
"type": "boolean",
"description": "When true, expresses the intent to exclude outdated built-in metadata shipped with Native Image for the matched versions."
},
"skipped-versions": {
"type": "array",
"description": "Versions explicitly excluded from support, each with a reason.",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": [ "version", "reason" ],
"properties": {
"version": {
"type": "string",
"minLength": 1
},
"reason": {
"type": "string",
"minLength": 1
}
}
}
}
}
},
"$defs": {
"moduleCoordinate": {
"type": "string",
"pattern": "^[^:]+:[^:]+$"
}
},
"examples": [
[
{
"metadata-version": "0.0.1",
"module": "org.example:library",
"tested-versions": [ "0.0.1", "0.0.2" ],
"default-for": "0\\.0\\..*"
},
{
"latest": true,
"metadata-version": "1.0.0",
"module": "org.example:library",
"tested-versions": [ "1.0.0", "1.1.0-M1", "1.1.0" ]
},
{
"metadata-version": "1.19.0",
"module": "io.opentelemetry:opentelemetry-exporter-logging",
"tested-versions": [ "1.19.0" ],
"override": true,
"skipped-versions": [
{ "version": "1.0.5", "reason": "Known incompatible API change." }
]
}
]
]
}
67 changes: 67 additions & 0 deletions schemas/metadata-root-index-schema-v1.0.0.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"$id": "https://github.com/oracle/graalvm-reachability-metadata/schemas/metadata-root-index.schema.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "Schema for metadata/index.json. This file lists modules known to the repository and optionally the directory where their metadata is stored, the allowed package prefixes for metadata, and inter-module requirements. See docs/CONTRIBUTING.md (Metadata structure).",
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["module"],
"properties": {
"module": {
"$ref": "#/$defs/moduleCoordinate",
"description": "Maven coordinates for the module in the form '<groupId>:<artifactId>'."
},
"directory": {
"type": "string",
"minLength": 1,
"pattern": "^[^\\s].*$",
"description": "Repository-relative path under 'metadata/' containing this module's metadata (e.g. 'org.example/library'). If omitted, the entry may reference requirements only."
},
"allowed-packages": {
"type": "array",
"description": "List of package (or fully-qualified name) prefixes considered valid sources of metadata entries for this module. Used to filter-in relevant JSON entries.",
"minItems": 1,
"uniqueItems": true,
"items": {
"type": "string",
"minLength": 1
}
},
"requires": {
"type": "array",
"description": "Optional list of module coordinates this module depends on. Each item is '<groupId>:<artifactId>'.",
"minItems": 1,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/moduleCoordinate"
}
}
}
},
"$defs": {
"moduleCoordinate": {
"type": "string",
"pattern": "^[^:]+:[^:]+$"
}
},
"examples": [
[
{
"directory": "org.example/library",
"module": "org.example:library"
},
{
"allowed-packages": ["org.package.name"],
"module": "org.example:dependant-library",
"requires": ["org.example:library"]
},
{
"allowed-packages" : [ "org.hibernate", "jakarta" ],
"directory" : "org.hibernate.orm/hibernate-envers",
"module" : "org.hibernate.orm:hibernate-envers",
"requires" : [ "org.hibernate.orm:hibernate-core" ]
}
]
]
}
30 changes: 30 additions & 0 deletions schemas/tests-project-index-schema-v1.0.0.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"$id": "https://github.com/oracle/graalvm-reachability-metadata/docs/schemas/tests-project-index-schema-v1.0.0.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Test Project Index",
"description": "Schema for tests/src/<groupId>/<artifactId>/<version>/index.json. This file optionally customizes the test invocation via 'test-command'. If omitted or if the property is not present, a default command will be used (gradlew nativeTest with environment overrides) per TestInvocationTask.",
"type": "object",
"additionalProperties": false,
"properties": {
"test-command": {
"type": "array",
"description": "Command line to run for this test project. Supports template parameters: <metadata_dir>, <group_id>, <artifact_id>, <version>.",
"minItems": 1,
"items": {
"type": "string",
"minLength": 1,
"description": "Part of the command line. May include template parameters like <metadata_dir>, <group_id>, <artifact_id>, <version>."
}
}
},
"examples": [
{
"test-command": [
"gradle",
"nativeTest",
"-Pmetadata.dir=<metadata_dir>",
"-Plibrary.version=<version>"
]
}
]
}
Loading
Loading