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
Two separate failures on the first run. Linux could not compile the native module: it needs libpipewire-0.3-dev, which its README states and the apt list omitted. Windows built everything, native module verified, then died on the upload with a bare JSONDecodeError. The cause was curl -sf, which discards the error body, so a failed release creation surfaced as an empty pipe and no reason at all. The step now captures status and body and prints them, treats a failed create as possibly the other matrix job having just created it, and passes target_commitish so the tag can be created. Embedded python gave way to jq: a multi-line heredoc inside a YAML literal block ends the block at the first unindented line, which is how the file became invalid YAML in the first place.
181 lines
6.7 KiB
YAML
181 lines
6.7 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
|
|
|
|
- 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 ""
|
|
}
|
|
|
|
ID=$(find_release)
|
|
if [ -z "$ID" ]; then
|
|
R=$(api POST "/releases" -H 'Content-Type: application/json' \
|
|
-d "{\"tag_name\":\"$TAG\",\"name\":\"Última versão\",\"target_commitish\":\"main\",\"body\":\"Instaladores publicados pelo 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"
|