Skip to content

Commit e22f2e7

Browse files
committed
chore: version bump to 1.1.0 with CI fixes
Signed-off-by: Damian Skrzyński <polprog.tech@gmail.com>
1 parent 78369be commit e22f2e7

5 files changed

Lines changed: 156 additions & 68 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "releaseboard"
7-
version = "1.10.0"
7+
version = "1.1.0"
88
description = "Internal release-readiness dashboard for engineering teams"
99
readme = "README.md"
1010
license = "AGPL-3.0-only"
@@ -17,7 +17,6 @@ classifiers = [
1717
"Development Status :: 5 - Production/Stable",
1818
"Environment :: Console",
1919
"Intended Audience :: Developers",
20-
"License :: OSI Approved :: GNU Affero General Public License v3",
2120
"Programming Language :: Python :: 3.12",
2221
"Programming Language :: Python :: 3.13",
2322
"Topic :: Software Development :: Build Tools",

src/releaseboard/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""ReleaseBoard — Internal release-readiness dashboard."""
22

3-
__version__ = "1.0.0"
3+
__version__ = "1.1.0"

tests/test_first_run.py

Lines changed: 96 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,16 @@ async def test_first_run_examples_endpoint(first_run_client):
7272
async def test_first_run_create_empty_config(first_run_client):
7373
"""POST /api/config/create with mode=empty should create a valid config."""
7474
client, config_path = first_run_client
75-
resp = await client.post("/api/config/create", json={
76-
"mode": "empty",
77-
"release_name": "Test Release",
78-
"target_month": 6,
79-
"target_year": 2025,
80-
"branch_pattern": "release/{YYYY}.{MM}",
81-
})
75+
resp = await client.post(
76+
"/api/config/create",
77+
json={
78+
"mode": "empty",
79+
"release_name": "Test Release",
80+
"target_month": 6,
81+
"target_year": 2025,
82+
"branch_pattern": "release/{YYYY}.{MM}",
83+
},
84+
)
8285
assert resp.status_code == 200
8386
data = resp.json()
8487
assert data["ok"] is True
@@ -104,10 +107,13 @@ async def test_first_run_create_import_config(first_run_client):
104107
},
105108
"repositories": [],
106109
}
107-
resp = await client.post("/api/config/create", json={
108-
"mode": "import",
109-
"config": imported,
110-
})
110+
resp = await client.post(
111+
"/api/config/create",
112+
json={
113+
"mode": "import",
114+
"config": imported,
115+
},
116+
)
111117
assert resp.status_code == 200
112118
data = resp.json()
113119
assert data["ok"] is True
@@ -118,10 +124,13 @@ async def test_first_run_create_import_config(first_run_client):
118124
async def test_first_run_create_invalid_config(first_run_client):
119125
"""POST /api/config/create with invalid data should return 422."""
120126
client, config_path = first_run_client
121-
resp = await client.post("/api/config/create", json={
122-
"mode": "import",
123-
"config": {"invalid": True},
124-
})
127+
resp = await client.post(
128+
"/api/config/create",
129+
json={
130+
"mode": "import",
131+
"config": {"invalid": True},
132+
},
133+
)
125134
assert resp.status_code == 422
126135
assert not config_path.exists()
127136

@@ -154,12 +163,15 @@ async def test_first_run_after_config_created_dashboard_works(tmp_path: Path):
154163
transport = ASGITransport(app=app)
155164
async with AsyncClient(transport=transport, base_url="http://test") as client:
156165
# Create config first
157-
resp = await client.post("/api/config/create", json={
158-
"mode": "empty",
159-
"release_name": "Post-Create Test",
160-
"target_month": 1,
161-
"target_year": 2025,
162-
})
166+
resp = await client.post(
167+
"/api/config/create",
168+
json={
169+
"mode": "empty",
170+
"release_name": "Post-Create Test",
171+
"target_month": 1,
172+
"target_year": 2025,
173+
},
174+
)
163175
assert resp.status_code == 200
164176

165177
# Now dashboard should serve normal content (state was initialized)
@@ -172,13 +184,15 @@ class TestRendererFirstRun:
172184

173185
def test_render_first_run_returns_html(self):
174186
from releaseboard.presentation.renderer import DashboardRenderer
187+
175188
renderer = DashboardRenderer()
176189
html = renderer.render_first_run(locale="en", config_path="releaseboard.json")
177190
assert "<!DOCTYPE html>" in html or "<!doctype html>" in html.lower()
178191
assert "ReleaseBoard" in html
179192

180193
def test_render_first_run_contains_form_elements(self):
181194
from releaseboard.presentation.renderer import DashboardRenderer
195+
182196
renderer = DashboardRenderer()
183197
html = renderer.render_first_run(locale="en")
184198
# Should have form inputs for release config
@@ -187,6 +201,7 @@ def test_render_first_run_contains_form_elements(self):
187201

188202
def test_render_first_run_polish_locale(self):
189203
from releaseboard.presentation.renderer import DashboardRenderer
204+
190205
renderer = DashboardRenderer()
191206
html = renderer.render_first_run(locale="pl")
192207
assert "<!DOCTYPE html>" in html or "<!doctype html>" in html.lower()
@@ -198,7 +213,11 @@ class TestFirstRunI18n:
198213
def test_en_first_run_keys_exist(self):
199214
locale_path = (
200215
Path(__file__).resolve().parent.parent
201-
/ "src" / "releaseboard" / "i18n" / "locales" / "en.json"
216+
/ "src"
217+
/ "releaseboard"
218+
/ "i18n"
219+
/ "locales"
220+
/ "en.json"
202221
)
203222
data = json.loads(locale_path.read_text(encoding="utf-8"))
204223
required_keys = [
@@ -217,7 +236,11 @@ def test_en_first_run_keys_exist(self):
217236
def test_pl_first_run_keys_exist(self):
218237
locale_path = (
219238
Path(__file__).resolve().parent.parent
220-
/ "src" / "releaseboard" / "i18n" / "locales" / "pl.json"
239+
/ "src"
240+
/ "releaseboard"
241+
/ "i18n"
242+
/ "locales"
243+
/ "pl.json"
221244
)
222245
data = json.loads(locale_path.read_text(encoding="utf-8"))
223246
required_keys = [
@@ -242,13 +265,15 @@ def test_serve_command_no_exists_constraint(self):
242265
import inspect
243266

244267
from releaseboard.cli.app import serve
268+
245269
sig = inspect.signature(serve)
246270
# The function should accept a Path without filesystem validation
247271
assert "config" in sig.parameters
248272

249273
def test_create_app_accepts_first_run(self):
250274
"""create_app should accept first_run keyword argument."""
251275
import inspect
276+
252277
sig = inspect.signature(create_app)
253278
assert "first_run" in sig.parameters
254279

@@ -258,6 +283,7 @@ class TestFirstRunShellParity:
258283

259284
def _render(self, locale="en"):
260285
from releaseboard.presentation.renderer import DashboardRenderer
286+
261287
return DashboardRenderer().render_first_run(locale=locale)
262288

263289
def test_has_sticky_header(self):
@@ -323,7 +349,11 @@ def test_polish_locale_renders_correctly(self):
323349
def test_prepare_config_i18n_keys_exist(self):
324350
locale_path = (
325351
Path(__file__).resolve().parent.parent
326-
/ "src" / "releaseboard" / "i18n" / "locales" / "en.json"
352+
/ "src"
353+
/ "releaseboard"
354+
/ "i18n"
355+
/ "locales"
356+
/ "en.json"
327357
)
328358
data = json.loads(locale_path.read_text(encoding="utf-8"))
329359
assert "first_run.prepare_config" in data
@@ -338,7 +368,10 @@ def test_dashboard_has_open_wizard_param_handler(self):
338368
"""The interactive scripts should check for open_wizard query param."""
339369
template_path = (
340370
Path(__file__).resolve().parent.parent
341-
/ "src" / "releaseboard" / "presentation" / "templates"
371+
/ "src"
372+
/ "releaseboard"
373+
/ "presentation"
374+
/ "templates"
342375
/ "_scripts_interactive.html.j2"
343376
)
344377
content = template_path.read_text(encoding="utf-8")
@@ -354,6 +387,7 @@ class TestFirstRunLogoLayout:
354387

355388
def _render(self, locale: str = "en") -> str:
356389
from releaseboard.presentation.renderer import DashboardRenderer
390+
357391
renderer = DashboardRenderer()
358392
return renderer.render_first_run(locale=locale)
359393

@@ -383,6 +417,7 @@ class TestFirstRunLangRestore:
383417

384418
def _render(self) -> str:
385419
from releaseboard.presentation.renderer import DashboardRenderer
420+
386421
renderer = DashboardRenderer()
387422
return renderer.render_first_run()
388423

@@ -406,7 +441,10 @@ def test_first_run_footer_has_rp_version_support(self):
406441
"""First-run footer template must handle rp_version."""
407442
template_path = (
408443
Path(__file__).resolve().parent.parent
409-
/ "src" / "releaseboard" / "presentation" / "templates"
444+
/ "src"
445+
/ "releaseboard"
446+
/ "presentation"
447+
/ "templates"
410448
/ "first_run.html.j2"
411449
)
412450
content = template_path.read_text(encoding="utf-8")
@@ -416,18 +454,24 @@ def test_dashboard_footer_uses_dynamic_version(self):
416454
"""Dashboard footer must use vm.version, not hardcoded version."""
417455
template_path = (
418456
Path(__file__).resolve().parent.parent
419-
/ "src" / "releaseboard" / "presentation" / "templates"
457+
/ "src"
458+
/ "releaseboard"
459+
/ "presentation"
460+
/ "templates"
420461
/ "_footer.html.j2"
421462
)
422463
content = template_path.read_text(encoding="utf-8")
423464
assert "v{{ vm.version }}" in content
424-
assert "v1.10.0" not in content # no hardcoded version
465+
assert "v1.1.0" not in content # no hardcoded version
425466

426467
def test_dashboard_footer_has_rp_version_conditional(self):
427468
"""Dashboard footer must include vm.rp_version conditional."""
428469
template_path = (
429470
Path(__file__).resolve().parent.parent
430-
/ "src" / "releaseboard" / "presentation" / "templates"
471+
/ "src"
472+
/ "releaseboard"
473+
/ "presentation"
474+
/ "templates"
431475
/ "_footer.html.j2"
432476
)
433477
content = template_path.read_text(encoding="utf-8")
@@ -438,6 +482,7 @@ def test_view_model_has_rp_version_field(self):
438482
import dataclasses
439483

440484
from releaseboard.presentation.view_models import DashboardViewModel
485+
441486
field_names = [f.name for f in dataclasses.fields(DashboardViewModel)]
442487
assert "rp_version" in field_names
443488

@@ -460,6 +505,7 @@ def _render_empty_dashboard(self) -> str:
460505
vm.interactive = True
461506

462507
from releaseboard.presentation.renderer import DashboardRenderer
508+
463509
renderer = DashboardRenderer()
464510
return renderer.render(vm)
465511

@@ -503,6 +549,7 @@ def test_empty_repos_state_not_shown_when_repos_exist(self):
503549
vm.interactive = True
504550

505551
from releaseboard.presentation.renderer import DashboardRenderer
552+
506553
renderer = DashboardRenderer()
507554
html = renderer.render(vm)
508555
assert 'id="emptyReposState"' not in html
@@ -538,6 +585,7 @@ def test_non_empty_state_shows_metrics_and_filters(self):
538585
vm.interactive = True
539586

540587
from releaseboard.presentation.renderer import DashboardRenderer
588+
541589
renderer = DashboardRenderer()
542590
html = renderer.render(vm)
543591
assert 'class="metrics-grid' in html
@@ -551,7 +599,11 @@ class TestEmptyStateI18nKeys:
551599
def test_en_empty_state_keys_exist(self):
552600
locale_path = (
553601
Path(__file__).resolve().parent.parent
554-
/ "src" / "releaseboard" / "i18n" / "locales" / "en.json"
602+
/ "src"
603+
/ "releaseboard"
604+
/ "i18n"
605+
/ "locales"
606+
/ "en.json"
555607
)
556608
data = json.loads(locale_path.read_text(encoding="utf-8"))
557609
for key in [
@@ -566,7 +618,11 @@ def test_en_empty_state_keys_exist(self):
566618
def test_pl_empty_state_keys_exist(self):
567619
locale_path = (
568620
Path(__file__).resolve().parent.parent
569-
/ "src" / "releaseboard" / "i18n" / "locales" / "pl.json"
621+
/ "src"
622+
/ "releaseboard"
623+
/ "i18n"
624+
/ "locales"
625+
/ "pl.json"
570626
)
571627
data = json.loads(locale_path.read_text(encoding="utf-8"))
572628
for key in [
@@ -590,6 +646,7 @@ class TestTripleLogoFix:
590646
def test_light_logo_visible_dark_hidden(self):
591647
"""fr-logo-light visible, dark/midnight hidden."""
592648
from releaseboard.presentation.renderer import DashboardRenderer
649+
593650
renderer = DashboardRenderer()
594651
html = renderer.render_first_run(locale="en")
595652
# The fix: .fr-header-logo .fr-logo-light { display: block }
@@ -600,11 +657,13 @@ def test_light_logo_visible_dark_hidden(self):
600657
def test_no_blanket_svg_display_block(self):
601658
"""Must NOT have .fr-header-logo svg { display: block } which causes triple render."""
602659
from releaseboard.presentation.renderer import DashboardRenderer
660+
603661
renderer = DashboardRenderer()
604662
html = renderer.render_first_run(locale="en")
605663
import re
664+
606665
# Must not have a rule that forces display:block on ALL svgs inside fr-header-logo
607-
matches = re.findall(r'\.fr-header-logo\s+svg\s*\{[^}]*display\s*:\s*block', html)
666+
matches = re.findall(r"\.fr-header-logo\s+svg\s*\{[^}]*display\s*:\s*block", html)
608667
assert len(matches) == 0, "Must not force display:block on all SVGs inside .fr-header-logo"
609668

610669

@@ -613,6 +672,7 @@ class TestLocalePreloadFirstRun:
613672

614673
def test_locale_preload_script_present(self):
615674
from releaseboard.presentation.renderer import DashboardRenderer
675+
616676
renderer = DashboardRenderer()
617677
html = renderer.render_first_run(locale="en")
618678
assert "localStorage.getItem('rb_locale')" in html
@@ -621,6 +681,7 @@ def test_locale_preload_script_present(self):
621681
def test_language_switch_saves_to_localstorage(self):
622682
"""First-run language switch must save to localStorage rb_locale."""
623683
from releaseboard.presentation.renderer import DashboardRenderer
684+
624685
renderer = DashboardRenderer()
625686
html = renderer.render_first_run(locale="en")
626687
assert "localStorage.setItem('rb_locale'" in html
@@ -632,18 +693,21 @@ class TestRedirectUrlsCarryLang:
632693
def test_with_lang_helper_defined(self):
633694
"""withLang() helper must be present to append ?lang= to redirect URLs."""
634695
from releaseboard.presentation.renderer import DashboardRenderer
696+
635697
renderer = DashboardRenderer()
636698
html = renderer.render_first_run(locale="en")
637699
assert "function withLang(url)" in html
638700

639701
def test_create_fresh_uses_with_lang(self):
640702
from releaseboard.presentation.renderer import DashboardRenderer
703+
641704
renderer = DashboardRenderer()
642705
html = renderer.render_first_run(locale="en")
643706
assert "withLang(data.redirect)" in html
644707

645708
def test_prepare_config_uses_with_lang(self):
646709
from releaseboard.presentation.renderer import DashboardRenderer
710+
647711
renderer = DashboardRenderer()
648712
html = renderer.render_first_run(locale="en")
649713
assert "withLang('/?open_wizard=1')" in html

0 commit comments

Comments
 (0)