Files
rmm-openwrt/.github/workflows/build.yml
T
benya 163018f156
Build and test / Tests and license checks (push) Has been cancelled
Build and test / OpenWrt 24.10.7 · x86-64 (push) Has been cancelled
Build and test / OpenWrt 25.12.4 · x86-64 (push) Has been cancelled
Build and test / OpenWrt 24.10.7 · ipq40xx-generic (push) Has been cancelled
Build and test / OpenWrt 25.12.4 · ipq40xx-generic (push) Has been cancelled
Build and test / OpenWrt 24.10.7 · mediatek-filogic (push) Has been cancelled
Build and test / OpenWrt 25.12.4 · mediatek-filogic (push) Has been cancelled
Build and test / OpenWrt 24.10.7 · ath79-generic (push) Has been cancelled
Build and test / OpenWrt 25.12.4 · ath79-generic (push) Has been cancelled
Build and test / OpenWrt 24.10.7 · ramips-mt7621 (push) Has been cancelled
Build and test / OpenWrt 25.12.4 · ramips-mt7621 (push) Has been cancelled
Build and test / Publish agent GitHub release assets (push) Has been cancelled
Build and test / Publish signed package repository (push) Has been cancelled
fix: authenticate Pages artifact retrieval
2026-07-31 12:24:45 +03:00

417 lines
17 KiB
YAML

