feat(labelprint): 4x6 label print proxy on a Raspberry Pi
A Pi 3B+ (stickah.local) shares a Phomemo PM246 to the LAN as a plain CUPS queue, so any machine can print 4x6 labels -- fulfillr-site's shipping labels in particular -- without installing the vendor driver, which is x86-64 only. The role builds the TSPL CUPS driver from source instead. It is Debian, not Fedora, so it lives in its own inventory and playbook (make deploy-labelprint / check-labelprint) and the home.debyl.io roles can never run against it. make bootfs renders its cloud-init first-boot files onto a freshly imaged SD card from the same templates the role uses. The Wi-Fi credentials for the home and rescue networks are in the vault. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
be0d02b938
commit
6cd4d56de1
@@ -0,0 +1,106 @@
|
||||
---
|
||||
# Renders the Raspberry Pi's cloud-init first-boot files onto a freshly written
|
||||
# SD card's boot partition:
|
||||
#
|
||||
# make bootfs BOOTFS=/Volumes/bootfs
|
||||
#
|
||||
# The image itself is still built by hand with rpi-imager -- this only writes
|
||||
# the two cloud-init files onto it. Everything it writes comes from the same
|
||||
# templates roles/labelprint uses, so the Pi boots with its Wi-Fi profiles and
|
||||
# rescue access point already in place, and the first deploy has nothing to
|
||||
# correct.
|
||||
#
|
||||
# The rendered files contain the Wi-Fi PSKs in the clear, as any Pi Wi-Fi setup
|
||||
# does. They land on the SD card, never in this repo.
|
||||
- hosts: localhost
|
||||
gather_facts: false
|
||||
connection: local
|
||||
vars_files:
|
||||
- vars/vault.yml
|
||||
- roles/labelprint/defaults/main.yml
|
||||
tasks:
|
||||
- name: check that BOOTFS points at a Raspberry Pi boot partition
|
||||
ansible.builtin.stat:
|
||||
path: "{{ bootfs }}/config.txt"
|
||||
register: labelprint_bootfs_check
|
||||
tags: bootfs
|
||||
|
||||
- name: refuse to write to anything else
|
||||
ansible.builtin.assert:
|
||||
that: labelprint_bootfs_check.stat.exists
|
||||
fail_msg: >-
|
||||
{{ bootfs }} has no config.txt, so it is not a Raspberry Pi boot
|
||||
partition. Write the image with rpi-imager first, then re-run with
|
||||
BOOTFS pointing at the mounted boot volume.
|
||||
tags: bootfs
|
||||
|
||||
- name: look for files rpi-imager already wrote
|
||||
ansible.builtin.stat:
|
||||
path: "{{ bootfs }}/{{ item }}"
|
||||
register: labelprint_bootfs_existing
|
||||
loop:
|
||||
- user-data
|
||||
- network-config
|
||||
- cmdline.txt
|
||||
tags: bootfs
|
||||
|
||||
# Keeps whatever rpi-imager put there, so a bad render can be undone by hand
|
||||
# without reflashing. force:false means the first run's backup is the one
|
||||
# that survives -- a second run must not overwrite it with our own output.
|
||||
- name: back up the files rpi-imager wrote
|
||||
ansible.builtin.copy:
|
||||
src: "{{ bootfs }}/{{ item.item }}"
|
||||
dest: "{{ bootfs }}/{{ item.item }}.rpi-imager.bak"
|
||||
mode: "0644"
|
||||
force: false
|
||||
loop: "{{ labelprint_bootfs_existing.results }}"
|
||||
loop_control:
|
||||
label: "{{ item.item }}"
|
||||
when: item.stat.exists
|
||||
tags: bootfs
|
||||
|
||||
- name: render the cloud-init files
|
||||
ansible.builtin.template:
|
||||
src: "roles/labelprint/templates/bootfs/{{ item }}.j2"
|
||||
dest: "{{ bootfs }}/{{ item }}"
|
||||
mode: "0644"
|
||||
loop:
|
||||
- user-data
|
||||
- network-config
|
||||
tags: bootfs
|
||||
|
||||
- name: hash the rendered user-data
|
||||
ansible.builtin.stat:
|
||||
path: "{{ bootfs }}/user-data"
|
||||
checksum_algorithm: sha1
|
||||
register: labelprint_user_data_stat
|
||||
tags: bootfs
|
||||
|
||||
- name: stamp the instance id with that hash
|
||||
ansible.builtin.template:
|
||||
src: roles/labelprint/templates/bootfs/meta-data.j2
|
||||
dest: "{{ bootfs }}/meta-data"
|
||||
mode: "0644"
|
||||
vars:
|
||||
labelprint_user_data_id: "{{ labelprint_user_data_stat.stat.checksum[:12] }}"
|
||||
tags: bootfs
|
||||
|
||||
# rpi-imager writes `ds=nocloud;i=<id>` onto the kernel command line, and
|
||||
# that id outranks the one in meta-data. Leave it alone and a card that has
|
||||
# booted even once is seen by cloud-init as the same instance forever: it
|
||||
# skips users, write_files and runcmd, silently, and the only symptom is a
|
||||
# Pi that came up with none of this applied. Both places have to agree.
|
||||
- name: pin the same instance id on the kernel command line
|
||||
ansible.builtin.replace:
|
||||
path: "{{ bootfs }}/cmdline.txt"
|
||||
regexp: '(ds=nocloud[^\s]*?);i=[^\s]+'
|
||||
replace: '\1;i={{ labelprint_hostname }}-{{ labelprint_user_data_stat.stat.checksum[:12] }}'
|
||||
tags: bootfs
|
||||
|
||||
- name: what to do next
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "Wrote user-data, network-config, meta-data and cmdline.txt to {{ bootfs }}."
|
||||
- "Instance id is {{ labelprint_hostname }}-{{ labelprint_user_data_stat.stat.checksum[:12] }}; the Pi re-applies this config whenever it changes."
|
||||
- "Eject the volume, boot the Pi, then: make deploy-labelprint"
|
||||
tags: bootfs
|
||||
Reference in New Issue
Block a user