Build target pi0 from 3rdIteration/seedsigner@dev (os 3rdIteration/seedsigner-os@codex/fix-reproducibility-of-zipped-img-files, smartcard enabled) #402
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build | |
| run-name: >- | |
| Build${{ github.event_name == 'workflow_dispatch' | |
| && format( | |
| ' target {0} from {1}@{2} (os {3}@{4}, smartcard {5})', | |
| github.event.inputs.target || 'pi0', | |
| github.event.inputs['app-repo'] || github.repository, | |
| github.event.inputs['source-ref'], | |
| github.event.inputs['os-repo'] || '3rdIteration/seedsigner-os', | |
| github.event.inputs['os-ref'], | |
| (fromJSON(github.event.inputs.smartcard || 'true') && 'enabled') || 'disabled' | |
| ) | |
| || github.event_name == 'push' && format(' push {0}', github.ref_name) | |
| || github.event_name == 'pull_request' && format(' PR #{0}', github.event.pull_request.number) | |
| || '' | |
| }} | |
| on: | |
| pull_request: | |
| # Build on changes to this workflow files in PRs to test proposed changes | |
| paths: | |
| - '.github/workflows/build.yml' | |
| push: | |
| branches: | |
| - main | |
| - dev | |
| workflow_dispatch: | |
| inputs: | |
| os-repo: | |
| description: The repository containing the SeedSigner OS sources | |
| required: false | |
| default: 3rdIteration/seedsigner-os | |
| os-ref: | |
| description: The seedsigner-os ref (tag/branch/sha1) to use | |
| default: main | |
| required: true | |
| app-repo: | |
| description: The repository containing the SeedSigner application sources | |
| required: false | |
| default: 3rdIteration/seedsigner | |
| source-ref: | |
| description: The seedsigner ref (tag/branch/sha1) to use | |
| default: dev | |
| required: true | |
| target: | |
| description: Build target profile (e.g., pi0, pi2, pi02w, pi4) | |
| required: false | |
| default: pi0 | |
| smartcard: | |
| description: Enable smartcard support for the build | |
| required: false | |
| default: 'true' | |
| type: boolean | |
| dev: | |
| description: Build dev image | |
| required: false | |
| default: 'false' | |
| type: boolean | |
| # Increment this number as part of a PR to trigger an image build for the PR | |
| # trigger = 0 | |
| jobs: | |
| build: | |
| name: >- | |
| build${{ github.event_name == 'workflow_dispatch' | |
| && format( | |
| ' ({0} from {1}@{2}, os {3}@{4}, smartcard {5})', | |
| github.event.inputs.target || 'pi0', | |
| github.event.inputs['app-repo'] || github.repository, | |
| github.event.inputs['source-ref'], | |
| github.event.inputs['os-repo'] || '3rdIteration/seedsigner-os', | |
| github.event.inputs['os-ref'], | |
| (fromJSON(github.event.inputs.smartcard || 'true') && 'enabled') || 'disabled' | |
| ) | |
| || '' | |
| }} | |
| runs-on: ubuntu-latest | |
| # Prevent resource consuming cron triggered runs in forks | |
| if: (!github.event.repository.fork || github.event_name == 'workflow_dispatch') | |
| env: | |
| DOCKER_DEFAULT_PLATFORM: linux/amd64 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: [ "${{ github.event_name == 'workflow_dispatch' && github.event.inputs.target || 'pi0' }}" ] | |
| # Default target pi0 to keep within 500mb storage limit for Github free | |
| steps: | |
| - name: checkout seedsigner-os | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs['os-repo'] || '3rdIteration/seedsigner-os' }} | |
| # use the os-ref input parameter in case of workflow_dispatch or default to main in case of cron triggers | |
| ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.os-ref || 'main' }} | |
| submodules: true | |
| path: "seedsigner-os" | |
| # get full history + tags for "git describe" | |
| fetch-depth: 0 | |
| - name: Prepare build metadata | |
| run: | | |
| echo "builder_hash=$(git -C seedsigner-os rev-parse --short HEAD)" | tee -a "$GITHUB_ENV" | |
| APP_REPO_INPUT="${{ github.event_name == 'workflow_dispatch' && github.event.inputs['app-repo'] || github.repository }}" | |
| if [[ "${APP_REPO_INPUT}" =~ ^https?:// ]]; then | |
| APP_REPO_URL="${APP_REPO_INPUT}" | |
| else | |
| APP_REPO_URL="https://github.com/${APP_REPO_INPUT}" | |
| fi | |
| APP_REF="${{ github.event_name == 'workflow_dispatch' && github.event.inputs['source-ref'] || github.sha }}" | |
| SS_ARGS="--${{ matrix.target }} --app-repo=${APP_REPO_URL} --app-commit-id=${APP_REF}" | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ github.event.inputs.dev }}" = "true" ]; then | |
| SS_ARGS+=" --dev" | |
| fi | |
| if [ "${{ github.event_name }}" != "workflow_dispatch" ] || [ "${{ github.event.inputs.smartcard }}" = "true" ]; then | |
| SS_ARGS+=" --smartcard" | |
| fi | |
| echo "SS_ARGS=${SS_ARGS}" | tee -a "$GITHUB_ENV" | |
| - name: free disk space | |
| run: | | |
| echo "Disk usage before cleanup:" | |
| df -h | |
| sudo rm -rf \ | |
| /opt/hostedtoolcache/CodeQL \ | |
| /usr/local/lib/android/sdk \ | |
| /usr/share/dotnet | |
| sudo apt-get clean | |
| docker system prune --all --force || true | |
| echo "Disk usage after cleanup:" | |
| df -h | |
| - name: restore build cache | |
| uses: actions/cache@v4 | |
| # Caching reduces the build time to ~50% (currently: ~30 mins instead of ~1 hour, | |
| # while consuming ~850 MB storage space). | |
| with: | |
| path: | | |
| seedsigner-os/.buildroot-ccache | |
| seedsigner-os/.ccache | |
| key: build-cache-${{ matrix.target }}-${{ env.builder_hash }} | |
| restore-keys: | | |
| build-cache-${{ matrix.target }}- | |
| - name: Build image with docker compose | |
| run: | | |
| cd seedsigner-os | |
| mkdir -p .buildroot-ccache .ccache images | |
| docker compose up --force-recreate --build | |
| docker compose down | |
| - name: Fix permissions | |
| run: | | |
| sudo chown -R $USER:$USER seedsigner-os/images seedsigner-os/.buildroot-ccache seedsigner-os/.ccache | |
| - name: list images | |
| run: | | |
| ls -la seedsigner-os/images | |
| - name: normalize image archives for reproducibility | |
| run: | | |
| # The seedsigner-os build script stamps images with a fixed timestamp using | |
| # "touch -d '2025-07-01 00:00:00'" before zipping. That call is interpreted in | |
| # the host's local timezone, which shifts the stored mtime across systems and | |
| # makes otherwise identical .img.zip files hash differently. Normalize here by | |
| # re-zipping with a fixed UTC timestamp. | |
| cd seedsigner-os/images | |
| shopt -s nullglob | |
| zip_files=(*.img.zip) | |
| SOURCE_DATE_EPOCH=1751328000 # 2025-07-01 00:00:00 UTC | |
| if ((${#zip_files[@]})); then | |
| for zip in "${zip_files[@]}"; do | |
| img_file="${zip%.zip}" | |
| unzip -o "$zip" | |
| TZ=UTC touch -d "@${SOURCE_DATE_EPOCH}" "$img_file" | |
| rm -f "$zip" | |
| TZ=UTC SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} zip -X -j "$zip" "$img_file" | |
| TZ=UTC touch -d "@${SOURCE_DATE_EPOCH}" "$zip" | |
| done | |
| fi | |
| - name: print sha256sum | |
| run: | | |
| cd seedsigner-os/images | |
| shopt -s nullglob | |
| zip_files=(*.img.zip) | |
| if ((${#zip_files[@]})); then | |
| unzip -o "${zip_files[@]}" | |
| fi | |
| if ((${#zip_files[@]})); then | |
| sha256sum "${zip_files[@]}" | |
| fi | |
| img_files=(*.img) | |
| if ((${#img_files[@]})); then | |
| sha256sum "${img_files[@]}" | |
| fi | |
| - name: upload images | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: seedsigner_os_images-${{ matrix.target }} | |
| path: "seedsigner-os/images/*.img.zip" | |
| if-no-files-found: error | |
| # maximum 90 days retention | |
| retention-days: 90 | |
| sha256sum: | |
| name: calculate sha256sum | |
| runs-on: ubuntu-latest | |
| needs: build | |
| steps: | |
| - name: download images | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: images | |
| - name: list images | |
| run: | | |
| ls -lRa images | |
| - name: get seedsigner latest commit hash | |
| id: get-seedsigner-hash | |
| run: | | |
| git init | |
| echo "source_hash=$(git rev-parse --short ${{ github.sha }})" >> $GITHUB_ENV | |
| - name: write sha256sum | |
| run: | | |
| cd images | |
| # each downloaded image is in its own subfolder | |
| find . -name "*.img.zip" -exec mv {} . \; | |
| shopt -s nullglob | |
| zip_files=(*.img.zip) | |
| if ((${#zip_files[@]})); then | |
| unzip -o "${zip_files[@]}" | |
| fi | |
| img_files=(*.img) | |
| checksum_files=() | |
| ((${#zip_files[@]})) && checksum_files+=("${zip_files[@]}") | |
| ((${#img_files[@]})) && checksum_files+=("${img_files[@]}") | |
| if ((${#checksum_files[@]})); then | |
| sha256sum "${checksum_files[@]}" > seedsigner_os.${{ env.source_hash }}.sha256 | |
| else | |
| echo "No image files found for checksums" >&2 | |
| exit 1 | |
| fi | |
| - name: upload checksums | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: seedsigner_os_images_sha256 | |
| path: "images/*.sha256" | |
| if-no-files-found: error | |
| # maximum 90 days retention | |
| retention-days: 90 |