From 1acb94e3a331331c87d5ab3c95926c64b5d40360 Mon Sep 17 00:00:00 2001 From: Naoyuki Sogo Date: Fri, 11 Sep 2026 18:51:44 +0900 Subject: [PATCH] ci: stop committing version.json back to branches; bake it at build time (#136) Backport of PR #136 (main) to maintenance2026. Adjusted for this branch: - kept "master" branch naming used in this line's run_test.yml / build_and_deploy.yml comments (unlike main, not yet renamed here) - kept `RUN npm prune --production` in the exec stage as-is (this branch still prunes; main's "don't prune" comment doesn't apply here) - did not add the entrypoint.sh ssh-agent block (PR #135, not backported to this branch) or the doc files that don't exist here yet (AGENTS.md, TEST_GUIDE.md, documentMD/design/{developer_guide,testing}.md) The `[skip ci] update version number` step in run_test.yml rewrote server/app/db/version.json and pushed it to every feature branch, so parallel PRs conflicted on that file and a stale `-beta` value rode onto main/maintenance at merge (issue #1014). - run_test.yml: drop the version-stamp / commit / push steps and the now-unused GITHUB_TOKEN from the test job. CI no longer touches the file. - version.json: the committed value is a fixed placeholder ("not defined - this is a development build"). Nothing in CI edits the tracked file, so it never diverges between branches -> no more conflicts. - build_and_deploy.yml: pass the computed version through the WHEEL_VERSION build-arg instead of editing the worktree; repack source.tar.gz from a clean tree with the real version dropped in (git archive shipped the placeholder before). - Dockerfile: a throwaway `versioner` stage runs `git describe` on the build context, so a plain `docker build .` still bakes a commit-identifying version (`-local`). `--build-arg WHEEL_VERSION` overrides it; an un-stamped source build keeps whatever version.json ships. No --dirty: .dockerignore hides tracked paths (documentMD/) from the stage's `COPY . .`, so --dirty would always fire. versionInfo.js is unchanged - it still statically imports version.json. (cherry picked from commit 931f0ae197a0b569a8755643c89d5e54067ffa9b) Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01C3jKNM1qubomM8UTRdkEWu --- .dockerignore | 3 +++ .github/workflows/build_and_deploy.yml | 29 +++++++++++++--------- .github/workflows/run_test.yml | 26 -------------------- Dockerfile | 34 ++++++++++++++++++++++++++ README.md | 5 ---- documentMD/design/design.md | 32 ++++++++++++++++++++++++ server/app/db/version.json | 2 +- 7 files changed, 88 insertions(+), 43 deletions(-) diff --git a/.dockerignore b/.dockerignore index 6c2c947b8..86645f053 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,6 @@ +# NOTE: do not add .git here. The Dockerfile's `versioner` stage runs +# `git describe` against it to stamp the build version (issue #1014); excluding +# it makes every build report "unknown". docs documentMD **/node_modules diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml index 0a8394d28..290b7529a 100644 --- a/.github/workflows/build_and_deploy.yml +++ b/.github/workflows/build_and_deploy.yml @@ -12,25 +12,23 @@ jobs: steps: - uses: actions/checkout@v4 - uses: eWaterCycle/setup-apptainer@v2 - - name: stamp version.json for this build - # master is branch-protected (rejects direct pushes even from Actions bots), - # so this can't be committed back - it only needs to be baked into the - # artifacts built below, which the Dockerfile's `COPY server server` picks up - # from this worktree. + - name: determine version number for this build + # This value is baked into the image via the WHEEL_VERSION build-arg (see + # the docker build step) and into source.tar.gz below. It is never + # committed back: master is branch-protected, and the per-branch + # `[skip ci] update version number` commits used to conflict on + # server/app/db/version.json between parallel PRs (issue #1014). run: | case "${BRANCH_NAME}" in master) TYPE=release ;; maintenance*) TYPE=maintenance ;; *) TYPE=beta ;; esac - VERSION_NUMBER="$(date '+%Y-%m%d-%H%M%S')-${TYPE}" - echo "{\"version\": \"${VERSION_NUMBER}\" }" > server/app/db/version.json - cat server/app/db/version.json - echo "VERSION_NUMBER=${VERSION_NUMBER}" >>$GITHUB_ENV + echo "VERSION_NUMBER=$(date '+%Y-%m%d-%H%M%S')-${TYPE}" >>$GITHUB_ENV # only master builds (TYPE=release) own the `latest` docker tag and the # GitHub "Latest" release; maintenance*/beta builds must not. echo "RELEASE_TYPE=${TYPE}" >>$GITHUB_ENV - - run: docker build -t ${DOCKER_USER_NAME}/${DOCKER_CONTAINER_NAME}:${BRANCH_NAME} . + - run: docker build --build-arg WHEEL_VERSION="${VERSION_NUMBER}" -t ${DOCKER_USER_NAME}/${DOCKER_CONTAINER_NAME}:${BRANCH_NAME} . - run: docker login -u ${DOCKER_USER_NAME} -p ${DOCKER_PASSWORD} - run: docker push ${DOCKER_USER_NAME}/${DOCKER_CONTAINER_NAME}:${BRANCH_NAME} - name: publish :latest for mainline releases only @@ -41,7 +39,16 @@ jobs: docker push ${DOCKER_USER_NAME}/${DOCKER_CONTAINER_NAME}:latest - run: docker save ${DOCKER_USER_NAME}/${DOCKER_CONTAINER_NAME}:${BRANCH_NAME} | gzip > wheel_docker.tar.gz - run: apptainer build wheel.sif docker-daemon://${DOCKER_USER_NAME}/${DOCKER_CONTAINER_NAME}:${BRANCH_NAME} - - run: git archive --format=tar.gz --prefix=OpenWHEEL-${BRANCH_NAME}/ -o source.tar.gz HEAD + - name: pack source.tar.gz with the version baked in + # git archive would ship the committed placeholder version.json; extract + # the tracked tree, drop in the real version, and re-pack so a run from + # source.tar.gz reports the same version as the images (issue #1014). + run: | + rm -rf _src && mkdir -p _src + git archive --prefix=OpenWHEEL-${BRANCH_NAME}/ HEAD | tar -x -C _src + echo "{\"version\": \"${VERSION_NUMBER}\" }" > _src/OpenWHEEL-${BRANCH_NAME}/server/app/db/version.json + tar -czf source.tar.gz -C _src OpenWHEEL-${BRANCH_NAME} + rm -rf _src - name: create release and upload artifact uses: softprops/action-gh-release@v2 with: diff --git a/.github/workflows/run_test.yml b/.github/workflows/run_test.yml index a1fe5f9a7..1eca943b1 100644 --- a/.github/workflows/run_test.yml +++ b/.github/workflows/run_test.yml @@ -19,7 +19,6 @@ jobs: WHEEL_TEST_REMOTEHOST: testServer WHEEL_TEST_REMOTE_PASSWORD: passw0rd TZ: JST-9 - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} runs-on: ubuntu-latest strategy: matrix: @@ -52,31 +51,6 @@ jobs: - run: npm ci - run: npm run test working-directory: ./server - - name: determine release type from branch name - run: | - case "${{ github.ref_name }}" in - master) TYPE=release ;; - maintenance*) TYPE=maintenance ;; - *) TYPE=beta ;; - esac - echo "VERSION_NUMBER=$(date '+%Y-%m%d-%H%M%S')-${TYPE}" >>$GITHUB_ENV - - run: cat server/app/db/version.json - - name: update version.json - id: create-json - uses: jsdaniell/create-json@v1.2.2 - with: - dir: "server/app/db" - name: "version.json" - json: '{"version": "${{ env.VERSION_NUMBER }}" }' - - run: cat server/app/db/version.json - - name: commit and push version.json - run: | - git remote set-url origin https://github-actions:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY} - git config user.name 'version-number-updater[bot]' - git config user.email 'action@github.com' - git add server/app/db/version.json - git commit -m '[skip ci] update version number' - git push origin HEAD:${GITHUB_REF} backup: needs: ["test"] runs-on: ubuntu-latest diff --git a/Dockerfile b/Dockerfile index 3518e8470..540a50c6b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,20 @@ RUN apt-get update && apt -y install curl git rsync openssh-server bzip2 python3 apt-get clean &&\ rm -rf /var/lib/apt/lists/* +# Resolve one version string for this build from the .git in the build context. +# This happens at build time so the value is frozen into the image (static, not +# recomputed at server start). Everything in this stage is discarded except +# /wheel-version - the copied .git never reaches the final image. See issue #1014. +# (Do not add .git to .dockerignore or every build falls back to "unknown".) +# No --dirty: git describe would inspect the working tree, but .dockerignore +# hides tracked paths (documentMD/ etc.) from `COPY . .`, so --dirty would always +# fire. The abbreviated commit is enough to identify the source. +FROM base AS versioner +WORKDIR /wheel-src +COPY . . +RUN git describe --tags --always --long --abbrev=12 > /wheel-version 2>/dev/null \ + || echo unknown > /wheel-version + FROM base AS run_base WORKDIR /usr/src/ COPY package.json package.json @@ -30,6 +44,18 @@ WORKDIR /usr/src COPY common common COPY client client COPY server server +# bake the build version (issue #1014): +# - WHEEL_VERSION build-arg wins (CI passes the release/beta string) +# - else the git-derived value from the versioner stage, marked -local +# - else leave version.json as shipped (committed placeholder, or a +# source.tar.gz that already carries a baked version) +COPY --from=versioner /wheel-version /tmp/wheel-version +ARG WHEEL_VERSION= +RUN if [ -n "${WHEEL_VERSION}" ]; then \ + printf '{"version": "%s" }\n' "${WHEEL_VERSION}" > server/app/db/version.json ; \ + elif [ "$(cat /tmp/wheel-version)" != unknown ]; then \ + printf '{"version": "%s-local" }\n' "$(cat /tmp/wheel-version)" > server/app/db/version.json ; \ + fi WORKDIR /usr/src/client RUN npm run build COPY entrypoint.sh /usr/src/server/ @@ -50,6 +76,14 @@ WORKDIR /usr/src COPY common common COPY server server RUN npm prune --production +# bake the build version - same rule as the dev stage (issue #1014) +COPY --from=versioner /wheel-version /tmp/wheel-version +ARG WHEEL_VERSION= +RUN if [ -n "${WHEEL_VERSION}" ]; then \ + printf '{"version": "%s" }\n' "${WHEEL_VERSION}" > server/app/db/version.json ; \ + elif [ "$(cat /tmp/wheel-version)" != unknown ]; then \ + printf '{"version": "%s-local" }\n' "$(cat /tmp/wheel-version)" > server/app/db/version.json ; \ + fi COPY --from=builder /usr/src/server/app/public /usr/src/server/app/public COPY entrypoint.sh /usr/src/server/ RUN rm -fr client server/app/config/* server/test diff --git a/README.md b/README.md index 83c288a42..5adbda5ed 100644 --- a/README.md +++ b/README.md @@ -85,8 +85,3 @@ run following commands 3. cd test; npm install (install e2e test modules) please read test/README.md for E2E test preparation - - -### CI/CD process -when you push new commit to github, server/app/db/version.json will be updated during CI/CD process. -So, you have to pull before make further commit. diff --git a/documentMD/design/design.md b/documentMD/design/design.md index 1bef06ef1..f2025053d 100644 --- a/documentMD/design/design.md +++ b/documentMD/design/design.md @@ -496,6 +496,38 @@ saveボタンの押下 | git commit revertボタンの押下 | git reset HEAD --hard cleanボタンの押下 | rm -fr && git reset HEAD --hard +## バージョン番号の管理 +サーバ起動時のログ(`starting WHEEL server (version X)`)とログ画面の「about」で +表示するバージョン番号は、`server/app/core/versionInfo.js` が +`server/app/db/version.json` の `version` を読んで使っているだけである。 + +リポジトリに commit されている値は固定のプレースホルダで、CI を含め誰もこの +ファイルを書き換えない。 + +```json +{"version": "not defined - this is a development build"} +``` + +(過去は `run_test.yml` が push のたびに日付ベースの値を各ブランチへ +commit-back していたが、並行して出している複数の PR が `version.json` で +必ず conflict するのと、merge のたびに stale な値が main / maintenance に +乗ってしまう問題があったため廃止した。GitLab issue `aicshud/WHEEL#1014`。) + +実際に表示される文字列は、実行経路ごとに **ビルド時** に焼き込まれる。 +アプリのコードが起動時にバージョンを計算することはない。 + +| 実行経路 | version.json を書くのは | 表示される文字列 | +|---|---|---| +| 公式リリース(`build_and_deploy.yml`) | CI が `docker build --build-arg WHEEL_VERSION=...` で渡す | `YYYY-MMDD-HHMMSS-{release\|maintenance\|beta}`(GitHub Release のタグと一致) | +| `docker build .`(`.git` あり・build-arg なし) | Dockerfile の `versioner` ステージが `git describe` した値 | `-local` | +| `docker build --build-arg WHEEL_VERSION=... .` | Dockerfile(build-arg 優先) | 渡した値そのまま | +| `source.tar.gz` を展開して build | CI が焼き込んだ値を保持 | `YYYY-MMDD-HHMMSS-{type}` | +| ソースから直接 `npm start`(ビルドなし) | 誰も書かない(プレースホルダのまま) | `not defined - this is a development build` | +| `compose.dev.yml` での開発 | 上記と同じ(`server/app` を bind mount するため、イメージに焼き込まれた値は隠れる) | `not defined - this is a development build` | + +`server/app/db/version.json` は手で書き換えない。中身はビルド処理が焼き込む +ものであり、リポジトリ上の値は「ビルドされていない」ことを示すプレースホルダ +として固定してある。 ## 画面遷移 ![画面遷移図](./screen_transition.svg) diff --git a/server/app/db/version.json b/server/app/db/version.json index ac17fd588..527f5a11e 100644 --- a/server/app/db/version.json +++ b/server/app/db/version.json @@ -1 +1 @@ -{"version": "2026-0903-094017-maintenance" } \ No newline at end of file +{"version": "not defined - this is a development build"}