-
Notifications
You must be signed in to change notification settings - Fork 39
chore: add automation for adding a new version #418
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
tstirrat15
wants to merge
4
commits into
main
Choose a base branch
from
tstirrat/new-version-automation
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
f0cb5db
chore: add automation for adding a new version
tstirrat15 43bc730
chore: refactor to use k8s yaml again
tstirrat15 b57b9d2
chore: refactor to reintroduce waypoint versions
tstirrat15 3b7f0d6
chore: add some justifications for the diff
tstirrat15 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| #!/usr/bin/env python3 | ||
| """ | ||
| Semantic diff of two update-graph YAML files. | ||
|
|
||
| Treats the `channels` list as an unordered set keyed by (name, datastore metadata). | ||
| Reports per-channel differences in nodes and edges. | ||
|
|
||
| Usage: | ||
| python3 diff-update-graphs.py <old-graph.yaml> <new-graph.yaml> | ||
| python3 diff-update-graphs.py # defaults to old-proposed-update-graph.yaml vs proposed-update-graph.yaml | ||
| """ | ||
|
|
||
| import sys | ||
| import yaml | ||
|
|
||
|
|
||
| def load_graph(path): | ||
| with open(path) as f: | ||
| return yaml.safe_load(f) | ||
|
|
||
|
|
||
| def index_channels(graph): | ||
| idx = {} | ||
| for ch in graph.get("channels", []): | ||
| key = (ch["name"], ch.get("metadata", {}).get("datastore", "")) | ||
| idx[key] = ch | ||
| return idx | ||
|
|
||
|
|
||
| def semver_sort_key(s): | ||
| # Simple sort key: split on dots and dashes, numeric parts as ints | ||
| import re | ||
| return [int(p) if p.isdigit() else p for p in re.split(r"[.\-]", s.lstrip("v"))] | ||
|
|
||
|
|
||
| def diff_graphs(old_path, new_path): | ||
| old = load_graph(old_path) | ||
| new = load_graph(new_path) | ||
|
|
||
| old_ch = index_channels(old) | ||
| new_ch = index_channels(new) | ||
|
|
||
| old_keys = set(old_ch.keys()) | ||
| new_keys = set(new_ch.keys()) | ||
|
|
||
| print(f"Comparing:\n OLD: {old_path}\n NEW: {new_path}\n") | ||
|
|
||
| only_old = sorted(old_keys - new_keys) | ||
| only_new = sorted(new_keys - old_keys) | ||
|
|
||
| if only_old: | ||
| print("=== Channels only in OLD ===") | ||
| for k in only_old: | ||
| print(f" {k}") | ||
| print() | ||
|
|
||
| if only_new: | ||
| print("=== Channels only in NEW ===") | ||
| for k in only_new: | ||
| print(f" {k}") | ||
| print() | ||
|
|
||
| print("=== Per-channel comparison ===") | ||
| any_diff = False | ||
| for key in sorted(old_keys & new_keys): | ||
| oc = old_ch[key] | ||
| nc = new_ch[key] | ||
|
|
||
| old_nodes = {n["id"]: n for n in oc.get("nodes", [])} | ||
| new_nodes = {n["id"]: n for n in nc.get("nodes", [])} | ||
|
|
||
| only_old_nodes = sorted(set(old_nodes) - set(new_nodes), key=semver_sort_key) | ||
| only_new_nodes = sorted(set(new_nodes) - set(old_nodes), key=semver_sort_key) | ||
|
|
||
| old_edges = {k: set(v) for k, v in (oc.get("edges") or {}).items()} | ||
| new_edges = {k: set(v) for k, v in (nc.get("edges") or {}).items()} | ||
|
|
||
| all_from_nodes = set(old_edges) | set(new_edges) | ||
| edge_diffs = {} | ||
| for fn in all_from_nodes: | ||
| oe = old_edges.get(fn, set()) | ||
| ne = new_edges.get(fn, set()) | ||
| only_old_e = sorted(oe - ne, key=semver_sort_key) | ||
| only_new_e = sorted(ne - oe, key=semver_sort_key) | ||
| if only_old_e or only_new_e: | ||
| edge_diffs[fn] = (only_old_e, only_new_e) | ||
|
|
||
| # Node field-level diffs | ||
| field_diffs = {} | ||
| for nid in sorted(set(old_nodes) & set(new_nodes), key=semver_sort_key): | ||
| on = old_nodes[nid] | ||
| nn = new_nodes[nid] | ||
| diffs = {} | ||
| for f in set(on) | set(nn): | ||
| if on.get(f) != nn.get(f): | ||
| diffs[f] = (on.get(f), nn.get(f)) | ||
| if diffs: | ||
| field_diffs[nid] = diffs | ||
|
|
||
| channel_label = f"{key[1]}/{key[0]}" | ||
| if not only_old_nodes and not only_new_nodes and not edge_diffs and not field_diffs: | ||
| print(f"\n {channel_label}: IDENTICAL") | ||
| else: | ||
| any_diff = True | ||
| print(f"\n {channel_label}: DIFFERS") | ||
| if only_old_nodes: | ||
| print(f" Nodes only in OLD: {only_old_nodes}") | ||
| if only_new_nodes: | ||
| print(f" Nodes only in NEW: {only_new_nodes}") | ||
| for fn in sorted(edge_diffs, key=semver_sort_key): | ||
| only_old_e, only_new_e = edge_diffs[fn] | ||
| if only_old_e: | ||
| print(f" Edge {fn} -> REMOVED targets: {only_old_e}") | ||
| if only_new_e: | ||
| print(f" Edge {fn} -> ADDED targets: {only_new_e}") | ||
| for nid, diffs in sorted(field_diffs.items(), key=lambda x: semver_sort_key(x[0])): | ||
| print(f" Node {nid} field diffs: {diffs}") | ||
|
|
||
| if not any_diff and not only_old and not only_new: | ||
| print("\nGraphs are SEMANTICALLY EQUIVALENT (order-agnostic).") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| if len(sys.argv) == 3: | ||
| old_path, new_path = sys.argv[1], sys.argv[2] | ||
| elif len(sys.argv) == 1: | ||
| old_path = "old-proposed-update-graph.yaml" | ||
| new_path = "proposed-update-graph.yaml" | ||
| else: | ||
| print("Usage: diff-update-graphs.py [<old.yaml> <new.yaml>]") | ||
| sys.exit(1) | ||
|
|
||
| diff_graphs(old_path, new_path) |
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| # Update Graph Diff: old-proposed-update-graph.yaml vs proposed-update-graph.yaml | ||
|
|
||
| The graphs are **not fully equivalent**. Below is every difference found. | ||
|
|
||
| --- | ||
|
|
||
| ## `memory/stable`: IDENTICAL — no differences. | ||
|
|
||
| --- | ||
|
|
||
| ## All four DB channels: `v1.37.1` edge set differs | ||
|
|
||
| All of cockroachdb, mysql, postgres, and spanner are affected. | ||
|
|
||
| | Graph | `v1.37.1` edges to | | ||
| |-------|-------------------| | ||
| | Old | `v1.38.0, v1.39.1, v1.40.1, v1.42.1, v1.45.4, v1.47.1, v1.48.0, v1.49.2, v1.51.1` | | ||
| | New | `v1.38.0` only | | ||
|
|
||
| The old code let `v1.37.1` skip past `v1.38.0`; the new waypoint algorithm blocks that | ||
| because `v1.38.0` is now a mandatory stop (`waypoint: true`). | ||
|
|
||
| --- | ||
|
|
||
| ## Phase nodes have broader outgoing edges in the new graph | ||
|
|
||
| The old code treated phase nodes as strict "step through me to the immediate next version" | ||
| nodes. The new waypoint algorithm allows phase nodes to reach any target up to (but not | ||
| past) the next waypoint. Affected nodes: | ||
|
|
||
| - **`cockroachdb/stable`**: `v1.30.0-phase1` | ||
| - Old: only `→ v1.30.0` | ||
| - New: `→ v1.30.0` through `v1.36.2` | ||
| - **`postgres/stable`**: `v1.14.0-phase2` | ||
| - Old: only `→ v1.14.0` | ||
| - New: `→ v1.14.0` through `v1.36.2` | ||
| - **`spanner/stable`**: `v1.22.2-phase2` and `v1.29.5-phase1` | ||
| - Old: each pointed only to the immediate next version | ||
| - New: each can reach further (up to the `v1.38.0` waypoint) | ||
|
|
||
| --- | ||
|
|
||
| ## `spanner/stable`: `v1.51.1` node missing from old graph | ||
|
|
||
| The old graph encoded the latest spanner version as a quirk: node `v1.49.2` had `tag: | ||
| v1.51.1`. The new graph correctly has `v1.49.2` with `tag: v1.49.2` and a separate | ||
| `v1.51.1` node, which adds outgoing edges from all existing spanner nodes to `v1.51.1`. | ||
|
|
||
| --- | ||
|
|
||
| ## Node field differences | ||
|
|
||
| | Channel | Node | Field | Old value | New value | | ||
| |---------|------|-------|-----------|-----------| | ||
| | `cockroachdb/stable` | `v1.30.0-phase1` | `phase` | missing | `write-both-read-new` | | ||
| | `spanner/stable` | `v1.29.5-phase1` | `phase` | missing | `write-both-read-new` | | ||
| | `spanner/stable` | `v1.49.2` | `tag` | `v1.51.1` | `v1.49.2` | | ||
|
|
||
| --- | ||
|
|
||
| ## Bottom line | ||
|
|
||
| The graphs differ in four ways: | ||
|
|
||
| 1. **`v1.37.1` edge narrowing** (all DB channels) — likely correct; `v1.38.0` is now a | ||
| hard waypoint. | ||
| 2. **Phase-node outgoing edge expansion** — semantic change: old code was "phase node → | ||
| immediate next release only"; new algorithm is "phase node → anything up to the next | ||
| waypoint". Whether multi-hop skips from a phase node are safe depends on whether | ||
| those intermediate versions require a migration stop. | ||
| 3. **`spanner/stable` `v1.51.1` node added** — likely a bug fix in the old graph where | ||
| `v1.49.2` was carrying the wrong tag. | ||
| 4. **`phase` field missing on phase nodes in old graph** — old serialization omitted the | ||
| `phase` field from those nodes; new graph includes it. |
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
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've never found this action useful and I often find it frustrating. I'd vote to get rid of it; I can restore and fix the issues if we don't want to do that.