Initial commit: AnyDesk installer for Raspberry Pi

This commit is contained in:
2026-07-13 06:38:23 +00:00
commit fffb3317ef
6 changed files with 214 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
# Build artefacts — fetched by build.sh, never committed
src/anydesk_arm64.deb
packages/
*.pkg
+108
View File
@@ -0,0 +1,108 @@
# insin_anydesk
Insin package that installs [AnyDesk](https://anydesk.com) on Raspberry Pi devices
(arm64) managed by a self-hosted Insin server.
The package is published to the Insin server and assigned to the `raspberry pi`
device group in the admin panel. Each device's `insin monitor` picks it up on
its next heartbeat, extracts the pkg, and runs `postinst` as root.
## Package structure
```
insin_anydesk/
├── build.sh # CI script: pull deb + CLI, pack, publish
├── src/ # pkg contents — `cd src && insin pack anydesk@<ver>`
│ ├── postinst # apt-installs bundled .deb, enables systemd unit
│ ├── prem # stops + apt-removes anydesk on pkg removal
│ ├── install.cfg # install_path=/opt/insin/anydesk
│ └── anydesk_arm64.deb # bundled at build time by build.sh (gitignored)
└── packages/ # `insin pack` output (gitignored)
```
Recognised files in the ZIP (per Insin's package manager):
| File | Role |
|---------------|-------------------------------------------------------------------|
| `postinst` | Bash script run as root after extraction. New installs and version bumps trigger it; same-version reassignments are skipped. |
| `prem` | Bash script run before removal, or before overlaying a new version on top of an existing install. |
| `install.cfg` | `key=value` overrides. Supported keys: `install_path`, `run_postinst`, `run_prem`. |
Everything else in the ZIP (the `.deb`) is copied verbatim into the install
directory (`install_path`) alongside the scripts, so `postinst` can reference it
as `./anydesk_arm64.deb`.
### Metadata
There is no manifest inside the pkg — Insin derives `name` and `version` from
the `name@version.pkg` filename. The version must parse as `MAJOR.MINOR.PATCH`
(`Version.Parse`). The pkg version tracks the upstream AnyDesk version:
`anydesk@6.4.0` bundles the AnyDesk 6.4.0-1 arm64 `.deb`.
Republishing the same version is rejected server-side with 409. To force a
retry after a failed install, bump the version.
## Environment variables
| Var | Where | Purpose |
|-----------------|-----------|----------------------------------------------------------------------------------|
| `INSIN_URL` | build | Base URL of the Insin server (no trailing slash, no `/api`). |
| `INSIN_TOKEN` | build | Service token minted in the Insin admin panel (Service Tokens → New token). |
| `GIT_URL` | repo host | Full clone URL of this repo on Gitea (already set globally). |
| `GIT_USER` | repo host | Gitea username for push/pull (already set globally). |
| `GIT_KEY` | repo host | Gitea personal access token (already set globally). |
| `ANYDESK_VERSION` | build (optional) | Full deb tag (e.g. `6.4.0-1`). Default: `6.4.0-1`. |
| `PKG_VERSION` | build (optional) | Insin pkg semver. Default: `ANYDESK_VERSION` with the `-N` suffix stripped. |
| `INSIN_CLI_RID` | build (optional) | Override CLI artifact rid. Default: derived from `uname -m`; only `linux-arm64` is currently published, so the runner must be arm64. |
Never echo `INSIN_TOKEN`, `GIT_KEY`, or `GIT_USER` to logs. `build.sh` pipes
CLI stderr through a sed redactor as a safety net.
## Build & publish
```sh
./build.sh
# or override the anydesk version
ANYDESK_VERSION=6.4.1-1 PKG_VERSION=6.4.1 ./build.sh
```
`build.sh`:
1. Downloads `anydesk_${ANYDESK_VERSION}_arm64.deb` from
`download.anydesk.com/rpi/` into `src/`.
2. Fetches the latest `insin` CLI (`linux-arm64`) from
`$INSIN_URL/api/v1/downloads/cli` into `/tmp/insin/`.
3. `cd src && insin pack anydesk@${PKG_VERSION}`
`packages/anydesk@${PKG_VERSION}.pkg`.
4. `insin publish packages/anydesk@${PKG_VERSION}.pkg`.
## Rolling out
Publishing puts the pkg on the server. It does **not** assign it to any
device. After publish:
- Admin panel → **Groups → raspberry pi → Assignments** → add
`anydesk@<version>`, OR
- `PUT $INSIN_URL/api/v1/admin/groups/<piGroupId>/assignments` with
`Authorization: AdminToken $INSIN_TOKEN` and body
`{ "packageName": "anydesk", "version": "<version>" }`.
Each Pi picks up the assignment on its next heartbeat (default 10 min) and
runs `postinst`.
## On-device runtime
- `postinst` and `prem` run under `/bin/bash -c "./<script>"` as a single
subshell — one shell invocation, not line-by-line. `set -euxo pipefail` is
set at the top so failures surface loudly in the `insin monitor` journal.
- Exit codes are **logged but ignored** by the agent. A failed `postinst`
still marks the package as installed. To force a retry, bump the version.
- On upgrade, old files are **not** wiped before the new pkg is overlaid —
stale files in `install_path` persist unless `prem` cleans them.
Irrelevant for AnyDesk itself (the deb owns `/opt`, `/etc`, `/usr`), but
worth remembering if the pkg starts carrying additional state.
## References
- Insin publishing skill: `~/.claude/skills/publishing-packages-to-insin/`.
- AnyDesk RPi downloads: <https://anydesk.com/en/downloads/raspberry-pi>.
+62
View File
@@ -0,0 +1,62 @@
#!/bin/bash
# Build & publish the insin anydesk package.
#
# Required env:
# INSIN_URL - e.g. https://insin.example.com
# INSIN_TOKEN - service token from the Insin admin panel
#
# Optional env:
# ANYDESK_VERSION - full deb version tag (default: 6.4.0-1)
# PKG_VERSION - insin package semver (default: derived from ANYDESK_VERSION)
# INSIN_CLI_RID - CLI artifact rid (default: auto-detected from uname -m)
set -euo pipefail
: "${INSIN_URL:?INSIN_URL must be set}"
: "${INSIN_TOKEN:?INSIN_TOKEN must be set}"
ANYDESK_VERSION="${ANYDESK_VERSION:-6.4.0-1}"
PKG_VERSION="${PKG_VERSION:-${ANYDESK_VERSION%-*}}"
case "${INSIN_CLI_RID:-$(uname -m)}" in
linux-arm64|aarch64|arm64) RID="linux-arm64" ;;
linux-x64|x86_64|amd64)
echo "ERROR: no linux-x64 insin CLI artifact — run on an arm64 runner" >&2
exit 1 ;;
*) RID="${INSIN_CLI_RID:-linux-arm64}" ;;
esac
HERE="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
SRC="$HERE/src"
redact() { sed -E "s|${INSIN_TOKEN}|***|g"; }
echo "==> Downloading AnyDesk ${ANYDESK_VERSION} arm64 .deb"
curl -fsSL "https://download.anydesk.com/rpi/anydesk_${ANYDESK_VERSION}_arm64.deb" \
-o "$SRC/anydesk_arm64.deb"
echo "==> Fetching latest insin CLI ($RID)"
CLI_META="$(curl -fsSL "$INSIN_URL/api/v1/downloads/cli" 2>&1 | redact)"
read -r CLI_VERSION CLI_FILE < <(python3 -c '
import json, sys
m = json.loads(sys.argv[1])[0]
a = next(x for x in m["artifacts"] if x["rid"] == sys.argv[2])
print(m["version"], a["filename"])
' "$CLI_META" "$RID")
mkdir -p /tmp/insin
curl -fsSL "$INSIN_URL/api/v1/downloads/cli/$CLI_VERSION/$CLI_FILE" \
-o /tmp/insin.tar.gz 2>&1 | redact
tar -xzf /tmp/insin.tar.gz -C /tmp/insin
chmod +x /tmp/insin/insin
echo "==> Packing anydesk@${PKG_VERSION}"
( cd "$SRC" && /tmp/insin/insin pack "anydesk@${PKG_VERSION}" )
PKG="$HERE/packages/anydesk@${PKG_VERSION}.pkg"
[[ -f "$PKG" ]] || { echo "ERROR: expected $PKG but it wasn't produced" >&2; exit 1; }
echo "==> Publishing $PKG"
/tmp/insin/insin publish "$PKG" 2>&1 | redact
echo "==> Done: anydesk@${PKG_VERSION} published"
+3
View File
@@ -0,0 +1,3 @@
install_path=/opt/insin/anydesk
run_postinst=true
run_prem=true
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
set -euxo pipefail
DEB="anydesk_arm64.deb"
if [[ ! -f "$DEB" ]]; then
echo "postinst: bundled $DEB not found in $(pwd)" >&2
exit 1
fi
if [[ $EUID -ne 0 ]]; then
echo "postinst: must run as root (uid=$EUID)" >&2
exit 1
fi
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y --no-install-recommends "./$DEB"
systemctl enable --now anydesk
anydesk --version || true
echo "postinst: anydesk installed"
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
set -euxo pipefail
if [[ $EUID -ne 0 ]]; then
echo "prem: must run as root (uid=$EUID)" >&2
exit 1
fi
export DEBIAN_FRONTEND=noninteractive
systemctl disable --now anydesk 2>/dev/null || true
apt-get remove -y anydesk 2>/dev/null || true
echo "prem: anydesk removed"