From 4750c4c3059a7c5959ce116d63b1d1f994829076 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Tue, 20 Oct 2026 22:59:17 +0300 Subject: [PATCH 1/2] Share one package version across the tools The ExpressLRS and Bind tools each hard-coded their own "r" version. Both now load SCRIPTS/ELRS/version.lua, which is "dev" in the source tree. Release builds will write the semver version (or a short commit hash for nightly and PR builds) into that file. --- src/SCRIPTS/ELRS/version.lua | 7 +++++++ src/SCRIPTS/TOOLS/ExpressLRS/main.lua | 3 ++- src/SCRIPTS/TOOLS/ExpressLRSBind/main.lua | 3 ++- 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 src/SCRIPTS/ELRS/version.lua diff --git a/src/SCRIPTS/ELRS/version.lua b/src/SCRIPTS/ELRS/version.lua new file mode 100644 index 0000000..993639e --- /dev/null +++ b/src/SCRIPTS/ELRS/version.lua @@ -0,0 +1,7 @@ +--------------------------------------------------------------------------- +-- Package Version -- +-- "dev" in the source tree; release builds write the semver version -- +-- (or a short commit hash) here. -- +--------------------------------------------------------------------------- + +return "dev" diff --git a/src/SCRIPTS/TOOLS/ExpressLRS/main.lua b/src/SCRIPTS/TOOLS/ExpressLRS/main.lua index 2689f63..93795cf 100644 --- a/src/SCRIPTS/TOOLS/ExpressLRS/main.lua +++ b/src/SCRIPTS/TOOLS/ExpressLRS/main.lua @@ -8,12 +8,13 @@ ---- # Unified tool for BW and color LCD radios (EdgeTX 2.11.6+/2.12.1+) # ---- ######################################################################### -local VERSION = "r3" local useLvgl = (lvgl ~= nil) ---@diagnostic disable-next-line: need-check-nil local loader = loadScript("/SCRIPTS/ELRS/loader.lua")() +local VERSION = loader("/SCRIPTS/ELRS/version.lua") + local crsf = loader("/SCRIPTS/ELRS/crsf.lua") local params = loader("/SCRIPTS/ELRS/crsf_params.lua", crsf) local CRSFSession = loader("/SCRIPTS/ELRS/crsf_session.lua", crsf, params) diff --git a/src/SCRIPTS/TOOLS/ExpressLRSBind/main.lua b/src/SCRIPTS/TOOLS/ExpressLRSBind/main.lua index d831bc1..c9246af 100644 --- a/src/SCRIPTS/TOOLS/ExpressLRSBind/main.lua +++ b/src/SCRIPTS/TOOLS/ExpressLRSBind/main.lua @@ -4,12 +4,13 @@ ---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html # ---- ######################################################################### -local VERSION = "r1" local useLvgl = (lvgl ~= nil) ---@diagnostic disable-next-line: need-check-nil local loader = loadScript("/SCRIPTS/ELRS/loader.lua")() +local VERSION = loader("/SCRIPTS/ELRS/version.lua") + local crsf = loader("/SCRIPTS/ELRS/crsf.lua") local msp = loader("/SCRIPTS/ELRS/msp.lua", crsf) local defer = loader("/SCRIPTS/ELRS/defer.lua") From 5070cdf455a57736ea82148b7fc1c0b14fa9cb79 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Tue, 20 Oct 2026 23:41:28 +0300 Subject: [PATCH 2/2] Build, nightly and release workflows make build stages src into dist/, writes the version into SCRIPTS/ELRS/version.lua, compiles every script with the standalone edgetx-luac and zips the SD card tree. Tools are pinned and downloaded into bin/; stylua no longer needs cargo. CI runs checks and the build on every PR and master push. Builds show the 7-character commit hash; master pushes replace the nightly prerelease. Pushing a vX.Y.Z tag publishes a release showing X.Y.Z. A workflow_run job comments the build link on PRs, including forks. --- .github/workflows/ci.yml | 73 ++++++++++++++++++++++++++++---- .github/workflows/pr-comment.yml | 60 ++++++++++++++++++++++++++ .github/workflows/release.yml | 51 ++++++++++++++++++++++ .gitignore | 1 + .luarc.json | 2 +- Makefile | 52 +++++++++++++++++++---- 6 files changed, 221 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/pr-comment.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5833982..03195b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,23 +7,78 @@ on: branches: [ master ] workflow_dispatch: +permissions: + contents: read + jobs: - check: - name: Lint and Typecheck - runs-on: ubuntu-24.04 + build: + name: Check and Build + runs-on: ubuntu-26.04 steps: - uses: actions/checkout@v6 with: submodules: true - - name: Cache cargo - uses: actions/cache@v5 - with: - path: ~/.cargo - key: cargo-stylua-${{ runner.os }} - - name: Install tools run: make install-tools - name: Run checks run: make check + + - name: Set version + env: + EVENT: ${{ github.event_name }} + PR: ${{ github.event.pull_request.number }} + # On pull_request, github.sha is GitHub's temporary merge commit. + SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: | + version="${SHA::7}" + if [ "$EVENT" = pull_request ]; then + zip_name="expresslrs-lua-pr${PR}-${version}" + elif [ "$GITHUB_REF" = refs/heads/master ]; then + zip_name=expresslrs-lua-nightly + else + zip_name="expresslrs-lua-${GITHUB_REF_NAME//\//-}-${version}" + fi + echo "VERSION=$version" >> "$GITHUB_ENV" + echo "ZIP_NAME=$zip_name" >> "$GITHUB_ENV" + + - name: Build + run: make build VERSION="$VERSION" ZIP_NAME="$ZIP_NAME" + + - uses: actions/upload-artifact@v4 + with: + name: ${{ env.ZIP_NAME }} + path: dist/*.zip + if-no-files-found: error + + nightly: + name: Publish Nightly + needs: build + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + runs-on: ubuntu-26.04 + permissions: + contents: write + concurrency: + group: nightly + cancel-in-progress: true + steps: + - uses: actions/download-artifact@v4 + with: + name: expresslrs-lua-nightly + path: dist + + - name: Publish release + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release delete nightly --repo "$GITHUB_REPOSITORY" --cleanup-tag --yes || true + gh release create nightly dist/*.zip \ + --repo "$GITHUB_REPOSITORY" \ + --target "$GITHUB_SHA" \ + --title "Nightly (development build)" \ + --prerelease \ + --latest=false \ + --notes "**Development build, not a stable release.** Built automatically from the latest \`master\` commit ($GITHUB_SHA). It may contain unfinished or untested changes; for normal use, install the [latest release](https://github.com/$GITHUB_REPOSITORY/releases/latest). + + The tools show version \`${GITHUB_SHA::7}\`. To install, extract the zip onto the SD card root." diff --git a/.github/workflows/pr-comment.yml b/.github/workflows/pr-comment.yml new file mode 100644 index 0000000..99c959e --- /dev/null +++ b/.github/workflows/pr-comment.yml @@ -0,0 +1,60 @@ +name: PR Build Comment + +# Runs in the base repository's context so it can comment on fork PRs. +# It never checks out or runs PR code. +on: + workflow_run: + workflows: [ CI ] + types: [ completed ] + +permissions: + actions: read + pull-requests: write + +jobs: + comment: + if: github.event.workflow_run.event == 'pull_request' + runs-on: ubuntu-26.04 + steps: + - name: Post build comment + env: + GH_TOKEN: ${{ github.token }} + SHA: ${{ github.event.workflow_run.head_sha }} + RUN_ID: ${{ github.event.workflow_run.id }} + RUN_URL: ${{ github.event.workflow_run.html_url }} + CONCLUSION: ${{ github.event.workflow_run.conclusion }} + HEAD_REPO: ${{ github.event.workflow_run.head_repository.full_name }} + HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }} + run: | + marker='' + + pr=$(gh pr list --repo "$GITHUB_REPOSITORY" --state open --limit 200 \ + --json number,headRefOid --jq ".[] | select(.headRefOid == \"$SHA\") | .number" | head -n 1) + if [ -z "$pr" ]; then + echo "No open PR for $SHA" + exit 0 + fi + + if [ "$CONCLUSION" = success ]; then + artifact=$(gh api "repos/$GITHUB_REPOSITORY/actions/runs/$RUN_ID/artifacts" \ + --jq '.artifacts[] | select(.name | startswith("expresslrs-lua-")) | "\(.id) \(.name)"' | head -n 1) + read -r artifact_id artifact_name <<< "$artifact" + body="$marker + ### Test build for ${SHA::7} + + - Download: [$artifact_name.zip](https://github.com/$GITHUB_REPOSITORY/actions/runs/$RUN_ID/artifacts/$artifact_id) (requires a GitHub login; extract it, then extract the inner zip onto the SD card root). The tools show version \`${SHA::7}\`. + - Or install from source with: \`edgetx-cli pkg install $HEAD_REPO@$HEAD_BRANCH\` (shows version \`dev\`)" + else + body="$marker + ### Build failed for ${SHA::7} + + See the [CI run]($RUN_URL)." + fi + + comment_id=$(gh api --paginate "repos/$GITHUB_REPOSITORY/issues/$pr/comments" \ + --jq ".[] | select(.body | startswith(\"$marker\")) | .id" | head -n 1) + if [ -n "$comment_id" ]; then + gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$comment_id" -f body="$body" > /dev/null + else + gh pr comment "$pr" --repo "$GITHUB_REPOSITORY" --body "$body" + fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..19681a7 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,51 @@ +name: Release + +on: + push: + tags: [ 'v[0-9]*' ] + +permissions: + contents: write + +jobs: + release: + name: Build and Release + runs-on: ubuntu-26.04 + steps: + - name: Check tag + run: | + # No pre-release suffixes: "1.1.0-rc.1" does not fit the B&W EXIT labels. + if ! [[ "$GITHUB_REF_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "::error::Release tags must be vMAJOR.MINOR.PATCH, got $GITHUB_REF_NAME" + exit 1 + fi + + - uses: actions/checkout@v6 + with: + submodules: true + fetch-depth: 0 + + - name: Install tools + run: make install-tools + + - name: Run checks + run: make check + + - name: Build + run: make build VERSION="${GITHUB_REF_NAME#v}" ZIP_NAME="expresslrs-lua-$GITHUB_REF_NAME" + + - name: Publish release + env: + GH_TOKEN: ${{ github.token }} + run: | + # Without an explicit start tag GitHub may diff against the nightly prerelease. + args=() + if prev=$(git describe --tags --abbrev=0 --match 'v[0-9]*' "$GITHUB_REF_NAME^" 2>/dev/null); then + args+=(--notes-start-tag "$prev") + fi + gh release create "$GITHUB_REF_NAME" dist/*.zip \ + --repo "$GITHUB_REPOSITORY" \ + --title "$GITHUB_REF_NAME" \ + --generate-notes \ + --latest \ + "${args[@]}" diff --git a/.gitignore b/.gitignore index 531c909..16ab8be 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ src/SCRIPTS/TOOLS/ExpressLRSBind/history.txt bin/ .claude/plans/ TODO.md +dist/ diff --git a/.luarc.json b/.luarc.json index f6b2728..e58f2be 100644 --- a/.luarc.json +++ b/.luarc.json @@ -10,7 +10,7 @@ "bit32": "disable" }, "workspace.library": ["edgetx-lua-stdlib"], - "workspace.ignoreDir": ["bin", "edgetx-lua-stdlib", "legacy"], + "workspace.ignoreDir": ["bin", "dist", "edgetx-lua-stdlib", "legacy"], "workspace.checkThirdParty": false, "diagnostics.disable": ["lowercase-global"] } diff --git a/Makefile b/Makefile index b7f3d9a..fe37f73 100644 --- a/Makefile +++ b/Makefile @@ -1,46 +1,82 @@ SRC_DIR := src +DIST_DIR := dist +STAGE_DIR := $(DIST_DIR)/sdcard +# Shown in the tools: at most 8 characters fit the B&W EXIT labels. +VERSION ?= $(or $(shell git rev-parse HEAD 2>/dev/null | cut -c1-7),dev) +ZIP_NAME ?= expresslrs-lua-$(VERSION) + +STYLUA_VERSION := 2.3.1 +STYLUA := bin/stylua +STYLUA_URL := https://github.com/JohnnyMorganz/StyLua/releases/download/v$(STYLUA_VERSION)/stylua-linux-x86_64.zip LUALS_VERSION := 3.17.1 LUALS_DIR := bin/lua-language-server LUALS := $(LUALS_DIR)/bin/lua-language-server LUALS_URL := https://github.com/LuaLS/lua-language-server/releases/download/$(LUALS_VERSION)/lua-language-server-$(LUALS_VERSION)-linux-x64.tar.gz -.PHONY: help install-tools install-stylua install-luals format format-check typecheck check sync push +LUAC_VERSION := 1.0.0 +LUAC_DIR := bin/edgetx-luac +LUAC := $(LUAC_DIR)/edgetx-luac +LUAC_URL := https://github.com/EdgeTX/edgetx-luac-action/releases/download/v$(LUAC_VERSION)/edgetx-luac-v$(LUAC_VERSION)-linux-x64.tar.gz + +.PHONY: help install-tools install-stylua install-luals install-luac format format-check typecheck check build sync push help: @echo "Usage: make " @echo "" - @echo " install-tools Install stylua and lua-language-server" - @echo " install-stylua Install stylua (via cargo)" + @echo " install-tools Install stylua, lua-language-server and edgetx-luac" + @echo " install-stylua Install stylua" @echo " install-luals Install lua-language-server" + @echo " install-luac Install the EdgeTX Lua compiler" @echo " format Format Lua files with stylua" @echo " format-check Check formatting without modifying files" @echo " typecheck Run lua-language-server type checking" @echo " check Run format-check and typecheck" + @echo " build Build an SD card zip with compiled bytecode (VERSION=, ZIP_NAME=)" @echo " sync Sync source files to EdgeTX simulator SD card" @echo " push Install package to EdgeTX radio and eject" install-stylua: - @command -v cargo >/dev/null 2>&1 || { echo "cargo is required (install Rust: https://rustup.rs)"; exit 1; } - cargo install stylua --features lua53 + mkdir -p bin + curl -fSL -o bin/stylua.zip $(STYLUA_URL) + unzip -o -q bin/stylua.zip -d bin + rm bin/stylua.zip + chmod +x $(STYLUA) install-luals: mkdir -p $(LUALS_DIR) curl -fSL $(LUALS_URL) | tar xz -C $(LUALS_DIR) -install-tools: install-stylua install-luals +install-luac: + mkdir -p $(LUAC_DIR) + curl -fSL $(LUAC_URL) | tar xz -C $(LUAC_DIR) + +install-tools: install-stylua install-luals install-luac format: - stylua $(SRC_DIR) + $(STYLUA) $(SRC_DIR) format-check: - stylua --check $(SRC_DIR) + $(STYLUA) --check $(SRC_DIR) typecheck: $(LUALS) --check . check: format-check typecheck +# Keep in sync with the dev libraries and excludes in edgetx.yml. +build: + rm -rf $(DIST_DIR) + mkdir -p $(STAGE_DIR) + cp -r $(SRC_DIR)/SCRIPTS $(SRC_DIR)/WIDGETS $(STAGE_DIR)/ + rm -rf $(STAGE_DIR)/SCRIPTS/CRSFSimulator + rm -f $(STAGE_DIR)/SCRIPTS/TOOLS/ExpressLRSBind/history.txt $(STAGE_DIR)/WIDGETS/ELRSVTXAdmin/presets.txt + find $(STAGE_DIR) -name '*.luac' -delete + printf 'return "%s"\n' '$(VERSION)' > $(STAGE_DIR)/SCRIPTS/ELRS/version.lua + find $(STAGE_DIR) -name '*.lua' | while read -r f; do $(LUAC) -s -o "$${f}c" "$$f" || exit 1; done + cd $(STAGE_DIR) && zip -q -r ../$(ZIP_NAME).zip SCRIPTS WIDGETS + @echo "Built $(DIST_DIR)/$(ZIP_NAME).zip (version $(VERSION))" + sync: edgetx-cli dev sync ../edgetx-sdcard