From b962c1be7f6b9286015e1cbe26b4ca50a848e92b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alojz=20Mas=C3=A1r?= Date: Sun, 15 Feb 2026 17:12:24 +0100 Subject: [PATCH] buildovanie cez task a verzia na jednom mieste --- .devcontainer/.env | 4 + .devcontainer/devcontainer.json | 123 ++++++++++--------------- .devcontainer/docker-compose.yml | 9 ++ .gitignore | 3 + .vscode/tasks.json | 30 ++++++ README.md | 91 +++++++++++++----- scripts/publish-devcontainer-image.ps1 | 74 +++++++++++++++ 7 files changed, 239 insertions(+), 95 deletions(-) create mode 100644 .devcontainer/.env create mode 100644 .devcontainer/docker-compose.yml create mode 100644 .vscode/tasks.json create mode 100644 scripts/publish-devcontainer-image.ps1 diff --git a/.devcontainer/.env b/.devcontainer/.env new file mode 100644 index 0000000..0083f06 --- /dev/null +++ b/.devcontainer/.env @@ -0,0 +1,4 @@ +PYTHON_BASE=3.13 +DEVCONTAINER_IMAGE_PULL_REPO=docker.masara.eu/python +DEVCONTAINER_IMAGE_PUSH_REPO=repo.masara.eu/python +DEVCONTAINER_IMAGE_REV=0.1 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 01c0d0c..0b494b1 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,74 +1,49 @@ -{ - "name": "Python Dev", - // !! nezabudni nastaviť aj verziu python pre aplikáciu - "image": "docker.masara.eu/python:3.13-0.1-devcontainer", - // "build": { - // "dockerfile": "Dockerfile", - // "context": ".", - // "args": { - // "PYTHON_VERSION": "3.13" - // } - // }, - "containerEnv": { - "PIP_INDEX_URL": "https://dv.masara.eu/repository/pypi-group/simple", - "PIP_ROOT_USER_ACTION": "ignore" - }, - "remoteEnv": { - "DOCKER_BUILDKIT": "1", - // verzia python pre aplikáciu - "PYTHON_BASE": "3.13" - }, - "workspaceFolder": "/workspace", - "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind", - "remoteUser": "root", - "customizations": { - "vscode": { - "extensions": [ - "ms-python.python", - "ms-python.vscode-pylance", - "ms-toolsai.jupyter", - "ms-python.black-formatter", - "ms-python.isort", - "ms-azuretools.vscode-docker" - ], - "settings": { - "python.formatting.provider": "black", - "python.analysis.extraPaths": [ - "${workspaceFolder}/.venv/lib/python3.13/site-packages", - "${workspaceFolder}/app" - ], - "editor.formatOnSave": true, - // "python.defaultInterpreterPath": "/workspace/.venv/bin/python", - "python.terminal.activateEnvironment": true, - "python.defaultInterpreterPath": ".venv/bin/python", - // nechceme obnovovať staré porty z predchádzajúcich session - "remote.restoreForwardedPorts": false, - // auto-forward portov len na základe outputu, nie procesov - "remote.autoForwardPortsSource": "output", - // vypni auto-attach JS debug (často generuje ghost porty) - "debug.javascript.autoAttachFilter": "disabled" - } - } - }, - "mounts": [ - "type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock" - ], - "postCreateCommand": "bash .devcontainer/post_create.sh", - "forwardPorts": [ - // 8000, - // 8080 - ], - "portsAttributes": { - // "8000": { - // "label": "App", - // "onAutoForward": "openBrowser" - // }, - // "8080": { - // "label": "Debug", - // "onAutoForward": "silent" - // }, - "*": { - "onAutoForward": "ignore" - } - } -} \ No newline at end of file +{ + "name": "Python Dev", + "dockerComposeFile": "docker-compose.yml", + "service": "dev", + "containerEnv": { + "PIP_INDEX_URL": "https://dv.masara.eu/repository/pypi-group/simple", + "PIP_ROOT_USER_ACTION": "ignore" + }, + "remoteEnv": { + "DOCKER_BUILDKIT": "1" + }, + "workspaceFolder": "/workspace", + "remoteUser": "root", + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-python.vscode-pylance", + "ms-toolsai.jupyter", + "ms-python.black-formatter", + "ms-python.isort", + "ms-azuretools.vscode-docker" + ], + "settings": { + "python.formatting.provider": "black", + "python.analysis.extraPaths": [ + "${workspaceFolder}/app" + ], + "editor.formatOnSave": true, + "python.terminal.activateEnvironment": true, + "python.defaultInterpreterPath": ".venv/bin/python", + "remote.restoreForwardedPorts": false, + "remote.autoForwardPortsSource": "output", + "debug.javascript.autoAttachFilter": "disabled" + } + } + }, + "mounts": [ + "type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock" + ], + "postCreateCommand": "bash .devcontainer/post_create.sh", + "forwardPorts": [ + ], + "portsAttributes": { + "*": { + "onAutoForward": "ignore" + } + } +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 0000000..d359b99 --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,9 @@ +services: + dev: + image: ${DEVCONTAINER_IMAGE_PULL_REPO}:${PYTHON_BASE}-${DEVCONTAINER_IMAGE_REV}-devcontainer + pull_policy: always + init: true + volumes: + - ..:/workspace:cached + environment: + PYTHON_BASE: ${PYTHON_BASE} diff --git a/.gitignore b/.gitignore index cf6d15e..28701c4 100644 --- a/.gitignore +++ b/.gitignore @@ -227,3 +227,6 @@ __marimo__/ # Built Visual Studio Code Extensions *.vsix + +# Track devcontainer version source of truth +!.devcontainer/.env diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..5773c64 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,30 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "devcontainer: build image", + "type": "shell", + "command": "powershell", + "args": [ + "-ExecutionPolicy", + "Bypass", + "-File", + "scripts/publish-devcontainer-image.ps1" + ], + "problemMatcher": [] + }, + { + "label": "devcontainer: build and push image", + "type": "shell", + "command": "powershell", + "args": [ + "-ExecutionPolicy", + "Bypass", + "-File", + "scripts/publish-devcontainer-image.ps1", + "-Push" + ], + "problemMatcher": [] + } + ] +} diff --git a/README.md b/README.md index 8718a40..618beaa 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,70 @@ -# Template projektu pre Python 3.13 - -## Template obsahuje - -* definíciu docker kontajnera s podporu docker a git -* python 3.13 -* Nastavenie vscode -* .dockerignore a .gitignore -* Hello world aplikáciu -* vzorový Dockerfile pre aplikáciu - -> Prvé otvorenie projektu môže trvať aj 10 min. Musí sa buildovať image. - -## Vytvorenie devcontainera - -Pri vytvorení devkontajnera sa automaticky: - -* kontroluje, či je vytvorené venv, ak nie, vytvorí sa a aktualizuje pip -* aktualizujú sa závislosti -* nastaví sa git credential helper - +# Python Project Template + +Template pre Python projekt s Dev Container workflow, kde sa devcontainer image taha z registry a nebuildi sa pri kazdom otvoreni. + +## Co projekt obsahuje + +- devcontainer konfiguraciu (`.devcontainer/devcontainer.json`) +- compose definiciu pre devcontainer (`.devcontainer/docker-compose.yml`) +- centralne nastavenie verzie Pythonu a image tagov (`.devcontainer/.env`) +- skript na build/push devcontainer image (`scripts/publish-devcontainer-image.ps1`) +- VS Code tasky na build/push (`.vscode/tasks.json`) +- vzorovy `Dockerfile` pre aplikaciu (`Dockerfile`) + +## Centralne nastavenie verzie a repo + +Vsetko sa riadi cez `.devcontainer/.env`: + +- `PYTHON_BASE` - verzia Pythonu pre devcontainer image +- `DEVCONTAINER_IMAGE_PULL_REPO` - odkial sa image taha pri otvoreni devcontainera (napr. `docker.masara.eu/python`) +- `DEVCONTAINER_IMAGE_PUSH_REPO` - kam sa image pushuje pri publikovani (napr. `repo.masara.eu/python`) +- `DEVCONTAINER_IMAGE_REV` - revizia image (napr. `0.1`) + +## Build a publish devcontainer image + +Predpoklady: + +- Docker je nainstalovany a bezi +- mas pristup do push registry (`repo.masara.eu`) + +### Cez VS Code task + +1. Spusti task `devcontainer: build and push image`. +2. Task zavola skript `scripts/publish-devcontainer-image.ps1 -Push`. + +### Cez terminal + +```powershell +# iba build +powershell -ExecutionPolicy Bypass -File scripts/publish-devcontainer-image.ps1 + +# build + push +powershell -ExecutionPolicy Bypass -File scripts/publish-devcontainer-image.ps1 -Push +``` + +Poznamka: + +- Ak `docker push` zlyha (napr. neautorizovany pristup), skript spravi fallback `docker login ` a push zopakuje este raz. + +## Pouzitie pri vytvoreni/otvoreni projektu + +1. Naklonuj alebo vytvor projekt z tejto sablony. +2. Nastav hodnoty v `.devcontainer/.env` (hlavne `PYTHON_BASE`, pull/push repo, revision). +3. Ak menis base image (napr. Python verziu), publikuj novu verziu devcontainer image do push registry. +4. Otvor projekt vo VS Code a pouzi `Reopen in Container`. + +Devcontainer sa spusti z image definovaneho v `.devcontainer/docker-compose.yml`: + +- image sa taha z `DEVCONTAINER_IMAGE_PULL_REPO` +- workspace je pripojeny bind mountom do `/workspace` +- `postCreateCommand` vytvori `.venv` a nainstaluje zavislosti z `requirements.txt` + +## Bezne scenare + +- Chcem iba zmenit Python verziu: + 1. Zmen `PYTHON_BASE` v `.devcontainer/.env`. + 2. Spusti `devcontainer: build and push image`. + 3. Vo VS Code daj `Rebuild/Reopen in Container`. + +- Chcem zmenit iba aplikacny kod: + - Nerebuildi sa devcontainer image, staci reopen/restart kontajnera podla potreby. diff --git a/scripts/publish-devcontainer-image.ps1 b/scripts/publish-devcontainer-image.ps1 new file mode 100644 index 0000000..96d5540 --- /dev/null +++ b/scripts/publish-devcontainer-image.ps1 @@ -0,0 +1,74 @@ +param( + [switch]$Push, + [string]$EnvFile = ".devcontainer/.env", + [string]$Dockerfile = ".devcontainer/Dockerfile" +) + +$ErrorActionPreference = "Stop" + +if (!(Test-Path $EnvFile)) { + throw "Env file not found: $EnvFile" +} + +$vars = @{} +Get-Content $EnvFile | ForEach-Object { + $line = $_.Trim() + if ($line -and -not $line.StartsWith("#")) { + $pair = $line -split "=", 2 + if ($pair.Count -eq 2) { + $vars[$pair[0].Trim()] = $pair[1].Trim() + } + } +} + +$required = @("PYTHON_BASE", "DEVCONTAINER_IMAGE_PUSH_REPO", "DEVCONTAINER_IMAGE_REV") +foreach ($key in $required) { + if (-not $vars.ContainsKey($key) -or [string]::IsNullOrWhiteSpace($vars[$key])) { + throw "Missing required key '$key' in $EnvFile" + } +} + +$pythonBase = $vars["PYTHON_BASE"] +$pushRepo = $vars["DEVCONTAINER_IMAGE_PUSH_REPO"] +$rev = $vars["DEVCONTAINER_IMAGE_REV"] + +$versionTag = "${pushRepo}:${pythonBase}-${rev}-devcontainer" +$channelTag = "${pushRepo}:${pythonBase}-devcontainer" +$registry = ($pushRepo -split "/", 2)[0] + +function Invoke-DockerPushWithLoginFallback { + param( + [Parameter(Mandatory = $true)] + [string]$Tag, + [Parameter(Mandatory = $true)] + [string]$Registry + ) + + Write-Host "Pushing $Tag" + docker push $Tag + if ($LASTEXITCODE -eq 0) { + return + } + + Write-Warning "docker push failed for $Tag. Trying docker login to $Registry and retrying once." + docker login $Registry + if ($LASTEXITCODE -ne 0) { + throw "docker login failed for $Registry" + } + + docker push $Tag + if ($LASTEXITCODE -ne 0) { + throw "docker push failed for $Tag after login fallback" + } +} + +Write-Host "Building $versionTag" +docker build -f $Dockerfile --build-arg "PYTHON_VERSION=$pythonBase" -t $versionTag -t $channelTag . +if ($LASTEXITCODE -ne 0) { + throw "docker build failed" +} + +if ($Push) { + Invoke-DockerPushWithLoginFallback -Tag $versionTag -Registry $registry + Invoke-DockerPushWithLoginFallback -Tag $channelTag -Registry $registry +}