From f12ba4f5b90397f7da5141146be134151150c94d Mon Sep 17 00:00:00 2001 From: Zolnai Zsolt <44463109+zzsolt@users.noreply.github.com> Date: Wed, 23 Sep 2026 12:33:27 +0200 Subject: [PATCH 1/3] Add Dependabot configuration Configure Dependabot to check for updates weekly for GitHub Actions and NuGet packages in specified directories. --- .github/dependabot.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..b9baac63 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,28 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "Europe/Budapest" + open-pull-requests-limit: 5 + + - package-ecosystem: "nuget" + directory: "/src/CsvVisualEditor" + schedule: + interval: "weekly" + day: "monday" + time: "06:15" + timezone: "Europe/Budapest" + open-pull-requests-limit: 5 + + - package-ecosystem: "nuget" + directory: "/tests/CsvVisualEditor.Core.Tests" + schedule: + interval: "weekly" + day: "monday" + time: "06:30" + timezone: "Europe/Budapest" + open-pull-requests-limit: 5 From 3aed50e547bbe99a44f21edfe028e0b5608edcaf Mon Sep 17 00:00:00 2001 From: Zolnai Zsolt <44463109+zzsolt@users.noreply.github.com> Date: Wed, 23 Sep 2026 12:34:30 +0200 Subject: [PATCH 2/3] Add tag-driven release workflow --- .github/workflows/release.yml | 139 ++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..72501fe0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,139 @@ +name: Release + +on: + push: + tags: + - "v*.*.*" + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: false + +permissions: + contents: read + +jobs: + build: + runs-on: windows-latest + timeout-minutes: 35 + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: 10.0.x + + - name: Resolve and validate release version + id: version + shell: pwsh + run: | + $tag = $env:GITHUB_REF_NAME + if ($tag -notmatch '^v(?\d+\.\d+\.\d+)$') { + throw "Release tags must be stable semantic versions such as v1.0.2. Got: $tag" + } + $version = $Matches.version + + $projectText = Get-Content 'src/CsvVisualEditor/CsvVisualEditor.csproj' -Raw + $projectVersion = [regex]::Match($projectText, '(?[^<]+)').Groups['version'].Value + if (-not $projectVersion) { + throw 'Could not read from CsvVisualEditor.csproj.' + } + if ($projectVersion -ne $version) { + throw "Tag version $version does not match project version $projectVersion." + } + + $notes = "packaging/release-notes-$version.md" + if (-not (Test-Path $notes)) { + throw "Missing release notes: $notes" + } + + "version=$version" >> $env:GITHUB_OUTPUT + + - name: Run complete production candidate gate + id: candidate + shell: pwsh + run: pwsh -NoProfile -File tools/build-local.ps1 -PackageVersion "${{ steps.version.outputs.version }}" + + - name: Build exact release body + if: success() + shell: pwsh + run: | + $version = "${{ steps.version.outputs.version }}" + $candidate = "artifacts/local/$version" + $notes = Get-Content "packaging/release-notes-$version.md" -Raw + $manifest = Get-Content "$candidate/release-manifest.json" -Raw | ConvertFrom-Json + $body = @" + $notes + + Validated release package: + + - ``$($manifest.zip)`` + - ZIP SHA-256: ``$($manifest.zipSha256)`` + - DLL SHA-256: ``$($manifest.dllSha256)`` + "@ + Set-Content -Path "$candidate/release-body.md" -Value $body -Encoding utf8 + + - name: Upload release diagnostics + if: always() && steps.candidate.outcome != 'skipped' + uses: actions/upload-artifact@v4 + with: + name: CsvVisualEditor-release-diagnostics-${{ steps.version.outputs.version }} + path: | + artifacts/local/${{ steps.version.outputs.version }}/*.log + artifacts/local/${{ steps.version.outputs.version }}/SHA256.txt + artifacts/local/${{ steps.version.outputs.version }}/release-manifest.json + artifacts/local/${{ steps.version.outputs.version }}/nppPluginList-entry.x64.json + artifacts/local/${{ steps.version.outputs.version }}/artifacts/ui-review/ + artifacts/local/${{ steps.version.outputs.version }}/host-review/ + artifacts/local/${{ steps.version.outputs.version }}/keyboard-input-review/ + artifacts/local/${{ steps.version.outputs.version }}/context-menu-review/ + artifacts/local/${{ steps.version.outputs.version }}/localized-host-review/ + if-no-files-found: warn + retention-days: 14 + + - name: Stage exact release input + if: success() + shell: pwsh + run: | + $version = "${{ steps.version.outputs.version }}" + New-Item -ItemType Directory -Path release-input -Force | Out-Null + Copy-Item "artifacts/local/$version/CsvVisualEditor-$version-win-x64.zip" release-input/ + Copy-Item "artifacts/local/$version/release-body.md" release-input/ + + - name: Upload exact release input + if: success() + uses: actions/upload-artifact@v4 + with: + name: CsvVisualEditor-release-input-${{ steps.version.outputs.version }} + path: release-input/ + if-no-files-found: error + retention-days: 7 + + publish: + needs: build + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Download exact release input + uses: actions/download-artifact@v4 + with: + name: CsvVisualEditor-release-input-${{ needs.build.outputs.version }} + path: release-input + + - name: Publish GitHub Release + uses: softprops/action-gh-release@v3.0.3 + with: + name: CSV Visual Editor ${{ needs.build.outputs.version }} + body_path: release-input/release-body.md + files: release-input/CsvVisualEditor-${{ needs.build.outputs.version }}-win-x64.zip + fail_on_unmatched_files: true + overwrite_files: false + make_latest: true From 3cc085c80f4f3f7b2b8207e3fb374f7793da7f18 Mon Sep 17 00:00:00 2001 From: Zolnai Zsolt <44463109+zzsolt@users.noreply.github.com> Date: Wed, 23 Sep 2026 12:38:14 +0200 Subject: [PATCH 3/3] Update CI policy for approved release workflow --- tests/ci/test_plan.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/ci/test_plan.py b/tests/ci/test_plan.py index 237e3072..532b6152 100644 --- a/tests/ci/test_plan.py +++ b/tests/ci/test_plan.py @@ -98,8 +98,23 @@ def test_local_candidate_has_every_gate_before_packaging(self): self.assertIn("Join-Path $layout 'CsvVisualEditor.dll'", text) self.assertNotIn("package/CsvVisualEditor'", text) - def test_no_hosted_translation_or_transfer_workflows_remain(self): - self.assertEqual(['ci.yml'], sorted(p.name for p in (ROOT / '.github/workflows').glob('*.yml'))) + def test_only_approved_hosted_workflows_remain(self): + self.assertEqual( + ['ci.yml', 'release.yml'], + sorted(p.name for p in (ROOT / '.github/workflows').glob('*.yml')), + ) + + def test_release_workflow_is_tag_only_and_uses_full_gate(self): + text = (ROOT / '.github/workflows/release.yml').read_text() + self.assertIn('tags:', text) + self.assertIn('"v*.*.*"', text) + self.assertNotIn('branches:', text) + self.assertIn('contents: read', text) + self.assertIn('contents: write', text) + self.assertIn('tools/build-local.ps1', text) + self.assertIn('softprops/action-gh-release@v3.0.3', text) + self.assertIn('overwrite_files: false', text) + self.assertIn('fail_on_unmatched_files: true', text) if __name__ == '__main__':