DEVICE-002: ARM machine-ID fallback — device-tree model + /etc/machine-id combo, then /proc/cpuinfo Serial (all-zero rejected), before the weak hostname fallback. Hardware-bound IDs on DMI-less devices. DEVICE-001: agent detects device_type (server/desktop/phone/tablet) from /sys signals — system battery (scope=Device peripherals excluded, UPS excluded), DRM connector state, framebuffer min-dimension for phone/tablet split. Reports device_type/device_model/os_distro in registration and system-info paths. SERVER-001: migration 061 — device_type, device_type_manual (operator override, never agent-written), device_model, os_distro on agents. effective_device_type computed into every serialized agent. SERVER-002: PUT /admin/agents/:id/device-type — set/clear override, enum-validated, journaled. WEB-001: device-type icons + fleet filter, device model in list, detail header badge with reclassify dropdown, os_distro surfaced. INSTALL-003: arm64 install path unblocked — helper (required manifest component) now cross-built aarch64-unknown-linux-musl via rust-lld in the server image, signed at boot (helperArches += arm64), listed in the release manifest. Install template already handled uname -m and pacman. Plus in-flight: desktop tray wiring, enrollment page polish, CI workflow updates, RAF session-broker/pacman-scanner docs, native installer scaffold.
216 lines
9.7 KiB
YAML
216 lines
9.7 KiB
YAML
name: nightly
|
|
# Rolling alpha channel. Every night, if CI is green on public HEAD and there
|
|
# are new commits since the last nightly, build lean artifacts and replace the
|
|
# `nightly` prerelease on Gitea and Codeberg. Versioned releases stay on
|
|
# release.yml — this channel never tags v*, never ships a manifest, and is
|
|
# therefore invisible to fleet self-upgrade. Manual installs only.
|
|
on:
|
|
schedule:
|
|
- cron: "0 9 * * *"
|
|
workflow_dispatch: {}
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
jobs:
|
|
nightly:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
with:
|
|
ref: public
|
|
fetch-depth: 0
|
|
|
|
- name: Preflight — CI green, new commits since last nightly
|
|
id: pre
|
|
run: |
|
|
set -euo pipefail
|
|
SHA=$(git rev-parse HEAD)
|
|
SHORT=$(git rev-parse --short HEAD)
|
|
API="${GITHUB_SERVER_URL}/api/v1"
|
|
|
|
STATE=$(curl -sf -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
|
"$API/repos/${GITHUB_REPOSITORY}/commits/$SHA/status" \
|
|
| python3 -c "import json,sys; print(json.load(sys.stdin).get('state',''))" || echo "")
|
|
echo "HEAD=$SHA ci_state=$STATE"
|
|
if [ "$STATE" != "success" ]; then
|
|
echo "[INFO] [nightly] CI not green on public HEAD (state=$STATE) — no build tonight"
|
|
echo "go=false" >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
|
|
PREV=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
|
"$API/repos/${GITHUB_REPOSITORY}/releases/tags/nightly" \
|
|
| python3 -c "
|
|
import json,sys
|
|
try:
|
|
r = json.load(sys.stdin)
|
|
sha = r.get('target_commitish','')
|
|
print(sha if len(sha) == 40 else '')
|
|
except Exception:
|
|
print('')")
|
|
if [ "$PREV" = "$SHA" ]; then
|
|
echo "[INFO] [nightly] public HEAD unchanged since last nightly — nothing to build"
|
|
echo "go=false" >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
|
|
BASE=$(grep -P '^\s*AgentVersion\s*=' server/internal/version/versions.go | grep -oP '"\K[^"]+')
|
|
VERSION="${BASE}-nightly.$(date -u +%Y%m%d).${SHORT}"
|
|
{
|
|
echo "go=true"
|
|
echo "sha=$SHA"
|
|
echo "short=$SHORT"
|
|
echo "prev=$PREV"
|
|
echo "version=$VERSION"
|
|
} >> "$GITHUB_OUTPUT"
|
|
echo "Building nightly $VERSION (prev nightly: ${PREV:-none})"
|
|
|
|
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
|
|
if: steps.pre.outputs.go == 'true'
|
|
with:
|
|
go-version-file: agent/go.mod
|
|
cache: true
|
|
|
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
|
if: steps.pre.outputs.go == 'true'
|
|
with:
|
|
node-version: 20
|
|
cache: npm
|
|
cache-dependency-path: web/package-lock.json
|
|
|
|
- uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
|
|
if: steps.pre.outputs.go == 'true'
|
|
|
|
- name: Build web UI and stage embed
|
|
if: steps.pre.outputs.go == 'true'
|
|
run: |
|
|
set -euo pipefail
|
|
cd web && npm ci && npm run build && cd ..
|
|
rm -rf server/internal/webui/dist
|
|
cp -r web/dist server/internal/webui/dist
|
|
test -s server/internal/webui/dist/index.html
|
|
|
|
# Agent + helper only. The server is NOT shipped on the nightly channel:
|
|
# its supported install paths are docker-compose-from-source today and the
|
|
# per-OS installers landing in v0.3.0 — both verify the signed release
|
|
# manifest, which nightly deliberately does not generate. Shipping a bare
|
|
# nightly server tarball would be an install artifact with a stub manifest,
|
|
# so it's left to the versioned (v*) channel where the manifest is real.
|
|
- name: Build agent + helper binaries (linux-amd64 + windows-amd64)
|
|
if: steps.pre.outputs.go == 'true'
|
|
run: |
|
|
set -euo pipefail
|
|
VERSION="${{ steps.pre.outputs.version }}"
|
|
mkdir -p dist
|
|
|
|
build_agent () { # $1=goos $2=suffix $3=ext
|
|
GOOS=$1 GOARCH=amd64 CGO_ENABLED=0 sh -c "
|
|
cd agent && go build -ldflags \"-s -w \
|
|
-X github.com/Fimeg/RedFlag/agent/internal/version.Version=$VERSION \
|
|
-X github.com/Fimeg/RedFlag/agent/internal/version.ConfigVersion=$VERSION \
|
|
-X github.com/Fimeg/RedFlag/agent/internal/version.BuildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)\" \
|
|
-o ../dist/redflag-agent-$2$3 ./cmd/agent/"
|
|
}
|
|
build_agent linux linux-amd64 ""
|
|
build_agent windows windows-amd64 ".exe"
|
|
|
|
cd helper && cargo build --release && cd ..
|
|
cp helper/target/release/redflag-helper dist/redflag-helper-linux-amd64
|
|
|
|
- name: Package
|
|
if: steps.pre.outputs.go == 'true'
|
|
run: |
|
|
set -euo pipefail
|
|
VERSION="${{ steps.pre.outputs.version }}"
|
|
cd dist
|
|
tar czf redflag-$VERSION-linux-amd64.tar.gz redflag-agent-linux-amd64 redflag-helper-linux-amd64
|
|
zip -q redflag-$VERSION-windows-amd64.zip redflag-agent-windows-amd64.exe
|
|
sha256sum redflag-$VERSION-linux-amd64.tar.gz redflag-$VERSION-windows-amd64.zip > checksums-$VERSION.txt
|
|
ls -la
|
|
|
|
- name: Write release notes
|
|
if: steps.pre.outputs.go == 'true'
|
|
run: |
|
|
set -euo pipefail
|
|
SHA="${{ steps.pre.outputs.sha }}"
|
|
PREV="${{ steps.pre.outputs.prev }}"
|
|
VERSION="${{ steps.pre.outputs.version }}"
|
|
{
|
|
echo "Nightly alpha build — untagged channel, replaced every night. Agent + helper test binaries only; not fleet-upgradable (no manifest). Install the server from a versioned (v*) release or build from source."
|
|
echo ""
|
|
echo "version: $VERSION"
|
|
echo "commit: $SHA"
|
|
echo "built: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
echo ""
|
|
if [ -n "$PREV" ] && git cat-file -e "$PREV" 2>/dev/null; then
|
|
echo "### Since last nightly"
|
|
echo '```'
|
|
git log --oneline --no-decorate "$PREV..$SHA" | head -50
|
|
echo '```'
|
|
fi
|
|
} > notes.md
|
|
cat notes.md
|
|
|
|
- name: Publish nightly release (Gitea)
|
|
if: steps.pre.outputs.go == 'true'
|
|
run: |
|
|
set -euo pipefail
|
|
API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}"
|
|
TOKEN="${{ secrets.GITHUB_TOKEN }}"
|
|
SHA="${{ steps.pre.outputs.sha }}"
|
|
VERSION="${{ steps.pre.outputs.version }}"
|
|
|
|
RID=$(curl -s -H "Authorization: token $TOKEN" "$API/releases/tags/nightly" \
|
|
| python3 -c "
|
|
import json,sys
|
|
try: print(json.load(sys.stdin).get('id',''))
|
|
except Exception: print('')")
|
|
[ -n "$RID" ] && curl -s -X DELETE -H "Authorization: token $TOKEN" "$API/releases/$RID"
|
|
curl -s -o /dev/null -X DELETE -H "Authorization: token $TOKEN" "$API/tags/nightly" || true
|
|
|
|
BODY=$(python3 -c "import json; print(json.dumps(open('notes.md').read()))")
|
|
RID=$(curl -sf -X POST "$API/releases" \
|
|
-H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
|
|
-d "{\"tag_name\":\"nightly\",\"target_commitish\":\"$SHA\",\"name\":\"nightly $VERSION\",\"prerelease\":true,\"draft\":false,\"body\":$BODY}" \
|
|
| python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")
|
|
for f in dist/redflag-$VERSION-linux-amd64.tar.gz dist/redflag-$VERSION-windows-amd64.zip dist/checksums-$VERSION.txt; do
|
|
curl -sf -o /dev/null -X POST "$API/releases/$RID/assets?name=$(basename "$f")" \
|
|
-H "Authorization: token $TOKEN" -F "attachment=@$f"
|
|
done
|
|
echo "Gitea nightly published (release id=$RID)"
|
|
|
|
- name: Publish nightly release (Codeberg)
|
|
if: steps.pre.outputs.go == 'true'
|
|
run: |
|
|
set -euo pipefail
|
|
TOKEN="${{ secrets.CODEBERG_TOKEN }}"
|
|
if [ -z "$TOKEN" ]; then
|
|
echo "[INFO] [nightly] CODEBERG_TOKEN not set — skipping codeberg publish"
|
|
exit 0
|
|
fi
|
|
API="https://codeberg.org/api/v1/repos/Fimeg/RedFlag"
|
|
SHA="${{ steps.pre.outputs.sha }}"
|
|
VERSION="${{ steps.pre.outputs.version }}"
|
|
|
|
# Make sure codeberg has the commit before targeting a release at it.
|
|
git push "https://Fimeg:${TOKEN}@codeberg.org/Fimeg/RedFlag.git" HEAD:public
|
|
|
|
RID=$(curl -s -H "Authorization: token $TOKEN" "$API/releases/tags/nightly" \
|
|
| python3 -c "
|
|
import json,sys
|
|
try: print(json.load(sys.stdin).get('id',''))
|
|
except Exception: print('')")
|
|
[ -n "$RID" ] && curl -s -X DELETE -H "Authorization: token $TOKEN" "$API/releases/$RID"
|
|
curl -s -o /dev/null -X DELETE -H "Authorization: token $TOKEN" "$API/tags/nightly" || true
|
|
|
|
BODY=$(python3 -c "import json; print(json.dumps(open('notes.md').read()))")
|
|
RID=$(curl -sf -X POST "$API/releases" \
|
|
-H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
|
|
-d "{\"tag_name\":\"nightly\",\"target_commitish\":\"$SHA\",\"name\":\"nightly $VERSION\",\"prerelease\":true,\"draft\":false,\"body\":$BODY}" \
|
|
| python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")
|
|
for f in dist/redflag-$VERSION-linux-amd64.tar.gz dist/redflag-$VERSION-windows-amd64.zip dist/checksums-$VERSION.txt; do
|
|
curl -sf -o /dev/null -X POST "$API/releases/$RID/assets?name=$(basename "$f")" \
|
|
-H "Authorization: token $TOKEN" -F "attachment=@$f"
|
|
done
|
|
echo "Codeberg nightly published (release id=$RID)"
|