build: split current and legacy OpenWrt packages

This commit is contained in:
benya
2026-07-30 02:11:41 +03:00
parent 5a1168c77b
commit 2cc1a515e4
7 changed files with 419 additions and 41 deletions
+256
View File
@@ -0,0 +1,256 @@
name: Build legacy OpenWrt packages
on:
workflow_dispatch:
inputs:
agent_tag:
description: Existing agent release tag to extend (for example agent-v0.6.6)
required: true
type: string
permissions:
contents: read
concurrency:
group: legacy-packages-${{ inputs.agent_tag }}
cancel-in-progress: false
jobs:
validate:
name: Validate agent release
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.agent_tag }}
- name: Validate tag and package version
env:
AGENT_TAG: ${{ inputs.agent_tag }}
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
if [[ ! "$AGENT_TAG" =~ ^agent-v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "agent_tag must look like agent-vMAJOR.MINOR.PATCH" >&2
exit 1
fi
agent_version="${AGENT_TAG#agent-v}"
source_version="$(sed -n 's/^const agentVersion = "\([^"]*\)"/\1/p' agent/go/cmd/rmm-agent/main.go)"
package_version="$(sed -n 's/^PKG_VERSION:=//p' agent/package/rmm-agent-go-production/Makefile)"
test "$source_version" = "$agent_version"
test "$package_version" = "$agent_version"
gh release view "$AGENT_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null
latest_agent_tag="$(gh api \
"repos/${GITHUB_REPOSITORY}/releases?per_page=100" \
--jq '[.[] | select(.draft == false and .prerelease == false and (.tag_name | startswith("agent-v")))] | first | .tag_name')"
if [ "$AGENT_TAG" != "$latest_agent_tag" ]; then
echo "legacy packages may extend only the latest agent release: ${latest_agent_tag}" >&2
exit 1
fi
legacy-packages:
name: OpenWrt ${{ matrix.release }} · ${{ matrix.label }}
needs: validate
runs-on: ubuntu-24.04
timeout-minutes: 90
strategy:
fail-fast: false
max-parallel: 4
matrix:
include:
# Legacy support intentionally excludes bcm27xx/bcm2711. It can be
# restored as an on-demand row when a real supported device needs it.
- { release: "21.02.7", target: x86, subtarget: "64", label: x86-64, goarch: amd64 }
- { release: "21.02.7", target: ramips, subtarget: mt7621, label: ramips-mt7621, goarch: mipsle, gomips: softfloat }
- { release: "21.02.7", target: ath79, subtarget: generic, label: ath79-generic, goarch: mips, gomips: softfloat }
- { release: "21.02.7", target: ipq40xx, subtarget: generic, label: ipq40xx-generic, goarch: arm, goarm: "7" }
- { release: "22.03.7", target: x86, subtarget: "64", label: x86-64, goarch: amd64 }
- { release: "22.03.7", target: ramips, subtarget: mt7621, label: ramips-mt7621, goarch: mipsle, gomips: softfloat }
- { release: "22.03.7", target: ath79, subtarget: generic, label: ath79-generic, goarch: mips, gomips: softfloat }
- { release: "22.03.7", target: ipq40xx, subtarget: generic, label: ipq40xx-generic, goarch: arm, goarm: "7" }
- { release: "23.05.5", target: x86, subtarget: "64", label: x86-64, goarch: amd64 }
- { release: "23.05.5", target: ramips, subtarget: mt7621, label: ramips-mt7621, goarch: mipsle, gomips: softfloat }
- { release: "23.05.5", target: ath79, subtarget: generic, label: ath79-generic, goarch: mips, gomips: softfloat }
- { release: "23.05.5", target: ipq40xx, subtarget: generic, label: ipq40xx-generic, goarch: arm, goarm: "7" }
- { release: "23.05.5", target: mediatek, subtarget: filogic, label: mediatek-filogic, goarch: arm64 }
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.agent_tag }}
- name: Resolve official OpenWrt SDK
id: sdk
shell: bash
env:
RELEASE: ${{ matrix.release }}
TARGET: ${{ matrix.target }}
SUBTARGET: ${{ matrix.subtarget }}
run: |
set -euo pipefail
record="$(awk -F '\t' \
-v release="$RELEASE" \
-v target="$TARGET" \
-v subtarget="$SUBTARGET" \
'$1 == release && $2 == target && $3 == subtarget { print $4 " " $5; exit }' \
.github/openwrt-sdk-lock.tsv)"
if [ -z "$record" ]; then
echo "No locked SDK found for OpenWrt ${RELEASE} ${TARGET}/${SUBTARGET}" >&2
exit 1
fi
read -r sha256 url <<<"$record"
echo "url=${url}" >> "$GITHUB_OUTPUT"
echo "sha256=${sha256}" >> "$GITHUB_OUTPUT"
- uses: docker/setup-buildx-action@v3
- name: Build signed agent and LuCI packages
uses: docker/build-push-action@v6
env:
OPENWRT_USIGN_SECRET_B64: ${{ secrets.OPENWRT_USIGN_SECRET_B64 }}
with:
context: .
file: deploy/luci-builder/Dockerfile
target: artifacts
push: false
outputs: type=local,dest=artifacts
build-args: |
OPENWRT_SDK_URL=${{ steps.sdk.outputs.url }}
OPENWRT_SDK_SHA256=${{ steps.sdk.outputs.sha256 }}
RMM_GOARCH=${{ matrix.goarch }}
RMM_GOARM=${{ matrix.goarm || '7' }}
RMM_GOMIPS=${{ matrix.gomips || 'softfloat' }}
RMM_GOAMD64=v1
REQUIRE_NATIVE_SIGNATURE=true
REPOSITORY_INDEX_REVISION=${{ github.run_id }}
secret-envs: |
openwrt_usign_secret_b64=OPENWRT_USIGN_SECRET_B64
cache-from: type=gha,scope=openwrt-${{ matrix.release }}-${{ matrix.label }}
cache-to: type=gha,mode=max,scope=openwrt-${{ matrix.release }}-${{ matrix.label }}
- name: Verify package artifacts
shell: bash
run: |
set -euo pipefail
find artifacts -maxdepth 1 -type f -print
test -f artifacts/SHA256SUMS
(cd artifacts && sha256sum --check SHA256SUMS)
find artifacts -maxdepth 1 -type f -name '*.ipk' -print -quit | grep -q .
test -f artifacts/Packages
test -f artifacts/Packages.gz
test -f artifacts/Packages.sig
public_key="$(find artifacts -maxdepth 1 -type f -regextype posix-extended \
-regex '.*/[0-9a-f]{16}' -print -quit)"
test -n "$public_key"
- uses: actions/upload-artifact@v4
with:
name: openwrt-${{ matrix.release }}-${{ matrix.label }}
path: artifacts/*
if-no-files-found: error
retention-days: 30
publish:
name: Extend release and signed package repository
needs: [validate, legacy-packages]
runs-on: ubuntu-24.04
permissions:
contents: write
id-token: write
attestations: write
pages: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
# Repository tooling comes from the default branch so an existing
# agent tag can be extended after this workflow itself was introduced.
- uses: actions/checkout@v4
with:
ref: ${{ github.event.repository.default_branch }}
- uses: actions/download-artifact@v4
with:
pattern: openwrt-*
path: legacy-artifacts
- name: Prepare legacy release assets
shell: bash
run: |
set -euo pipefail
mkdir legacy-publish
while IFS= read -r file; do
artifact="$(basename "$(dirname "$file")")"
cp "$file" "legacy-publish/${artifact}-$(basename "$file")"
done < <(find legacy-artifacts -type f ! -name 'SHA256SUMS' -print | sort)
- name: Attest legacy release assets
uses: actions/attest-build-provenance@v2
with:
subject-path: legacy-publish/*
- name: Combine and reconstruct all release package artifacts
env:
AGENT_TAG: ${{ inputs.agent_tag }}
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
mkdir release-assets
gh release download "$AGENT_TAG" \
--repo "$GITHUB_REPOSITORY" \
--pattern 'openwrt-*' \
--dir release-assets
cp legacy-publish/* release-assets/
bash scripts/unpack-release-package-assets.sh \
release-assets \
.github/openwrt-sdk-lock.tsv \
combined-artifacts
bash scripts/prepare-package-repository.sh \
combined-artifacts \
package-repository \
"${AGENT_TAG#agent-v}"
- uses: sigstore/cosign-installer@v3
- name: Prepare combined release checksums
shell: bash
run: |
set -euo pipefail
(
cd release-assets
sha256sum ./openwrt-* > SHA256SUMS
)
- name: Attest combined release checksums
uses: actions/attest-build-provenance@v2
with:
subject-path: release-assets/SHA256SUMS
- name: Sign and upload combined release checksums
env:
AGENT_TAG: ${{ inputs.agent_tag }}
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
cosign sign-blob --yes \
--bundle release-assets/SHA256SUMS.sigstore.json \
release-assets/SHA256SUMS
mapfile -t legacy_files < <(find legacy-publish -type f -print | sort)
gh release upload "$AGENT_TAG" \
"${legacy_files[@]}" \
release-assets/SHA256SUMS \
release-assets/SHA256SUMS.sigstore.json \
--repo "$GITHUB_REPOSITORY" \
--clobber
- uses: actions/upload-pages-artifact@v3
with:
path: package-repository
- name: Deploy complete package repository to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
+35 -24
View File
@@ -55,6 +55,38 @@ jobs:
grep -Eq '^[[:space:]]*DEPENDS:=.*[[:space:]]\+ip([[:space:]]|$)' "$file"
! grep -Eq '\+ip-(tiny|full)' "$file"
done
- name: Verify package release support tiers
shell: bash
run: |
set -euo pipefail
bash -n \
scripts/prepare-package-repository.sh \
scripts/unpack-release-package-assets.sh
current_matrix="$(awk '
/Tagged releases cover the current OpenWrt lines/ { in_matrix = 1 }
in_matrix && /^[[:space:]]*- \{ release:/ { count++ }
in_matrix && /^[[:space:]]*steps:/ { print count; exit }
' .github/workflows/build.yml)"
legacy_matrix="$(awk '
/Legacy support intentionally excludes/ { in_matrix = 1 }
in_matrix && /^[[:space:]]*- \{ release:/ { count++ }
in_matrix && /^[[:space:]]*steps:/ { print count; exit }
' .github/workflows/build-legacy.yml)"
test "$current_matrix" = 10
test "$legacy_matrix" = 13
fixture="$(mktemp -d)"
trap 'rm -rf "$fixture"' EXIT
mkdir -p "$fixture/assets"
touch "$fixture/assets/openwrt-24.10.7-ramips-mt7621-Packages"
touch "$fixture/assets/openwrt-21.02.7-x86-64-rmm-agent.ipk"
bash scripts/unpack-release-package-assets.sh \
"$fixture/assets" \
.github/openwrt-sdk-lock.tsv \
"$fixture/output"
test -f "$fixture/output/openwrt-24.10.7-ramips-mt7621/Packages"
test -f "$fixture/output/openwrt-21.02.7-x86-64/rmm-agent.ipk"
openwrt-packages:
name: OpenWrt ${{ matrix.release }} · ${{ matrix.label }}
@@ -67,40 +99,19 @@ jobs:
max-parallel: 4
matrix:
include:
# The matrix covers the common CPU families: amd64, mipsel, mips,
# ARMv7 and ARM64. Add a target/subtarget row when another device
# family needs an architecture-specific package.
- { release: "21.02.7", target: x86, subtarget: "64", label: x86-64, goarch: amd64 }
- { release: "21.02.7", target: ramips, subtarget: mt7621, label: ramips-mt7621, goarch: mipsle, gomips: softfloat }
- { release: "21.02.7", target: ath79, subtarget: generic, label: ath79-generic, goarch: mips, gomips: softfloat }
- { release: "21.02.7", target: ipq40xx, subtarget: generic, label: ipq40xx-generic, goarch: arm, goarm: "7" }
- { release: "21.02.7", target: bcm27xx, subtarget: bcm2711, label: bcm27xx-bcm2711, goarch: arm64 }
- { release: "22.03.7", target: x86, subtarget: "64", label: x86-64, goarch: amd64 }
- { release: "22.03.7", target: ramips, subtarget: mt7621, label: ramips-mt7621, goarch: mipsle, gomips: softfloat }
- { release: "22.03.7", target: ath79, subtarget: generic, label: ath79-generic, goarch: mips, gomips: softfloat }
- { release: "22.03.7", target: ipq40xx, subtarget: generic, label: ipq40xx-generic, goarch: arm, goarm: "7" }
- { release: "22.03.7", target: bcm27xx, subtarget: bcm2711, label: bcm27xx-bcm2711, goarch: arm64 }
- { release: "23.05.5", target: x86, subtarget: "64", label: x86-64, goarch: amd64 }
- { release: "23.05.5", target: ramips, subtarget: mt7621, label: ramips-mt7621, goarch: mipsle, gomips: softfloat }
- { release: "23.05.5", target: ath79, subtarget: generic, label: ath79-generic, goarch: mips, gomips: softfloat }
- { release: "23.05.5", target: ipq40xx, subtarget: generic, label: ipq40xx-generic, goarch: arm, goarm: "7" }
- { release: "23.05.5", target: bcm27xx, subtarget: bcm2711, label: bcm27xx-bcm2711, goarch: arm64 }
- { release: "23.05.5", target: mediatek, subtarget: filogic, label: mediatek-filogic, goarch: arm64 }
# Tagged releases cover the current OpenWrt lines and the common
# router CPU families. OpenWrt 21.02-23.05 are built on demand by
# build-legacy.yml so a legacy SDK cannot block the main release.
- { release: "24.10.7", target: x86, subtarget: "64", label: x86-64, goarch: amd64 }
- { release: "24.10.7", target: ramips, subtarget: mt7621, label: ramips-mt7621, goarch: mipsle, gomips: softfloat }
- { release: "24.10.7", target: ath79, subtarget: generic, label: ath79-generic, goarch: mips, gomips: softfloat }
- { release: "24.10.7", target: ipq40xx, subtarget: generic, label: ipq40xx-generic, goarch: arm, goarm: "7" }
- { release: "24.10.7", target: bcm27xx, subtarget: bcm2711, label: bcm27xx-bcm2711, goarch: arm64 }
- { release: "24.10.7", target: mediatek, subtarget: filogic, label: mediatek-filogic, goarch: arm64 }
- { release: "25.12.4", target: x86, subtarget: "64", label: x86-64, goarch: amd64 }
- { release: "25.12.4", target: ramips, subtarget: mt7621, label: ramips-mt7621, goarch: mipsle, gomips: softfloat }
- { release: "25.12.4", target: ath79, subtarget: generic, label: ath79-generic, goarch: mips, gomips: softfloat }
- { release: "25.12.4", target: ipq40xx, subtarget: generic, label: ipq40xx-generic, goarch: arm, goarm: "7" }
- { release: "25.12.4", target: bcm27xx, subtarget: bcm2711, label: bcm27xx-bcm2711, goarch: arm64 }
- { release: "25.12.4", target: mediatek, subtarget: filogic, label: mediatek-filogic, goarch: arm64 }
steps:
+6 -4
View File
@@ -1,12 +1,13 @@
# Актуальный инженерный checklist
Синхронизировано с кодом 2026-07-23. Источник продуктового порядка — `ROADMAP.md`.
Синхронизировано с кодом 2026-07-30. Источник продуктового порядка — `ROADMAP.md`.
## Работает сейчас
- [x] Go server, SQLite/WAL, Docker/Compose и `/healthz`.
- [x] Go agent 0.6.5, heartbeat, команды, backoff и OpenWrt init integration.
- [x] APK/IPK и LuCI-пакет.
- [x] Go agent 0.6.6, heartbeat, команды, backoff и OpenWrt init integration.
- [x] APK/IPK и LuCI-пакет: OpenWrt 24.10/25.12 в основном релизе,
OpenWrt 21.02/22.03/23.05 в ручном legacy workflow.
- [x] Multi-user, роли admin/user, владение и передача роутеров.
- [x] Профиль, e-mail, смена/сброс пароля и управление сессиями.
- [x] Одноразовый secure enrollment; reusable token secrets хранятся как hash.
@@ -43,7 +44,8 @@
- [ ] Подписанный webhook channel.
- [ ] Quiet hours/timezone и per-device notification overrides.
- [ ] Конфигурационные backup artifacts и retention.
- [x] CI для тестов, Docker, multi-version APK/IPK и release artifacts.
- [x] CI для тестов, Docker, текущей multi-architecture APK/IPK-матрицы и отдельной
legacy-сборки без блокировки основного релиза.
- [x] Keyless Cosign, provenance/SBOM контейнеров и подписанные checksum релизов.
- [x] Нативные подписанные `Packages.sig`/`packages.adb` и публикация package feed.
- [ ] Signed update manifest и безопасное обновление агента из кабинета.
+13 -5
View File
@@ -5,16 +5,16 @@ Versioning.
## Server releases
Tags use `server-vMAJOR.MINOR.PATCH`, for example `server-v0.8.0`.
Tags use `server-vMAJOR.MINOR.PATCH`, for example `server-v0.8.1`.
A server release contains the Go API, web interface, database migrations and the coupled
SSH reverse-tunnel service. The GitHub Actions server workflow tests the server and
publishes two images with the same version:
```text
ghcr.io/benya9669/openwrt-rmm-server:0.8.0
ghcr.io/benya9669/openwrt-rmm-server:0.8.1
ghcr.io/benya9669/openwrt-rmm-server:latest
ghcr.io/benya9669/openwrt-rmm-tunnel:0.8.0
ghcr.io/benya9669/openwrt-rmm-tunnel:0.8.1
ghcr.io/benya9669/openwrt-rmm-tunnel:latest
```
@@ -35,6 +35,14 @@ An agent release contains the Go runtime, LuCI application and OpenWrt IPK/APK p
Before tagging, the tag version must match `agentVersion` in the Go source and
`PKG_VERSION` in the production Go package. CI rejects a mismatched release tag.
Tagged releases automatically build the current OpenWrt 24.10 and 25.12 package matrix.
OpenWrt 21.02, 22.03 and 23.05 are a manual legacy tier that extends an existing agent
release without blocking current packages. Run it after the tagged workflow completes:
```sh
gh workflow run build-legacy.yml -f agent_tag=agent-v0.6.6
```
The LuCI application is shipped as part of the router bundle. It can retain its own package
version for package-manager upgrades, but it does not require a separate GitHub release
line unless it later becomes an independent product.
@@ -55,8 +63,8 @@ transition period; it should not silently reuse `v1`.
## Release commands
```sh
git tag -a server-v0.8.0 -m "OpenWrt RMM Server 0.8.0"
git push origin server-v0.8.0
git tag -a server-v0.8.1 -m "OpenWrt RMM Server 0.8.1"
git push origin server-v0.8.1
git tag -a agent-v0.6.6 -m "OpenWrt RMM Agent 0.6.6"
git push origin agent-v0.6.6
+7 -3
View File
@@ -1,6 +1,6 @@
# OpenWrt RMM — актуальный roadmap
Актуализировано: 2026-07-23. Текущая стабильная линия агента: `0.6.5`.
Актуализировано: 2026-07-30. Текущая стабильная линия агента: `0.6.6`.
## Цель продукта
@@ -49,6 +49,8 @@ OpenWrt через исходящее соединение агента. Пол
- [x] SMTP STARTTLS/TLS и Telegram bot configuration через environment.
- [x] Версионированные server/tunnel images в GHCR и production Compose overlay.
- [x] SBOM/provenance, keyless Cosign и подписанный APK/IPK package repository.
- [x] Быстрая матрица текущих OpenWrt 24.10/25.12 и отдельная ручная legacy-сборка
OpenWrt 21.02/22.03/23.05 без блокировки основного релиза.
## Следующие этапы
@@ -110,6 +112,8 @@ OpenWrt через исходящее соединение агента. Пол
## Ближайший приоритет
1. Создать и сохранить ключи package feed, включить GitHub Pages и проверить первый подписанный agent/server release.
1. Завершить и проверить `agent-v0.6.6`, при необходимости добавить legacy-пакеты,
развернуть server `0.8.1` и проверить свежие SSH/LuCI-сессии.
2. Развернуть и проверить notification release на production.
3. Добавить webhook и quiet hours, затем начать конфигурационные backup/restore.
3. Добавить активное подтверждение проводных клиентов, webhook и quiet hours, затем
начать конфигурационные backup/restore.
+43 -5
View File
@@ -1,7 +1,13 @@
# Signed OpenWrt package repository
Agent release tags publish a repository for every supported OpenWrt release and target.
The default public base URL is:
Agent release tags automatically publish the current OpenWrt support tier:
- OpenWrt 24.10 and 25.12;
- `x86/64`, `ramips/mt7621`, `ath79/generic`, `ipq40xx/generic` and
`mediatek/filogic`.
OpenWrt 21.02, 22.03 and 23.05 packages are added to an existing agent release by the
manual legacy workflow described below. The default public base URL is:
```text
https://benya9669.github.io/openwrt-rmm/feeds/stable/openwrt
@@ -55,11 +61,43 @@ The public keys are committed under `keys/openwrt/`. Their expected identifiers
The release build verifies that each private key matches the committed public key before
publishing a signed feed.
An `agent-v*` release fails closed when the key required by an OpenWrt generation is
missing. Manual workflow runs may still create unsigned test artifacts. BuildKit secret
An `agent-v*` release and the legacy publication workflow fail closed when a required
native signing key is missing. A manual run of the general **Build and test** workflow
may still create unsigned test artifacts, but it does not publish them. BuildKit secret
mounts expose private keys only to the repository-index build step; private keys are not
copied into images, artifacts or build cache.
## Support tiers and legacy packages
The normal `agent-v*` workflow builds ten current package targets. This keeps the release
gate fast and prevents an obsolete SDK from blocking packages for supported OpenWrt
versions.
To extend the latest agent release with signed OpenWrt 21.02, 22.03 and 23.05 packages,
run **Actions → Build legacy OpenWrt packages → Run workflow** from the default branch
and enter its existing tag, for example `agent-v0.6.6`. The same operation is available
through GitHub CLI:
```sh
gh workflow run build-legacy.yml -f agent_tag=agent-v0.6.6
```
Run it only after the main agent release has completed. The legacy workflow:
1. checks out and builds the exact agent tag;
2. signs the IPK repositories with the configured `usign` key;
3. combines them with the current release and reconstructs the complete repository;
4. refreshes signed checksums and uploads the legacy files to the existing GitHub Release;
5. redeploys the complete current plus legacy repository to GitHub Pages.
The workflow refuses an older agent tag because publishing it would roll the `stable`
feed back from the latest agent version.
The legacy matrix contains `x86/64`, `ramips/mt7621`, `ath79/generic` and
`ipq40xx/generic`; OpenWrt 23.05 also contains `mediatek/filogic`.
`bcm27xx/bcm2711` is intentionally on-demand and should be added only when a supported
Raspberry Pi 4 installation needs a package.
## OpenWrt 24.10 and older: IPK/opkg
Choose the directory matching the firmware release and target. For example MT7621 on
@@ -106,7 +144,7 @@ Release assets also include a keyless Sigstore bundle for the combined checksums
cosign verify-blob \
--bundle SHA256SUMS.sigstore.json \
--certificate-identity-regexp \
'^https://github.com/Benya9669/openwrt-rmm/.github/workflows/build.yml@refs/tags/agent-v.*$' \
'^https://github.com/Benya9669/openwrt-rmm/.github/workflows/(build\.yml@refs/tags/agent-v.*|build-legacy\.yml@refs/heads/main)$' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
SHA256SUMS
sha256sum --check SHA256SUMS
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
set -euo pipefail
if [ "$#" -ne 3 ]; then
echo "usage: $0 <release-assets-dir> <sdk-lock-file> <output-dir>" >&2
exit 2
fi
source_dir="$1"
lock_file="$2"
output_dir="$3"
if [ ! -d "$source_dir" ]; then
echo "release assets directory does not exist: $source_dir" >&2
exit 1
fi
if [ ! -f "$lock_file" ]; then
echo "SDK lock file does not exist: $lock_file" >&2
exit 1
fi
if [ -e "$output_dir" ] && [ -n "$(find "$output_dir" -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null)" ]; then
echo "output directory must be empty: $output_dir" >&2
exit 1
fi
mkdir -p "$output_dir"
artifact_count=0
while IFS=$'\t' read -r release target subtarget _sha256 _url; do
[ -n "$release" ] || continue
case "$release" in
\#*) continue ;;
esac
case "$target/$subtarget" in
x86/64) label="x86-64" ;;
*) label="${target}-${subtarget}" ;;
esac
artifact_name="openwrt-${release}-${label}"
destination="$output_dir/$artifact_name"
matched=0
for asset in "$source_dir/$artifact_name"-*; do
[ -f "$asset" ] || continue
mkdir -p "$destination"
basename="${asset##*/}"
cp "$asset" "$destination/${basename#"$artifact_name"-}"
matched=1
done
if [ "$matched" -eq 1 ]; then
artifact_count=$((artifact_count + 1))
fi
done < "$lock_file"
if [ "$artifact_count" -eq 0 ]; then
echo "no prefixed OpenWrt package assets found in $source_dir" >&2
exit 1
fi
echo "reconstructed ${artifact_count} OpenWrt package artifact directories"