Public Access
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
621b9936c8 | ||
|
|
33063038a1 |
+31
-18
@@ -2,14 +2,37 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## [0.1.0] - 2026-06-22
|
## [0.4.1] - 2026-06-22
|
||||||
|
|
||||||
## [0.1.0] - 2026-06-22
|
### Bug Fixes
|
||||||
|
|
||||||
## [0.1.0] - 2026-06-22
|
- Correct version tags, changelog, and release script recovery
|
||||||
|
|
||||||
## [0.1.0] - 2026-06-22
|
## [0.4.0] - 2026-06-22
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Add DEFAULT_INFRASTRUCTURE and configurable task prefix
|
||||||
|
## [0.3.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
|
||||||
|
|
||||||
## [0.1.0] - 2026-06-22
|
## [0.1.0] - 2026-06-22
|
||||||
@@ -18,18 +41,8 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
- Extract reusable dev/CI tools from GRM into devx package
|
- Extract reusable dev/CI tools from GRM into devx package
|
||||||
|
|
||||||
## [unreleased]
|
### Bug Fixes
|
||||||
|
|
||||||
### 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
|
|
||||||
|
|
||||||
|
- 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
|
recurse_submodules = false
|
||||||
|
|
||||||
commit_preprocessors = [
|
commit_preprocessors = [
|
||||||
# Strip DEVX-N task ID prefix from merge commits so git-cliff sees conventional commits
|
# Strip DEVX-N: task ID prefix from squash-merge commits so git-cliff sees conventional commits
|
||||||
{ pattern = "^DEVX-\\d+\\s+", replace = "" },
|
{ pattern = "^DEVX-\\d+:\\s+", replace = "" },
|
||||||
]
|
]
|
||||||
|
|
||||||
commit_parsers = [
|
commit_parsers = [
|
||||||
@@ -66,3 +66,6 @@ commit_parsers = [
|
|||||||
features_always_bump_minor = true
|
features_always_bump_minor = true
|
||||||
breaking_always_bump_major = false
|
breaking_always_bump_major = false
|
||||||
initial_tag = "0.1.0"
|
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."""
|
"""devx — reusable development and CI/CD tools for oblachno-oss projects."""
|
||||||
|
|
||||||
__version__ = "0.1.0"
|
__version__ = "0.4.1"
|
||||||
|
|||||||
+22
-4
@@ -271,16 +271,34 @@ def main(dry_run: bool, skip_tests: bool) -> None:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Release lock: if HEAD is already a release commit, another release
|
# Release lock: if HEAD is already a release commit, check if the tag
|
||||||
# run is in progress (or already completed). Skip to prevent duplicate tags.
|
# 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()
|
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}') and tag {tag} exists. Skipping.",
|
||||||
|
msg=head_msg,
|
||||||
|
tag=release_tag,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
# Tag is missing — recover by creating and pushing it
|
||||||
click.echo(
|
click.echo(
|
||||||
_(
|
_(
|
||||||
"HEAD is already a release commit ('{msg}'). Another release may have just completed. Skipping.",
|
"HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.",
|
||||||
msg=head_msg,
|
msg=head_msg,
|
||||||
|
tag=release_tag,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
changelog = get_changelog(release_version)
|
||||||
|
create_and_push_tag(release_version, changelog, dry_run)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Check if any user-facing files changed since the last tag.
|
# Check if any user-facing files changed since the last tag.
|
||||||
|
|||||||
@@ -651,5 +651,11 @@
|
|||||||
},
|
},
|
||||||
"Unknown check category '{check}'. Available: all, user-facing{tags}": {
|
"Unknown check category '{check}'. Available: all, user-facing{tags}": {
|
||||||
"en": "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.dict("os.environ", {})
|
||||||
@patch("devx.ci.release.has_user_facing_changes", return_value=True)
|
@patch("devx.ci.release.has_user_facing_changes", return_value=True)
|
||||||
@patch("devx.ci.release.run_cmd")
|
@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:
|
def test_release_lock_skips_when_head_is_release_commit_and_tag_exists(
|
||||||
"""If HEAD is already a release commit, should skip to prevent duplicate releases."""
|
self, mock_run_cmd: MagicMock, mock_uf: MagicMock
|
||||||
# First call: git rev-parse (master), second: git log -1 (release commit)
|
) -> 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 = [
|
mock_run_cmd.side_effect = [
|
||||||
MagicMock(returncode=0, stdout="master\n", stderr=""),
|
MagicMock(returncode=0, stdout="master\n", stderr=""),
|
||||||
MagicMock(returncode=0, stdout="release: v0.5.0\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()
|
runner = CliRunner()
|
||||||
result = runner.invoke(main, [])
|
result = runner.invoke(main, [])
|
||||||
@@ -331,6 +334,27 @@ class TestMain:
|
|||||||
assert "already a release commit" in result.output
|
assert "already a release commit" in result.output
|
||||||
assert "Skipping" 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.dict("os.environ", {})
|
||||||
@patch("devx.ci.release.has_user_facing_changes", return_value=True)
|
@patch("devx.ci.release.has_user_facing_changes", return_value=True)
|
||||||
@patch("devx.ci.release.has_unreleased_changes", return_value=False)
|
@patch("devx.ci.release.has_unreleased_changes", return_value=False)
|
||||||
|
|||||||
Reference in New Issue
Block a user