diff --git a/.github/workflows/publish-gitea.yml b/.github/workflows/publish-gitea.yml new file mode 100644 index 00000000..edd0ef42 --- /dev/null +++ b/.github/workflows/publish-gitea.yml @@ -0,0 +1,152 @@ +# 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 + + - 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 -euo pipefail + if [ -z "${GITEA_TOKEN:-}" ]; then + echo "::error::segredo GITEA_TOKEN não configurado no repositório" + exit 1 + fi + + # A release `latest` é recriada a cada publicação: o electron-updater + # busca latest.yml antes de saber qual versão existe, então a URL não + # pode conter número de versão. + ID=$(curl -sf -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_API/releases/tags/$TAG" | python3 -c \ + "import json,sys;print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) + + if [ -n "$ID" ]; then + echo "release existente ($ID) — removendo anexos desta plataforma" + curl -sf -H "Authorization: token $GITEA_TOKEN" "$GITEA_API/releases/$ID/assets" \ + | python3 -c "import json,sys;[print(a['id'],a['name']) for a in json.load(sys.stdin)]" \ + | while read -r aid aname; do + case "$aname" in + *.exe|*.AppImage|*.deb|latest*.yml) + curl -s -o /dev/null -X DELETE -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_API/releases/$ID/assets/$aid" ;; + esac + done + else + ID=$(curl -sf -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H 'Content-Type: application/json' \ + -d "{\"tag_name\":\"$TAG\",\"name\":\"Última versão\",\"body\":\"Instaladores publicados automaticamente pelo CI.\"}" \ + "$GITEA_API/releases" | python3 -c "import json,sys;print(json.load(sys.stdin)['id'])") + echo "release criada: $ID" + 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") + echo "enviando $name" + curl -sf -X POST -H "Authorization: token $GITEA_TOKEN" \ + -F "attachment=@$f" "$GITEA_API/releases/$ID/assets?name=$name" > /dev/null + sent=$((sent+1)) + done + [ "$sent" -gt 0 ] || { echo "::error::nada para enviar — o build não produziu instaladores"; exit 1; } + echo "$sent arquivo(s) publicados" diff --git a/packages/desktop/electron-builder.yml b/packages/desktop/electron-builder.yml index f09bd18a..0c5cc7dd 100644 --- a/packages/desktop/electron-builder.yml +++ b/packages/desktop/electron-builder.yml @@ -22,11 +22,19 @@ asarUnpack: npmRebuild: false afterPack: ./scripts/afterPack.js publish: - # Fork: releases (and therefore the electron-updater feed) come from this - # repository, not upstream's. - - provider: github - owner: syncwrld - repo: resenhacord + # Updates are served from this fork's own Gitea, not from GitHub. + # + # GitHub is only the build machine — it has the Windows runners the native + # audio module needs. Its repository is private, and electron-updater against + # a private GitHub repo would need a token shipped inside the app, which is a + # leaked token. Gitea serves release assets to anyone, so no credential ends + # up in the installer. + # + # The tag is fixed at `latest` on purpose: electron-updater fetches + # latest.yml before it knows which version exists, so the URL cannot contain + # a version. CI replaces that release's assets on every publish. + - provider: generic + url: https://git.resenha.website/devsyncwrld/backspace/releases/download/latest/ protocols: - name: Backspace schemes: