Skip to content

Commit 5c65058

Browse files
committed
Add script to create NOOBS/PINN releases
This script replaces our old NOOBS release system (which required creating noobs tar balls at build time) and creates the necessary tarballs and metadata from the update tars instead. In order to cope well with our mirrors each release is stored in a separate directory and extracting and going live can be performed as two separate steps: "./release-pinn.sh VERSION" will create the noobs files in pinn/VERSION/DEVICE... and also store an OS list in pinn/VERSION/os_list.json - that file can then be used eg for local testing as well. When run with the "-r" option, eg "./release-pinn.sh -r VERSION" an actual release is performed, i.e. the os_list.json file from the version directory is copied to the "live" pinn/os_list.json and can be picked up by PINN/NOOBs. The script autmatically skips re-creating already existing versions (unless run with the "-f" option) so the "release" step will be very quick (xz compression of the release tarballs takes ages...). In order to create legacy PINN release (eg 9.2.6 for RPi) the scripts supports a "-d" option to specify the devices - by default all of RPi2, RPi4 and RPi5 will be created. There are also a few additional options mainly intended for local testing/development: -C disables compression and -D / -U set the local webroot base directory / URL. Signed-off-by: Matthias Reichl <hias@horus.com>
1 parent 29efcf6 commit 5c65058

1 file changed

Lines changed: 383 additions & 0 deletions

File tree

release-pinn.sh

