Skip to content

Commit 4ddc727

Browse files
authored
Merge branch 'main' into fix/shaclgen-maxcount-zero
2 parents c40dfa4 + de82371 commit 4ddc727

59 files changed

Lines changed: 4259 additions & 895 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/CODEOWNERS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515

1616
# --- Per-subsystem ownership (opt-in) ---
1717

18+
# javagen
19+
/docs/generators/java.rst @gouttegd
20+
/packages/linkml/src/linkml/generators/javagen.py @gouttegd
21+
/packages/linkml/src/linkml/generators/javagen/ @gouttegd
22+
/tests/linkml/test_generators/test_javagen.py @gouttegd
23+
1824
# pydanticgen:
1925
/packages/linkml/src/linkml/generators/pydanticgen/ @sneakers-the-rat @kevinschaper
2026
/docs/generators/pydantic.rst @sneakers-the-rat @kevinschaper

.github/dependabot.yml

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,30 @@ updates:
1212
github-actions:
1313
patterns:
1414
- "*"
15+
# Wait 7 days after a release before opening an update PR, giving time for
16+
# malware/CVE advisories to surface (mirrors the uv `exclude-newer` cooldown).
17+
# Note: github-actions only supports default-days, not the semver-*-days properties.
18+
cooldown:
19+
default-days: 7
1520

16-
# Activate after migration to uv as package manager
17-
# - package-ecosystem: "uv"
18-
# directories:
19-
# - "/"
20-
# schedule:
21-
# interval: "weekly"
22-
# day: "sunday"
23-
# groups:
24-
# # Individual pull requests for major/minor updates and grouped for patch updates
25-
# angular:
26-
# applies-to: version-updates
27-
# patterns:
28-
# - "*"
29-
# update-types:
30-
# - "patch"
31-
# open-pull-requests-limit: 10
21+
- package-ecosystem: "uv"
22+
directory: "/"
23+
schedule:
24+
interval: "weekly"
25+
day: "sunday"
26+
groups:
27+
# Individual pull requests for major/minor updates and grouped for patch updates
28+
patch-updates:
29+
applies-to: version-updates
30+
patterns:
31+
- "*"
32+
update-types:
33+
- "patch"
34+
open-pull-requests-limit: 10
35+
# Wait 7 days after a release before opening an update PR, giving time for
36+
# malware/CVE advisories to surface (mirrors the uv `exclude-newer` cooldown).
37+
cooldown:
38+
default-days: 7
39+
semver-major-days: 7
40+
semver-minor-days: 7
41+
semver-patch-days: 7
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env python3
2+
"""Decide whether the resolved ``uv.lock`` dependency set changed between refs.
3+
4+
``uv.lock`` is regenerated non-deterministically: reordering, hashes, and
5+
metadata can differ between two lockfiles that resolve to exactly the same
6+
packages. ``uv audit`` only cares about the multiset of ``(name, version)``
7+
pairs, so this script compares that set between a base ref and ``HEAD`` and
8+
prints ``true`` only when it actually differs.
9+
10+
Usage:
11+
uv_lock_deps_changed.py <base_ref>
12+
13+
Prints ``true`` when the resolved ``(name, version)`` set at ``HEAD`` differs
14+
from the one at ``<base_ref>`` (or when the lockfile is absent at either ref),
15+
otherwise ``false``.
16+
"""
17+
18+
from __future__ import annotations
19+
20+
import subprocess
21+
import sys
22+
23+
import tomllib
24+
25+
26+
def package_set(ref: str) -> set[tuple[str, str | None]] | None:
27+
"""Return the ``{(name, version)}`` set from ``uv.lock`` at ``ref``.
28+
29+
Args:
30+
ref: A git ref (SHA, branch, ``HEAD``) to read ``uv.lock`` from.
31+
32+
Returns:
33+
The set of ``(name, version)`` tuples for every locked package, or
34+
``None`` if ``uv.lock`` does not exist at ``ref``.
35+
"""
36+
result = subprocess.run(
37+
["git", "show", f"{ref}:uv.lock"],
38+
capture_output=True,
39+
text=True,
40+
)
41+
if result.returncode != 0:
42+
return None
43+
data = tomllib.loads(result.stdout)
44+
return {(pkg["name"], pkg.get("version")) for pkg in data.get("package", [])}
45+
46+
47+
def resolved_set_changed(base_ref: str) -> bool:
48+
"""Return whether the resolved dependency set differs between refs.
49+
50+
Args:
51+
base_ref: The ref to compare ``HEAD`` against.
52+
53+
Returns:
54+
``True`` if the ``(name, version)`` set differs, or if ``uv.lock`` is
55+
missing at either ref; ``False`` when the sets are identical.
56+
"""
57+
base = package_set(base_ref)
58+
head = package_set("HEAD")
59+
return base is None or head is None or base != head
60+
61+
62+
def main() -> None:
63+
"""Print ``true``/``false`` for the base ref given as the sole argument."""
64+
print("true" if resolved_set_changed(sys.argv[1]) else "false")
65+
66+
67+
if __name__ == "__main__":
68+
main()

