Compare commits

..
36 Commits
Author SHA1 Message Date
devx-ci-bot 66bace57d9 release: v0.21.2
Post-merge / detect-type (push) Successful in 12s
Post-merge / validate-commit-msg (push) Has been skipped
Post-merge / release (push) Has been skipped
Post-merge / sync-wiki (push) Has been skipped
Post-merge / vikunja (push) Has been skipped
Post-merge / configure-repo (push) Has been skipped
Post-merge / publish (push) Has been skipped
Build Images / detect-type (push) Successful in 32s
Build Images / build-and-push (push) Has been skipped
Build Images / cleanup (push) Has been skipped
Post-merge / badges (push) Successful in 31s
2026-06-27 13:46:57 +00:00
emil 5c8d74015e DEVX-79: fix: gate auto-merge on release-dry-run and unmask failures
Post-merge / detect-type (push) Successful in 10s
Post-merge / validate-commit-msg (push) Successful in 10s
Post-merge / sync-wiki (push) Successful in 17s
Post-merge / vikunja (push) Successful in 12s
Post-merge / configure-repo (push) Successful in 11s
Post-merge / release (push) Successful in 30s
Post-merge / badges (push) Successful in 35s
Post-merge / publish (push) Successful in 18s
2026-06-27 13:46:16 +00:00
gitea-actions-bot 48eec986f6 chore: update badge URLs to commit 3a45f8e1 [skip ci] 2026-06-27 13:28:35 +00:00
emil 1fc1cfb23f DEVX-78: style: compact devx-setup-image to pass checkmake maxbodylength (5 lines)
Post-merge / detect-type (push) Successful in 10s
Post-merge / validate-commit-msg (push) Successful in 12s
Post-merge / release (push) Successful in 16s
Build Images / detect-type (push) Successful in 35s
Post-merge / vikunja (push) Successful in 13s
Post-merge / sync-wiki (push) Successful in 24s
Post-merge / publish (push) Has been skipped
Post-merge / configure-repo (push) Successful in 12s
Post-merge / badges (push) Successful in 36s
Build Images / build-and-push (push) Successful in 3m23s
Build Images / cleanup (push) Successful in 1m0s
2026-06-27 13:27:41 +00:00
gitea-actions-bot f24b6914f6 chore: update badge URLs to commit b687262f [skip ci] 2026-06-27 13:24:52 +00:00
gitea-actions-bot 84d02ca221 chore: update badge URLs to commit 6043e937 [skip ci] 2026-06-27 13:24:13 +00:00
devx-ci-bot 18ac8f4ba9 release: v0.21.1
Post-merge / detect-type (push) Successful in 9s
Post-merge / validate-commit-msg (push) Has been skipped
Post-merge / release (push) Has been skipped
Post-merge / sync-wiki (push) Has been skipped
Post-merge / vikunja (push) Has been skipped
Post-merge / configure-repo (push) Has been skipped
Build Images / detect-type (push) Successful in 36s
Post-merge / publish (push) Has been skipped
Build Images / build-and-push (push) Has been skipped
Build Images / cleanup (push) Has been skipped
Post-merge / badges (push) Successful in 32s
2026-06-27 13:24:07 +00:00
emil 9fb9be9c35 DEVX-77: fix: devx-setup-image configures Gitea PyPI registry and shows pip errors
Post-merge / detect-type (push) Successful in 9s
Post-merge / validate-commit-msg (push) Successful in 10s
Post-merge / sync-wiki (push) Successful in 19s
Post-merge / configure-repo (push) Successful in 10s
Post-merge / vikunja (push) Successful in 11s
Post-merge / release (push) Successful in 27s
Build Images / detect-type (push) Successful in 39s
Post-merge / badges (push) Successful in 30s
Post-merge / publish (push) Successful in 16s
Build Images / build-and-push (push) Successful in 3m17s
Build Images / cleanup (push) Successful in 1m15s
2026-06-27 13:23:29 +00:00
gitea-actions-bot 9a46723391 chore: update badge URLs to commit f7a68467 [skip ci] 2026-06-27 13:05:31 +00:00
devx-ci-bot 5828d3f07b release: v0.21.0
Post-merge / sync-wiki (push) Has been skipped
Post-merge / vikunja (push) Has been skipped
Post-merge / publish (push) Has been skipped
Post-merge / configure-repo (push) Has been skipped
Build Images / detect-type (push) Successful in 38s
Build Images / build-and-push (push) Has been skipped
Post-merge / badges (push) Successful in 28s
Build Images / cleanup (push) Has been skipped
Post-merge / detect-type (push) Successful in 8s
Post-merge / validate-commit-msg (push) Has been skipped
Post-merge / release (push) Has been skipped
2026-06-27 13:04:50 +00:00
gitea-actions-bot 4232f4baee chore: update badge URLs to commit 88ae1247 [skip ci] 2026-06-27 13:04:46 +00:00
emil a9fd1a47af DEVX-76: feat: add --auto-login to publish, extract configure_tea_login to gitea_cli
Post-merge / publish (push) Successful in 17s
Post-merge / detect-type (push) Successful in 10s
Post-merge / validate-commit-msg (push) Successful in 10s
Build Images / build-and-push (push) Successful in 3m6s
Post-merge / vikunja (push) Successful in 11s
Build Images / cleanup (push) Successful in 54s
Post-merge / sync-wiki (push) Successful in 18s
Build Images / detect-type (push) Successful in 33s
Post-merge / configure-repo (push) Successful in 10s
Post-merge / badges (push) Successful in 27s
Post-merge / release (push) Successful in 32s
2026-06-27 13:04:06 +00:00
gitea-actions-bot ecd10241fb chore: update badge URLs to commit 022fdc32 [skip ci] 2026-06-27 08:02:52 +00:00
emil 5fb497d108 DEVX-75: fix: remove tag fallback step from release workflow
Post-merge / detect-type (push) Successful in 10s
Post-merge / validate-commit-msg (push) Successful in 9s
Post-merge / release (push) Successful in 10s
Post-merge / sync-wiki (push) Successful in 16s
Post-merge / configure-repo (push) Successful in 9s
Post-merge / vikunja (push) Successful in 10s
Post-merge / publish (push) Has been skipped
Post-merge / badges (push) Successful in 25s
2026-06-27 08:02:04 +00:00
gitea-actions-bot 7d4c32c761 chore: update badge URLs to commit 894ff869 [skip ci] 2026-06-27 07:47:37 +00:00
emil cb8af53c26 DEVX-74: fix: publish job uses setup-release for build + tea login
Post-merge / badges (push) Successful in 26s
Post-merge / detect-type (push) Successful in 9s
Post-merge / validate-commit-msg (push) Successful in 9s
Post-merge / release (push) Successful in 11s
Post-merge / vikunja (push) Successful in 11s
Post-merge / publish (push) Has been skipped
Post-merge / configure-repo (push) Successful in 9s
Post-merge / sync-wiki (push) Successful in 18s
2026-06-27 07:46:55 +00:00
gitea-actions-bot 954ede87a7 chore: update badge URLs to commit 5e80905f [skip ci] 2026-06-27 07:43:15 +00:00
gitea-actions-bot eb30faf027 chore: update badge URLs to commit 2979764a [skip ci] 2026-06-27 07:42:40 +00:00
devx-ci-bot 6c4157b5c6 release: v0.20.3
Post-merge / detect-type (push) Successful in 10s
Post-merge / release (push) Has been skipped
Post-merge / sync-wiki (push) Has been skipped
Post-merge / validate-commit-msg (push) Has been skipped
Post-merge / vikunja (push) Has been skipped
Post-merge / configure-repo (push) Has been skipped
Post-merge / publish (push) Has been skipped
Build Images / detect-type (push) Successful in 40s
Build Images / build-and-push (push) Has been skipped
Post-merge / badges (push) Successful in 29s
Build Images / cleanup (push) Has been skipped
2026-06-27 07:42:30 +00:00
emil ea7ddb2036 DEVX-73: fix: release publish failures and duplicate release commits
Post-merge / detect-type (push) Successful in 10s
Post-merge / validate-commit-msg (push) Successful in 9s
Post-merge / sync-wiki (push) Successful in 17s
Post-merge / vikunja (push) Successful in 12s
Build Images / detect-type (push) Successful in 38s
Post-merge / release (push) Successful in 25s
Post-merge / configure-repo (push) Successful in 10s
Post-merge / badges (push) Successful in 31s
Post-merge / publish (push) Failing after 15s
Build Images / build-and-push (push) Successful in 2m47s
Build Images / cleanup (push) Successful in 51s
2026-06-27 07:41:50 +00:00
gitea-actions-bot ef63ada2f0 chore: update badge URLs to commit bc706211 [skip ci] 2026-06-27 07:21:27 +00:00
gitea-actions-bot cdd5f5a8da chore: update badge URLs to commit a0473322 [skip ci] 2026-06-27 07:20:49 +00:00
devx-ci-bot 63ae375b4b release: v0.20.2
Post-merge / detect-type (push) Successful in 15s
Post-merge / validate-commit-msg (push) Has been skipped
Post-merge / release (push) Has been skipped
Post-merge / sync-wiki (push) Has been skipped
Post-merge / vikunja (push) Has been skipped
Post-merge / configure-repo (push) Has been skipped
Post-merge / publish (push) Has been skipped
Post-merge / badges (push) Successful in 34s
2026-06-27 07:20:10 +00:00
emil 8862ea4639 DEVX-72: ci: add hadolint Dockerfile linter to CI
Post-merge / detect-type (push) Successful in 11s
Post-merge / validate-commit-msg (push) Successful in 8s
Build Images / detect-type (push) Successful in 40s
Post-merge / release (push) Failing after 26s
Build Images / build-and-push (push) Successful in 3m6s
Build Images / cleanup (push) Successful in 49s
Post-merge / sync-wiki (push) Successful in 23s
Post-merge / vikunja (push) Successful in 14s
Post-merge / configure-repo (push) Successful in 14s
Post-merge / publish (push) Has been skipped
Post-merge / badges (push) Successful in 35s
2026-06-27 07:19:31 +00:00
gitea-actions-bot 8ca0a1b208 chore: update badge URLs to commit 55d2bb5c [skip ci] 2026-06-27 03:08:47 +00:00
emil 670f5a099a DEVX-71: ci: use pre-built tier images in CI workflows
Post-merge / detect-type (push) Successful in 9s
Post-merge / validate-commit-msg (push) Successful in 10s
Post-merge / vikunja (push) Successful in 11s
Post-merge / sync-wiki (push) Successful in 18s
Post-merge / configure-repo (push) Successful in 9s
Post-merge / publish (push) Has been skipped
Post-merge / badges (push) Successful in 26s
Post-merge / release (push) Successful in 19s
2026-06-27 03:08:06 +00:00
gitea-actions-bot a5277a0790 chore: update badge URLs to commit 177dda82 [skip ci] 2026-06-27 02:42:47 +00:00
devx-ci-bot 203d16b19d release: v0.20.2
Post-merge / detect-type (push) Successful in 37s
Post-merge / validate-commit-msg (push) Has been skipped
Build Images / detect-type (push) Successful in 40s
Post-merge / release (push) Has been skipped
Post-merge / sync-wiki (push) Has been skipped
Post-merge / configure-repo (push) Has been skipped
Build Images / cleanup (push) Has been skipped
Post-merge / vikunja (push) Has been skipped
Build Images / build-and-push (push) Has been skipped
Post-merge / publish (push) Has been skipped
Post-merge / badges (push) Successful in 57s
2026-06-27 04:41:07 +02:00
gitea-actions-bot 12871fb343 chore: update badge URLs to commit 917159e3 [skip ci] 2026-06-27 04:40:59 +02:00
emil a585ef09b6 DEVX-70: fix: correct sed substitution in ci-full Dockerfile
Build Images / detect-type (push) Successful in 35s
Post-merge / detect-type (push) Successful in 33s
Post-merge / validate-commit-msg (push) Successful in 37s
Post-merge / sync-wiki (push) Successful in 53s
Post-merge / vikunja (push) Successful in 45s
Post-merge / badges (push) Successful in 1m24s
Post-merge / configure-repo (push) Successful in 41s
Post-merge / release (push) Successful in 1m37s
Post-merge / publish (push) Failing after 51s
Build Images / build-and-push (push) Successful in 2m59s
Build Images / cleanup (push) Successful in 1m13s
2026-06-27 02:38:53 +00:00
gitea-actions-bot 92aea7df10 chore: update badge URLs to commit d8f48949 [skip ci] 2026-06-27 04:29:12 +02:00
gitea-actions-bot ee3ad74634 chore: update badge URLs to commit ac4f73ef [skip ci] 2026-06-27 04:27:29 +02:00
devx-ci-bot 626ea67b28 release: v0.20.1
Post-merge / badges (push) Successful in 56s
Build Images / detect-type (push) Successful in 45s
Build Images / cleanup (push) Has been skipped
Build Images / build-and-push (push) Has been skipped
Post-merge / detect-type (push) Successful in 42s
Post-merge / sync-wiki (push) Has been skipped
Post-merge / configure-repo (push) Has been skipped
Post-merge / validate-commit-msg (push) Has been skipped
Post-merge / release (push) Has been skipped
Post-merge / vikunja (push) Has been skipped
Post-merge / publish (push) Has been skipped
2026-06-27 02:26:36 +00:00
emil 87c3fa6634 DEVX-69: fix: correct image references in tier Dockerfiles
Build Images / detect-type (push) Successful in 50s
Post-merge / detect-type (push) Successful in 48s
Post-merge / sync-wiki (push) Successful in 56s
Post-merge / validate-commit-msg (push) Successful in 1m2s
Post-merge / release (push) Successful in 1m21s
Post-merge / vikunja (push) Successful in 49s
Post-merge / configure-repo (push) Successful in 48s
Post-merge / badges (push) Successful in 1m10s
Post-merge / publish (push) Failing after 51s
Build Images / build-and-push (push) Failing after 3m7s
Build Images / cleanup (push) Has been skipped
2026-06-27 02:24:24 +00:00
gitea-actions-bot 048d161192 chore: update badge URLs to commit d600d21c [skip ci] 2026-06-27 01:37:07 +00:00
gitea-actions-bot 762eee4a55 chore: update badge URLs to commit d0ea4c99 [skip ci] 2026-06-27 03:36:02 +02:00
21 changed files with 414 additions and 122 deletions
+31 -8
View File
@@ -8,11 +8,15 @@ on:
jobs:
quality:
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-quality:latest
timeout-minutes: 10
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
- name: Set up environment
run: make setup-quality
run: make setup-image
- name: Lint all
run: |
. .venv/bin/activate
@@ -60,7 +64,11 @@ jobs:
detect-changes:
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
timeout-minutes: 10
defaults:
run:
shell: bash
outputs:
user-facing-changed: ${{ steps.detect.outputs.user-facing-changed }}
steps:
@@ -68,7 +76,7 @@ jobs:
with:
fetch-depth: 0
- name: Set up environment
run: make setup-ci
run: make setup-image
- name: Detect changed paths
id: detect
env:
@@ -84,29 +92,39 @@ jobs:
needs: [quality, detect-changes]
if: needs.detect-changes.outputs.user-facing-changed == 'true'
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-full:latest
timeout-minutes: 10
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up environment
run: make setup-release
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
run: make setup-image
- name: Release dry-run validation
env:
PYTHONPATH: src
run: |
. .venv/bin/activate
export PATH="$HOME/.local/bin:$PATH"
python3 -m devx.ci.release --dry-run || true
python3 -m devx.ci.release --dry-run
pr-review:
if: github.event_name == 'pull_request'
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
timeout-minutes: 10
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
- name: Set up environment
run: make setup-ci
run: make setup-image
- name: Run automated PR review
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
@@ -122,21 +140,26 @@ jobs:
# Auto-merge runs after all CI checks pass. It reads the task ID
# from the branch name, validates the PR title, and squash-merges.
# Uses always() so it runs even when detect-changes skips (no user-facing changes).
needs: [quality, detect-changes, pr-review]
needs: [quality, detect-changes, pr-review, release-dry-run]
if: >-
always() &&
github.event_name == 'pull_request' &&
needs.quality.result == 'success' &&
needs.pr-review.result == 'success'
needs.pr-review.result == 'success' &&
(needs.release-dry-run.result == 'success' || needs.release-dry-run.result == 'skipped')
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
timeout-minutes: 10
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.REPO_TOKEN }}
- name: Set up environment
run: make setup-ci
run: make setup-image
- name: Squash merge with task ID
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
+41 -18
View File
@@ -33,7 +33,11 @@ on:
jobs:
detect-type:
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
timeout-minutes: 10
defaults:
run:
shell: bash
outputs:
is-release: ${{ steps.check.outputs.is-release }}
steps:
@@ -41,7 +45,7 @@ jobs:
with:
fetch-depth: 1
- name: Set up environment
run: make setup-ci
run: make setup-image
- name: Check if this is a release commit
id: check
env:
@@ -54,13 +58,17 @@ jobs:
needs: [detect-type]
if: needs.detect-type.outputs.is-release == 'false'
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
timeout-minutes: 5
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Set up environment
run: make setup-ci
run: make setup-image
- name: Validate latest commit message
env:
PYTHONPATH: src
@@ -74,7 +82,11 @@ jobs:
needs: [detect-type]
if: needs.detect-type.outputs.is-release == 'false'
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-full:latest
timeout-minutes: 15
defaults:
run:
shell: bash
outputs:
tag: ${{ steps.release-tag.outputs.tag }}
steps:
@@ -85,7 +97,7 @@ jobs:
- name: Set up environment
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
run: make setup-release
run: make setup-image
- name: Configure git
run: |
git config user.name "devx-ci-bot"
@@ -98,13 +110,6 @@ jobs:
. .venv/bin/activate
export PATH="$HOME/.local/bin:$PATH"
python3 -m devx.ci.release
- name: Extract tag (fallback if GITHUB_OUTPUT not set)
if: steps.release-tag.outputs.tag == ''
run: |
tag=$(git describe --tags --abbrev=0 2>/dev/null || true)
if [ -n "$tag" ]; then
echo "tag=$tag" >> "$GITHUB_OUTPUT"
fi
- name: Notify on failure
if: failure()
env:
@@ -123,15 +128,17 @@ jobs:
needs: [release]
if: needs.release.outputs.tag != ''
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-full:latest
timeout-minutes: 10
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up environment
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
run: make setup-release
run: make setup-image EXTRAS=release
- name: Build and publish release
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
@@ -139,7 +146,7 @@ jobs:
run: |
. .venv/bin/activate
export PATH="$HOME/.local/bin:$PATH"
python3 -m devx.ci.publish "${{ needs.release.outputs.tag }}" "${{ github.repository }}"
python3 -m devx.ci.publish "${{ needs.release.outputs.tag }}" "${{ github.repository }}" --auto-login
- name: Notify on failure
if: failure()
env:
@@ -158,13 +165,17 @@ jobs:
needs: [detect-type]
if: needs.detect-type.outputs.is-release == 'false'
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
timeout-minutes: 10
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up environment
run: make setup-ci
run: make setup-image
- name: Sync documentation to wiki
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
@@ -189,7 +200,11 @@ jobs:
needs: [detect-type]
if: always()
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-quality:latest
timeout-minutes: 10
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
with:
@@ -201,7 +216,7 @@ jobs:
git fetch origin master
git reset --hard origin/master
- name: Set up environment
run: make setup-ci
run: make setup-image
- name: Generate and push badges
env:
PRE_COMMIT_ALLOW_NO_CONFIG: "1"
@@ -225,13 +240,17 @@ jobs:
needs: [detect-type]
if: needs.detect-type.outputs.is-release == 'false'
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
timeout-minutes: 10
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up environment
run: make setup-ci
run: make setup-image
- name: Update Vikunja task
env:
VIKUNJA_TOKEN: ${{ secrets.VIKUNJA_TOKEN }}
@@ -257,11 +276,15 @@ jobs:
needs: [detect-type]
if: needs.detect-type.outputs.is-release == 'false'
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
timeout-minutes: 10
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
- name: Set up environment
run: make setup-ci
run: make setup-image
- name: Ensure branch protection and labels
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
+14
View File
@@ -0,0 +1,14 @@
# Hadolint configuration for devx Dockerfiles
# https://github.com/hadolint/hadolint#configure
ignored:
- DL3008 # Don't require pinning apt package versions
- DL3013 # Don't require pinning pip package versions
- DL3018 # Don't require pinning apk package versions
- DL3007 # Using latest is intentional for tier images (rebuilt on every merge)
- SC2102 # False positive: pip extras [release,molecule,deploy] look like shell ranges
trustedRegistries:
- git.oblachno.oblachno.fyi
- docker.io
- gitea/runner-images
+1 -1
View File
@@ -469,7 +469,7 @@ Each image is tagged `latest` and pushed to
jobs:
quality:
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images:ci-quality-latest
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-quality:latest
steps:
- uses: actions/checkout@v4
- name: Set up environment
+43
View File
@@ -2,6 +2,49 @@
All notable changes to this project will be documented in this file.
## [0.21.2] - 2026-06-27
### Bug Fixes
- Gate auto-merge on release-dry-run and unmask failures
## [0.21.1] - 2026-06-27
### Bug Fixes
- Devx-setup-image configures Gitea PyPI registry and shows pip errors
## [0.21.0] - 2026-06-27
### Features
- Add --auto-login to publish, extract configure_tea_login to gitea_cli
### Bug Fixes
- Publish job uses setup-release for build + tea login
- Remove tag fallback step from release workflow
## [0.20.3] - 2026-06-27
### Bug Fixes
- Release publish failures and duplicate release commits
## [0.20.2] - 2026-06-27
## [0.20.2] - 2026-06-27
### Bug Fixes
- Correct sed substitution in ci-full Dockerfile
## [0.20.1] - 2026-06-27
### Bug Fixes
- Correct image references in tier Dockerfiles
## [0.20.0] - 2026-06-27
### Features
+13 -4
View File
@@ -1,4 +1,4 @@
.PHONY: all setup setup-ci setup-quality setup-release setup-image install update lint lint-all test test-unit pytest-cov clean install-tools install-hooks activate-scripts checkmake check-mutable-globals check-dep-docs check-test-speed build-images push-images build-images-dry-run clean-images
.PHONY: all setup setup-ci setup-quality setup-release setup-image install update lint lint-all lint-dockerfiles test test-unit pytest-cov clean install-tools install-hooks activate-scripts checkmake check-mutable-globals check-dep-docs check-test-speed build-images push-images build-images-dry-run clean-images
PYTHON := python3
VENV := .venv
@@ -25,10 +25,10 @@ setup-quality: $(VENV)/bin/activate .env install-tools
# Setup for release jobs (needs git-cliff, tea, lint tools)
setup-release: $(VENV)/bin/activate .env
@$(BIN)/pip install -e '.[ci,lint]' 2>/dev/null; \
@$(BIN)/pip install -e '.[ci,lint,release]' 2>/dev/null; \
$(BIN)/python -m devx.tools.install_tools --tool git-cliff --tool tea; \
export PATH="$(HOME)/.local/bin:$$PATH"; \
$(BIN)/python -m devx.tools.setup --bin "$(BIN)" --extras "ci,lint" --no-pre-commit
$(BIN)/python -m devx.tools.setup --bin "$(BIN)" --extras "ci,lint,release" --no-pre-commit
# Setup for pre-built image jobs (deps already in image, just link venv + install project)
setup-image:
@@ -96,9 +96,18 @@ create-pr: devx-create-pr
push-with-pr: devx-push-with-pr
git-push: devx-push
lint-all: lint workflow-lint
lint-all: lint workflow-lint lint-dockerfiles
@echo "[lint-all] All linting checks passed."
lint-dockerfiles:
@echo "[lint-dockerfiles] Linting Dockerfiles with hadolint..."
@if command -v hadolint >/dev/null 2>&1; then \
find docker -name 'Dockerfile*' -exec hadolint {} +; \
echo "[lint-dockerfiles] All Dockerfiles passed."; \
else \
echo "[lint-dockerfiles] hadolint not found — skipping (install with: pip install hadolint or download from GitHub)"; \
fi
test-unit: devx-test-unit
pytest-cov: devx-pytest-cov
+6 -6
View File
@@ -16,12 +16,12 @@ quality badges.
[![CI](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions/workflows/ci.yml/badge.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![License: GPL-3.0](https://img.shields.io/badge/license-GPL--3.0-blue)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/src/branch/master/LICENSE)
[![Coverage](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/fd23e5b2b0f6130f9d2c7b6b759eaae1c179d364/coverage.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Tests](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/fd23e5b2b0f6130f9d2c7b6b759eaae1c179d364/tests.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Docs](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/fd23e5b2b0f6130f9d2c7b6b759eaae1c179d364/docs.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
[![Code Quality](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/fd23e5b2b0f6130f9d2c7b6b759eaae1c179d364/quality.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Version](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/fd23e5b2b0f6130f9d2c7b6b759eaae1c179d364/version.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
[![Python](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/fd23e5b2b0f6130f9d2c7b6b759eaae1c179d364/python.svg)](https://www.python.org/downloads/)
[![Coverage](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/3a45f8e1896d9e0e07620fda23c78073514a14b9/coverage.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Tests](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/3a45f8e1896d9e0e07620fda23c78073514a14b9/tests.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Docs](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/3a45f8e1896d9e0e07620fda23c78073514a14b9/docs.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
[![Code Quality](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/3a45f8e1896d9e0e07620fda23c78073514a14b9/quality.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Version](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/3a45f8e1896d9e0e07620fda23c78073514a14b9/version.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
[![Python](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/3a45f8e1896d9e0e07620fda23c78073514a14b9/python.svg)](https://www.python.org/downloads/)
## Why devx?
+4 -2
View File
@@ -7,7 +7,9 @@
# Layers on top of ci-quality: adds release tools, molecule, deploy deps,
# git-cliff, and OpenTofu.
FROM git.oblachno.oblachno.fyi/oblachno-oss/runner-images:ci-quality-latest
FROM git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-quality:latest
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Install devx[release,molecule,deploy] from local source
COPY . /tmp/devx
@@ -18,7 +20,7 @@ RUN pip install --no-cache-dir /tmp/devx[release,molecule,deploy] \
RUN python3 -m devx.tools.install_tools --tool git-cliff
# Install OpenTofu (for infra deploy jobs)
RUN ARCH=$(uname -m | sed 's/x86_64/amd64') \
RUN ARCH=$(uname -m | sed 's/x86_64/amd64/') \
&& VERSION=1.12.3 \
&& curl -fsSL "https://github.com/opentofu/opentofu/releases/download/v${VERSION}/tofu_${VERSION}_$(uname -s | tr '[:upper:]' '[:lower:]')_${ARCH}.tar.gz" \
| tar -xz -C /usr/local/bin tofu
+6 -1
View File
@@ -5,7 +5,7 @@
#
# Layers on top of ci-base: adds lint tools + actionlint + checkmake.
FROM git.oblachno.oblachno.fyi/oblachno-oss/runner-images:ci-base-latest
FROM git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
# Install devx[lint] from local source (adds ruff, pyright, bandit, etc.)
COPY . /tmp/devx
@@ -15,3 +15,8 @@ RUN pip install --no-cache-dir /tmp/devx[lint] \
# Install CI/CD binary tools
RUN python3 -m devx.tools.install_tools --tool actionlint \
&& python3 -m devx.tools.install_checkmake
# Install hadolint (Dockerfile linter)
RUN curl -fsSL "https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64" \
-o /usr/local/bin/hadolint \
&& chmod +x /usr/local/bin/hadolint
+6 -6
View File
@@ -12,12 +12,12 @@ project to be reusable across all oblachno-oss repositories.
[![CI](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions/workflows/ci.yml/badge.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![License: GPL-3.0](https://img.shields.io/badge/license-GPL--3.0-blue)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/src/branch/master/LICENSE)
[![Coverage](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/fd23e5b2b0f6130f9d2c7b6b759eaae1c179d364/coverage.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Tests](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/fd23e5b2b0f6130f9d2c7b6b759eaae1c179d364/tests.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Docs](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/fd23e5b2b0f6130f9d2c7b6b759eaae1c179d364/docs.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
[![Code Quality](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/fd23e5b2b0f6130f9d2c7b6b759eaae1c179d364/quality.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Version](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/fd23e5b2b0f6130f9d2c7b6b759eaae1c179d364/version.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
[![Python](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/fd23e5b2b0f6130f9d2c7b6b759eaae1c179d364/python.svg)](https://www.python.org/downloads/)
[![Coverage](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/3a45f8e1896d9e0e07620fda23c78073514a14b9/coverage.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Tests](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/3a45f8e1896d9e0e07620fda23c78073514a14b9/tests.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Docs](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/3a45f8e1896d9e0e07620fda23c78073514a14b9/docs.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
[![Code Quality](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/3a45f8e1896d9e0e07620fda23c78073514a14b9/quality.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Version](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/3a45f8e1896d9e0e07620fda23c78073514a14b9/version.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
[![Python](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/3a45f8e1896d9e0e07620fda23c78073514a14b9/python.svg)](https://www.python.org/downloads/)
## Overview
+1 -1
View File
@@ -1,3 +1,3 @@
"""devx — reusable development and CI/CD tools for oblachno-oss projects."""
__version__ = "0.20.0"
__version__ = "0.21.2"
+2 -47
View File
@@ -22,14 +22,12 @@ from __future__ import annotations
import logging
import os
import shutil
import subprocess # nosec B404
import click
from dotenv import load_dotenv # pyright: ignore[reportMissingImports,reportUnknownVariableType]
from devx.config import GITEA_API_URL
from devx.gitea_cli import TeaCLI, TeaCLIError
from devx.gitea_cli import TeaCLI, TeaCLIError, configure_tea_login
from devx.i18n import _
load_dotenv()
@@ -37,49 +35,6 @@ load_dotenv()
logger = logging.getLogger("devx")
def _configure_tea_login(login_name: str = "devx") -> None:
"""Configure tea CLI login from REPO_TOKEN and DEVX_GITEA_API_URL.
Idempotent: if a login with the same name already exists, it is not re-added.
Skips silently if tea is not installed or REPO_TOKEN is not set.
"""
tea_bin = shutil.which("tea")
if tea_bin is None:
click.echo("notify_failure: tea not installed — skipping login configuration.")
return
token = os.environ.get("REPO_TOKEN", "")
if not token:
click.echo("notify_failure: REPO_TOKEN not set — skipping login configuration.")
return
gitea_url = GITEA_API_URL.replace("/api/v1", "")
result = subprocess.run( # nosec B603
[tea_bin, "login", "list", "--output", "simple"],
capture_output=True,
text=True,
check=False,
)
if result.returncode == 0 and login_name in result.stdout:
click.echo(f"notify_failure: tea login '{login_name}' already configured.")
return
click.echo(f"notify_failure: configuring tea login '{login_name}' for {gitea_url}...")
subprocess.run( # nosec B603
[tea_bin, "login", "add", "--name", login_name, "--url", gitea_url, "--token", token],
capture_output=True,
text=True,
check=False,
)
subprocess.run( # nosec B603
[tea_bin, "login", "default", login_name],
capture_output=True,
text=True,
check=False,
)
def _create_issue_via_tea(repo: str, title: str, body: str) -> int:
"""Create issue via tea CLI. Returns issue index.
@@ -124,7 +79,7 @@ def main(repo: str, run_id: str, workflow: str, commit: str, auto_login: bool) -
raise click.ClickException(_("ERROR: REPO_TOKEN is not set."))
if auto_login:
_configure_tea_login()
configure_tea_login()
title = f"[CI] {workflow} workflow failed (run #{run_id})"
body = (
+12 -1
View File
@@ -29,7 +29,7 @@ import click
from dotenv import load_dotenv # pyright: ignore[reportMissingImports,reportUnknownVariableType]
from devx.config import GITEA_API_URL
from devx.gitea_cli import TeaCLI, TeaCLIError
from devx.gitea_cli import TeaCLI, TeaCLIError, configure_tea_login
from devx.i18n import _
load_dotenv()
@@ -221,12 +221,20 @@ def is_release_commit(tag: str) -> bool:
help="Auto-detect latest tag and check if HEAD is a release commit. "
"Skips publish if no tag or HEAD is not a release commit for that tag.",
)
@click.option(
"--auto-login",
is_flag=True,
default=False,
help="Configure tea CLI login from REPO_TOKEN before creating the Gitea release. "
"Eliminates the need for a separate tea login step in containerized CI jobs.",
)
def main(
tag: str | None,
repo: str | None,
registry_url: str | None,
skip_build: bool,
from_tag: bool,
auto_login: bool,
) -> None:
if repo is None:
repo = os.environ.get("GITHUB_REPOSITORY", "")
@@ -287,6 +295,9 @@ def main(
tea = TeaCLI(repo=repo)
if auto_login:
configure_tea_login()
# Check if release already exists (idempotent — avoids failure when
# called multiple times, e.g. by both post-merge and publish workflows)
try:
+14
View File
@@ -669,6 +669,20 @@ def main(dry_run: bool, skip_tests: bool, verify: bool) -> None:
return
current_tag = get_latest_tag()
# If the bumped version equals the current tag version, there's nothing
# new to release. git-cliff didn't bump because the commits since the last
# tag don't warrant a version change (e.g., only ci:/chore: commits).
# Creating a release commit with the same version would cause a tag
# conflict.
if current_tag and current_tag.lstrip("v") == new_version:
click.echo(
_(
"Version stays at v{version} — no version bump from git-cliff. "
"Commits since last tag don't warrant a new release. Skipping.",
version=new_version,
)
)
return
click.echo(
_(
"Bumping version: {current} -> v{new_version}",
+52
View File
@@ -40,15 +40,67 @@ Usage::
from __future__ import annotations
import json
import os
import shutil
import subprocess # nosec B404
from typing import Any
import click
from devx.config import GITEA_API_URL
from devx.i18n import _
class TeaCLIError(Exception):
"""Raised when a tea CLI command fails."""
def configure_tea_login(login_name: str = "devx") -> None:
"""Configure tea CLI login from REPO_TOKEN and DEVX_GITEA_API_URL.
Idempotent: if a login with the same name already exists, it is not re-added.
Skips silently if tea is not installed or REPO_TOKEN is not set.
Used by CI scripts (publish, notify_failure) that need tea login but
run in containerized environments where ``make setup`` was not called.
"""
tea_bin = shutil.which("tea")
if tea_bin is None:
click.echo(_("tea not installed — skipping login configuration."))
return
token = os.environ.get("REPO_TOKEN", "")
if not token:
click.echo(_("REPO_TOKEN not set — skipping login configuration."))
return
gitea_url = GITEA_API_URL.replace("/api/v1", "")
result = subprocess.run( # nosec B603
[tea_bin, "login", "list", "--output", "simple"],
capture_output=True,
text=True,
check=False,
)
if result.returncode == 0 and login_name in result.stdout:
click.echo(_("tea login '{name}' already configured.", name=login_name))
return
click.echo(_("Configuring tea login '{name}' for {url}...", name=login_name, url=gitea_url))
subprocess.run( # nosec B603
[tea_bin, "login", "add", "--name", login_name, "--url", gitea_url, "--token", token],
capture_output=True,
text=True,
check=False,
)
subprocess.run( # nosec B603
[tea_bin, "login", "default", login_name],
capture_output=True,
text=True,
check=False,
)
class TeaCLI:
"""Wrapper around the ``tea`` Gitea CLI tool.
+17 -10
View File
@@ -69,6 +69,7 @@ DEVX_PIP_INSTALL := if [ -z "$$REPO_TOKEN" ]; then . ./.env 2>/dev/null; fi; \
.PHONY: devx-clean devx-pre-push
.PHONY: devx-check-mutable-globals devx-check-dep-docs devx-check-test-coverage devx-check-docs devx-check-test-speed
.PHONY: devx-test-unit devx-pytest-cov
.PHONY: devx-setup-image
# ── Vikunja task and PR management ────────────────────────────────────────────
@@ -254,18 +255,24 @@ devx-clean:
#
# When running inside a pre-built Docker runner image (ci-base, ci-quality,
# ci-full), all deps are already installed in /opt/venv. This target links
# the venv and installs the project itself (no-deps, fast).
# Falls back to devx-setup-ci if /opt/venv is not present (local dev).
# the venv and installs the project itself (with optional extras).
#
# Usage:
# make devx-setup-image (runtime deps only)
# make devx-setup-image EXTRAS=lint (runtime + lint deps)
# make devx-setup-image EXTRAS=ci,lint (runtime + ci + lint deps)
#
# Falls back to setup-ci if /opt/venv is not present (local dev).
# Note: the fallback target name is project-specific (setup-ci, not
# devx-setup-ci) — each project defines its own setup-ci target.
devx-setup-image:
@if [ -d /opt/venv ]; then \
ln -sf /opt/venv $(DEVX_VENV); \
. $(DEVX_BIN)/activate && pip install -e . --no-deps 2>/dev/null; \
echo "[devx-setup-image] Linked /opt/venv and installed project (no-deps)."; \
else \
echo "[devx-setup-image] /opt/venv not found — falling back to devx-setup-ci"; \
$(MAKE) devx-setup-ci; \
fi
@if [ -d /opt/venv ]; then ln -sf /opt/venv $(DEVX_VENV); . $(DEVX_BIN)/activate; \
_U="$${DEVX_GITEA_PYPI_USER:-$${GITEA_PYPI_USER:-emil}}"; \
if [ -n "$$REPO_TOKEN" ]; then export PIP_EXTRA_INDEX_URL="https://$$_U:$$REPO_TOKEN@$(DEVX_GITEA_PYPI_HOST)/api/packages/$(DEVX_GITEA_PYPI_ORG)/pypi/simple/"; fi; \
pip install -e .$(if $(EXTRAS),[$(EXTRAS)],); \
echo "[devx-setup-image] Linked /opt/venv$(if $(EXTRAS), with [$(EXTRAS)],)."; \
else echo "[devx-setup-image] /opt/venv not found — falling back to setup-ci"; $(MAKE) setup-ci; fi
# ── Docker image build / push / cleanup ───────────────────────────────────────
#
+40
View File
@@ -1990,5 +1990,45 @@
"pl": "{file} już istnieje. Użyj --force, aby nadpisać.",
"ru": "{file} already exists. Use --force to overwrite.",
"zh": "{file} already exists. Use --force to overwrite."
},
"Version stays at v{version} — no version bump from git-cliff. Commits since last tag don't warrant a new release. Skipping.": {
"bg": "",
"de": "",
"en": "Version stays at v{version} — no version bump from git-cliff. Commits since last tag don't warrant a new release. Skipping.",
"pl": "",
"ru": "",
"zh": ""
},
"tea not installed — skipping login configuration.": {
"bg": "tea not installed — skipping login configuration.",
"de": "tea not installed — skipping login configuration.",
"en": "tea not installed — skipping login configuration.",
"pl": "tea not installed — skipping login configuration.",
"ru": "tea not installed — skipping login configuration.",
"zh": "tea not installed — skipping login configuration."
},
"REPO_TOKEN not set — skipping login configuration.": {
"bg": "REPO_TOKEN not set — skipping login configuration.",
"de": "REPO_TOKEN not set — skipping login configuration.",
"en": "REPO_TOKEN not set — skipping login configuration.",
"pl": "REPO_TOKEN not set — skipping login configuration.",
"ru": "REPO_TOKEN not set — skipping login configuration.",
"zh": "REPO_TOKEN not set — skipping login configuration."
},
"tea login '{name}' already configured.": {
"bg": "tea login '{name}' already configured.",
"de": "tea login '{name}' already configured.",
"en": "tea login '{name}' already configured.",
"pl": "tea login '{name}' already configured.",
"ru": "tea login '{name}' already configured.",
"zh": "tea login '{name}' already configured."
},
"Configuring tea login '{name}' for {url}...": {
"bg": "Configuring tea login '{name}' for {url}...",
"de": "Configuring tea login '{name}' for {url}...",
"en": "Configuring tea login '{name}' for {url}...",
"pl": "Configuring tea login '{name}' for {url}...",
"ru": "Configuring tea login '{name}' for {url}...",
"zh": "Configuring tea login '{name}' for {url}..."
}
}
+35 -1
View File
@@ -7,7 +7,7 @@ from unittest.mock import MagicMock, patch
import pytest
from devx.gitea_cli import TeaCLI, TeaCLIError, _extract_issue_number, _extract_pr_number
from devx.gitea_cli import TeaCLI, TeaCLIError, _extract_issue_number, _extract_pr_number, configure_tea_login
class TestExtractIssueNumber:
@@ -359,3 +359,37 @@ class TestWhoami:
mock_result = MagicMock(returncode=0, stdout="testuser", stderr="")
with patch("subprocess.run", return_value=mock_result):
assert cli.whoami() == "testuser"
class TestConfigureTeaLogin:
@patch.dict("os.environ", {"REPO_TOKEN": ""}, clear=True)
@patch("devx.gitea_cli.shutil.which", return_value="/usr/bin/tea")
def test_no_token_skips(self, mock_which: MagicMock) -> None:
"""configure_tea_login with no token prints skip message and returns."""
configure_tea_login()
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
@patch("devx.gitea_cli.shutil.which", return_value=None)
def test_no_tea_skips(self, mock_which: MagicMock) -> None:
"""configure_tea_login with no tea binary prints skip message and returns."""
configure_tea_login()
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
@patch("devx.gitea_cli.shutil.which", return_value="/usr/bin/tea")
@patch("devx.gitea_cli.subprocess.run")
def test_configures_login_when_not_present(self, mock_subprocess: MagicMock, mock_which: MagicMock) -> None:
"""configure_tea_login adds login when not already configured."""
mock_list = MagicMock(returncode=0, stdout="")
mock_subprocess.return_value = mock_list
configure_tea_login()
assert mock_subprocess.call_count >= 2 # login list + login add + login default
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
@patch("devx.gitea_cli.shutil.which", return_value="/usr/bin/tea")
@patch("devx.gitea_cli.subprocess.run")
def test_skips_when_already_configured(self, mock_subprocess: MagicMock, mock_which: MagicMock) -> None:
"""configure_tea_login skips if login already exists."""
mock_list = MagicMock(returncode=0, stdout="devx https://git.example.com")
mock_subprocess.return_value = mock_list
configure_tea_login()
assert mock_subprocess.call_count == 1 # only login list, no add
+14 -14
View File
@@ -4,8 +4,8 @@ from unittest.mock import MagicMock, patch
from click.testing import CliRunner
from devx.ci.notify_failure import _configure_tea_login, main
from devx.gitea_cli import TeaCLIError
from devx.ci.notify_failure import main
from devx.gitea_cli import TeaCLIError, configure_tea_login
class TestNotifyFailure:
@@ -116,7 +116,7 @@ class TestNotifyFailure:
assert "REPO_TOKEN" in result.output
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
@patch("devx.ci.notify_failure.shutil.which", return_value=None)
@patch("devx.gitea_cli.shutil.which", return_value=None)
@patch("devx.ci.notify_failure.TeaCLI")
def test_auto_login_no_tea_skips(self, mock_tea_cls: MagicMock, mock_which: MagicMock) -> None:
"""--auto-login with tea not installed skips login and still creates issue."""
@@ -134,7 +134,7 @@ class TestNotifyFailure:
assert "issue #60" in result.output
@patch.dict("os.environ", {"REPO_TOKEN": ""}, clear=True)
@patch("devx.ci.notify_failure.shutil.which", return_value="/usr/bin/tea")
@patch("devx.gitea_cli.shutil.which", return_value="/usr/bin/tea")
@patch("devx.ci.notify_failure.TeaCLI")
def test_auto_login_no_token_skips_login(self, mock_tea_cls: MagicMock, mock_which: MagicMock) -> None:
"""--auto-login with no REPO_TOKEN skips login but raises before creating issue."""
@@ -152,20 +152,20 @@ class TestNotifyFailure:
class TestConfigureTeaLogin:
@patch.dict("os.environ", {"REPO_TOKEN": ""}, clear=True)
@patch("devx.ci.notify_failure.shutil.which", return_value="/usr/bin/tea")
@patch("devx.gitea_cli.shutil.which", return_value="/usr/bin/tea")
def test_no_token_skips(self, mock_which: MagicMock) -> None:
"""_configure_tea_login with no token prints skip message and returns."""
_configure_tea_login()
"""configure_tea_login with no token prints skip message and returns."""
configure_tea_login()
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
@patch("devx.ci.notify_failure.shutil.which", return_value=None)
@patch("devx.gitea_cli.shutil.which", return_value=None)
def test_no_tea_skips(self, mock_which: MagicMock) -> None:
"""_configure_tea_login with no tea binary prints skip message and returns."""
_configure_tea_login()
"""configure_tea_login with no tea binary prints skip message and returns."""
configure_tea_login()
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
@patch("devx.ci.notify_failure.shutil.which", return_value="/usr/bin/tea")
@patch("devx.ci.notify_failure.subprocess.run")
@patch("devx.gitea_cli.shutil.which", return_value="/usr/bin/tea")
@patch("devx.gitea_cli.subprocess.run")
@patch("devx.ci.notify_failure.TeaCLI")
def test_auto_login_configures_tea(
self, mock_tea_cls: MagicMock, mock_subprocess: MagicMock, mock_which: MagicMock
@@ -192,8 +192,8 @@ class TestConfigureTeaLogin:
assert mock_subprocess.call_count >= 2
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
@patch("devx.ci.notify_failure.shutil.which", return_value="/usr/bin/tea")
@patch("devx.ci.notify_failure.subprocess.run")
@patch("devx.gitea_cli.shutil.which", return_value="/usr/bin/tea")
@patch("devx.gitea_cli.subprocess.run")
@patch("devx.ci.notify_failure.TeaCLI")
def test_auto_login_skips_if_already_configured(
self, mock_tea_cls: MagicMock, mock_subprocess: MagicMock, mock_which: MagicMock
+34
View File
@@ -551,3 +551,37 @@ class TestFromTag:
result = runner.invoke(main, ["", "owner/repo", "--skip-build"])
assert result.exit_code != 0
assert "Tag is required" in result.output
class TestPublishAutoLogin:
"""Tests for --auto-login flag in publish."""
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
@patch("devx.ci.publish.configure_tea_login")
@patch("devx.ci.publish.TeaCLI")
def test_auto_login_calls_configure(self, mock_tea_cls: MagicMock, mock_login: MagicMock) -> None:
"""--auto-login calls configure_tea_login before creating release."""
mock_tea = MagicMock()
mock_tea.list_releases.return_value = []
mock_tea.create_release.return_value = {"tag_name": "v1.0.0"}
mock_tea_cls.return_value = mock_tea
with patch("devx.ci.publish.generate_release_notes", return_value="notes"):
runner = CliRunner()
result = runner.invoke(main, ["v1.0.0", "owner/repo", "--skip-build", "--auto-login"])
assert result.exit_code == 0
mock_login.assert_called_once()
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
@patch("devx.ci.publish.configure_tea_login")
@patch("devx.ci.publish.TeaCLI")
def test_no_auto_login_skips_configure(self, mock_tea_cls: MagicMock, mock_login: MagicMock) -> None:
"""Without --auto-login, configure_tea_login is not called."""
mock_tea = MagicMock()
mock_tea.list_releases.return_value = []
mock_tea.create_release.return_value = {"tag_name": "v1.0.0"}
mock_tea_cls.return_value = mock_tea
with patch("devx.ci.publish.generate_release_notes", return_value="notes"):
runner = CliRunner()
result = runner.invoke(main, ["v1.0.0", "owner/repo", "--skip-build"])
assert result.exit_code == 0
mock_login.assert_not_called()
+28 -2
View File
@@ -1207,7 +1207,7 @@ class TestMain:
@patch("devx.ci.release.update_init_version")
@patch("devx.ci.release.get_changelog", return_value="changelog")
@patch("devx.ci.release.get_latest_tag", return_value="v0.1.0")
@patch("devx.ci.release.get_bumped_version", return_value="0.1.0")
@patch("devx.ci.release.get_bumped_version", return_value="0.2.0")
@patch("devx.ci.release.has_unreleased_changes", return_value=True)
@patch("devx.ci.release.run_cmd")
def test_full_flow_tag_exists(
@@ -1232,7 +1232,33 @@ class TestMain:
result = runner.invoke(main, [])
assert result.exit_code == 0
assert "already existed" in result.output
mock_tag.assert_called_once_with("0.1.0", "changelog", False)
mock_tag.assert_called_once_with("0.2.0", "changelog", False)
@patch.dict("os.environ", {})
@patch("devx.ci.release.verify_tag_consistency", return_value=[])
@patch("devx.ci.release.fetch_tags")
@patch("devx.ci.release.has_user_facing_changes", return_value=True)
@patch("devx.ci.release.get_latest_tag", return_value="v0.1.0")
@patch("devx.ci.release.get_bumped_version", return_value="0.1.0")
@patch("devx.ci.release.has_unreleased_changes", return_value=True)
@patch("devx.ci.release.run_cmd")
def test_skips_when_version_doesnt_bump(
self,
mock_run_cmd: MagicMock,
mock_has: MagicMock,
mock_bumped: MagicMock,
mock_latest: MagicMock,
mock_user: MagicMock,
mock_ft: MagicMock,
mock_vtc: MagicMock,
) -> None:
"""Release is skipped when git-cliff doesn't bump the version."""
mock_run_cmd.return_value = MagicMock(returncode=0, stdout="master\n", stderr="")
runner = CliRunner()
result = runner.invoke(main, [])
assert result.exit_code == 0
assert "no version bump" in result.output
assert "Skipping" in result.output
@patch.dict("os.environ", {})
@patch("devx.ci.release.verify_tag_consistency", return_value=[])