From f28b27602c1ce18d08c649af54f99502adfb4166 Mon Sep 17 00:00:00 2001 From: flowelx Date: Sat, 7 Feb 2026 22:46:58 +0300 Subject: [PATCH 1/5] feat: add pytest and ci --- app_python/app.py | 10 +++++----- app_python/requirements.txt | 5 ++++- app_python/tests/test_app.py | 22 ++++++++++++++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 app_python/tests/test_app.py diff --git a/app_python/app.py b/app_python/app.py index c5dcddd911..9b5cdef281 100644 --- a/app_python/app.py +++ b/app_python/app.py @@ -50,14 +50,14 @@ def format_datetime_iso(dt: datetime): async def get_service_info(request: Request): """ Root endpoint returning comprehensive service and system information. - + Returns: - dict: JSON containing service, system, runtime, and request information. + dict: JSON with service, system, runtime, and request information. """ logger.info( f"GET / from {request.client.host if request.client else 'unknown'}" ) - + service_info = { 'name': 'devops-info-request', 'version': '1.0.0', @@ -110,7 +110,7 @@ async def get_service_info(request: Request): async def health_check(request: Request): """ Health check endpoint for service monitoring. - + Returns: dict: Service health status with timestamp and uptime. """ @@ -157,4 +157,4 @@ async def internal_error_handler(request: Request, exc: Exception): host=HOST, port=PORT, reload=DEBUG - ) \ No newline at end of file + ) diff --git a/app_python/requirements.txt b/app_python/requirements.txt index 9abb353041..00365486c9 100644 --- a/app_python/requirements.txt +++ b/app_python/requirements.txt @@ -1,2 +1,5 @@ fastapi==0.115.0 -uvicorn[standard]==0.32.0 \ No newline at end of file +uvicorn[standard]==0.32.0 +pytest==8.0.0 +httpx +flake8 \ No newline at end of file diff --git a/app_python/tests/test_app.py b/app_python/tests/test_app.py new file mode 100644 index 0000000000..0583f71165 --- /dev/null +++ b/app_python/tests/test_app.py @@ -0,0 +1,22 @@ +from fastapi.testclient import TestClient +from app import app + +client = TestClient(app) + +def test_root_endpoint(): + response = client.get("/") + assert response.status_code == 200 + + data = response.json() + assert "service" in data + assert data["service"]["name"] == "devops-info-request" + +def test_health_endpoint(): + response = client.get("/health") + assert response.status_code == 200 + data = response.json() + assert data["status"] == "healthy" + +def test_404_error(): + response = client.get("/not-exists") + assert response.status_code == 404 \ No newline at end of file From 1202d241d2756f6974f40e5b79f74b2555c8d166 Mon Sep 17 00:00:00 2001 From: flowelx Date: Sat, 7 Feb 2026 22:48:50 +0300 Subject: [PATCH 2/5] feat: add ci --- .github/workflows/python-ci.yml | 61 +++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 .github/workflows/python-ci.yml diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml new file mode 100644 index 0000000000..1a3adcfdd7 --- /dev/null +++ b/.github/workflows/python-ci.yml @@ -0,0 +1,61 @@ +name: Python CI/CD + +on: + push: + branches: [ main, lab03 ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + cache: 'pip' + cache-dependency-path: 'app_python/requirements.txt' + + - name: Install dependencies + run: | + cd app_python + pip install -r requirements.txt + + - name: Run a linter + run: | + cd app_python + flake8 app.py + + - name: Run tests + run: | + cd app_python + pytest tests/test_app.py + + build: + runs-on: ubuntu-latest + needs: test + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: ./app_python + push: true + tags: | + ${{ secrets.DOCKER_USERNAME }}/fastapi-app:latest + ${{ secrets.DOCKER_USERNAME }}/fastapi-app:$(date +%Y.%m.%d) \ No newline at end of file From 93d863e5e8db975f4d1d747d79fc8da29d7d9f9c Mon Sep 17 00:00:00 2001 From: flowelx Date: Sat, 7 Feb 2026 23:04:22 +0300 Subject: [PATCH 3/5] feat: add status badge and complete ci --- .github/workflows/python-ci.yml | 6 ++++++ app_python/README.md | 2 ++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 1a3adcfdd7..6b1e35f1fd 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -36,6 +36,12 @@ jobs: cd app_python pytest tests/test_app.py + - name: Security scan with pip-audit + run: | + cd app_python + pip install pip-audit + pip-audit -r requirements.txt || echo "Security scan completed" + build: runs-on: ubuntu-latest needs: test diff --git a/app_python/README.md b/app_python/README.md index 06d8b50485..27a5c91a20 100644 --- a/app_python/README.md +++ b/app_python/README.md @@ -1,5 +1,7 @@ # DevOps Info Service (Python / FastAPI) +[![Python CI/CD](https://github.com/flowelx/DevOps-Core-Course/actions/workflows/python-ci.yml/badge.svg)](https://github.com/flowelx/DevOps-Core-Course/actions/workflows/python-ci.yml) + ## Overview This FastAPI application delivers runtime and system data through HTTP endpoints. Built as a modular platform for DevOps education, it enables practical exploration of containerization, CI/CD pipelines, monitoring solutions, and infrastructure automation concepts. From 71444e8aa0fa2bd8bfd549e509838b11cf3f7517 Mon Sep 17 00:00:00 2001 From: flowelx Date: Sun, 8 Feb 2026 02:28:05 +0300 Subject: [PATCH 4/5] docs: add documentation for lab03 --- .github/workflows/python-ci.yml | 12 +- app_python/.gitignore | 1 + app_python/docs/LAB03.md | 180 ++++++++++++++++++ app_python/docs/screenshots/successful-ci.jpg | Bin 0 -> 31531 bytes 4 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 app_python/docs/LAB03.md create mode 100644 app_python/docs/screenshots/successful-ci.jpg diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 6b1e35f1fd..dc7c04b0fb 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -3,8 +3,14 @@ name: Python CI/CD on: push: branches: [ main, lab03 ] + paths: + - 'app_python/**' + - '.github/workflows/python-ci.yml' pull_request: branches: [ main ] + paths: + - 'app_python/**' + - '.github/workflows/python-ci.yml' jobs: test: @@ -26,7 +32,7 @@ jobs: cd app_python pip install -r requirements.txt - - name: Run a linter + - name: Run linter run: | cd app_python flake8 app.py @@ -63,5 +69,5 @@ jobs: context: ./app_python push: true tags: | - ${{ secrets.DOCKER_USERNAME }}/fastapi-app:latest - ${{ secrets.DOCKER_USERNAME }}/fastapi-app:$(date +%Y.%m.%d) \ No newline at end of file + ${{ secrets.DOCKER_USERNAME }}/fastapi-lab-app:latest + ${{ secrets.DOCKER_USERNAME }}/fastapi-lab-app:$(date +%Y.%m.%d) \ No newline at end of file diff --git a/app_python/.gitignore b/app_python/.gitignore index 4de420a8f7..3b9bb78f47 100644 --- a/app_python/.gitignore +++ b/app_python/.gitignore @@ -3,6 +3,7 @@ __pycache__/ *.py[cod] venv/ *.log +.pytest_cache # IDE .vscode/ diff --git a/app_python/docs/LAB03.md b/app_python/docs/LAB03.md new file mode 100644 index 0000000000..f603717eec --- /dev/null +++ b/app_python/docs/LAB03.md @@ -0,0 +1,180 @@ +# Lab 3 — Continuous Integration (CI/CD) + +## 1. Unit Testing + +### Framework chosen + +I chose `pytest` because of using plain `assert` statement instead of complex assertion methods. The framework has clear output with `-v` flag showing exactly what passed/failed. `pytest` is well-documented with many tutorials and examples. + +### Test Structure + +**Test Coverage:** + +1. `test_root_endpoint()` - Tests `GET /` endpoint + +2. `test_health_endpoint()` - Tests `GET /health` endpoint + +3. `test_404_error` - Tests error handling + +Each test is independent. Tests use FastAPI's `TestClient` (no live server needed). + +### How to Run Tests Locally + +```bash +cd app_python +pip install -r requirements.txt +pytest tests/test_app.py -v +``` + +### Terminal Output Showing All Tests Passing + +```bash +=================================================================== test session starts ==================================================================== +platform linux -- Python 3.14.2, pytest-8.0.0, pluggy-1.6.0 +rootdir: /home/flowelx/DevOps-Core-Course/app_python +plugins: anyio-4.12.1 +collected 3 items + +tests/test_app.py ... [100%] + +===================================================================== warnings summary ===================================================================== +venv/lib/python3.14/site-packages/starlette/_utils.py:40 +venv/lib/python3.14/site-packages/starlette/_utils.py:40 +venv/lib/python3.14/site-packages/starlette/_utils.py:40 +venv/lib/python3.14/site-packages/starlette/_utils.py:40 +venv/lib/python3.14/site-packages/starlette/_utils.py:40 +venv/lib/python3.14/site-packages/starlette/_utils.py:40 +venv/lib/python3.14/site-packages/starlette/_utils.py:40 +venv/lib/python3.14/site-packages/starlette/_utils.py:40 +tests/test_app.py::test_404_error + /home/flowelx/DevOps-Core-Course/app_python/venv/lib/python3.14/site-packages/starlette/_utils.py:40: DeprecationWarning: 'asyncio.iscoroutinefunction' is deprecated and slated for removal in Python 3.16; use inspect.iscoroutinefunction() instead + return asyncio.iscoroutinefunction(obj) or (callable(obj) and asyncio.iscoroutinefunction(obj.__call__)) + +venv/lib/python3.14/site-packages/fastapi/routing.py:233 +venv/lib/python3.14/site-packages/fastapi/routing.py:233 + /home/flowelx/DevOps-Core-Course/app_python/venv/lib/python3.14/site-packages/fastapi/routing.py:233: DeprecationWarning: 'asyncio.iscoroutinefunction' is deprecated and slated for removal in Python 3.16; use inspect.iscoroutinefunction() instead + is_coroutine = asyncio.iscoroutinefunction(dependant.call) + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +============================================================== 3 passed, 11 warnings in 0.27s ============================================================== +``` + +## 2. GitHub Actions CI Workflow + +### Workflow Trigger Strategy + +**Configuration:** + +```yaml +on: + push: + branches: [ main, lab03 ] + pull_request: + branches: [ main ] +``` + +CI runs on feature branch and main. It saves GitHub Actions minutes, focused on importnant branches. +Docker build only runs on push to `main`. This prevents unnecessary Docker builds for every commit. + +### Marketplace Actions Chosen + +1. `actions/checkout@v4` - Official GitHub action, reliable, well-maintained + +2. `actions/setup-python@v5` - Handles multiple Python versions, caching built-in + +3. `docker/login-action@v3` - Secure token-based login, handles credentials properly + +4. `docker/build-push-action@v5` - Single action for both operations, supports caching + +### Docker Tagging Strategy + +**Strategy:** Calendar Versioning + +**Format:** `YYYY.NN.DD` + +It is convinient for frequent updates. There is no need to track breaking changes. + +### Successful Workflow Run + +**Link to Workflow Run:** https://github.com/flowelx/DevOps-Core-Course/actions/runs/21786077651/job/62857660802 + +**Screenshot of Green Checkmark:** + +![successfull ci](screenshots/successful-ci.jpg) + +## CI Best Practices & Security + +### Caching Implementation + +**Python Package Caching:** + +```yaml +- uses: actions/setup-python@v5 + with: + cache: 'pip' + cache-dependency-path: 'app_python/requirements.txt' +``` + +### CI Best Practices Applied + +1. Path-based Triggers + +```yaml +paths: + - 'app_python/**' + - '.github/workflows/python-ci.yml' +``` + +2. **Job Dependencies** + +```yaml +build: + needs: test +``` + +3. **Conditional Execution** + +```yaml +if: github.event_name == 'push' && github.ref == 'refs/heads/main' +``` + +4. **Security Scanning** + +```yaml +- name: Security scan with pip-audit + run: | + cd app_python + pip install pip-audit + pip-audit -r requirements.txt || echo "Security scan completed" +``` + +5. **Linting** + +```yaml +- name: Run linter + run: | + cd app_python + flake8 app.py +``` + +6. **Test Reporting** + +```yaml +pytest tests/test_app.py +``` + +### Security Scanning Results + +**Tool Used:** `pip-audit` + +I couldn't use Snyk because the site did not open with or without vpn. So I applied `pip-audit`. + +**Scan Results:** + +``` +Found 2 known vulnerabilities in 1 package +Name Version ID Fix Versions +--------- ------- -------------- ------------ +starlette 0.38.6 CVE-2024-47874 0.40.0 +starlette 0.38.6 CVE-2025-54121 0.47.2 +``` diff --git a/app_python/docs/screenshots/successful-ci.jpg b/app_python/docs/screenshots/successful-ci.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5320ec759e27225f2e79a22e9bcc2c09678f24da GIT binary patch literal 31531 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%0DpW2-_t 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

O-K++#P?)uQe+nR%qA1W4Vgh8ZC8#( zY`aFzE{nhEl!p*vHLtZ(x1DWCnR4Ie2e$JWOK2*4u1$!hFHU`+j-%t-n{T?p)n=gJ zv#RPk(mIGG0P~KFm&C(DJrm9jUZb$O)v`b;&sQO~Ipc6-ISn@I&x_ z(MN(#2QKtexL_F&ntPK(>GsumsZz&f7uz8=PqOpxR7_#pYO%2uc7~3_wHUaLl!0!8 z(bx>H&1AhEL}*?Z>?9JJ&c%xN+u9yKDT%`$%dgm`^eav1)6n<|NT;q8FewE{wi~W| zR=EUDpFOIW8)8ohF{EynR5^L({Myql(=|;ybtxEnJ)CbkxHh+9y*2;T^_(666!AUs zQjCFHX5Bnc!GmKdQpVMYHuOYbn%Ys9eE7B9{G>pZ``L1|QaW>5jUj*fRXS$qgsz2$ zL+{^U!H_JZRyrV+``1(>j5EH`9!;tW*=0uDJwLl^IBk+U@nXh}faBh*c+!j7ZSs?0 zhp@#|<<=$%yRX#ofafP^c1>rVC(U0OH+#!n?|-Id-w6qwU8;J2rkg7050^iwY+()P zT)2YNfTMEb;Y4)M6hzZ#ylQoml5` zXue(+RW1SPPdRw2t0MXXaEILVUKOaht9W6(;yRtmhSFO93!l@E@}I3bXL-<$QCX(K zM024sH_|9I7Q)Iy#AM}n73v3yId~II(u(m6WpJVUHK7Cz`b&<93rj>@aK)x$fOFo z*m5)ax=jMdPbc=zZ*lEmD)&4sEsolZhRM=-NrcGUTTEswru(GJguNh9sKqPXJVHse zl@#ZevNt7flaUw!ls zBL-~S_)fH`Qq^+{(&R&zIYUGqq?1MYfYq!bTB4viHi+P@KZaf1{**7@Oy@^0e*l74 zE<~d;Brc+X4v>W78iU&OE8VRHpcm0i-F32)8%&sf$E@Lf(+0exq4~BA?$$EXpF&#A z@#TfhcF$O>j-gsg+1Rz6LcQ{-;Sd{51tAV?x~!r+2aip+=VmD!c?Xcr-gKC`SRnE` z+S5trDh+3wzk%0=)K%nRN;XID&5W1?yROM3n>ZTT=NhGaWA*|Q){B^lg=`yaQGvC- zm~NR_5ro@)JJ(q+5g&Tih>5IA{NR-XCZZiL3$Au;@0?BXez&kCf(DBJ`{*}+lrqQe zo@UDXCwPFtqQ>4SNcWp4&7P!Fhsy@3h<3 z_zhC#^nE7b1yLQ|E2oetK9wS(bsbm8gvp7o6#XQX;oCJ7*TKU?K(m<5zG2w)sCy{L zQ%xgl<>S`qE~F7HxlJ~K^o4%r&L8`Ls}Ut>3HkzszVHnx}Sh6*{0Ft_mREUj1Fi)M4NgvZJC9b|$-hF1BrI`VmX6*1K`0;T57@NLwdhZWwwzRrdqDoQy~) zcBT3qF|}jV*Q@09wtFh-@>NNCa}R4Vc82_b*oyy2{GEe_b@7+OPXm{{=s&)(>~i~W zgU+ompUMya7?z#nfh#73mx*-6%Sg1@eYaw5fzKhwl|q4%u>eW>w(I)?*Y26d?sSvK zIN(l83#8tT;o9|jrhHe#&I)XSwHWIb7;kH=%j)=uEc=#tt+>z(oR?cDZ7_W-8q}Ht z9ygd=`ve&62#nu!&P8<%wWiF`j)`p4%#z?~LyNtASCzgraCQ<1umROmN1K_1V{sAw~b< z@3q&AcBV!9_cFEitlXj9z4Ws6#dP$Cjhh26ae}A?%jV!V5@48 z88gEE9SZ;iK@md{|CR;I#F>LsjU$ywG!+C*)rRzgp!!T1I6$_>KX1ble51?=!>1$z z0s%^N3JMUYA~8W2F%0qVOaC*2|2GO74TAndz-Z~8bwr5z=Q4nOWJAhHzoLYw7OB6m z{lWl+0|105bx7|o7ytlQ1^yWahV=jBvHtQ#{o8xeqtemelCVJRgMac+!k+)+`4jjX zrAQ1L_$6TSA!+;-z|a7s0ub^`!1#c~V939e1t7vqpZzlrrmVti7oTB@zbE`vuD{Cuk9quz z{k1HT0r~FUe_;Gwt~Zm%e^WC600f|b(0}6j8O7j#qN6Z{UxoiyWdTRAY2km~fuVjY zODk|BEa(S-I2iRa4eK9s`Hkr(j_DG=oqwJP#<}}Nt}XvPj>!l;K-~FjyZ(~)2T6!} zj%yQD6BD)Z2lCe|VJeXFm#zR&_$$lrjruPVet~`}8l|s6d5}#xtM&{2EBPDGufV^c zF+4!jzrcTmK>y5unO@}~!#u4RS9Ms}fBFC*6JdqhDj;=jynR9-$;C~xUk?86lmPNc z^Go^3hH8^bHT0+uY}m5k4m)pdqu@I%yf(^-;xB(Xq(YhR&eb zpx!Xfj}tH5WV;$VW)EA%Qv+^VAJuN>OMT?ke7Py-FZM+*t(;fm_tVqqrHA$spJRk> z$OW_I*OwnX4Wb^e;lI&_Hq<|i-VweVMAI>sR=y@1yK$Nv%(f=KaS>_|_tzuw-A|8v zze|XNgM<5rGO);4AtFd9Tvx%?$GhnZEF`&a_BRQCdN_c5W54}u!$9uhnfX_`?)IIc zsAQVyWs_CQlW$ai)bM9rGoRiIoz;9=%D}gDB7Z-Uz%tl)b7|A*ysE|d9qqAI^tY7$ zl}`nt&kBgKg$>jnybus}Kvz846n^shfylAVaQhYu^y1FTOy6(ZPUoZoL9fVPAN9@} zRtguq+HEmsXc=z*ZXQ6s^t@;HjqL?Rw@kpOeDts!b?mJ>S@U>eBcOBqS*ekdS$EHQ zCfZGF=r?ZKV>7|{)ms*{Fq^&v+(TkLNFN#!#tK6GJl*_wY_&6$m%V8+^ADyP#Ey~T zEerIUFztmGMp*(7%oO`?J}U4q)9pX?^|#sfzAw7A5TDrtIMOC0xd{p@{$;Wq@&hn) zvn3nai<3!gsoU}fHE25Y{R(V2sDQ*>t5cUsRuKtRibC5ph(JoQJLNqa30{c-lEh21 zYp;Zkpu|&8NK-`;#wmL!(MTVjhJi=%0KYH|A%BV}DHP#n{F3I4-D_yj4A$478-oWfsy1kn>Zk~g z4_J)2*xkbq2ef2kiM{1d`l)7i8SMZAZ|Q3e8zdB3?;MrFJVDBE0|?Hn5Jxhr!Qmrg z>9+ni^Dd(1F9M2ia9&awn)RIDmNs#HWE_?)lYIT|r z8S#lJrurx~$duzik9FurP(o5tCy{m3JKp3lo~ZRjuS<&1kKcj?~>%31atTpfD65soKPSBUGf$@Q4b=XIL0t|j)X zk^)$&^Hl;~O8Fv4=AvB7;45*w4l>RpdWsQ~4o5o61W~&ZgiUtz@*oj$4Pr%Fzai#N zWO6PEe5OJMt8%>R=Ov|LudYX}bKs*X)f*G?UT4yUI7ce*eMq>;Y_Ub3N$-kZMcS?C zlO%#>#F&HsuiP=#qjGmiUJ1igDxZ}@KL0j zZf`KL3%IyqZ$jk&Dhnym4)c4<)ebbE>YM>6R&X$Mhwz1k@fz6=v6NhrwI$`8`Xsl8 zM5}^8Lha}+=ESsg3LAgHPOb&BNKNVX3gP)3R*GGoI~C+gfgq(xuIEuMowv!l)JTCm zskThldxWlG&yt`GR~YkTI|jvt#1=@AEcc$c83KXSBq6;GMIz}5Hm-T4qBa4OsJ+~BD3dZGQBfW5 z6)}&C#pg_EybLHO;gNV-4N^rfd#SF0C(A)fzq*k?%3>N(Yrv~?J&IQA&ACz7HiR-* zyeOVb34(2mDrxdjg^y)y;O@ueJ90@-y*ZYScat9LGE}AmayUQN1WQ-{01OrMF7}0n z(@4nqNGxl|5X6gt`|nI5intZnSqot9E_yrM>x#M6qW%6dKHSn>&93t}R;+2#&zpSRgZWVV;_wrM` zT&HrWNQb7CL@{+ZW9`h)aA7@@`~=SOvj?EkBO5;ca@N|};p<8%z0i}qa)RQa(#L$T zdcyW&727NIEy`BhCmQH|27kD^vlySZafL^5FxMLLt^bQ~7#H=no*>eBzK!rRZ4e@X z;4_qX$ihU99F9jU8r_eeqvtH%FA0l!r}##Lf?1R{bqPo^SQaxAr3AZ95b9Zn9bljZ z=*wFrc1U%)*Ln?+KUU;F*S0;>%(awIKn6Wz)dj0k-wmhatmBm_!^$xn3Z3Nu*bHz7 zV5?_qm{HfrkTL2&8=l_lwI$RI&{C-wrY$B6%BKT=Y{a#rfb==yNgpT{vhnc%>t?b0 zn2U6M6pU=@ddd7qv5+Ay3K6YLsqpv*Z&BAY+8cv8P{gzj6k%baVw#FClnKbDB8Sts zWbH+`8HLm(~*0vBC%-SNXDnlDh9|efmO3o>a)zuxjJlr63hj?Mz z)y&FoMC-8!nuyqNIe2pvspeL@p)wYBWRaZNT}p7sh?l_#R@d%8K(!M9+z~r^FWd9{ z8Q&aZryi{`A7Q&r2oh+jH4!+TkG;g!e5qz2k;AJCJEz*B-&3{mT!*cvkp)7R{ZmU9 zhLwpYiY&o<^0skSVhuuD5*#b<(|ar_%}``iuO2!$-=gFP5lTL8pSGpJCHw(jffg`=ha5N z^v2T?oEDysG>+5k0hkSzoersOd=70T$uF%_OOO^MDtz!m>$fP^L)2T#&5&O>?E z4}app!4%(vJT$*CTvL2#WVGgKSnu`WP|q(6lzJTD@|1%%Mzh3IF;&icwm}O+6NhaN zzuag#mq}6|*tL1|h1{OlOJ}!OnlF~GUby3%3b!8Y56)We-i0> zPuO>fojxI$8C1CPix;g-%F0Xtg1Ph5EzjpkfK#8}g=5ZgX=*XS{Bpl+vbB1hwOLQe zd6Kv2WxDWi`ArVSxI!`0ZywXq_2)c}Z&`~r{N!cQDpErhro_z;DKV=XlJXppgyQuu zU#az^t&Az@xlf-in+?1=z>oP+KAt-&(3y{DUI&p_$?%|B?_D}O^qo9>!Xu;28POr& zM=27&s)?3-?x);}KE;trcku*}si+A78A)BVp+*w-HR6D0WO`_8(~)}dSheBCGKj<( zeS?-YJW3^g0mQjJvHX2o0}kqyv`l4}Ck7n6Dw1}Rea}*TXcMXqAypBKnjf-fLImt2LD@3??D(`eM)Q^?A;B}NW zecS{vzS3b2B8-yq-6O7#aWJu~w&1Xh5f_S~Cu~#+Fs+F&LEn$SLPIybB42+Iv>=oQ z?AE-05hYu*NHYqwQt~EA238DYs;nv5(D?CiW?9}P2oZ0E(r>5*-Pck(goiWLl<%I~ zin}&Kc&ghA3KPDp$NA6}2~xP}NCl^>pH0^1Ikp!%yms8d9SlLYh7^6B2Jd4uo=*0) z+#k>}mn8&?XdMOVC6zS6DEF2WgG)&iEgW_QZW^IO)XDtwB*%GT5Hq;k7Vp4^&)Je% zurPbThB7;fDA%sf0IFTb^KoPPp}bf$^H^i9`0;?WGG1L##TmOhJR}*pZ$)EO{RCUB zUJgC;?n+ixx$b4Bo}2XSxuKFTCxKwL-1`ZhQvk|--uJ4y9q#S#&$-Bx*vempJC!!% z>qb$8ln7Hboi2Y;)@EMLOx5nk4`)p?^e&FX^}88P%%zuXVO9xjmy*e&qt zO9r^-tp-Z%4+s!9x*JKLU5xYGal)f<=PTGE>m$f^v+&?r2v&k&SMold+RL60h42<4 z5QOjW#lYR};l}3%^O>GnzPayP+xn$DQuDe2^HWSVRWy8VhN58ig+Qq+!5M}Tgi?6K z)q{GY{s@0t;LHuC&1WotHdvFe3Vx!NK~+t59#ZIvnc>AZm*?dKM|kh}vSh5rfzvdL zm@s7syYp-=BqrTy!u(tQsQ+kOrBL&Lj8nv&Y2uFsbT4JEVV0A*f0U2BXE7!` z(G;>z+kyhz4?nGJziJ+T>XdnBTIgee<;(v(d^2EF=?7qNts@AB47}@o+wKm^f13Q; zyXgD32fI7AtSt_N8v-+zWHsLxaGw0f6B0Q0Jhyn6*jak7S4o3e*rh7#SsurN&poA? zLlxKb4@I}cb~@J{-n#JJO{fKI%i-WH*02(6Dqgv? z^T2!jdoiD|%u~4D219|N$9fBS{B2K~ndz*1pIx{=Cz$dnDW9JlY& z;!sO(4|)WM7@1JC%aU|1Z2h$ZSO2<%-7Y?h`1=Qxw>A67FF?X-ehq}~cH2}YUDbTX zq({InRpLbkvp9mWM{Ki=-5qgA3oul16KM+Bl@DAkMinJ(^xe{D z2|pfE%!i_m@q=CRAX9>|M)fak=azGpp%$F%+T+Dk ziPlPt<94OvGy;&m59NL3Gajl#YsB_xVT?md_CBk7n6%&M2X^B}+hD-m;BDiyp0nmm z`2$V&`!t3k`;OhlKsiCD>p%^=SJ=8UebOhG9Y@iDTTK<=dpaQpD%eOQm3Ea)Dz_1y zeh^8agfDr{Ed(ArxEXO}vs1Jyb}w$Lpyk1H0m<$5-C!`OkItAoB6hU%eo3MGiwjd6 z0zV2%^-Dh@u3K`fXt{3k=J^ZymKFS>d^Kj-y1-o-S2-VdJGHRrdTsp&mCUodIQxFk zAAkwdt48gH<8A~}?`5ypWbUjr}w{s7d0cw~*t7!+SNaE^+DEC-+ zUAj4!Mx86SKw3ca4j&8bdEdw?A11R9B`Vg?F1VEETkEGbqsCU_tLdv9cuF=`g~)xn zC@7KBplC`=wtuZ4Uo6SNSrV`zEESR+pE*dS+;-^n9sA_rRu-Je21s_T5uUNDC~@1? z0w4vTJl|>qZv#>la1^>ynb{ido5JByW!>4BzcWItO{n=)HRX8r_az)9-|T^=KGX7_ z;DeW(g<0{Q;V^8Cs0Msg08d%*H-mGBX&(y`7+7Fr}T`WyFKgkkG30$XGq5S@3Lj=)~*LP7txf+w?Q0i)M zn#E2}M{ETm3cL9P(i3~Vy&>O;REpWn^c3+yga6?MU#KDAu-6$*Qd$nKKk+h07eq9! z#Zg3zG3ff-xy}*e%JFYq90(bqeAC2R47+qT@Ewk6vEIN5|}j z12x0pnx7nAWP9b?rW`?0-`-|Nw|8G~7O{Q+NDVJt%Fz`gT0R?LxW>z8@E-kVu+~RK z+kNrFVbv1s=eLr(zQu7);?BH)~S66MN{OMr_+M&TI#S?Nl7|8NJf}CQsb! z7HsJy`QomH$!G-KY*kBul@Q&-7Ea}N0nUULIJckIKV3xYHhA=ZqdOwa|H$$EV4PDe zs=M?|n(j$^6-wQoGE*J1z|lxSX@Q?Pp@BLRpoI#}>Hj8Wf9=b*Cg{|Cd)|Z$9#U48 zV`@=Rb;mmlB2md`-=ST}ca`)OtjZwj*fFBsyM8B+e@^fuA$3La%!sm}j!Z!#FIQw5 zA>L8yfX;M5pAkB?@mZEj^`}O8$OYHVK!^oj*C4;%de}`gn~uaP$@?gkXGaistcN02 z=1N^+zPSZ(^ABl#{_P#MeodB8c|Cr-PqfUcBHLYGlDXn3)_f%<|6SFD!u0=fW9`5n zRPoY~m4&9_@!BQ_Pti#;Wl=P|jrmb>nnoTd3&+r`FZHJTf*6z|zzf?aAk;t*h7uuQ zMzllhk@(Iv3ZtHHn`Zz?5`=yym;BY_s#DzT3!7uR3(SSc)7xRJF)@DiUFiksgUVc@ zB?)__+HZ(^pgR{)TZBUvF&dTzo%?HRZSoESIxlgKu~i^cMRGo2ePn~NW#k9qA^t>k z*zxcgzmZkWngYZ9E33EE>aF!Ro_ymjD^f$`MwRXhk9c>q2x&w}Z8KKYeCuh*doSg( zH3Sm5k*B1~_8{LkH3G-JWf#k8Aor$GOE|v++f_ZN6+LT`ZpLqE*Cmc3*eNDMb~VFOZ15ynNMO$kpz zB((Tmj&|1ft1oa&a^(Z#VwzQ2eQwxuI*CFo(0z>SyVn>s)HeJx3UyPn7vYD_@6XNu zHy0rx|i_=4dr+1he@VY!fHMfG7a|M!#!?58?( z4GC?fQTPZeptrpNRJVi9G_V2c5Ff?}z-3s9vo8p|2cU%BDe{@b@$yb1NtTck6}%u< z%M^b_m`R?|0bl0jz~$BFF?~jE0L?W^VSJ_K$II+b432L&35CrFp)~r#fX+wu>iX)L z8VW8I_>#iA81Z{ilj$k<7RJBdk9X=mx!7#m=N|dQCv0 zjmL|lc?MXdnBjF1|D1D0SmP&`=yhmI(^VruMvNKD8~#tszs*Kg6o5h9^BFTNV7Cq{ z#gfCEU#gEv94}_3iRghUzC9=?AqkJSwJ&7-DV;7B5VD?-6+wmL?nR>D2bn?U-Xr%A z3|eKnK7eVd=;X$D$x2+@J$G=Q-a59hM%JF3`R&*)llTQND-qVKKC1DL3Ux_B z;jh~?-B+M?67_c?xuptm#~9;POejk`&;?NwaNQDd^R`d+i`EM1K^sW!Uxp-?(wsVC z3I{PSsUnfDVVVoO6#4Hy%_>lT+NG$n!99mb#~P~T86PYDK6HE#4|zZV&n4Tx)#jjV zxNSY86~*tW$3XECUyaW;C@Ln{dcHxgHVzeE0yweY3yZ-?V_2!VnLcVJe(k+(b9Co{ zc|lg;+tviUTj573IZ-t2{ zHvtM)jJsN#HxuKar=N2v{BCP^1Ne|mJSERzT(AM!u_sr3Uu#)o8_V{Z{2yJAb0 zY}}Dn`G9#dVZYy-T_o1lc}CqF;H=JV%IQ}>JclE_XUFy3c43PFaA)DPlKM?Eb-|^6 z2G{7$U!uR4<=t!CRz3p=B*mmn;4sj6e`b`P?L{b!);mQz?mZy5SD##bEn9|=>q?B z9;k2lx~=|=Us1+0)<@p@$K#o?Wg}}8G5cE?8vB_ATu+|`-%6%0vBbZCc?LPsB0OG-%$zZ&By6e1BWPy!**ZTl3a_1elTc! z+5Gms0j^`TKB6(|0nJ=8vJoCv#x0f4onSNH@ayNg#_Q zwf$0!Q;E+o&S<_Tc2zEef7lgWu?8M2GJ#|2%H2~RueKaAU?)wX{e*V7fIKI{(9X{Bi3{E{XN_qU@IgTZ_ax?1Xnjs z0B_VWktU2pLagsGKaKz15D=MK$>v@%tv)~LJ0?*pv z${eUcU7qHd8A;sF3+mL{pW-zRBGsdSVI^@5ZdxdR%5};9S}a@?N{zaUR<8@O@!lw^ zi>hQYF5^xPWgkPzC8z7meQ_$+*;@{@;(i3ukJy|ULv8%2?JGmGlxO$y*p}2{&2)AH zswBu11%P@n?B2a%`|`B6In4;)V&%uP`iX7SXBs_xJ7p2-G;bCoX+`Q;XI_P~Vu6;R zP-#L%f^z1Mxc8PgkHx%)I>Mi5rabFt+<7?zG1QOBkWp?b#xh-`nfU=&TI=|+^1lFQ CQ=k3- literal 0 HcmV?d00001 From 44c49eacde929dac524608ae595ca8425cd0b9d7 Mon Sep 17 00:00:00 2001 From: flowelx Date: Sun, 8 Feb 2026 02:41:17 +0300 Subject: [PATCH 5/5] docs: add status badge screenshot --- app_python/docs/LAB03.md | 4 ++++ app_python/docs/screenshots/status-badge.jpg | Bin 0 -> 14833 bytes 2 files changed, 4 insertions(+) create mode 100644 app_python/docs/screenshots/status-badge.jpg diff --git a/app_python/docs/LAB03.md b/app_python/docs/LAB03.md index f603717eec..c31a958187 100644 --- a/app_python/docs/LAB03.md +++ b/app_python/docs/LAB03.md @@ -104,6 +104,10 @@ It is convinient for frequent updates. There is no need to track breaking change ## CI Best Practices & Security +### Status Badge in README + +![status badge](screenshots/status-badge.jpg) + ### Caching Implementation **Python Package Caching:** diff --git a/app_python/docs/screenshots/status-badge.jpg b/app_python/docs/screenshots/status-badge.jpg new file mode 100644 index 0000000000000000000000000000000000000000..526a6e96ef799ec75d11d326fd015b177968e72e GIT binary patch literal 14833 zcmd^mWmH|wvf#lT4({#;C%AiXcMIJjAfX_mqT^toqG6*UqhJzXV&mfCth;i`n@%}IYgM@;DhJr?f zfkDJWMM1^;FNe2204gjX9s&abj0^yd3I>4+_BH?@0Du7?-Us?V)PDvDXc#D1Nbo;Y z+`lOQLIVKa`vDH|wgNze00V#nA%O3g{-N0)|E&DKm!s=Q8$Uh&_3p7x_}E_s!v3(# zcW(b0Af|OU(sER$UfowJO!bLHUtn^qz3)`T3w@g*KF?p9m75bI0F&^-O6_D^bb_{G^Q2 zkhJ@J*??_k5EHl=f`(^%%-^qtQvltZNn&zoX8X##rB%f}5D_pMINJGDu#1 zN)?&xr`K}dPR2gebhuS!)QlJDqkITmw0OM8RXRbTe(cHL_9oJIqr-=>==c7DiBo8K z(*@bjY4g#U%VV;@=hSPXWo0wN@8|$2Ym)oMcefWj008i!BYJ!qF5JnICp|)3-FiZX zDdJ1YPk9*{_jfd0q;YMy?(|HXf#M8*&cm(}@QOIuD+1yp~9K4&>sv%gPoJ_61l`OURo}fPF*Kn|Mi6AUf zzfc+N@#A$e#*1#EMXdPZ9>&qm3)*@>;^4Dy_@rk)The)}IR?ZmPL+49?W=3U%;aK` zL-cg=McaYoSl|0-{{)!xH$YCnU&VhU6pwx+P^qJt{|?E+xA{&67c=@l{1_mQHTz{> zi|W1sD7l?8X+{qQRIyP~E9i2sXe;bBCwV_br|P>}7K;YQWjDqh?~l_M-|9J&|6E4W z`wzd{e~jkujOkOk|1BFzY$}_dI6rpT^_JJcyqbGrM0A=CCt*d2@!DjtSseB2?tvA$ zDJ?s4D(?fmmR?jxLkXUHz`tYkUk6~`MVJNnF0v2+F!1+p4;a)R9`EuC2@L>4$ArNE zqGGY5Q805DV6%Qu3?L<=ViS57ZP<5#2ZI8C14P)ewX8kL4sC9H(|OS(FGhD0b0_P1 z+zB3Mh;3{pd;=&Qy#ZF<0AG=8okS5DQM55uw!q26(Ss-OKL?Q95IBHg8O+l;u1B36 zBbt_QMTgL`5JWR7f-A23)oXvy!fI8vb{3f=$ST2iqOYiHA#K6oTaMq>ke0VH!37fL z$9SSC4A%{L(Q~6`xmD_={;o;AO2wnAbN7q9ul%qT{{>RFPbWj48mw7MH#`?kfNgv} z+H$!zmzcyMGSm%>atNKdO&&c6&yzv620dUEbv^O8yaX6ZZK19VS)f`th@qQMNCb4~ z4?aU6)l4cx+vbNB+MJC#Zqo5mB3I*neFMx^#v=x@s)WhlRatwlsu^97sd8@daEbMG zguzB4W`PXGpJ_y&YB3>kFCtGGFXU1PDy7Xs#eOvlm49@8uQCpw+v%ti0Buw3b4t`jwGNG5G9ElZOK%2}0{S z4!!-T4EiTrji>je5p>L_mWHD`mT_j}5nI^`#D^;KT4-01 ztsmS(I+`DO4dd(~`KzghuQi&qRuE&gh>Qed=zeTk^}zlbc7D%xsT;hLfBOXPjzfU@ zte#pxlr&a@-?aQ)_6Bg4RY)ND5#mLSc?t z(vvkW$EsI5L4%HPC7v3Bqz(z4+Y#p%X>yM<9<`1B@G>c{A#?}Gm7^v_eW76^uesY> z^odB*-^|PV4bWlyv_E}|#KrFCz561Z9?C?%7$F;~hhfBGy>r&WcegK(wieA>5z$6h z{}}o50P{&%mGMdi*1zhp*iViAOZvI8BC7h`0^Cf!H!r-ilh=e#Q{79N z0RJQgU(yx#zWFB=etydui>uRy$WQE7FvxHo;|rw;RC5#`u-3f@8C5eiQ%-w%M_?u) z?1alwm0r2lw^e>Y3$lt;O1m{_X*sINw!P|iJ(8Zz^f6cbVz&tN3{?w0D;i=4ohqwn z-DVIQCQu22?O%if6Wv&$lqVmmRdx~0#Mdg$K9GJV7QZP_R=k-d2HbfXl8?75YD3C-&co3nGcOsqO0Oajv_&eGIxX)`mzWrCP#gm zen24*<-{*DEmaMkpjpN9 z5Bb}RE!fb;!F%aE4^8O}qnyN$DIR<^$6)keNaiSdQB;nCuc~yHjQtkZmHq{`gKTZA zKhwJdSYNR$qmvU8-T>1Qxgf0W)Ej-5|F@q{S0-@}C*LnTcdmyH;wFi61o`|to_{_3 z8%VzR)O0i9`yp5MV3zw&Ap{(RF4zxKSH{X_erZxgd03|%*XlfUPPR@EMmA1ZRAHPo z>y6h%g@BiQA{h!&^$^&h@WwFO7XBUm&|G)?T#?Yc@$d#%DiK78u-8+;x0+RIKF52q zzwDj4#S=ZvXO-+6MUG73#zkA30#y$VAX5KpK zn6vXQ;E#O|_JixmgWO5M9Dlw$Ir=hN|6Y{$ofw4rXYYlCfcx(oIoSJFj7o~eB3uVV z$52#KHZTlGn7;gL3w~G11;Oq6?@5d)iA|TA5NcS41}CP9Bb#zdXe=^B=|kK* zN7;9^0vrMg92OQH7V0mx;vED)qN1U*uqKcSLjf_!lnfo~$Uis*<`?vCVX}!R8@Uwr zOf+NLtz^(+*(!6KJ`KoC$W2?$%4SmvuDZ^hYb4{7NC+?26TFOR zIa?E=H<|4|A2tj*IKAo7QFNPx`qd*E*~)I3LV-wZ5RK!udkA%Wn$6b`DEL6RUv6Cd z;<3}~aL&P$10Cud*Yxv>VlnL-B@_mHm%5qB+B!I2l~iZ&O%p7KFwhcaWXY74O`TGr zr9>>smXlsQr z)J%ohCk2<;GzsCNF0Ex(dSJjFv^!TYvot6a3RVPNysA=7fR>=yY@#7w4DM2iQHk-@ z2n9W<0isvNv}WSBXid`QiD~{+x}DknYdLI#)x?Dhqq3bO&86G$%0FKscM zy&UG~CYP^$q;bv;7@skq@(gXzr3<_sgq|^4q0mq$5|IyMELqW^J5&sfgtnLQZ0m9F zWlgNmgih(PGH^O_ZP115dAMKC(%i4jq#hACzt8Z&E0|CAB#+WIJATRxS#FY>egnuq z`M((TBb5zSKo+SE=2MirR*|nll{F`i20Mjd3^TVxd=G|gY{N{rk<@U3#x%{blRF&OmqX8HTBH>xbBjALr}R$jg*o~ZrW&sC>E z2CX%JSSM{ONy?tgX>T`I9PcCwp5#-xt|MN<4`+=S#gNk!TbgPQ*eau!!L9{v!fhCa zTec8|MSdg3C&Vry9Y&tr31YbdOHQiyO=D=)W3sG6hu5TtA=k-?f%~iIXx&AX!FmDn zR8!Ytzo7&tI+SQnaEK>$eQ;ZbmTIPtW+*-Qt5OcWA_vy`c@@N*zGxMln03nMnW{jy zM^FlaPSZu}d-?LMYeP|W$o*Y#<$;=BD^enr0m6jw>)S#pIuWi?>(Xa;{frI}eLa}U zD$*4NVcaP2DF~oOTTWA5Wr0uil>SL)ks+1fNzCQJyCoQH8fa^+8Y~^)$zf(A7UlVt z$4w|Q-+t-PH_}s_M?Q$MR?B&)7?*R)&_LZbr(|9z*Ur?AnCpKt91JPr02I6TqkpCv z_9}DACih3%GY{OI08J@k)*FmnVvL|#?=H;nPY+Y+)ZSX~Y?UcYe;0PK0hNpM$gf}S z`0%Wkxl$WN#asAH%_2r|P_57sVv;-=wk(?>WP50eq_mO00qV%vc~m4YoJz80W26Hiid}xYXW+D3sGwfC=b#HX+k{2k=Rxxy%q#uvM(a#HwvPC9JCef9z zMn$l&QszAj44r4M84{CG8;n#IHPOd&=gaZVBrxv=I**};R1EbBQ6^OxZFLA8DSKDk zsF#sPxsH?j@IcbYJJvtZ6Hmt(=!1oF%0I&S+h2&zJQFmI*7l9i0FflWW3oPq*Uujq z1x7{=saQ0Kwam%idr0v+^;^?Q+dyc4q;mxbgw54T>`@b1qQ6S>*PhluGq8>^SFpk2 z%ilX0nUXD=O1~Cm`4aeZ7#VntJj1vXG&Y@0vcor-b>uJl0jokVl@ER*@%H{;ltn~C zxB6y*yk4Hm90Gy~qPi|gSPRA(_ic}EMpnkbrk3xK`dxa8uvF=oXKdnVxx7e3Wn~x|X&F}! z?ufq=X6I%ID~j`=C9I;OGu+o(#P%XSFbL5uNuc&#d!M0bvEThIEW)ty{+1h2?)}#U zWR2mN6HZ^+(UB&{u0`W$D$3_SHshslh7-3pm|9qumc&bA@&Pa@b*GQQsIt`iy5YTI znLQL-2Xl{)W~j`-%HhApQ5ZstTNOsw9r=D@>$Oj4JrMbV6&QZtAsy3NG!wEXPgQ$5 z!b;nNy2K&kNrn*HxK}~$5v#$`pLo`oBr~u~kxnmty+B{jv0XD9-K>>S_teCp8vJk^ ztI~%EO_hk2$62h_A0?YlDNI+E*KZbY5url2uBR+1QvB2b1(J5lbm>n* zk>;6Y*k?rGzck&;ZqKj;*>$s>Giz(*Hu9j!vc}@7bch#G_77~UMB(fCjMSjhrFeeV z@Jg_RuXkX19P6*~v<7Y4h7u#sBnElG{(N0Z-rRSiBwGp37g+KkkZmg|ph}%9oZzU( zNTGy`ArI+p`CZ_Bg6uznGQYTCWTzw%t%eo*E!`Jsr1KbKi=hmb0u|oQD$=8|X!Z!y z!7mVzd!=+13w-Q*+M2BB-l(QEgDq#!{**{s#ztffSKe88ezBAGy6BFv+K9cFD)q<~ z+K9=zH&Vet=t>vj|1&_%Por@Kx9WULm)F=3ev+YqU{F%p_pGAHx!+yWi~#) z-a$1_E1%^NJY!c@3jGU1*>sH`Bz%Wc$9jwRvH}Ikxd)q+p5)NH=@L6F$q+Ql{vcOq zLwc9;QL#c;^DGKOhW;j*_&XrWIXycc4ujzDJ}@qKO4a!A(qFUoB9{!;r)Ye`W&*9k zi!7+y7A?ZN+&WSRuiFNUh=?mES)1n&IH@7gw`St^$nvrtO6rt1iZ}$dQQK&+%1;@M z*x?Cs)2tpbmlA0?4&tSt$6=1uPia-zFtA5a$TWH>#|`vlr)y_9YL+P(DgA_cYKn>| zP+?p?l16FlGnd1KKzb)7E9xfAF|N@^)G{&`QF^23vj(rsMA@3k z0eN}FFc?G7vU}cH0q&z_lMY%e-#PN~-4zHFPB z?^fwfQoE2osHLt;K*Q5Af@m#B7QYegMp(MfHF?1Gb@@7+jOM9S(6ODw1w+{AYSL6C zS${*@p<`%GQp|IR2DQ38?~&W*^{AdSAEl5R0FR4-GZ#Wto75sT!jvv`>QmU<) z)Cwk4p0)vjj8y(b7#IIdi_19uR|so^Qdug!#F=FY)hZleAr+_1ZEZP}5)cxKEF^xo zK2cHwRDFv5mp7Xf2#iRS4=KAqwAP_T~K=f|_^*6xfu!e9Y+ahIa zhhF)Hc1_{)XF6MP1gWp&E>Pp0EvZnTCXKk{wV&7UHY@K-RT5GmGbyf`Q~m}>_$n>S zHCVZTDg6e(HskqjT>1Z1@&-V6P7__m$37m4xi$XoRky>h`75W=V^W>Zyn^V%d>roo zKmdP`%X|Z@{vP`NZSp5E-jkq@kM-%xuU|C{|ANvlo;sG`Tk(gU_EZKLjcCb1W3YwD zQ9yppq=$7gGG$^MjTAzvqy~Dm=Z`k&h^V99gsLcgnmBY-kZn%;;&ob@Ez8`Gqm`y3 zGD_pnWSuQTRsz>)b;;?WRl`SJg1)8IslqvIS28U}Hx0?4jxPegP_AJR>86Da+v!Qe zk&N7DXeq@JePmiaJ6ptncooXP3lWyA@x`q+LvR7OT?yDyRJO|FRH?>DF-j7JyY7$d zw3V?@lagA2q?L4Sq2Gt&6@#w9S6QfBg0WymOKE&qRm5-;)km!@{m4Rb#=O*N3-5A` zR@m6T)6nS;J+Fh7Z;}&X30JPX#~EBirmVY?Umn9Z*Ad%z9ZRVmZ&?d*ACZ}!F6A0h zvXWf00ipWnJ;pOO?6mCk3nU3$^5w37Vp%U`R1bL-jJc(caL?N4@N+8bGN{Jv&~lZo zKGM2y)lCJmit}?76f#v~xvUJ|5Ut+ouy$23xdE3knpFx(88Y@{iY}Yv0#-9RYf1(5 zIDP44x1{W*Y=3M?FH#vPZDTy=VmU^!QK1U#^}u*Y^{wuDo0vc@(nmCKrTP($2-u2q z=dk&iGC~QU+(|J9sNmFn`v;C@;IGd3DLFt8vyzYHN+4{ zLbH!Kh8*@ENkH@$)t^=ZOear;+ppd2|Dmwa-OkwWhUU2X>4yCMIDsDc?o9-P0EY$# z!2Eqk0R})NWfA_Rm^|pQZ$&}`unuv-9aHp`Ud!6pAOpeL?z62eeFeM zrXoI2SBnqesfcX*0T7(>Q@)^6n*YFU(2hhU*!X;-ULi-~qx1pt3BPfurrHQXNjtKP zM{-eN87!n|4%AXi{~2Vt7TYVo0m*2%6uV_MJ6alAH9!45Jv!4u(OYS#aUi0{X7Q7J zOd%+`!kNw+BI>qFQmoiA;54o0@FgO)IQokcy&q-{RB4*Y{XieIUTTY}qL($t{?APl zjx9El00bOD8Ej@-`Z>anp5f&Ai8_bu;Zu;Jhx66;Sx$$?H*sGmyXmpWz!Ax}!YwO% z8rLTWI-o3^sY|{YiUpA9jNlx~1%+rh8f3t4i?)x;{t)jQg!<-8F-lR#M6n$FLf3Ez zquGYYRx875M16&PH>)%q=U``0a{J@PSW=rGL__OVwGIBgmf^g>#H(u)V{T53D#KHH z-5b^7Q6f&bO8ejpfG76|V~4H#pg1@e?iD~_M^-b{GWUoS1h5ASnZ9Zw z%A#j^^JJS6=NZTIUQ;I#Vms&K2D!cgOzN}E0~kkb2_5(7Dra4vKNl~Tv=xpGRBLAY9S^6nmz5&8NWs6Nw5Q3DB z7tOGC&Px0dE^|j74xe-P&CKQ>HGR)ePN|G~KlQVoVwLm~0XRFi*AIZ9Cr=v5|$I3{idq$aX*5=+E+An3Sy*N=|}4-or>6OXE*PBu1u zaydoIoy+C%3q?a#bC{E3Hqh4M)xUfKfG#T+dxW=z_x6emdYV$M(IV?Y^= zRJynYSBu#Z{=+bfb+t%S3RA0z;g=&R6BG>;GQ;%|fd!t}x&`RD_$&ozBa;oqw7|pJ zy4F@+7lA0HJ1^s2%#VUJBgHfA8{7*nq?(71q6s->fn4@6N1u^-b^TuMoXS1zYab-m zsE>V9msWw^rBKc2hBD<1jVPYyJ?<*0Uj_&7M!eo=;9OEkTp=unA}98`+WANxWErN8 zab+3G2T#$FG0$x3Ro9qIQm>}>my_rorweM2a&zOb8()1C>2gQ;*-aWhH?Cu=_|tZd zD3faiT6Pc|X}@m0BCkeLx2Es+R_}=(=_?;LS1}34E+{zdeEj6GRw|R$ju}w}_X5QuV3wX(L2EMVrHvf$2>1 zSuo{#8fB!h?RP%yx387Z;TlX8gu;ETHlFg)H~r;kL1_{rMmQIl&!CVN8(mYMv;Kg=d+yyqYLFR>m&tS2do zf#=Yb$X02r0n!dc;=hZ4IDR1ixz3)AuPEvt6>hg2aA|^;y~K=Oj8&pfc6iTtJo02H ze!QPcy{N_RYFOwYI9g-ZDoqVeA8>km16>a`bT_7cIXh5 z&8l;#S5MJsN?MK+n~(M;m2LD&Yh-I*m3!u#sQdZsLrms$I8zUV%Jlnn$(K5*noD^# z!uz!Y8l^e`y87_ThT>_-J_^L6T@8vTQIr?Uqt6i4-qdoE$MOTz)rQq`(pme^)a`xH z8^9rw*q7g|e^sEh^s+;;5I-p7q1&RDPA0G_*?%j{(JQT)c{CIewyoPBClAPExYyygj z_IVcfd@u+DK}$G?qY+Ord#u4f>30!QAGG3tP$7z$nLea|Yn!Qmx~%y6+a1IH1R5-gx& zkm92(D)>$7kM9Y`lWS)5pf|uSF*@lHtv+BN9u`}7>mgRR<=8vK`NF0tTBzifIQ5{& zcF{_~A)2N+2CG*6g`6~UoVlZEQ%44ozFk_rzC9olXrcZ4Hbo%@O7k?E5_|0{i)K?0 zFDTUu_Oqw1uEiw|XStrFepX36a>Q0@uIkUnkvc)tGI0;kFP#EWiJSg>`*u zi%5^M{zb(?(4wF2VFpZgByS8KKIcKxrY7_B6q8wiR$}u~DUz}{ZMS@}S^Iv8j;_!K zWxBK&j}E~LV-w;k{n~OfC{Vs8jnr1oIj)hij6FqwYPGmYwUSMhYeSt6f3(EoE$JfR zL@;n-jqj`UBe{>PL~s>a!ipDba0_y)vkN5Gtk5)m5UMI z2Ze6h-+TXxa;o2ng4QFE54b&)6ePWT~3Q_sdG+_H~aL~_P6SL8E zb-@}z#uY=TbR@$dbZpC9K1Iga6_xom?Hw8C4I)X?y2BY@aZyb~Q@upy3`v!wV5~ozKFT~QnE|S1 zYJP$4lv4Zz@5sKfH-^oQ|C9q3|Sn?dSCTH{uU?^1S@Bkw!^j ztGxVcbMy&hk-^EkW!5M7?p*uprXB#|-ShXKT~qp}WAJ~LLz4V^3dFo;Gf)XL9usp~ z$V^GtRMRmakzsz$FijB^CPmFm#Q{d~y#nQTEwvJ$rOK3NbEwo@q}s<7BuZQ@j(J0o z5Fw~FQ@0%7e{%hI?7;!d|HS{v|FQ^}`w{=w5j+5bzIW7ry&3jj>;K0xW1aUY-xD{I z*MB7Zm*GEtT}sqR_kG8|P2T zGX0{NTQ4xUz@8Oia+y-3`&YJcY)M_N(IZK)iD%2IT<39WPuO39Lz4}Ikgl;sCaheS z4<)E@CSw2!Kh9p@y^fURfRQo_PLP=RKI+L0PRb%!1bk`*qDT-c{7!$X^&BLYz99>p z-r$AjeZs1TQ%!5r8A4wqQ=I1C7TX__4H6vuJ$ZLlzpv7JODw`ly_Y~k$H4rp1ZIP} zUnK8t-oFR`$6^D%0j>xcYF+|3_WX+xh{Gn!0*Wp8(O=A{w0_*h+Cg`%&1+F1*?)u>l(v3brRRi4&&|#-z+@HfpH*Vp25Da;HL9 zGxPBn{5Z-i#DlOEd3qC#*8NK}Tnt{4czdJ^4wPnzz*f(bVSMVg^~{)@RUj%i`qm?d z@^6<-472a+VBhh4C(-p-(`8^V?WWkcqji$61Dg0?@sAwpVq|`s5ZinR8WNWd0KwY%tsOLpGj^&$6LN|4nT zvv37C)IAO9qbDO}s|g0?_P#q+SRAp(jui^zJGTT2BrFi&xv&neQNw1Z3PNZLRL8eB z_QA*-X=*BNLzdqF!a?dV{BEm=42*E7LcTW00dD|>(x&sCHEQ{w;QF}BvtW_cQw|qJ z`wjIaHPl!GJWfCY%XE3BYb`V?MANuI83(-{_S8NM>hM-YDT1mNv1wZnARMk*;q)86 z4DE5t6{esKF)a03I7g>-1n}CJkX|W?Dh3-Z3m(A~coeY)IzG6fT~3Dr(AS~$x*#a@ zYY*280g)OPrmO`7vnL-A`=dQ{6?w#FxMmm;q!BQof{%^9>bXJIn7g~U^@mD}0D+xU zmwtKz?bON^CsA3~Q9@=@kW{&BkM z?z`32WMK((GW|%yIj1A)4K=mnjmeEEW#fcThn=T zPWV+ zi#|`&-LtM70m%((`<8<^ru?+mZw`lEa=Xd7B-9800e0fY&cSPoe+=>Um2-h$p`XaL zL)TEQef?hI5V3cLqZ@4W{Xq$eYR9ItB_YFeH9p%hn}qWl00o@DcG-l@fF>zef*k9s zOrJfk(%R4{QFD8yel=06gjN_H9g3{yMojSx;riY#9?w^L*U}4!RMXblE?aI({htrt z?F217v@DLM$E!E1$)n=&s2-G|3PmGq2)~f-D1?Z*+KS;(UjR)s!%<$aUwBK&aS*C+ zVOBPOc`hq;y)qgpVkbTHOm!>x)-tb^&45Q>;Bsc6QaO(#P7&zD3U-e0Pt#rFHq$J0 zv)|`7e<0&+w2Pf9UFrEYVbk6o(QR}Ta_#{!<$z)#pC2|!ssfg-EK}%PyFDDl5OWw5 zd%}IopznE1%PpLe9^s^IAVHE!8LkVM0IKPbd{hG?IA2~JPbAgP>$)WMTUWpiqTA7M zy{xb#;s`8V4dWDLRq1`7rS50L1^Wf}ad>HC?+aLOIZhnhl%Od{3?PC=lY^@h9R-sq z46nwg&#$$Vriyx*uZWh28B-_HdGK>mv{<-DqcA55S6jx{F73!wdfEiN#C5&Fhk`~1 zxZ+6$L2*bJ3to0_09bnKC0*c@fHe3LkXlJ;eg+;5?8%urE0|xDjP&ZNv-&A%?a}| zIZ}}M1|3)(MQ=?pP31wNCJ-eIF6>}-q9?5AQ4`cTax1c9cZp&tEc|0>ta{{X+ zdMrR3NB}Amr!mLiHD@|oAsjU{Ky$f+u#Vg<9O=Qxw#R`FP&*1R6w{r&>BRd1k4=*! zI{WRHOdoRT1Ii9j8430v0Jnl}{+_(r7#kQJphzHVUuC6=<;6Ju%;f)Q6YDoV8Th3( zy7e;!8&zu$7;N(E!Daf_(*RAI;0m00Y4fhrq2>Ybk#7ljUO{36PR?01%%*pchv^WQ zy+X`n(RE!89U^4iZO>*3QZhsqYZw^CCN;gP>5&uEQ#X6=k<4Mi<%<5~A(-UULOJrG z72V-8B7kT)7XLnj5(tA}{f(2Xjqg(cY2uV&o{V6wZ)Ht&+r!Pe3U{Si!zXxD2DvT46AJeXBQ)BO*oR{2X}i{mZzCp72~NMsF|U#NHB z{F%ssU$VT&0K!0*EVEstNpll{gmZgX2z1A%fLGiOc#eIrb?rr;XEkXS%qItEMPOg@ zlArLS`BX5VWrn%cYqt>sc9{ZW$%H+(3xX3Giq|gXP^=)>X7Ilw`2zs z@f!faD#7l=gX>uCn8Hd}6_lbi{~aFPac5qQ%h&KOv5+V7yObPhcZ+#6<8sP%UyYhZ zMWESQKtkVj^Uc8XY61Wh>ZLRl21?NZGr;Hc z(Tp$b$DglWP^%O(9?D7`nL4lIv+c8PaMK3Sa9j|tN(F^RVDE1)f&^(`g>lnCGqoNo zUA+BOcthyjIe_hBLYEU`Bh~x;-``Qs6wrWl2)OzEoh@yS<>h-jlif-F{9H# z)zv)|21sRv(xr>9sPaKPHfUwN5lX3_nep$ieZ=gUdHS^W0A;%RCUl`ewq@mI37r;H4K;JMKw zreMBhmbizcpRT;rFgG>DZdOvF(?5y=ipYtyV{XI!T;_;dSsy|(BwRp0jRv)^*N-|dkK27dde_Z zcn_BV+K@pi7;WCtPiPc0y~>PC_Ta{?OyVTbSjG`DN#$S=x?kC#r})P(pkPVzY%zrx zxr&r{-T-E!7y{lyH^nfzmkyu+ehw8iQT5jcC-G?_Cg0IGRXnfsr` ziMRXO6&;CfdiCR^Dh?YfJ-#aPl9uh$TI{ajtcM%!$SGKz0Rtb<^z-qon?Bm=nSU(9 zl1#5drD-1z(2iel4iPQm6qYQvVD&RrbVYtJ#;ZFH6tAQ?ZZq$PhJp}-l2gfgDmbS6 zbajyDy8B&RJQPyZR-OCnx7=_mMHV=`(Xp~{T=C@)B*@d?ZaI(ker z&mE>@whlcoMr2(QAl_!Boc6>C4u>z<{oTvI`tPV#_x_iQMSj8mEaK%KTYybcN literal 0 HcmV?d00001