400 lines
11 KiB
YAML
400 lines
11 KiB
YAML
name: Build and Release OpenWrt packages
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
tags:
|
|
- "v*"
|
|
pull_request:
|
|
workflow_dispatch:
|
|
|
|
# Gitea's built-in GITEA_TOKEN needs release write permission for tag releases.
|
|
permissions:
|
|
contents: read
|
|
actions: read
|
|
releases: write
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- openwrt: "25.12.0"
|
|
format: "apk"
|
|
sdk_file: "openwrt-sdk-25.12.0-x86-64_gcc-14.3.0_musl.Linux-x86_64.tar.zst"
|
|
sdk_sha256: "9f371906ce6d2f95418f69fac06df58bf9df0ffc4abed6206c30f1b547fcfc12"
|
|
sdk_url: "https://downloads.openwrt.org/releases/25.12.0/targets/x86/64/openwrt-sdk-25.12.0-x86-64_gcc-14.3.0_musl.Linux-x86_64.tar.zst"
|
|
|
|
- openwrt: "24.10.8"
|
|
format: "ipk"
|
|
sdk_file: "openwrt-sdk-24.10.8-x86-64_gcc-13.3.0_musl.Linux-x86_64.tar.zst"
|
|
sdk_sha256: "ac4a0405d2eea821b06f93c14ba13ffa90ad0457648903df7dde02570027ab21"
|
|
sdk_url: "https://downloads.openwrt.org/releases/24.10.8/targets/x86/64/openwrt-sdk-24.10.8-x86-64_gcc-13.3.0_musl.Linux-x86_64.tar.zst"
|
|
|
|
- openwrt: "23.05.5"
|
|
format: "ipk"
|
|
sdk_file: "openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64.tar.xz"
|
|
sdk_sha256: "9e5b6813a3f810123391f5f3ea10dfd1c14be1a880f410c70ec4424bbd1573d2"
|
|
sdk_url: "https://downloads.openwrt.org/releases/23.05.5/targets/x86/64/openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64.tar.xz"
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Resolve package version
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
|
|
VERSION="${GITHUB_REF_NAME#v}"
|
|
else
|
|
VERSION="$(sed -n 's/^PKG_VERSION:=//p' Makefile | head -n1)"
|
|
fi
|
|
|
|
if [ -z "${VERSION}" ]; then
|
|
echo "Unable to resolve package version"
|
|
exit 1
|
|
fi
|
|
|
|
echo "PACKAGE_VERSION=${VERSION}" >> "${GITHUB_ENV}"
|
|
echo "Package version: ${VERSION}"
|
|
|
|
- name: Install build dependencies
|
|
shell: bash
|
|
run: |
|
|
set -eux
|
|
|
|
if command -v sudo >/dev/null 2>&1; then
|
|
SUDO=sudo
|
|
else
|
|
SUDO=
|
|
fi
|
|
|
|
${SUDO} apt-get update
|
|
${SUDO} env DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
|
build-essential \
|
|
clang \
|
|
flex \
|
|
bison \
|
|
gawk \
|
|
gettext \
|
|
git \
|
|
libncurses-dev \
|
|
libssl-dev \
|
|
python3 \
|
|
python3-pyelftools \
|
|
python3-setuptools \
|
|
rsync \
|
|
swig \
|
|
unzip \
|
|
wget \
|
|
xz-utils \
|
|
zlib1g-dev \
|
|
zstd
|
|
|
|
- name: Download and verify OpenWrt SDK
|
|
shell: bash
|
|
run: |
|
|
set -eux
|
|
|
|
wget -O "${{ matrix.sdk_file }}" "${{ matrix.sdk_url }}"
|
|
echo "${{ matrix.sdk_sha256 }} ${{ matrix.sdk_file }}" | sha256sum -c -
|
|
|
|
- name: Extract OpenWrt SDK
|
|
shell: bash
|
|
run: |
|
|
set -eux
|
|
|
|
rm -rf _sdk
|
|
mkdir -p _sdk
|
|
|
|
case "${{ matrix.sdk_file }}" in
|
|
*.tar.zst)
|
|
tar --zstd -xf "${{ matrix.sdk_file }}" \
|
|
--strip-components=1 \
|
|
-C _sdk
|
|
;;
|
|
*.tar.xz)
|
|
tar -xJf "${{ matrix.sdk_file }}" \
|
|
--strip-components=1 \
|
|
-C _sdk
|
|
;;
|
|
*)
|
|
echo "Unsupported SDK archive: ${{ matrix.sdk_file }}"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
- name: Prepare LuCI feed
|
|
shell: bash
|
|
working-directory: _sdk
|
|
run: |
|
|
set -eux
|
|
|
|
./scripts/feeds update luci
|
|
./scripts/feeds install luci-base
|
|
|
|
- name: Add package source
|
|
shell: bash
|
|
run: |
|
|
set -eux
|
|
|
|
PKG="_sdk/package/luci-app-led-scheduler"
|
|
|
|
rm -rf "${PKG}"
|
|
mkdir -p "${PKG}"
|
|
|
|
cp -a \
|
|
Makefile \
|
|
LICENSE \
|
|
README.md \
|
|
htdocs \
|
|
root \
|
|
"${PKG}/"
|
|
|
|
# For tag builds, package version follows the Git tag.
|
|
# Example: v0.3.1 -> PKG_VERSION:=0.3.1
|
|
sed -i \
|
|
"s/^PKG_VERSION:=.*/PKG_VERSION:=${PACKAGE_VERSION}/" \
|
|
"${PKG}/Makefile"
|
|
|
|
grep '^PKG_VERSION:=' "${PKG}/Makefile"
|
|
|
|
- name: Configure SDK
|
|
shell: bash
|
|
working-directory: _sdk
|
|
run: |
|
|
set -eux
|
|
make defconfig
|
|
|
|
- name: Build package
|
|
shell: bash
|
|
working-directory: _sdk
|
|
run: |
|
|
set -eux
|
|
|
|
make -j"$(nproc)" \
|
|
package/luci-app-led-scheduler/compile \
|
|
V=s
|
|
|
|
- name: Collect package
|
|
shell: bash
|
|
run: |
|
|
set -eux
|
|
|
|
mkdir -p artifacts
|
|
|
|
FILE="$(find _sdk/bin -type f \
|
|
-name "luci-app-led-scheduler*.${{ matrix.format }}" \
|
|
-print -quit)"
|
|
|
|
if [ -z "${FILE}" ]; then
|
|
echo "No ${{ matrix.format }} package found"
|
|
exit 1
|
|
fi
|
|
|
|
ORIGINAL_NAME="$(basename "${FILE}")"
|
|
RELEASE_NAME="openwrt-${{ matrix.openwrt }}-${ORIGINAL_NAME}"
|
|
|
|
cp -v "${FILE}" "artifacts/${RELEASE_NAME}"
|
|
|
|
echo "Built package:"
|
|
ls -lh artifacts/
|
|
|
|
- name: Upload Actions artifact
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: luci-app-led-scheduler-openwrt-${{ matrix.openwrt }}-${{ matrix.format }}
|
|
path: artifacts/*
|
|
if-no-files-found: error
|
|
|
|
release:
|
|
# Ordinary pushes only build artifacts.
|
|
# A tag such as v0.3.1 additionally creates/updates a Gitea Release.
|
|
if: ${{ gitea.ref_type == 'tag' }}
|
|
needs:
|
|
- build
|
|
|
|
runs-on: ubuntu-latest
|
|
|
|
permissions:
|
|
contents: read
|
|
actions: read
|
|
releases: write
|
|
|
|
steps:
|
|
- name: Download all build artifacts
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
pattern: luci-app-led-scheduler-openwrt-*
|
|
path: release-assets
|
|
merge-multiple: true
|
|
|
|
- name: Show release files
|
|
shell: bash
|
|
run: |
|
|
set -eux
|
|
find release-assets -maxdepth 1 -type f -print
|
|
test -n "$(find release-assets -maxdepth 1 -type f -print -quit)"
|
|
|
|
- name: Create or update Gitea Release
|
|
id: release
|
|
shell: bash
|
|
env:
|
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
TAG="${GITHUB_REF_NAME}"
|
|
VERSION="${TAG#v}"
|
|
API="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}"
|
|
|
|
BODY="$(cat <<EOF
|
|
luci-app-led-scheduler ${VERSION}
|
|
|
|
OpenWrt compatibility:
|
|
|
|
- OpenWrt 25.12.0: APK
|
|
- OpenWrt 24.10.8: IPK
|
|
- OpenWrt 23.05.5: IPK
|
|
- Package architecture: all
|
|
|
|
The package version is derived from Git tag \`${TAG}\`.
|
|
EOF
|
|
)"
|
|
|
|
export TAG VERSION BODY
|
|
|
|
PAYLOAD="$(python3 - <<'PY'
|
|
import json
|
|
import os
|
|
|
|
print(json.dumps({
|
|
"tag_name": os.environ["TAG"],
|
|
"name": f"luci-app-led-scheduler {os.environ['VERSION']}",
|
|
"body": os.environ["BODY"],
|
|
"draft": False,
|
|
"prerelease": False,
|
|
}))
|
|
PY
|
|
)"
|
|
|
|
# Make reruns of the same tag safe:
|
|
# reuse the release if it already exists, otherwise create it.
|
|
HTTP_CODE="$(curl -sS \
|
|
-o /tmp/existing-release.json \
|
|
-w '%{http_code}' \
|
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
"${API}/releases/tags/${TAG}")"
|
|
|
|
if [ "${HTTP_CODE}" = "200" ]; then
|
|
RELEASE_ID="$(python3 - <<'PY'
|
|
import json
|
|
with open("/tmp/existing-release.json", "r", encoding="utf-8") as f:
|
|
print(json.load(f)["id"])
|
|
PY
|
|
)"
|
|
|
|
curl -fsS \
|
|
-X PATCH \
|
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
-H "Content-Type: application/json" \
|
|
-d "${PAYLOAD}" \
|
|
"${API}/releases/${RELEASE_ID}" \
|
|
>/tmp/release.json
|
|
elif [ "${HTTP_CODE}" = "404" ]; then
|
|
curl -fsS \
|
|
-X POST \
|
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
-H "Content-Type: application/json" \
|
|
-d "${PAYLOAD}" \
|
|
"${API}/releases" \
|
|
>/tmp/release.json
|
|
|
|
RELEASE_ID="$(python3 - <<'PY'
|
|
import json
|
|
with open("/tmp/release.json", "r", encoding="utf-8") as f:
|
|
print(json.load(f)["id"])
|
|
PY
|
|
)"
|
|
else
|
|
echo "Failed to query existing release, HTTP ${HTTP_CODE}"
|
|
cat /tmp/existing-release.json || true
|
|
exit 1
|
|
fi
|
|
|
|
echo "Release ID: ${RELEASE_ID}"
|
|
echo "RELEASE_ID=${RELEASE_ID}" >> "${GITHUB_ENV}"
|
|
|
|
- name: Remove duplicate release attachments
|
|
shell: bash
|
|
env:
|
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
API="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}"
|
|
|
|
curl -fsS \
|
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
"${API}/releases/${RELEASE_ID}/assets" \
|
|
>/tmp/assets.json
|
|
|
|
python3 - <<'PY' >/tmp/delete-assets.txt
|
|
import json
|
|
from pathlib import Path
|
|
|
|
wanted = {p.name for p in Path("release-assets").iterdir() if p.is_file()}
|
|
|
|
with open("/tmp/assets.json", "r", encoding="utf-8") as f:
|
|
assets = json.load(f)
|
|
|
|
for asset in assets:
|
|
if asset.get("name") in wanted:
|
|
print(asset["id"])
|
|
PY
|
|
|
|
while IFS= read -r ASSET_ID; do
|
|
[ -n "${ASSET_ID}" ] || continue
|
|
|
|
echo "Removing existing attachment ID ${ASSET_ID}"
|
|
|
|
curl -fsS \
|
|
-X DELETE \
|
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
"${API}/releases/${RELEASE_ID}/assets/${ASSET_ID}" \
|
|
>/dev/null
|
|
done </tmp/delete-assets.txt
|
|
|
|
- name: Upload packages to Gitea Release
|
|
shell: bash
|
|
env:
|
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
API="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}"
|
|
|
|
for FILE in release-assets/*; do
|
|
[ -f "${FILE}" ] || continue
|
|
|
|
NAME="$(basename "${FILE}")"
|
|
echo "Uploading ${NAME}"
|
|
|
|
# Multipart upload is compatible with Gitea release attachments.
|
|
curl -fsS \
|
|
-X POST \
|
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
-F "attachment=@${FILE}" \
|
|
"${API}/releases/${RELEASE_ID}/assets?name=${NAME}" \
|
|
>/dev/null
|
|
done
|
|
|
|
echo "Release assets uploaded:"
|
|
find release-assets -maxdepth 1 -type f -printf ' %f\n'
|