Files
rclone-dropbox-bisync/windows/bootstrap.ps1
T
f-denkena a70a66defa windows/bootstrap: repair empty winget source before install
0x8a15000f ("Data required by the source is missing") aborts Git.Git /
GoLang.Go install on a virgin DesktopAppInstaller cache, then iex dies
on missing git. Reset+update the source once, pin --source winget, retry
without the pin, then fail loudly if the index is still empty.
2026-09-12 13:14:33 +02:00

150 lines
6.0 KiB
PowerShell

# rclone-dropbox-bisync Windows setup
# Paths: D: (volume label DataStorage) \Dropbox and \rclone-bisync
# Paste into PowerShell, or: irm <raw-url>/windows/bootstrap.ps1 | iex
$ErrorActionPreference = 'Stop'
$GitBase = 'https://git.denkena-consulting.com/f-denkena'
$PackRepo = "$GitBase/rclone-dropbox-bisync.git"
$RcloneRepo = "$GitBase/rclone.git"
$Src = Join-Path $env:LOCALAPPDATA 'rclone-dropbox-bisync\src'
$Dest = Join-Path $env:LOCALAPPDATA 'rclone-dropbox-bisync'
$Dropbox = 'D:\Dropbox'
$Workdir = 'D:\rclone-bisync'
function Refresh-Path {
$env:Path = [Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' +
[Environment]::GetEnvironmentVariable('Path', 'User')
}
# 0x8a15000f: winget source index missing/corrupt (common on a fresh DesktopAppInstaller).
function Repair-WingetSource {
if ($script:WingetSourceRepaired) { return }
$winget = Get-Command winget -ErrorAction SilentlyContinue
if (-not $winget) { return }
$prev = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
& winget source reset --force --disable-interactivity 2>&1 | Out-Null
& winget source update --disable-interactivity 2>&1 | Out-Null
} finally {
$ErrorActionPreference = $prev
}
$script:WingetSourceRepaired = $true
}
function Install-WithWinget($WingetId) {
Repair-WingetSource
$wingetArgs = @(
'install', '-e', '--id', $WingetId,
'--accept-package-agreements', '--accept-source-agreements',
'--disable-interactivity', '--source', 'winget'
)
$prev = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
& winget @wingetArgs
if ($LASTEXITCODE -eq 0) { return }
# Index still empty: reset once more, then retry without pinning the source
# (some hosts only have msstore until cdn.winget.microsoft.com is reachable).
& winget source reset --force --disable-interactivity 2>&1 | Out-Null
& winget source update --disable-interactivity 2>&1 | Out-Null
& winget install -e --id $WingetId --accept-package-agreements --accept-source-agreements --disable-interactivity
if ($LASTEXITCODE -eq 0) { return }
} finally {
$ErrorActionPreference = $prev
}
throw "winget failed to install $WingetId (exit $LASTEXITCODE / 0x8a15000f = empty source cache). Run: winget source reset --force; winget source update"
}
function Ensure-Cmd($Name, $WingetId) {
if (Get-Command $Name -ErrorAction SilentlyContinue) { return }
if (-not (Get-Command winget -ErrorAction SilentlyContinue)) {
throw "Need $Name on PATH (or winget to install $WingetId)"
}
Install-WithWinget $WingetId
Refresh-Path
if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
throw "$Name still missing after winget install $WingetId; open a new PowerShell and retry"
}
}
Ensure-Cmd git Git.Git
Ensure-Cmd go GoLang.Go
New-Item -ItemType Directory -Force -Path $Src, (Join-Path $Dest 'bin') | Out-Null
if (-not (Test-Path (Join-Path $Src 'packaging\.git'))) {
git clone --depth 1 $PackRepo (Join-Path $Src 'packaging')
} else {
git -C (Join-Path $Src 'packaging') pull --ff-only
}
if (-not (Test-Path (Join-Path $Src 'rclone\.git'))) {
git clone --depth 1 --branch bisync-delta-list $RcloneRepo (Join-Path $Src 'rclone')
} else {
git -C (Join-Path $Src 'rclone') fetch origin bisync-delta-list
git -C (Join-Path $Src 'rclone') checkout bisync-delta-list
git -C (Join-Path $Src 'rclone') pull --ff-only
}
Write-Host 'Building rclone (forked)...'
Push-Location (Join-Path $Src 'rclone')
$env:CGO_ENABLED = '0'
go build -trimpath -o (Join-Path $Dest 'bin\rclone.exe') .
Pop-Location
Write-Host 'Building rclone-bisyncd...'
Push-Location (Join-Path $Src 'packaging\cmd\rclone-bisyncd')
go build -trimpath -o (Join-Path $Dest 'bin\rclone-bisyncd.exe') .
Pop-Location
Copy-Item -Force (Join-Path $Src 'packaging\windows\tray.ps1') (Join-Path $Dest 'tray.ps1')
if (-not (Test-Path 'D:\')) { throw 'D: is missing (expected DataStorage volume)' }
if (-not (Test-Path $Dropbox)) { throw "Dropbox folder not found: $Dropbox" }
New-Item -ItemType Directory -Force -Path $Workdir | Out-Null
$testFile = Join-Path $Dropbox 'RCLONE_TEST'
if (-not (Test-Path $testFile)) { New-Item -ItemType File -Path $testFile | Out-Null }
$cfgPath = Join-Path $Dest 'config.json'
@{
enabled = $false
path1 = $Dropbox
path2 = 'dropbox:'
workdir = $Workdir
interval_minutes = 15
rclone = (Join-Path $Dest 'bin\rclone.exe')
check_filename = 'RCLONE_TEST'
session_name = 'datastorage-dropbox'
} | ConvertTo-Json | Set-Content -Encoding UTF8 $cfgPath
$env:RCLONE_BISYNC_CONFIG = $cfgPath
$bin = Join-Path $Dest 'bin'
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
if ($userPath -notlike "*$bin*") {
[Environment]::SetEnvironmentVariable('Path', "$bin;$userPath", 'User')
}
$run = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run'
$tray = Join-Path $Dest 'tray.ps1'
New-ItemProperty -Path $run -Name 'rclone-dropbox-bisync-tray' -PropertyType String -Force `
-Value "powershell.exe -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$tray`"" | Out-Null
$daemon = Join-Path $Dest 'bin\rclone-bisyncd.exe'
schtasks /Delete /F /TN 'rclone-dropbox-bisync' 2>$null
schtasks /Create /F /TN 'rclone-dropbox-bisync' /SC ONLOGON /RL LIMITED /TR "`"$daemon`"" | Out-Null
New-ItemProperty -Path $run -Name 'rclone-dropbox-bisync-daemon' -PropertyType String -Force `
-Value "`"$daemon`"" | Out-Null
Start-Process powershell.exe -WindowStyle Hidden -ArgumentList @(
'-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $tray
)
Start-Process $daemon -WindowStyle Hidden
Write-Host @"
Configured:
path1 $Dropbox (D: DataStorage)
workdir $Workdir
session datastorage-dropbox (shared with Linux)
daemon looping; 15 min after each run; mkdir lock
If dropbox: has RCLONE_TEST and listings are missing, the daemon
dry-run --resync then live --resync on its own. Never after a failed run.
"@