Public Access
DEVX-6: fix: correct version tags, changelog, and release script recovery
This commit is contained in:
+25
-18
@@ -2,14 +2,31 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [0.1.0] - 2026-06-22
|
||||
## [0.4.0] - 2026-06-22
|
||||
|
||||
## [0.1.0] - 2026-06-22
|
||||
### Features
|
||||
|
||||
## [0.1.0] - 2026-06-22
|
||||
- Add DEFAULT_INFRASTRUCTURE and configurable task prefix
|
||||
## [0.3.0] - 2026-06-22
|
||||
|
||||
## [0.1.0] - 2026-06-22
|
||||
### Features
|
||||
|
||||
- Add --no-ansible-collections option to setup tool
|
||||
## [0.2.0] - 2026-06-22
|
||||
|
||||
### Features
|
||||
|
||||
- Pluggable change classification framework
|
||||
## [0.1.2] - 2026-06-22
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Make sync-wiki and vikunja depend on release
|
||||
## [0.1.1] - 2026-06-22
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Disable push whitelist, allow direct pushes to master
|
||||
## [0.1.0] - 2026-06-22
|
||||
|
||||
## [0.1.0] - 2026-06-22
|
||||
@@ -18,18 +35,8 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
- Extract reusable dev/CI tools from GRM into devx package
|
||||
|
||||
## [unreleased]
|
||||
|
||||
### Features
|
||||
|
||||
- Extract reusable development and CI/CD tools from GRM into a standalone Python package
|
||||
- Port core modules: config, exceptions, i18n, api_clients, gitea_cli
|
||||
- Port 14 CI scripts: auto_merge, check_translations, classify_changes, detect_release_commit, discover_runners, doc_coverage, notify_failure, post_merge, pr_review, publish, push_badges, release, sync_wiki, validate_commit_msg
|
||||
- Port 6 dev tools: check_test_speed, generate_badges, install_checkmake, install_tools, setup, configure_repo
|
||||
- Port 5 molecule tools as optional extra: platforms, distribute_molecule, discover_runners, molecule_ci_guard, molecule_all
|
||||
- Add CLI entry point with subcommands: devx ci, devx tools, devx molecule
|
||||
- Add Gitea PyPI registry publishing support in publish.py
|
||||
- Add configurable workflow-only patterns in classify_changes.py
|
||||
- Add configurable version file path in release.py
|
||||
- Replicate GRM's automated workflow: CI, auto-merge, post-merge, release, badges, wiki sync, Vikunja
|
||||
### Bug Fixes
|
||||
|
||||
- Use python3 and venv python in workflows and Makefile
|
||||
- Fix post-merge job failures (configure-repo, badges, notify-failure)
|
||||
- Allow release bot to push to protected master
|
||||
|
||||
+5
-2
@@ -39,8 +39,8 @@ sort_commits = "oldest"
|
||||
recurse_submodules = false
|
||||
|
||||
commit_preprocessors = [
|
||||
# Strip DEVX-N task ID prefix from merge commits so git-cliff sees conventional commits
|
||||
{ pattern = "^DEVX-\\d+\\s+", replace = "" },
|
||||
# Strip DEVX-N: task ID prefix from squash-merge commits so git-cliff sees conventional commits
|
||||
{ pattern = "^DEVX-\\d+:\\s+", replace = "" },
|
||||
]
|
||||
|
||||
commit_parsers = [
|
||||
@@ -66,3 +66,6 @@ commit_parsers = [
|
||||
features_always_bump_minor = true
|
||||
breaking_always_bump_major = false
|
||||
initial_tag = "0.1.0"
|
||||
# Refactor commits bump patch — structural changes to src/ or pyproject.toml
|
||||
# affect users even though no new feature was added.
|
||||
refactor_always_bump_patch = true
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""devx — reusable development and CI/CD tools for oblachno-oss projects."""
|
||||
|
||||
__version__ = "0.1.0"
|
||||
__version__ = "0.4.0"
|
||||
|
||||
+22
-4
@@ -271,17 +271,35 @@ def main(dry_run: bool, skip_tests: bool) -> None:
|
||||
)
|
||||
)
|
||||
|
||||
# Release lock: if HEAD is already a release commit, another release
|
||||
# run is in progress (or already completed). Skip to prevent duplicate tags.
|
||||
# Release lock: if HEAD is already a release commit, check if the tag
|
||||
# exists. If the tag is missing (e.g., tag push failed in a previous run),
|
||||
# create and push it instead of skipping — this recovers from the
|
||||
# common failure mode where the commit was pushed but the tag was not.
|
||||
head_msg = run_cmd(["git", "log", "-1", "--pretty=%s"]).stdout.strip()
|
||||
if re.match(r"^release: v\d+\.\d+\.\d+", head_msg):
|
||||
release_match = re.match(r"^release: v(\d+\.\d+\.\d+)", head_msg)
|
||||
if release_match:
|
||||
release_version = release_match.group(1)
|
||||
release_tag = f"v{release_version}"
|
||||
if tag_exists(release_tag):
|
||||
click.echo(
|
||||
_(
|
||||
"HEAD is already a release commit ('{msg}'). Another release may have just completed. Skipping.",
|
||||
"HEAD is already a release commit ('{msg}') and tag {tag} exists. Skipping.",
|
||||
msg=head_msg,
|
||||
tag=release_tag,
|
||||
)
|
||||
)
|
||||
return
|
||||
# Tag is missing — recover by creating and pushing it
|
||||
click.echo(
|
||||
_(
|
||||
"HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.",
|
||||
msg=head_msg,
|
||||
tag=release_tag,
|
||||
)
|
||||
)
|
||||
changelog = get_changelog(release_version)
|
||||
create_and_push_tag(release_version, changelog, dry_run)
|
||||
return
|
||||
|
||||
# Check if any user-facing files changed since the last tag.
|
||||
# If only workflow/infra files changed, skip the release entirely.
|
||||
|
||||
@@ -651,5 +651,11 @@
|
||||
},
|
||||
"Unknown check category '{check}'. Available: all, user-facing{tags}": {
|
||||
"en": "Unknown check category '{check}'. Available: all, user-facing{tags}"
|
||||
},
|
||||
"HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.": {
|
||||
"en": "HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag."
|
||||
},
|
||||
"HEAD is already a release commit ('{msg}') and tag {tag} exists. Skipping.": {
|
||||
"en": "HEAD is already a release commit ('{msg}') and tag {tag} exists. Skipping."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,12 +318,15 @@ class TestMain:
|
||||
@patch.dict("os.environ", {})
|
||||
@patch("devx.ci.release.has_user_facing_changes", return_value=True)
|
||||
@patch("devx.ci.release.run_cmd")
|
||||
def test_release_lock_skips_when_head_is_release_commit(self, mock_run_cmd: MagicMock, mock_uf: MagicMock) -> None:
|
||||
"""If HEAD is already a release commit, should skip to prevent duplicate releases."""
|
||||
# First call: git rev-parse (master), second: git log -1 (release commit)
|
||||
def test_release_lock_skips_when_head_is_release_commit_and_tag_exists(
|
||||
self, mock_run_cmd: MagicMock, mock_uf: MagicMock
|
||||
) -> None:
|
||||
"""If HEAD is a release commit and the tag exists, skip."""
|
||||
# git rev-parse, git log -1, git tag -l (tag exists)
|
||||
mock_run_cmd.side_effect = [
|
||||
MagicMock(returncode=0, stdout="master\n", stderr=""),
|
||||
MagicMock(returncode=0, stdout="release: v0.5.0\n", stderr=""),
|
||||
MagicMock(returncode=0, stdout="v0.5.0\n", stderr=""), # tag -l finds tag
|
||||
]
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, [])
|
||||
@@ -331,6 +334,27 @@ class TestMain:
|
||||
assert "already a release commit" in result.output
|
||||
assert "Skipping" in result.output
|
||||
|
||||
@patch.dict("os.environ", {})
|
||||
@patch("devx.ci.release.get_changelog", return_value="## changelog")
|
||||
@patch("devx.ci.release.create_and_push_tag", return_value=True)
|
||||
@patch("devx.ci.release.run_cmd")
|
||||
def test_release_lock_recovers_when_tag_missing(
|
||||
self, mock_run_cmd: MagicMock, mock_create_tag: MagicMock, mock_changelog: MagicMock
|
||||
) -> None:
|
||||
"""If HEAD is a release commit but the tag is missing, create the tag."""
|
||||
# git rev-parse, git log -1, git tag -l (tag NOT found)
|
||||
mock_run_cmd.side_effect = [
|
||||
MagicMock(returncode=0, stdout="master\n", stderr=""),
|
||||
MagicMock(returncode=0, stdout="release: v0.5.0\n", stderr=""),
|
||||
MagicMock(returncode=0, stdout="", stderr=""), # tag -l finds nothing
|
||||
]
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, [])
|
||||
assert result.exit_code == 0
|
||||
assert "tag v0.5.0 is missing" in result.output
|
||||
assert "Recovering" in result.output
|
||||
mock_create_tag.assert_called_once_with("0.5.0", "## changelog", False)
|
||||
|
||||
@patch.dict("os.environ", {})
|
||||
@patch("devx.ci.release.has_user_facing_changes", return_value=True)
|
||||
@patch("devx.ci.release.has_unreleased_changes", return_value=False)
|
||||
|
||||
Reference in New Issue
Block a user