fix: app version display pattern in admin panel sidebar
This commit is contained in:
@@ -834,6 +834,45 @@ def _run_git_command(*args: str) -> str:
|
|||||||
return result.stdout.strip()
|
return result.stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_version_branch(raw_branch: str) -> str:
|
||||||
|
branch = str(raw_branch or "").strip()
|
||||||
|
for prefix in ("refs/heads/", "refs/remotes/origin/", "origin/"):
|
||||||
|
if branch.startswith(prefix):
|
||||||
|
branch = branch[len(prefix) :]
|
||||||
|
break
|
||||||
|
if branch == "HEAD":
|
||||||
|
return ""
|
||||||
|
return re.sub(r"[^A-Za-z0-9._-]+", "-", branch).strip("-")[:48]
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_version_branch() -> str:
|
||||||
|
for env_name in (
|
||||||
|
"REMNAWAVE_MINISHOP_BRANCH",
|
||||||
|
"GIT_BRANCH",
|
||||||
|
"BRANCH_NAME",
|
||||||
|
"GITHUB_REF_NAME",
|
||||||
|
"CI_COMMIT_REF_NAME",
|
||||||
|
):
|
||||||
|
branch = _normalize_version_branch(os.getenv(env_name, ""))
|
||||||
|
if branch:
|
||||||
|
return branch
|
||||||
|
return _normalize_version_branch(
|
||||||
|
_run_git_command("branch", "--show-current")
|
||||||
|
or _run_git_command("symbolic-ref", "--quiet", "--short", "HEAD")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _format_app_version(tag: str, sha: str, branch: str) -> str:
|
||||||
|
branch_suffix = "" if not branch or branch == "main" else f"-{branch}"
|
||||||
|
if tag and sha:
|
||||||
|
return f"{tag}{branch_suffix}+g{sha}"
|
||||||
|
if sha:
|
||||||
|
return f"dev{branch_suffix}+g{sha}"
|
||||||
|
if tag:
|
||||||
|
return f"{tag}{branch_suffix}"
|
||||||
|
return f"dev{branch_suffix}+unknown"
|
||||||
|
|
||||||
|
|
||||||
def _resolve_app_version() -> str:
|
def _resolve_app_version() -> str:
|
||||||
global _APP_VERSION_CACHE
|
global _APP_VERSION_CACHE
|
||||||
if _APP_VERSION_CACHE:
|
if _APP_VERSION_CACHE:
|
||||||
@@ -855,21 +894,8 @@ def _resolve_app_version() -> str:
|
|||||||
|
|
||||||
tag = _run_git_command("describe", "--tags", "--abbrev=0")
|
tag = _run_git_command("describe", "--tags", "--abbrev=0")
|
||||||
sha = _run_git_command("rev-parse", "--short", "HEAD")
|
sha = _run_git_command("rev-parse", "--short", "HEAD")
|
||||||
dirty = bool(_run_git_command("status", "--porcelain"))
|
branch = _resolve_version_branch()
|
||||||
|
version = _format_app_version(tag, sha, branch)
|
||||||
if tag and sha:
|
|
||||||
commits_since_tag = _run_git_command("rev-list", f"{tag}..HEAD", "--count")
|
|
||||||
if commits_since_tag and commits_since_tag != "0":
|
|
||||||
version = f"{tag}+{commits_since_tag}.g{sha}"
|
|
||||||
else:
|
|
||||||
version = tag
|
|
||||||
elif sha:
|
|
||||||
version = f"dev+g{sha}"
|
|
||||||
else:
|
|
||||||
version = "dev+unknown"
|
|
||||||
|
|
||||||
if dirty:
|
|
||||||
version = f"{version}-dirty"
|
|
||||||
|
|
||||||
_APP_VERSION_CACHE = version
|
_APP_VERSION_CACHE = version
|
||||||
return version
|
return version
|
||||||
|
|||||||
+30
-17
@@ -1,32 +1,45 @@
|
|||||||
# Resolve the application version from .git at build time and emit a single
|
# Resolve the application version from .git at build time and emit a tiny
|
||||||
# tiny ``.build-version`` file. The .git tree is consumed in this throwaway
|
# .build-version file. The .git tree is consumed in this throwaway stage and
|
||||||
# stage and never copied into the runtime image — only the resulting one-line
|
# never copied into the runtime image; only the tag + commit version string
|
||||||
# version string ships. This matches the runtime fallback chain in
|
# ships. Non-main builds include the branch name so they are visibly distinct
|
||||||
# ``_resolve_app_version`` (REMNAWAVE_MINISHOP_VERSION env > .build-version
|
# from release builds. This matches the runtime fallback chain in _resolve_app_version
|
||||||
# file > live git > "dev+unknown") so the admin sidebar always shows a tag /
|
# (REMNAWAVE_MINISHOP_VERSION env > .build-version file > live git >
|
||||||
# sha even though the runtime images have no git tooling and no .git tree.
|
# "dev+unknown") so the admin sidebar always shows a tag / sha even though the
|
||||||
|
# runtime images have no git tooling and no .git tree.
|
||||||
FROM alpine:3.20 AS version-builder
|
FROM alpine:3.20 AS version-builder
|
||||||
RUN apk add --no-cache git
|
RUN apk add --no-cache git
|
||||||
WORKDIR /repo
|
WORKDIR /repo
|
||||||
|
ARG REMNAWAVE_MINISHOP_BRANCH=""
|
||||||
|
ARG GIT_BRANCH=""
|
||||||
|
ARG BRANCH_NAME=""
|
||||||
|
ARG GITHUB_REF_NAME=""
|
||||||
|
ARG CI_COMMIT_REF_NAME=""
|
||||||
COPY .git ./.git
|
COPY .git ./.git
|
||||||
RUN set -eu; \
|
RUN set -eu; \
|
||||||
git config --global --add safe.directory /repo; \
|
git config --global --add safe.directory /repo; \
|
||||||
tag=$(git describe --tags --abbrev=0 2>/dev/null || true); \
|
tag=$(git describe --tags --abbrev=0 2>/dev/null || true); \
|
||||||
sha=$(git rev-parse --short HEAD 2>/dev/null || true); \
|
sha=$(git rev-parse --short HEAD 2>/dev/null || true); \
|
||||||
dirty=$(git status --porcelain 2>/dev/null | head -c1 || true); \
|
branch="${REMNAWAVE_MINISHOP_BRANCH:-${GIT_BRANCH:-${BRANCH_NAME:-${GITHUB_REF_NAME:-${CI_COMMIT_REF_NAME:-}}}}}"; \
|
||||||
|
if [ -z "$branch" ]; then branch=$(git branch --show-current 2>/dev/null || true); fi; \
|
||||||
|
if [ -z "$branch" ]; then branch=$(git symbolic-ref --quiet --short HEAD 2>/dev/null || true); fi; \
|
||||||
|
case "$branch" in \
|
||||||
|
refs/heads/*) branch="${branch#refs/heads/}" ;; \
|
||||||
|
refs/remotes/origin/*) branch="${branch#refs/remotes/origin/}" ;; \
|
||||||
|
origin/*) branch="${branch#origin/}" ;; \
|
||||||
|
HEAD) branch="" ;; \
|
||||||
|
esac; \
|
||||||
|
branch_slug=$(printf '%s' "$branch" | sed -E 's/[^A-Za-z0-9._-]+/-/g; s/^-+//; s/-+$//' | cut -c1-48); \
|
||||||
|
branch_suffix=""; \
|
||||||
|
if [ -n "$branch_slug" ] && [ "$branch_slug" != "main" ]; then branch_suffix="-$branch_slug"; fi; \
|
||||||
if [ -n "$tag" ] && [ -n "$sha" ]; then \
|
if [ -n "$tag" ] && [ -n "$sha" ]; then \
|
||||||
commits_since_tag=$(git rev-list "$tag..HEAD" --count 2>/dev/null || true); \
|
version="${tag}${branch_suffix}+g${sha}"; \
|
||||||
if [ -n "$commits_since_tag" ] && [ "$commits_since_tag" != "0" ]; then \
|
|
||||||
version="${tag}+${commits_since_tag}.g${sha}"; \
|
|
||||||
else \
|
|
||||||
version="$tag"; \
|
|
||||||
fi; \
|
|
||||||
elif [ -n "$sha" ]; then \
|
elif [ -n "$sha" ]; then \
|
||||||
version="dev+g${sha}"; \
|
version="dev${branch_suffix}+g${sha}"; \
|
||||||
|
elif [ -n "$tag" ]; then \
|
||||||
|
version="${tag}${branch_suffix}"; \
|
||||||
else \
|
else \
|
||||||
version="dev+unknown"; \
|
version="dev${branch_suffix}+unknown"; \
|
||||||
fi; \
|
fi; \
|
||||||
if [ -n "$dirty" ]; then version="${version}-dirty"; fi; \
|
|
||||||
printf '%s' "$version" > /build-version; \
|
printf '%s' "$version" > /build-version; \
|
||||||
printf '%s' "${tag:-unknown}" > /build-tag; \
|
printf '%s' "${tag:-unknown}" > /build-tag; \
|
||||||
printf '%s' "${sha:-unknown}" > /build-commit
|
printf '%s' "${sha:-unknown}" > /build-commit
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
"""Tests for ``_resolve_app_version`` — the source of the admin sidebar footer.
|
"""Tests for ``_resolve_app_version``: the admin sidebar footer source.
|
||||||
|
|
||||||
The admin sidebar shows ``{appVersion}`` next to a "remnawave-minishop" GitHub
|
The admin sidebar shows ``{appVersion}`` next to a "remnawave-minishop" GitHub
|
||||||
link. That string is rendered by
|
link. Release builds from ``main`` should render the latest reachable tag and
|
||||||
``backend/bot/app/web/webapp/assets.py::_resolve_app_version`` through the
|
commit sha, without a dirty suffix. Builds from other branches include the
|
||||||
following precedence chain:
|
branch name. That string is rendered by
|
||||||
|
``backend/bot/app/web/webapp/assets.py::_resolve_app_version`` through this
|
||||||
|
precedence chain:
|
||||||
|
|
||||||
1. ``REMNAWAVE_MINISHOP_VERSION`` env var — manual override;
|
1. ``REMNAWAVE_MINISHOP_VERSION`` env var: manual override;
|
||||||
2. ``$APP_ROOT/.build-version`` file — baked at Docker build time by the
|
2. ``$APP_ROOT/.build-version`` file: baked at Docker build time by the
|
||||||
``version-builder`` stage in ``deploy/docker/Dockerfile`` (consumes .git
|
``version-builder`` stage in ``deploy/docker/Dockerfile``;
|
||||||
in a throwaway stage and ships only this one tiny file);
|
3. live ``git describe`` / ``rev-parse``: local dev fallback where .git is
|
||||||
3. live ``git describe`` / ``rev-parse`` — works in local dev where .git
|
present;
|
||||||
is present;
|
|
||||||
4. ``"dev+unknown"`` as the ultimate fallback.
|
4. ``"dev+unknown"`` as the ultimate fallback.
|
||||||
|
|
||||||
Before the build-time bake, the admin footer in production silently fell back
|
The Docker image carries no git binary and no .git tree at runtime, so the
|
||||||
to step 4 because the Docker image carries no .git tree and no git binary.
|
baked ``.build-version`` file is the production source for the sidebar.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import importlib
|
import importlib
|
||||||
@@ -28,11 +29,20 @@ from unittest.mock import patch
|
|||||||
import bot.app.web.subscription_webapp # noqa: F401
|
import bot.app.web.subscription_webapp # noqa: F401
|
||||||
from bot.app.web.webapp import assets as assets_module
|
from bot.app.web.webapp import assets as assets_module
|
||||||
|
|
||||||
|
_VERSION_ENV_NAMES = (
|
||||||
|
"REMNAWAVE_MINISHOP_VERSION",
|
||||||
|
"REMNAWAVE_MINISHOP_BRANCH",
|
||||||
|
"GIT_BRANCH",
|
||||||
|
"BRANCH_NAME",
|
||||||
|
"GITHUB_REF_NAME",
|
||||||
|
"CI_COMMIT_REF_NAME",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _reset_cache() -> None:
|
def _reset_cache() -> None:
|
||||||
# The resolver memoizes the first result in a module-level global.
|
# The resolver memoizes the first result in a module-level global.
|
||||||
assets_module._APP_VERSION_CACHE = None # type: ignore[attr-defined]
|
assets_module._APP_VERSION_CACHE = None # type: ignore[attr-defined]
|
||||||
# Some callers reach through the facade re-export — clear that too.
|
# Some callers reach through the facade re-export; clear that too.
|
||||||
runtime = importlib.import_module("bot.app.web.webapp._runtime")
|
runtime = importlib.import_module("bot.app.web.webapp._runtime")
|
||||||
runtime._APP_VERSION_CACHE = None # type: ignore[attr-defined]
|
runtime._APP_VERSION_CACHE = None # type: ignore[attr-defined]
|
||||||
|
|
||||||
@@ -41,6 +51,13 @@ def _resolve():
|
|||||||
return assets_module._resolve_app_version()
|
return assets_module._resolve_app_version()
|
||||||
|
|
||||||
|
|
||||||
|
def _clean_version_env() -> dict:
|
||||||
|
env = dict(os.environ)
|
||||||
|
for name in _VERSION_ENV_NAMES:
|
||||||
|
env.pop(name, None)
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
class EnvOverrideTests(unittest.TestCase):
|
class EnvOverrideTests(unittest.TestCase):
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
_reset_cache()
|
_reset_cache()
|
||||||
@@ -62,12 +79,13 @@ class EnvOverrideTests(unittest.TestCase):
|
|||||||
self.assertEqual(called["git"], 0)
|
self.assertEqual(called["git"], 0)
|
||||||
|
|
||||||
def test_blank_env_var_falls_through(self):
|
def test_blank_env_var_falls_through(self):
|
||||||
env = {"REMNAWAVE_MINISHOP_VERSION": " "}
|
env = _clean_version_env()
|
||||||
|
env["REMNAWAVE_MINISHOP_VERSION"] = " "
|
||||||
with (
|
with (
|
||||||
patch.dict(os.environ, env),
|
patch.dict(os.environ, env, clear=True),
|
||||||
patch.object(assets_module, "_run_git_command", lambda *a: ""),
|
patch.object(assets_module, "_run_git_command", lambda *a: ""),
|
||||||
):
|
):
|
||||||
# No env, no file, no git → fallback string.
|
# No env, no file, no git: fallback string.
|
||||||
self.assertEqual(_resolve(), "dev+unknown")
|
self.assertEqual(_resolve(), "dev+unknown")
|
||||||
|
|
||||||
|
|
||||||
@@ -80,24 +98,22 @@ class BuildVersionFileTests(unittest.TestCase):
|
|||||||
|
|
||||||
def test_reads_baked_version_file(self):
|
def test_reads_baked_version_file(self):
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
(Path(tmp) / ".build-version").write_text("v3.4.5+12.gabcdef1", encoding="utf-8")
|
(Path(tmp) / ".build-version").write_text("v3.4.5+gabcdef1", encoding="utf-8")
|
||||||
env = dict(os.environ)
|
env = _clean_version_env()
|
||||||
env.pop("REMNAWAVE_MINISHOP_VERSION", None)
|
|
||||||
with (
|
with (
|
||||||
patch.dict(os.environ, env, clear=True),
|
patch.dict(os.environ, env, clear=True),
|
||||||
patch.object(assets_module, "APP_ROOT", Path(tmp)),
|
patch.object(assets_module, "APP_ROOT", Path(tmp)),
|
||||||
# ensure live git doesn't accidentally win if file read fails:
|
# Ensure live git does not accidentally win if file read fails.
|
||||||
patch.object(assets_module, "_run_git_command", lambda *a: ""),
|
patch.object(assets_module, "_run_git_command", lambda *a: ""),
|
||||||
):
|
):
|
||||||
self.assertEqual(_resolve(), "v3.4.5+12.gabcdef1")
|
self.assertEqual(_resolve(), "v3.4.5+gabcdef1")
|
||||||
|
|
||||||
def test_strips_trailing_whitespace_and_newlines_in_file(self):
|
def test_strips_trailing_whitespace_and_newlines_in_file(self):
|
||||||
# Shell ``printf '%s'`` writes no newline, but earlier helpers used
|
# Shell ``printf '%s'`` writes no newline, but older helpers may have
|
||||||
# ``echo`` which appends one. Both must produce the same result.
|
# used ``echo``. Both must produce the same result.
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
(Path(tmp) / ".build-version").write_text("v1.2.3\n\n", encoding="utf-8")
|
(Path(tmp) / ".build-version").write_text("v1.2.3\n\n", encoding="utf-8")
|
||||||
env = dict(os.environ)
|
env = _clean_version_env()
|
||||||
env.pop("REMNAWAVE_MINISHOP_VERSION", None)
|
|
||||||
with (
|
with (
|
||||||
patch.dict(os.environ, env, clear=True),
|
patch.dict(os.environ, env, clear=True),
|
||||||
patch.object(assets_module, "APP_ROOT", Path(tmp)),
|
patch.object(assets_module, "APP_ROOT", Path(tmp)),
|
||||||
@@ -108,14 +124,13 @@ class BuildVersionFileTests(unittest.TestCase):
|
|||||||
def test_empty_file_falls_through_to_git_then_unknown(self):
|
def test_empty_file_falls_through_to_git_then_unknown(self):
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
(Path(tmp) / ".build-version").write_text("", encoding="utf-8")
|
(Path(tmp) / ".build-version").write_text("", encoding="utf-8")
|
||||||
env = dict(os.environ)
|
env = _clean_version_env()
|
||||||
env.pop("REMNAWAVE_MINISHOP_VERSION", None)
|
|
||||||
with (
|
with (
|
||||||
patch.dict(os.environ, env, clear=True),
|
patch.dict(os.environ, env, clear=True),
|
||||||
patch.object(assets_module, "APP_ROOT", Path(tmp)),
|
patch.object(assets_module, "APP_ROOT", Path(tmp)),
|
||||||
patch.object(assets_module, "_run_git_command", lambda *a: ""),
|
patch.object(assets_module, "_run_git_command", lambda *a: ""),
|
||||||
):
|
):
|
||||||
# No env, empty file, no live git → ultimate fallback.
|
# No env, empty file, no live git: ultimate fallback.
|
||||||
self.assertEqual(_resolve(), "dev+unknown")
|
self.assertEqual(_resolve(), "dev+unknown")
|
||||||
|
|
||||||
|
|
||||||
@@ -126,56 +141,83 @@ class LiveGitFallbackTests(unittest.TestCase):
|
|||||||
_reset_cache()
|
_reset_cache()
|
||||||
self.addCleanup(_reset_cache)
|
self.addCleanup(_reset_cache)
|
||||||
|
|
||||||
def _run_with_git(self, replies: dict, *, dirty: bool = False) -> str:
|
def _run_with_git(self, replies: dict) -> str:
|
||||||
# Map (subcommand, *args) tuples to canned stdout values.
|
|
||||||
def fake_git(*args):
|
|
||||||
return replies.get(args, "")
|
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
# ``.build-version`` deliberately absent so we fall through.
|
# ``.build-version`` deliberately absent so we fall through.
|
||||||
env = dict(os.environ)
|
env = _clean_version_env()
|
||||||
env.pop("REMNAWAVE_MINISHOP_VERSION", None)
|
|
||||||
base = {
|
base = {
|
||||||
("describe", "--tags", "--abbrev=0"): replies.get("tag", ""),
|
("describe", "--tags", "--abbrev=0"): replies.get("tag", ""),
|
||||||
("rev-parse", "--short", "HEAD"): replies.get("sha", ""),
|
("rev-parse", "--short", "HEAD"): replies.get("sha", ""),
|
||||||
("status", "--porcelain"): "M file\n" if dirty else "",
|
("branch", "--show-current"): replies.get("branch", ""),
|
||||||
("rev-list", f"{replies.get('tag', '')}..HEAD", "--count"): replies.get(
|
|
||||||
"commits_since_tag", "0"
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def real_fake_git(*args):
|
def fake_git(*args):
|
||||||
return base.get(args, "")
|
return base.get(args, "")
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch.dict(os.environ, env, clear=True),
|
patch.dict(os.environ, env, clear=True),
|
||||||
patch.object(assets_module, "APP_ROOT", Path(tmp)),
|
patch.object(assets_module, "APP_ROOT", Path(tmp)),
|
||||||
patch.object(assets_module, "_run_git_command", real_fake_git),
|
patch.object(assets_module, "_run_git_command", fake_git),
|
||||||
):
|
):
|
||||||
return _resolve()
|
return _resolve()
|
||||||
|
|
||||||
def test_tag_with_zero_commits_since_returns_bare_tag(self):
|
def test_tag_with_sha_returns_tag_plus_sha(self):
|
||||||
result = self._run_with_git({"tag": "v2.0.0", "sha": "abcdef1", "commits_since_tag": "0"})
|
result = self._run_with_git({"tag": "v2.0.0", "sha": "abcdef1"})
|
||||||
self.assertEqual(result, "v2.0.0")
|
self.assertEqual(result, "v2.0.0+gabcdef1")
|
||||||
|
|
||||||
def test_tag_plus_distance_plus_sha_format(self):
|
def test_main_branch_does_not_add_branch_suffix(self):
|
||||||
|
result = self._run_with_git({"tag": "v2.0.0", "sha": "abcdef1", "branch": "main"})
|
||||||
|
self.assertEqual(result, "v2.0.0+gabcdef1")
|
||||||
|
|
||||||
|
def test_non_main_branch_adds_branch_suffix(self):
|
||||||
|
result = self._run_with_git({"tag": "v2.0.0", "sha": "abcdef1", "branch": "dev"})
|
||||||
|
self.assertEqual(result, "v2.0.0-dev+gabcdef1")
|
||||||
|
|
||||||
|
def test_branch_name_is_sanitized_for_version(self):
|
||||||
|
result = self._run_with_git(
|
||||||
|
{"tag": "v2.0.0", "sha": "abcdef1", "branch": "feature/cool build"}
|
||||||
|
)
|
||||||
|
self.assertEqual(result, "v2.0.0-feature-cool-build+gabcdef1")
|
||||||
|
|
||||||
|
def test_commit_distance_is_not_included(self):
|
||||||
result = self._run_with_git({"tag": "v2.0.0", "sha": "abcdef1", "commits_since_tag": "7"})
|
result = self._run_with_git({"tag": "v2.0.0", "sha": "abcdef1", "commits_since_tag": "7"})
|
||||||
self.assertEqual(result, "v2.0.0+7.gabcdef1")
|
self.assertEqual(result, "v2.0.0+gabcdef1")
|
||||||
|
|
||||||
def test_sha_only_when_no_tag(self):
|
def test_sha_only_when_no_tag(self):
|
||||||
result = self._run_with_git({"sha": "abcdef1"})
|
result = self._run_with_git({"sha": "abcdef1"})
|
||||||
self.assertEqual(result, "dev+gabcdef1")
|
self.assertEqual(result, "dev+gabcdef1")
|
||||||
|
|
||||||
|
def test_tag_only_when_no_sha(self):
|
||||||
|
result = self._run_with_git({"tag": "v2.0.0"})
|
||||||
|
self.assertEqual(result, "v2.0.0")
|
||||||
|
|
||||||
def test_neither_tag_nor_sha_is_unknown(self):
|
def test_neither_tag_nor_sha_is_unknown(self):
|
||||||
result = self._run_with_git({})
|
result = self._run_with_git({})
|
||||||
self.assertEqual(result, "dev+unknown")
|
self.assertEqual(result, "dev+unknown")
|
||||||
|
|
||||||
def test_dirty_suffix_is_appended(self):
|
def test_dirty_suffix_is_not_appended_or_queried(self):
|
||||||
result = self._run_with_git(
|
calls = []
|
||||||
{"tag": "v2.0.0", "sha": "abcdef1", "commits_since_tag": "0"},
|
|
||||||
dirty=True,
|
def fake_git(*args):
|
||||||
)
|
calls.append(args)
|
||||||
self.assertEqual(result, "v2.0.0-dirty")
|
replies = {
|
||||||
|
("describe", "--tags", "--abbrev=0"): "v2.0.0",
|
||||||
|
("rev-parse", "--short", "HEAD"): "abcdef1",
|
||||||
|
("status", "--porcelain"): "M file\n",
|
||||||
|
}
|
||||||
|
return replies.get(args, "")
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
env = _clean_version_env()
|
||||||
|
with (
|
||||||
|
patch.dict(os.environ, env, clear=True),
|
||||||
|
patch.object(assets_module, "APP_ROOT", Path(tmp)),
|
||||||
|
patch.object(assets_module, "_run_git_command", fake_git),
|
||||||
|
):
|
||||||
|
result = _resolve()
|
||||||
|
|
||||||
|
self.assertEqual(result, "v2.0.0+gabcdef1")
|
||||||
|
self.assertNotIn(("status", "--porcelain"), calls)
|
||||||
|
|
||||||
|
|
||||||
class CacheBehaviourTests(unittest.TestCase):
|
class CacheBehaviourTests(unittest.TestCase):
|
||||||
@@ -191,8 +233,7 @@ class CacheBehaviourTests(unittest.TestCase):
|
|||||||
return "abcdef1" if args == ("rev-parse", "--short", "HEAD") else ""
|
return "abcdef1" if args == ("rev-parse", "--short", "HEAD") else ""
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
env = dict(os.environ)
|
env = _clean_version_env()
|
||||||
env.pop("REMNAWAVE_MINISHOP_VERSION", None)
|
|
||||||
with (
|
with (
|
||||||
patch.dict(os.environ, env, clear=True),
|
patch.dict(os.environ, env, clear=True),
|
||||||
patch.object(assets_module, "APP_ROOT", Path(tmp)),
|
patch.object(assets_module, "APP_ROOT", Path(tmp)),
|
||||||
|
|||||||
Reference in New Issue
Block a user