Skip to content

Commit b8acb5d

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 b8acb5d

1 file changed

Lines changed: 397 additions & 0 deletions

File tree

release-pinn.sh

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

0 commit comments

Comments
 (0)