Skip to content

Commit 4c90a9d

Browse files
authored
accept yaml files (#1)
* accept yaml files * mount image with offset * make shrinking image optional * make tar for rubikpi and compress image * avoid sudo in compress image * Revert "avoid sudo in compress image" This reverts commit 1b54014.
1 parent 70a7d0d commit 4c90a9d

File tree

6 files changed

+141
-93
lines changed

6 files changed

+141
-93
lines changed

.github/workflows/test-raspi.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
name: 'Test modifying a Raspberry Pi image'
22
on:
3-
# push:
4-
# branches:
5-
# - 'main'
6-
# - 'releases/**'
7-
# pull_request:
3+
push:
4+
branches:
5+
- 'main'
6+
- 'releases/**'
7+
pull_request:
88
workflow_dispatch:
99

1010
jobs:
1111
build:
1212
runs-on: ubuntu-24.04-arm
1313
steps:
1414
- uses: actions/checkout@v4
15-
- uses: photonvision/photon-image-runner@HEAD
15+
- uses: ./ # photonvision/photon-image-runner@HEAD
1616
with:
1717
image_url: https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2025-05-13/2025-05-13-raspios-bookworm-arm64-lite.img.xz
1818
additional_mb: 200
@@ -28,7 +28,7 @@ jobs:
2828
sudo xz -T 0 -v ${{ steps.build_image.outputs.image }}
2929
- uses: actions/upload-artifact@v4
3030
with:
31-
name: ${{ steps.build_image.outputs.image }}.xz
31+
name: raspi.img.xz
3232
path: ${{ steps.build_image.outputs.image }}.xz
3333
compression-level: 0
3434
if-no-files-found: error

.github/workflows/test-rubikpi.yml

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: 'Test modifying a Raspberry Pi image'
1+
name: 'Test modifying a Rubik Pi image'
22
on:
33
push:
44
branches:
@@ -12,23 +12,36 @@ jobs:
1212
runs-on: ubuntu-24.04-arm
1313
steps:
1414
- uses: actions/checkout@v4
15-
- uses: photonvision/photon-image-runner@HEAD
15+
- uses: ./
1616
with:
1717
image_url: https://people.canonical.com/~platform/images/qualcomm-iot/rubikpi3/ubuntu-server-24.04/x00/ubuntu-24.04-preinstalled-server-arm64+rubikpi3-20250912-127.yaml
1818
minimum_free_mb: 2000
19+
root_location: "offset=569376768"
20+
shrink_image: "no"
1921
commands: |
20-
echo "Testing Raspberry Pi image"
22+
echo "Testing Rubik Pi image"
2123
uname -a
2224
lsblk
2325
echo "${loopdev}"
2426
id: build_image
2527
- name: Compress image
2628
run: |
27-
sudo xz -T 0 -v ${{ steps.build_image.outputs.image }}
29+
imagedir=$(dirname ${{ steps.build_image.outputs.image }})
30+
tardir=${RUNNER_TEMP}/photonvision_rubikpi3
31+
mkdir --parents ${tardir}
32+
if ls ${imagedir}*.tar.gz 1>/dev/null 2>&1; then
33+
sudo tar -xzf ${imagedir}*.tar.gz -C ${tardir}
34+
fi
35+
sudo mv ${imagedir}/rawprogram*.xml ${tardir}/ 2>/dev/null || true
36+
sudo mv ${imagedir}/dtb.bin ${tardir}/ 2>/dev/null || true
37+
sudo mv ${imagedir}/*.img ${tardir}/ 2>/dev/null || true
38+
sudo find ${tardir} -mindepth 2 -type f -exec mv {} ${tardir}/ \;
39+
sudo find ${tardir} -mindepth 1 -type d -empty -delete
40+
sudo tar -I 'xz -T0' -cf ./photonvision_rubikpi3.tar.xz ${tardir} --checkpoint=10000 --checkpoint-action=echo='%T'
2841
- uses: actions/upload-artifact@v4
2942
with:
30-
name: ${{ steps.build_image.outputs.image }}.xz
31-
path: ${{ steps.build_image.outputs.image }}.xz
43+
name: photonvision_rubikpi3.tar.xz
44+
path: ./photonvision_rubikpi3.tar.xz
3245
compression-level: 0
3346
if-no-files-found: error
3447
retention-days: 1

action.yml

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ inputs:
2222
description: 'Partition number for root'
2323
required: false
2424
default: '2'
25+
root_location:
26+
description: 'Location of the root as either "partition=X" or "offset=XXX"'
27+
required: false
28+
default: 'partition=2'
29+
shrink_image:
30+
description: 'Make the output image file as small as possible'
31+
reqiured: false
32+
default: 'yes'
2533

2634
outputs:
2735
image:
@@ -33,41 +41,40 @@ runs:
3341
steps:
3442
- name: Get base image
3543
run: |
36-
${GITHUB_ACTION_PATH}/get_image.sh ${{ inputs.image_url }}
44+
sudo --preserve-env bash ${GITHUB_ACTION_PATH}/get_image.sh ${{ inputs.image_url }}
3745
shell: bash
3846
id: get_image
3947

40-
# - name: Mount image
41-
# run: |
42-
# sudo --preserve-env bash "${GITHUB_ACTION_PATH}/mount_image.sh" "${{ steps.get_image.outputs.image }}" "${{ inputs.additional_mb }}" "${{ inputs.minimum_free_mb }}" "${{ inputs.root_partition }}"
43-
# shell: bash
44-
# id: mount_image
45-
46-
# - name: Run commands
47-
# run: |
48-
# echo "Running the commands"
49-
# chroot_scriptdir=/tmp/build
50-
# scriptdir="${{ env.rootdir }}${chroot_scriptdir}"
51-
# sudo mkdir --parents "${scriptdir}"
52-
# sudo mount --bind "${{ github.workspace }}" "${scriptdir}"
48+
- name: Mount image
49+
run: |
50+
sudo --preserve-env bash "${GITHUB_ACTION_PATH}/mount_image.sh" "${{ steps.get_image.outputs.image }}" "${{ inputs.additional_mb }}" "${{ inputs.minimum_free_mb }}" "${{ inputs.root_location }}"
51+
shell: bash
52+
id: mount_image
5353

54-
# cat >> "${scriptdir}/commands.sh" << EOF
55-
# #!/bin/bash
56-
# # set -exo pipefail
57-
# # export SHELLOPTS
58-
# # export BASHOPTS
59-
# cd "${chroot_scriptdir}"
60-
# ${{ inputs.commands }}
61-
# EOF
54+
- name: Run commands
55+
run: |
56+
echo "Running the commands"
57+
chroot_scriptdir=/tmp/build
58+
scriptdir="${{ env.rootdir }}${chroot_scriptdir}"
59+
sudo mkdir --parents "${scriptdir}"
60+
sudo mount --bind "${{ github.workspace }}" "${scriptdir}"
6261
63-
# sudo chmod +x "${scriptdir}/commands.sh"
62+
cat >> "${scriptdir}/commands.sh" << EOF
63+
#!/bin/bash
64+
# set -exo pipefail
65+
# export SHELLOPTS
66+
# export BASHOPTS
67+
cd "${chroot_scriptdir}"
68+
${{ inputs.commands }}
69+
EOF
6470
65-
# sudo --preserve-env chroot "${{ env.rootdir }}" bash -c "${chroot_scriptdir}/commands.sh"
66-
# shell: bash
67-
# id: run_commands
71+
sudo chmod +x "${scriptdir}/commands.sh"
72+
sudo --preserve-env chroot "${{ env.rootdir }}" bash -c "${chroot_scriptdir}/commands.sh"
73+
shell: bash
74+
id: run_commands
6875

69-
# - name: Clean up and pack image
70-
# run: |
71-
# sudo --preserve-env bash ${GITHUB_ACTION_PATH}/pack_image.sh "${{ steps.get_image.outputs.image }}"
72-
# shell: bash
73-
# id: pack_image
76+
- name: Clean up and pack image
77+
run: |
78+
sudo --preserve-env bash ${GITHUB_ACTION_PATH}/pack_image.sh "${{ steps.get_image.outputs.image }}" "${{ inputs.shrink_image }}"
79+
shell: bash
80+
id: pack_image

get_image.sh

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,42 @@ set -euo pipefail
33

44
url=$1
55
download_path="${RUNNER_TEMP}/image"
6+
echo "download_path=${download_path}" >> "$GITHUB_ENV"
67
mkdir --parent "${download_path}"
78

8-
if [[ ${url} = @("*.yaml"|"*.yml") ]]; then
9+
image=""
10+
11+
if [[ ${url} = @(*.yaml|*.yml) ]]; then
912
apt-get --quiet update
1013
apt-get --yes --quiet install yq
1114
wget --no-verbose --output-document="manifest.yaml" "${url}"
1215
echo "=== Manifest contents ==="
1316
cat manifest.yaml
1417
echo "========================="
15-
yq -r '.urls[] | "\(.url) \(.sha256sum)"' manifest.yaml | while read -r url sha; do
16-
filename="$(basename ${url})"
17-
echo "Downloading: ${filename} from ${url}"
18-
wget --no-verbose --output-document=${download_path}/${filename} ${url}
19-
echo "$sha $filename" | sha256sum -c -
18+
yq -r '.urls[] | "\(.url) \(.sha256sum)"' ./manifest.yaml > urls
19+
while read -r file_url sha; do
20+
filename="$(basename ${file_url})"
21+
echo "Downloading: ${filename} from ${file_url}"
22+
wget --no-verbose --output-document=${download_path}/${filename} ${file_url}
23+
echo "$sha ${download_path}/$filename" | sha256sum -c -
2024
[[ ${filename} = *.img.xz ]] && image="${download_path}/${filename}"
21-
done
25+
done < urls
2226
else
2327
image="${download_path}/$(basename ${url})"
2428
echo "Downloading ${image} from ${url}"
2529
wget --no-verbose --output-document="${image}" ${url}
2630
fi
2731

32+
echo "Image: ${image}"
33+
34+
ls -l ${download_path}
35+
2836
if [[ ${image} = *.xz ]]; then
2937
echo "Unzipping ${image}"
3038
unxz ${image}
3139
image=${image%.xz}
3240
fi
3341

34-
echo "PWD: $(pwd)"
3542
ls -l ${download_path}
3643

3744
if [[ ${image} != *.img ]]; then

mount_image.sh

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,41 @@ set -euo pipefail
44
image="$1"
55
additional_mb=$2
66
minimum_free=$3
7-
rootpartition=$4
8-
echo "rootpartition=${rootpartition}" >> "$GITHUB_ENV"
7+
root_location=$4
98

109
####
1110
# Prepare and mount the image
1211
####
1312

14-
loopdev=$(losetup --find --show --partscan ${image})
13+
case ${root_location,,} in
14+
partition* )
15+
rootpartition=${root_location#*=}
16+
loopdev=$(losetup --find --show --partscan ${image})
17+
rootdev="${loopdev}p${rootpartition}"
18+
;;
19+
offset* )
20+
rootpartition=1
21+
rootoffset=${root_location#*=}
22+
loopdev=$(losetup --find --show --offset=${rootoffset} ${image})
23+
rootdev="${loopdev}"
24+
;;
25+
* )
26+
echo "Don't understand value for root_location: ${root_location}"
27+
exit 1
28+
;;
29+
esac
30+
1531
echo "loopdev=${loopdev}" >> "$GITHUB_ENV"
32+
echo "rootpartition=${rootpartition}" >> "$GITHUB_ENV"
33+
echo "rootdev=${rootdev}" >> "$GITHUB_ENV"
34+
echo "Root device is: ${rootdev}"
1635

1736
echo "Partitions in the mounted image:"
1837
lsblk "${loopdev}"
1938

2039
part_type=$(blkid -o value -s PTTYPE "${loopdev}")
2140
echo "part_type=${part_type}" >> "$GITHUB_ENV"
22-
echo "Image is using ${part_type} partition table"
23-
24-
rootdev="${loopdev}p${rootpartition}"
25-
echo "rootdev=${rootdev}" >> "$GITHUB_ENV"
26-
echo "Root device is: ${rootdev}"
41+
echo "Image is using ${part_type:=NO} partition table"
2742

2843
rootdir="./rootfs"
2944
rootdir="$(realpath ${rootdir})"
@@ -48,9 +63,11 @@ if [[ ${additional_mb} -gt 0 ]]; then
4863
if [[ "${part_type}" == "gpt" ]]; then
4964
sgdisk --move-second-header "${loopdev}"
5065
fi
51-
parted --script "${loopdev}" resizepart ${rootpartition} 100%
52-
e2fsck -p -f "${loopdev}p${rootpartition}"
53-
resize2fs "${loopdev}p${rootpartition}"
66+
if [[ ${rootpartition} -gt 0 ]]; then
67+
parted --script "${loopdev}" resizepart ${rootpartition} 100%
68+
fi
69+
e2fsck -p -f "${rootdev}"
70+
resize2fs "${rootdev}"
5471
echo "Finished resizing disk image."
5572
fi
5673

@@ -69,6 +86,7 @@ df --block-size=M "${rootdir}"
6986
# Set up the environment
7087
mount -t proc /proc "${rootdir}/proc"
7188
mount -t sysfs /sys "${rootdir}/sys"
89+
mount -t tmpfs /tmpfs "${rootdir}/run"
7290
mount --rbind /dev "${rootdir}/dev"
7391

7492
# Temporarily replace resolv.conf for networking

pack_image.sh

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ set -euo pipefail
55
# Clean up and shrink image
66
####
77
image="$1"
8+
shrink="$2"
89

910
if [[ -e "${rootdir}/etc/resolv.conf.bak" ]]; then
1011
mv "${rootdir}/etc/resolv.conf.bak" "${rootdir}/etc/resolv.conf"
@@ -15,37 +16,39 @@ echo "Zero filling empty space"
1516

1617
umount --recursive "${rootdir}"
1718

18-
echo "Resizing root filesystem to minimal size."
19-
e2fsck -v -f -p -E discard "${rootdev}"
20-
resize2fs -M "${rootdev}"
21-
rootfs_blocksize=$(tune2fs -l ${rootdev} | grep "^Block size" | awk '{print $NF}')
22-
rootfs_blockcount=$(tune2fs -l ${rootdev} | grep "^Block count" | awk '{print $NF}')
23-
24-
echo "Resizing rootfs partition."
25-
rootfs_partstart=$(parted -m --script "${loopdev}" unit B print | grep "^${rootpartition}:" | awk -F ":" '{print $2}' | tr -d 'B')
26-
rootfs_partsize=$((${rootfs_blockcount} * ${rootfs_blocksize}))
27-
rootfs_partend=$((${rootfs_partstart} + ${rootfs_partsize} - 1))
28-
rootfs_partoldend=$(parted -m --script "${loopdev}" unit B print | grep "^${rootpartition}:" | awk -F ":" '{print $3}' | tr -d 'B')
29-
if [ "$rootfs_partoldend" -gt "$rootfs_partend" ]; then
30-
echo y | parted ---pretend-input-tty "${loopdev}" unit B resizepart "${rootpartition}" "${rootfs_partend}"
31-
else
32-
echo "Rootfs partition not resized as it was not shrunk"
33-
fi
19+
if [[ ${shrink,,} = y* ]]; then
20+
echo "Resizing root filesystem to minimal size."
21+
e2fsck -v -f -p -E discard "${rootdev}"
22+
resize2fs -M "${rootdev}"
23+
rootfs_blocksize=$(tune2fs -l ${rootdev} | grep "^Block size" | awk '{print $NF}')
24+
rootfs_blockcount=$(tune2fs -l ${rootdev} | grep "^Block count" | awk '{print $NF}')
25+
26+
echo "Resizing rootfs partition."
27+
rootfs_partstart=$(parted -m --script "${loopdev}" unit B print | grep "^${rootpartition}:" | awk -F ":" '{print $2}' | tr -d 'B')
28+
rootfs_partsize=$((${rootfs_blockcount} * ${rootfs_blocksize}))
29+
rootfs_partend=$((${rootfs_partstart} + ${rootfs_partsize} - 1))
30+
rootfs_partoldend=$(parted -m --script "${loopdev}" unit B print | grep "^${rootpartition}:" | awk -F ":" '{print $3}' | tr -d 'B')
31+
if [ "$rootfs_partoldend" -gt "$rootfs_partend" ]; then
32+
echo y | parted ---pretend-input-tty "${loopdev}" unit B resizepart "${rootpartition}" "${rootfs_partend}"
33+
else
34+
echo "Rootfs partition not resized as it was not shrunk"
35+
fi
3436

35-
free_space=$(parted -m --script "${loopdev}" unit B print free | tail -1)
36-
if [[ "${free_space}" =~ "free" ]]; then
37-
initial_image_size=$(stat -L --printf="%s" "${image}")
38-
image_size=$(echo "${free_space}" | awk -F ":" '{print $2}' | tr -d 'B')
39-
if [[ "${part_type}" == "gpt" ]]; then
40-
# for GPT partition table, leave space at the end for the secondary GPT
41-
# it requires 33 sectors, which is 16896 bytes
42-
image_size=$((image_size + 16896))
43-
fi
44-
echo "Shrinking image from ${initial_image_size} to ${image_size} bytes."
45-
truncate -s "${image_size}" "${image}"
46-
if [[ "${part_type}" == "gpt" ]]; then
47-
# use sgdisk to fix the secondary GPT after truncation
48-
sgdisk -e "${image}"
37+
free_space=$(parted -m --script "${loopdev}" unit B print free | tail -1)
38+
if [[ "${free_space}" =~ "free" ]]; then
39+
initial_image_size=$(stat -L --printf="%s" "${image}")
40+
image_size=$(echo "${free_space}" | awk -F ":" '{print $2}' | tr -d 'B')
41+
if [[ "${part_type}" == "gpt" ]]; then
42+
# for GPT partition table, leave space at the end for the secondary GPT
43+
# it requires 33 sectors, which is 16896 bytes
44+
image_size=$((image_size + 16896))
45+
fi
46+
echo "Shrinking image from ${initial_image_size} to ${image_size} bytes."
47+
truncate -s "${image_size}" "${image}"
48+
if [[ "${part_type}" == "gpt" ]]; then
49+
# use sgdisk to fix the secondary GPT after truncation
50+
sgdisk -e "${image}"
51+
fi
4952
fi
5053
fi
5154

0 commit comments

Comments
 (0)