Lines changed: 383 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,383 @@
1+
#!/bin/sh
2+
3+
BASE_DIR="/var/www/releases"
4+
BASE_URL="https://releases.libreelec.tv"
5+
FORCE="no"
6+
COMPRESS="yes"
7+
COPY_TO_MAIN="no"
8+
DEVICES="RPi2 RPi4 RPi5"
9+
10+
DESCRIPTION="A fast and user-friendly Kodi mediacenter distribution"
11+
12+
usage()
13+
{
14+
cat << EOF
15+
usage: $0 [options...] VERSION
16+
-d devices comma separated list of devices (default: RPi2,RPi4,RPi5)
17+
-f force overwriting existing versions (default: skip extraction)
18+
-r release this version (create pinn/os_list.json)
19+
-C disable image compression (default: create .tar.xz images)
20+
-D directory base output directory (default: /var/www/releases)
21+
-U url base URL (default: https://releases.libreelec.tv)
22+
EOF
23+
}
24+
25+
while getopts d:frCD:U: opt; do
26+
case "${opt}" in
27+
d) DEVICES=$(echo "${OPTARG}" | tr ',' ' ');;
28+
f) FORCE="yes";;
29+
r) COPY_TO_MAIN="yes";;
30+
C) COMPRESS="no";;
31+
D) BASE_DIR="${OPTARG}";;
32+
U) BASE_URL="${OPTARG}";;
33+
\?) usage; exit 1;;
34+
esac
35+
done
36+
37+
shift $((OPTIND - 1))
38+
39+
if [ $# -ne 1 ]; then
40+
usage
41+
exit 1
42+
fi
43+
44+
VERSION="$1"
45+
46+
set -e
47+
48+
PINN_DIR="${BASE_DIR}/pinn"
49+
PINN_VERSION_DIR="${PINN_DIR}/${VERSION}"
50+
ICON_FILE="LibreELEC.png"
51+
MARKETING_FILE="marketing.tar"
52+
53+
PINN_ICON_FILE="${PINN_DIR}/${ICON_FILE}"
54+
PINN_MARKETING_FILE="${PINN_DIR}/${MARKETING_FILE}"
55+
56+
if [ "${COMPRESS}" = "yes" ]; then
57+
SYSTEM_TAR="System.tar.xz"
58+
else
59+
SYSTEM_TAR="System.tar"
60+
fi
61+
62+
base_setup()
63+
{
64+
if [ ! -d "${PINN_DIR}" ]; then
65+
mkdir -p "${PINN_DIR}"
66+
fi
67+
68+
if [ ! -e "${PINN_ICON_FILE}" ]; then
69+
# copy icon from old noobs release
70+
if [ -e "${BASE_DIR}/noobs/LibreELEC_RPi5/LibreELEC_RPi5.png" ]; then
71+
cp "${BASE_DIR}/noobs/LibreELEC_RPi5/LibreELEC_RPi5.png" "${PINN_ICON_FILE}"
72+
else
73+
echo "error: no LibreELEC.png icon file found, copy it to ${PINN_ICON_FILE}"
74+
exit 1
75+
fi
76+
fi
77+
78+
if [ ! -e "${PINN_MARKETING_FILE}" ]; then
79+
# copy marketing.tar from old noobs release
80+
if [ -e "${BASE_DIR}/noobs/LibreELEC_RPi5/marketing.tar" ]; then
81+
cp "${BASE_DIR}/noobs/LibreELEC_RPi5/marketing.tar" "${PINN_MARKETING_FILE}"
82+
else
83+
echo "error: no marketing.tar file found, copy it to ${PINN_MARKETING_FILE}"
84+
exit 1
85+
fi
86+
fi
87+
}
88+
89+
prepare_common()
90+
{
91+
PINN_TEMP=$(mktemp -d --tmpdir pinn.XXXXXXXXXX)
92+
93+
trap cleanup 0
94+
}
95+
96+
cleanup()
97+
{
98+
if [ -n "${PINN_TEMP}" ]; then
99+
rm -rf "${PINN_TEMP}"
100+
fi
101+
}
102+
103+
prepare_version()
104+
{
105+
if [ ! -d "${PINN_VERSION_DIR}" ]; then
106+
mkdir -p "${PINN_VERSION_DIR}"
107+
fi
108+
}
109+
110+
# args: update-tar-file
111+
create_system_tar()
112+
{
113+
update_dir="${PINN_TEMP}/update-tar"
114+
rm -rf "${update_dir}"
115+
mkdir -p "${update_dir}"
116+
tar xf "$1" --strip-components=1 -C "${update_dir}"
117+
118+
system_dir="${PINN_TEMP}/system"
119+
120+
rm -rf "${system_dir}"
121+
rm -f "${PINN_TEMP}/${SYSTEM_TAR}"
122+
mkdir -p "${system_dir}"
123+
(
124+
cd "${update_dir}"
125+
mv "target/KERNEL" "${system_dir}/kernel.img"
126+
mv "target/SYSTEM" "${system_dir}"
127+
mv "3rdparty/bootloader/"* "${system_dir}"
128+
129+
cd "${system_dir}"
130+
tar -acf "${PINN_TEMP}/${SYSTEM_TAR}" --owner=root:0 --group=root:0 -- *
131+
)
132+
SYSTEM_SIZE=$(du -s -B 1000000 "${system_dir}" | awk '{print $1}' )
133+
rm -rf "${system_dir}" "${update_dir}"
134+
}
135+
136+
# args: device
137+
get_supported_models()
138+
{
139+
case "$1" in
140+
RPi) echo '[ "Pi Model", "Compute Module Rev", "Pi Zero" ]';;
141+
RPi2) echo '[ "Pi 2", "Pi 3", "Pi Compute Module 3" ]';;
142+
RPi4) echo '[ "Pi 4" ]';;
143+
RPi5) echo '[ "Pi 5" ]';;
144+
*)
145+
echo "unsupported device $1" >&2
146+
exit 1
147+
;;
148+
esac
149+
}
150+
151+
# args: device
152+
get_target_models()
153+
{
154+
case "$1" in
155+
RPi) echo "RPi 0/1";;
156+
RPi2) echo "RPi 2/3";;
157+
RPi4) echo "RPi 4";;
158+
RPi5) echo "RPi 5";;
159+
*)
160+
echo "unsupported device $1" >&2
161+
exit 1
162+
;;
163+
esac
164+
}
165+
166+
# args: update_tar
167+
get_release_date()
168+
{
169+
stat -c %y "$1" | awk '{print $1}'
170+
}
171+
172+
# args: device, dir, release date
173+
create_os_json()
174+
{
175+
cat > "$2/os.json" << EOF
176+
{
177+
"name": "LibreELEC_$1",
178+
"version": "${VERSION}",
179+
"release_date": "$3",
180+
"description": "${DESCRIPTION} for $(get_target_models "$1")",
181+
"username": "root",
182+
"password": "libreelec",
183+
"supports_backup": true,
184+
"group": "Media",
185+
"url": "https://libreelec.tv/",
186+
"supported_models": $(get_supported_models "$1")
187+
}
188+
EOF
189+
}
190+
191+
# args: dir, systen size, system sha512
192+
create_partitions_json()
193+
{
194+
cat > "$1/partitions.json" << EOF
195+
{
196+
"partitions": [
197+
{
198+
"label": "System",
199+
"filesystem_type": "FAT",
200+
"partition_size_nominal": 512,
201+
"want_maximised": false,
202+
"uncompressed_tarball_size": $2,
203+
"sha512sum": "$3",
204+
"mkfs_options": ""
205+
},
206+
{
207+
"label": "Storage",
208+
"empty_fs": true,
209+
"filesystem_type": "ext4",
210+
"partition_size_nominal": 512,
211+
"want_maximised": true,
212+
"uncompressed_tarball_size": 1,
213+
"mkfs_options": "-m 0"
214+
}
215+
]
216+
}
217+
EOF
218+
}
219+
220+
# args device, dir
221+
create_partition_setup()
222+
{
223+
case "$1" in
224+
RPi5) cmdline_args="quiet console=ttyAMA10,115200 console=tty0";;
225+
*) cmdline_args="quiet console=tty0";;
226+
esac
227+
228+
cat > "$2/partition_setup.sh" <<EOF
229+
#!/bin/sh
230+
set -ex
231+
232+
if [ -z "\${part1}" ] || [ -z "\${part2}" ] || [ -z "\${id1}" ] || [ -z "\${id2}" ]; then
233+
echo "error: missing environment variables id1/2 or part1/2"
234+
exit 1
235+
fi
236+
237+
mkdir -p "/tmp/le-boot"
238+
mount "\${part1}" "/tmp/le-boot"
239+
if [ -e "/tmp/le-boot/cmdline.txt" ]; then
240+
sed -e 's/boot=[^ ]*/boot='"\${id1}/" -e 's/disk=[^ ]*/disk='"\${id2}/" -i "/tmp/le-boot/cmdline.txt"
241+
else
242+
echo "boot=\${id1} disk=\${id2} ${cmdline_args}" > "/tmp/le-boot/cmdline.txt"
243+
fi
244+
umount "/tmp/le-boot"
245+
rmdir "/tmp/le-boot"
246+
EOF
247+
}
248+
249+
# args: device, update tar, dest dir, release date
250+
extract_device()
251+
{
252+
device="$1"
253+
update_tar="$2"
254+
dest_dir="$3"
255+
release_date="$4"
256+
257+
create_system_tar "${update_tar}"
258+
259+
mkdir -p "${dest_dir}"
260+
261+
system_tar_sha512=$(sha512sum "${PINN_TEMP}/${SYSTEM_TAR}" | awk '{print $1}')
262+
mv "${PINN_TEMP}/${SYSTEM_TAR}" "${dest_dir}"
263+
cp "${PINN_ICON_FILE}" "${PINN_MARKETING_FILE}" "${dest_dir}"
264+
265+
create_os_json "${device}" "${dest_dir}" "${release_date}"
266+
create_partitions_json "${dest_dir}" "${SYSTEM_SIZE}" "${system_tar_sha512}"
267+
create_partition_setup "${device}" "${dest_dir}"
268+
}
269+
270+
# args: device, release date, download size
271+
get_os_entry()
272+
{
273+
url="${BASE_URL}/pinn/${VERSION}/${DEVICE}"
274+
cat << EOF
275+
{
276+
"os_name": "LibreELEC_$1",
277+
"description": "${DESCRIPTION} for $(get_target_models "$1")",
278+
"release_date": "$2",
279+
"version": "${VERSION}",
280+
"supported_models": $(get_supported_models "$1"),
281+
"url": "https://libreelec.tv/",
282+
"group": "Media",
283+
"download_size": $3,
284+
"os_info": "${url}/os.json",
285+
"partitions_info": "${url}/partitions.json",
286+
"icon": "${url}/${ICON_FILE}",
287+
"marketing_info": "${url}/${MARKETING_FILE}",
288+
"partition_setup": "${url}/partition_setup.sh",
289+
"tarballs": [
290+
"${url}/${SYSTEM_TAR}"
291+
],
292+
"nominal_size": 1024
293+
}
294+
EOF
295+
}
296+
LIST_SEP=",
297+
"
298+
299+
if [ "${COPY_TO_MAIN}" = "yes" ]; then
300+
echo "Releasing ${VERSION}"
301+
else
302+
echo "Creating ${VERSION} PINN files"
303+
fi
304+
305+
base_setup
306+
prepare_common
307+
prepare_version
308+
309+
OS_LIST=""
310+
311+
EXTRACTED_FILES="no"
312+
313+
for DEVICE in ${DEVICES}; do
314+
UPDATE_TAR="${BASE_DIR}/LibreELEC-${DEVICE}.aarch64-${VERSION}.tar"
315+
if [ ! -e "${UPDATE_TAR}" ]; then
316+
UPDATE_TAR="${BASE_DIR}/LibreELEC-${DEVICE}.arm-${VERSION}.tar"
317+
if [ ! -e "${UPDATE_TAR}" ]; then
318+
echo "error: no update tar for $DEVICE available, exiting"
319+
exit 1
320+
fi
321+
fi
322+
323+
release_date=$(get_release_date "${UPDATE_TAR}")
324+
325+
PINN_DEVICE_DIR="${PINN_VERSION_DIR}/${DEVICE}"
326+
327+
need_extract="yes"
328+
if [ -d "${PINN_DEVICE_DIR}" ]; then
329+
if [ "${FORCE}" = "yes" ]; then
330+
echo "fored re-extract of ${DEVICE}"
331+
rm -rf "${PINN_DEVICE_DIR}"
332+
else
333+
need_extract="no"
334+
echo "skipping extract of ${DEVICE}"
335+
fi
336+
else
337+
echo "extracting ${DEVICE}"
338+
fi
339+
340+
if [ "${need_extract}" = "yes" ]; then
341+
extract_device "${DEVICE}" "${UPDATE_TAR}" "${PINN_DEVICE_DIR}" "${release_date}"
342+
EXTRACTED_FILES="yes"
343+
fi
344+
345+
if [ ! -e "${PINN_DEVICE_DIR}/${SYSTEM_TAR}" ]; then
346+
echo "error: ${PINN_DEVICE_DIR}/${SYSTEM_TAR} is missing"
347+
exit 1
348+
fi
349+
350+
download_size=$(stat -c %s "${PINN_DEVICE_DIR}/${SYSTEM_TAR}" | awk '{print $1}')
351+
352+
os_entry=$(get_os_entry "${DEVICE}" "${release_date}" "${download_size}")
353+
354+
if [ -n "${OS_LIST}" ]; then
355+
OS_LIST="${OS_LIST}${LIST_SEP}"
356+
fi
357+
358+
OS_LIST="${OS_LIST}${os_entry}"
359+
done
360+
361+
if [ -z "${OS_LIST}" ]; then
362+
echo "error: empty OS list"
363+
exit 1
364+
fi
365+
366+
if [ "${EXTRACTED_FILES}" = "yes" ] || [ ! -e "${PINN_VERSION_DIR}/os_list.json" ]; then
367+
cat > "${PINN_VERSION_DIR}/os_list.json" << EOF
368+
{
369+
"os_list": [
370+
${OS_LIST}
371+
]
372+
}
373+
EOF
374+
375+
echo "wrote ${VERSION} OS list to ${PINN_VERSION_DIR}/os_list.json"
376+
fi
377+
378+
if [ "${COPY_TO_MAIN}" = "yes" ]; then
379+
cp "${PINN_VERSION_DIR}/os_list.json" "${PINN_DIR}/os_list.json"
380+
echo "wrote release OS list to ${PINN_DIR}/os_list.json"
381+
fi
382+
383+
echo "done."

0 commit comments

Comments
 (0)