Version 1.1
This commit is contained in:
16
.env.example
Normal file
16
.env.example
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copy to .env and adjust as needed.
|
||||
|
||||
# Port exposed on the host
|
||||
WEB_PORT=5000
|
||||
|
||||
# Flask environment (development/production)
|
||||
FLASK_ENV=production
|
||||
|
||||
# These are read by the app (see app.py) and are also used by docker-compose.yml.
|
||||
SECRET_KEY=change-me
|
||||
# Store the SQLite DB in the Flask instance folder
|
||||
SQLALCHEMY_DATABASE_URI=sqlite:////instance/liturgie.db
|
||||
|
||||
# Default admin bootstrap (created only if the user does not exist yet)
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=admin
|
||||
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
### Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.pyo
|
||||
*.pyd
|
||||
.Python
|
||||
*.egg-info/
|
||||
.eggs/
|
||||
dist/
|
||||
build/
|
||||
|
||||
### Virtual environments
|
||||
.venv/
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
### IDEs
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
### OS
|
||||
Thumbs.db
|
||||
Desktop.ini
|
||||
.DS_Store
|
||||
|
||||
### Secrets / local env
|
||||
.env
|
||||
|
||||
### App data (don’t commit runtime db/uploads)
|
||||
instance/*.db
|
||||
static/uploads/*
|
||||
!static/uploads/readme.txt
|
||||
82
README.md
82
README.md
@@ -0,0 +1,82 @@
|
||||
# Psalmbord Online
|
||||
|
||||
## Run with Docker Compose
|
||||
|
||||
This project includes a `Dockerfile` + `docker-compose.yml` to run the Flask app behind gunicorn.
|
||||
|
||||
### Configure environment (optional)
|
||||
|
||||
Copy the example env file:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
### Start
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
Open:
|
||||
|
||||
- http://localhost:5000 (or `WEB_PORT`)
|
||||
|
||||
### Persisted data
|
||||
|
||||
Docker compose bind-mounts the following so data survives container rebuilds/recreates:
|
||||
|
||||
- `./instance/` -> SQLite database file (stored at `instance/liturgie.db`)
|
||||
- `./static/uploads/` -> uploaded backgrounds/logos
|
||||
|
||||
### Default admin user
|
||||
|
||||
On startup, `init_db.py` ensures DB/tables exist and creates an admin user **only if it does not already exist**.
|
||||
|
||||
Defaults:
|
||||
|
||||
- Username: `admin`
|
||||
- Password: `admin`
|
||||
|
||||
Override via `.env`:
|
||||
|
||||
```env
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=change-me
|
||||
```
|
||||
|
||||
## Release / publish (git + docker push)
|
||||
|
||||
This repo includes helper scripts that:
|
||||
|
||||
1. Prompt for a version (e.g. `1.2.3`)
|
||||
2. Create a git commit with message `Version <version>`
|
||||
3. Push to: `https://git.alphen.cloud/bramval/PsalmbordOnlineCE`
|
||||
4. Build + push Docker image to:
|
||||
- `git.alphen.cloud/bramval/psalmbordonlinece:<version>`
|
||||
- `git.alphen.cloud/bramval/psalmbordonlinece:latest`
|
||||
|
||||
### Recommended (cross-platform Python)
|
||||
|
||||
```bash
|
||||
python release.py
|
||||
```
|
||||
|
||||
### Windows (PowerShell)
|
||||
|
||||
```powershell
|
||||
./release.ps1
|
||||
```
|
||||
|
||||
### Linux / macOS (bash)
|
||||
|
||||
```bash
|
||||
chmod +x ./release.sh
|
||||
./release.sh
|
||||
```
|
||||
|
||||
If Docker push fails due to authentication, run:
|
||||
|
||||
```bash
|
||||
docker login git.alphen.cloud
|
||||
```
|
||||
|
||||
20
app.py
20
app.py
@@ -12,8 +12,18 @@ UPLOAD_FOLDER = 'static/uploads'
|
||||
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SECRET_KEY'] = 'your_secret_key_here'
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///liturgie.db'
|
||||
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'your_secret_key_here')
|
||||
|
||||
# Prefer an env override, otherwise store the SQLite DB in Flask's `instance/` folder.
|
||||
# This keeps the DB out of the repo root and makes Docker persistence easier.
|
||||
default_db_path = os.path.join(app.instance_path, 'liturgie.db')
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get(
|
||||
'SQLALCHEMY_DATABASE_URI',
|
||||
f"sqlite:///{default_db_path}",
|
||||
)
|
||||
|
||||
# Ensure the instance folder exists so SQLite can create/open the DB file.
|
||||
os.makedirs(app.instance_path, exist_ok=True)
|
||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
|
||||
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
|
||||
@@ -672,7 +682,11 @@ def change_password():
|
||||
return render_template('change_password.html', msg=msg)
|
||||
|
||||
if __name__ == '__main__':
|
||||
if not os.path.exists('liturgie.db'):
|
||||
# Ensure instance folder exists when running without Docker.
|
||||
os.makedirs(app.instance_path, exist_ok=True)
|
||||
|
||||
# Create DB on first run when using the default instance DB path.
|
||||
if app.config['SQLALCHEMY_DATABASE_URI'].startswith('sqlite:///') and not os.path.exists(default_db_path):
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
# Create initial admin user if not exists
|
||||
|
||||
35
docker-compose.yml
Normal file
35
docker-compose.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
services:
|
||||
web:
|
||||
build:
|
||||
context: .
|
||||
container_name: psalmbordonline-web
|
||||
ports:
|
||||
# Host:Container
|
||||
- "${WEB_PORT:-5000}:5000"
|
||||
environment:
|
||||
# Flask
|
||||
- FLASK_ENV=${FLASK_ENV:-production}
|
||||
# The app reads these from env (with sensible defaults in app.py)
|
||||
- SECRET_KEY=${SECRET_KEY:-change-me}
|
||||
# Use an absolute path inside the container. Note the 4 slashes for sqlite.
|
||||
- SQLALCHEMY_DATABASE_URI=${SQLALCHEMY_DATABASE_URI:-sqlite:////instance/liturgie.db}
|
||||
# Default admin bootstrap (only created if not existing)
|
||||
- ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
|
||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin}
|
||||
command:
|
||||
# Ensure the SQLite DB + tables exist when running under gunicorn (the __main__
|
||||
# block in app.py does not run under gunicorn).
|
||||
- sh
|
||||
- -c
|
||||
- "python init_db.py && gunicorn wsgi:app -b 0.0.0.0:5000"
|
||||
volumes:
|
||||
# Persist SQLite DB (stored under Flask's instance folder)
|
||||
- ./instance:/instance
|
||||
# Persist uploaded images/backgrounds
|
||||
- ./static/uploads:/static/uploads
|
||||
restart: unless-stopped
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
42
init_db.py
Normal file
42
init_db.py
Normal file
@@ -0,0 +1,42 @@
|
||||
"""Container startup helper.
|
||||
|
||||
When the app is started via gunicorn, the `if __name__ == '__main__'` block in
|
||||
app.py is not executed, so the SQLite DB/tables/admin user may not be created.
|
||||
|
||||
This script makes startup idempotent by ensuring tables exist and creating a
|
||||
default admin user if missing.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
from app import app, db, User, Church
|
||||
|
||||
|
||||
def main() -> None:
|
||||
admin_username = os.environ.get("ADMIN_USERNAME", "admin")
|
||||
admin_password = os.environ.get("ADMIN_PASSWORD", "admin")
|
||||
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
|
||||
if not User.query.filter_by(username=admin_username).first():
|
||||
admin_church = Church.query.filter_by(name="Admin").first()
|
||||
if not admin_church:
|
||||
admin_church = Church(name="Admin")
|
||||
db.session.add(admin_church)
|
||||
db.session.commit()
|
||||
|
||||
admin_user = User(
|
||||
username=admin_username,
|
||||
password=generate_password_hash(admin_password),
|
||||
church_id=admin_church.id,
|
||||
is_admin=True,
|
||||
)
|
||||
db.session.add(admin_user)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
87
release.ps1
Normal file
87
release.ps1
Normal file
@@ -0,0 +1,87 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Ensure-Success($exitCode, $what) {
|
||||
if ($exitCode -ne 0) {
|
||||
throw "Failed: $what (exit code $exitCode)"
|
||||
}
|
||||
}
|
||||
|
||||
function Get-VersionFromUser {
|
||||
$version = Read-Host "Enter version (e.g. 1.2.3)"
|
||||
$version = $version.Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($version)) {
|
||||
throw "Version cannot be empty"
|
||||
}
|
||||
# Basic docker tag safety: allow only common characters
|
||||
if ($version -notmatch '^[0-9A-Za-z][0-9A-Za-z._-]{0,127}$') {
|
||||
throw "Invalid version '$version'. Use only letters/numbers and . _ - (max 128 chars)"
|
||||
}
|
||||
return $version
|
||||
}
|
||||
|
||||
$TargetGitUrl = "https://git.alphen.cloud/bramval/PsalmbordOnlineCE.git"
|
||||
$DockerImage = "git.alphen.cloud/bramval/psalmbordonlinece"
|
||||
|
||||
$gitCmd = Get-Command git -ErrorAction SilentlyContinue
|
||||
if (-not $gitCmd) { throw "git is not available in PATH" }
|
||||
$dockerCmd = Get-Command docker -ErrorAction SilentlyContinue
|
||||
if (-not $dockerCmd) { throw "docker is not available in PATH" }
|
||||
|
||||
$version = Get-VersionFromUser
|
||||
$commitMessage = "Version $version"
|
||||
|
||||
$branch = (git branch --show-current).Trim()
|
||||
if ($branch -ne 'main') {
|
||||
Write-Host "You are on branch '$branch' (expected 'main')." -ForegroundColor Yellow
|
||||
$ans = Read-Host "Continue and push 'main' anyway? (y/N)"
|
||||
if ($ans.ToLower() -ne 'y') { throw "Aborted by user" }
|
||||
}
|
||||
|
||||
Write-Host "\n== Git: ensuring remote 'origin' points to $TargetGitUrl ==" -ForegroundColor Cyan
|
||||
$originUrl = (git remote get-url origin 2>$null)
|
||||
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($originUrl)) {
|
||||
Write-Host "Remote 'origin' not found; adding it." -ForegroundColor Yellow
|
||||
git remote add origin $TargetGitUrl
|
||||
Ensure-Success $LASTEXITCODE "git remote add"
|
||||
} elseif ($originUrl -ne $TargetGitUrl) {
|
||||
Write-Host "Remote 'origin' is '$originUrl' -> updating to '$TargetGitUrl'" -ForegroundColor Yellow
|
||||
git remote set-url origin $TargetGitUrl
|
||||
Ensure-Success $LASTEXITCODE "git remote set-url"
|
||||
}
|
||||
|
||||
Write-Host "\n== Git: status ==" -ForegroundColor Cyan
|
||||
git status -sb
|
||||
|
||||
$confirm = Read-Host "Proceed with commit+push and docker push? (y/N)"
|
||||
if ($confirm.ToLower() -ne 'y') { throw "Aborted by user" }
|
||||
|
||||
Write-Host "\n== Git: add/commit/push ==" -ForegroundColor Cyan
|
||||
git add -A
|
||||
Ensure-Success $LASTEXITCODE "git add"
|
||||
|
||||
# Commit might fail if nothing to commit; handle gracefully
|
||||
git commit -m $commitMessage
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "No changes to commit (or commit failed). Continuing to push anyway..." -ForegroundColor Yellow
|
||||
} else {
|
||||
Write-Host "Committed: $commitMessage" -ForegroundColor Green
|
||||
}
|
||||
|
||||
git push -u origin main
|
||||
Ensure-Success $LASTEXITCODE "git push"
|
||||
|
||||
Write-Host "\n== Docker: build/tag/push ==" -ForegroundColor Cyan
|
||||
Write-Host "Docker image: $DockerImage" -ForegroundColor Gray
|
||||
Write-Host "Tags: $version, latest" -ForegroundColor Gray
|
||||
|
||||
docker build -t "${DockerImage}:${version}" -t "${DockerImage}:latest" .
|
||||
Ensure-Success $LASTEXITCODE "docker build"
|
||||
|
||||
docker push "${DockerImage}:${version}"
|
||||
Ensure-Success $LASTEXITCODE "docker push (version)"
|
||||
|
||||
docker push "${DockerImage}:latest"
|
||||
Ensure-Success $LASTEXITCODE "docker push (latest)"
|
||||
|
||||
Write-Host "\nDone." -ForegroundColor Green
|
||||
Write-Host "If docker push failed due to auth, run: docker login git.alphen.cloud" -ForegroundColor Yellow
|
||||
153
release.py
Normal file
153
release.py
Normal file
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Release helper: git commit/push + docker build/push.
|
||||
|
||||
Flow:
|
||||
1) Prompt for version
|
||||
2) git add -A
|
||||
3) git commit -m "Version <version>" (skips if nothing to commit)
|
||||
4) git push -u origin main (ensures origin points to target repo)
|
||||
5) docker build + push tags: <version> and latest
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Config:
|
||||
target_git_url: str = "https://git.alphen.cloud/bramval/PsalmbordOnlineCE.git"
|
||||
docker_image: str = "git.alphen.cloud/bramval/psalmbordonlinece"
|
||||
branch: str = "main"
|
||||
|
||||
|
||||
VERSION_RE = re.compile(r"^[0-9A-Za-z][0-9A-Za-z._-]{0,127}$")
|
||||
|
||||
|
||||
def run(cmd: list[str], *, check: bool = True) -> subprocess.CompletedProcess[str]:
|
||||
"""Run command and stream output; return CompletedProcess."""
|
||||
print(f"\n$ {' '.join(cmd)}")
|
||||
return subprocess.run(cmd, text=True, check=check)
|
||||
|
||||
|
||||
def capture(cmd: list[str]) -> str:
|
||||
"""Run command and capture stdout."""
|
||||
return subprocess.check_output(cmd, text=True).strip()
|
||||
|
||||
|
||||
def ensure_tools() -> None:
|
||||
for tool in ("git", "docker"):
|
||||
if shutil.which(tool) is None:
|
||||
raise SystemExit(f"Error: '{tool}' is not available in PATH")
|
||||
|
||||
|
||||
def prompt_version() -> str:
|
||||
version = input("Enter version (e.g. 1.2.3): ").strip()
|
||||
if not version:
|
||||
raise SystemExit("Error: version cannot be empty")
|
||||
if not VERSION_RE.match(version):
|
||||
raise SystemExit(
|
||||
"Error: invalid version. Use only letters/numbers and . _ - (max 128 chars)"
|
||||
)
|
||||
return version
|
||||
|
||||
|
||||
def prompt_yes_no(question: str, default_no: bool = True) -> bool:
|
||||
prompt = "(y/N)" if default_no else "(Y/n)"
|
||||
ans = input(f"{question} {prompt}: ").strip().lower()
|
||||
if not ans:
|
||||
return not default_no
|
||||
return ans in ("y", "yes")
|
||||
|
||||
|
||||
def ensure_origin(config: Config) -> None:
|
||||
try:
|
||||
origin_url = capture(["git", "remote", "get-url", "origin"])
|
||||
except subprocess.CalledProcessError:
|
||||
origin_url = ""
|
||||
|
||||
if not origin_url:
|
||||
print(f"Remote 'origin' not found; adding {config.target_git_url}")
|
||||
run(["git", "remote", "add", "origin", config.target_git_url])
|
||||
return
|
||||
|
||||
if origin_url != config.target_git_url:
|
||||
print(
|
||||
f"Remote 'origin' is '{origin_url}' -> updating to '{config.target_git_url}'"
|
||||
)
|
||||
run(["git", "remote", "set-url", "origin", config.target_git_url])
|
||||
|
||||
|
||||
def main() -> int:
|
||||
config = Config()
|
||||
|
||||
ensure_tools()
|
||||
version = prompt_version()
|
||||
commit_message = f"Version {version}"
|
||||
|
||||
# Branch warning
|
||||
try:
|
||||
current_branch = capture(["git", "branch", "--show-current"])
|
||||
except subprocess.CalledProcessError:
|
||||
current_branch = ""
|
||||
if current_branch and current_branch != config.branch:
|
||||
print(
|
||||
f"Warning: you are on branch '{current_branch}' (expected '{config.branch}')."
|
||||
)
|
||||
if not prompt_yes_no(f"Continue and push '{config.branch}' anyway?", default_no=True):
|
||||
return 1
|
||||
|
||||
ensure_origin(config)
|
||||
|
||||
print("\n== Git: status ==")
|
||||
run(["git", "status", "-sb"], check=False)
|
||||
|
||||
if not prompt_yes_no("Proceed with commit+push and docker push?", default_no=True):
|
||||
print("Aborted.")
|
||||
return 1
|
||||
|
||||
print("\n== Git: add/commit/push ==")
|
||||
run(["git", "add", "-A"])
|
||||
|
||||
# commit may fail if nothing to commit
|
||||
try:
|
||||
run(["git", "commit", "-m", commit_message])
|
||||
print(f"Committed: {commit_message}")
|
||||
except subprocess.CalledProcessError:
|
||||
print("No changes to commit (or commit failed). Continuing...")
|
||||
|
||||
run(["git", "push", "-u", "origin", config.branch])
|
||||
|
||||
print("\n== Docker: build/tag/push ==")
|
||||
print(f"Docker image: {config.docker_image}")
|
||||
print(f"Tags: {version}, latest")
|
||||
|
||||
run(
|
||||
[
|
||||
"docker",
|
||||
"build",
|
||||
"-t",
|
||||
f"{config.docker_image}:{version}",
|
||||
"-t",
|
||||
f"{config.docker_image}:latest",
|
||||
".",
|
||||
]
|
||||
)
|
||||
run(["docker", "push", f"{config.docker_image}:{version}"])
|
||||
run(["docker", "push", f"{config.docker_image}:latest"])
|
||||
|
||||
print("\nDone.")
|
||||
print("If docker push failed due to auth, run: docker login git.alphen.cloud")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Make Ctrl+C exit nicely
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except KeyboardInterrupt:
|
||||
print("\nAborted (Ctrl+C).")
|
||||
raise SystemExit(130)
|
||||
86
release.sh
Normal file
86
release.sh
Normal file
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
TARGET_GIT_URL="https://git.alphen.cloud/bramval/PsalmbordOnlineCE.git"
|
||||
DOCKER_IMAGE="git.alphen.cloud/bramval/psalmbordonlinece"
|
||||
BRANCH="main"
|
||||
|
||||
command -v git >/dev/null 2>&1 || { echo "git is not available in PATH" >&2; exit 1; }
|
||||
command -v docker >/dev/null 2>&1 || { echo "docker is not available in PATH" >&2; exit 1; }
|
||||
|
||||
read -r -p "Enter version (e.g. 1.2.3): " version
|
||||
version="${version//[[:space:]]/}"
|
||||
if [[ -z "$version" ]]; then
|
||||
echo "Version cannot be empty" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Basic docker tag safety
|
||||
if [[ ! "$version" =~ ^[0-9A-Za-z][0-9A-Za-z._-]{0,127}$ ]]; then
|
||||
echo "Invalid version '$version'. Use only letters/numbers and . _ - (max 128 chars)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
commit_message="Version ${version}"
|
||||
|
||||
current_branch="$(git branch --show-current)"
|
||||
if [[ "$current_branch" != "$BRANCH" ]]; then
|
||||
echo "You are on branch '$current_branch' (expected '$BRANCH')." >&2
|
||||
read -r -p "Continue and push '$BRANCH' anyway? (y/N): " ans
|
||||
if [[ "${ans,,}" != "y" ]]; then
|
||||
echo "Aborted by user" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "== Git: ensuring remote 'origin' points to ${TARGET_GIT_URL} =="
|
||||
if git remote get-url origin >/dev/null 2>&1; then
|
||||
origin_url="$(git remote get-url origin)"
|
||||
if [[ "$origin_url" != "$TARGET_GIT_URL" ]]; then
|
||||
echo "Remote 'origin' is '$origin_url' -> updating to '$TARGET_GIT_URL'"
|
||||
git remote set-url origin "$TARGET_GIT_URL"
|
||||
fi
|
||||
else
|
||||
echo "Remote 'origin' not found; adding it."
|
||||
git remote add origin "$TARGET_GIT_URL"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "== Git: status =="
|
||||
git status -sb
|
||||
|
||||
read -r -p "Proceed with commit+push and docker push? (y/N): " confirm
|
||||
if [[ "${confirm,,}" != "y" ]]; then
|
||||
echo "Aborted by user" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "== Git: add/commit/push =="
|
||||
git add -A
|
||||
|
||||
set +e
|
||||
git commit -m "$commit_message"
|
||||
commit_ec=$?
|
||||
set -e
|
||||
if [[ $commit_ec -ne 0 ]]; then
|
||||
echo "No changes to commit (or commit failed). Continuing to push anyway..." >&2
|
||||
else
|
||||
echo "Committed: $commit_message"
|
||||
fi
|
||||
|
||||
git push -u origin "$BRANCH"
|
||||
|
||||
echo
|
||||
echo "== Docker: build/tag/push =="
|
||||
echo "Docker image: $DOCKER_IMAGE"
|
||||
echo "Tags: $version, latest"
|
||||
|
||||
docker build -t "${DOCKER_IMAGE}:${version}" -t "${DOCKER_IMAGE}:latest" .
|
||||
docker push "${DOCKER_IMAGE}:${version}"
|
||||
docker push "${DOCKER_IMAGE}:latest"
|
||||
|
||||
echo
|
||||
echo "Done."
|
||||
echo "If docker push failed due to auth, run: docker login git.alphen.cloud" >&2
|
||||
@@ -263,7 +263,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contact form at BOTTOM -->
|
||||
<!-- Contact form at BOTTOM
|
||||
<div class="contact-section" style="max-width:none; margin:3.8rem 0 0 0; width:100%;">
|
||||
<h2>Contacteer ons</h2>
|
||||
<form method="POST" action="/contact" autocomplete="off">
|
||||
@@ -334,4 +334,5 @@
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
-->
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user