Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 36531662d3 | |||
| 35b87a9a98 | |||
| 80262aca9b |
@@ -4,17 +4,20 @@ on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
bump:
|
||||
description: "Version bump type (major stays fixed from latest release)"
|
||||
release_type:
|
||||
description: "Release type"
|
||||
required: false
|
||||
default: "patch"
|
||||
type: choice
|
||||
options:
|
||||
- patch
|
||||
- minor
|
||||
- major
|
||||
- minor
|
||||
- patch
|
||||
- dev
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -38,12 +41,70 @@ jobs:
|
||||
REPO: ${{ gitea.repository }}
|
||||
EVENT_NAME: ${{ gitea.event_name }}
|
||||
REF_NAME: ${{ gitea.ref_name }}
|
||||
BUMP_TYPE: ${{ gitea.event.inputs.bump || 'patch' }}
|
||||
RELEASE_TYPE: ${{ gitea.event.inputs.release_type || 'patch' }}
|
||||
DOCKER_API_VERSION: '1.44'
|
||||
run: |
|
||||
if [ "$EVENT_NAME" = "push" ] && [ -n "$REF_NAME" ]; then
|
||||
latest_stable_tag() {
|
||||
local releases_file="$1"
|
||||
python3 -c "import json, sys; releases = json.load(open(sys.argv[1], encoding='utf-8')); print(next((release.get('tag_name', '').strip() for release in releases if not release.get('prerelease') and not release.get('draft')), ''))" "$releases_file"
|
||||
}
|
||||
|
||||
next_dev_suffix() {
|
||||
local releases_file="$1"
|
||||
local base_tag="$2"
|
||||
python3 -c "import json, re, sys; releases_path, base_tag = sys.argv[1], sys.argv[2].strip(); pattern = re.compile(r'^' + re.escape(base_tag) + r'-dev\\.(\\d+)$'); releases = json.load(open(releases_path, encoding='utf-8')); highest = max([int(match.group(1)) for release in releases for match in [pattern.match(release.get('tag_name', '').strip())] if match], default=0); print(highest + 1)" "$releases_file" "$base_tag"
|
||||
}
|
||||
|
||||
make_dev_tag() {
|
||||
local releases_file="$1"
|
||||
local stable_tag="$2"
|
||||
local major minor patch next_suffix
|
||||
|
||||
if [[ "$stable_tag" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
|
||||
major=${BASH_REMATCH[1]}
|
||||
minor=${BASH_REMATCH[2]}
|
||||
patch=${BASH_REMATCH[3]}
|
||||
else
|
||||
major=0
|
||||
minor=8
|
||||
patch=31
|
||||
fi
|
||||
|
||||
patch=$((patch + 1))
|
||||
next_suffix="$(next_dev_suffix "$releases_file" "v${major}.${minor}.${patch}")"
|
||||
printf 'v%s.%s.%s-dev.%s' "$major" "$minor" "$patch" "$next_suffix"
|
||||
}
|
||||
|
||||
releases_file="$(mktemp)"
|
||||
trap 'rm -f "${releases_file:-}"' EXIT
|
||||
|
||||
if [ "$EVENT_NAME" = "push" ] && [ "$REF_NAME" = "main" ]; then
|
||||
prerelease=true
|
||||
if ! curl -fsSL -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/json" "https://git.invario-software.eu/api/v1/repos/$REPO/releases" -o "$releases_file"; then
|
||||
echo "Error: could not fetch release list"
|
||||
exit 1
|
||||
fi
|
||||
latest_tag="$(latest_stable_tag "$releases_file")"
|
||||
if [ -z "$latest_tag" ]; then
|
||||
latest_tag="v0.8.31"
|
||||
fi
|
||||
TAG="$(make_dev_tag "$releases_file" "$latest_tag")"
|
||||
elif [ "$EVENT_NAME" = "workflow_dispatch" ] && [ "$RELEASE_TYPE" = "dev" ]; then
|
||||
prerelease=true
|
||||
if ! curl -fsSL -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/json" "https://git.invario-software.eu/api/v1/repos/$REPO/releases" -o "$releases_file"; then
|
||||
echo "Error: could not fetch release list"
|
||||
exit 1
|
||||
fi
|
||||
latest_tag="$(latest_stable_tag "$releases_file")"
|
||||
if [ -z "$latest_tag" ]; then
|
||||
latest_tag="v0.8.31"
|
||||
fi
|
||||
TAG="$(make_dev_tag "$releases_file" "$latest_tag")"
|
||||
elif [ "$EVENT_NAME" = "push" ] && [ -n "$REF_NAME" ]; then
|
||||
prerelease=false
|
||||
TAG="$REF_NAME"
|
||||
else
|
||||
prerelease=false
|
||||
latest_tag="v0.8.31"
|
||||
if meta_json=$(curl -fsSL -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/json" "https://git.invario-software.eu/api/v1/repos/$REPO/releases/latest" 2>/dev/null); then
|
||||
tag_name=$(printf "%s" "$meta_json" | sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n1)
|
||||
@@ -60,14 +121,14 @@ jobs:
|
||||
major=0; minor=8; patch=31
|
||||
fi
|
||||
|
||||
if [ "${BUMP_TYPE:-}" = "major" ]; then
|
||||
if [ "${RELEASE_TYPE:-}" = "major" ]; then
|
||||
major=$((major + 1)); minor=0; patch=0
|
||||
elif [ "${BUMP_TYPE:-}" = "minor" ]; then
|
||||
elif [ "${RELEASE_TYPE:-}" = "minor" ]; then
|
||||
minor=$((minor + 1)); patch=0
|
||||
else
|
||||
patch=$((patch + 1))
|
||||
fi
|
||||
|
||||
|
||||
TAG="v${major}.${minor}.${patch}"
|
||||
fi
|
||||
|
||||
@@ -109,6 +170,7 @@ jobs:
|
||||
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
|
||||
echo "image=$IMAGE" >> "$GITHUB_OUTPUT"
|
||||
echo "lower_repo=$LOWER_REPO" >> "$GITHUB_OUTPUT"
|
||||
echo "prerelease=$prerelease" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Ensure Docker CLI is available and up to date
|
||||
run: |
|
||||
@@ -148,6 +210,7 @@ jobs:
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
|
||||
- name: Build and push release image
|
||||
if: steps.meta.outputs.prerelease != 'true'
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
@@ -157,6 +220,16 @@ jobs:
|
||||
${{ steps.meta.outputs.image }}
|
||||
git.invario-software.eu/${{ steps.meta.outputs.lower_repo }}:latest
|
||||
|
||||
- name: Build and push prerelease image
|
||||
if: steps.meta.outputs.prerelease == 'true'
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: |
|
||||
${{ steps.meta.outputs.image }}
|
||||
|
||||
- name: Create release-only docker bundle
|
||||
run: |
|
||||
mkdir -p release-bundle
|
||||
@@ -253,5 +326,6 @@ jobs:
|
||||
uses: https://gitea.com/actions/gitea-release-action@v1
|
||||
with:
|
||||
tag_name: ${{ steps.meta.outputs.tag }}
|
||||
prerelease: ${{ steps.meta.outputs.prerelease }}
|
||||
files: |
|
||||
inventarsystem-docker-bundle.tar.gz
|
||||
@@ -9,13 +9,13 @@ LOG_DIR="$PROJECT_DIR/logs"
|
||||
LOG_FILE="$LOG_DIR/update.log"
|
||||
STATE_FILE="$PROJECT_DIR/.release-version"
|
||||
REPO_SLUG="Invario/Inventarsystem"
|
||||
API_URL="https://git.invario-software.eu/api/v1/repos/$REPO_SLUG/releases/latest"
|
||||
API_BASE_URL="https://git.invario-software.eu/api/v1/repos/$REPO_SLUG"
|
||||
BUNDLE_ASSET="inventarsystem-docker-bundle.tar.gz"
|
||||
ENV_FILE="$PROJECT_DIR/.docker-build.env"
|
||||
APP_IMAGE_REPO="git.invario-software.eu/invario/inventarsystem"
|
||||
COMPOSE_FILE="docker-compose-multitenant.yml"
|
||||
MIN_ROOT_FREE_MB="${INVENTAR_MIN_ROOT_FREE_MB:-2048}"
|
||||
MODE="release"
|
||||
MODE="stable"
|
||||
|
||||
mkdir -p "$LOG_DIR"
|
||||
chmod 777 "$LOG_DIR" 2>/dev/null || true
|
||||
@@ -119,6 +119,7 @@ usage() {
|
||||
Usage: $0 [options]
|
||||
|
||||
Options:
|
||||
-dev Install the latest prerelease build
|
||||
--multitenant Use docker-compose-multitenant.yml (default)
|
||||
-h, --help Show this help message
|
||||
EOF
|
||||
@@ -127,6 +128,10 @@ EOF
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-dev)
|
||||
MODE="development"
|
||||
shift
|
||||
;;
|
||||
--multitenant)
|
||||
COMPOSE_FILE="docker-compose-multitenant.yml"
|
||||
shift
|
||||
@@ -179,7 +184,8 @@ create_backup() {
|
||||
|
||||
fetch_release_metadata() {
|
||||
local meta_file="$1"
|
||||
curl -fsSL "$API_URL" -o "$meta_file"
|
||||
local endpoint="${2:-latest}"
|
||||
curl -fsSL "$API_BASE_URL/releases/$endpoint" -o "$meta_file"
|
||||
}
|
||||
|
||||
parse_latest_tag() {
|
||||
@@ -207,6 +213,124 @@ for asset in data.get('assets', []):
|
||||
PY
|
||||
}
|
||||
|
||||
parse_release_tag() {
|
||||
local meta_file="$1"
|
||||
python3 - <<'PY' "$meta_file"
|
||||
import json, sys
|
||||
with open(sys.argv[1], 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
print(data.get('tag_name', '').strip())
|
||||
PY
|
||||
}
|
||||
|
||||
strip_prerelease_suffix() {
|
||||
local tag="$1"
|
||||
python3 - <<'PY' "$tag"
|
||||
import re, sys
|
||||
tag = sys.argv[1].strip()
|
||||
match = re.match(r'^(v\d+\.\d+\.\d+)', tag)
|
||||
print(match.group(1) if match else tag)
|
||||
PY
|
||||
}
|
||||
|
||||
compare_semver_tags() {
|
||||
local left_tag="$1"
|
||||
local right_tag="$2"
|
||||
python3 - <<'PY' "$left_tag" "$right_tag"
|
||||
import re, sys
|
||||
|
||||
def parse(tag):
|
||||
match = re.match(r'^v(\d+)\.(\d+)\.(\d+)', tag.strip())
|
||||
if not match:
|
||||
return None
|
||||
return tuple(int(part) for part in match.groups())
|
||||
|
||||
left = parse(sys.argv[1])
|
||||
right = parse(sys.argv[2])
|
||||
if left is None or right is None:
|
||||
print('unknown')
|
||||
elif left > right:
|
||||
print('gt')
|
||||
elif left < right:
|
||||
print('lt')
|
||||
else:
|
||||
print('eq')
|
||||
PY
|
||||
}
|
||||
|
||||
parse_prerelease_flag() {
|
||||
local meta_file="$1"
|
||||
python3 - <<'PY' "$meta_file"
|
||||
import json, sys
|
||||
with open(sys.argv[1], 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
print('true' if data.get('prerelease') else 'false')
|
||||
PY
|
||||
}
|
||||
|
||||
latest_stable_release() {
|
||||
local meta_file="$1"
|
||||
local releases_file="$2"
|
||||
local release_count=0
|
||||
|
||||
if ! curl -fsSL "$API_BASE_URL/releases" -o "$releases_file"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! python3 - <<'PY' "$releases_file" "$meta_file"
|
||||
import json, sys
|
||||
releases_path, target_path = sys.argv[1], sys.argv[2]
|
||||
with open(releases_path, 'r', encoding='utf-8') as f:
|
||||
releases = json.load(f)
|
||||
for release in releases:
|
||||
if not release.get('prerelease') and not release.get('draft'):
|
||||
with open(target_path, 'w', encoding='utf-8') as out:
|
||||
json.dump(release, out)
|
||||
raise SystemExit(0)
|
||||
raise SystemExit(1)
|
||||
PY
|
||||
then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
latest_prerelease() {
|
||||
local meta_file="$1"
|
||||
local releases_file="$2"
|
||||
|
||||
if ! curl -fsSL "$API_BASE_URL/releases" -o "$releases_file"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! python3 - <<'PY' "$releases_file" "$meta_file"
|
||||
import json, sys
|
||||
releases_path, target_path = sys.argv[1], sys.argv[2]
|
||||
with open(releases_path, 'r', encoding='utf-8') as f:
|
||||
releases = json.load(f)
|
||||
for release in releases:
|
||||
if release.get('prerelease') and not release.get('draft'):
|
||||
with open(target_path, 'w', encoding='utf-8') as out:
|
||||
json.dump(release, out)
|
||||
raise SystemExit(0)
|
||||
raise SystemExit(1)
|
||||
PY
|
||||
then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
fetch_latest_release_bundle() {
|
||||
local release_meta="$1"
|
||||
local bundle_url
|
||||
bundle_url="$(parse_asset_url "$release_meta" "$BUNDLE_ASSET")"
|
||||
if [ -z "$bundle_url" ]; then
|
||||
return 1
|
||||
fi
|
||||
printf '%s' "$bundle_url"
|
||||
}
|
||||
|
||||
refresh_runtime_scripts_from_main() {
|
||||
local start_url stop_url restart_url update_url
|
||||
start_url="https://git.invario-software.eu/$REPO_SLUG/raw/branch/main/start.sh"
|
||||
@@ -368,12 +492,66 @@ main() {
|
||||
archive_logs
|
||||
create_backup
|
||||
|
||||
# If user requested a development install, perform a simple dev deploy flow
|
||||
# If user requested a development install, deploy the latest prerelease.
|
||||
if [ "$MODE" = "development" ]; then
|
||||
log_message "Requested development install"
|
||||
local tag="dev"
|
||||
local app_image="$APP_IMAGE_REPO:$tag"
|
||||
local compose_path="$PROJECT_DIR/$COMPOSE_FILE"
|
||||
local tag meta_file releases_file stable_meta_file stable_tag prerelease_tag prerelease_base_tag bundle_url app_image compose_path
|
||||
local tmp_dir
|
||||
|
||||
tmp_dir="$(mktemp -d)"
|
||||
meta_file="$tmp_dir/prerelease.json"
|
||||
stable_meta_file="$tmp_dir/stable.json"
|
||||
releases_file="$tmp_dir/releases.json"
|
||||
trap 'rm -rf "${tmp_dir:-}"' EXIT
|
||||
|
||||
if ! fetch_release_metadata "$stable_meta_file" "latest"; then
|
||||
log_message "ERROR: Could not fetch latest stable release metadata"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
stable_tag="$(parse_release_tag "$stable_meta_file")"
|
||||
if [ -z "$stable_tag" ]; then
|
||||
log_message "ERROR: Could not determine latest stable release tag"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if latest_prerelease "$meta_file" "$releases_file"; then
|
||||
prerelease_tag="$(parse_release_tag "$meta_file")"
|
||||
prerelease_base_tag="$(strip_prerelease_suffix "$prerelease_tag")"
|
||||
else
|
||||
prerelease_tag=""
|
||||
prerelease_base_tag=""
|
||||
fi
|
||||
|
||||
if [ -n "$prerelease_tag" ]; then
|
||||
case "$(compare_semver_tags "$stable_tag" "$prerelease_base_tag")" in
|
||||
gt|eq)
|
||||
log_message "Latest stable release ($stable_tag) supersedes prerelease ($prerelease_tag); using stable release"
|
||||
tag="$stable_tag"
|
||||
meta_file="$stable_meta_file"
|
||||
;;
|
||||
lt)
|
||||
tag="$prerelease_tag"
|
||||
;;
|
||||
*)
|
||||
log_message "WARNING: Could not compare stable and prerelease versions; preferring prerelease when available"
|
||||
tag="$prerelease_tag"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
log_message "No prerelease found; falling back to stable release $stable_tag"
|
||||
tag="$stable_tag"
|
||||
meta_file="$stable_meta_file"
|
||||
fi
|
||||
|
||||
bundle_url="$(fetch_latest_release_bundle "$meta_file")"
|
||||
if [ -z "$bundle_url" ]; then
|
||||
log_message "ERROR: Release asset not found: $BUNDLE_ASSET"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
app_image="$APP_IMAGE_REPO:$tag"
|
||||
compose_path="$PROJECT_DIR/$COMPOSE_FILE"
|
||||
|
||||
if [ ! -f "$compose_path" ]; then
|
||||
log_message "ERROR: compose file not found: $compose_path"
|
||||
@@ -399,6 +577,9 @@ EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
download_and_extract_bundle "$bundle_url" "$tmp_dir"
|
||||
refresh_runtime_scripts_from_main
|
||||
|
||||
# Bring up stack
|
||||
docker compose -f "$compose_path" --env-file "$ENV_FILE" pull mongodb redis >> "$LOG_FILE" 2>&1 || true
|
||||
docker compose -f "$compose_path" --env-file "$ENV_FILE" up -d --remove-orphans >> "$LOG_FILE" 2>&1
|
||||
@@ -409,7 +590,7 @@ EOF
|
||||
fi
|
||||
|
||||
echo "$tag" > "$STATE_FILE"
|
||||
log_message "Development update completed successfully"
|
||||
log_message "Development update completed successfully to prerelease $tag"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -419,8 +600,8 @@ EOF
|
||||
|
||||
trap 'rm -rf "${tmp_dir:-}"' EXIT
|
||||
|
||||
log_message "Checking latest Gitea release for $REPO_SLUG..."
|
||||
if ! fetch_release_metadata "$meta_file"; then
|
||||
log_message "Checking latest stable Gitea release for $REPO_SLUG..."
|
||||
if ! fetch_release_metadata "$meta_file" "latest"; then
|
||||
log_message "WARNING: Could not fetch release metadata. Falling back to self-healing start path."
|
||||
if INVENTAR_SETUP_CRON=0 bash "$PROJECT_DIR/start.sh" >> "$LOG_FILE" 2>&1; then
|
||||
if verify_stack_health; then
|
||||
@@ -436,7 +617,7 @@ EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
latest_tag="$(parse_latest_tag "$meta_file")"
|
||||
latest_tag="$(parse_release_tag "$meta_file")"
|
||||
if [ -z "$latest_tag" ]; then
|
||||
log_message "WARNING: Could not determine latest release tag. Falling back to self-healing start path."
|
||||
if INVENTAR_SETUP_CRON=0 bash "$PROJECT_DIR/start.sh" >> "$LOG_FILE" 2>&1; then
|
||||
|
||||
Reference in New Issue
Block a user