From e2e471f12ca97d687ef508f7921530eeeaecab1c Mon Sep 17 00:00:00 2001 From: Alberto Balbo Date: Wed, 23 Sep 2026 14:19:43 +0200 Subject: [PATCH] Workflow Gitea Actions (ci e release) e guida all'installazione su Unraid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ci.yml compila con gli avvisi come errori, esegue i test e costruisce l'immagine a ogni push; release.yml pubblica l'immagine sul registro di Gitea e crea la release con il template Unraid a ogni tag, in modo idempotente rispetto alla catena locale. Sul server non c'è ancora un runner: docs/DOCKER.md spiega come registrarlo e quale segreto serve. Il README di deploy/unraid guida passo per passo l'installazione, compreso il registro in HTTP da dichiarare a Docker di Unraid. Co-Authored-By: Claude Fable 5.1 --- Encelado/.gitea/workflows/ci.yml | 36 ++++++++++++ Encelado/.gitea/workflows/release.yml | 78 ++++++++++++++++++++++++++ Encelado/CLAUDE.md | 2 +- Encelado/README.md | 2 +- Encelado/build/README.md | 4 ++ Encelado/deploy/unraid/README.md | 81 +++++++++++++++++++-------- Encelado/docs/ARCHITECTURE.md | 1 + Encelado/docs/DOCKER.md | 16 ++++++ 8 files changed, 196 insertions(+), 24 deletions(-) create mode 100644 Encelado/.gitea/workflows/ci.yml create mode 100644 Encelado/.gitea/workflows/release.yml diff --git a/Encelado/.gitea/workflows/ci.yml b/Encelado/.gitea/workflows/ci.yml new file mode 100644 index 0000000..260bdbd --- /dev/null +++ b/Encelado/.gitea/workflows/ci.yml @@ -0,0 +1,36 @@ +# Verifica a ogni push e pull request: compilazione con gli avvisi come errori e la +# suite dei test. Un secondo lavoro costruisce l'immagine (senza pubblicarla) per +# accorgersi subito di una Dockerfile rotta: richiede un runner con Docker. +# +# Runner: act_runner con l'etichetta ubuntu-latest (vedi docs/DOCKER.md, «Gitea Actions»). +name: ci + +on: + push: + branches: [main] + pull_request: + +jobs: + verifica: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: "10.0.x" + - name: Compilazione (avvisi = errori) + run: dotnet build Encelado.slnx -c Release -nologo -warnaserror + - name: Test + run: dotnet test tests/Encelado.Tests -c Release --no-build --nologo -v q + + immagine: + runs-on: ubuntu-latest + needs: verifica + steps: + - uses: actions/checkout@v4 + - name: Costruzione dell'immagine (i test girano anche dentro la build) + run: | + docker build --pull \ + --build-arg VERSION=ci \ + --build-arg GIT_COMMIT="${GITHUB_SHA::12}" \ + -t encelado:ci . diff --git a/Encelado/.gitea/workflows/release.yml b/Encelado/.gitea/workflows/release.yml new file mode 100644 index 0000000..edc1301 --- /dev/null +++ b/Encelado/.gitea/workflows/release.yml @@ -0,0 +1,78 @@ +# Rilascio a ogni tag vX.Y.Z: l'immagine sul registro dei container di Gitea +# (/alby96/encelado:X.Y.Z e :latest) e la release con il template Unraid allegato. +# +# È la stessa cosa che fa build/Release.proj -t:Rilascia dal PC; qui gira sul runner +# quando il tag arriva sul server. Idempotente: un'immagine già presente viene +# sovrascritta con lo stesso contenuto, una release già creata dalla catena viene +# lasciata com'è e le manca solo l'allegato, che viene aggiunto se non c'è. +# +# Serve il segreto REGISTRY_TOKEN (Impostazioni ▸ Actions ▸ Segreti del repository): +# un token dell'utente con i permessi package:write e repository:write. +# Solo linux/amd64: la build esegue la suite dei test, e sotto QEMU (arm64) sarebbe +# lentissima; il server Unraid è amd64. +name: release + +on: + push: + tags: ["v*"] + +jobs: + immagine: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Versione e registro dal tag e dall'indirizzo del server + run: | + echo "VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_ENV" + echo "REGISTRY=$(echo "${{ gitea.server_url }}" | sed -E 's#^https?://##; s#/$##')" >> "$GITHUB_ENV" + echo "OWNER=$(echo "${{ gitea.repository_owner }}" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV" + + - uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ gitea.actor }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - uses: docker/setup-buildx-action@v3 + with: + # Il registro di Gitea è in HTTP: buildx deve poterlo trattare come insicuro. + driver-opts: network=host + buildkitd-config-inline: | + [registry."${{ env.REGISTRY }}"] + http = true + insecure = true + + - uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64 + push: true + build-args: | + VERSION=${{ env.VERSION }} + GIT_COMMIT=${{ gitea.sha }} + tags: | + ${{ env.REGISTRY }}/${{ env.OWNER }}/encelado:${{ env.VERSION }} + ${{ env.REGISTRY }}/${{ env.OWNER }}/encelado:latest + + - name: Release con il template Unraid allegato + env: + TOKEN: ${{ secrets.REGISTRY_TOKEN }} + API: ${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }} + run: | + set -eu + TAG="${GITHUB_REF_NAME}" + ASSET="encelado-unraid-${VERSION}.xml" + cp deploy/unraid/encelado.xml "$ASSET" + ID=$(curl -sf -H "Authorization: token $TOKEN" "$API/releases/tags/$TAG" | sed -n 's/.*"id":\([0-9]*\).*/\1/p' | head -1 || true) + if [ -z "$ID" ]; then + BODY=$(printf '{"tag_name":"%s","name":"Encelado %s","body":"Versione %s.\\n\\nImmagine: `%s/%s/encelado:%s` (anche `:latest`). Template Unraid allegato.","draft":false,"prerelease":false}' "$TAG" "$VERSION" "$VERSION" "$REGISTRY" "$OWNER" "$VERSION") + ID=$(curl -sf -H "Authorization: token $TOKEN" -H "Content-Type: application/json" --data-binary "$BODY" "$API/releases" | sed -n 's/.*"id":\([0-9]*\).*/\1/p' | head -1) + echo "release $TAG creata (id $ID)" + else + echo "release $TAG già presente (id $ID): aggiungo solo l'allegato mancante" + fi + if ! curl -sf -H "Authorization: token $TOKEN" "$API/releases/$ID/assets" | grep -q "\"name\":\"$ASSET\""; then + curl -sf -H "Authorization: token $TOKEN" -F "attachment=@$ASSET" "$API/releases/$ID/assets?name=$ASSET" > /dev/null + echo "allegato $ASSET caricato" + fi diff --git a/Encelado/CLAUDE.md b/Encelado/CLAUDE.md index d5b2da7..f7a6617 100644 --- a/Encelado/CLAUDE.md +++ b/Encelado/CLAUDE.md @@ -23,7 +23,7 @@ Bot di trading in C# (.NET 10) su **eToro** con la strategia "Correlation Basket | `docs/QUESTIONS.md` | domande poste per fase, risposte o default applicati, con data | | `docs/PIANO_5.0.md`, `docs/PROMPT_5.0.md`, `docs/POSTMORTEM_ordini_pendenti.md` | il piano della 5.0 (fasi, stime, decisioni vincolanti), la specifica originale e il post-mortem delle gambe orfane del 16-21/9/2026 | | `docs/GLOSSARY.md`, `docs/KNOWN_ISSUES.md`, `CHANGELOG.md`, `docs/adr/` | glossario, problemi noti, cronologia, decisioni architetturali | -| `build/README.md` | catena di verifica, pacchetto (immagine Docker) e rilascio | +| `build/README.md` | catena di verifica, pacchetto (immagine Docker) e rilascio; `.gitea/workflows/` per le Actions | ## Progetti diff --git a/Encelado/README.md b/Encelado/README.md index 4bbbce3..acc915f 100644 --- a/Encelado/README.md +++ b/Encelado/README.md @@ -50,7 +50,7 @@ dotnet msbuild build/Release.proj -t:Docker # l'immagine, con scripts\screenshots.ps1 # rigenera docs/img dal server campione ``` -Attività di VS Code: `verifica`, `backtest`, `avvia in locale (script)`, `avvia in docker`, `ferma docker`, `screenshot`, `immagine docker`, `pacchetto`, `rilascia su Gitea`. +Attività di VS Code: `verifica`, `backtest`, `avvia in locale (script)`, `avvia in docker`, `ferma docker`, `screenshot`, `immagine docker`, `pacchetto`, `rilascia su Gitea`. Su Gitea, `.gitea/workflows/ci.yml` verifica ogni push e `release.yml` pubblica l'immagine a ogni tag (serve un runner: `docs/DOCKER.md`). ``` src/Encelado.Core logica pura: basket, cross sintetici, decisore, cost gate, sizing, esecutore, registro ordini, kill-switch, apprendimento, notifiche diff --git a/Encelado/build/README.md b/Encelado/build/README.md index 2aae0ca..0b434a9 100644 --- a/Encelado/build/README.md +++ b/Encelado/build/README.md @@ -71,6 +71,10 @@ dotnet msbuild build/Release.proj -t:Backtest -p:Dati="%USERPROFILE%\Documents\E 5. `Pacchetto`: zip portabile `bin/installer/Encelado__portabile.zip`, copia del template `deploy/unraid/encelado.xml` con la versione nel nome, **tag** `v` (solo ora, a pacchetto pronto, e solo con l'albero pulito). 6. `Rilascia`: `git push --tags` e verifica che il tag sia sul remoto; `docker login` sul registro di Gitea con il token (via stdin), `docker push` dei due tag, `docker logout`; release su Gitea con lo zip e il template allegati. +## Gitea Actions + +`.gitea/workflows/ci.yml` (build + test + `docker build` a ogni push) e `release.yml` (immagine sul registro e release a ogni tag `v*`) fanno sul server quello che `Verifica` e `Rilascia` fanno dal PC. Richiedono un runner registrato e il segreto `REGISTRY_TOKEN`: vedi `docs/DOCKER.md`, «Gitea Actions». La catena resta il percorso di riferimento finché il runner non c'è. + ## Chi chiede la versione MSBuild non può chiedere niente a nessuno: è un motore di compilazione. La domanda la fa diff --git a/Encelado/deploy/unraid/README.md b/Encelado/deploy/unraid/README.md index 6d66c23..2de2549 100644 --- a/Encelado/deploy/unraid/README.md +++ b/Encelado/deploy/unraid/README.md @@ -1,32 +1,69 @@ # Encelado su Unraid Il template `encelado.xml` installa il container dall'immagine sul registro di Gitea -(`192.168.30.23:3000/alby96/encelado:latest`). Istruzioni complete su volumi, variabili, -healthcheck e aggiornamento in `docs/DOCKER.md`. +(`192.168.30.23:3000/alby96/encelado:latest`). Dettagli su volumi, variabili, healthcheck e +aggiornamento in `docs/DOCKER.md`; guida operativa in `docs/RUNBOOK.md`. -## Installazione +## 1. Il registro di Gitea è in HTTP: dillo a Docker di Unraid -1. **Registro privato**: se il registro di Gitea è in HTTP, Unraid deve fidarsi. - In *Impostazioni ▸ Docker* (modalità avanzata) aggiungi `192.168.30.23:3000` agli - `insecure-registries` del demone, oppure crea `/boot/config/docker.cfg` con - `DOCKER_OPTS="--insecure-registry 192.168.30.23:3000"`. Se il pacchetto è privato, - dal terminale di Unraid: `docker login 192.168.30.23:3000` con utente e token Gitea. -2. **Template**: *Docker ▸ Aggiungi container ▸ Template* e incolla l'URL raw - `http://192.168.30.23:3000/Alby96/Encelado/raw/branch/main/deploy/unraid/encelado.xml`, - oppure copia il file in `/boot/config/plugins/dockerMan/templates-user/`. -3. Compila **ENCELADO_WEB_TOKEN** (obbligatorio: senza, l'interfaccia resta chiusa in - localhost), le chiavi eToro (o lascia vuoto e inseriscile poi da *Impostazioni ▸ - Chiavi eToro* con `ENCELADO_KEY_PASSPHRASE` impostata), `TZ`, eventualmente Telegram. -4. Avvia. Al primo avvio il container semina `encelado.json` e `strategy.json` in - `/mnt/user/appdata/encelado/config`. L'interfaccia è su `http://IP:8080/`; il token - va inserito una volta e resta in un cookie per trenta giorni. +Docker rifiuta un registro senza TLS finché non è dichiarato «insecure». Su Unraid, dal +terminale (o da *Tools ▸ Terminal*): -## Aggiornamento +```sh +echo 'DOCKER_OPTS="--insecure-registry 192.168.30.23:3000"' >> /boot/config/docker.cfg +/etc/rc.d/rc.docker restart # oppure Impostazioni ▸ Docker: disabilita e riabilita +docker info | grep -A2 "Insecure Registries" # deve elencare 192.168.30.23:3000 +``` -*Docker ▸ Controlla aggiornamenti*: il tag `latest` segue l'ultima release. Il container si -ferma con SIGTERM (45 s di grazia): i basket aperti restano sul conto con gli stop -nativi e vengono ripresi dallo stato salvato e dalla riconciliazione al riavvio -(`docs/RUNBOOK.md`, «Recupero dopo inattività»). +Il repository Encelado è pubblico, quindi anche il pacchetto lo è: `docker pull` non ha +bisogno di login. Se un giorno diventa privato: `docker login 192.168.30.23:3000` con +utente Gitea e un token con permesso `package:read`. + +## 2. Installa dal template + +*Docker ▸ Add Container*, in fondo alla pagina **Template repositories** non serve: incolla +l'URL del template nel campo **Template** (in alto, «Select a template» ▸ voce vuota, poi il +campo URL), oppure scarica il file e mettilo in `/boot/config/plugins/dockerMan/templates-user/`: + +``` +http://192.168.30.23:3000/Alby96/Encelado/raw/branch/main/deploy/unraid/encelado.xml +``` + +Lo stesso file è allegato a ogni release su Gitea con la versione nel nome. + +## 3. Compila i campi + +| Campo | Cosa mettere | +|---|---| +| **Porta web** | `8080` (o un'altra libera: la WebUI del container la segue) | +| **/config**, **/data** | lascia i default `/mnt/user/appdata/encelado/config` e `/data` | +| **ENCELADO_WEB_TOKEN** | **obbligatorio**: una stringa lunga a piacere. Senza, il server ascolta solo dentro il container e dalla rete non si vede niente | +| **ETORO_API_KEY**, **ETORO_USER_KEY** | le chiavi del conto **demo** (dal portale sviluppatori eToro; demo e reale hanno chiavi diverse). Oppure lasciale vuote e inseriscile poi dall'interfaccia, mettendo una **ENCELADO_KEY_PASSPHRASE** | +| **ETORO_ENVIRONMENT** | `demo` | +| **ENCELADO_EXECUTION_MODE** | `Demo` | +| **TZ** | `Europe/Rome` | +| **TELEGRAM_BOT_TOKEN**, **TELEGRAM_CHAT_ID** | facoltativi: notifiche ogni ora, eventi, riepilogo serale e comandi dalla chat | +| PUID / PGID | `99` / `100` (nobody:users, i default di Unraid) | + +`ENCELADO_CONFIRM_LIVE` resta vuoto: serve solo per il conto reale, che oggi non è praticabile. + +## 4. Primo avvio + +1. **Apply**. Il container semina `encelado.json` e `strategy.json` in `/mnt/user/appdata/encelado/config`. +2. Apri `http://:8080/`, inserisci il token (resta in un cookie per trenta giorni). +3. Se non hai messo le chiavi nelle variabili: **Impostazioni ▸ Chiavi eToro ▸ Verifica e salva** (vengono provate contro eToro e salvate cifrate in `/config/etoro.keys.enc`). +4. Con `ENCELADO_AUTOSTART` al default (`1`) il motore è già partito; altrimenti **AVVIA**. Il chip in basso a sinistra dice `DEMO`. +5. Per un test lungo guarda: contatore «in attesa · orfane · esterne» (orfane deve restare 0), **Storico ▸ Ordini** (unità richieste ed eseguite uguali, esito `Filled`), il log. Il kill-switch è il pulsante in alto a destra, o un file `STOP` in `/mnt/user/appdata/encelado/config`. + +Il log è in `docker logs encelado` e in `/mnt/user/appdata/encelado/data/logs/encelado.log`; il ledger in `.../data/data/ledger/`. + +## 5. Aggiornamento + +*Docker ▸ Check for Updates*: il tag `latest` segue l'ultima release. Il container si ferma +con SIGTERM (45 s di grazia): i basket aperti restano sul conto con gli stop nativi e +vengono ripresi dallo stato salvato e dalla riconciliazione al riavvio (`docs/RUNBOOK.md`, +«Recupero dopo inattività»). Per tornare a una versione precisa cambia il tag nel campo +**Repository** (`…/encelado:5.0.0`). ## Icona diff --git a/Encelado/docs/ARCHITECTURE.md b/Encelado/docs/ARCHITECTURE.md index 915b38d..3d9a420 100644 --- a/Encelado/docs/ARCHITECTURE.md +++ b/Encelado/docs/ARCHITECTURE.md @@ -32,6 +32,7 @@ Encelado.slnx │ wwwroot/ (index.html, login.html, app.css, app.js, icon.svg, manifest.webmanifest — incorporati) ├── tools/Encelado.Backtest ticks, baskets, falsify, learn (referenzia Core ed Engine) ├── tests/Encelado.Tests xunit (net10.0, gira su Windows e nello stadio di build dell'immagine) +├── .gitea/workflows/ ci.yml (build, test, docker build a ogni push), release.yml (immagine sul registro e release a ogni tag) ├── deploy/docker/ entrypoint.sh (TZ, PUID/PGID, gosu, exec) ├── deploy/unraid/ encelado.xml (template Container v2), README.md ├── Dockerfile, docker-compose.yml, .dockerignore diff --git a/Encelado/docs/DOCKER.md b/Encelado/docs/DOCKER.md index 0892b0a..216029e 100644 --- a/Encelado/docs/DOCKER.md +++ b/Encelado/docs/DOCKER.md @@ -67,6 +67,22 @@ Il server ascolta su tutte le interfacce **solo** quando `ENCELADO_WEB_TOKEN` è Il Gitea dell'utente è raggiunto in HTTP: chi fa `docker pull` deve dichiararlo `insecure-registry` (Docker Desktop: *Settings ▸ Docker Engine* → `"insecure-registries": ["192.168.30.23:3000"]`; Unraid: vedi `deploy/unraid/README.md`). Il push dalla catena di rilascio (`-t:Rilascia`) fa `docker login` con il token di `build/gitea.json` e `docker logout` subito dopo. +## Gitea Actions + +Due workflow in `.gitea/workflows/`, sul modello di Rea: + +| Workflow | Quando | Cosa fa | +|---|---|---| +| `ci.yml` | push su `main`, pull request | `dotnet build -warnaserror` e `dotnet test`; poi `docker build` dell'immagine (senza pubblicarla) per accorgersi di una Dockerfile rotta | +| `release.yml` | tag `v*` | costruisce e pubblica `/alby96/encelado:` e `:latest` sul registro di Gitea (solo `linux/amd64`: la build esegue i test e sotto QEMU sarebbe lentissima), poi crea la release con il template Unraid allegato; se la release esiste già (creata dalla catena) aggiunge solo l'allegato mancante | + +Servono due cose che al 2026-09-23 **mancano**: + +1. **Un runner.** Sul server non ce n'è nessuno (`/api/v1/repos/Alby96/Encelado/actions/runners` è vuoto): i workflow restano in coda. Il modo più semplice è un container `gitea/act_runner` su Unraid con il socket di Docker montato (`/var/run/docker.sock`), registrato con il token di *Site Administration ▸ Actions ▸ Runners ▸ Create new runner* e con l'etichetta `ubuntu-latest:docker://catthehacker/ubuntu:act-latest`. Con il registro in HTTP il runner (e la sua immagine di lavoro) deve avere `192.168.30.23:3000` fra gli `insecure-registries`. +2. **Il segreto `REGISTRY_TOKEN`** nel repository (*Impostazioni ▸ Actions ▸ Segreti*): un token dell'utente con `package:write` e `repository:write` (lo stesso di `build/gitea.json` va bene). + +Finché il runner non c'è, il rilascio si fa dal PC con `dotnet msbuild build/Release.proj -t:Rilascia -p:Versione=X.Y.Z`, che produce lo stesso risultato (immagine sul registro, tag, release con allegati); i due percorsi sono idempotenti fra loro. + ## Prove in locale: la sandbox `deploy/local` `deploy/local/config` e `deploy/local/data` (ignorate da git) sono il `/config` e il `/data` delle prove sul PC, in tutti e tre i modi di avvio: