ci: nightly alpha channel + hand profile readme to profile-engine
This commit is contained in:
parent
0e1a375af6
commit
b8cdb91a21
2 changed files with 217 additions and 79 deletions
|
|
@ -187,9 +187,9 @@ jobs:
|
|||
fi
|
||||
echo "All action refs are SHA-pinned."
|
||||
|
||||
# Push public to Codeberg when all checks pass — nightly mirror.
|
||||
# Also updates the .profile README with latest release badges and
|
||||
# recent activity. Requires CODEBERG_TOKEN secret in the repo settings.
|
||||
# Push public to Codeberg when all checks pass. The .profile README is
|
||||
# rendered by the profile-engine repo — single writer, do not stamp it here.
|
||||
# Requires CODEBERG_TOKEN secret in the repo settings.
|
||||
sync-codeberg:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [go-vet, go-test, rust-test, cross-compile, web-build, installer-integrity, no-ai-attribution, action-pins]
|
||||
|
|
@ -207,79 +207,3 @@ jobs:
|
|||
git remote add codeberg-ci "https://${{ secrets.CODEBERG_TOKEN }}@codeberg.org/Fimeg/RedFlag.git"
|
||||
git push codeberg-ci public:public
|
||||
echo "Pushed public to codeberg.org/Fimeg/RedFlag"
|
||||
|
||||
- name: Update profile README
|
||||
run: |
|
||||
set -euo pipefail
|
||||
TOKEN="${{ secrets.CODEBERG_TOKEN }}"
|
||||
if [ -z "$TOKEN" ]; then
|
||||
echo "[INFO] [sync] CODEBERG_TOKEN not set — skipping profile update"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Clone the .profile repo
|
||||
git clone "https://${TOKEN}@codeberg.org/Fimeg/.profile.git" /tmp/profile
|
||||
cd /tmp/profile
|
||||
|
||||
# Fetch latest RedFlag release tag from codeberg API
|
||||
RF_VER=$(curl -sf "https://codeberg.org/api/v1/repos/Fimeg/RedFlag/releases/latest" \
|
||||
-H "Authorization: token ${TOKEN}" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tag_name','v0.2.x'))" 2>/dev/null || echo "v0.2.x")
|
||||
echo "RedFlag: $RF_VER"
|
||||
|
||||
# Fetch latest Souveraine release tag from codeberg API
|
||||
SV_VER=$(curl -sf "https://codeberg.org/api/v1/repos/Fimeg/Souveraine/releases/latest" \
|
||||
-H "Authorization: token ${TOKEN}" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tag_name','dev'))" 2>/dev/null || echo "dev")
|
||||
echo "Souveraine: $SV_VER"
|
||||
|
||||
# Fetch recent activity (last 6 push/create/release events)
|
||||
curl -sf "https://codeberg.org/api/v1/users/Fimeg/events?limit=20" \
|
||||
-H "Authorization: token ${TOKEN}" | python3 -c "
|
||||
import sys, json
|
||||
events = json.load(sys.stdin)
|
||||
count = 0
|
||||
for e in events:
|
||||
if count >= 6: break
|
||||
t = e.get('type','')
|
||||
repo = e.get('repo',{}).get('name','')
|
||||
url = 'https://codeberg.org/' + repo
|
||||
if t == 'ReleaseEvent':
|
||||
tag = e.get('payload',{}).get('release',{}).get('tag_name','')
|
||||
print(f' - [**{repo}**]({url}) — released **{tag}**')
|
||||
count += 1
|
||||
elif t == 'PushEvent':
|
||||
n = len(e.get('payload',{}).get('commits',[]))
|
||||
print(f' - [**{repo}**]({url}) — {n} commit{"s" if n!=1 else ""}')
|
||||
count += 1
|
||||
elif t == 'CreateEvent':
|
||||
ref = e.get('payload',{}).get('ref','')
|
||||
print(f' - [**{repo}**]({url}) — created **{ref}**')
|
||||
count += 1
|
||||
" > /tmp/activity.md || true
|
||||
|
||||
# If no activity fetched, write placeholder
|
||||
if [ ! -s /tmp/activity.md ]; then
|
||||
echo " - _no recent activity_" > /tmp/activity.md
|
||||
fi
|
||||
echo "Activity:"
|
||||
cat /tmp/activity.md
|
||||
|
||||
# Stamp badges. shields.io needs literal dashes escaped as --
|
||||
TODAY=$(date -u +%Y--%m--%d)
|
||||
sed -i "s|RedFlag-v[^ ]*-green|RedFlag-${RF_VER}-green|" README.md
|
||||
sed -i "s|Souveraine-[^ ]*-blue|Souveraine-${SV_VER}-blue|" README.md
|
||||
sed -i "s|Updated-[^ ]*-gray|Updated-${TODAY}-gray|" README.md
|
||||
|
||||
# Stamp activity section (replace between <!--ACTIVITY--> markers)
|
||||
awk '
|
||||
/<!--ACTIVITY-->/ { print; system("cat /tmp/activity.md"); inblock=1; next }
|
||||
/<!--\/ACTIVITY-->/ { inblock=0 }
|
||||
!inblock' README.md > README.tmp && mv README.tmp README.md
|
||||
|
||||
# Commit and push if changed
|
||||
git config user.name "sync-bot"
|
||||
git config user.email "ci@codeberg.org"
|
||||
git diff --quiet README.md || {
|
||||
git commit -m "sync: badges + activity for ${TODAY}"
|
||||
git push origin main
|
||||
echo "Profile README updated"
|
||||
}
|
||||
|
|
|
|||
214
.gitea/workflows/nightly.yml
Normal file
214
.gitea/workflows/nightly.yml
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
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,re
|
||||
try:
|
||||
m = re.search(r'commit: ([0-9a-f]{40})', json.load(sys.stdin).get('body',''))
|
||||
print(m.group(1) if m 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@29eef336d9b2848a0b548edc03f92a220660cdb8 # 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
|
||||
|
||||
- name: Build binaries (linux-amd64 + windows-amd64)
|
||||
if: steps.pre.outputs.go == 'true'
|
||||
run: |
|
||||
set -euo pipefail
|
||||
VERSION="${{ steps.pre.outputs.version }}"
|
||||
mkdir -p dist
|
||||
|
||||
build_go () { # $1=goos $2=suffix $3=ext
|
||||
GOOS=$1 GOARCH=amd64 CGO_ENABLED=0 sh -c "
|
||||
cd server && go build -ldflags \"-s -w \
|
||||
-X github.com/Fimeg/RedFlag/server/internal/version/versions.AgentVersion=$VERSION \
|
||||
-X github.com/Fimeg/RedFlag/server/internal/version/versions.ConfigVersion=$VERSION\" \
|
||||
-o ../dist/redflag-server-$2$3 cmd/server/main.go"
|
||||
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_go linux linux-amd64 ""
|
||||
build_go 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-server-linux-amd64 redflag-agent-linux-amd64 redflag-helper-linux-amd64
|
||||
zip -q redflag-$VERSION-windows-amd64.zip redflag-server-windows-amd64.exe 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. Not fleet-upgradable (no manifest)."
|
||||
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)"
|
||||
Loading…
Reference in a new issue