name: Build and test
on:
push:
branches: [main]
tags: ["agent-v*"]
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: build-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/agent-v') }}
jobs:
quality:
name: Tests and license checks
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Validate immutable signed agent release
if: startsWith(github.ref, 'refs/tags/agent-v')
shell: bash
run: |
set -euo pipefail
git cat-file -p "$GITHUB_REF_NAME" | grep -q -- '-----BEGIN PGP SIGNATURE-----'
bash scripts/extract-release-notes.sh \
"$GITHUB_REF_NAME" \
CHANGELOG.md \
release-notes.md
- run: go test ./...
- run: go vet ./...
- name: Install web test dependencies
run: npm ci
- run: npm run check:web
- name: Install Playwright browser
run: npx playwright install --with-deps chromium
- name: Run browser E2E tests
run: npm run test:e2e
- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: |
playwright-report/
test-results/
if-no-files-found: ignore
- name: Verify license copies and package metadata
shell: bash
run: |
set -euo pipefail
cmp LICENSES/AGPL-3.0-only.txt web/licenses/AGPL-3.0-only.txt
for file in \
agent/LICENSE \
agent/package/rmm-agent/LICENSE \
agent/package/rmm-agent-go/LICENSE \
agent/package/rmm-agent-go-production/LICENSE \
agent/package/luci-app-rmm-agent/LICENSE \
web/licenses/MIT.txt; do
cmp LICENSES/MIT.txt "$file"
done
missing_license="$(grep -L 'PKG_LICENSE:=MIT' agent/package/*/Makefile || true)"
missing_file="$(grep -L 'PKG_LICENSE_FILES:=LICENSE' agent/package/*/Makefile || true)"
test -z "$missing_license"
test -z "$missing_file"
for file in \
agent/package/rmm-agent/Makefile \
agent/package/rmm-agent-go/Makefile \
agent/package/rmm-agent-go-production/Makefile; do
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"
mkdir -p "$fixture/repository-input/openwrt-24.10.7-ramips-mt7621"
mkdir "$fixture/repository-input/control"
printf 'Package: rmm-agent-go-production\nVersion: 0.6.8-1\n' > "$fixture/repository-input/control/control"
tar -C "$fixture/repository-input/control" -czf "$fixture/repository-input/control.tar.gz" control
ar r "$fixture/repository-input/openwrt-24.10.7-ramips-mt7621/rmm-agent-go-production_0.6.8-1_mipsel_24kc.ipk" "$fixture/repository-input/control.tar.gz"
bash scripts/prepare-package-repository.sh \
"$fixture/repository-input" \
"$fixture/repository" \
"0.6.8"
node -e '
const fs = require("node:fs");
const manifest = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
if (manifest.agent.version !== "0.6.8" || manifest.packages.length !== 1 || manifest.packages[0].feed_url.includes("/stable/") || manifest.packages[0].package_version !== "0.6.8-1") process.exit(1);
' "$fixture/repository/update-manifest.json"
openwrt-packages:
name: OpenWrt ${{ matrix.release }} · ${{ matrix.label }}
if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/agent-v')
needs: quality
runs-on: ubuntu-24.04
timeout-minutes: 90
strategy:
fail-fast: false
max-parallel: 4
matrix:
include:
# 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: 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: mediatek, subtarget: filogic, label: mediatek-filogic, goarch: arm64 }
steps:
- uses: actions/checkout@v4
- name: Verify agent release version
if: startsWith(github.ref, 'refs/tags/agent-v')
shell: bash
run: |
set -euo pipefail
expected="${GITHUB_REF_NAME#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" = "$expected"
test "$package_version" = "$expected"
- 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"
echo "Using locked SDK ${url}"
- uses: docker/setup-buildx-action@v3
- name: Build agent and LuCI packages
uses: docker/build-push-action@v6
env:
OPENWRT_USIGN_SECRET_B64: ${{ secrets.OPENWRT_USIGN_SECRET_B64 }}
OPENWRT_APK_SECRET_B64: ${{ secrets.OPENWRT_APK_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=${{ startsWith(github.ref, 'refs/tags/agent-v') }}
REPOSITORY_INDEX_REVISION=${{ github.run_id }}
secret-envs: |
openwrt_usign_secret_b64=OPENWRT_USIGN_SECRET_B64
openwrt_apk_secret_b64=OPENWRT_APK_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' -o -name '*.apk' \) -print -quit | grep -q .
if find artifacts -maxdepth 1 -type f -name '*.ipk' -print -quit | grep -q .; then
test -f artifacts/Packages
test -f artifacts/Packages.gz
if [[ "$GITHUB_REF" == refs/tags/agent-v* ]]; then
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"
fi
else
test -f artifacts/packages.adb
if [[ "$GITHUB_REF" == refs/tags/agent-v* ]]; then
test -f artifacts/rmm-openwrt.pem
fi
fi
- uses: actions/upload-artifact@v4
with:
name: openwrt-${{ matrix.release }}-${{ matrix.label }}
path: artifacts/*
if-no-files-found: error
retention-days: 90
release:
name: Publish agent GitHub release assets
if: startsWith(github.ref, 'refs/tags/agent-v')
needs: openwrt-packages
runs-on: ubuntu-24.04
permissions:
contents: write
pages: read
id-token: write
attestations: write
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
pattern: openwrt-*
path: release-assets
- name: Prepare release assets
shell: bash
run: |
set -euo pipefail
mkdir publish
while IFS= read -r file; do
artifact="$(basename "$(dirname "$file")")"
cp "$file" "publish/${artifact}-$(basename "$file")"
done < <(find release-assets -type f \( -name '*.ipk' -o -name '*.apk' \) -print | sort)
find publish -type f -print -quit | grep -q .
- name: Prepare release notes
shell: bash
run: |
set -euo pipefail
bash scripts/extract-release-notes.sh \
"$GITHUB_REF_NAME" \
CHANGELOG.md \
release-notes.md
- name: Prepare signed package repository
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
version="${GITHUB_REF_NAME#agent-v}"
artifact_url="$(gh api "repos/${GITHUB_REPOSITORY}/pages/deployments?per_page=1" --jq '.[0].artifact_url // empty' || true)"
if [ -n "$artifact_url" ]; then
mkdir previous-pages
curl --fail --location --retry 3 \
--header "Authorization: Bearer ${GH_TOKEN}" \
"$artifact_url" -o previous-pages.zip
unzip -q previous-pages.zip -d previous-pages
elif gh api "repos/${GITHUB_REPOSITORY}/pages/deployments?per_page=1" --jq 'length' | grep -qx '0'; then
echo "No prior Pages deployment; publishing the first retained feed."
else
echo "Unable to retrieve the deployed Pages artifact; refusing to discard retained feeds." >&2
exit 1
fi
bash scripts/prepare-package-repository.sh \
release-assets \
package-repository \
"$version"
if [ -d previous-pages ]; then
cp -a previous-pages/. package-repository/
# Reapply the new release after retaining every prior immutable feed.
bash scripts/prepare-package-repository.sh release-assets package-repository-new "$version"
cp -a package-repository-new/. package-repository/
fi
mkdir -p "package-repository/manifests/$version"
cp package-repository/update-manifest.json "package-repository/manifests/$version/update-manifest.json"
- name: Sign stable update manifest with the package key
env:
OPENWRT_APK_SECRET_B64: ${{ secrets.OPENWRT_APK_SECRET_B64 }}
shell: bash
run: |
set -euo pipefail
test -n "$OPENWRT_APK_SECRET_B64"
umask 077
trap 'rm -f manifest-signing.pem' EXIT
printf '%s' "$OPENWRT_APK_SECRET_B64" | base64 --decode > manifest-signing.pem
openssl dgst -sha256 \
-sign manifest-signing.pem \
-out package-repository/update-manifest.sig \
package-repository/update-manifest.json
openssl dgst -sha256 \
-verify keys/openwrt/apk/rmm-openwrt.pem \
-signature package-repository/update-manifest.sig \
package-repository/update-manifest.json
cp package-repository/update-manifest.sig "package-repository/manifests/${GITHUB_REF_NAME#agent-v}/update-manifest.sig"
rm -f manifest-signing.pem
trap - EXIT
- uses: sigstore/cosign-installer@v3
- name: Sign and verify stable update manifest
shell: bash
run: |
set -euo pipefail
cosign sign-blob --yes \
--bundle package-repository/update-manifest.sigstore.json \
package-repository/update-manifest.json
cosign verify-blob \
--bundle package-repository/update-manifest.sigstore.json \
--certificate-identity-regexp "^https://github.com/${GITHUB_REPOSITORY}/\\.github/workflows/build\\.yml@refs/tags/agent-v" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
package-repository/update-manifest.json
cp package-repository/update-manifest.sigstore.json "package-repository/manifests/${GITHUB_REF_NAME#agent-v}/update-manifest.sigstore.json"
- uses: actions/upload-artifact@v4
with:
name: package-repository-site
path: package-repository
if-no-files-found: error
retention-days: 7
- name: Attest agent release assets
uses: actions/attest-build-provenance@v2
with:
subject-path: publish/*
- name: Attest stable update manifest
uses: actions/attest-build-provenance@v2
with:
subject-path: package-repository/update-manifest.json
- name: Create release and upload packages
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
mapfile -t files < <(find publish -type f -print | sort)
if gh release view "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then
echo "release already exists; published agent tags are immutable" >&2
exit 1
fi
gh release create "${GITHUB_REF_NAME}" "${files[@]}" \
--repo "${GITHUB_REPOSITORY}" \
--notes-file release-notes.md \
--title "OpenWrt RMM Agent ${GITHUB_REF_NAME#agent-v}"
package-repository:
name: Publish signed package repository
if: startsWith(github.ref, 'refs/tags/agent-v')
needs: release
runs-on: ubuntu-24.04
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/download-artifact@v4
with:
name: package-repository-site
path: package-repository
- uses: actions/upload-pages-artifact@v3
with:
path: package-repository
- name: Deploy package repository to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4