Files
stream-control/.gitea/workflows/release.yml
jeanotx32 6a45b3063f
All checks were successful
release / build (push) Successful in 16s
release / verify-windows (push) Successful in 1m13s
Fix : widows giea 2
2026-08-11 22:48:15 +02:00

261 lines
11 KiB
YAML

name: release
# Construit l'image du plan de contrôle et publie le bundle d'agent.
#
# Prérequis côté Gitea :
# - un secret REGISTERYKEY (jeton d'accès avec les portées repository + package)
# - les paquets activés sur l'instance
# - un runner ubuntu-latest ayant accès au socket Docker
# - un runner windows-latest (test de fumée de l'agent)
on:
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
env:
IMAGE_NAME: stream-control-server
PACKAGE_NAME: stream-control-agent
jobs:
build:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.meta.outputs.version }}
registry: ${{ steps.meta.outputs.registry }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '24'
# --- Identité de la version -----------------------------------------
- name: Calculer version et registre
id: meta
run: |
set -euo pipefail
# Une étiquette v1.2.3 donne 1.2.3 ; sinon 0.1.0-<sha court>.
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
VERSION="${GITHUB_REF#refs/tags/}"
VERSION="${VERSION#v}"
else
VERSION="0.1.0-${GITHUB_SHA:0:7}"
fi
# L'hôte du registre est celui de l'instance Gitea. La variable
# REGISTRY_HOST permet de le forcer (instance derrière un autre nom).
REGISTRY="${{ vars.REGISTRY_HOST }}"
if [ -z "$REGISTRY" ]; then
REGISTRY="${GITHUB_SERVER_URL#http://}"
REGISTRY="${REGISTRY#https://}"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "registry=$REGISTRY" >> "$GITHUB_OUTPUT"
echo "Version $VERSION → registre $REGISTRY"
# --- Qualité ---------------------------------------------------------
- name: Installer les dépendances
run: npm ci
- name: Contrôle de types
run: npm run typecheck
- name: Compiler
run: npm run build
- name: Produire le bundle d'agent
run: npm run bundle -w @stream-control/agent
# Un bundle qui ne démarre pas ne doit jamais être publié. Le serveur est
# volontairement injoignable ici : on vérifie que le diagnostic s'exécute.
- name: Vérifier le bundle (Linux)
env:
AGENT_TOKEN: ci-smoke-test
SERVER_URL: ws://127.0.0.1:1/ws/agent
run: |
set -euo pipefail
OUT="$(node packages/agent/dist-bundle/agent.cjs --check 2>&1 || true)"
echo "$OUT"
echo "$OUT" | grep -q 'obs-websocket' || { echo 'Diagnostic incomplet'; exit 1; }
# --- Image du plan de contrôle ---------------------------------------
- name: Authentification au registre
run: |
echo "${{ secrets.REGISTERYKEY }}" \
| docker login "${{ steps.meta.outputs.registry }}" \
--username "${{ github.repository_owner }}" --password-stdin
- name: Construire et pousser l'image
run: |
set -euo pipefail
BASE="${{ steps.meta.outputs.registry }}/${{ github.repository_owner }}/${IMAGE_NAME}"
VERSION="${{ steps.meta.outputs.version }}"
docker build \
--tag "$BASE:$VERSION" \
--tag "$BASE:sha-${GITHUB_SHA:0:7}" \
--label "org.opencontainers.image.revision=$GITHUB_SHA" \
--label "org.opencontainers.image.version=$VERSION" \
--label "org.opencontainers.image.source=$GITHUB_SERVER_URL/${{ github.repository }}" \
.
docker push "$BASE:$VERSION"
docker push "$BASE:sha-${GITHUB_SHA:0:7}"
# :latest ne suit que main et les étiquettes, jamais une branche de travail.
if [[ "$GITHUB_REF" == refs/heads/main || "$GITHUB_REF" == refs/tags/* ]]; then
docker tag "$BASE:$VERSION" "$BASE:latest"
docker push "$BASE:latest"
fi
# --- Bundle d'agent et scripts d'installation ------------------------
- name: Publier les artefacts d'agent
run: |
set -euo pipefail
API="${GITHUB_SERVER_URL}/api/packages/${{ github.repository_owner }}/generic/${PACKAGE_NAME}"
AUTH="${{ github.repository_owner }}:${{ secrets.REGISTERYKEY }}"
VERSION="${{ steps.meta.outputs.version }}"
publish() {
local version="$1" file="$2" path="$3"
curl -sS -f -X PUT --user "$AUTH" --upload-file "$path" \
"$API/$version/$file"
echo " publié : $version/$file"
}
# Version immuable, tracée.
publish "$VERSION" agent.cjs packages/agent/dist-bundle/agent.cjs
publish "$VERSION" install-agent.sh deploy/install-agent.sh
publish "$VERSION" install-agent.ps1 deploy/install-agent.ps1
# Alias « latest », que visent les commandes d'installation. Le registre
# générique refuse d'écraser un fichier : on supprime la version d'abord.
if [[ "$GITHUB_REF" == refs/heads/main || "$GITHUB_REF" == refs/tags/* ]]; then
curl -sS -X DELETE --user "$AUTH" "$API/latest" >/dev/null || true
publish latest agent.cjs packages/agent/dist-bundle/agent.cjs
publish latest install-agent.sh deploy/install-agent.sh
publish latest install-agent.ps1 deploy/install-agent.ps1
fi
- name: Récapitulatif
run: |
BASE="${{ steps.meta.outputs.registry }}/${{ github.repository_owner }}/${IMAGE_NAME}"
{
echo "### Publié"
echo ""
echo "| Artefact | Référence |"
echo "| --- | --- |"
echo "| Image | \`$BASE:${{ steps.meta.outputs.version }}\` |"
echo "| Agent | \`${PACKAGE_NAME}/${{ steps.meta.outputs.version }}/agent.cjs\` |"
} >> "$GITHUB_STEP_SUMMARY"
# Le bundle doit démarrer sur Windows : chemins, PowerShell, résolution de
# modules. Un test sur Linux seul ne couvre pas ces différences.
verify-windows:
runs-on: windows-latest
needs: build
steps:
- uses: actions/checkout@v4
# Le runner fournit Node 20, alors que le projet exige 22+ et que les
# scripts d'installation déposent 22+ sur les VM. Sans cette étape, le
# test de fumée validerait le bundle contre une version que la production
# n'utilisera jamais.
- uses: actions/setup-node@v4
with:
node-version: '22'
# Deux pièges de Windows PowerShell 5.1 sont à l'œuvre dans les étapes
# `shell: powershell` ci-dessous, et valent d'être connus avant de les
# modifier :
#
# 1. Les guillemets doubles imbriqués dans un argument passé à un
# exécutable natif sont perdus. `node -p 'x.split(".")[0]'` arrive
# donc comme `x.split(.)[0]` et lève une SyntaxError. On lit ici
# `node --version` et on découpe côté PowerShell : rien ne traverse
# cette frontière.
# 2. Le runner écrit ce bloc dans un .ps1 sans BOM, que PowerShell 5.1
# relit dans la page de codes ANSI. Tout caractère non-ASCII y devient
# illisible (« trouvé » s'affichait « trouvǸ »). Les chaînes affichées
# restent donc en ASCII — les commentaires, eux, ne sont jamais rendus.
- name: Confirmer la version de Node
shell: powershell
run: |
$raw = (& node --version)
if ($raw -match '^v(\d+)\.') { $major = [int]$Matches[1] } else { $major = 0 }
Write-Host "Node $raw"
if ($major -lt 22) { throw "Node 22+ requis, version lue : $raw" }
# Le script d'installation Windows n'est exécutable que sur Windows :
# ce contrôle de syntaxe est la seule barrière avant qu'il n'atterrisse
# sur une VM de production.
# shell: powershell (Windows PowerShell 5.1) et non pwsh : PowerShell 7
# n'est pas garanti sur un runner Windows, alors que 5.1 est toujours là.
# C'est aussi la version que les VM d'enregistrement exécuteront.
- name: Valider la syntaxe du script d'installation
shell: powershell
run: |
$path = (Resolve-Path 'deploy/install-agent.ps1').Path
# Le script doit rester en ASCII pur. Il est publié sans BOM et exécuté
# par PowerShell 5.1, qui relit alors le fichier dans la page de codes
# ANSI. Au-delà de l'affichage abîmé, certains caractères cassent le
# parseur : le tiret cadratin (E2 80 94) se relit en CP1252 comme « — »,
# dont le dernier caractère est U+201D — un délimiteur de chaîne valide
# en PowerShell. La chaîne se ferme en plein milieu et l'erreur signalée
# est une accolade manquante, des lignes plus loin.
$bad = [System.IO.File]::ReadAllBytes($path) | Where-Object { $_ -gt 127 }
if ($bad) {
throw "install-agent.ps1 doit rester en ASCII : $($bad.Count) octet(s) hors plage"
}
$errors = $null
[System.Management.Automation.Language.Parser]::ParseFile(
$path, [ref]$null, [ref]$errors) | Out-Null
if ($errors) {
$errors | ForEach-Object { Write-Host "$($_.Extent.StartLineNumber): $($_.Message)" }
throw "$($errors.Count) erreur(s) de syntaxe dans install-agent.ps1"
}
Write-Host 'install-agent.ps1 : syntaxe valide.'
- name: Récupérer le bundle publié
shell: powershell
run: |
$ErrorActionPreference = 'Stop'
# PowerShell 5.1 ne négocie pas TLS 1.2 par défaut sur toutes les éditions.
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$url = "$env:GITHUB_SERVER_URL/api/packages/${{ github.repository_owner }}/generic/$env:PACKAGE_NAME/${{ needs.build.outputs.version }}/agent.cjs"
$pair = "${{ github.repository_owner }}:${{ secrets.REGISTERYKEY }}"
$auth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes($pair))
Invoke-WebRequest -Uri $url -Headers @{ Authorization = "Basic $auth" } `
-OutFile agent.cjs -UseBasicParsing
Write-Host "Bundle : $((Get-Item agent.cjs).Length) octets"
- name: Exécuter le diagnostic
shell: powershell
env:
AGENT_TOKEN: ci-smoke-test
SERVER_URL: ws://127.0.0.1:1/ws/agent
run: |
$out = (& node agent.cjs --check 2>&1 | Out-String)
$code = $LASTEXITCODE
Write-Host $out
Write-Host "code de sortie du diagnostic : $code"
# On valide que le diagnostic s'est déroulé, pas son verdict.
foreach ($marker in @('obs-websocket', 'powershell')) {
if ($out -notmatch [regex]::Escape($marker)) {
throw "Diagnostic incomplet : marqueur $marker absent de la sortie"
}
}
Write-Host 'Bundle fonctionnel sous Windows.'
# --check sort en 1 : le serveur est injoignable par construction ici.
# Sans ce exit explicite, le wrapper PowerShell propage $LASTEXITCODE
# et fait échouer l'étape malgré des assertions vertes.
exit 0