Skip to content

Commit b3d62a3

Browse files
authored
feat: implement CI/CD pipeline with linting, testing, release workflows
feat: implement CI/CD pipeline with linting, testing, release workflows
2 parents 3a1bc2c + 5590ee9 commit b3d62a3

7 files changed

Lines changed: 158 additions & 40 deletions

File tree

.github/workflows/ci-cd.yaml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: CI/CD Pipeline
2+
3+
on:
4+
push:
5+
branches:
6+
- '**'
7+
tags:
8+
- 'v*.*.*'
9+
pull_request:
10+
branches:
11+
- '**'
12+
workflow_dispatch:
13+
14+
jobs:
15+
lint:
16+
uses: ./.github/workflows/ruff.yaml
17+
18+
test:
19+
needs: lint
20+
uses: ./.github/workflows/pytest.yaml
21+
22+
docker:
23+
needs: test
24+
uses: ./.github/workflows/docker-build-and-scan.yaml
25+
with:
26+
DOCKER_PATH_CONTEXT: .
27+
DOCKER_BUILD_DOCKERFILE: ./Dockerfile
28+
DOCKER_TAGS: ${{ github.repository }}:${{ github.sha }}
29+
DOCKER_LOAD_BOOL: false
30+
DOCKER_PUSH_BOOL: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }}
31+
secrets: inherit
32+
33+
release:
34+
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
35+
needs: [test, docker]
36+
uses: ./.github/workflows/release.yaml
37+
secrets: inherit

.github/workflows/pytest.yaml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
1-
name: Unit Test & Coverage
1+
name: Unit Test & Coverage (Reusable)
22

33
on:
4-
pull_request:
5-
branches:
6-
- '*'
4+
workflow_call:
75

86
jobs:
97
unit-test-coverage:
108
runs-on: ubuntu-latest
119
permissions:
1210
contents: read
1311
env:
14-
UV_VERSION: '0.10.2'
12+
UV_VERSION: '>=0.10.2'
1513
PYTHON_VERSION: '3.13'
1614
steps:
1715
- name: Checkout repository
@@ -45,7 +43,7 @@ jobs:
4543
uv run -- coverage report --show-missing
4644
4745
- name: Save uv caches
48-
if: steps.cache-restore.outputs.cache-hit != 'true'
46+
if: always()
4947
uses: actions/cache/save@v5
5048
with:
5149
path: |

.github/workflows/release.yaml

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
name: UV - Release
2-
1+
name: UV - Release (Reusable)
32
on:
4-
push:
5-
branches:
6-
- main
7-
3+
workflow_call:
84
jobs:
95
Semantic-Release:
106
runs-on: ubuntu-latest
@@ -19,7 +15,7 @@ jobs:
1915
dist_artifacts_name: dist
2016
dist_artifacts_dir: dist
2117
lock_file_artifact: uv.lock
22-
UV_VERSION: '0.10.2'
18+
UV_VERSION: '>=0.10.2'
2319
PYTHON_VERSION: '3.13'
2420
GITHUB_ACTIONS_AUTHOR_NAME: github-actions
2521
GITHUB_ACTIONS_AUTHOR_EMAIL: actions@users.noreply.github.com
@@ -28,11 +24,9 @@ jobs:
2824
uses: actions/checkout@v6
2925
with:
3026
ref: ${{ github.ref_name }}
31-
3227
- name: Setup | Force release branch to be at workflow sha
3328
run: |
3429
git reset --hard ${{ github.sha }}
35-
3630
- name: Restore global uv cache
3731
id: cache-restore
3832
uses: actions/cache/restore@v5
@@ -44,31 +38,25 @@ jobs:
4438
key: uv-main-${{ env.UV_VERSION }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('pyproject.toml', 'uv.lock') }}
4539
restore-keys: |
4640
uv-main-
47-
4841
- name: Install uv
4942
uses: astral-sh/setup-uv@v7
5043
with:
5144
version: ${{ env.UV_VERSION }}
5245
python-version: ${{ env.PYTHON_VERSION }}
5346
enable-cache: false
54-
5547
- name: Action | Semantic Version Release
5648
id: release
5749
uses: python-semantic-release/python-semantic-release@v10.5.3
5850
with:
5951
github_token: ${{ secrets.GITHUB_TOKEN }}
60-
git_committer_name: 'github-actions'
61-
git_committer_email: 'actions@users.noreply.github.com'
62-
63-
- name: Publish | Upload to GitHub Release Assets
52+
- name: Action | Create GitHub Release
6453
uses: python-semantic-release/publish-action@v10.5.3
6554
if: steps.release.outputs.released == 'true'
6655
with:
6756
github_token: ${{ secrets.GITHUB_TOKEN }}
6857
tag: ${{ steps.release.outputs.tag }}
69-
7058
- name: Save uv caches
71-
if: steps.cache-restore.outputs.cache-hit != 'true'
59+
if: always()
7260
uses: actions/cache/save@v5
7361
with:
7462
path: |

