Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 45 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,47 @@ jobs:
permissions:
contents: read

# Empacota ANTES da tag: config de empacotamento quebrada não pode deixar uma
# tag publicada sem binário. Se este job falhar, nada foi criado ainda.
empacotar:
name: Empacotar
needs: [preparar, ci]
runs-on: windows-latest
steps:
- uses: actions/checkout@v7

- uses: actions/setup-node@v7
with:
node-version: 24
cache: npm

- run: npm ci

- name: Garantir o binário do Electron
shell: pwsh
run: |
if (-not (Test-Path node_modules/electron/dist/electron.exe)) {
node node_modules/electron/install.js
}

- name: Instalador e portátil
run: npm run dist

# O E2E roda contra out/; só aqui o asar, o koffi desempacotado e a DLL em
# resources/ existem para ser testados.
- name: Fumaça no app empacotado
run: node --disable-warning=MODULE_TYPELESS_PACKAGE_JSON scripts/fumaca-empacotado.ts "dist/win-unpacked/WSLC UI.exe"

- uses: actions/upload-artifact@v7
with:
name: instaladores
path: dist/*.exe
if-no-files-found: error
retention-days: 7

publicar:
name: Publicar
needs: [preparar, ci]
needs: [preparar, ci, empacotar]
runs-on: ubuntu-latest
permissions:
contents: write
Expand All @@ -85,6 +123,11 @@ jobs:
- name: Gerar as notas
run: npm run patchnotes -- --notas --saida notas.md

- uses: actions/download-artifact@v7
with:
name: instaladores
path: dist

- name: Criar a tag anotada
env:
TAG: ${{ needs.preparar.outputs.tag }}
Expand All @@ -103,5 +146,5 @@ jobs:
extra=()
# Versão com sufixo (0.2.0-rc.1) sai marcada como pré-lançamento.
if [[ "$VERSAO" == *-* ]]; then extra+=(--prerelease); fi
gh release create "$TAG" --title "$TAG" --notes-file notas.md --verify-tag "${extra[@]}"
gh release create "$TAG" --title "$TAG" --notes-file notas.md --verify-tag "${extra[@]}" dist/*.exe
echo "[$TAG](https://github.com/$GITHUB_REPOSITORY/releases/tag/$TAG) publicada." >> "$GITHUB_STEP_SUMMARY"
5 changes: 0 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,3 @@ playwright-report/
# Artefatos de build incremental do TypeScript / TanStack Router
*.tsbuildinfo
.tanstack/

# SDK da Microsoft vendorizado — não redistribuído aqui.
# Baixe o .nupkg Microsoft.WSL.Containers: ver vendor/wslcsdk/README.md
vendor/wslcsdk/include/
vendor/wslcsdk/win-x64/
47 changes: 39 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,32 @@ UI: o app já mostra o ID e tem terminal próprio). Detalhes na regra 18 do ROAD

## Motor nativo (wslcsdk via FFI)

Além da CLI, o app carrega a **API C nativa** (`wslcsdk.dll`, vendorizada do NuGet
`Microsoft.WSL.Containers` em `vendor/wslcsdk/`) via **koffi**: toda a superfície do header está
vinculada em `src/main/services/wslc/native/bindings.ts`, e a view Sistema mostra o status do SDK
(versão, DLL, componentes faltando).
Além da CLI, o app carrega a **API C nativa** (`wslcsdk.dll`, do NuGet `Microsoft.WSL.Containers`)
via **koffi**: toda a superfície do header está vinculada em
`src/main/services/wslc/native/bindings.ts`, e a view Sistema mostra o status do SDK.

### A DLL vem junto — e são duas

O app **empacota** o SDK (`vendor/wslcsdk/`, licença MIT da Microsoft) e escolhe qual usar em tempo
de execução, porque a versão do SDK precisa **casar** com a do WSL instalado. Isso foi medido nesta
máquina, nas duas direções:

| | WSL 2.9.4 | WSL 2.9.9 |
| --------- | ------------------------------------------------ | ------------------------------------------------- |
| SDK 2.9.3 | funciona | `WSLC_E_SDK_UPDATE_NEEDED` já no `WslcGetVersion` |
| SDK 2.9.9 | **segfault** em `WslcGetSessionTerminationEvent` | funciona |

SDK novo demais é o caso perigoso: **nada no header denuncia** — a declaração da função que quebra é
byte a byte idêntica nas duas versões, e os 18 structs também. Não há binding que se defenda; o
processo simplesmente morre. Daí a regra em `native/bundled.ts`: usar a DLL mais nova que **não
passe** da versão do WSL. Quem quiser outra escolhe o arquivo na aba **Sistema** — o app sonda a DLL
(carrega, lê, descarrega) antes de aceitar, e a troca vale ao reabrir, porque a sessão viva segura
handles da atual.

A 2.9.9 também mudou **duas assinaturas** sem mudar nada visível (`WslcSessionAuthenticate` ganhou
`tokenType`; `WslcInstallWithDependencies` ganhou `components` e `options`), o que corromperia login
em registry e instalação guiada em silêncio. O `bindings.ts` detecta a ABI pela presença do símbolo
`WslcOpenContainer` e adapta as chamadas.

**Fases 1 a 7 (roadmap completo + cobertura 100%):** toggle **Motor: CLI / Nativo** em Sistema
(persistido em `settings.json`). No motor nativo o app mantém uma sessão própria (`WslcUi`,
Expand Down Expand Up @@ -276,6 +298,7 @@ npm test # vitest (test:watch, test:coverage)
npm run test:e2e # build + Playwright contra o app Electron (e2e/)
npm run check # typecheck + lint + format:check + test + patchnotes
npm run patchnotes # valida o patchnotes.json (--notas gera as notas da release)
npm run dist # instalador NSIS + portátil em dist/ (electron-builder)
npm run cenario:nativo # povoa a sessão nativa com o cenário de teste (app fechado)
```

Expand Down Expand Up @@ -486,17 +509,25 @@ Publicar uma versão é **subir o `version` do `package.json` e escrever as nota
3. roda o CI inteiro no estado da `main` que vai virar tag;
4. cria a tag anotada `v<versao>` e publica a release com o corpo gerado do `patchnotes.json`.

Versão com sufixo (`0.2.0-rc.1`) sai marcada como pré-lançamento. A release leva as notas e a tag,
sem instalador: a `wslcsdk.dll` da Microsoft não é redistribuída aqui (ver [Licença](#licença)).
Entre o passo 3 e o 4 o workflow **empacota** (instalador NSIS e portátil) e roda um teste de fumaça
que abre o `.exe` de verdade — asar, koffi desempacotado e DLL em `resources/` são três coisas que só
existem no app empacotado e que o E2E, rodando contra `out/`, nunca tocaria. Empacotar antes de criar
a tag é de propósito: uma config quebrada não pode deixar uma tag publicada sem binário.

Os dois `.exe` sobem como assets da release. **Não são assinados**: o SmartScreen vai avisar "editor
desconhecido" e exigir _Mais informações → Executar assim mesmo_.

Versão com sufixo (`0.2.0-rc.1`) sai marcada como pré-lançamento.

## Licença

Código deste repositório: **MIT** — ver [LICENSE](LICENSE).

### Componentes de terceiros

- **`Microsoft.WSL.Containers`** (`wslcsdk.dll` / `wslcsdk.h`) — SDK da Microsoft, em preview, sob a
licença do próprio pacote NuGet. **Não é redistribuído aqui**; baixe conforme
- **`Microsoft.WSL.Containers`** (`wslcsdk.dll`) — SDK da Microsoft, em preview, sob licença **MIT**
(© Microsoft Corporation). É **redistribuído aqui**, no repositório e dentro do instalador, com
`LICENSE.txt` e `NOTICE.txt` do próprio pacote — ver
[`vendor/wslcsdk/README.md`](vendor/wslcsdk/README.md).
- **`wslc.exe`** — parte do WSL, distribuída pela Microsoft. Este projeto apenas o consome; não o
inclui nem o modifica.
Expand Down
17 changes: 17 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,23 @@ Electron é por pasta de dados). No caminho, uma brecha real foi fechada: o
`setWindowOpenHandler` chamava `shell.openExternal` direto, fora do IPC — os
links de referência em Sistema abririam o navegador até em modo demo.

### O que a 2.9.9 do SDK mudou (medido)

`WslcOpenContainer` **levanta a limitação principal** documentada acima: um container criado numa
execução do app pode ser reaberto por nome ou ID em outra. Medido: soltar a sessão derruba o
container para EXITED, mas o registro fica; reabrir devolve um handle utilizável, e `Start` o põe
de volta em RUNNING. O app passou a lembrar em disco (`native/known.ts`) os containers que criou e a
reabri-los, em vez de apagá-los ao fechar — o que só era necessário porque, sem abrir por ID, eles
virariam órfãos invisíveis. Na ABI 2.9.3 o comportamento antigo continua.

Continua **não havendo enumeração** de containers: o único jeito de reencontrá-los é o app lembrar
os nomes.

E há uma armadilha nova, já tratada em `native/bundled.ts`: o SDK precisa CASAR com a versão do WSL.
SDK novo demais dá segfault (`WslcGetSessionTerminationEvent` num WSL mais antigo); SDK velho demais
é recusado com `WSLC_E_SDK_UPDATE_NEEDED` em qualquer chamada. Duas assinaturas também mudaram sem
mudança visível no header — ver `SdkAbi`.

**Cobertura 100% da superfície do wslc 2.9.4 (CLI e SDK)** — tudo que a CLI e
o wslcsdk.h expõem está na UI, implementado nos dois motores quando possível,
ou documentado com o motivo quando não: `session enter/run/shell` (interativos
Expand Down
Binary file added build/icon.ico
Binary file not shown.
Binary file added build/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading