blob: 116af71dad99aefbc7ddb7a32f431769d5d222d5 [file] [log] [blame]
Fabian Meumertzheim31872502024-06-05 11:45:25 -07001# Merges an arbitrary number of MODULE.bazel.lock files.
2#
3# Input: an array of MODULE.bazel.lock JSON objects (as produced by `jq -s`).
4# Output: a single MODULE.bazel.lock JSON object.
5#
6# This script assumes that all files are valid JSON and have a numeric
7# "lockFileVersion" field. It will not fail on any such files, but only
8# preserves information for files with a version of 10 or higher.
9#
10# The first file is considered to be the base when deciding which values to
11# keep in case of conflicts.
12
13# Like unique, but preserves the order of the first occurrence of each element.
14def stable_unique:
15 reduce .[] as $item ([]; if index($item) == null then . + [$item] else . end);
16
17# Given an array of objects, shallowly merges the result of applying f to each
18# object into a single object, with a few special properties:
19# 1. Values are uniquified before merging and then merged with last-wins
20# semantics. Assuming that the first value is the base, this ensures that
21# later occurrences of the base value do not override other values. For
22# example, when this is called with B A1 A2 and A1 contains changes to a
23# field but A2 does not (compared to B), the changes in A1 will be preserved.
24# 2. Object keys on the top level are sorted lexicographically after merging,
25# but are additionally split on ":". This ensures that module extension IDs,
26# which start with labels, sort as strings in the same way as they due as
27# structured objects in Bazel (that is, //python/extensions:python.bzl
28# sorts before //python/extensions/private:internal_deps.bzl).
29def shallow_merge(f):
30 map(f) | stable_unique | add | to_entries | sort_by(.key | split(":")) | from_entries;
31
32(
33 # Ignore all MODULE.bazel.lock files that do not have the maximum
34 # lockFileVersion.
35 (map(.lockFileVersion) | max) as $maxVersion
36 | map(select(.lockFileVersion == $maxVersion))
37 | {
38 lockFileVersion: $maxVersion,
39 registryFileHashes: shallow_merge(.registryFileHashes),
40 selectedYankedVersions: shallow_merge(.selectedYankedVersions),
41 # Group extension results by extension ID across all lockfiles with
42 # shallowly merged factors map, then shallowly merge the results.
43 moduleExtensions: (map(.moduleExtensions | to_entries)
44 | flatten
45 | group_by(.key)
46 | shallow_merge({(.[0].key): shallow_merge(.value)}))
47 }
48)? //
49 # We get here if the lockfiles with the highest lockFileVersion could not be
50 # processed, for example because all lockfiles have lockFileVersion < 10.
51 # In this case Bazel 7.2.0+ would ignore all lockfiles, so we might as well
52 # return the first lockfile for the proper "mismatched version" error
53 # message.
54 .[0]