diff --git a/.github/dependabot.yml b/.github/dependabot.yml index eb8ba18c..34810881 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -52,6 +52,46 @@ updates: commit-message: prefix: "chore(deps)" + # Java dependencies + - package-ecosystem: "maven" + directory: "/java" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 10 + labels: + - "dependencies" + - "java" + commit-message: + prefix: "chore(deps)" + + # PHP dependencies + - package-ecosystem: "composer" + directory: "/php" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 10 + labels: + - "dependencies" + - "php" + commit-message: + prefix: "chore(deps)" + + # Go dependencies (the module has none today; this keeps the go directive and + # any future require block from going stale) + - package-ecosystem: "gomod" + directory: "/go" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 10 + labels: + - "dependencies" + - "go" + commit-message: + prefix: "chore(deps)" + # GitHub Actions - package-ecosystem: "github-actions" directory: "/" diff --git a/.github/workflows/csharp.yml b/.github/workflows/csharp.yml index 4f31be75..c619a16f 100644 --- a/.github/workflows/csharp.yml +++ b/.github/workflows/csharp.yml @@ -88,7 +88,7 @@ jobs: - name: Setup .NET SDK uses: actions/setup-dotnet@v6 with: - dotnet-version: '8.0.x' + dotnet-version: '10.0.x' - name: Restore Dependencies run: dotnet restore - name: Check formatting @@ -115,7 +115,7 @@ jobs: - name: Setup .NET SDK uses: actions/setup-dotnet@v6 with: - dotnet-version: '8.0.x' + dotnet-version: '10.0.x' - name: Restore Dependencies run: dotnet restore - name: Build @@ -123,7 +123,7 @@ jobs: # separate decision that would also fail on new analyzer releases. run: dotnet build --configuration Release --no-restore - name: Test - run: dotnet test --configuration Release --no-build -f net8 + run: dotnet test --configuration Release --no-build pushToNuget: runs-on: ubuntu-latest @@ -145,7 +145,7 @@ jobs: - name: Setup .NET SDK uses: actions/setup-dotnet@v6 with: - dotnet-version: '8.0.x' + dotnet-version: '10.0.x' - name: Check if version already published to NuGet.org id: version-check run: | @@ -236,7 +236,7 @@ jobs: - name: Setup .NET SDK uses: actions/setup-dotnet@v6 with: - dotnet-version: '8.0.x' + dotnet-version: '10.0.x' - name: Install dependencies for PDF generation run: | sudo apt-get update @@ -277,7 +277,7 @@ jobs: - name: Setup .NET SDK uses: actions/setup-dotnet@v6 with: - dotnet-version: '8.0.x' + dotnet-version: '10.0.x' - name: Install DocFX run: | dotnet tool install -g docfx diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 11674efe..bd959c68 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -82,7 +82,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v6 with: - go-version: '1.21' + go-version: '1.26' # This module has no dependencies: go/go.sum does not exist and # go.mod has no require block. Pointing the dependency cache at a # file that is not there made setup-go emit "Restore cache failed: @@ -120,7 +120,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v6 with: - go-version: '1.21' + go-version: '1.26' # This module has no dependencies: go/go.sum does not exist and # go.mod has no require block. Pointing the dependency cache at a # file that is not there made setup-go emit "Restore cache failed: diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index d46fe412..98a4c95b 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -79,10 +79,10 @@ jobs: with: persist-credentials: false submodules: true - - name: Set up JDK 11 + - name: Set up JDK 21 uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '21' distribution: 'temurin' cache: maven - name: Build with Maven @@ -99,7 +99,7 @@ jobs: strategy: fail-fast: false matrix: - java: ['11', '17', '21'] + java: ['21', '25'] steps: - uses: actions/checkout@v7 with: @@ -127,10 +127,10 @@ jobs: with: persist-credentials: false submodules: true - - name: Set up JDK 11 + - name: Set up JDK 21 uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '21' distribution: 'temurin' cache: maven - name: Check formatting with Spotless @@ -172,10 +172,10 @@ jobs: echo "have_creds=false" >> "$GITHUB_OUTPUT" echo "::warning::Skipping Maven Central publishing: CENTRAL_USERNAME, CENTRAL_TOKEN, GPG_PRIVATE_KEY and GPG_PASSPHRASE secrets are not configured. Generate a token at https://central.sonatype.com/account and add these repository secrets to enable publishing. No GitHub release will be created." fi - - name: Set up JDK 11 + - name: Set up JDK 21 uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '21' distribution: 'temurin' cache: maven server-id: central diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 430fb4b7..a6502545 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -79,10 +79,10 @@ jobs: with: persist-credentials: false submodules: true - - name: Set up PHP 8.3 + - name: Set up PHP 8.4 uses: shivammathur/setup-php@v2 with: - php-version: '8.3' + php-version: '8.4' extensions: mbstring tools: composer:v2 - name: Validate composer.json @@ -106,7 +106,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: ['8.1', '8.2', '8.3', '8.4'] + php-version: ['8.4', '8.5'] steps: - uses: actions/checkout@v7 with: @@ -143,7 +143,7 @@ jobs: - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.3' + php-version: '8.4' extensions: mbstring tools: composer:v2 - name: Check if version already published diff --git a/.github/workflows/release-audit.yml b/.github/workflows/release-audit.yml index 8e2d776c..d12141d0 100644 --- a/.github/workflows/release-audit.yml +++ b/.github/workflows/release-audit.yml @@ -14,6 +14,7 @@ name: release-audit on: push: branches: main + pull_request: schedule: # Weekly, so drift introduced outside a push is still noticed. - cron: '17 6 * * 1' @@ -33,6 +34,22 @@ concurrency: cancel-in-progress: false jobs: + # Hard failure: reads only the working tree, so a disagreement between the + # implementations is always a defect and never a transient registry answer. + consistency: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - name: Setup Node.js + uses: actions/setup-node@v7 + with: + node-version: '22' + - name: Check that every implementation declares the same version + run: node scripts/version-consistency.mjs + audit: runs-on: ubuntu-latest timeout-minutes: 10 diff --git a/.gitignore b/.gitignore index b76f3f28..86e8e3e5 100644 --- a/.gitignore +++ b/.gitignore @@ -298,6 +298,9 @@ paket-files/ __pycache__/ *.pyc +# setuptools metadata, written by `pip install -e python/` +*.egg-info/ + # Cake - Uncomment if you are using it # tools/** # !tools/packages.config diff --git a/.gitkeep b/.gitkeep index 247b6a30..d365b986 100644 --- a/.gitkeep +++ b/.gitkeep @@ -1,2 +1,3 @@ # .gitkeep file auto-generated at 2026-08-28T06:09:39.810Z for PR creation at branch issue-288-cc91e23553e8 for issue https://github.com/link-foundation/links-notation/issues/288 -# Updated: 2026-08-28T07:33:05.436Z \ No newline at end of file +# Updated: 2026-08-28T07:33:05.436Z +# Updated: 2026-08-28T11:11:17.987Z \ No newline at end of file diff --git a/.gitpod.yml b/.gitpod.yml index ca4ad7d3..c91fc70d 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -6,7 +6,7 @@ tasks: command: | echo "🔧 Testing C# implementation..." cd csharp - dotnet test -c Release -f net8 + dotnet test -c Release echo "✅ C# tests completed" - name: Test JavaScript diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3e719fe7..2247a9b1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v6.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -14,7 +14,7 @@ repos: # JavaScript/TypeScript - repo: https://github.com/pre-commit/mirrors-eslint - rev: v8.56.0 + rev: v10.9.1 hooks: - id: eslint files: \.(js|ts)$ @@ -24,20 +24,20 @@ repos: # Python - repo: https://github.com/psf/black - rev: 24.1.1 + rev: 26.5.1 hooks: - id: black language_version: python3 files: \.py$ - repo: https://github.com/PyCQA/isort - rev: 5.13.2 + rev: 9.0.1 hooks: - id: isort files: \.py$ - repo: https://github.com/PyCQA/flake8 - rev: 7.0.0 + rev: 7.3.0 hooks: - id: flake8 files: \.py$ @@ -53,7 +53,7 @@ repos: # Markdown - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.39.0 + rev: v0.49.1 hooks: - id: markdownlint args: ['--fix'] diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d34e24d..74007033 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- CI/CD: Dependabot covers every ecosystem in the repository. The `maven`, + `composer` and `gomod` manifests were unwatched, so Java, PHP and Go were + never offered updates ([#292](https://github.com/link-foundation/links-notation/issues/292)) +- CI/CD: `scripts/version-consistency.mjs`, a hard-failing check on every pull + request that the seven implementations declare the same version. The release + audit only warns, because a bump legitimately lands before the release that + publishes it; disagreement between the implementations reads only the working + tree and is always a defect + ([#292](https://github.com/link-foundation/links-notation/issues/292)) - Native PHP implementation of the Links Notation parser and formatter (`link-foundation/links-notation` on Packagist), with a full PHPUnit test suite ported from Python, PSR-12 linting and a dedicated CI workflow @@ -49,6 +58,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 registry confirms it ([#290](https://github.com/link-foundation/links-notation/issues/290)) ### Changed +- Every language's dependencies updated to their current releases, including the + major bumps: PHPUnit 10 to 13, PHP_CodeSniffer 3 to 4, xunit 2 to xunit.v3 4, + JUnit 5 to 6, `maven.compiler.release` 11 to 21, Go 1.21 to 1.24 (CI on 1.26), + `net8.0` to `net10.0`, and PHP `>=8.1` to `>=8.4` (CI on 8.4 and 8.5). Each + suite was run against the new versions before the bump was kept + ([#292](https://github.com/link-foundation/links-notation/issues/292)) +- C#: the test project runs on Microsoft.Testing.Platform. The .NET 10 SDK + dropped the VSTest path xunit v2 used, so `csharp/global.json` selects the new + runner and the test project builds as a self-executing `Exe` + ([#292](https://github.com/link-foundation/links-notation/issues/292)) - Grammar: a reference is a `delimited_reference` (`n_quoted_reference` or `empty_reference`) or a `simple_reference`; the three delimiters `"`, `'` and `` ` `` are documented as equivalent, and an even delimiter run that does diff --git a/csharp/Link.Foundation.Links.Notation.Tests/Link.Foundation.Links.Notation.Tests.csproj b/csharp/Link.Foundation.Links.Notation.Tests/Link.Foundation.Links.Notation.Tests.csproj index 3c648021..846a5042 100644 --- a/csharp/Link.Foundation.Links.Notation.Tests/Link.Foundation.Links.Notation.Tests.csproj +++ b/csharp/Link.Foundation.Links.Notation.Tests/Link.Foundation.Links.Notation.Tests.csproj @@ -1,8 +1,11 @@ - net8 + net10.0 false + + Exe latest enable Link.Foundation.Links.Notation.Tests @@ -14,10 +17,10 @@ - - - - + + diff --git a/csharp/Link.Foundation.Links.Notation/Link.Foundation.Links.Notation.csproj b/csharp/Link.Foundation.Links.Notation/Link.Foundation.Links.Notation.csproj index 7dd48c19..4e6dc31a 100644 --- a/csharp/Link.Foundation.Links.Notation/Link.Foundation.Links.Notation.csproj +++ b/csharp/Link.Foundation.Links.Notation/Link.Foundation.Links.Notation.csproj @@ -4,9 +4,9 @@ Link.Foundation's Links Notation parser and formatter for .NET Konstantin Diachenko Link.Foundation.Links.Notation - 0.15.0 + 0.16.0 Konstantin Diachenko - net8 + net10.0 Link.Foundation.Links.Notation Link.Foundation;Protocols.Lino;Lino;IListExtensions;Link;Parser https://link-foundation.github.io/links-notation @@ -41,7 +41,6 @@ Test cases suite is updated. - diff --git a/csharp/README.md b/csharp/README.md index 2b96e521..d74072a3 100644 --- a/csharp/README.md +++ b/csharp/README.md @@ -172,7 +172,7 @@ Note: C# formatting checks are integrated into the CI pipeline using ## Dependencies -- .NET 8.0 +- .NET 10.0 - Microsoft.CSharp (4.7.0) - Pegasus (4.1.0) - Platform.Collections (0.3.2) diff --git a/csharp/README.ru.md b/csharp/README.ru.md index b9f5b665..7cf7e53f 100644 --- a/csharp/README.ru.md +++ b/csharp/README.ru.md @@ -143,7 +143,7 @@ var customParser = new Parser(); ## Зависимости -- .NET 8.0 +- .NET 10.0 - Microsoft.CSharp (4.7.0) - Pegasus (4.1.0) - Platform.Collections (0.3.2) diff --git a/csharp/docfx.json b/csharp/docfx.json index 25952a32..a1f67985 100644 --- a/csharp/docfx.json +++ b/csharp/docfx.json @@ -10,7 +10,7 @@ ], "dest": "obj/api", "filter": "filter.yml", - "properties": { "TargetFramework": "net8" } + "properties": { "TargetFramework": "net10.0" } } ], "build": { diff --git a/csharp/global.json b/csharp/global.json new file mode 100644 index 00000000..3140116d --- /dev/null +++ b/csharp/global.json @@ -0,0 +1,5 @@ +{ + "test": { + "runner": "Microsoft.Testing.Platform" + } +} diff --git a/dev/log/issues/292/pulls/294/README.md b/dev/log/issues/292/pulls/294/README.md new file mode 100644 index 00000000..8e78757e --- /dev/null +++ b/dev/log/issues/292/pulls/294/README.md @@ -0,0 +1,26 @@ +# Investigation log — issue #292 / pull request #294 + +Evidence and analysis collected while working on +[link-foundation/links-notation#292](https://github.com/link-foundation/links-notation/issues/292). + +`dev/log/` is gitignored (`.gitignore` line 25, `[Ll]og/`), so only the written analysis is +committed, with `git add -f`. + +## Reading order + +1. [`analysis/REQUIREMENTS.md`](analysis/REQUIREMENTS.md) — the six requirements the issue states, + each mapped to what was done, and the one that cannot be finished here. +2. [`analysis/VERIFICATION.md`](analysis/VERIFICATION.md) — what was actually run, what each major + bump forced, and the negative test of the new consistency check. + +## Supporting evidence + +| File | What it is | +| --- | --- | +| `analysis/registry-latest.txt` | what each registry currently serves for every dependency this repository declares | +| `analysis/maven-plugin-versions.txt` | the last three published versions of each Maven plugin, showing which newest artefacts are betas | +| `analysis/release-audit-after-bump.txt` | `scripts/release-audit.mjs` with 0.16.0 declared and 0.15.0 everywhere published | +| `issue/issue-292.json`, `issue/issue-292-comments.json` | the issue as read (the comments array is empty) | +| `pr/pr-294.json` | the pull request as it stood before the description was rewritten | + +The registry snapshot is reproducible: `node experiments/issue-292/registry-latest.mjs`. diff --git a/dev/log/issues/292/pulls/294/analysis/REQUIREMENTS.md b/dev/log/issues/292/pulls/294/analysis/REQUIREMENTS.md new file mode 100644 index 00000000..88e7b0f0 --- /dev/null +++ b/dev/log/issues/292/pulls/294/analysis/REQUIREMENTS.md @@ -0,0 +1,42 @@ +# Requirements — issue #292 + +Every requirement stated in +[#292](https://github.com/link-foundation/links-notation/issues/292), with what was done and how it +was checked. + +| # | Requirement | Status | Where | +| --- | --- | --- | --- | +| 1 | Update every dependency in every language | done | see [VERIFICATION.md](VERIFICATION.md); `registry-latest.txt` shows each declared version equals the registry's current release | +| 2 | Take the major bumps deliberately | done | phpunit 10→13, php_codesniffer 3→4, xunit 2→xunit.v3 4, `maven.compiler.release` 11→21, go 1.21→1.24, net8→net10, php 8.1→8.4; each suite run before the bump was kept | +| 3 | Bump peggy and Pegasus carefully, verifying against the conformance fixtures | done | peggy and Pegasus were already at the current release (5.1.0 / 4.1.0); regenerating `js/src/parser-generated.js` produced a byte-identical file and all 204 JS tests pass | +| 4 | Publish every language at the same version | partly | all seven declarations are 0.16.0 and `scripts/version-consistency.mjs` now enforces it; whether Maven Central and Packagist actually receive it depends on credentials this pull request cannot supply — see below | +| 5 | CI that fails when the languages disagree, or when a version is ahead of what is published | done | `scripts/version-consistency.mjs` (hard failure, every pull request) and `scripts/release-audit.mjs` (warning) — see the note on why the second one warns | +| 6 | Automated dependency updates across all ecosystems | done | `.github/dependabot.yml` gained `maven`, `composer` and `gomod`; `cargo`, `npm`, `pip` and `github-actions` were already there | + +## Requirement 4 — what this pull request cannot do + +The issue asks for the languages to be released together. Five of the seven already are: js, python, +rust, csharp and go each match their registry. java and php have **never** published — Maven Central +holds nothing under `io.github.link-foundation:links-notation` and Packagist holds nothing under +`link-foundation/links-notation`. That is not a version-declaration problem, so bumping a file cannot +fix it; it needs the Maven Central and Packagist credentials to be configured on the repository, +which is tracked separately in +[#192](https://github.com/link-foundation/links-notation/issues/192). + +What this pull request can do, and does, is make the disagreement impossible to reintroduce silently: +`version-consistency.mjs` fails the build the moment the seven declarations diverge, and the release +audit keeps annotating the two languages whose registry has nothing. + +## Requirement 5 — why one check fails and the other warns + +The two checks answer different questions and deserve different severities. + +`version-consistency.mjs` reads only the working tree. If `java/pom.xml` says 0.16.0 and +`go/VERSION` says 0.15.0, that is true of the commit and will still be true tomorrow — there is no +transient explanation, so it fails. + +`release-audit.mjs` asks seven registries what they serve. A declared version being ahead of a +published one is the *normal* state between the bump and the release that publishes it: this very +pull request declares 0.16.0 while every registry still serves 0.15.0. Failing on that would mean +every release-preparing pull request is red by construction. It annotates instead, which is the +behaviour introduced in #290 and deliberately left alone. diff --git a/dev/log/issues/292/pulls/294/analysis/VERIFICATION.md b/dev/log/issues/292/pulls/294/analysis/VERIFICATION.md new file mode 100644 index 00000000..fba7d96a --- /dev/null +++ b/dev/log/issues/292/pulls/294/analysis/VERIFICATION.md @@ -0,0 +1,108 @@ +# Verification — issue #292 + +Nothing here is taken from the tables in the issue. Several of those entries were already stale by +the time the issue was written (`xunit.runner.visualstudio` was listed as needing 2.x→3.x when NuGet +already served 4.0.0; java and php were listed at 0.3.0/0.2.0 when both were already 0.15.0 on +`main`). Every version below was read from the registry, and every suite below was run. + +## What the registries actually serve + +`registry-latest.txt` is the output of `experiments/issue-292/registry-latest.mjs`, which asks npm, +PyPI, crates.io, NuGet, repo1.maven.org and Packagist directly. After this pull request, every +dependency the repository declares equals the current release there. + +One caveat worth recording: `search.maven.org`'s solr index answered `5.12.2` for `junit-bom` while +`repo1.maven.org/maven2/org/junit/junit-bom/maven-metadata.xml` already served `6.1.3`. The script +reads the repository metadata for that reason. + +Four Maven plugins were deliberately **not** taken to their newest artefact +(`maven-plugin-versions.txt`): `maven-compiler-plugin` 4.0.0-beta-4, `maven-jar-plugin` +4.0.0-beta-1, `maven-source-plugin` 4.0.0-beta-1 and `maven-surefire-plugin` 3.6.0-M1 are a beta and +a milestone. The newest *stable* releases (3.15.0, 3.5.1, 3.4.0, 3.5.6) are what the pom declares. + +## Suites run against the new versions + +| Language | Command | Result | +| --- | --- | --- | +| js | `bun test`, `bun run lint`, peggy regeneration | 204 tests pass; regenerating `src/parser-generated.js` produces a byte-identical file, so peggy 5.1.0 emits the same parser | +| python | `pytest`, `black --check`, `isort --check`, `flake8` | 193 passed, 1 skipped; all three linters clean | +| rust | `cargo test`, `cargo update` | passes; `cargo update` locked 0 packages — nom 8.0.0, syn 3.0.4, quote 1.0.47 and proc-macro2 1.0.107 are already the current releases | +| csharp | `dotnet test`, `dotnet format --verify-no-changes`, `dotnet pack` | 196 tests pass on net10.0 under xunit.v3 4.0.0 | +| java | `mvn test`, `mvn spotless:check`, `mvn package` | 133 tests pass on JDK 21 with JUnit 6.1.3 | +| php | `phpunit`, `phpcs` | 183 tests / 497 assertions pass under PHPUnit 13 on **both** php:8.4 and php:8.5 containers; PSR-12 clean under PHP_CodeSniffer 4 | +| go | `go build`, `go vet`, `gofmt -l`, `go test` | passes on go1.26.4; the module has no external dependencies | + +## Major bumps and what each one forced + +- **JUnit 5 → 6** requires Java 17+, so `maven.compiler.release` went 11 → 21 and the CI matrix went + `['11','17','21']` → `['21','25']`. The READMEs' "Java 11 or higher" line was updated with it. +- **PHPUnit 10 → 13** requires `php >=8.4.1`, so `composer.json` went `>=8.1` → `>=8.4` and the CI + matrix went `['8.1','8.2','8.3','8.4']` → `['8.4','8.5']`. PHP 8.4 and 8.5 are not installed + locally, so both were run in the official docker images. +- **xunit 2 → xunit.v3 4 on net10.0** was the only bump that needed a change beyond a version + number. The .NET 10 SDK removed the VSTest path, and `dotnet test` failed with *"Testing with + VSTest target is no longer supported by Microsoft.Testing.Platform on .NET 10 SDK and later"*. + Neither `` nor a `dotnet.config` `[dotnet.test.runner]` section + fixed it. The documented opt-in is `global.json`: + + ```json + { "test": { "runner": "Microsoft.Testing.Platform" } } + ``` + + with `TestingPlatformDotnetTestSupport` **removed**, and the test project built as + `Exe` because an xunit v3 project is its own test host. `Microsoft.NET.Test.Sdk`, + `xunit.runner.visualstudio` and `coverlet.collector` are VSTest components and are gone. + `Microsoft.CSharp` was dropped too — it is part of the framework on net10.0, and keeping it emitted + NU1510. + +## Negative test of the new consistency check + +The check has to fail when the implementations disagree, not merely pass when they agree: + +``` +$ sed -i 's|0.16.0|0.3.0|' java/pom.xml +$ node scripts/version-consistency.mjs; echo "exit=$?" +js: 0.16.0 +python: 0.16.0 +rust: 0.16.0 +csharp: 0.16.0 +go: 0.16.0 +java: 0.3.0 +php: 0.16.0 +::error::the implementations declare different versions: 0.16.0 (js, python, rust, csharp, go, php); 0.3.0 (java) +exit=1 +$ git checkout java/pom.xml && node scripts/version-consistency.mjs; echo "exit=$?" +All 7 implementations declare 0.16.0. +exit=0 +``` + +## Release audit after the bump + +`release-audit-after-bump.txt` is the audit's output with 0.16.0 declared and every registry still +serving 0.15.0 — seven warnings and exit 0. This is exactly the state +[REQUIREMENTS.md](REQUIREMENTS.md) describes as the reason that check must not be a hard failure. + +## Pre-commit hooks + +`.pre-commit-config.yaml` pins its hooks by git tag, which is a dependency declaration like any +other, and they had drifted badly: `pre-commit-hooks` v4.5.0 (v6.0.0 current), `mirrors-eslint` +v8.56.0 while `js/package.json` asks for eslint 10, `black` 24.1.1 while `python/pyproject.toml` +asks for 26.5, `isort` 5.13.2 against 9.0, `flake8` 7.0.0 against 7.3, `markdownlint-cli` v0.39.0 +(v0.49.1 current). All six were bumped to match. `doublify/pre-commit-rust` stays at v1.0 — that is +still its newest tag. + +`pre-commit run --all-files` does **not** pass on this repository, and did not before this pull +request either. It is not wired into any workflow, and the failures are all outside the shipped +implementations: `black` and `isort` want to reformat `csharp/scripts/format-files.py` and two +`experiments/` scripts, `flake8` reports E203/E501 in `docs/comparison/generate_comparison_svgs.py`, +and `markdownlint` reports hundreds of MD013/MD024/MD040 findings across `docs/case-studies/` and +the READMEs. The `python/` package itself is clean under all three linters. Reformatting the rest of +the repository is not part of #292, so it was left alone rather than folded into a dependency +update — the auto-fixing hooks were run once, and their edits reverted. + +## Dependabot ecosystems + +`.github/dependabot.yml` watched `cargo`, `npm`, `pip` and `github-actions`. It now also watches +`maven` (/java), `composer` (/php) and `gomod` (/go), so every manifest in the repository is +covered. `.pre-commit-config.yaml` is not: Dependabot has no `pre-commit` ecosystem, so those tags +stay a manual bump. diff --git a/dev/log/issues/292/pulls/294/analysis/maven-plugin-versions.txt b/dev/log/issues/292/pulls/294/analysis/maven-plugin-versions.txt new file mode 100644 index 00000000..1efde17f --- /dev/null +++ b/dev/log/issues/292/pulls/294/analysis/maven-plugin-versions.txt @@ -0,0 +1,7 @@ +org/apache/maven/plugins/maven-compiler-plugin 4.0.0-beta-2 4.0.0-beta-3 4.0.0-beta-4 +org/apache/maven/plugins/maven-surefire-plugin 3.5.5 3.5.6 3.6.0-M1 +org/apache/maven/plugins/maven-jar-plugin 3.5.0 3.5.1 4.0.0-beta-1 +org/apache/maven/plugins/maven-source-plugin 3.3.1 3.4.0 4.0.0-beta-1 +org/apache/maven/plugins/maven-javadoc-plugin 3.11.2 3.11.3 3.12.0 +org/apache/maven/plugins/maven-gpg-plugin 3.2.6 3.2.7 3.2.8 +org/sonatype/central/central-publishing-maven-plugin 0.9.0 0.10.0 0.11.0 diff --git a/dev/log/issues/292/pulls/294/analysis/registry-latest.txt b/dev/log/issues/292/pulls/294/analysis/registry-latest.txt new file mode 100644 index 00000000..deb04584 --- /dev/null +++ b/dev/log/issues/292/pulls/294/analysis/registry-latest.txt @@ -0,0 +1,23 @@ +js bun-types 1.4.0 +js eslint 10.9.1 +js peggy 5.1.0 +js prettier 3.9.6 +js typescript 7.0.2 +python pytest 9.1.1 +python pytest-timeout 2.4.0 +python black 26.5.1 +python isort 9.0.1 +python flake8 7.3.0 +rust nom 8.0.0 +rust syn 3.0.4 +rust quote 1.0.47 +rust proc-macro2 1.0.107 +csharp xunit.v3 4.0.0 +csharp Pegasus 4.1.0 +csharp Platform.Collections 0.4.0 +csharp Microsoft.SourceLink.GitHub 10.0.400 +java org.junit:junit-bom 6.1.3 +java com.diffplug.spotless:spotless-maven-plugin 3.10.1 +java com.google.googlejavaformat:google-java-format 1.36.1 +php phpunit/phpunit 13.3.2 +php squizlabs/php_codesniffer 4.0.4 diff --git a/dev/log/issues/292/pulls/294/analysis/release-audit-after-bump.txt b/dev/log/issues/292/pulls/294/analysis/release-audit-after-bump.txt new file mode 100644 index 00000000..545a69dd --- /dev/null +++ b/dev/log/issues/292/pulls/294/analysis/release-audit-after-bump.txt @@ -0,0 +1,9 @@ +::warning::js: declared 0.16.0, latest on npm is 0.15.0. +::warning::python: declared 0.16.0, latest on PyPI is 0.15.0. +::warning::rust: declared 0.16.0, latest on crates.io is 0.15.0. +::warning::csharp: declared 0.16.0, latest on NuGet.org is 0.15.0. +::warning::go: declared 0.16.0, latest on proxy.golang.org is 0.15.0. +::warning::java: declared 0.16.0, but nothing is published on Maven Central. The publish job for this language has never successfully released anything. +::warning::php: declared 0.16.0, but nothing is published on Packagist. The publish job for this language has never successfully released anything. + +7 implementation(s) drifted. diff --git a/dev/log/issues/292/pulls/294/issue/issue-292-comments.json b/dev/log/issues/292/pulls/294/issue/issue-292-comments.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/dev/log/issues/292/pulls/294/issue/issue-292-comments.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/dev/log/issues/292/pulls/294/issue/issue-292.json b/dev/log/issues/292/pulls/294/issue/issue-292.json new file mode 100644 index 00000000..e7bdba87 --- /dev/null +++ b/dev/log/issues/292/pulls/294/issue/issue-292.json @@ -0,0 +1 @@ +{"author":{"id":"MDQ6VXNlcjE0MzE5MDQ=","is_bot":false,"login":"konard","name":"Konstantin Diachenko"},"body":"The eight language implementations have drifted apart in two ways: their dependencies are stale by major versions, and their released versions do not match each other or the repository. Both need fixing, and the second is the one a downstream consumer feels first.\n\nVersions resolved from the registries today.\n\n## Dependencies, per language\n\n### Rust — `rust/links-notation/Cargo.toml`\n| dependency | pinned | latest |\n| --- | --- | --- |\n| `nom` | 8.0 | 8.x — confirm current patch |\n| `links-notation-macro` | path, 0.1.0 | in-tree |\n\n### JS — `js/package.json`\n| dependency | pinned | latest | |\n| --- | --- | --- | --- |\n| `eslint` | ^10.8.1 | 10.9.1 | |\n| `typescript` | ^7.0.2 | 7.x | |\n| `peggy` | ^5.1.0 | 5.x | grammar generator — bump deliberately |\n| `bun-types` | ^1.3.14 | 1.x | |\n\n### Python — `python/pyproject.toml`\n| dependency | pinned | latest | |\n| --- | --- | --- | --- |\n| `pytest` | >=9.0 | 9.1.1 | |\n| `pytest-timeout` | >=2.4 | 2.x | |\n| `black` | >=26.0 | 26.x | |\n| `isort` | >=8.0 | 8.x | |\n| `flake8` | >=7.3 | 7.x | |\n\n### C# — `csharp/`\n| dependency | pinned | latest | |\n| --- | --- | --- | --- |\n| `Pegasus` | 4.1.0 | | parser generator — bump deliberately |\n| `Platform.Collections` | 0.4.0 | | |\n| `Microsoft.CSharp` | 4.7.0 | | |\n| `Microsoft.SourceLink.GitHub` | 10.0.400 | | |\n| `Microsoft.NET.Test.Sdk` | 18.9.0 | current | |\n| `xunit` | 2.9.3 | **3.x** | **major** |\n| `xunit.runner.visualstudio` | 3.1.5 | current | |\n| `coverlet.collector` | 10.0.1 | current | |\n| `TargetFramework` | `net8` | net10 is current | |\n\n### Java — `java/pom.xml`\n| dependency | pinned | |\n| --- | --- | --- |\n| `junit-jupiter` | 5.14.4 | |\n| `maven-compiler-plugin` | 3.15.0 | |\n| `maven-surefire-plugin` | 3.5.6 | |\n| `maven-jar-plugin` | 3.5.1 | |\n| `maven-source-plugin` | 3.4.0 | |\n| `maven-javadoc-plugin` | 3.12.0 | |\n| `spotless-maven-plugin` | 2.46.1 | |\n| `maven.compiler.source/target` | **11** | Java 21 is the current LTS |\n\n### PHP — `php/composer.json`\n| dependency | pinned | latest | |\n| --- | --- | --- | --- |\n| `phpunit/phpunit` | ^10.5 | **13.3.2** | **3 majors** |\n| `squizlabs/php_codesniffer` | ^3.10 | **4.0.4** | **major** |\n| `php` | >=8.1 | 8.4 is current | |\n\n### Go — `go/go.mod`\n| | pinned | |\n| --- | --- | --- |\n| `go` directive | **1.21** | 1.23+ is current; no external dependencies |\n\n## Released versions do not agree\n\n| language | in-tree | published | |\n| --- | --- | --- | --- |\n| Rust | — | **0.15.0** (crates.io) | current |\n| Python | 0.15.0 | **0.15.0** (PyPI) | current |\n| JS | 0.15.0 | **0.13.0** (npm) | **two releases unpublished** |\n| C# | — | **0.13.0** (NuGet) | behind |\n| Java | **0.3.0** (`pom.xml`) | — | twelve minors behind the others |\n| PHP | **0.2.0** (`composer.json`) | — | thirteen minors behind |\n\n`#192` already tracks Java and Go publishing. The JS gap is new and immediate: the npm package is two releases older than what this repository tests, so anyone installing from npm gets a different parser.\n\nThat is already visible downstream — `lino-objects-codec` pins `^0.11.0` in JS and `0.13.0` in C# because that is what the registries offer, while its Rust implementation is on 0.14.0. Filed as link-foundation/lino-objects-codec#47.\n\n## What this issue asks for\n\n1. **Update every dependency in every language** — Rust, JS, Python, C#, Java, PHP, Go, and the macro crate.\n2. **Take the major bumps deliberately**: `phpunit` 10→13, `php_codesniffer` 3→4, `xunit` 2→3, and the language baselines (`maven.compiler` 11→21, `go` 1.21→1.23+, `net8`→`net10`, `php` 8.1→8.4). Each may need code or config changes.\n3. **Bump `peggy` and `Pegasus` carefully** — they generate the parsers, so a change there can alter accepted grammar. Verify against the conformance fixtures rather than only the unit tests.\n4. **Publish every language at the same version**, so `0.15.0` means the same grammar on crates.io, npm, PyPI, NuGet, Maven, Packagist and Go. JS is the immediate gap; Java at 0.3.0 and PHP at 0.2.0 are the largest.\n5. **Release the languages together** from here on, and add CI that fails when any language's in-tree version is ahead of what is published for it, or when the eight disagree.\n6. **Enable automated dependency updates** (Dependabot or Renovate) across all seven ecosystems so this does not accumulate again.\n\n## Why alignment matters beyond tidiness\n\nThe value of eight implementations is that a document written by one reads identically in the others — which holds only if they are the same version of the grammar. #288 is the live case: whatever is decided about the empty reference must land *and publish* in all eight, or a document written by a Rust producer will not read the same way in a JS consumer. That is the failure the shared conformance fixtures exist to prevent, and they cannot catch it while the languages ship on different schedules.\n","createdAt":"2026-08-28T07:35:19Z","labels":[],"number":292,"title":"Update all dependencies in all eight languages, and publish them at one version (js 0.15.0 in-tree vs 0.13.0 on npm; java 0.3.0; php 0.2.0)"} diff --git a/dev/log/issues/292/pulls/294/pr/pr-294.json b/dev/log/issues/292/pulls/294/pr/pr-294.json new file mode 100644 index 00000000..1a9a0fb4 --- /dev/null +++ b/dev/log/issues/292/pulls/294/pr/pr-294.json @@ -0,0 +1 @@ +{"body":"## 🤖 AI-Powered Solution Draft\n\nThis pull request is being automatically generated to solve issue #292.\n\n### 📋 Issue Reference\nFixes #292\n\n### 🚧 Status\n**Work in Progress** - The AI assistant is currently analyzing and implementing the solution draft.\n\n### 📝 Implementation Details\n_Details will be added as the solution draft is developed..._\n\n---\n*This PR was created automatically by the AI issue solver*","createdAt":"2026-08-28T11:11:27Z","headRefName":"issue-292-a602b59a2375","isDraft":true,"number":294,"title":"[WIP] Update all dependencies in all eight languages, and publish them at one version (js 0.15.0 in-tree vs 0.13.0 on npm; java 0.3.0; php 0.2.0)"} diff --git a/experiments/issue-292/registry-latest.mjs b/experiments/issue-292/registry-latest.mjs new file mode 100644 index 00000000..50a853cb --- /dev/null +++ b/experiments/issue-292/registry-latest.mjs @@ -0,0 +1,81 @@ +// Asks each registry what the current release of every dependency this +// repository declares is, so the bumps in #292 can be checked against the +// registries rather than against the (already stale) tables in the issue. +// +// node experiments/issue-292/registry-latest.mjs + +const npm = async (name) => + (await (await fetch(`https://registry.npmjs.org/${name}/latest`)).json()).version; + +const pypi = async (name) => + (await (await fetch(`https://pypi.org/pypi/${name}/json`)).json()).info.version; + +const crates = async (name) => + (await (await fetch(`https://crates.io/api/v1/crates/${name}`, { + headers: { 'User-Agent': 'links-notation-dependency-audit' }, + })).json()).crate.max_stable_version; + +const nuget = async (name) => { + const id = name.toLowerCase(); + const { versions } = await ( + await fetch(`https://api.nuget.org/v3-flatcontainer/${id}/index.json`) + ).json(); + return versions.filter((v) => !v.includes('-')).at(-1); +}; + +// search.maven.org's solr index answered with 5.12.2 for junit-bom while +// repo1 already served 6.1.3, so read the repository's own metadata instead. +const maven = async (coordinates) => { + const path = coordinates.replace(/[.:]/g, '/'); + const metadata = await ( + await fetch(`https://repo1.maven.org/maven2/${path}/maven-metadata.xml`) + ).text(); + return metadata.match(/([^<]+)<\/latest>/)[1]; +}; + +const packagist = async (name) => { + const { packages } = await ( + await fetch(`https://repo.packagist.org/p2/${name}.json`) + ).json(); + return packages[name].find((r) => !/-(alpha|beta|RC)/i.test(r.version)).version; +}; + +const DEPENDENCIES = [ + ['js', 'bun-types', npm], + ['js', 'eslint', npm], + ['js', 'peggy', npm], + ['js', 'prettier', npm], + ['js', 'typescript', npm], + ['python', 'pytest', pypi], + ['python', 'pytest-timeout', pypi], + ['python', 'black', pypi], + ['python', 'isort', pypi], + ['python', 'flake8', pypi], + ['rust', 'nom', crates], + ['rust', 'syn', crates], + ['rust', 'quote', crates], + ['rust', 'proc-macro2', crates], + ['csharp', 'xunit.v3', nuget], + ['csharp', 'Pegasus', nuget], + ['csharp', 'Platform.Collections', nuget], + ['csharp', 'Microsoft.SourceLink.GitHub', nuget], + ['java', 'org.junit:junit-bom', maven], + ['java', 'com.diffplug.spotless:spotless-maven-plugin', maven], + ['java', 'com.google.googlejavaformat:google-java-format', maven], + ['php', 'phpunit/phpunit', packagist], + ['php', 'squizlabs/php_codesniffer', packagist], +]; + +const results = await Promise.all( + DEPENDENCIES.map(async ([language, name, lookup]) => { + try { + return [language, name, await lookup(name)]; + } catch (error) { + return [language, name, `error: ${error.message}`]; + } + }), +); + +for (const [language, name, version] of results) { + console.log(`${language}\t${name}\t${version}`); +} diff --git a/go/VERSION b/go/VERSION index a5510516..04a373ef 100644 --- a/go/VERSION +++ b/go/VERSION @@ -1 +1 @@ -0.15.0 +0.16.0 diff --git a/go/go.mod b/go/go.mod index 85e02ca7..6e67d7d3 100644 --- a/go/go.mod +++ b/go/go.mod @@ -1,4 +1,4 @@ module github.com/link-foundation/links-notation/go -go 1.21 +go 1.24 diff --git a/java/.mvn/README.md b/java/.mvn/README.md index 8b5c6cb5..985d392a 100644 --- a/java/.mvn/README.md +++ b/java/.mvn/README.md @@ -4,7 +4,7 @@ [google-java-format](https://github.com/google/google-java-format) needs, because Spotless runs the formatter in-process and the formatter reads `javac`'s internal AST classes. -Without these flags JDK 11 prints +Without these flags an older JDK prints ``` WARNING: An illegal reflective access operation has occurred diff --git a/java/README.md b/java/README.md index a8c583a8..5acb9796 100644 --- a/java/README.md +++ b/java/README.md @@ -246,7 +246,7 @@ mvn spotless:check ## Requirements -- Java 11 or higher +- Java 21 or higher - Maven 3.6+ ## Package Information diff --git a/java/README.ru.md b/java/README.ru.md index e63237d9..35068800 100644 --- a/java/README.ru.md +++ b/java/README.ru.md @@ -246,7 +246,7 @@ mvn spotless:check ## Требования -- Java 11 или выше +- Java 21 или выше - Maven 3.6+ ## Информация о пакете diff --git a/java/pom.xml b/java/pom.xml index d714364d..8624edab 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -6,7 +6,7 @@ io.github.link-foundation links-notation - 0.15.0 + 0.16.0 jar Links Notation @@ -37,12 +37,13 @@ - - 11 + + 21 UTF-8 - 5.14.4 + 6.1.3 @@ -103,11 +104,11 @@ com.diffplug.spotless spotless-maven-plugin - 2.46.1 + 3.10.1 - 1.19.2 + 1.36.1 diff --git a/js/bun.lock b/js/bun.lock index 0295fa3e..a017749d 100644 --- a/js/bun.lock +++ b/js/bun.lock @@ -5,8 +5,8 @@ "": { "name": "@linksplatform/protocols-lino", "devDependencies": { - "bun-types": "^1.3.14", - "eslint": "^10.8.1", + "bun-types": "^1.4.0", + "eslint": "^10.9.1", "peggy": "^5.1.0", "prettier": "^3.9.6", "typescript": "^7.0.2", @@ -96,7 +96,7 @@ "brace-expansion": ["brace-expansion@5.0.9", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg=="], - "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], + "bun-types": ["bun-types@1.4.0", "", { "dependencies": { "@types/node": "*" } }, "sha512-iIKw23BspnQQYd3prITOBxeUsxBHnwzX6YJfGMuNOZzeNcMmVqzIIVGRm1l69ogaPQmb4wB6BN8mA5bE9YuC5Q=="], "commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], @@ -108,7 +108,7 @@ "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], - "eslint": ["eslint@10.8.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", "@eslint/config-helpers": "^0.7.0", "@eslint/core": "^1.2.1", "@eslint/plugin-kit": "^0.7.2", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.2.0", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "minimatch": "^10.2.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ=="], + "eslint": ["eslint@10.9.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", "@eslint/config-helpers": "^0.7.0", "@eslint/core": "^1.2.1", "@eslint/plugin-kit": "^0.7.2", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.2.0", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "minimatch": "^10.2.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-9VaAkDURekixUQJy0oJYl2DcN6oKMfxay7XzaGYAWQwsb6qfKf+x76R2k1L8kb1boc+FyCAaTA9GmiKaaiaF+A=="], "eslint-scope": ["eslint-scope@9.1.2", "", { "dependencies": { "@types/esrecurse": "^4.3.1", "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ=="], diff --git a/js/package-lock.json b/js/package-lock.json index 168d449b..320ee7db 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -1,16 +1,16 @@ { "name": "links-notation", - "version": "0.15.0", + "version": "0.16.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "links-notation", - "version": "0.15.0", + "version": "0.16.0", "license": "Unlicense", "devDependencies": { - "bun-types": "^1.3.14", - "eslint": "^10.8.1", + "bun-types": "^1.4.0", + "eslint": "^10.9.1", "peggy": "^5.1.0", "prettier": "^3.9.6", "typescript": "^7.0.2" @@ -683,9 +683,9 @@ } }, "node_modules/eslint": { - "version": "10.9.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.9.0.tgz", - "integrity": "sha512-5KeEOJZBfEVA47boFiBsf+6MmmJpffM7qEBg4pLla2e4nlKgdKlqCW0oSLOGsT8Wl5uCGJptLV1bkaiShj90Gw==", + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.9.1.tgz", + "integrity": "sha512-9VaAkDURekixUQJy0oJYl2DcN6oKMfxay7XzaGYAWQwsb6qfKf+x76R2k1L8kb1boc+FyCAaTA9GmiKaaiaF+A==", "dev": true, "license": "MIT", "workspaces": [ diff --git a/js/package.json b/js/package.json index 39333178..a2924eee 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "links-notation", - "version": "0.15.0", + "version": "0.16.0", "description": "Links Notation parser for JavaScript", "main": "dist/index.js", "types": "index.d.ts", @@ -32,8 +32,8 @@ }, "homepage": "https://github.com/link-foundation/links-notation#readme", "devDependencies": { - "bun-types": "^1.3.14", - "eslint": "^10.8.1", + "bun-types": "^1.4.0", + "eslint": "^10.9.1", "peggy": "^5.1.0", "prettier": "^3.9.6", "typescript": "^7.0.2" diff --git a/php/README.md b/php/README.md index 8513db49..a4502b00 100644 --- a/php/README.md +++ b/php/README.md @@ -264,7 +264,7 @@ Exception thrown when parsing fails. ## Requirements -- PHP 8.1 or higher +- PHP 8.4 or higher - `ext-mbstring` - Composer 2 diff --git a/php/README.ru.md b/php/README.ru.md index 6a13b73b..90d74971 100644 --- a/php/README.ru.md +++ b/php/README.ru.md @@ -264,7 +264,7 @@ echo Formatter::formatLinks($parser->parse($input)); ## Требования -- PHP 8.1 или новее +- PHP 8.4 или новее - расширение `ext-mbstring` - Composer 2 diff --git a/php/composer.json b/php/composer.json index 154634b4..ce0162b1 100644 --- a/php/composer.json +++ b/php/composer.json @@ -5,7 +5,7 @@ "keywords": ["links", "notation", "lino", "parser", "formatter", "links-platform"], "homepage": "https://github.com/link-foundation/links-notation", "license": "Unlicense", - "version": "0.15.0", + "version": "0.16.0", "authors": [ { "name": "Link Foundation", @@ -17,12 +17,12 @@ "source": "https://github.com/link-foundation/links-notation" }, "require": { - "php": ">=8.1", + "php": ">=8.4", "ext-mbstring": "*" }, "require-dev": { - "phpunit/phpunit": "^10.5", - "squizlabs/php_codesniffer": "^3.10" + "phpunit/phpunit": "^13.3", + "squizlabs/php_codesniffer": "^4.0" }, "autoload": { "psr-4": { diff --git a/python/pyproject.toml b/python/pyproject.toml index 70fc403c..ebb44fcf 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "links-notation" -version = "0.15.0" +version = "0.16.0" description = "Python implementation of the Links Notation parser" readme = "README.md" license = "Unlicense" @@ -29,9 +29,9 @@ requires-python = ">=3.9" dependencies = [] [project.optional-dependencies] -test = ["pytest>=9.0", "pytest-timeout>=2.4"] -lint = ["black>=26.0", "isort>=8.0", "flake8>=7.3"] -dev = ["pytest>=9.0", "pytest-timeout>=2.4", "black>=26.0", "isort>=8.0", "flake8>=7.3"] +test = ["pytest>=9.1", "pytest-timeout>=2.4"] +lint = ["black>=26.5", "isort>=9.0", "flake8>=7.3"] +dev = ["pytest>=9.1", "pytest-timeout>=2.4", "black>=26.5", "isort>=9.0", "flake8>=7.3"] [project.urls] Homepage = "https://github.com/link-foundation/links-notation" diff --git a/rust/links-notation/Cargo.toml b/rust/links-notation/Cargo.toml index 6e73db63..4d533053 100644 --- a/rust/links-notation/Cargo.toml +++ b/rust/links-notation/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "links-notation" -version = "0.15.0" +version = "0.16.0" edition = "2021" description = "Rust implementation of the Links Notation parser" license = "Unlicense" diff --git a/scripts/declared-versions.mjs b/scripts/declared-versions.mjs new file mode 100644 index 00000000..02bc001d --- /dev/null +++ b/scripts/declared-versions.mjs @@ -0,0 +1,37 @@ +// The version each language implementation declares in its own source tree. +// +// Kept in one place because two checks need it: scripts/version-consistency.mjs +// (do the implementations agree with each other?) and scripts/release-audit.mjs +// (does each one agree with its registry?). + +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +const root = new URL('..', import.meta.url).pathname; +export const read = (p) => readFileSync(join(root, p), 'utf8'); + +export const match = (text, re, what) => { + const m = text.match(re); + if (!m) throw new Error(`could not read ${what}`); + return m[1].trim(); +}; + +export const declaredVersions = { + js: () => JSON.parse(read('js/package.json')).version, + python: () => match(read('python/pyproject.toml'), /^version\s*=\s*"([^"]+)"/m, 'python version'), + rust: () => match(read('rust/links-notation/Cargo.toml'), /^version\s*=\s*"([^"]+)"/m, 'rust version'), + csharp: () => + match( + read('csharp/Link.Foundation.Links.Notation/Link.Foundation.Links.Notation.csproj'), + /([^<]+)<\/VersionPrefix>/, + 'csharp version', + ), + go: () => read('go/VERSION').trim(), + java: () => + match( + read('java/pom.xml'), + /links-notation<\/artifactId>\s*([^<]+)<\/version>/, + 'java version', + ), + php: () => JSON.parse(read('php/composer.json')).version, +}; diff --git a/scripts/release-audit.mjs b/scripts/release-audit.mjs index 1867b072..d8d5e0a6 100644 --- a/scripts/release-audit.mjs +++ b/scripts/release-audit.mjs @@ -10,19 +10,10 @@ // Run it locally with `node scripts/release-audit.mjs`; CI runs it from // .github/workflows/release-audit.yml and annotates every mismatch. -import { readFileSync } from 'node:fs'; -import { join } from 'node:path'; +import { declaredVersions, match, read } from './declared-versions.mjs'; -const root = new URL('..', import.meta.url).pathname; -const read = (p) => readFileSync(join(root, p), 'utf8'); const verbose = process.env.CI_VERBOSE === 'true'; -const match = (text, re, what) => { - const m = text.match(re); - if (!m) throw new Error(`could not read ${what}`); - return m[1].trim(); -}; - async function head(url) { const response = await fetch(url, { headers: { 'user-agent': 'links-notation-release-audit' } }); if (verbose) console.log(` GET ${url} -> ${response.status}`); @@ -33,7 +24,7 @@ const languages = [ { name: 'js', registry: 'npm', - declared: () => JSON.parse(read('js/package.json')).version, + declared: declaredVersions.js, published: async () => { const name = JSON.parse(read('js/package.json')).name; const r = await head(`https://registry.npmjs.org/${name}`); @@ -44,7 +35,7 @@ const languages = [ { name: 'python', registry: 'PyPI', - declared: () => match(read('python/pyproject.toml'), /^version\s*=\s*"([^"]+)"/m, 'python version'), + declared: declaredVersions.python, published: async () => { const name = match(read('python/pyproject.toml'), /^name\s*=\s*"([^"]+)"/m, 'python name'); const r = await head(`https://pypi.org/pypi/${name}/json`); @@ -55,7 +46,7 @@ const languages = [ { name: 'rust', registry: 'crates.io', - declared: () => match(read('rust/links-notation/Cargo.toml'), /^version\s*=\s*"([^"]+)"/m, 'rust version'), + declared: declaredVersions.rust, published: async () => { const r = await head('https://crates.io/api/v1/crates/links-notation'); if (!r.ok) return null; @@ -65,12 +56,7 @@ const languages = [ { name: 'csharp', registry: 'NuGet.org', - declared: () => - match( - read('csharp/Link.Foundation.Links.Notation/Link.Foundation.Links.Notation.csproj'), - /([^<]+)<\/VersionPrefix>/, - 'csharp version', - ), + declared: declaredVersions.csharp, published: async () => { const r = await head('https://api.nuget.org/v3-flatcontainer/link.foundation.links.notation/index.json'); if (!r.ok) return null; @@ -81,7 +67,7 @@ const languages = [ { name: 'go', registry: 'proxy.golang.org', - declared: () => read('go/VERSION').trim(), + declared: declaredVersions.go, published: async () => { const r = await head('https://proxy.golang.org/github.com/link-foundation/links-notation/go/@latest'); if (!r.ok) return null; @@ -91,7 +77,7 @@ const languages = [ { name: 'java', registry: 'Maven Central', - declared: () => match(read('java/pom.xml'), /links-notation<\/artifactId>\s*([^<]+)<\/version>/, 'java version'), + declared: declaredVersions.java, published: async () => { const r = await head( 'https://repo1.maven.org/maven2/io/github/link-foundation/links-notation/maven-metadata.xml', @@ -104,7 +90,7 @@ const languages = [ { name: 'php', registry: 'Packagist', - declared: () => JSON.parse(read('php/composer.json')).version, + declared: declaredVersions.php, published: async () => { const r = await head('https://repo.packagist.org/p2/link-foundation/links-notation.json'); if (!r.ok) return null; diff --git a/scripts/version-consistency.mjs b/scripts/version-consistency.mjs new file mode 100644 index 00000000..1d25c657 --- /dev/null +++ b/scripts/version-consistency.mjs @@ -0,0 +1,47 @@ +#!/usr/bin/env node +// Fails when the language implementations do not all declare the same version. +// +// The point of shipping the same grammar in seven languages is that a document +// written by one reads identically in the others, which only holds while they +// are the same release. They had drifted to js/python/rust/csharp/go 0.15.0, +// java 0.3.0 and php 0.2.0 before this check existed, and nothing failed. +// +// Run it locally with `node scripts/version-consistency.mjs`; CI runs it from +// .github/workflows/release-audit.yml on every pull request. Unlike the +// registry audit, this one is a hard failure: it reads only the working tree, +// so it is deterministic and a disagreement is always a defect. + +import { declaredVersions } from './declared-versions.mjs'; + +const declared = new Map(); +let failed = false; + +for (const [name, read] of Object.entries(declaredVersions)) { + try { + declared.set(name, read()); + } catch (error) { + console.log(`::error::${name}: ${error.message}`); + failed = true; + } +} + +const byVersion = new Map(); +for (const [name, version] of declared) { + if (!byVersion.has(version)) byVersion.set(version, []); + byVersion.get(version).push(name); +} + +for (const [name, version] of declared) console.log(`${name}: ${version}`); + +if (byVersion.size > 1) { + const groups = [...byVersion.entries()] + .sort((a, b) => b[1].length - a[1].length) + .map(([version, names]) => `${version} (${names.join(', ')})`) + .join('; '); + console.log(`::error::the implementations declare different versions: ${groups}`); + failed = true; +} else if (!failed) { + console.log(`\nAll ${declared.size} implementations declare ${[...byVersion.keys()][0]}.`); +} + +process.exit(failed ? 1 : 0);