├── .github └── workflows │ ├── build-group.yml │ └── build.yml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── README.md ├── deno.jsonc ├── deno.lock ├── images ├── alpine │ ├── build-context │ │ └── Dockerfile │ └── docker-compose.yaml └── debian │ ├── build-context │ └── Dockerfile │ └── docker-compose.yaml ├── renovate.json └── src ├── alpine.config.ts ├── debian.config.ts ├── github.ts ├── main.ts └── utils.ts /.github/workflows/build-group.yml: -------------------------------------------------------------------------------- 1 | name: 🔨 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | tasks: 7 | required: true 8 | type: string 9 | push: 10 | required: true 11 | type: boolean 12 | default: false 13 | manifests: 14 | required: false 15 | type: string 16 | default: "" 17 | 18 | jobs: 19 | build: 20 | name: 🧩 ${{ matrix.name }} 21 | runs-on: ${{ matrix.runsOn }} 22 | strategy: 23 | matrix: 24 | include: ${{ fromJson(inputs.tasks) }} 25 | fail-fast: false 26 | steps: 27 | - uses: actions/checkout@v4 28 | - uses: docker/setup-qemu-action@v3 29 | - uses: docker/setup-buildx-action@v3 30 | with: 31 | version: latest 32 | buildkitd-config-inline: | 33 | [registry."docker.io"] 34 | mirrors = ["mirror.gcr.io"] 35 | - if: ${{ inputs.push }} 36 | uses: docker/login-action@v3 37 | with: 38 | username: ${{ secrets.DOCKER_USERNAME }} 39 | password: ${{ secrets.DOCKER_PASSWORD }} 40 | - uses: docker/login-action@v3 41 | with: 42 | registry: ghcr.io 43 | username: ${{ github.actor }} 44 | password: ${{ secrets.GITHUB_TOKEN }} 45 | - run: git clone --branch ${{ matrix.gitRef }} https://github.com/merbanan/rtl_433 ${{ matrix.context }}/rtl_433 46 | - uses: docker/build-push-action@v6 47 | with: 48 | context: ${{ matrix.context }} 49 | file: ${{ matrix.file }} 50 | build-args: ${{ matrix.buildArgs }} 51 | platforms: ${{ matrix.platforms }} 52 | pull: true 53 | push: ${{ inputs.push }} 54 | cache-from: ${{ matrix.cacheFrom }} 55 | cache-to: ${{ matrix.cacheTo }} 56 | tags: ${{ matrix.tags }} 57 | 58 | manifest: 59 | if: ${{ inputs.push && inputs.manifests != '' }} 60 | name: 📦 Manifests 61 | needs: 62 | - build 63 | runs-on: ubuntu-latest 64 | steps: 65 | - uses: docker/setup-buildx-action@v3 66 | with: 67 | version: latest 68 | buildkitd-config-inline: | 69 | [registry."docker.io"] 70 | mirrors = ["mirror.gcr.io"] 71 | - uses: docker/login-action@v3 72 | with: 73 | username: ${{ secrets.DOCKER_USERNAME }} 74 | password: ${{ secrets.DOCKER_PASSWORD }} 75 | - uses: docker/login-action@v3 76 | with: 77 | registry: ghcr.io 78 | username: ${{ github.actor }} 79 | password: ${{ secrets.GITHUB_TOKEN }} 80 | - run: ${{ inputs.manifests }} -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: 🐳 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | pull_request: 8 | branches: 9 | - master 10 | push: 11 | branches: 12 | - master 13 | 14 | jobs: 15 | setup: 16 | runs-on: ubuntu-latest 17 | outputs: 18 | groups: ${{ steps.config.outputs.matrix }} 19 | steps: 20 | - uses: denoland/setup-deno@v1 21 | with: 22 | deno-version: v1.x 23 | - uses: actions/checkout@v4 24 | - id: config 25 | run: deno run --allow-net --allow-read --allow-write --allow-env src/main.ts 26 | 27 | builds: 28 | name: 🏷️ ${{ matrix.name }} 29 | needs: 30 | - setup 31 | uses: ./.github/workflows/build-group.yml 32 | strategy: 33 | matrix: 34 | include: ${{ fromJson(needs.setup.outputs.groups) }} 35 | fail-fast: false 36 | with: 37 | tasks: ${{ toJson(matrix.tasks) }} 38 | push: ${{ github.ref == 'refs/heads/master' || github.event_name == 'schedule' }} 39 | manifests: ${{ matrix.manifests }} 40 | secrets: inherit -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | 3 | .vscode/* 4 | !.vscode/settings.json 5 | !.vscode/extensions.json 6 | 7 | node_modules/ 8 | 9 | # Ignore Yarn 2 files including Zero-Installs 10 | .yarn/* 11 | !.yarn/releases 12 | !.yarn/plugins 13 | !.yarn/sdks 14 | !.yarn/versions 15 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["denoland.vscode-deno"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true, 3 | "editor.defaultFormatter": "denoland.vscode-deno" 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🐋 [rtl_433](https://hub.docker.com/r/hertzg/rtl_433) Docker Image [![Buildx](https://github.com/hertzg/rtl_433_docker/actions/workflows/buildx.yml/badge.svg)](https://github.com/hertzg/rtl_433_docker/actions/workflows/buildx.yml) 2 | 3 | Repository containing 4 | [multiarch docker images](https://hub.docker.com/r/hertzg/rtl_433) definitions 5 | of [rtl_433](https://github.com/merbanan/rtl_433) utility from 6 | [merbanan](https://github.com/merbanan). 7 | 8 | Default `latest` images include `rtlsdr` only with `alpine` linux as base to 9 | keep the final result as slim as possible.\ 10 | There is also `debian` based images which include `rtlsdr` and `sopysdr` with 11 | all modules but are slightly bigger `~3mb` vs `~50mb`. 12 | 13 | There are multiple flavours (`alpine` & `debian` based) built for multiple 14 | platforms (mainly `x86`, `arm` and more). 15 | 16 | ## Usage 17 | 18 | :warning: Tags have been refactored to follow "a more logical" structure. 19 | :warning: 20 | 21 | ``` 22 | hertzg/rtl_433:-- 23 | hertzg/rtl433:-- 24 | ghcr.io/hertzg/rtl_433_docker:-- 25 | ``` 26 | 27 | For convenience the **latest version of** `alpine` base is considered as 28 | "default" so you can just use 29 | 30 | :information_source: Keep in mind that only the \***\*latest release\*\*** of 31 | rtl_433 (`22.11` as of 2023-05-28) is considered `:latest` as 32 | ``. :information_source: 33 | 34 | ``` 35 | docker run hertzg/rtl_433:latest -V 36 | 37 | # is the exact same image as 38 | 39 | docker run hertzg/rtl_433:22.11 -V # as of 28 May 2023 the 22.11 is the latest released version 40 | docker run hertzg/rtl_433:22.11-alpine -V # shorthand for 22.11-alpine-latest 41 | docker run hertzg/rtl_433:22.11-alpine-3 -V # targets Alpine major version 3 42 | docker run hertzg/rtl_433:22.11-alpine-3.18 -V # targets Alpine minor version 3.18 43 | docker run hertzg/rtl_433:22.11-alpine-3.18.0 -V # targets Alpine patch version 3.18.0 44 | docker run hertzg/rtl_433:22.11-alpine-latest -V # targets latest Alpine version 45 | ``` 46 | 47 | :bulb: For Debian based builds you just use `debian` as `` instead of 48 | `alpine` and provide a correct debian version. 49 | 50 | If you want the latest `master` branch build use `:master` or `:nightly` for 51 | nightly builds. The following are 52 | 53 | ``` 54 | # for alpine based images 55 | docker run hertzg/rtl_433:master -V 56 | docker run hertzg/rtl_433:master-alpine -V 57 | docker run hertzg/rtl_433:master-alpine-3 -V 58 | docker run hertzg/rtl_433:master-alpine-3.18 -V 59 | docker run hertzg/rtl_433:master-alpine-3.18.0 -V 60 | docker run hertzg/rtl_433:master-alpine-latest -V 61 | ``` 62 | 63 | The tool itself is very versatile, you can run in multiple ways. The docker 64 | image uses the `rtl_433` executable as `ENTRYPOINT` so all the command line 65 | arguments get passed to it when used together with `docker run`. 66 | 67 | First you need to find the bus and device ids for your SDR device. Here I use 68 | `lsusb` to find the bus and device ids for my `RTL2838` receiver. 69 | 70 | Same approach should work for other USB devices as well. 71 | 72 | ```shell script 73 | pi@raspberry:~ $ lsusb 74 | Bus xxx Device xxx: ID xxxx:xxxx ... 75 | Bus xxx Device xxx: ID xxxx:xxxx ... 76 | Bus 001 Device 003: ID 0bda:2838 Realtek Semiconductor Corp. RTL2838 DVB-T 77 | Bus xxx Device xxx: ID xxxx:xxxx ... 78 | Bus xxx Device xxx: ID xxxx:xxxx ... 79 | ``` 80 | 81 | Based on the output my device can be referenced on usb bus `001` as device 82 | `003`. 83 | 84 | Next we need to start the container and share the `/dev/bus/usb/001/003` device 85 | to it using the `--device` or `-d` flag. 86 | 87 | **Note**: This device enumerator could (most likely will) change if you 88 | plug/unplug the device or the hub it's attached to, but in most cases if you use 89 | the bus path that was generated right after boot and leave the device untouched 90 | (don't re-plug) the device, the bus path might say the same. 91 | ([#14](https://github.com/hertzg/rtl_433_docker/issues/14)) 92 | 93 | ```shell script 94 | pi@raspberry:~ $ docker run --device /dev/bus/usb/001/003 hertzg/rtl_433 95 | ``` 96 | 97 | The `--device` or `-d` flag will share the host usb device to the container it 98 | will most likely be unusable by the host or any other containers. 99 | 100 | To use a specific version of `rtl_433` use the docker image tags described from the 101 | table later down 102 | 103 | ```shell script 104 | pi@raspberry:~ $ docker run --device /dev/bus/usb/001/003 hertzg/rtl_433:master 105 | pi@raspberry:~ $ docker run --device /dev/bus/usb/001/003 hertzg/rtl_433:latest 106 | pi@raspberry:~ $ docker run --device /dev/bus/usb/001/003 hertzg/rtl_433:20.02 107 | pi@raspberry:~ $ docker run --device /dev/bus/usb/001/003 hertzg/rtl_433:18.05 108 | pi@raspberry:~ $ docker run --device /dev/bus/usb/001/003 hertzg/rtl_433:alpine-20.02 109 | pi@raspberry:~ $ docker run --device /dev/bus/usb/001/003 hertzg/rtl_433:alpine-3-20.02 110 | pi@raspberry:~ $ docker run --device /dev/bus/usb/001/003 hertzg/rtl_433:alpine-3.14-20.02 111 | pi@raspberry:~ $ docker run --device /dev/bus/usb/001/003 hertzg/rtl_433:alpine-3.14.3-20.02 112 | pi@raspberry:~ $ docker run --device /dev/bus/usb/001/003 hertzg/rtl_433:-- 113 | ``` 114 | 115 | To pass arguments to `rtl_433` executable use can use the docker commands by 116 | appending them at the end of the run command 117 | 118 | ```shell script 119 | pi@raspberry:~ $ docker run --device /dev/bus/usb/001/003 hertzg/rtl_433 -h 120 | rtl_433 version 20.02 branch at 202002171252 inputs file rtl_tcp RTL-SDR 121 | Use -h for usage help and see https://triq.org/ for documentation. 122 | Trying conf file at "rtl_433.conf"... 123 | Trying conf file at "/root/.config/rtl_433/rtl_433.conf"... 124 | Trying conf file at "/usr/local/etc/rtl_433/rtl_433.conf"... 125 | Trying conf file at "/etc/rtl_433/rtl_433.conf"... 126 | Generic RF data receiver and decoder for ISM band devices using RTL-SDR and SoapySDR. 127 | 128 | Usage: 129 | = General options = 130 | [-V] Output the version string and exit 131 | [-v] Increase verbosity (can be used multiple times). 132 | -v : verbose, -vv : verbose decoders, -vvv : debug decoders, -vvvv : trace decoding). 133 | [-c ] Read config options from a file 134 | = Tuner options = 135 | [-d | : | | rtl_tcp | help] 136 | [-g | help] (default: auto) 137 | [-t ] apply a list of keyword=value settings for SoapySDR devices 138 | e.g. -t "antenna=A,bandwidth=4.5M,rfnotch_ctrl=false" 139 | [-f ] Receive frequency(s) (default: 433920000 Hz) 140 | [-H ] Hop interval for polling of multiple frequencies (default: 600 seconds) 141 | [-p ] Set sample rate (default: 250000 Hz) 143 | = Demodulator options = 144 | [-R | help] Enable only the specified device decoding protocol (can be used multiple times) 145 | Specify a negative number to disable a device decoding protocol (can be used multiple times) 146 | [-G] Enable blacklisted device decoding protocols, for testing only. 147 | [-X | help] Add a general purpose decoder (prepend -R 0 to disable all decoders) 148 | [-l ] Change detection level used to determine pulses (0-16384) (0=auto) (default: 0) 149 | [-z ] Override short value in data decoder 150 | [-x ] Override long value in data decoder 151 | [-n ] Specify number of samples to take (each sample is 2 bytes: 1 each of I & Q) 152 | [-Y auto | classic | minmax] FSK pulse detector mode. 153 | = Analyze/Debug options = 154 | [-a] Analyze mode. Print a textual description of the signal. 155 | [-A] Pulse Analyzer. Enable pulse analysis and decode attempt. 156 | Disable all decoders with -R 0 if you want analyzer output only. 157 | [-y ] Verify decoding of demodulated test data (e.g. "{25}fb2dd58") with enabled devices 158 | = File I/O options = 159 | [-S none | all | unknown | known] Signal auto save. Creates one file per signal. 160 | Note: Saves raw I/Q samples (uint8 pcm, 2 channel). Preferred mode for generating test files. 161 | [-r | help] Read data from input file instead of a receiver 162 | [-w | help] Save data stream to output file (a '-' dumps samples to stdout) 163 | [-W | help] Save data stream to output file, overwrite existing file 164 | = Data output options = 165 | [-F kv | json | csv | mqtt | influx | syslog | null | help] Produce decoded output in given format. 166 | Append output to file with : (e.g. -F csv:log.csv), defaults to stdout. 167 | Specify host/port for syslog with e.g. -F syslog:127.0.0.1:1514 168 | [-M time[:] | protocol | level | stats | bits | help] Add various meta data to each output. 169 | [-K FILE | PATH | ] Add an expanded token or fixed tag to every output line. 170 | [-C native | si | customary] Convert units in decoded output. 171 | [-T ] Specify number of seconds to run, also 12:34 or 1h23m45s 172 | [-E hop | quit] Hop/Quit after outputting successful event(s) 173 | [-h] Output this usage help and exit 174 | Use -d, -g, -R, -X, -F, -M, -r, -w, or -W without argument for more help 175 | ``` 176 | 177 | To see all the output formatters you can pass `-F` without arguments 178 | 179 | ``` 180 | $ docker run hertzg/rtl_433 -F 181 | rtl_433 version 20.02 branch at 202002171252 inputs file rtl_tcp RTL-SDR 182 | Use -h for usage help and see https://triq.org/ for documentation. 183 | /usr/local/bin/rtl_433: option requires an argument: F 184 | Trying conf file at "rtl_433.conf"... 185 | Trying conf file at "/root/.config/rtl_433/rtl_433.conf"... 186 | Trying conf file at "/usr/local/etc/rtl_433/rtl_433.conf"... 187 | Trying conf file at "/etc/rtl_433/rtl_433.conf"... 188 | /usr/local/bin/rtl_433: option requires an argument: F 189 | = Output format option = 190 | [-F kv|json|csv|mqtt|influx|syslog|null] Produce decoded output in given format. 191 | Without this option the default is KV output. Use "-F null" to remove the default. 192 | Append output to file with : (e.g. -F csv:log.csv), defaults to stdout. 193 | Specify MQTT server with e.g. -F mqtt://localhost:1883 194 | Add MQTT options with e.g. -F "mqtt://host:1883,opt=arg" 195 | MQTT options are: user=foo, pass=bar, retain[=0|1], [=topic] 196 | Supported MQTT formats: (default is all) 197 | events: posts JSON event data 198 | states: posts JSON state data 199 | devices: posts device and sensor info in nested topics 200 | The topic string will expand keys like [/model] 201 | E.g. -F "mqtt://localhost:1883,user=USERNAME,pass=PASSWORD,retain=0,devices=rtl_433[/id]" 202 | With MQTT each rtl_433 instance needs a distinct driver selection. The MQTT Client-ID is computed from the driver string. 203 | If you use multiple RTL-SDR, perhaps set a serial and select by that (helps not to get the wrong antenna). 204 | Specify InfluxDB 2.0 server with e.g. -F "influx://localhost:9999/api/v2/write?org=&bucket=,token=" 205 | Specify InfluxDB 1.x server with e.g. -F "influx://localhost:8086/write?db=&p=&u=" 206 | Additional parameter -M time:unix:usec:utc for correct timestamps in InfluxDB recommended 207 | Specify host/port for syslog with e.g. -F syslog:127.0.0.1:1514 208 | ``` 209 | 210 | You can also use `-d`, `-g`, `-R`, `-X`, `-F`, `-M`, `-r`, `-w`, or `-W` without 211 | argument for more help on their usages as described in the help. 212 | 213 | ## Example Usages 214 | 215 | Use rtl_433 to capture 433Mhz traffic, decode it and send to your local MQTT 216 | broker. 217 | 218 | ```shell script 219 | pi@raspberry:~ $ docker run --device /dev/bus/usb/001/003 hertzg/rtl_433 -Fmqtt://127.0.0.1:1883 220 | ``` 221 | 222 | same as above and also send the data to influxdb 223 | 224 | ```shell script 225 | pi@raspberry:~ $ docker run --device /dev/bus/usb/001/003 hertzg/rtl_433 -Mtime:unix:usec:utc -Fmqtt://127.0.0.1:1883 -Finflux://127.0.0.1:8086/write?db=rtl433 226 | ``` 227 | 228 | same as above but with extra information like signal, protocol information and 229 | system stats 230 | 231 | ```shell script 232 | pi@raspberry:~ $ docker run --device /dev/bus/usb/001/003 hertzg/rtl_433 -Mtime:unix:usec:utc -Mbits -Mlevel -Mprotocol -Mstats:2:300 -Fmqtt://127.0.0.1:1883 -Finflux://127.0.0.1:8086/write?db=rtl433 233 | ``` 234 | 235 | same as above but for `docker-compose.yaml` using MQTT and InfluxDB from the 236 | same compose stack 237 | 238 | ```yaml 239 | version: "3" 240 | services: 241 | rtl433: 242 | image: hertzg/rtl_433:latest 243 | devices: 244 | - "/dev/bus/usb/001/003" 245 | command: 246 | - "-Mtime:unix:usec:utc" 247 | - "-Mbits" 248 | - "-Mlevel" 249 | - "-Mprotocol" 250 | - "-Mstats:2:300" 251 | - "-Fmqtt://mosquitto:1883,retain=1" 252 | - "-Finflux://influxdb:8086/write?db=rtl433" 253 | 254 | mosquitto: ... 255 | 256 | influxdb: ... 257 | ``` 258 | 259 | ### Multi-arch 260 | 261 | Images are ready to run on different architectures. Due to popularity of small 262 | "credit card" sized devices and such each tag has multi-arch manifest supporting 263 | following platforms: 264 | 265 | | Architecture | Alpine | Debian | 266 | | ---------------- | ------ | ------ | 267 | | `linux/386` | ✔️ | ➖ | 268 | | `linux/amd64` | ✔️ | ✔️ | 269 | | `linux/arm/v5` | ️❌ | ➖ | 270 | | `linux/arm/v6` | ✔️ | ❌️ | 271 | | `linux/arm/v7` | ✔️ | ✔️ | 272 | | `linux/arm64/v8` | ✔️ | ✔️ | 273 | | `linux/mips64le` | ❌️ | ✔️ | 274 | | `linux/ppc64le` | ✔️ | ✔️ | 275 | | `linux/s390x` | ⚠️ | ✔️ | 276 | 277 | #### Legend 278 | 279 | - ✔️ : Supported and built for 280 | - ⚠️ : Not buildable 281 | - ➖ : Not supported (think of it as ❌️) 282 | - ❌️ : Not supported by the base image 283 | -------------------------------------------------------------------------------- /deno.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run --watch src/main.ts" 4 | }, 5 | "imports": { 6 | "@actions/core": "npm:@actions/core@^1.10.1", 7 | "@std/cli": "jsr:@std/cli@^0.224.4" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /deno.lock: -------------------------------------------------------------------------------- 1 | { 2 | "version": "3", 3 | "packages": { 4 | "specifiers": { 5 | "npm:@actions/core@1.10.0": "npm:@actions/core@1.10.0", 6 | "npm:@actions/core@^1.10.1": "npm:@actions/core@1.10.1", 7 | "npm:@actions/github@5.1.1": "npm:@actions/github@5.1.1_@octokit+core@3.6.0" 8 | }, 9 | "npm": { 10 | "@actions/core@1.10.0": { 11 | "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", 12 | "dependencies": { 13 | "@actions/http-client": "@actions/http-client@2.1.0", 14 | "uuid": "uuid@8.3.2" 15 | } 16 | }, 17 | "@actions/core@1.10.1": { 18 | "integrity": "sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==", 19 | "dependencies": { 20 | "@actions/http-client": "@actions/http-client@2.1.0", 21 | "uuid": "uuid@8.3.2" 22 | } 23 | }, 24 | "@actions/github@5.1.1_@octokit+core@3.6.0": { 25 | "integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==", 26 | "dependencies": { 27 | "@actions/http-client": "@actions/http-client@2.1.0", 28 | "@octokit/core": "@octokit/core@3.6.0", 29 | "@octokit/plugin-paginate-rest": "@octokit/plugin-paginate-rest@2.21.3_@octokit+core@3.6.0", 30 | "@octokit/plugin-rest-endpoint-methods": "@octokit/plugin-rest-endpoint-methods@5.16.2_@octokit+core@3.6.0" 31 | } 32 | }, 33 | "@actions/http-client@2.1.0": { 34 | "integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==", 35 | "dependencies": { 36 | "tunnel": "tunnel@0.0.6" 37 | } 38 | }, 39 | "@octokit/auth-token@2.5.0": { 40 | "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", 41 | "dependencies": { 42 | "@octokit/types": "@octokit/types@6.41.0" 43 | } 44 | }, 45 | "@octokit/core@3.6.0": { 46 | "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", 47 | "dependencies": { 48 | "@octokit/auth-token": "@octokit/auth-token@2.5.0", 49 | "@octokit/graphql": "@octokit/graphql@4.8.0", 50 | "@octokit/request": "@octokit/request@5.6.3", 51 | "@octokit/request-error": "@octokit/request-error@2.1.0", 52 | "@octokit/types": "@octokit/types@6.41.0", 53 | "before-after-hook": "before-after-hook@2.2.3", 54 | "universal-user-agent": "universal-user-agent@6.0.0" 55 | } 56 | }, 57 | "@octokit/endpoint@6.0.12": { 58 | "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", 59 | "dependencies": { 60 | "@octokit/types": "@octokit/types@6.41.0", 61 | "is-plain-object": "is-plain-object@5.0.0", 62 | "universal-user-agent": "universal-user-agent@6.0.0" 63 | } 64 | }, 65 | "@octokit/graphql@4.8.0": { 66 | "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", 67 | "dependencies": { 68 | "@octokit/request": "@octokit/request@5.6.3", 69 | "@octokit/types": "@octokit/types@6.41.0", 70 | "universal-user-agent": "universal-user-agent@6.0.0" 71 | } 72 | }, 73 | "@octokit/openapi-types@12.11.0": { 74 | "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==", 75 | "dependencies": {} 76 | }, 77 | "@octokit/plugin-paginate-rest@2.21.3_@octokit+core@3.6.0": { 78 | "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", 79 | "dependencies": { 80 | "@octokit/core": "@octokit/core@3.6.0", 81 | "@octokit/types": "@octokit/types@6.41.0" 82 | } 83 | }, 84 | "@octokit/plugin-rest-endpoint-methods@5.16.2_@octokit+core@3.6.0": { 85 | "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", 86 | "dependencies": { 87 | "@octokit/core": "@octokit/core@3.6.0", 88 | "@octokit/types": "@octokit/types@6.41.0", 89 | "deprecation": "deprecation@2.3.1" 90 | } 91 | }, 92 | "@octokit/request-error@2.1.0": { 93 | "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", 94 | "dependencies": { 95 | "@octokit/types": "@octokit/types@6.41.0", 96 | "deprecation": "deprecation@2.3.1", 97 | "once": "once@1.4.0" 98 | } 99 | }, 100 | "@octokit/request@5.6.3": { 101 | "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", 102 | "dependencies": { 103 | "@octokit/endpoint": "@octokit/endpoint@6.0.12", 104 | "@octokit/request-error": "@octokit/request-error@2.1.0", 105 | "@octokit/types": "@octokit/types@6.41.0", 106 | "is-plain-object": "is-plain-object@5.0.0", 107 | "node-fetch": "node-fetch@2.6.11", 108 | "universal-user-agent": "universal-user-agent@6.0.0" 109 | } 110 | }, 111 | "@octokit/types@6.41.0": { 112 | "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", 113 | "dependencies": { 114 | "@octokit/openapi-types": "@octokit/openapi-types@12.11.0" 115 | } 116 | }, 117 | "before-after-hook@2.2.3": { 118 | "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", 119 | "dependencies": {} 120 | }, 121 | "deprecation@2.3.1": { 122 | "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", 123 | "dependencies": {} 124 | }, 125 | "is-plain-object@5.0.0": { 126 | "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", 127 | "dependencies": {} 128 | }, 129 | "node-fetch@2.6.11": { 130 | "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", 131 | "dependencies": { 132 | "whatwg-url": "whatwg-url@5.0.0" 133 | } 134 | }, 135 | "once@1.4.0": { 136 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 137 | "dependencies": { 138 | "wrappy": "wrappy@1.0.2" 139 | } 140 | }, 141 | "tr46@0.0.3": { 142 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", 143 | "dependencies": {} 144 | }, 145 | "tunnel@0.0.6": { 146 | "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", 147 | "dependencies": {} 148 | }, 149 | "universal-user-agent@6.0.0": { 150 | "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", 151 | "dependencies": {} 152 | }, 153 | "uuid@8.3.2": { 154 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", 155 | "dependencies": {} 156 | }, 157 | "webidl-conversions@3.0.1": { 158 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", 159 | "dependencies": {} 160 | }, 161 | "whatwg-url@5.0.0": { 162 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 163 | "dependencies": { 164 | "tr46": "tr46@0.0.3", 165 | "webidl-conversions": "webidl-conversions@3.0.1" 166 | } 167 | }, 168 | "wrappy@1.0.2": { 169 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 170 | "dependencies": {} 171 | } 172 | } 173 | }, 174 | "remote": { 175 | "https://deno.land/std@0.188.0/semver/mod.ts": "200f50cf6872212667df532fb09f0b1a33d3427a5201f75fad30a0d0c6dbcce3" 176 | }, 177 | "workspace": { 178 | "dependencies": [ 179 | "jsr:@std/cli@^0.224.4", 180 | "npm:@actions/core@^1.10.1" 181 | ] 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /images/alpine/build-context/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG alpineVersion=latest 2 | FROM alpine:${alpineVersion} AS builder 3 | 4 | RUN apk add --no-cache --virtual .buildDeps \ 5 | build-base \ 6 | libusb-dev \ 7 | openssl-dev \ 8 | librtlsdr-dev \ 9 | cmake \ 10 | git 11 | 12 | WORKDIR /build 13 | 14 | ADD ./rtl_433 ./rtl_433 15 | WORKDIR ./rtl_433 16 | WORKDIR ./build 17 | RUN cmake -DENABLE_OPENSSL=ON .. 18 | RUN make 19 | WORKDIR /build/root 20 | WORKDIR /build/rtl_433/build 21 | RUN make DESTDIR=/build/root/ install 22 | RUN ls -lah /build/root 23 | 24 | ARG alpineVersion=latest 25 | FROM alpine:${alpineVersion} 26 | 27 | ARG rtl433GitRevision=master 28 | LABEL maintainer="georgedot@gmail.com" \ 29 | vcs-ref="${rtl433GitRevision}" 30 | 31 | RUN apk add --no-cache libusb librtlsdr tzdata openssl 32 | WORKDIR /root 33 | COPY --from=builder /build/root/ / 34 | 35 | ENTRYPOINT ["/usr/local/bin/rtl_433"] 36 | -------------------------------------------------------------------------------- /images/alpine/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | master: 5 | image: hertzg/rtl_433:alpine-master 6 | build: 7 | context: ../.. 8 | args: 9 | - alpineVersion=latest 10 | - rtl433GitRevision=master 11 | -------------------------------------------------------------------------------- /images/debian/build-context/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG debianVersion=latest 2 | FROM debian:${debianVersion} AS builder 3 | 4 | RUN apt-get update && apt-get install -y \ 5 | build-essential \ 6 | cmake \ 7 | git \ 8 | libusb-1.0-0-dev \ 9 | libsoapysdr-dev \ 10 | libssl-dev \ 11 | librtlsdr-dev \ 12 | && rm -rf /var/lib/apt/lists/* 13 | 14 | WORKDIR /build 15 | 16 | ADD ./rtl_433 ./rtl_433 17 | WORKDIR ./rtl_433 18 | WORKDIR ./build 19 | RUN cmake -DENABLE_OPENSSL=ON .. 20 | RUN make 21 | WORKDIR /build/root 22 | WORKDIR /build/rtl_433/build 23 | RUN make DESTDIR=/build/root/ install 24 | RUN ls -lah /build/root 25 | 26 | ARG debianVersion=latest 27 | FROM debian:${debianVersion} 28 | 29 | ARG rtl433GitRevision=master 30 | LABEL maintainer="georgedot@gmail.com" \ 31 | vcs-ref="${rtl433GitRevision}" 32 | 33 | RUN apt-get update && apt-get install -y \ 34 | libusb-1.0-0 \ 35 | librtlsdr0 \ 36 | '^libsoapysdr0\.[6-8]$' \ 37 | '^libssl(1\.1|3)$' \ 38 | '^soapysdr0\.[6-8]-module-all$' \ 39 | && rm -rf /var/lib/apt/lists/* 40 | 41 | WORKDIR /root 42 | COPY --from=builder /build/root/ / 43 | 44 | ENTRYPOINT ["/usr/local/bin/rtl_433"] 45 | -------------------------------------------------------------------------------- /images/debian/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | master: 5 | image: hertzg/rtl_433:debian-master 6 | build: 7 | context: ../.. 8 | args: 9 | - debianVersion=latest 10 | - rtl433GitRevision=master 11 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | ":semanticCommits" 4 | ], 5 | "assigneesFromCodeOwners": true, 6 | "packageRules": [{ 7 | "matchPackagePatterns": ["*"], 8 | "matchUpdateTypes": ["minor", "patch"], 9 | "groupName": "all non-major dependencies", 10 | "groupSlug": "all-minor-patch", 11 | "automerge": false, 12 | "labels": ["dependencies"] 13 | }, { 14 | "matchPackagePatterns": ["*"], 15 | "matchUpdateTypes": ["major"], 16 | "labels": ["dependencies", "breaking"] 17 | }] 18 | } 19 | -------------------------------------------------------------------------------- /src/alpine.config.ts: -------------------------------------------------------------------------------- 1 | import { BuildTask } from "./main.ts"; 2 | import { semMajor, semMinor, sortRtl433TagsDesc } from "./utils.ts"; 3 | 4 | const fetchLastAlpineCycleVersions = async () => { 5 | const res = await fetch("https://endoflife.date/api/alpine.json"); 6 | 7 | const cycles = (await res.json()) as Array<{ 8 | cycle: string; 9 | releaseDate: string; 10 | eol: string; 11 | latest: string; 12 | latestReleaseDate: string; 13 | lts: boolean; 14 | }>; 15 | 16 | return cycles 17 | .slice(0, 2) 18 | .map((cycles) => cycles.latest) 19 | .slice(0, 1); 20 | }; 21 | 22 | const ALPINE_VERSIONS = await fetchLastAlpineCycleVersions(); 23 | const ALPINE_LATEST_VERSION = ALPINE_VERSIONS[0]; 24 | 25 | const generateTags = (baseVersion: string, gitRef: string) => { 26 | const tags = [`${gitRef}-alpine-${baseVersion}`]; 27 | 28 | if (baseVersion.includes(".")) { 29 | tags.push( 30 | ...[ 31 | `${gitRef}-alpine-${semMinor(baseVersion)}`, 32 | `${gitRef}-alpine-${semMajor(baseVersion)}`, 33 | ] 34 | ); 35 | } 36 | 37 | if (baseVersion === "latest") { 38 | tags.push(...[`${gitRef}-alpine`]); 39 | } 40 | 41 | return tags; 42 | }; 43 | 44 | export const createAlpineBuildTasks = (gitRefs: string[]): BuildTask[] => { 45 | const [latestGitRef] = sortRtl433TagsDesc(gitRefs); 46 | 47 | const variants = gitRefs.flatMap((gitRef) => 48 | ALPINE_VERSIONS.map((alpineVersion) => { 49 | const isLatestGitRef = gitRef === latestGitRef; 50 | const isLatestBase = alpineVersion === ALPINE_LATEST_VERSION; 51 | return { 52 | gitRef, 53 | alpineVersion, 54 | isLatestGitRef, 55 | isLatestBase, 56 | }; 57 | }) 58 | ); 59 | 60 | const tasks: BuildTask[] = variants.map( 61 | ({ gitRef, alpineVersion, isLatestBase, isLatestGitRef }) => { 62 | const tags = generateTags(alpineVersion, gitRef); 63 | 64 | if (isLatestGitRef) { 65 | tags.push(...generateTags(alpineVersion, "latest")); 66 | } 67 | 68 | if (isLatestBase) { 69 | tags.push(...generateTags("latest", gitRef)); 70 | tags.push(`${gitRef}`); 71 | } 72 | 73 | if (isLatestGitRef && isLatestBase) { 74 | tags.push(...generateTags("latest", "latest")); 75 | tags.push("latest"); 76 | } 77 | 78 | return { 79 | name: `${gitRef}-alpine-${alpineVersion}`, 80 | gitRef: gitRef, 81 | context: "./images/alpine/build-context", 82 | file: "./images/alpine/build-context/Dockerfile", 83 | tags, 84 | buildArgs: { 85 | rtl433GitVersion: gitRef, 86 | alpineVersion: alpineVersion, 87 | }, 88 | platforms: [ 89 | "linux/amd64", 90 | "linux/arm64/v8", 91 | "linux/arm/v6", 92 | "linux/arm/v7", 93 | "linux/ppc64le", 94 | // "linux/386", 95 | ], 96 | cacheFrom: `type=gha,scope=alpine-${alpineVersion}-${gitRef}`, 97 | cacheTo: `type=gha,scope=alpine-${alpineVersion}-${gitRef}`, 98 | }; 99 | } 100 | ); 101 | 102 | return tasks; 103 | }; 104 | -------------------------------------------------------------------------------- /src/debian.config.ts: -------------------------------------------------------------------------------- 1 | import { BuildTask } from "./main.ts"; 2 | import { sortRtl433TagsDesc } from "./utils.ts"; 3 | 4 | const fetchLastDebianCycleCodenames = async () => { 5 | const res = await fetch("https://endoflife.date/api/debian.json"); 6 | 7 | const cycles = (await res.json()) as Array<{ 8 | cycle: string; 9 | codename: string; 10 | releaseDate: string; 11 | eol: string; 12 | extendedSupport: string; 13 | link: string; 14 | latest: string; 15 | latestReleaseDate: string; 16 | lts: boolean; 17 | }>; 18 | 19 | return cycles 20 | .slice(0, 2) 21 | .map((cycles) => cycles.codename.toLocaleLowerCase()) 22 | .slice(0, 1); 23 | }; 24 | 25 | const DEBIAN_VERSIONS = await fetchLastDebianCycleCodenames(); 26 | const DEBIAN_LATEST_VERSION = DEBIAN_VERSIONS[0]; 27 | 28 | const BROKEN_RTLVERSIONS_FOR_DEBIAN_CYCLES = new Map([ 29 | ["bookworm", ["19.08", "18.12"]], 30 | ]); 31 | 32 | const generateTags = (baseVersion: string, gitRef: string) => { 33 | const tags = [`${gitRef}-debian-${baseVersion}`]; 34 | 35 | if (baseVersion === "latest") { 36 | tags.push(...[`${gitRef}-debian`]); 37 | } 38 | 39 | return tags; 40 | }; 41 | 42 | export const createDebianBuildTasks = (gitRefs: string[]): BuildTask[] => { 43 | const [latestGitRef] = sortRtl433TagsDesc(gitRefs); 44 | 45 | const variants = gitRefs.flatMap((gitRef) => 46 | DEBIAN_VERSIONS.map((debianVersion) => { 47 | const isLatestGitRef = gitRef === latestGitRef; 48 | const isLatestBase = debianVersion === DEBIAN_LATEST_VERSION; 49 | return { 50 | gitRef, 51 | debianVersion, 52 | isLatestGitRef, 53 | isLatestBase, 54 | }; 55 | }) 56 | ); 57 | 58 | const tasks: BuildTask[] = variants 59 | .filter(({ gitRef, debianVersion }) => { 60 | if (BROKEN_RTLVERSIONS_FOR_DEBIAN_CYCLES.has(debianVersion)) { 61 | const brokenRefs = 62 | BROKEN_RTLVERSIONS_FOR_DEBIAN_CYCLES.get(debianVersion)!; 63 | return !brokenRefs.includes(gitRef); 64 | } 65 | 66 | return true; 67 | }) 68 | .map(({ gitRef, debianVersion, isLatestGitRef, isLatestBase }) => { 69 | const tags = generateTags(debianVersion, gitRef); 70 | 71 | if (isLatestBase) { 72 | tags.push(...generateTags("latest", gitRef)); 73 | } 74 | 75 | if (isLatestGitRef) { 76 | tags.push(...generateTags(debianVersion, "latest")); 77 | } 78 | 79 | if (isLatestBase && isLatestGitRef) { 80 | tags.push(...generateTags("latest", "latest")); 81 | } 82 | 83 | return { 84 | name: `${gitRef}-debian-${debianVersion}`, 85 | gitRef: gitRef, 86 | context: "./images/debian/build-context", 87 | file: "./images/debian/build-context/Dockerfile", 88 | tags, 89 | buildArgs: { 90 | rtl433GitVersion: gitRef, 91 | debianVersion: debianVersion, 92 | }, 93 | platforms: [ 94 | "linux/amd64", 95 | "linux/arm/v7", 96 | "linux/arm64/v8", 97 | "linux/mips64le", 98 | "linux/ppc64le", 99 | "linux/s390x", 100 | ], 101 | cacheFrom: `type=gha,scope=debian-${debianVersion}-${gitRef}`, 102 | cacheTo: `type=gha,scope=debian-${debianVersion}-${gitRef}`, 103 | }; 104 | }); 105 | 106 | return tasks; 107 | }; 108 | -------------------------------------------------------------------------------- /src/github.ts: -------------------------------------------------------------------------------- 1 | export interface GithubRepoTag { 2 | name: string; 3 | commit: { 4 | sha: string; 5 | url: string; 6 | }; 7 | zipball_url: string; 8 | tarball_url: string; 9 | node_id: string; 10 | } 11 | 12 | export const getGithubRepoTags = async ( 13 | repo: string, 14 | ): Promise => { 15 | const res = await fetch(`https://api.github.com/repos/${repo}/tags`); 16 | const tags = await res.json(); 17 | return tags; 18 | }; 19 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createAlpineBuildTasks } from "./alpine.config.ts"; 2 | import { createDebianBuildTasks } from "./debian.config.ts"; 3 | import { setOutput } from "@actions/core"; 4 | import { getGithubRepoTags } from "./github.ts"; 5 | import { 6 | tagify, 7 | stringifyTagsWithRepos, 8 | stringifyBuildArgs, 9 | stringifyPlatforms, 10 | crossTagsAndRepos, 11 | } from "./utils.ts"; 12 | import { toPlatformPath } from "@actions/core"; 13 | 14 | export interface BuildTask { 15 | name: string; 16 | gitRef: string; 17 | context: string; 18 | file: string; 19 | tags: string[]; 20 | buildArgs: { 21 | [key: string]: string; 22 | rtl433GitVersion: string; 23 | }; 24 | platforms: string[]; 25 | cacheFrom: string; 26 | cacheTo: string; 27 | } 28 | 29 | const REPOS = [ 30 | "hertzg/rtl433", 31 | "hertzg/rtl_433", 32 | "ghcr.io/hertzg/rtl_433_docker", 33 | ]; 34 | 35 | const tags: string[] = ["master"]; 36 | tags.push( 37 | ...(await getGithubRepoTags("merbanan/rtl_433")) 38 | .map((tag) => tag.name) 39 | .filter((tag) => /^[0-9\.]*$/i.test(tag)) 40 | ); 41 | 42 | const alpineTasks = createAlpineBuildTasks(tags); 43 | const debianTasks = createDebianBuildTasks(tags); 44 | 45 | const tasks = [...alpineTasks, ...debianTasks].sort( 46 | (a, b) => a.name.localeCompare(b.name) * -1 47 | ); 48 | 49 | interface TaskGroup { 50 | [key: string]: { 51 | tasks: TaskGroupEntry[]; 52 | manifests: string; 53 | }; 54 | } 55 | 56 | interface TaskGroupEntry { 57 | name: string; 58 | gitRef: string; 59 | context: string; 60 | file: string; 61 | tags: string; 62 | runsOn: string; 63 | buildArgs: string; 64 | platforms: string; 65 | cacheFrom: string; 66 | cacheTo: string; 67 | } 68 | 69 | const generateRunAfterScript = (task: BuildTask) => { 70 | const lines: string[] = []; 71 | for (const repo of REPOS) { 72 | for (const tag of task.tags) { 73 | const arches = task.platforms.map( 74 | (platform) => `${repo}:${tag}-${tagify(platform)}` 75 | ); 76 | lines.push( 77 | `docker buildx imagetools create -t ${repo}:${tag} ${arches.join(" ")}` 78 | ); 79 | } 80 | } 81 | 82 | return lines; 83 | }; 84 | 85 | const platformToRunner = (platform: string) => { 86 | // const [os, arch, ...rest] = platform.split("/"); 87 | 88 | return "ubuntu-24.04"; 89 | }; 90 | 91 | const groups: TaskGroup = {}; 92 | for (const task of tasks) { 93 | const groupKey = `${task.name}`; 94 | if (!groups[groupKey]) { 95 | const runAfter = generateRunAfterScript(task); 96 | groups[groupKey] = { 97 | tasks: [], 98 | manifests: runAfter.join("\n"), 99 | }; 100 | } 101 | 102 | for (const platform of task.platforms) { 103 | const suffixedTags = task.tags.map((tag) => `${tag}-${tagify(platform)}`); 104 | groups[groupKey].tasks.push({ 105 | name: platform, 106 | gitRef: task.gitRef, 107 | context: task.context, 108 | file: task.file, 109 | runsOn: platformToRunner(platform), 110 | tags: stringifyTagsWithRepos(suffixedTags, REPOS), 111 | buildArgs: stringifyBuildArgs(task.buildArgs), 112 | platforms: stringifyPlatforms([platform]), 113 | cacheFrom: task.cacheFrom, 114 | cacheTo: task.cacheTo, 115 | }); 116 | } 117 | } 118 | 119 | const groupEntries = Object.entries(groups).map(([key, group]) => { 120 | return { 121 | name: key, 122 | tasks: group.tasks, 123 | manifests: group.manifests, 124 | }; 125 | }); 126 | setOutput("matrix", groupEntries); 127 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import { type BuildTask } from "./main.ts"; 2 | 3 | export const fetchGitTagNames = async (repo: string) => { 4 | const res = await fetch(`https://api.github.com/repos/${repo}/tags`); 5 | const tags: { name: string; [key: string]: unknown }[] = await res.json(); 6 | return tags.map((tag) => tag.name); 7 | }; 8 | 9 | export const semUp = (sem: string, dots: number) => 10 | sem.split(".").slice(0, dots).join("."); 11 | 12 | export const semMinor = (sem: string) => semUp(sem, 2); 13 | 14 | export const semMajor = (sem: string) => semUp(sem, 1); 15 | 16 | const RTL433_TAG_REGEX = /^(\d+)\.(\d+)$/i; 17 | export const sortRtl433TagsDesc = (tags: string[]) => { 18 | return tags 19 | .filter((tag) => RTL433_TAG_REGEX.test(tag)) 20 | .sort((a, b) => b.localeCompare(a, undefined, { numeric: true })); 21 | }; 22 | 23 | export const crossTagsAndRepos = (tags: string[], repos: string[]) => { 24 | return tags.flatMap((tag) => repos.map((repo) => `${repo}:${tag}`)); 25 | }; 26 | 27 | export const stringifyTagsWithRepos = (tags: string[], repos: string[]) => 28 | crossTagsAndRepos(tags, repos).join("\n"); 29 | 30 | export const stringifyBuildArgs = (buildArgs: Record) => 31 | Object.entries(buildArgs) 32 | .map(([key, value]) => `${key}=${value}`) 33 | .join("\n"); 34 | 35 | export const stringifyPlatforms = (platforms: string[]) => platforms.join(","); 36 | 37 | export const tagify = (str: string) => 38 | str.replace(/[^a-zA-Z0-9-_]/g, "_").slice(0, 128); 39 | --------------------------------------------------------------------------------