.github/workflows/check-external-links.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: Check Sphinx external links
22
env:
3-
UV_VERSION: "0.7.13"
3+
UV_VERSION: "0.11.21"
44
on:
55
push:
66
branches: [main]
@@ -14,23 +14,23 @@ jobs:
1414
timeout-minutes: 30
1515
steps:
1616
- name: Checkout
17-
uses: actions/checkout@v6
17+
uses: actions/checkout@v7
1818

1919
- name: Set up Python 3.
20-
uses: actions/setup-python@v6.2.0
20+
uses: actions/setup-python@v7.0.0
2121
with:
2222
python-version: "3.12"
2323

2424
- name: Install uv
25-
uses: astral-sh/setup-uv@v8.2.0
25+
uses: astral-sh/setup-uv@v9.0.0
2626
with:
2727
version: ${{ env.UV_VERSION }}
2828

2929
- name: Install dependencies
3030
run: uv pip install --system requests
3131

3232
- name: Restore link cache
33-
uses: actions/cache@v5
33+
uses: actions/cache@v6
3434
with:
3535
path: .github/link-cache.csv
3636
key: link-cache-${{ github.run_id }}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
name: Audit dependencies
2+
3+
permissions:
4+
contents: read
5+
6+
# Global environment variables applied to all steps
7+
env:
8+
UV_VERSION: "0.11.21"
9+
UV_PREVIEW: "1" # Enables the preview uv audit and malware engines
10+
UV_MALWARE_CHECK: "1" # Automatically blocks malicious installs on sync/run
11+
12+
on:
13+
push:
14+
branches:
15+
- main
16+
pull_request:
17+
merge_group:
18+
workflow_dispatch:
19+
20+
jobs:
21+
security-check:
22+
name: Validate Dependencies
23+
runs-on: ubuntu-latest
24+
25+
steps:
26+
- name: Check out repository
27+
uses: actions/checkout@v7
28+
with:
29+
# Full history so we can diff against a base ref to see whether the
30+
# resolved dependency set changed (see "Detect dependency changes").
31+
fetch-depth: 0
32+
33+
# Pin uv to a known-good, recent release.
34+
- name: Install uv and setup uv caching
35+
uses: astral-sh/setup-uv@v9.0.0
36+
with:
37+
version: ${{ env.UV_VERSION }}
38+
enable-cache: true
39+
40+
- name: Set up Python
41+
uses: actions/setup-python@v7.0.0
42+
id: setup-python
43+
with:
44+
python-version: 3.13
45+
46+
# The CVE audit reflects the state of the *upstream advisory database*,
47+
# not the change under test: a newly-published advisory against an
48+
# already-pinned package would otherwise turn every open PR — and the next
49+
# innocent merge to main — red, regardless of whether it touched deps.
50+
#
51+
# So gate the audit on whether the change actually altered dependencies,
52+
# relative to each event's natural base:
53+
# * pull_request -> the PR base
54+
# * push (main) -> the commit before the push (github.event.before)
55+
# * merge_group -> the queue base
56+
# * otherwise (workflow_dispatch, first/force push) -> audit
57+
#
58+
# A pyproject.toml change is always a real dependency change. uv.lock is
59+
# regenerated non-deterministically, so a textual change there is only
60+
# treated as real if the resolved (name, version) set actually differs.
61+
- name: Detect dependency changes
62+
id: deps
63+
shell: bash
64+
run: |
65+
set -euo pipefail
66+
case "${{ github.event_name }}" in
67+
pull_request) base="${{ github.event.pull_request.base.sha }}" ;;
68+
merge_group) base="${{ github.event.merge_group.base_sha }}" ;;
69+
push) base="${{ github.event.before }}" ;;
70+
*) base="" ;;
71+
esac
72+
73+
zero="0000000000000000000000000000000000000000"
74+
if [ -z "$base" ] || [ "$base" = "$zero" ] || ! git cat-file -e "$base^{commit}" 2>/dev/null; then
75+
echo "No comparable base ref for '${{ github.event_name }}'; auditing."
76+
echo "changed=true" >> "$GITHUB_OUTPUT"
77+
exit 0
78+
fi
79+
80+
changed_files="$(git diff --name-only "$base...HEAD")"
81+
82+
if grep -qE '(^|/)pyproject\.toml$' <<<"$changed_files"; then
83+
echo "pyproject.toml changed; auditing."
84+
echo "changed=true" >> "$GITHUB_OUTPUT"
85+
elif grep -qE '(^|/)uv\.lock$' <<<"$changed_files"; then
86+
changed="$(python3 .github/scripts/uv_lock_deps_changed.py "$base")"
87+
if [ "$changed" = "true" ]; then
88+
echo "uv.lock resolved dependency set changed; auditing."
89+
else
90+
echo "uv.lock changed but the resolved dependency set is identical; skipping audit."
91+
fi
92+
echo "changed=$changed" >> "$GITHUB_OUTPUT"
93+
else
94+
echo "No dependency files changed; skipping audit."
95+
echo "changed=false" >> "$GITHUB_OUTPUT"
96+
fi
97+
98+
# Step 1: Run uv audit to check for vulnerabilities (CVEs)
99+
#
100+
# GHSA-6w46-j5rx-g56g (pytest predictable tmpdir path) is fixed only in
101+
# pytest 9.0.3, but pytest 9 removed the private _pytest.assertion.util
102+
# ._diff_text API that tests/conftest.py depends on. We pin pytest <9
103+
# (see packages/linkml/pyproject.toml) and ignore this single test-only,
104+
# low-risk advisory until conftest is migrated to stdlib difflib.
105+
- name: Audit lockfile for CVEs
106+
if: steps.deps.outputs.changed == 'true'
107+
run: uv audit --ignore GHSA-6w46-j5rx-g56g
108+
109+
# Step 2: Run a sync. If a package contains known malware,
110+
# the OSV-lookup triggers an immediate, non-zero failure exit.
111+
#
112+
# NOTE: The malware gate only blocks malware that has *already* been
113+
# published as an OSV advisory. There is a window between a malicious
114+
# upload and its advisory. That gap is covered by uv's dependency
115+
# cooldown (`exclude-newer`, a resolution-time setting added in uv
116+
# 0.9.17), which is enabled with a 7-day window in the root pyproject.toml:
117+
#
118+
# [tool.uv]
119+
# exclude-newer = "7 days"
120+
#
121+
- name: Verify Environment Sync (Anti-Malware Gate)
122+
run: uv sync --frozen --all-groups

