Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
de811ab
Planning
helgeklein Nov 1, 2026
585628e
Implementation
helgeklein Nov 1, 2026
1587f78
Implementation
helgeklein Nov 1, 2026
cd688a8
Implementation
helgeklein Nov 1, 2026
6564efa
Implementation
helgeklein Nov 1, 2026
5867009
APSW to SQLite
helgeklein Nov 1, 2026
12b9e51
Planning update
helgeklein Nov 1, 2026
e7427e0
Implementation
helgeklein Nov 1, 2026
bbae378
Implementation
helgeklein Nov 1, 2026
c376e51
Implementation
helgeklein Nov 1, 2026
13f5d9f
Implementation
helgeklein Nov 1, 2026
bf9acd3
Implementation
helgeklein Nov 1, 2026
6efcf64
Implementation
helgeklein Nov 1, 2026
d3347a3
Implementation
helgeklein Nov 1, 2026
c3da525
Implementation
helgeklein Nov 1, 2026
34aeeb9
Implementation
helgeklein Nov 1, 2026
54e9c50
Implementation
helgeklein Nov 1, 2026
c5e6e67
Implementation
helgeklein Nov 2, 2026
08e7dc3
Implementation
helgeklein Nov 2, 2026
9252723
Implementation
helgeklein Nov 2, 2026
800cd62
Implementation
helgeklein Nov 2, 2026
9a7cbea
Implementation
helgeklein Nov 2, 2026
ae94cf4
Implementation: new search activated
helgeklein Nov 2, 2026
16f4853
Plan deletion
helgeklein Nov 2, 2026
0507506
Version bump and review finding
helgeklein Nov 2, 2026
a5abb68
Todo update
helgeklein Nov 2, 2026
3af4977
Implementation: fixes and improvements
helgeklein Nov 3, 2026
5e3d6bb
Bugfix
helgeklein Nov 3, 2026
86f8b91
Test fix
helgeklein Nov 3, 2026
1632892
Companion lint fix
helgeklein Nov 3, 2026
8e80564
Dockerfile fix
helgeklein Nov 3, 2026
395a104
Companion test fixes
helgeklein Nov 3, 2026
2fd4a6f
Companion test fixes
helgeklein Nov 3, 2026
d5860f1
Companion test fixes
helgeklein Nov 3, 2026
efaf504
Companion test fixes
helgeklein Nov 3, 2026
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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Git LFS configuration for website media assets.
# Strict donor parity: source images, generated WebP derivatives, and
# Hugo's cached processed images are committed and fetched via Git LFS.
companion/src/** text eol=lf
archive_testdata/*.zip -text
website/assets/images/** filter=lfs diff=lfs merge=lfs -text
website/resources/_gen/images/** filter=lfs diff=lfs merge=lfs -text
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/devcontainer-validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ jobs:
- name: Report devcontainer runtime contract
shell: bash
run: |
docker run --rm --entrypoint sh sambee:devcontainer-validation -c '
docker run --rm --entrypoint sh -v "$PWD:/workspace:ro" -e PYTHONPATH=/workspace/backend sambee:devcontainer-validation -c '
python --version
python -c "import sqlite3; print(\"sqlite3 module: \" + sqlite3.sqlite_version)"
python -c "from app.services.directory_index import validate_sqlite_runtime; validate_sqlite_runtime(); print(\"Directory-index SQLite features verified\")"
dpkg-query -W -f="\${Package} \${Version}\\n" libsqlite3-0
'
1 change: 1 addition & 0 deletions .github/workflows/docker-image-validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ jobs:
docker exec sambee-smoke-${{ matrix.platform_id }} sh -c '
python --version
python -c "import sqlite3; print(\"sqlite3 module: \" + sqlite3.sqlite_version)"
python -c "from app.services.directory_index import validate_sqlite_runtime; validate_sqlite_runtime(); print(\"Directory-index SQLite features verified\")"
dpkg-query -W -f="\${Package} \${Version}\\n" libsqlite3-0
'

Expand Down
33 changes: 30 additions & 3 deletions .github/workflows/verify-companion-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ jobs:
needs: change-scope
if: needs.change-scope.outputs.requires-build == 'true'
runs-on: windows-latest
timeout-minutes: 45
env:
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
steps:
Expand All @@ -66,11 +67,20 @@ jobs:
- name: Run Companion checks
shell: pwsh
run: |
New-Item -ItemType Directory -Force companion/recovery-report | Out-Null
@{ source_sha = '${{ github.sha }}'; run_id = '${{ github.run_id }}'; target = 'windows-x64' } |
ConvertTo-Json | Set-Content companion/recovery-report/source.json
cd companion
npx tsc --noEmit
if ($LASTEXITCODE -ne 0) { throw 'Companion TypeScript check failed.' }
npm run lint
if ($LASTEXITCODE -ne 0) { throw 'Companion lint failed.' }
cd src-tauri
cargo test
if ($LASTEXITCODE -ne 0) { throw 'Native Rust tests failed.' }
cargo test --features recovery-verification server::directory_manager::tests -- --test-threads=2 2>&1 |
Tee-Object -FilePath ../recovery-report/native-tests.log
if ($LASTEXITCODE -ne 0) { throw 'Native recovery tests failed.' }

- name: Assert verification-only secret boundary
shell: pwsh
Expand Down Expand Up @@ -102,7 +112,20 @@ jobs:

- name: Build Windows x64 package
shell: pwsh
run: cd companion; npm run tauri -- build --config $env:TAURI_CI_CONFIG --target x86_64-pc-windows-msvc --bundles nsis -v
run: |
cd companion
npm run tauri -- build --config $env:TAURI_CI_CONFIG --target x86_64-pc-windows-msvc --features recovery-verification --bundles nsis -v
if ($LASTEXITCODE -ne 0) { throw 'Verification package build failed.' }

- name: Run packaged-process recovery smoke test
timeout-minutes: 5
shell: pwsh
run: |
python companion/scripts/recovery-smoke.py `
--executable companion/src-tauri/target/x86_64-pc-windows-msvc/release/sambee-companion.exe `
--report-dir companion/recovery-report `
--source-sha '${{ github.sha }}' --run-id '${{ github.run_id }}'
if ($LASTEXITCODE -ne 0) { throw 'Packaged recovery smoke test failed. Inspect recovery-report.' }

- name: Assert updater artifacts are absent
shell: pwsh
Expand All @@ -113,9 +136,13 @@ jobs:
}

- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9
if: ${{ always() }}
with:
name: companion-verification-windows-x64-${{ github.run_id }}
path: companion/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/
name: companion-verification-windows-x64-${{ github.sha }}-${{ github.run_id }}
path: |
companion/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/
companion/src-tauri/target/x86_64-pc-windows-msvc/release/sambee-companion.exe
companion/recovery-report/
if-no-files-found: error
retention-days: 7

Expand Down
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ COPY frontend/package*.json ./
COPY frontend/scripts ./scripts
RUN npm ci
COPY frontend/ ./
COPY shared/ /shared/
COPY VERSION /VERSION
COPY GIT_COMMIT /GIT_COMMIT
RUN npm run build
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.0.26
2.0.27
21 changes: 21 additions & 0 deletions backend/app/api/_smb_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from app.core.security import decrypt_password
from app.models.connection import Connection
from app.storage.smb import SMBBackend
from app.storage.smb_errors import SMBConnectionCapacityError, SMBConnectionRetired, SMBMutationUncertain

BACKEND_DISCONNECT_TIMEOUT_SECONDS = 5.0
ITEM_NOT_FOUND_CODE = "item_not_found"
Expand All @@ -20,6 +21,26 @@

def file_target_http_error(error: Exception, *, requires_file: bool = True) -> HTTPException:
"""Map physical item failures without exposing storage or operating-system details."""
if isinstance(error, SMBMutationUncertain):
return HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail={"code": "smb_mutation_uncertain", "message": str(error), "recoverable": False, "mutation_uncertain": True},
)
if isinstance(error, SMBConnectionRetired):
return HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail={
"code": "smb_recovery_busy",
"message": (
"SMB recovery capacity is occupied. Wait and retry. If workers remain blocked, restart the service."
if isinstance(error, SMBConnectionCapacityError)
else "SMB recovery is busy. Wait briefly, then retry the request."
),
"recoverable": True,
"retry_after_ms": error.retry_after_seconds * 1000,
},
headers={"Retry-After": str(error.retry_after_seconds)},
)
if isinstance(error, FileNotFoundError):
status_code, code, message = (
status.HTTP_404_NOT_FOUND,
Expand Down
Loading
Loading