This is an automated email from the ASF dual-hosted git repository.
tisonkun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datasketches-tck.git
The following commit(s) were added to refs/heads/main by this push:
new e748b0a Make the latest snapshots from every language the default
(#15)
e748b0a is described below
commit e748b0a038a17c6d5c771afc4efd9ee35a5a7ec5
Author: Lee Rhodes <[email protected]>
AuthorDate: Sun Oct 4 17:17:22 2026 -0700
Make the latest snapshots from every language the default (#15)
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: tison <[email protected]>
---
.github/workflows/update-snapshots.yml | 90 ++++++++++++++++++++++++++++++++++
cmd/tck/__snapshots__/output_test.snap | 12 +++++
cmd/tck/output.go | 9 ++++
cmd/tck/output_test.go | 20 ++++++++
cmd/tck/snapshots.go | 2 +-
docs/serialization-snapshots.md | 41 ++++++++++++----
internal/snapshots/reconcile.go | 9 ++++
7 files changed, 172 insertions(+), 11 deletions(-)
diff --git a/.github/workflows/update-snapshots.yml
b/.github/workflows/update-snapshots.yml
new file mode 100644
index 0000000..a00d33b
--- /dev/null
+++ b/.github/workflows/update-snapshots.yml
@@ -0,0 +1,90 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Publishes the latest self-tested snapshots from every source language, so
+# that main always holds the newest snapshot set. This workflow is not a
+# required check.
+
+name: Update snapshots
+
+on:
+ schedule:
+ - cron: "0 6 * * 1"
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+concurrency:
+ group: ${{ github.workflow }}
+ cancel-in-progress: false
+
+jobs:
+ update:
+ name: Update snapshots from every source
+ if: github.repository == 'apache/datasketches-tck'
+ runs-on: ubuntu-24.04
+ timeout-minutes: 90
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/checkout@v7
+
+ - name: Setup mise
+ uses: jdx/mise-action@5228313ee0372e111a38da051671ca30fc5a96db # v3.6.3
+
+ - name: Update snapshots
+ id: update
+ shell: bash
+ run: |
+ status=0
+ for language in cpp go java; do
+ if ! mise run tck -- snapshots update "$language" HEAD 2>&1 | tee
-a update.log; then
+ status=1
+ fi
+ done
+ echo "status=${status}" >> "$GITHUB_OUTPUT"
+ {
+ echo '## Snapshot update'
+ echo
+ echo '```'
+ grep -E '^(Update |Summary:|Source revision:|✓|✗|Error:)'
update.log || true
+ echo '```'
+ } >> "$GITHUB_STEP_SUMMARY"
+
+ # Sources that updated successfully are published even when another
+ # source failed, so one broken upstream does not hold back the others.
+ - name: Commit updated snapshots
+ run: |
+ if [[ -z "$(git status --porcelain -- config.toml
serialization_test_data)" ]]; then
+ echo "Snapshots are already up to date."
+ exit 0
+ fi
+ git config user.name "github-actions[bot]"
+ git config user.email
"41898282+github-actions[bot]@users.noreply.github.com"
+ git add -- config.toml serialization_test_data
+ {
+ echo "chore: update snapshots to the latest upstream revisions"
+ echo
+ grep -E '^(Update |Summary:|Source revision:)' update.log || true
+ } > commit-message.txt
+ git commit --quiet --file commit-message.txt
+ git push origin HEAD:${{ github.ref_name }}
+
+ - name: Report failed sources
+ if: steps.update.outputs.status != '0'
+ run: |
+ grep -E '^(✗|Error:)' update.log || true
+ exit 1
diff --git a/cmd/tck/__snapshots__/output_test.snap
b/cmd/tck/__snapshots__/output_test.snap
index 33d7e1b..7f29055 100644
--- a/cmd/tck/__snapshots__/output_test.snap
+++ b/cmd/tck/__snapshots__/output_test.snap
@@ -24,3 +24,15 @@ Summary: 1 added · 1 modified (1 probabilistic) · 1 deleted
✗ serialization_test_data/go_generated_files has 2 blocking changes.
---
+
+[TestPrintResultReportsUnwrittenUpdate - 1]
+╭────────┬───────────┬───────┬──────────────────┬─────────────────────────╮
+│ CHANGE │ STABILITY │ CHECK │ SNAPSHOT │ CONTENT (SIZE · SHA256) │
+├────────┼───────────┼───────┼──────────────────┼─────────────────────────┤
+│ M │ unstable │ allow │ probabilistic.sk │ old 12 B · aaaaaaaaaaaa │
+│ │ │ │ │ new 12 B · bbbbbbbbbbbb │
+╰────────┴───────────┴───────┴──────────────────┴─────────────────────────╯
+Summary: 0 added · 1 modified (1 probabilistic) · 0 deleted
+✓ snapshots already matches its source revision; 1 probabilistic content
modification was not written.
+
+---
diff --git a/cmd/tck/output.go b/cmd/tck/output.go
index 401e04b..4776f46 100644
--- a/cmd/tck/output.go
+++ b/cmd/tck/output.go
@@ -85,6 +85,15 @@ func printResult(output io.Writer, root string, mode
snapshots.Mode, result snap
}
switch {
+ case (mode == snapshots.ModeUpdate || mode == snapshots.ModeSync) &&
!result.Written:
+ _, err := fmt.Fprintf(
+ output,
+ "✓ %s already matches its source revision; %d
probabilistic content %s not written.\n",
+ target,
+ unstableModified,
+ plural(unstableModified, "modification was",
"modifications were"),
+ )
+ return err
case mode == snapshots.ModeUpdate:
_, err := fmt.Fprintf(output, "✓ Updated %s.\n", target)
return err
diff --git a/cmd/tck/output_test.go b/cmd/tck/output_test.go
index 49b8a2b..15f2d2c 100644
--- a/cmd/tck/output_test.go
+++ b/cmd/tck/output_test.go
@@ -74,6 +74,26 @@ func TestPrintResultAllowsOnlyUnstableModifications(t
*testing.T) {
snaps.WithConfig(snaps.Raw()).MatchSnapshot(t, output.String())
}
+func TestPrintResultReportsUnwrittenUpdate(t *testing.T) {
+ t.Parallel()
+
+ before := testMetadata(12, 0xaa)
+ after := testMetadata(12, 0xbb)
+ result := snapshots.Result{
+ Target: "snapshots",
+ PreviousRevision: "a",
+ Revision: "a",
+ Changes: []snapshots.Change{{
+ Status: snapshots.ChangeModified, Stability:
snapshots.Unstable,
+ Path: "probabilistic.sk", Before: &before, After:
&after,
+ }},
+ }
+
+ var output bytes.Buffer
+ require.NoError(t, printResult(&output, ".", snapshots.ModeUpdate,
result))
+ snaps.WithConfig(snaps.Raw()).MatchSnapshot(t, output.String())
+}
+
func testMetadata(size int64, fill byte) snapshots.FileMetadata {
metadata := snapshots.FileMetadata{Size: size}
for index := range metadata.SHA256 {
diff --git a/cmd/tck/snapshots.go b/cmd/tck/snapshots.go
index be11fc6..19c8aca 100644
--- a/cmd/tck/snapshots.go
+++ b/cmd/tck/snapshots.go
@@ -172,7 +172,7 @@ func reconcileDetails(mode snapshots.Mode) string {
case snapshots.ModeSync:
return "Regenerate all snapshot directories from the
repositories and commits in config.toml without changing the config."
case snapshots.ModeUpdate:
- return "Resolve an upstream commit, branch, or tag, record its
exact commit ID in config.toml, and regenerate that source's snapshots."
+ return "Resolve an upstream commit, branch, or tag, record its
exact commit ID in config.toml, and regenerate that source's snapshots.
Snapshots are left untouched when the revision is unchanged and only
probabilistic snapshots differ."
default:
panic(fmt.Sprintf("unsupported snapshot mode %q", mode))
}
diff --git a/docs/serialization-snapshots.md b/docs/serialization-snapshots.md
index b4ca612..3b4eda9 100644
--- a/docs/serialization-snapshots.md
+++ b/docs/serialization-snapshots.md
@@ -21,7 +21,9 @@ The serialization corpus is a compatibility boundary between
DataSketches implem
The DataSketches implementation repositories use the same
`serialization_test_data/<language>_generated_files` layout for their
cross-language fixtures, so a TCK checkout can serve directly as an
implementation's test data root, and moving files between the TCK and an
implementation is a plain copy.
-This repository generates snapshots from the upstream sources configured in
`config.toml` instead of following the latest branch. Each source records its
repository and exact commit so a checkout remains reproducible.
+The `main` branch of this repository publishes the latest self-tested
snapshots from every source language. Each language validates its own snapshots
with its generators and round-trip tests before they reach the TCK, so a
snapshot set that passes there is ready to publish. Whether each implementation
can read the others' snapshots is then answered by running its cross-language
tests against the latest set, not by reviewing the `.sk` files by hand.
+
+`config.toml` records the repository and exact commit each snapshot directory
was generated from, so any TCK commit remains reproducible:
```toml
[snapshot.go]
@@ -29,26 +31,43 @@ repository = "https://github.com/apache/datasketches-go.git"
commit = "730c0ca31e00b8becf8b70591ae8ca73954912d0"
```
+## Update to the latest snapshots
+
+With the [toolchain installed](#set-up-the-toolchain), use `HEAD` to update
each source to its default branch:
+
+```shell
+mise run tck -- snapshots update cpp HEAD
+mise run tck -- snapshots update go HEAD
+mise run tck -- snapshots update java HEAD
+```
+
+For each source, the command resolves the default branch to an exact commit
ID, writes it to `config.toml`, and regenerates
`serialization_test_data/<language>_generated_files`. Generation happens before
either file set is changed, so a source whose generation fails is left
untouched.
+
+When a source's commit has not changed and only probabilistic snapshots
differ, its snapshots are left as they are, so repeated updates do not add
binary churn to the history.
+
+The [Update snapshots](#github-actions) workflow runs these commands weekly
and commits the result to `main`. It continues with the other languages if one
fails, then reports the failure after committing successful updates.
+
## Update one source
-With the [toolchain installed](#set-up-the-toolchain), adopt a different
upstream branch, tag, or commit by specifying one source language and revision:
+Update a single source to its default branch, or to a specific branch, tag, or
commit:
```shell
-mise run tck -- snapshots update go main
+mise run tck -- snapshots update go HEAD
+mise run tck -- snapshots update go v0.2.0
```
-The command resolves the supplied revision to an exact commit ID, writes it to
`[snapshot.go]` in `config.toml`, and regenerates
`serialization_test_data/go_generated_files`. Generation happens before either
file set is changed, so a generation failure leaves the repository untouched.
+Pinning an older revision is useful for reproducing a release or for holding a
source back while an upstream problem is fixed. If the upstream build or output
layout changed, update the corresponding adapter in
`internal/snapshots/<language>.go` before running the command.
-Updates intentionally handle one source at a time so each upstream change can
be reviewed independently. If the upstream build or output layout changed,
update the corresponding adapter in `internal/snapshots/<language>.go` before
running the command.
+## Review an update
-Review the resolved pin and corpus together:
+Review the resolved pins and the corpus together:
```shell
git diff --stat
git diff -- config.toml serialization_test_data/go_generated_files
```
-Added and deleted files change the set of compatibility cases. Unexpected
changes to deterministic files should be understood from the upstream change
before they are accepted.
+Most modified files are probabilistic snapshots, which are expected to change
on every generation. The update report classifies each change, so the
meaningful part of a review is short: added and deleted files change the set of
compatibility cases, and changes to deterministic snapshots, such as a flag
bit, should be understood from the upstream change.
Verify that generation is reproducible at the new pin and run the repository
checks:
@@ -69,7 +88,7 @@ mise run tck -- snapshots check go
Check mode does not modify the repository. It fails for added or deleted files
and for content changes to deterministic snapshots. It reports, but allows,
content changes to existing snapshots classified as probabilistic by
`internal/snapshots/stability.go`.
-This command answers whether the repository matches its pin; it does not
determine whether the pin is the latest upstream commit.
+This command answers whether the repository matches its pin; it does not
determine whether the pin is the latest upstream commit. Use update mode for
that.
## Synchronize configured snapshots
@@ -92,7 +111,7 @@ mise run tck -- snapshots --help
Mise supplies Go, CMake and CTest, Java, and Maven. Git is required for every
source language, a C++ compiler is required for C++, and Make is required for
Go.
-Check mode accepts `cpp`, `go`, `java`, or `all`. Synchronization takes no
arguments, while update requires one source language and revision.
+Check mode accepts `cpp`, `go`, `java`, or `all`. Synchronization takes no
arguments. Update accepts one of `cpp`, `go`, or `java`, followed by a required
revision.
## Use the corpus from an implementation
@@ -100,6 +119,8 @@ An implementation consumes the `.sk` files as test
fixtures. Its compatibility t
This repository centralizes the fixture corpus and source-side generation.
Consumer tests remain in the individual DataSketches implementation
repositories.
+By default, an implementation should test against the latest snapshot set on
`main`. A failure then shows exactly which source language and sketch disagree,
as early as possible. To keep a release or a CI run from changing underneath
it, an implementation can instead pin a specific TCK commit.
+
## Review policy for probabilistic snapshots
Some upstream generators contain randomness, so byte-for-byte reproduction is
not a valid invariant for every file. The stability policy is source-specific
because upstream implementations do not always seed or exercise an algorithm in
the same way.
@@ -112,7 +133,7 @@ The probabilistic classification only controls byte-level
comparison in this rep
`.github/workflows/check.yml` runs `mise run check` for pull requests and
pushes to `main`. It validates the Go implementation of the TCK tooling, but it
does not run the upstream snapshot generators or modify committed snapshots.
-Snapshot generation is kept out of the required workflow because it clones and
builds three external projects, and because an automated update cannot decide
whether an upstream compatibility change should be adopted. A pull request that
updates a pin should include the generated corpus changes and record which
source-language checks were run locally.
+`.github/workflows/update-snapshots.yml` runs `snapshots update <language>
HEAD` for `cpp`, `go`, and `java` every Monday and on manual dispatch, and
commits any updated pins and snapshots directly to `main`. It is not a required
check. The job summary lists each source's revision change and change counts.
If a source fails to generate, the other sources are still committed and the
run fails so that the broken upstream is noticed.
## Implementation notes
diff --git a/internal/snapshots/reconcile.go b/internal/snapshots/reconcile.go
index 3d915d1..92b7316 100644
--- a/internal/snapshots/reconcile.go
+++ b/internal/snapshots/reconcile.go
@@ -40,6 +40,8 @@ type Result struct {
PreviousRevision string
Revision string
Changes []Change
+ // Written reports whether the snapshot directory was replaced.
+ Written bool
}
func (result Result) RevisionChanged() bool {
@@ -138,11 +140,17 @@ func Reconcile(
if err := replaceDirectory(target, generated); err !=
nil {
return Result{}, err
}
+ result.Written = true
}
return result, nil
}
revisionChanged := result.RevisionChanged()
+ if !revisionChanged && !result.HasBlockingChanges() {
+ // Nothing new upstream. Rewriting the probabilistic snapshots
would only
+ // add binary churn to the history without adopting anything.
+ return result, nil
+ }
if revisionChanged {
if err := config.setCommit(language, resolvedRevision); err !=
nil {
return Result{}, err
@@ -169,6 +177,7 @@ func Reconcile(
}
return Result{}, err
}
+ result.Written = true
}
return result, nil
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]