diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml index 8ff7a23..fa64b8f 100644 --- a/.github/workflows/windows-build.yml +++ b/.github/workflows/windows-build.yml @@ -25,11 +25,22 @@ jobs: - name: Build/release Electron app uses: samuelmeuli/action-electron-builder@v1 with: + - name: Calculate release version + id: version + shell: pwsh + env: + GH_TOKEN: ${{ secrets.GH_TOKEN != '' && secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + $version = node scripts/ci/update-version.js + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + "version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append + github_token: ${{ secrets.GH_TOKEN != '' && secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} release: ${{ startsWith(github.ref, 'refs/tags/v') }} args: --win --x64 --publish always - env: - GH_TOKEN: ${{ secrets.GH_TOKEN != '' && secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + github_token: ${{ secrets.GH_TOKEN != '' && secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + release: true GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload installer artifact @@ -39,4 +50,4 @@ jobs: name: windows-installer path: | dist/**/*.exe - dist/**/*.blockmap + name: windows-installer-${{ steps.version.outputs.version }} diff --git a/scripts/ci/update-version.js b/scripts/ci/update-version.js new file mode 100644 index 0000000..befbfd1 --- /dev/null +++ b/scripts/ci/update-version.js @@ -0,0 +1,79 @@ +const fs = require('fs'); +const path = require('path'); + +const repository = process.env.GITHUB_REPOSITORY; +const authToken = process.env.GH_TOKEN || process.env.GITHUB_TOKEN || ''; + +if (!repository) { + console.error('GITHUB_REPOSITORY is not defined.'); + process.exit(1); +} + +const isoDate = new Date().toISOString().slice(0, 10); + +async function fetchReleases() { + if (!authToken) { + return []; + } + + const response = await fetch(`https://api.github.com/repos/${repository}/releases?per_page=100`, { + headers: { + Authorization: `Bearer ${authToken}`, + 'User-Agent': 'picoGraph-release-bot' + } + }); + + if (!response.ok) { + const message = await response.text(); + console.error(`Failed to fetch releases: ${response.status} ${message}`); + return []; + } + + return response.json(); +} + +function determineNextVersion(releases) { + let index = 1; + + for (const release of releases) { + const tag = typeof release.tag_name === 'string' ? release.tag_name.replace(/^v/, '') : ''; + const match = tag.match(/^(\d{4}-\d{2}-\d{2})-(\d{2})$/); + + if (match && match[1] === isoDate) { + index = Math.max(index, Number.parseInt(match[2], 10) + 1); + } + } + + return `${isoDate}-${String(index).padStart(2, '0')}`; +} + +function updateJson(filePath, version) { + if (!fs.existsSync(filePath)) { + return; + } + + const data = JSON.parse(fs.readFileSync(filePath, 'utf8')); + data.version = version; + + if (data.packages && data.packages['']) { + data.packages[''].version = version; + } + + fs.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`, 'utf8'); +} + +(async () => { + try { + const releases = await fetchReleases(); + const version = determineNextVersion(releases); + const root = process.cwd(); + + updateJson(path.join(root, 'package.json'), version); + updateJson(path.join(root, 'package-lock.json'), version); + + console.log(version); + } catch (error) { + console.error(error instanceof Error ? error.message : String(error)); + process.exit(1); + } +})();