diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index f2f4275..914e430 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -40,11 +40,15 @@ jobs: username: ${{ gitea.actor }} password: ${{ secrets.TOKEN }} + - name: Compute short SHA + run: echo "SHORT_SHA=${GITEA_SHA::7}" >> $GITEA_ENV + - name: Build and push uses: docker/build-push-action@v5 with: context: . push: true + build-args: BUILD_VERSION=dev-${{ env.SHORT_SHA }} tags: | ${{ env.IMAGE }}:dev ${{ env.IMAGE }}:dev-${{ gitea.sha }} diff --git a/Dockerfile b/Dockerfile index 608e6f6..0a95f88 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,8 +7,13 @@ COPY package*.json ./ RUN npm ci --omit=dev COPY . . -RUN awk -F'"' '/"version"/{printf "const VERSION = \"%s\";\n", $4; exit}' \ - package.json > js/version.js +ARG BUILD_VERSION="" +RUN if [ -n "$BUILD_VERSION" ]; then \ + printf 'const VERSION = "%s";\n' "$BUILD_VERSION" > js/version.js; \ + else \ + awk -F'"' '/"version"/{printf "const VERSION = \"%s\";\n", $4; exit}' \ + package.json > js/version.js; \ + fi RUN mkdir -p /app/data && chown -R app:app /app USER app diff --git a/js/app.js b/js/app.js index 72cc2b0..1380b38 100644 --- a/js/app.js +++ b/js/app.js @@ -38,6 +38,9 @@ window.addEventListener('popstate', e => { // ── Bootstrap ───────────────────────────────────────────────────────────────── -if (VERSION) document.getElementById('nav-version').textContent = `v${VERSION}`; +if (VERSION) { + const label = /^\d/.test(VERSION) ? `v${VERSION}` : VERSION; + document.getElementById('nav-version').textContent = label; +} handleRoute(); diff --git a/tests/helpers.test.js b/tests/helpers.test.js index 69008a6..a2af546 100644 --- a/tests/helpers.test.js +++ b/tests/helpers.test.js @@ -115,6 +115,24 @@ describe('fmtDateFull', () => { }) }) +// ── versionLabel() ─────────────────────────────────────────────────────────── +// Mirrors the logic in app.js — semver strings get a v prefix, dev strings don't. + +function versionLabel(v) { + return /^\d/.test(v) ? `v${v}` : v +} + +describe('version label formatting', () => { + it('prepends v for semver strings', () => { + expect(versionLabel('1.1.2')).toBe('v1.1.2') + expect(versionLabel('2.0.0')).toBe('v2.0.0') + }) + + it('does not prepend v for dev build strings', () => { + expect(versionLabel('dev-abc1234')).toBe('dev-abc1234') + }) +}) + // ── CSS regressions ─────────────────────────────────────────────────────────── const css = readFileSync(join(__dirname, '../css/app.css'), 'utf8') @@ -127,3 +145,15 @@ describe('CSS regressions', () => { expect(css).toMatch(/\.badge\s*\{[^}]*text-align\s*:\s*center/s) }) }) + +// ── CI workflow regressions ─────────────────────────────────────────────────── + +const ciYml = readFileSync(join(__dirname, '../.gitea/workflows/ci.yml'), 'utf8') + +describe('CI workflow regressions', () => { + it('build-dev job passes BUILD_VERSION build arg', () => { + // Regression: dev image showed semver instead of dev- because + // BUILD_VERSION was never passed to docker build. + expect(ciYml).toContain('BUILD_VERSION') + }) +})