-
Notifications
You must be signed in to change notification settings - Fork 0
open-wiki: English docs, CI with a 76% coverage floor, tagged releases, five decisions and a UI draft #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| name: CI | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| pull_request: | ||
| workflow_dispatch: | ||
|
|
||
| concurrency: | ||
| group: ci-${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| env: | ||
| # The coverage floor every app has to clear. vitest.shared.ts carries the same | ||
| # number so the failure happens locally first; scripts/ci/check-coverage.mjs | ||
| # re-reads it here so a package cannot configure its own gate away. | ||
| COVERAGE_THRESHOLD: "76" | ||
|
|
||
| jobs: | ||
| # One matrix entry per workspace package that has tests. Runs on Linux because | ||
| # it only reads package.json files — nothing is installed or built here. | ||
| discover: | ||
| name: Discover workspace | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| packages: ${{ steps.scan.outputs.packages }} | ||
| any: ${{ steps.scan.outputs.any }} | ||
| rust: ${{ steps.scan.outputs.rust }} | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 22 | ||
|
|
||
| - id: scan | ||
| run: node scripts/ci/workspace-packages.mjs >> "$GITHUB_OUTPUT" | ||
|
|
||
| # Windows because that is the only platform the product supports. | ||
| test: | ||
| name: test · ${{ matrix.package.path }} | ||
| needs: discover | ||
| if: needs.discover.outputs.any == 'true' | ||
| runs-on: windows-latest | ||
| strategy: | ||
| # One package failing must not hide the state of the others. | ||
| fail-fast: false | ||
| matrix: | ||
| package: ${{ fromJSON(needs.discover.outputs.packages) }} | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - uses: pnpm/action-setup@v4 | ||
|
|
||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 22 | ||
| cache: pnpm | ||
|
|
||
| - run: pnpm install --frozen-lockfile | ||
|
|
||
| - name: Test with coverage | ||
| run: pnpm --filter "${{ matrix.package.name }}" run ${{ matrix.package.script }} | ||
|
|
||
| - name: Enforce the coverage floor | ||
| run: node scripts/ci/check-coverage.mjs "${{ matrix.package.path }}" | ||
|
|
||
| - name: Upload the coverage report | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: coverage-${{ matrix.package.slug }} | ||
| path: ${{ matrix.package.path }}/coverage | ||
| if-no-files-found: ignore | ||
| retention-days: 7 | ||
|
|
||
| checks: | ||
| name: typecheck & lint | ||
| needs: discover | ||
| if: needs.discover.outputs.any == 'true' | ||
| runs-on: windows-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - uses: pnpm/action-setup@v4 | ||
|
|
||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 22 | ||
| cache: pnpm | ||
|
|
||
| - run: pnpm install --frozen-lockfile | ||
|
|
||
| - run: pnpm run typecheck | ||
|
|
||
| - run: pnpm run lint | ||
|
|
||
| # Skipped until crates/ exists. No coverage gate here: the recorder is driven | ||
| # through its JSON-RPC boundary, and a line count over WASAPI glue would | ||
| # measure the wrong thing. | ||
| rust: | ||
| name: rust | ||
| needs: discover | ||
| if: needs.discover.outputs.rust == 'true' | ||
| runs-on: windows-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - uses: dtolnay/rust-toolchain@stable | ||
| with: | ||
| components: clippy, rustfmt | ||
|
|
||
| - uses: Swatinem/rust-cache@v2 | ||
|
|
||
| - run: cargo fmt --all --check | ||
|
|
||
| - run: cargo clippy --all-targets --all-features -- -D warnings | ||
|
|
||
| - run: cargo test --locked --all-features | ||
|
|
||
| # The single check to require on the branch. Skipped jobs are fine — an empty | ||
| # workspace has nothing to test — but a failed or cancelled one is not. | ||
| ci: | ||
| name: CI | ||
| if: always() | ||
| needs: [discover, test, checks, rust] | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Fail if any job failed or was cancelled | ||
| if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') | ||
| run: exit 1 | ||
|
|
||
| - name: Report | ||
| run: | | ||
| echo "discover: ${{ needs.discover.result }}" | ||
| echo "test: ${{ needs.test.result }}" | ||
| echo "checks: ${{ needs.checks.result }}" | ||
| echo "rust: ${{ needs.rust.result }}" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| name: Release | ||
|
|
||
| on: | ||
| push: | ||
| tags: ["v*"] | ||
| workflow_dispatch: | ||
|
|
||
| concurrency: | ||
| # Never two runs publishing the same tag. | ||
| group: release-${{ github.ref }} | ||
| cancel-in-progress: false | ||
|
|
||
| permissions: | ||
| contents: write | ||
|
|
||
| jobs: | ||
| release: | ||
| name: Build and publish the installer | ||
| runs-on: windows-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- workflow ---'
cat -n .github/workflows/release.yml
printf '%s\n' '--- package manifests and scripts ---'
git ls-files '*package.json' '*pnpm-lock.yaml' '*npmrc' '*yarnrc*' | head -80
rg -n --glob 'package.json' '"(scripts|packageManager|engines|prepare|preinstall|install|postinstall)"' .
printf '%s\n' '--- checkout and credential references ---'
rg -n 'actions/checkout|persist-credentials|github\.token|GITHUB_TOKEN|\.git/config|pnpm install|electron-builder|nsis|makensis' .github package.json pnpm-workspace.yaml 2>/dev/null || trueRepository: protonspy/open-wiki Length of output: 6183 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- tracked workflow files ---'
git ls-files '.github/workflows/*'
printf '%s\n' '--- release-related files ---'
git ls-files | rg '(^|/)(package\.json|pnpm-workspace\.yaml|pnpm-lock\.yaml|.*release.*|.*package.*|.*electron.*|.*nsis.*)$' | head -120
printf '%s\n' '--- repository configuration ---'
git ls-files | rg '(^|/)(\.npmrc|\.pnpmfile\.cjs|\.gitignore|CODEOWNERS|README.*|CONTRIBUTING.*)$' | head -120Repository: protonspy/open-wiki Length of output: 468 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- root package.json ---'
cat -n package.json
printf '%s\n' '--- workspace definition ---'
cat -n pnpm-workspace.yaml
printf '%s\n' '--- tracked desktop files ---'
git ls-files 'apps/desktop/*' | head -80
printf '%s\n' '--- distribution ADR ---'
cat -n docs/adr/0009-distribution-through-github-releases.mdRepository: protonspy/open-wiki Length of output: 4839 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
workflow = Path(".github/workflows/release.yml").read_text()
checkout = workflow.index("actions/checkout@v4")
install = workflow.index("pnpm install --frozen-lockfile")
package = workflow.index('pnpm --filter "`@project-wiki/desktop`" run package')
assert checkout < install < package
assert "persist-credentials: false" not in workflow
assert "contents: write" in workflow
assert "GH_TOKEN: ${{ github.token }}" in workflow
print("checkout precedes install and packaging")
print("persist-credentials override: absent")
print("job permission: contents: write")
print("release check token: github.token")
PYRepository: protonspy/open-wiki Length of output: 300 Disable checkout credential persistence. This workflow grants Set 🧰 Tools🪛 zizmor (1.28.0)[warning] 21-24: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false (artipacked) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
|
|
||
| # Until task 10.1 lands there is nothing to package. Saying so beats a | ||
| # pnpm error about a filter matching no project. | ||
| - name: Check the desktop app exists | ||
| shell: pwsh | ||
| run: | | ||
| if (-not (Test-Path "apps/desktop/package.json")) { | ||
| Write-Error "apps/desktop does not exist yet — there is nothing to release (plan task 10.1)." | ||
| exit 1 | ||
| } | ||
|
|
||
| - name: Check the tag matches the app version | ||
| if: startsWith(github.ref, 'refs/tags/v') | ||
| shell: pwsh | ||
| run: | | ||
| $tag = "${{ github.ref_name }}".TrimStart("v") | ||
| $version = (Get-Content "apps/desktop/package.json" -Raw | ConvertFrom-Json).version | ||
| if ($tag -ne $version) { | ||
| Write-Error "tag v$tag does not match apps/desktop/package.json version $version" | ||
|
Comment on lines
+37
to
+40
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- workflow files ---'
git ls-files '.github/workflows/release.yml'
printf '%s\n' '--- relevant workflow sections ---'
sed -n '1,90p' .github/workflows/release.yml
printf '%s\n' '--- all github.ref_name uses ---'
rg -n -C 4 'github\.ref_name|RELEASE_TAG|ConvertFrom-Json|TrimStart' .github/workflows/release.ymlRepository: protonspy/open-wiki Length of output: 4892 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
import shutil
import subprocess
candidates = {
"tag-injection": 'v"; Write-Output "TAG_INJECTION',
"tag-injection-with-version-prefix": 'v1.2.3"; Write-Output "TAG_INJECTION',
"release-command-injection": 'v"; Write-Output "RELEASE_INJECTION',
}
workflow_lines = {
"version-check": '$tag = "${TAG}".TrimStart("v")',
"release-view": 'gh release view "${TAG}" 2>$null',
"existing-release-error": 'Write-Error "release ${TAG} already exists"',
"no-release-output": 'Write-Host "no release for ${TAG} yet"',
}
for label, tag in candidates.items():
ref = f"refs/tags/{tag}"
result = subprocess.run(
["git", "check-ref-format", ref],
text=True,
capture_output=True,
)
print(f"{label}: git_ref_name_allowed={result.returncode == 0}")
if result.returncode == 0:
for name, template in workflow_lines.items():
print(f" {name}: {template.replace('${TAG}', tag)}")
print("pwsh_available:", bool(shutil.which("pwsh")))
print("powershell_available:", bool(shutil.which("powershell")))
PYRepository: protonspy/open-wiki Length of output: 364 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
import subprocess
tags = [
'v$(Write-Output TAG_INJECTION)',
'v1.2.3$(Write-Output TAG_INJECTION)',
'v`$(Write-Output TAG_INJECTION)',
'v$(Get-Date)',
'v${HOME}',
]
templates = {
"version-check": '$tag = "${TAG}".TrimStart("v"); Write-Output "VERSION_DONE"',
"release-view": 'Write-Output "before"; Write-Output "${TAG}"; Write-Output "after"',
"existing-release-error": 'Write-Error "release ${TAG} already exists" 2>$null',
"no-release-output": 'Write-Host "no release for ${TAG} yet"',
}
for tag in tags:
allowed = subprocess.run(
["git", "check-ref-format", f"refs/tags/{tag}"],
capture_output=True,
).returncode == 0
print(f"\nTAG {tag!r} allowed={allowed}")
if not allowed:
continue
for name, template in templates.items():
script = template.replace("${TAG}", tag)
result = subprocess.run(
["pwsh", "-NoProfile", "-NonInteractive", "-Command", script],
text=True,
capture_output=True,
)
print(f"{name}: exit={result.returncode}")
print(" stdout:", repr(result.stdout.strip()))
print(" stderr:", repr(result.stderr.strip()))
PYRepository: protonspy/open-wiki Length of output: 951 🏁 Script executed: #!/bin/bash
set -eu
pwsh -NoProfile -NonInteractive -Command @'
$tag = 'v$(Get-Date)'
function gh { param([Parameter(ValueFromRemainingArguments=$true)]$Args); Write-Output ("GH_ARGS=" + ($Args -join '|')) }
Write-Output '--- interpolated workflow source ---'
$tag = "v$(Get-Date)".TrimStart("v")
Write-Output ("TAG_VALUE=" + $tag)
gh release view "v$(Get-Date)" 2>$null
Write-Error "release v$(Get-Date) already exists" 2>$null
Write-Host "no release for v$(Get-Date) yet"
Write-Output '--- environment value ---'
$env:RELEASE_TAG = 'v$(Get-Date)'
$tag = ($env:RELEASE_TAG).TrimStart("v")
Write-Output ("TAG_VALUE=" + $tag)
gh release view "$env:RELEASE_TAG" 2>$null
Write-Error "release $env:RELEASE_TAG already exists" 2>$null
Write-Host "no release for $env:RELEASE_TAG yet"
'@Repository: protonspy/open-wiki Length of output: 389 🏁 Script executed: #!/bin/bash
set -eu
pwsh -NoProfile -NonInteractive -Command - <<'PS'
$tag = 'v$(Get-Date)'
function gh {
param([Parameter(ValueFromRemainingArguments=$true)]$Args)
Write-Output ("GH_ARGS=" + ($Args -join '|'))
}
Write-Output '--- interpolated workflow source ---'
$tag = "v$(Get-Date)".TrimStart("v")
Write-Output ("TAG_VALUE=" + $tag)
gh release view "v$(Get-Date)" 2>$null
Write-Error "release v$(Get-Date) already exists" 2>$null
Write-Host "no release for v$(Get-Date) yet"
Write-Output '--- environment value ---'
$env:RELEASE_TAG = 'v$(Get-Date)'
$tag = ($env:RELEASE_TAG).TrimStart("v")
Write-Output ("TAG_VALUE=" + $tag)
gh release view "$env:RELEASE_TAG" 2>$null
Write-Error "release $env:RELEASE_TAG already exists" 2>$null
Write-Host "no release for $env:RELEASE_TAG yet"
PSRepository: protonspy/open-wiki Length of output: 420 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
import json
import pathlib
import subprocess
package = pathlib.Path("apps/desktop/package.json")
version = json.loads(package.read_text())["version"] if package.is_file() else "1.2.3"
tag = f"v$(Write-Output('{version}'))"
allowed = subprocess.run(
["git", "check-ref-format", f"refs/tags/{tag}"],
capture_output=True,
).returncode == 0
print("package_version:", version)
print("tag:", tag)
print("git_ref_name_allowed:", allowed)
PY
pwsh -NoProfile -NonInteractive -Command - <<'PS'
$version = '1.2.3'
$tag = "v$(Write-Output('1.2.3'))".TrimStart("v")
Write-Output ("tag=" + $tag)
Write-Output ("matches=" + ($tag -eq $version))
PSRepository: protonspy/open-wiki Length of output: 261 Pass A valid tag can execute a PowerShell subexpression during source interpolation and still produce a version-matching 🧰 Tools🪛 zizmor (1.28.0)[error] 37-37: code injection via template expansion (template-injection): may expand into attacker-controllable code (template-injection) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
| exit 1 | ||
| } | ||
| Write-Host "releasing $version" | ||
|
|
||
| - name: Refuse to republish an existing release | ||
| if: startsWith(github.ref, 'refs/tags/v') | ||
| shell: pwsh | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| run: | | ||
| # A published release has been downloaded; deleting it does not undo that. | ||
| gh release view "${{ github.ref_name }}" 2>$null | ||
| if ($LASTEXITCODE -eq 0) { | ||
| Write-Error "release ${{ github.ref_name }} already exists — bump the version and tag again" | ||
| exit 1 | ||
| } | ||
| Write-Host "no release for ${{ github.ref_name }} yet" | ||
|
Comment on lines
+45
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- workflow context ---'
sed -n '1,100p' .github/workflows/release.yml
printf '%s\n' '--- ADR and release-related references ---'
rg -n -C 3 'ADR.?0009|exactly.?once|gh release|release create|publish' .github README.md docs . 2>/dev/null | head -250Repository: protonspy/open-wiki Length of output: 12761 🌐 Web query:
💡 Result: The GitHub CLI (gh) follows standard exit code conventions, where 0 indicates success and 1 indicates a general failure [1]. While some legacy man pages may list specific exit codes such as 4 for authentication [2][3], the current official GitHub CLI documentation defines its exit codes as follows [1]: 0: Successful execution. 1: Command failed for any reason. 2: Command was running but was cancelled. Regarding your specific queries: Exit Code 1: If a command like gh release view fails (for instance, due to a 404 "Not Found" error when a release does not exist), the CLI exits with code 1 [4][5]. It is common practice in automation scripts to check for these failures [5]. Authentication and Rate Limits: There is no distinct exit code for authentication errors or API rate limit exceeded errors in the current documentation; these are treated as command failures and result in an exit code of 1 [1]. Note on documentation: While you may encounter older or community-generated documentation suggesting an exit code of 4 for authentication [2][3], the official CLI help currently directs users to the standard convention where 1 represents all failures [1]. When writing scripts, you should generally rely on exit code 0 for success and non-zero (typically 1) for any failure, rather than relying on granular exit code differentiation [4]. Citations:
🌐 Web query:
💡 Result: When using softprops/action-gh-release (v2), the action is designed to automatically detect if a GitHub release already exists for the specified tag [1][2]. If an existing release is found, the action will update it rather than creating a new one [1][3]. Regarding the behavior of release data and files: 1. Release Info: If release metadata fields (such as name, body, or prerelease status) are not explicitly defined in the action configuration, the existing release will retain its original information [1][2]. 2. File Overwriting: The action includes an Citations:
🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- ADR details ---'
sed -n '55,70p' docs/adr/0009-distribution-through-github-releases.md
printf '%s\n' '--- workflow publish configuration ---'
sed -n '88,120p' .github/workflows/release.yml
printf '%s\n' '--- repository references to action-gh-release inputs ---'
rg -n -C 3 'softprops/action-gh-release|overwrite_files|fail_on_unmatched_files|tag_name|draft|make_latest|overwrite' .github docs plans README.md . 2>/dev/null | head -250Repository: protonspy/open-wiki Length of output: 6824 Fail closed on release lookup errors. Continue only for an explicit 404. Authentication, rate-limit, and network failures must stop the workflow. 🧰 Tools🪛 zizmor (1.28.0)[error] 52-52: code injection via template expansion (template-injection): may expand into attacker-controllable code (template-injection) [error] 54-54: code injection via template expansion (template-injection): may expand into attacker-controllable code (template-injection) [error] 57-57: code injection via template expansion (template-injection): may expand into attacker-controllable code (template-injection) 🤖 Prompt for AI Agents |
||
|
|
||
| - uses: pnpm/action-setup@v4 | ||
|
|
||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 22 | ||
| cache: pnpm | ||
|
|
||
| - run: pnpm install --frozen-lockfile | ||
|
|
||
| - name: Build the installer | ||
| shell: pwsh | ||
| env: | ||
| # electron-builder picks these up on its own and signs when they are | ||
| # set. Absent, it builds unsigned — see adr:0009. | ||
| CSC_LINK: ${{ secrets.WINDOWS_CERTIFICATE }} | ||
| CSC_KEY_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }} | ||
| # Publishing is this workflow's job, not electron-builder's. | ||
| GH_TOKEN: "" | ||
| run: pnpm --filter "@project-wiki/desktop" run package | ||
|
|
||
| - name: Collect the artifacts and their checksums | ||
| id: collect | ||
| shell: pwsh | ||
| run: | | ||
| $dir = "apps/desktop/release" | ||
| $installers = @(Get-ChildItem -Path $dir -Filter *.exe -File -ErrorAction SilentlyContinue) | ||
| if ($installers.Count -eq 0) { | ||
| Write-Error "no .exe found in $dir — the package script produced no installer" | ||
| exit 1 | ||
| } | ||
| $installers | ForEach-Object { Write-Host " $($_.Name) $([math]::Round($_.Length / 1MB, 1)) MB" } | ||
| Get-FileHash -Algorithm SHA256 $installers.FullName | | ||
| ForEach-Object { "$($_.Hash.ToLower()) $(Split-Path $_.Path -Leaf)" } | | ||
| Out-File -FilePath "$dir/SHA256SUMS.txt" -Encoding utf8 | ||
|
Comment on lines
+84
to
+92
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- workflow files ---'
git ls-files '.github/workflows/release.yml' 'ADR*' '*ADR*'
printf '%s\n' '--- release workflow ---'
cat -n .github/workflows/release.yml | sed -n '1,125p'
printf '%s\n' '--- ADR 0009 references ---'
rg -n -i -C 4 '0009|single NSIS|NSIS|installer|electron-builder' . --glob '!node_modules' --glob '!dist' --glob '!build'Repository: protonspy/open-wiki Length of output: 13756 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- package and build configuration references ---'
rg -n -i -C 5 'electron-builder|nsis|win\.target|portable|uninstaller|artifactName|publish|package script' . --glob '!node_modules' --glob '!dist' --glob '!build'
printf '%s\n' '--- workflow expressions and artifact publication ---'
rg -n -C 5 'installers|SHA256SUMS|upload-artifact|Get-ChildItem|package|artifact|\.exe' .github/workflows/release.ymlRepository: protonspy/open-wiki Length of output: 13245 🏁 Script executed: #!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
p = Path(".github/workflows/release.yml")
lines = p.read_text().splitlines()
for start, end in [(70, 110)]:
for n in range(start, min(end, len(lines)) + 1):
print(f"{n:4}: {lines[n-1]}")
PY
printf '%s\n' '--- candidate ADR files ---'
find . -type f \( -iname '*adr*' -o -iname '*decision*' \) -not -path '*/node_modules/*' -not -path '*/dist/*' -not -path '*/build/*' -printRepository: protonspy/open-wiki Length of output: 2368 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- desktop package files ---'
git ls-files 'apps/desktop/*' 'package.json' 'pnpm-workspace.yaml' | sed -n '1,160p'
printf '%s\n' '--- package scripts and electron-builder configuration ---'
rg -n -i -C 8 '"package"|"build"|electron-builder|nsis|win[[:space:]]*:' \
--glob 'package.json' --glob '*.json' --glob '*.yaml' --glob '*.yml' \
apps package.json pnpm-workspace.yaml 2>/dev/null || true
printf '%s\n' '--- release references ---'
rg -n -C 4 'apps/desktop/release|SHA256SUMS|single NSIS|expected exactly one' \
. --glob '!node_modules' --glob '!dist' --glob '!build'Repository: protonspy/open-wiki Length of output: 2773 Require exactly one NSIS installer. The workflow treats every 🤖 Prompt for AI Agents |
||
| Get-Content "$dir/SHA256SUMS.txt" | ||
|
|
||
| - name: Publish the release | ||
| if: startsWith(github.ref, 'refs/tags/v') | ||
| uses: softprops/action-gh-release@v2 | ||
| with: | ||
| files: | | ||
| apps/desktop/release/*.exe | ||
| apps/desktop/release/SHA256SUMS.txt | ||
| generate_release_notes: true | ||
| # A tag carrying a suffix — v0.1.0-beta.1 — is not a stable release. | ||
| prerelease: ${{ contains(github.ref_name, '-') }} | ||
| fail_on_unmatched_files: true | ||
|
|
||
| # workflow_dispatch builds without a tag: useful for checking the packaging | ||
| # still works without publishing anything. | ||
| - name: Upload the installer as a build artifact | ||
| if: ${{ !startsWith(github.ref, 'refs/tags/v') }} | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: installer-${{ github.sha }} | ||
| path: apps/desktop/release/* | ||
| retention-days: 7 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| --- | ||
| status: accepted | ||
| --- | ||
|
|
||
| # 0001 · No backend: BYOK, no accounts, no telemetry | ||
|
|
||
| ## Context | ||
|
|
||
| The application handles two categories of sensitive data: meeting audio and a project's | ||
| internal documentation. It needs transcription, which is a third-party service. The | ||
| question is who talks to that service — a backend of ours, or the user's machine. | ||
|
|
||
| A backend would bring real convenience: a single credential, aggregated billing, | ||
| configuration changes without a release. It would also bring the position of data | ||
| processor under the LGPD, infrastructure cost proportional to usage, and the obligation | ||
| to answer what happens to the audio of a confidential meeting that passed through our | ||
| servers. | ||
|
|
||
| ## Decision | ||
|
|
||
| There is no backend. The user supplies their own transcription credential, the | ||
| application talks to the provider directly, and there is no account, no authentication | ||
| of our own and no telemetry of any kind — including anonymous crash telemetry. | ||
|
Comment on lines
+21
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win Use one authentication vocabulary across the ADRs. The statements in
📍 Affects 2 files
🤖 Prompt for AI Agents |
||
|
|
||
| ## Consequences | ||
|
|
||
| The audio and the documents never pass through a server of ours, which leaves us in the | ||
| position of a software vendor rather than a data processor. That simplifies the LGPD | ||
| position considerably — and stops being true the instant any hosted component is added. | ||
| This is why it is an ADR and not a line in a README: the cost is not adding the | ||
| component, it is losing the position. | ||
|
|
||
| The cost is onboarding: the user has to create an account somewhere else and paste a | ||
| credential before the first recording works. The application validates the credential on | ||
| the spot precisely because a wrong key discovered after an hour of recording is the worst | ||
| possible way to discover it. | ||
|
|
||
| Without telemetry, we do not know what breaks on anyone's machine. Diagnosis depends on | ||
| the local log and on what the user reports. | ||
|
|
||
| There is a way out for anyone who does not want even that: transcribe locally, with no | ||
| credential at all. It exists because this ADR is only convincing if the privacy argument | ||
| has a path that depends on trusting no third party whatsoever. | ||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
checksjob never runs while no package declares a test script.if: needs.discover.outputs.any == 'true'gates thetypecheck & lintjob on whether any workspace package has atest/test:coveragescript (perscripts/ci/workspace-packages.mjs). Per the PR's own status, no packages exist yet, soanyisfalsetoday. This means thechecksjob — which runspnpm run typecheckandpnpm run lintat the root, validating the verytsconfig.base.json/tsconfig.json/vitest.shared.tsfiles this PR adds — is skipped entirely, and theciaggregator job (lines 124-134) treats a skipped job as passing. The root typecheck/lint commands already tolerate an empty workspace (--if-present), so this job doesn't need to depend on test-script presence at all.🐛 Proposed fix
checks: name: typecheck & lint needs: discover - if: needs.discover.outputs.any == 'true' runs-on: windows-latest📝 Committable suggestion
🤖 Prompt for AI Agents