.github/workflows/ruff.yaml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
1-
name: UV Build - Ruff Lint
1+
name: Ruff Lint (Reusable)
22

33
on:
4-
pull_request:
5-
branches:
6-
- '*'
4+
workflow_call:
75

86
jobs:
97
ruff-lint:
108
runs-on: ubuntu-latest
119
permissions:
1210
contents: read
1311
env:
14-
UV_VERSION: '0.10.2'
12+
UV_VERSION: '>=0.10.2'
1513
PYTHON_VERSION: '3.13'
1614

1715
steps:
@@ -46,7 +44,7 @@ jobs:
4644
uv run ruff check tests
4745
4846
- name: Save uv caches
49-
if: steps.cache-restore.outputs.cache-hit != 'true'
47+
if: always()
5048
uses: actions/cache/save@v5
5149
with:
5250
path: |

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ dependencies = [
2525
build = ["uv >= 0.10.2"]
2626

2727
[build-system]
28-
requires = ["uv_build >= 0.10.2, < 0.11.0"]
28+
requires = ["uv_build >= 0.10.2"]
2929
build-backend = "uv_build"
3030

3131
[project.scripts]

src/sample_python_app/core/display.py

Lines changed: 104 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
"""Handles formatting and displaying astronomical data using rich and pyfiglet."""
22

3+
from datetime import datetime
4+
35
from pyfiglet import Figlet
6+
from rich.align import Align
47
from rich.console import Console
8+
from rich.panel import Panel
9+
from rich.table import Table
10+
from rich.text import Text
511

612
from sample_python_app.core.config import settings
713
from sample_python_app.core.logging import setup_logger
@@ -19,16 +25,107 @@ def display_astronomical_data(astro):
1925
"""
2026
logger = setup_logger(mode="silent")
2127
console = Console()
22-
header = Figlet(font="small", width=100).renderText("Astronomical Data")
23-
logger.info("Displaying Astronomical Data header.")
24-
console.print(f"[bold magenta]{header}[/bold magenta]")
28+
# Synthwave color palette (no longer used)
29+
30+
header = Figlet(font="slant", width=120).renderText("SYNTHWAVE SUNRISE 🌅")
31+
logger.info("Displaying Synthwave Sunrise header.")
32+
header_text = Text(header)
33+
header_text.stylize("bold magenta")
34+
2535
sunrise_local = astro.sunrise.astimezone(settings.tz)
36+
sunset_local = astro.sunset.astimezone(settings.tz)
2637
date_art = Figlet(font="mini", width=150).renderText(
2738
sunrise_local.strftime("%A, %B %d, %Y")
2839
)
29-
logger.info(f'Displaying date: {sunrise_local.strftime("%A, %B %d, %Y")}')
30-
console.print(f"[bold cyan]{date_art}[/bold cyan]")
31-
for name, value in astro.formatted(settings.tz, settings.DATE_FORMAT).items():
40+
logger.info(f'Displaying date: {sunrise_local.strftime("%A, %B %d, %Y")})')
41+
date_text = Text(date_art)
42+
date_text.stylize("bold cyan")
43+
44+
# Stylized sunrise/sunset
45+
sun_art = Figlet(font="starwars", width=120).renderText("SUNRISE")
46+
sun_set_art = Figlet(font="starwars", width=120).renderText("SUNSET")
47+
sun_text = Text(sun_art)
48+
sun_text.stylize("bold yellow")
49+
# Sunrise time as figlet
50+
sunrise_time_art = Figlet(font="big", width=100).renderText(
51+
sunrise_local.strftime("%H:%M:%S")
52+
)
53+
sunrise_time_text = Text(sunrise_time_art)
54+
sunrise_time_text.stylize("bold yellow")
55+
56+
sun_set_text = Text(sun_set_art)
57+
sun_set_text.stylize("bold blue")
58+
# Sunrise time as figlet with AM/PM
59+
sunrise_time_str = sunrise_local.strftime("%I:%M:%S %p")
60+
sunrise_time_art = Figlet(font="big", width=100).renderText(sunrise_time_str)
61+
sunrise_time_text = Text(sunrise_time_art)
62+
sunrise_time_text.stylize("bold yellow")
63+
64+
sun_set_text = Text(sun_set_art)
65+
sun_set_text.stylize("bold blue")
66+
# Sunset time as figlet with AM/PM
67+
sunset_time_str = sunset_local.strftime("%I:%M:%S %p")
68+
sunset_time_art = Figlet(font="big", width=100).renderText(sunset_time_str)
69+
sunset_time_text = Text(sunset_time_art)
70+
sunset_time_text.stylize("bold blue")
71+
from rich.table import Table
72+
73+
astro_table = Table(show_header=True, header_style="bold magenta", box=None)
74+
astro_table.add_column("Event", style="bold #ff00cc")
75+
astro_table.add_column("Local Time", style="bold #00eaff")
76+
tz = settings.tz
77+
time_fmt = "%I:%M:%S %p %Z"
78+
# Color mapping for event types
79+
event_colors = {
80+
"sunrise": "#ffe066", # yellow
81+
"sunset": "#5dade2", # blue
82+
"transit": "#ffb347", # orange
83+
"civil twilight begin": "#f7cac9", # pink
84+
"civil twilight end": "#92a8d1", # light blue
85+
"nautical twilight begin": "#f9d423", # gold
86+
"nautical twilight end": "#6a89cc", # purple-blue
87+
"astronomical twilight begin": "#b388ff", # violet
88+
"astronomical twilight end": "#2e86c1", # deep blue
89+
}
90+
for name, dt in astro.as_local(tz).items():
3291
label = name.replace("_", " ").title()
92+
if isinstance(dt, datetime):
93+
value = dt.strftime(time_fmt)
94+
else:
95+
value = str(dt)
3396
logger.info(f"Displaying {label}: {value}")
34-
console.print(f"[bold cyan]{label}: [white]{value}[/white]")
97+
# Pick color based on event type
98+
color = event_colors.get(label.lower(), "#e17055") # fallback: coral
99+
astro_table.add_row(
100+
f"[{color}]{label}[/{color}]", f"[{color}]{value}[/{color}]"
101+
)
102+
103+
# Compose all parts into a single renderable for the panel
104+
from rich.console import Group
105+
106+
from rich.columns import Columns
107+
108+
# Combine sunrise and sunset figlet art and times in the same row
109+
sun_figlet_row = Columns(
110+
[
111+
Group(Align.center(sun_text), Align.center(sunrise_time_text)),
112+
Group(Align.center(sun_set_text), Align.center(sunset_time_text)),
113+
],
114+
align="center",
115+
expand=True,
116+
)
117+
118+
panel_content = Group(
119+
Align.center(header_text),
120+
Align.center(date_text),
121+
sun_figlet_row,
122+
Align.center(astro_table),
123+
)
124+
console.print(
125+
Panel(
126+
panel_content,
127+
title="[bold #ff6ec7]Synthwave Astronomical Events[/bold #ff6ec7]",
128+
border_style="#ff00cc",
129+
padding=(1, 2),
130+
)
131+
)

uv.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)