OpenSSF Scorecard / Scorecard analysis (push) Waiting to run
CI / Build & test (Node 20) (push) Canceled after 0s
CI / Build & test (Node 24) (push) Canceled after 0s
CI / Build & test (push) Canceled after 0s
CodeQL / Analyze (javascript-typescript) (push) Canceled after 0s
Security / Secret scan (gitleaks) (push) Canceled after 0s
Security / Dependency scan (OSV-Scanner) (push) Canceled after 0s
Security / IaC/config scan (Trivy) (push) Canceled after 0s
Security / License compliance scan (Trivy) (push) Canceled after 0s
Linux got past pipewire and stopped on pulse/pulseaudio.h — the module compiles both backends, so both sets of headers are needed. Windows reached the upload and Gitea rejected the release creation with HTTP 422, 'invalid UTF-8 within /name': the JSON body carried accented text and the Windows runner's shell mangles the encoding on the way out. The payload is now ASCII, with a comment saying why, so it does not get 'improved' back into Portuguese.
184 lines
6.9 KiB
YAML
184 lines
6.9 KiB
YAML
# Compila no GitHub, publica no Gitea.
|
|
#
|
|
# O GitHub entra só como máquina de build — é dele que vêm os runners Windows
|
|
# de que o módulo nativo de áudio precisa. A distribuição fica no Gitea, que
|
|
# serve os arquivos a qualquer um: assim o electron-updater não precisa de
|
|
# credencial embutida no app, o que aconteceria com um repositório privado no
|
|
# GitHub.
|
|
name: Publicar no Gitea
|
|
|
|
on:
|
|
push:
|
|
tags: ['v*']
|
|
workflow_dispatch:
|
|
inputs:
|
|
tag:
|
|
description: Tag a publicar (ex. v1.1.0)
|
|
required: true
|
|
type: string
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
build:
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- os: windows-2022
|
|
args: --win --x64
|
|
- os: ubuntu-latest
|
|
args: --linux --x64
|
|
runs-on: ${{ matrix.os }}
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
|
|
|
- name: Install Linux build dependencies
|
|
if: runner.os == 'Linux'
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y \
|
|
libx11-dev libxtst-dev libxt-dev \
|
|
libxkbcommon-dev libxkbcommon-x11-dev libxkbfile-dev \
|
|
libxrandr-dev libxinerama-dev libx11-xcb-dev \
|
|
libpipewire-0.3-dev libpulse-dev
|
|
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
|
with:
|
|
version: 10.34.3
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
|
with:
|
|
node-version: 20
|
|
cache: pnpm
|
|
|
|
- name: Cache Electron binaries
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.cache/electron
|
|
~/.cache/electron-builder
|
|
~\AppData\Local\electron\Cache
|
|
~\AppData\Local\electron-builder\Cache
|
|
key: electron-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
restore-keys: electron-cache-${{ runner.os }}-
|
|
|
|
- name: Install dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Build shared package
|
|
run: pnpm --filter @backspace/shared build
|
|
|
|
# O postinstall termina em `|| console.warn` para quem não tem ferramentas
|
|
# de build. No CI isso esconde falha: o instalador sairia sem captura de
|
|
# áudio do sistema e ninguém saberia. Verifica-se o resultado.
|
|
- name: Verify native audio module compiled
|
|
if: runner.os == 'Windows'
|
|
shell: bash
|
|
run: |
|
|
found=$(find node_modules/.pnpm -path '*electron-native-screenshare*' -name '*.node' | head -5)
|
|
[ -n "$found" ] || { echo "::error::sem .node compilado — instalador sairia sem áudio do sistema"; exit 1; }
|
|
echo "$found"
|
|
|
|
- name: Compile desktop TypeScript
|
|
working-directory: packages/desktop
|
|
run: pnpm exec tsc
|
|
|
|
# --publish never: o electron-builder não sabe enviar para o Gitea. Ele
|
|
# gera os instaladores e o latest.yml (o índice que o app consulta), e o
|
|
# passo seguinte faz o upload.
|
|
- name: Build installers
|
|
working-directory: packages/desktop
|
|
run: pnpm exec electron-builder ${{ matrix.args }} --publish never
|
|
env:
|
|
CSC_IDENTITY_AUTO_DISCOVERY: "false"
|
|
|
|
- name: Upload to Gitea release
|
|
shell: bash
|
|
env:
|
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
GITEA_API: https://git.resenha.website/api/v1/repos/devsyncwrld/backspace
|
|
TAG: latest
|
|
run: |
|
|
set -uo pipefail
|
|
if [ -z "${GITEA_TOKEN:-}" ]; then
|
|
echo "::error::segredo GITEA_TOKEN não configurado"
|
|
exit 1
|
|
fi
|
|
|
|
# Sem -f e imprimindo o corpo: a versão anterior usava `curl -sf`, que
|
|
# engole a resposta de erro, então uma falha aqui só aparecia como um
|
|
# JSONDecodeError sem dizer o motivo.
|
|
api() {
|
|
local method=$1 path=$2; shift 2
|
|
curl -s -w '\n%{http_code}' -X "$method" \
|
|
-H "Authorization: token $GITEA_TOKEN" "$GITEA_API$path" "$@"
|
|
}
|
|
body() { sed '$d' <<<"$1"; }
|
|
code() { tail -n1 <<<"$1"; }
|
|
# jq em vez de python embutido: um heredoc multilinha dentro de um
|
|
# bloco literal de YAML encerra o bloco na primeira linha sem recuo.
|
|
json_id() { jq -r '.id // empty'; }
|
|
|
|
find_release() {
|
|
local r; r=$(api GET "/releases/tags/$TAG")
|
|
[ "$(code "$r")" = "200" ] && body "$r" | json_id || echo ""
|
|
}
|
|
|
|
# O corpo do POST fica em ASCII de proposito: o shell do runner
|
|
# Windows corrompe UTF-8 na requisicao e o Gitea recusa com
|
|
# "invalid UTF-8 within /name" (HTTP 422).
|
|
ID=$(find_release)
|
|
if [ -z "$ID" ]; then
|
|
R=$(api POST "/releases" -H 'Content-Type: application/json' \
|
|
-d "{\"tag_name\":\"$TAG\",\"name\":\"Latest build\",\"target_commitish\":\"main\",\"body\":\"Installers published automatically by CI.\"}")
|
|
if [ "$(code "$R")" = "201" ]; then
|
|
ID=$(body "$R" | json_id)
|
|
echo "release criada: $ID"
|
|
else
|
|
echo "criação retornou HTTP $(code "$R"): $(body "$R")"
|
|
# O outro job da matriz pode tê-la criado no mesmo instante.
|
|
ID=$(find_release)
|
|
[ -n "$ID" ] || { echo "::error::não foi possível obter nem criar a release"; exit 1; }
|
|
echo "release encontrada após corrida: $ID"
|
|
fi
|
|
else
|
|
echo "release existente: $ID"
|
|
fi
|
|
|
|
# Remove só os anexos que esta plataforma vai repor, para os dois jobs
|
|
# não apagarem o trabalho um do outro.
|
|
R=$(api GET "/releases/$ID/assets")
|
|
if [ "$(code "$R")" = "200" ]; then
|
|
body "$R" | jq -r '.[] | "\(.id) \(.name)"' | while read -r aid aname; do
|
|
case "$aname" in
|
|
${{ runner.os == 'Windows' && '*.exe|latest.yml' || '*.AppImage|*.deb|latest-linux.yml' }})
|
|
echo "removendo anexo antigo: $aname"
|
|
api DELETE "/releases/$ID/assets/$aid" > /dev/null ;;
|
|
esac
|
|
done
|
|
fi
|
|
|
|
shopt -s nullglob
|
|
sent=0
|
|
for f in packages/desktop/dist-electron/*.exe \
|
|
packages/desktop/dist-electron/*.AppImage \
|
|
packages/desktop/dist-electron/*.deb \
|
|
packages/desktop/dist-electron/latest*.yml; do
|
|
name=$(basename "$f")
|
|
R=$(api POST "/releases/$ID/assets?name=$name" -F "attachment=@$f")
|
|
if [ "$(code "$R")" != "201" ]; then
|
|
echo "::error::falha ao enviar $name — HTTP $(code "$R"): $(body "$R")"
|
|
exit 1
|
|
fi
|
|
echo "enviado: $name"
|
|
sent=$((sent+1))
|
|
done
|
|
[ "$sent" -gt 0 ] || { echo "::error::o build não produziu instaladores"; exit 1; }
|
|
echo "$sent arquivo(s) publicados"
|