Repository navigation
Expand file tree
/
Copy pathgenerate_ansible_lockfile.sh
More file actions
executable file
·161 lines (146 loc) · 5.37 KB
/
Copy pathgenerate_ansible_lockfile.sh
File metadata and controls
executable file
·161 lines (146 loc) · 5.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ANSIBLE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
if [[ -z ${ROS_DISTRO:-} ]]; then
echo "Error: ROS_DISTRO is not set." >&2
exit 1
fi
if [[ -z ${ROS_SNAPSHOT_DATE:-} ]]; then
echo "Error: ROS_SNAPSHOT_DATE is not set (e.g. 2026-04-13)." >&2
exit 1
fi
# The Ubuntu archive counterpart of ROS_SNAPSHOT_DATE. Take it at the same time
# as the apt versions recorded below: snapshot.ubuntu.com is what keeps those
# builds downloadable once the live pockets supersede them, so a timestamp from
# before this run would freeze the archive at versions that are not the ones
# pinned here.
if [[ -z ${UBUNTU_SNAPSHOT_DATE:-} ]]; then
echo "Error: UBUNTU_SNAPSHOT_DATE is not set (snapshot.ubuntu.com timestamp, e.g. 20260805T000000Z)." >&2
echo "Hint: date -u +%Y%m%dT000000Z" >&2
exit 1
fi
if [[ ! $UBUNTU_SNAPSHOT_DATE =~ ^[0-9]{8}T[0-9]{6}Z$ ]]; then
echo "Error: UBUNTU_SNAPSHOT_DATE must look like 20260805T000000Z, got '${UBUNTU_SNAPSHOT_DATE}'." >&2
exit 1
fi
ARCH=$(dpkg --print-architecture)
OUTPUT_FILE="${ANSIBLE_DIR}/vars/locked-versions-${ROS_DISTRO}-${ARCH}.yaml"
main() {
echo "Generating Ansible lockfile: $OUTPUT_FILE"
if [[ ! -f $OUTPUT_FILE ]]; then
echo "Error: Lockfile not found: $OUTPUT_FILE" >&2
echo "Create it with apt_pins package names first, then run this script." >&2
exit 1
fi
# apt_pins package names come from the existing lockfile (no hardcoded list).
# cspell:ignore isinstance safeload
mapfile -t apt_packages < <(python3 -c "
import yaml, sys
data = yaml.safe_load(open('$OUTPUT_FILE')) or {}
pins = data.get('apt_pins') or {}
if not isinstance(pins, dict):
sys.exit('Error: apt_pins must be a mapping')
for pkg in sorted(pins):
print(pkg)
")
# pip_pins package names come from the existing lockfile (no hardcoded list).
mapfile -t pip_packages < <(python3 -c "
import yaml, sys
data = yaml.safe_load(open('$OUTPUT_FILE')) or {}
pins = data.get('pip_pins') or {}
if not isinstance(pins, dict):
sys.exit('Error: pip_pins must be a mapping')
for pkg in sorted(pins):
print(pkg)
")
declare -A apt_versions
declare -A pip_versions
local has_error=false
for pkg in "${apt_packages[@]}"; do
local ver
ver=$(dpkg-query -W -f='${Version}' "$pkg" 2>/dev/null) || true
if [[ -z $ver ]]; then
echo "Error: APT package '$pkg' is not installed." >&2
has_error=true
continue
fi
apt_versions[$pkg]=$ver
# Origin check: an apt_pins package served by ros.org should be snapshot-covered, not pinned.
if apt-cache policy "$pkg" 2>/dev/null | grep -q 'ros.org'; then
echo "Warning: '$pkg' resolves from a ros.org source; consider removing it from apt_pins (snapshot covers it)." >&2
fi
done
for pkg in "${pip_packages[@]}"; do
local ver
ver=$(pip3 show "$pkg" 2>/dev/null | awk '/^Version:/{print $2}' || true)
[[ -z $ver ]] && ver=$(pipx runpip "$pkg" show "$pkg" 2>/dev/null | awk '/^Version:/{print $2}' || true)
if [[ -z $ver ]]; then
echo "Error: pip package '$pkg' is not installed (checked pip3 and pipx)." >&2
has_error=true
else
pip_versions[$pkg]=$ver
fi
done
$has_error && exit 1
# Preserve any existing nvidia_pins block verbatim. It is populated separately by
# emit_nvidia_pins.py (this generator has no way to freshly measure the NVIDIA
# closure), so a rerun here must not silently drop it.
local nvidia_block
nvidia_block=$(python3 -c "
import yaml
data = yaml.safe_load(open('$OUTPUT_FILE')) or {}
pins = data.get('nvidia_pins') or {}
if pins:
print('nvidia_pins:')
for pkg in sorted(pins):
print(f' {pkg}: {pins[pkg]}')
else:
print('nvidia_pins: {}')
")
# Preserve any existing ros_overrides block verbatim.
local overrides
overrides=$(python3 -c "
import yaml
data = yaml.safe_load(open('$OUTPUT_FILE')) or {}
ov = data.get('ros_overrides') or {}
if ov:
print(yaml.safe_dump({'ros_overrides': ov}, default_flow_style=False).rstrip())
else:
print('ros_overrides: {}')
")
{
cat <<HEADER
# Dependency Version Lockfile
# ROS Distro: ${ROS_DISTRO}
# Platform: ${ARCH}
# Ubuntu: $(lsb_release -rs) ($(lsb_release -cs))
#
# ros_snapshot_date freezes every ROS package snapshots.ros.org serves on that date.
ros_snapshot_date: "${ROS_SNAPSHOT_DATE}"
#
# ubuntu_snapshot_date freezes what the Ubuntu archive can still serve: snapshot.ubuntu.com
# republishes the pockets as they stood at that timestamp, so every apt_pins build below
# stays downloadable after the live pockets supersede it. Take it at the same time as the
# versions below, or a pin here will resolve to nothing.
ubuntu_snapshot_date: ${UBUNTU_SNAPSHOT_DATE}
apt_pins:
HEADER
for pkg in "${apt_packages[@]}"; do
echo " ${pkg}: ${apt_versions[$pkg]}"
done
if [[ ${#pip_packages[@]} -eq 0 ]]; then
echo "pip_pins: {}"
else
echo "pip_pins:"
for pkg in "${pip_packages[@]}"; do
echo " ${pkg}: ${pip_versions[$pkg]}"
done
fi
echo "$nvidia_block"
echo "$overrides"
} >"$OUTPUT_FILE"
echo "Generated: $OUTPUT_FILE"
"${SCRIPT_DIR}/validate_lockfiles.sh" "$OUTPUT_FILE"
}
main "$@"