diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml deleted file mode 100644 index 6cd57d7..0000000 --- a/.gitea/workflows/ci.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: CI - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - quality: - name: Typecheck + Tests - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Typecheck (main + preload + shared) - run: npm run typecheck:node - - - name: Typecheck (renderer) - run: npm run typecheck:web - - - name: Run unit tests - run: npm run test:run - - build: - name: Build (Windows) - runs-on: windows-latest - needs: quality - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Build production bundle (no installer) - run: npm run build - - - name: Smoke-test unpacked build - run: npm run dist:dir - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Upload unpacked artifact - uses: actions/upload-artifact@v4 - with: - name: exercise-reminder-unpacked - path: release/win-unpacked/ - retention-days: 7 diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml deleted file mode 100644 index cfd33ad..0000000 --- a/.gitea/workflows/release.yml +++ /dev/null @@ -1,75 +0,0 @@ -name: Release - -on: - push: - tags: - - 'v*.*.*' - -jobs: - release: - name: Build installer + publish release - runs-on: windows-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Verify version matches tag - shell: pwsh - run: | - $tag = "${{ gitea.ref_name }}" - $expected = $tag.TrimStart('v') - $actual = (Get-Content package.json | ConvertFrom-Json).version - if ($expected -ne $actual) { - Write-Error "Tag $tag does not match package.json version $actual" - exit 1 - } - Write-Host "Version match: $actual" - - - name: Typecheck - run: npm run typecheck - - - name: Run unit tests - run: npm run test:run - - - name: Build NSIS installer - run: npm run dist - env: - # electron-builder uses this when --publish flag is set; we publish - # to a Gitea release manually below to avoid hard-coupling. - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Generate release notes from commits - id: notes - shell: pwsh - run: | - $tag = "${{ gitea.ref_name }}" - $prev = git describe --tags --abbrev=0 "$tag^" 2>$null - if ($prev) { - $log = git log --pretty=format:"- %s" "$prev..$tag" - } else { - $log = git log --pretty=format:"- %s" "$tag" - } - $notes = "### Изменения`n`n$log`n`n---`n`nУстановщик ниже — запустить и следовать мастеру. Если приложение уже стояло — обновится поверх с сохранением настроек." - $encoded = $notes -replace "`r?`n", "%0A" - "notes=$encoded" | Out-File -FilePath $env:GITEA_OUTPUT -Append - - - name: Create Gitea release with artifacts - uses: akkuman/gitea-release-action@v1 - with: - server_url: ${{ gitea.server_url }} - token: ${{ secrets.GITEA_TOKEN }} - name: 'Exercise Reminder ${{ gitea.ref_name }}' - body: ${{ steps.notes.outputs.notes }} - files: | - release/Exercise-Reminder-Setup-*.exe - release/Exercise-Reminder-Setup-*.exe.blockmap - release/latest.yml diff --git a/scripts/upload-release-assets.ps1 b/scripts/upload-release-assets.ps1 index 4f8dc2c..dc72a26 100644 --- a/scripts/upload-release-assets.ps1 +++ b/scripts/upload-release-assets.ps1 @@ -157,21 +157,66 @@ if ($curlCmd) { } } +$maxRetries = 4 +$backoffs = @(15, 45, 120, 300) # seconds between attempts + foreach ($asset in @($installer, $blockmap, $manifest)) { $name = Split-Path $asset -Leaf $size = (Get-Item $asset).Length - Write-Host ("Uploading {0} ({1:N1} MB)..." -f $name, ($size / 1MB)) -ForegroundColor Cyan $uri = "$apiBase/repos/$repoOwner/$repoName/releases/$($release.id)/assets?name=$([uri]::EscapeDataString($name))" - # -f: fail on HTTP errors; -s -S: silent but show errors; --data-binary @file - & $curl ` - --fail-with-body ` - --silent --show-error ` - -H "Authorization: token $env:GITEA_TOKEN" ` - -H "Content-Type: application/octet-stream" ` - --data-binary "@$asset" ` - $uri - if ($LASTEXITCODE -ne 0) { - Write-Error "Upload failed for $name (curl exit $LASTEXITCODE)" + + $attempt = 0 + $uploaded = $false + while (-not $uploaded -and $attempt -le $maxRetries) { + if ($attempt -gt 0) { + $wait = $backoffs[[Math]::Min($attempt - 1, $backoffs.Length - 1)] + Write-Host (" Retrying in {0}s (attempt {1}/{2})..." -f $wait, ($attempt + 1), ($maxRetries + 1)) -ForegroundColor Yellow + Start-Sleep -Seconds $wait + + # Re-check whether prior attempt actually succeeded server-side before + # 504-ing the client. If asset is already there, treat as success. + try { + $check = Invoke-RestMethod ` + -Uri "$apiBase/repos/$repoOwner/$repoName/releases/$($release.id)/assets" ` + -Method Get -Headers $headers + $existing = $check | Where-Object { $_.name -eq $name } + if ($existing -and $existing.size -eq $size) { + Write-Host " Asset already present server-side ($($existing.size) bytes) — skipping retry." -ForegroundColor DarkGray + $uploaded = $true + break + } + # If asset is present but with wrong size (half-uploaded), delete first. + if ($existing) { + Write-Host " Removing partial asset id=$($existing.id) ($($existing.size) bytes) before retry..." -ForegroundColor DarkGray + Invoke-RestMethod ` + -Uri "$apiBase/repos/$repoOwner/$repoName/releases/$($release.id)/assets/$($existing.id)" ` + -Method Delete -Headers $headers | Out-Null + } + } catch { + # If the list call itself fails, just proceed with the retry. + } + } + + Write-Host ("Uploading {0} ({1:N1} MB)..." -f $name, ($size / 1MB)) -ForegroundColor Cyan + & $curl ` + --fail-with-body ` + --silent --show-error ` + --connect-timeout 30 ` + --max-time 900 ` + -H "Authorization: token $env:GITEA_TOKEN" ` + -H "Content-Type: application/octet-stream" ` + --data-binary "@$asset" ` + $uri + if ($LASTEXITCODE -eq 0) { + $uploaded = $true + } else { + Write-Host " curl exit $LASTEXITCODE — will retry." -ForegroundColor Yellow + $attempt++ + } + } + + if (-not $uploaded) { + Write-Error "Upload failed for $name after $($maxRetries + 1) attempts." exit 1 } }