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]

Reply via email to