.github/workflows/doc-pages.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: Sphinx Documentation
22
env:
3-
UV_VERSION: "0.7.13"
3+
UV_VERSION: "0.11.21"
44
on:
55
push:
66
branches: [main]
@@ -13,7 +13,7 @@ jobs:
1313
python-version: [ "3.12" ]
1414
steps:
1515
- name: Check out repository
16-
uses: actions/checkout@v6
16+
uses: actions/checkout@v7
1717
with:
1818
fetch-depth: 0
1919

@@ -24,13 +24,13 @@ jobs:
2424
git fetch upstream --tags
2525
2626
- name: Install uv
27-
uses: astral-sh/setup-uv@v8.2.0
27+
uses: astral-sh/setup-uv@v9.0.0
2828
with:
2929
version: ${{ env.UV_VERSION }}
3030
enable-cache: true
3131

3232
- name: Set up Python ${{ matrix.python-version }}
33-
uses: actions/setup-python@v6.2.0
33+
uses: actions/setup-python@v7.0.0
3434
id: setup-python
3535
with:
3636
python-version: ${{ matrix.python-version }}

.github/workflows/docker-build.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ jobs:
1919

2020
steps:
2121
- name: Checkout
22-
uses: actions/checkout@v6
22+
uses: actions/checkout@v7
2323
with:
2424
fetch-depth: 0
2525

2626
- name: Docker metadata
2727
id: meta
28-
uses: docker/metadata-action@v6.1.0
28+
uses: docker/metadata-action@v6.2.0
2929
with:
3030
images: linkml/linkml
3131
tags: |
@@ -42,20 +42,20 @@ jobs:
4242
echo "Ref: ${{ github.ref }}"
4343
4444
- name: Set up QEMU
45-
uses: docker/setup-qemu-action@v4.1.0
45+
uses: docker/setup-qemu-action@v4.2.0
4646

4747
- name: Set up Docker Buildx
48-
uses: docker/setup-buildx-action@v4.1.0
48+
uses: docker/setup-buildx-action@v4.2.0
4949

5050
- name: Login to DockerHub
5151
if: startsWith(github.ref, 'refs/tags/v')
52-
uses: docker/login-action@v4.2.0
52+
uses: docker/login-action@v4.5.1
5353
with:
5454
username: cjmungall
5555
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
5656

5757
- name: Build and push
58-
uses: docker/build-push-action@v7.2.0
58+
uses: docker/build-push-action@v7.3.0
5959
with:
6060
context: .
6161
platforms: linux/amd64,linux/arm64/v8

.github/workflows/docs-test.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: Build and test documentation
22
env:
3-
UV_VERSION: "0.7.13"
3+
UV_VERSION: "0.11.21"
44
on:
55
push:
66
branches:
@@ -19,7 +19,7 @@ jobs:
1919
python-version: ["3.12"]
2020
steps:
2121
- name: Check out repository
22-
uses: actions/checkout@v6
22+
uses: actions/checkout@v7
2323
with:
2424
fetch-depth: 0
2525

@@ -30,13 +30,13 @@ jobs:
3030
git fetch upstream --tags
3131
3232
- name: Install uv
33-
uses: astral-sh/setup-uv@v8.2.0
33+
uses: astral-sh/setup-uv@v9.0.0
3434
with:
3535
version: ${{ env.UV_VERSION }}
3636
enable-cache: true
3737

3838
- name: Set up Python ${{ matrix.python-version }}
39-
uses: actions/setup-python@v6.2.0
39+
uses: actions/setup-python@v7.0.0
4040
id: setup-python
4141
with:
4242
python-version: ${{ matrix.python-version }}

0 commit comments

Comments
 (0)