Skip to content

Commit 7df82e9

Browse files
authored
Merge pull request #553 from nanotaboada/ci/verify-tag-reachable-from-master-549
ci(cd): verify tag commit is reachable from master (#549)
2 parents e0c074d + 5169a71 commit 7df82e9

File tree

4 files changed

+45
-11
lines changed

4 files changed

+45
-11
lines changed

.github/workflows/python-cd.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ jobs:
2424
with:
2525
fetch-depth: 0
2626

27+
- name: Verify tag commit is reachable from master
28+
run: |
29+
if ! git merge-base --is-ancestor ${{ github.sha }} origin/master; then
30+
echo "Error: Tag commit ${{ github.sha }} is not reachable from master."
31+
echo "Tags must only be created from master after the release PR is merged."
32+
exit 1
33+
fi
34+
echo "Verified: tag commit ${{ github.sha }} is reachable from master."
35+
2736
- name: Extract version from tag
2837
id: version
2938
run: |

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ This project uses famous football coaches as release codenames, following an A-Z
4444

4545
### Added
4646

47+
- CD workflow now verifies tag commit is reachable from `master` before
48+
proceeding with build and publish steps (#549)
49+
4750
### Changed
4851

4952
- `GET /players/` cache check changed from `if not players` to

tests/conftest.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import warnings
2+
from typing import Any, Generator
3+
24
import pytest
35
from fastapi.testclient import TestClient
46
from main import app
7+
from tests.player_stub import Player, nonexistent_player
58

69
# Suppress the DeprecationWarning from httpx
710
warnings.filterwarnings("ignore", category=DeprecationWarning)
@@ -20,3 +23,17 @@ def client():
2023
"""
2124
with TestClient(app) as test_client:
2225
yield test_client
26+
27+
28+
@pytest.fixture(scope="function")
29+
def nonexistent_player_in_db(client: Any) -> Generator[Player, None, None]:
30+
"""
31+
Creates the nonexistent player in the database and removes it on teardown.
32+
33+
Yields:
34+
Player: The player stub created in the database.
35+
"""
36+
player: Player = nonexistent_player()
37+
client.post("/players/", json=player.__dict__)
38+
yield player
39+
client.delete(f"/players/squadnumber/{player.squad_number}")

tests/test_main.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,17 @@ def test_request_post_player_body_nonexistent_response_status_created(client):
194194
"""POST /players/ with nonexistent player returns 201 Created with a valid UUID"""
195195
# Arrange
196196
player = nonexistent_player()
197-
# Act
198-
response = client.post(PATH, json=player.__dict__)
199-
# Assert
200-
assert response.status_code == 201
201-
body = response.json()
202-
assert "id" in body
203-
assert UUID(body["id"]).version == 4 # UUID v4 (API-created)
197+
try:
198+
# Act
199+
response = client.post(PATH, json=player.__dict__)
200+
# Assert
201+
assert response.status_code == 201
202+
body = response.json()
203+
assert "id" in body
204+
assert UUID(body["id"]).version == 4 # UUID v4 (API-created)
205+
finally:
206+
# Teardown — remove the created player
207+
client.delete(PATH + "squadnumber/" + str(player.squad_number))
204208

205209

206210
# PUT /players/squadnumber/{squad_number} --------------------------------------
@@ -278,11 +282,12 @@ def test_request_delete_player_squadnumber_unknown_response_status_not_found(cli
278282
assert response.status_code == 404
279283

280284

281-
def test_request_delete_player_squadnumber_existing_response_status_no_content(client):
285+
def test_request_delete_player_squadnumber_existing_response_status_no_content(
286+
client, nonexistent_player_in_db
287+
):
282288
"""DELETE /players/squadnumber/{squad_number} with existing number returns 204 No Content"""
283-
# Arrange — create the player to be deleted
284-
player = nonexistent_player()
285-
client.post(PATH, json=player.__dict__)
289+
# Arrange
290+
player = nonexistent_player_in_db
286291
# Act
287292
response = client.delete(PATH + "squadnumber/" + str(player.squad_number))
288293
# Assert

0 commit comments

Comments
 (0)