Skip to content
Merged
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
28 changes: 27 additions & 1 deletion .github/actions/create-package-release/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ inputs:
token:
description: "Token used to create the release (needs contents: write)."
required: true
assets:
description: >
Optional whitespace-separated list of asset files to attach to the
Comment thread
danabauer marked this conversation as resolved.
release (e.g. overture-schema.json). Uploaded atomically as part of
`gh release create`. Ignored on a dry run.
required: false
default: ""

outputs:
release-url:
Expand All @@ -68,6 +75,7 @@ runs:
LATEST: ${{ inputs.latest }}
VANITY_TAG: ${{ inputs.vanity-tag }}
DRY_RUN: ${{ inputs.dry-run }}
ASSETS: ${{ inputs.assets }}
run: |
set -euo pipefail

Expand All @@ -91,6 +99,16 @@ runs:
latest_flag="--latest=false"
[ "$LATEST" = "true" ] && latest_flag="--latest"

# Validate requested assets exist before the dry-run branch, so a dry
# run catches a missing or misnamed asset.
read -ra assets <<< "$ASSETS"
for asset in "${assets[@]}"; do
if [ ! -f "$asset" ]; then
echo "::error::Release asset not found: ${asset}"
exit 1
fi
done

if [ "$DRY_RUN" = "true" ]; then
[ "$tag_exists" = "true" ] && echo "::warning::Release ${TAG} already exists; a real run would fail here."
{
Expand All @@ -100,6 +118,9 @@ runs:
if [ -n "$VANITY_TAG" ]; then
echo "Would also create vanity tag \`${VANITY_TAG}\` (no release)."
fi
if [ ${#assets[@]} -gt 0 ]; then
echo "Would attach assets: \`${assets[*]}\`."
fi
echo ""
echo "<details><summary>Notes</summary>"
echo ""
Expand All @@ -116,7 +137,8 @@ runs:
--target "$TARGET" \
--title "\`${PACKAGE}\` ${VERSION}" \
--notes-file "${RUNNER_TEMP}/notes.md" \
$latest_flag
$latest_flag \
"${assets[@]}"

url=$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json url --jq .url)
echo "release-url=${url}" >> "$GITHUB_OUTPUT"
Expand All @@ -143,4 +165,8 @@ runs:
echo ""
echo "Vanity tag \`${VANITY_TAG}\` continues the legacy series (no separate release)."
fi
if [ -n "$ASSETS" ]; then
echo ""
echo "Attached assets: \`${ASSETS}\`."
fi
} >> "$GITHUB_STEP_SUMMARY"
17 changes: 17 additions & 0 deletions .github/workflows/release-trigger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,21 @@ jobs:
with:
persist-credentials: false

# For the `overture-schema package, `uv` is needed to generate the
# official JSON Schema release asset.
- name: Install uv
Comment thread
lowlydba marked this conversation as resolved.
if: matrix.package == 'overture-schema'
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
enable-cache: false

- name: Generate unified JSON schema
if: matrix.package == 'overture-schema'
run: |
set -euo pipefail
uv sync --locked --all-packages
uv run overture-schema json-schema --tag overture > overture-schema.json
Comment thread
vcschapp marked this conversation as resolved.

- name: Create release
Comment thread
lowlydba marked this conversation as resolved.
uses: ./.github/actions/create-package-release
with:
Expand All @@ -112,6 +127,8 @@ jobs:
# The umbrella package continues the legacy bare tag series as a
# vanity tag (no second release); see docs/versioning.md.
vanity-tag: ${{ matrix.package == 'overture-schema' && format('v{0}', matrix.version) || '' }}
# Only the umbrella release carries the unified JSON schema artifact.
Comment thread
vcschapp marked this conversation as resolved.
assets: ${{ matrix.package == 'overture-schema' && 'overture-schema.json' || '' }}
# App token so the release fires its own `release: published` event
# for release-publish.yaml (GITHUB_TOKEN would not); see #637.
token: ${{ steps.app-token.outputs.token }}
12 changes: 11 additions & 1 deletion docs/versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ attached. The umbrella package is the primary entrypoint for most consumers,
so its bare tags keep the long-standing convention alive; all other packages
use only the package-prefixed scheme.

The umbrella `overture-schema` release additionally carries **`overture-schema.json`**
as a release asset. This is the Pydantic-generated unified JSON Schema for the
whole official Overture schema, generated at release time by
`$ overture-schema json-schema --tag overture`. Including `overture-schema.json`
as a release artifact provides a bridge for any consumers who were using the
now-deprecated hand-authored YAML-mastered JSON Schema under `schema/` and want
to continue consuming JSON Schema. We attach this artifact only to the umbrella
`overture-schema` release, not for any other package.
Comment thread
vcschapp marked this conversation as resolved.

### Guardrails

- A changelog fragment is **required** on any change to a package, enforced by
Expand Down Expand Up @@ -191,7 +200,8 @@ changes that package, whether or not it bumps the version.
flowchart LR
A[bump + towncrier build<br/>merged to main] --> B[release-trigger:<br/>GitHub Release per package]
B --> C[PyPI publish<br/>Trusted Publishing] --> D[public PyPI]
E[no-bump merge] --> F[.postN internal build<br/>CodeArtifact only]
B --> E[overture-schema.json<br/>attached to umbrella overture-schema GitHub release]
F[no-bump merge] --> G[.postN internal build<br/>CodeArtifact only]
```

## Why
Expand Down
1 change: 1 addition & 0 deletions packages/overture-schema-cli/changelog.d/671.misc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added a test verifying CLI correctly generates the unified JSON Schema.
15 changes: 15 additions & 0 deletions packages/overture-schema-cli/tests/test_cli_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,21 @@ def test_json_schema_generates_valid_output(self, cli_runner: CliRunner) -> None
schema = json.loads(result.output)
assert isinstance(schema, dict)

def test_official_unified_schema_release_artifact(
self, cli_runner: CliRunner
) -> None:
"""Test the exact command the release workflow uses to build `overture-schema.json` as a
release artifact and sanity-check that the output seems reasonable.
"""
result = cli_runner.invoke(cli, ["json-schema", "--tag", "overture"])
assert result.exit_code == 0

schema = json.loads(result.output)

assert schema["$schema"] == "https://json-schema.org/draft/2020-12/schema"
assert "$defs" in schema and schema["$defs"]
assert any(k in schema for k in ("anyOf", "oneOf"))


class TestValidateCommand:
"""Tests for the validate command."""
Expand Down
1 change: 1 addition & 0 deletions packages/overture-schema-system/changelog.d/671.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Ensured the `json_schema()` function includes the JSON Schema dialect (`$schema`).
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,12 @@ def json_schema(thing: object) -> JsonSchemaValue:
"""
match _Kind.of(thing):
case _Kind.BASE_MODEL:
return cast(BaseModel, thing).model_json_schema(
schema = cast(BaseModel, thing).model_json_schema(
schema_generator=GenerateOmitNullableOptionalJsonSchema
)
case _Kind.UNION:
tap: TypeAdapter = TypeAdapter(thing)
return tap.json_schema(
schema = tap.json_schema(
schema_generator=GenerateOmitNullableOptionalJsonSchema
)
case _:
Expand All @@ -179,6 +179,11 @@ def json_schema(thing: object) -> JsonSchemaValue:
f"`{thing.__name__ if isinstance(thing, type) else type(thing).__name__}`"
)

# Declare the JSON Schema dialect on the root document because Pydantic doesn't do this.
schema["$schema"] = GenerateOmitNullableOptionalJsonSchema.schema_dialect

return schema


class _Kind(str, Enum):
BASE_MODEL = "base_model"
Expand Down
33 changes: 31 additions & 2 deletions packages/overture-schema-system/tests/test_json_schema.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from pydantic import BaseModel
from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue

from overture.schema.system.json_schema import GenerateOmitNullableOptionalJsonSchema
from overture.schema.system.json_schema import (
GenerateOmitNullableOptionalJsonSchema,
json_schema,
)


class TestenerateOmitOptionalJsonSchema:
class TestGenerateOmitOptionalJsonSchema:
def test_nullable_with_none_default_becomes_optional(self) -> None:
"""Test that X | None = None becomes optional without default."""

Expand Down Expand Up @@ -200,3 +203,29 @@ class TestModel(BaseModel):
types = {item["type"] for item in union_schema["anyOf"]}
assert types == {"string", "integer"}
assert "null" not in types


class TestJsonSchema:
def test_model_schema_declares_dialect(self) -> None:
class Model(BaseModel):
x: str

schema = json_schema(Model)

assert (
schema["$schema"] == GenerateOmitNullableOptionalJsonSchema.schema_dialect
)
assert schema["$schema"] == "https://json-schema.org/draft/2020-12/schema"

def test_union_schema_declares_dialect(self) -> None:
class A(BaseModel):
a: str

class B(BaseModel):
b: int

schema = json_schema(A | B)

assert (
schema["$schema"] == GenerateOmitNullableOptionalJsonSchema.schema_dialect
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Regenerated the JSON Schema baseline to include the `$schema` dialect declaration.
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "Addresses are structured labels for the geographic locations where businesses and individuals\nreside.\n\nWhile address formats around the world have some general points in common, the specifics vary\nextensively from place to place. The rules for dividing an address up into parts or fields vary,\nas do the names of those parts or fields.\n\nThe address schema uses a simplified approach to capture the common structure of addresses\nworldwide while accommodating local variance. The schema is heavily based on the OpenAddresses\n(www.openaddresses.io) project.\n\nFor sub-country administrative levels (and non-administrative levels such as neighborhoods), the\nschema provides the `address_levels` field. This is where the names of cities and towns,\nprovinces, state, and regions, and similar addressing units are found.",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Regenerated the JSON Schema baseline to include the `$schema` dialect declaration.
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "Bathymetry features provide topographic representations of underwater areas, such as parts of\nlake beds or ocean floors.",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@
"type": "string"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "Infrastructure features provide basic information about real-world infrastructure entities\nsuch as bridges, airports, runways, aerialways, communication towers, and power lines.",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@
"type": "string"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "Land features are representations of physical land surfaces.\n\nIn Overture data releases, land features are sourced from OpenStreetMap. TODO. Finish this when\nI get more info from Jennings.\n\n\n\nPhysical representations of land surfaces.\n\nGlobal land derived from the inverse of OSM Coastlines. Translates `natural` tags from OpenStreetMap.\n\nTODO: Update this description when the relationship to `land_cover` is better understood.",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "Land cover features indicate the primary natural or artificial surface material covering a land\narea on the earth, including vegetation types like forests and crops, built environments like\ncities, and natural surfaces like wetlands or barren ground.\n\nLand cover features relate to `LandUse` features in the following way: land cover is the\nphysical thing covering the land, while land use is the human use to which the land is being\nput.\n\nTODO: Explain relationship to `Land` features.",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@
"type": "string"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "Land use features specify the predominant human use of an area of land, for example commercial\nactivity, recreation, farming, housing, education, or military use.\n\nLand use features relate to `LandCover` features in the following way: land use is the human\nactivity being done with the land, while land cover is the physical thing that covers it.\n\nTODO: Explain relationship to `Land` features.",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@
"type": "string"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "Water features represent ocean and inland water bodies.\n\nIn Overture data releases, water features are sourced from OpenStreetMap. There are two main\ncategories of water feature: ocean and inland water bodies.\n\nOcean\n-----\nThe `subytpe` value `\"ocean\"` indicates an ocean area feature whose geometry represents the\nsurface area of an ocean or part of an ocean. Ocean area may be tiled into many small polygons\nof consistent complexity to ensure manageable geometry. In Overture data releases, ocean area\nfeatures are created from OpenStreetMap coastlines data (`natural=coastline`) using a QA'd\nversion of the output from the OSMCoastline tool. In aggregate, all the ocean area features\nrepresent the inverse of the land features with subtype `\"land\"` and class `\"land\"`.\n\nThe names and recommended label position for oceans and seas can be found in features with the\nsubtype `\"physical\"` and the class `\"ocean\"` or `\"sea\"`.\n\nInland Water\n------------\nSubtypes other than `\"ocean\"` (and `\"physical\"`) represent inland water bodies. In Overture data\nreleases, these features are sourced from the OpenStreetMap tag `natural=*` where the tag value\nindicates a water body, as well as the tags `natural=water`, `waterway=*`,\nand `water=*`.",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Regenerated the JSON Schema baseline to include the `$schema` dialect declaration.
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "Buildings are man-made structures with roofs that exist permanently in one place.\n\nA building's geometry represents the two-dimensional footprint of the building as viewed from\ndirectly above, looking down. Fields such as `height` and `num_floors` allow the\nthree-dimensional shape to be approximated. Some buildings, identified by the `has_parts` field,\nhave associated `BuildingPart` features which can be used to generate a more representative 3D\nmodel of the building.",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "Building parts represent parts of larger building features. They allow buildings to be modeled\nin rich detail suitable for creating realistic 3D models.\n\nEvery building part is associated with a parent `Building` feature via the `building_id` field.\nIn addition, a building part has a footprint geometry and may include additional details such as\nits height, the number of floors, and the color and material of its facade and roof.\n\nBuilding parts can float or be stacked on top of each other. The `min_height`, `min_floor`,\n`height`, and `num_floors`, fields can be used to arrange the parts correctly along the\nvertical dimension.",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Regenerated the JSON Schema baseline to include the `$schema` dialect declaration.
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "Division areas are polygon features that represent the land or maritime area covered by a\ndivision.\n\nEach division area belongs to a division which it references by ID, and for which the division\narea provides an area polygon. For ease of use, every division area repeats the subtype, names,\ncountry, and region properties of the division it belongs to.",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "Divisions are recognized official or non-official organizations of people as seen from a given\npolitical perspective.\n\nExamples include countries, provinces, cities, towns, neighborhoods, etc.",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "Boundaries represent borders between divisions of the same subtype.\n\nSome boundaries may be disputed by the divisions on one or both sides.",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Regenerated the JSON Schema baseline to include the `$schema` dialect declaration.
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "Places are point representations of real-world facilities, businesses, services, or amenities.",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Regenerated the JSON Schema baseline to include the `$schema` dialect declaration.
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "Connectors create physical connections between segments.\n\nConnectors are compatible with GeoJSON Point features.",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2228,6 +2228,7 @@
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"oneOf": [
{
"$ref": "#/$defs/RoadSegment"
Expand Down
1 change: 1 addition & 0 deletions packages/overture-schema/changelog.d/671.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Each `overture-schema` release now attaches `overture-schema.json`, the unified JSON Schema for the whole official Overture schema.
Loading