Repository navigation
Promote develop to main (2026-10-12) - #44
Merged
Merged
Conversation
Bumps [org.assertj:assertj-core](https://github.com/assertj/assertj) from 3.27.4 to 3.27.7. - [Release notes](https://github.com/assertj/assertj/releases) - [Commits](assertj/assertj@assertj-build-3.27.4...assertj-build-3.27.7) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-version: 3.27.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [gradle/actions](https://github.com/gradle/actions) from 4 to 6. - [Release notes](https://github.com/gradle/actions/releases) - [Commits](gradle/actions@v4...v6) --- updated-dependencies: - dependency-name: gradle/actions dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 9. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](actions/github-script@v7...v9) --- updated-dependencies: - dependency-name: actions/github-script dependency-version: '9' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [org.junit:junit-bom](https://github.com/junit-team/junit-framework) from 5.13.4 to 6.1.3. - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](junit-team/junit-framework@r5.13.4...r6.1.3) --- updated-dependencies: - dependency-name: org.junit:junit-bom dependency-version: 6.1.3 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v4...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 6. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](actions/upload-artifact@v4...v6) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4 to 6. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](actions/setup-java@v4...v6) --- updated-dependencies: - dependency-name: actions/setup-java dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 8.14.4 to 9.8.0. - [Release notes](https://github.com/gradle/gradle/releases) - [Commits](gradle/gradle@v8.14.4...v9.8.0) --- updated-dependencies: - dependency-name: gradle-wrapper dependency-version: 9.8.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps org.gradle.toolchains.foojay-resolver-convention from 0.9.0 to 1.0.0. --- updated-dependencies: - dependency-name: org.gradle.toolchains.foojay-resolver-convention dependency-version: 1.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
The Gradle 9.8.0 wrapper update committed it with CRLF in the repository, so every checkout showed it as modified. The working copy keeps CRLF; only what the repository stores changes.
A composite build runs the included build under this repository's Gradle, 9.8.0 since the wrapper update, and relix-core is still on 8.14.4, where its pitest plugin fails on Gradle 9.
TSV is IANA TSV with a header row; a tab, line feed, carriage return or backslash in a value is escaped, so a row is always one line for cut, awk and sort. NDJSON is one object per line, in the json format's encoding. Each format now hands back an encoder that turns a result set into text a row at a time, so the command can write, flush and check each row itself. The delimited formats take a header option and a NULL spelling, which the table and Markdown formats honour too. Fixes #5
…n interrupt -o/--output, or RELIX_OUTPUT, chooses the format; with neither, a terminal gets a table and a pipe gets tsv. --no-header, --null, --color (honouring NO_COLOR, and colouring only a table) and --line-buffered modify it. A table or Markdown prints every query, labelled. A machine format prints one result set: the script's last query, or the one --query=NAME names; a query not printed is not run. --all with ndjson prints every query, each row tagged "_query": NAME, and is a usage error with any other machine format. --fail-empty and --fail-rows exit 1 on what the printed queries returned. Rows stream through a buffered sink over the raw standard output, flushed at the end of each result set or per row with --line-buffered. A failed write closes the row stream, which the engine takes as cancellation, and the run exits 141 silently, so relix -e Naturals | head ends when head does. A shutdown hook cancels the running query and closes the stream and the session on SIGINT; the run exits 130. Fixes #6
-i binds a relation name to a file, an http(s) URL (with --remote) or
standard input, in csv, tsv, json or ndjson. Without a format the extension
decides; standard input must name one. The engine's own readers parse it
(Relix.input), so a file or URL is reopened for each read and standard input
is read once.
The heading is inferred from the first --infer-rows records, or declared
with --schema NAME='{ ... }', read by the engine's parser as a source's
schema block. --unbounded=NAME marks an input that never ends, so that
tail -f ... | relix -i log=ndjson:- streams.
Only one input may be standard input, and then the script must come from
-e or one file. A malformed binding, a format that cannot be told, a file
that is not there, a URL without --remote, a name bound twice, and a
--schema or --unbounded for a name no -i binds are usage errors; text that
cannot be read as its format fails the script with exit 4.
Shadowing a catalog relation waits for the catalog (#8). The test that a
blocking operator over an unbounded input is refused is disabled: the
engine's plan-time check does not yet see an unbounded Relix.input.
Part of #7
…nning A run now starts with the declarations of every catalog/*.relix file in the .relix/ directories from ~/.relix down to the working directory (stopping after $HOME or at a root marker), then the $RELIX_CATALOG_PATH and --catalog files. Each file is parsed under its absolute path, so a relative source path reads from beside the file. The nearest declaration of a name replaces the farther ones, and the survivors are defined together before the -i inputs and the script, each of which can replace a catalog name in turn. relixrc sets default output and profile; options and environment variables beat it. -N turns discovery off. Part of #8.
A project catalog can declare a connection or HTTP source that sends a
${SECRET} to a host of its author's choosing, so, as direnv does, a project
.relix/ is now loaded only after `relix catalog trust [DIR]` has recorded a
hash of its files in ~/.relix/trusted. Changing, adding or removing any file
there, profiles.json and relixrc included, means trusting it again.
`--list` shows each trusted directory and whether it has changed;
`--revoke DIR` removes one.
An untrusted level is parsed but never defined, and is skipped with one
stderr line naming the command that trusts it. A script that then needs a
name only that level declares exits 5. ~/.relix, --catalog files and the
script are trusted without asking, and RELIX_TRUST_ALL=1 trusts everything.
The format options move into a mixin of their own, so that the catalog's
listings print through the same renderers as a run.
Part of #9.
`files` lists every catalog file in load order, with the names each declares and whether it is trusted. `ls` lists the relations with their kind, arity and the file whose declaration won; `schema NAME` lists a relation's columns and types. Both are queries over the engine's relix.relations and relix.columns, in a session holding only the catalog. `where NAME` lists every declaration of a name, nearest first, as wins, shadowed or untrusted. Each prints rows through the run's renderers, so a table on a terminal and tsv or ndjson in a pipe. `schema` and `where` exit 3 for a name the catalog does not declare. Part of #10.
… provenance Each view of a script is now a command that writes one thing to stdout. They read scripts as run does, with the catalog and -i inputs, through one loop (ScriptRunner) that takes each script through its session, its inputs and analysis and hands it to the command's view; run's rows are one such view. - check: nothing on stdout, diagnostics on stderr, exit 0 or 3, so `find … | xargs relix check` is a lint pass. --json writes every script's diagnostics as one document instead. - explain: each query's physical plan; --json writes one object per query per line. - optimize: the script with each query statement replaced by its rewritten query, every other statement and comment kept as written, so it works as a filter. A rewrite the engine cannot render as Relix that parses back is left as written, with a warning. --report prints the rules and the trees. - trace: the event feed of each query, rows discarded, each query's feed ending in a ROWS event. - bundle: the playground's JSON bundle, still produced for a script that does not analyse. - ir: the IR report of the whole script. - provenance: annotated rows, --semiring and --weight; a table on a terminal and JSON in a pipe. `run --trace[=FILE]` writes the event feed to stderr, or FILE, while the rows go to stdout. A file is named only with `=`, so `relix --trace q.relix` traces q.relix rather than writing over it. The example scripts the tests run every command over are in src/test/resources/examples. Part of #11.
`relix fmt [SCRIPT...]` prints each script with the engine's statement printer, the one behind Relix.definitions(), and standard input when no file is named. `-w` rewrites the files that change in place, and `--check` changes nothing, names each file that would change, and exits 1 if any would. `--glyphs` (the default) and `--keywords` choose the operator spelling. The keyword spelling is read from the reference's spellings page. The printer drops comments, and DarkCollective/relix-core#99 asks for that and the spelling choice in the engine. Until then fmt keeps them itself: - a comment between statements stays where it was, on its own line or after the statement it followed; - a statement with a comment inside it is left exactly as written; - blank lines between things collapse to one. The comments are found by ScriptText's scan, which follows the language's two lexers: `--` and `/* */` outside strings, and only at the start of a line inside an inline table, whose rule and cells may hold `--`. Inline tables' columns are aligned. A script that does not parse is reported as FILE:LINE:COL, left alone, and the run exits 3. Over every example script, fmt is idempotent, parses back to the same statements in either spelling, and keeps every comment in order. Part of #12.
… completion - `drivers ls` lists the JDBC drivers relix can install, with each one's scheme and whether it is installed. `drivers install NAME` downloads and installs one. Together they replace the run-time --download-drivers. - `connectors ls` lists the connectors installed, and `--available` adds the ones the hosted catalog offers. `connectors install TYPE` installs one. - `doc [TOPIC]` shows a reference page, found by glyph, keyword or name, rendered for a terminal and paged through $PAGER on one. `--markdown` prints the page as it is. With no topic it lists the pages as rows. A language keyword keeps a name it shares with a function (`doc fix` is the operator), and `--function` finds the function. - `help [COMMAND]` is picocli's, and `version` prints the front-end and engine versions. - `completion bash|zsh|fish|powershell` prints a completion script built from the command model. bash's is picocli's, which runs under zsh too. fish and PowerShell get theirs here. The listings print through the run's renderers. The index doc looks pages up in is built here, over relix-docs' language pages and the installed functions' pages, until DarkCollective/relix-core#95 puts it in relix-docs. It holds every key of relix-console's index except the REPL's page about itself. Each completion script is syntax-checked by its shell when that shell is installed. CI installs zsh and fish on Linux for this; the runners already have bash and PowerShell. Part of #13.
* Re-enable the test that a blocking operator over an unbounded input is refused DarkCollective/relix-core#98 made stream() refuse τ and γ over an unbounded Relix.input with UnboundedRelationException, so the test #34 disabled now runs, for τ and for γ, and checks the error names the input as unbounded. δ is not among them: it streams by design. * Print relix fmt's output with the engine's script printer DarkCollective/relix-core#99 made ScriptPrinter keep a script's comments and print the ASCII spelling, so fmt no longer scans for comments itself or respells the glyphs token by token. It prints ScriptPrinter.print(Relix.parse(text, source), spelling) and aligns the inline tables' columns, as before. A statement with a comment inside it is now formatted too, with the comment kept. The engine's printer drops a blank line between a comment and the statement or namespace after it; a disabled test records that until the engine keeps it. * Print every relix optimize rewrite, now that each one parses DarkCollective/relix-core#100 made render() of an optimized relation always parse back, a seeded closure included, so optimize no longer parses each rewrite, leaves a query it cannot write as written, or warns about it. The introspection example's closure query is now rewritten, and the test checks the output with relix check; the examples' optimize runs write nothing to stderr. * Look relix doc's pages up in relix-docs' ReferenceLookup DarkCollective/relix-core#95 put the reference index in relix-docs: ReferenceLookup indexes the language pages and the installed functions' pages, and a language keyword keeps a name it shares with a function. That is what DocIndex built here, so DocIndex and its test go and doc uses ReferenceLookup. relix-cli has no pages of its own, so it passes none. The listing gives the functions grouped by category, as ReferenceLookup offers them, rather than in the order the libraries list them.
The engine's printer now writes a comment after a Markdown row back on that row (relix-core fix/table-row-comments). fmt's column alignment took a row to end in |, so a commented row ended the run being aligned and every row after it was left as the printer wrote it. A row's cells are now aligned without its comment, which follows them after two spaces.
A value given with -a is bound to $NAME through the engine's Relix.Builder.parameters, never pasted into the script, so a value holding a quote cannot change the query. The value is text, which the engine reads as the type the parameter was analysed to (relix-core#101): -a id=42 binds a number when $id is compared with a NUMBER, and text that does not read as that type is an analysis error. An unbound parameter is an analysis error (exit 3) that names it. The engine's message names its Java remedy, so EngineMessages puts it in the command's terms: "give it a value with -a NAME=VALUE". fmt, bundle and optimize print $NAME, never the value. Part of #14.
DarkCollective/relix-core#107 made the engine's printer keep a blank line between a comment and the statement or namespace after it, so FmtTest.Layout.blankAfterComment runs again: "-- header", a blank line, "namespace demo;", then a comment set apart from its query the same way, format to themselves.
`relix completion zsh` printed bash's script, which zsh runs through bashcompinit when it is sourced. Homebrew installs zsh completions as a file on $fpath instead, which compinit binds by its first line and runs as a function on the first Tab. The script now starts with `#compdef relix`, and ends by completing the word it was called for when it runs inside a completion; sourced, it does what it did before. Part of #15.
…ease ./gradlew releaseArchive builds this platform's archive: the jlink image, with relix-solver-ojalgo and relix-docs, picocli and the H2 driver force-merged; man/man1, one page per command from picocli's ManPageGenerator through Asciidoctor's manpage backend; and completions/ for bash, zsh, fish and PowerShell. H2 is now a runtime dependency, the one driver the image bundles. Each stage of a pipeline starts a JVM, so the image carries an AppCDS archive (lib/relix.jsa), trained at build time on a run that joins and groups a bound CSV file, over the JDK archive jlink generates. The launcher maps it and sends the JVM's warnings to standard error rather than into the rows. The generated launchers are patched: the sh one resolves a relative symbolic link, as Homebrew installs it, and the Windows one no longer runs from its own bin directory, which took relative paths and .relix/ discovery away from the user's. ./gradlew packageTest unpacks the archive with the system's tar and runs its launcher: a script on stdin over a catalog in a temporary tree (a CSV source and an H2 connection, trusted first), tsv out; OPTIMIZE and relix doc, for the providers jlink would drop unnoticed; -Xshare:on with the command's classes loaded from the archive, and the same flag refusing a missing one; and man over every page. CI runs it on each platform. The Release workflow builds and tests the five platform archives. On a tag it attaches them and checksums.txt to the GitHub Release and pushes the formula and the manifest, written whole from packaging/, to the tap and the bucket; run by hand it is a dry run, which checks their checksums against the archives and publishes nothing. Part of #15.
docs/guide/command-line is the command-line section of the Relix programming guide, laid out as the engine's manuals are: a README.md index of chapters, and index.json as the machine-readable index. Hand-written pages cover the command and where scripts come from, input binding (-i, formats, standard input, headings, unbounded inputs, -a), output formats, the catalog (discovery, the overlay, trust, relixrc), secrets and profiles, and pipelines with jq, awk, find and xargs. The command reference, commands/*.md, is generated by CommandPages from the picocli model --help and the man pages come from, followed by each command's hand-written examples in docs/command-examples. ./gradlew commandPages rewrites them; GuideStructureTest fails while they are stale, while the index misses a page, or while a link to a page or a heading, here or in the engine's manuals, is broken. GuideExamplesTest runs every shell block over a copy of the fixture under src/test/resources/docs-fixture, the ~/shop the pages describe, and compares standard output and the exit status. A plain relix command runs in the test's JVM; anything else runs through bash, with a relix shim that treats a stage's streams as a terminal when they are the example's own, as a reader's would be. The guard was probed by mutation: a changed row, a changed or unshown exit status, a dropped output block, a broken link or heading, an unlisted page, a stale command page, an unrun bash block, a change to the table renderer and a change to the harness itself each fail it. ./gradlew docsArchive builds relix-<version>-docs.zip, which the release workflow attaches to each GitHub Release for the site to render. Fixes #16
… own The site renders these chapters after the engine's programming guide's, so each says it is about the command line.
…dings On Linux the JVM decodes its arguments by the locale, so under LC_ALL=C `relix doc σ` looked up '??'. A reader's terminal is UTF-8. On Windows, text=auto checked the generated command pages out with CRLF endings, which then differed from the generator's output; they keep LF, as the .relix files the tests compare do.
picocli wraps the usage synopsis with the platform's line separator, so on Windows the generated pages held CRLF and never matched the checked-in ones.
rc9 is released and carries everything the command uses, so develop builds against it rather than the engine's snapshot, and the snapshot repository drops out of the build. The pin's comment and CONTRIBUTING now say so: develop pins a released engine while one has what the command needs, and returns to 1.0.0-SNAPSHOT only while it needs engine work that is not yet released.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The batch for 2026-10-12: thirty-one commits from
develop, tidied from thirty-five. It is the first promotion since the command skeleton, and it carries the first release's contents: the engine pinned at 1.0.0-rc9 and the release workflow.The four
fixup!commits are squashed into the commits they fix (the report commands, the management commands, catalog inspection, and trusted.relix/loading), andgit diff origin/develop promote/2026-10-12prints nothing.Merge with Rebase and merge, then reset
developto the newmain. That putsrelease.ymlon the default branch, so its dry run can be dispatched beforev0.1.0-rc1is tagged.