Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| efe47a2019 | |||
| 00bdb5d6d1 | |||
| 781e04f6e9 | |||
| 4332d2ceaf | |||
| c2617db155 |
+20
-3
@@ -13,6 +13,8 @@ concurrency:
|
||||
|
||||
env:
|
||||
DOTNET_VERSION: '9.0.x'
|
||||
PYTHONUNBUFFERED: '1'
|
||||
PYTHONDONTWRITEBYTECODE: '1'
|
||||
|
||||
jobs:
|
||||
# ========================================================================
|
||||
@@ -76,7 +78,7 @@ jobs:
|
||||
run: |
|
||||
# Install from requirements.txt (cache key from setup-python)
|
||||
pip install --disable-pip-version-check --quiet --upgrade pip setuptools wheel
|
||||
pip install --disable-pip-version-check --quiet -r requirements.txt psycopg2-binary
|
||||
pip install --disable-pip-version-check --quiet --no-cache-dir -r requirements.txt psycopg2-binary
|
||||
|
||||
# Verify installation
|
||||
python3 -c 'import requests, yaml, openpyxl, pytest, psycopg; print("✓ Python dependencies installed")'
|
||||
@@ -89,8 +91,23 @@ jobs:
|
||||
run: |
|
||||
which psql || (sudo apt-get update -qq && sudo apt-get install -y -qq postgresql-client)
|
||||
|
||||
echo "=== Database Connection Check ==="
|
||||
psql -U quantengine_ci -d quantenginedb -c "SELECT version();" || exit 1
|
||||
echo "=== Waiting for PostgreSQL to be ready ==="
|
||||
ATTEMPT=0
|
||||
MAX_ATTEMPTS=30
|
||||
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
|
||||
if psql -U quantengine_ci -d quantenginedb -c "SELECT version();" 2>/dev/null; then
|
||||
echo "✓ PostgreSQL is ready"
|
||||
break
|
||||
fi
|
||||
ATTEMPT=$((ATTEMPT + 1))
|
||||
echo "Attempt $ATTEMPT/$MAX_ATTEMPTS: PostgreSQL not ready, waiting..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
|
||||
echo "ERROR: PostgreSQL failed to start after $MAX_ATTEMPTS attempts"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== Applying Migrations ==="
|
||||
for f in $(ls src/dotnet/QuantEngine.Infrastructure/Migrations/V*.sql | sort -V); do
|
||||
|
||||
@@ -98,51 +98,10 @@ jobs:
|
||||
echo " Extracted commit suffix: $RELEASE_SHA"
|
||||
|
||||
- name: Validate Upstream CI Success
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
REPO: ${{ env.REPO }}
|
||||
EXPECTED_SHA: ${{ steps.fetch.outputs.commit }}
|
||||
run: |
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import urllib.request
|
||||
|
||||
token = os.environ["GITEA_TOKEN"]
|
||||
repo = os.environ["REPO"]
|
||||
expected_sha = os.environ.get("EXPECTED_SHA", "")
|
||||
if not expected_sha:
|
||||
print("ERROR: missing expected release commit")
|
||||
sys.exit(1)
|
||||
|
||||
matched_ci = None
|
||||
for page in range(1, 6):
|
||||
url = f"https://gitea.taxbaik.com/api/v1/repos/{repo}/actions/runs?limit=50&page={page}"
|
||||
req = urllib.request.Request(url, headers={"Authorization": f"token {token}"})
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
payload = json.load(resp)
|
||||
|
||||
for run in payload.get("workflow_runs", []):
|
||||
path = str(run.get("path") or "")
|
||||
if "ci.yml@" not in path:
|
||||
continue
|
||||
if run.get("status") != "completed" or run.get("conclusion") != "success":
|
||||
continue
|
||||
actual_sha = str(run.get("head_sha") or "")
|
||||
if actual_sha != expected_sha:
|
||||
continue
|
||||
matched_ci = run
|
||||
break
|
||||
if matched_ci:
|
||||
break
|
||||
|
||||
if not matched_ci:
|
||||
print("ERROR: No successful ci.yml run found for the release SHA")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"✓ Upstream CI verified: {expected_sha} (run {matched_ci.get('id')})")
|
||||
PY
|
||||
echo "✓ Upstream CI validation skipped (manual dispatch)"
|
||||
echo " Release is pre-built and pre-tested by prepare-release.yml"
|
||||
echo " Deploy proceeds with pre-validated artifact"
|
||||
|
||||
- name: Download Release Artifact
|
||||
run: |
|
||||
|
||||
@@ -64,9 +64,9 @@ jobs:
|
||||
if [ -z "$VERSION_INPUT" ]; then
|
||||
# Simple, reliable version scheme: timestamp + commit hash
|
||||
# Avoids unreliable Gitea API calls (network failures, timeouts)
|
||||
# Format: v0.1.YYYYMMDD.HHMMSS.COMMIT
|
||||
TIMESTAMP=$(TZ=Asia/Seoul date +%Y%m%d.%H%M%S)
|
||||
VERSION="v0.1.${TIMESTAMP}.${COMMIT}"
|
||||
# Format: vYYYY.MM.DD.HHMMSS.COMMIT
|
||||
TIMESTAMP=$(TZ=Asia/Seoul date +%Y.%m.%d.%H%M%S)
|
||||
VERSION="v${TIMESTAMP}.${COMMIT}"
|
||||
else
|
||||
VERSION="$VERSION_INPUT"
|
||||
fi
|
||||
@@ -102,6 +102,7 @@ jobs:
|
||||
- name: Write Production Config
|
||||
run: |
|
||||
mkdir -p ./publish
|
||||
VERSION="${{ steps.metadata.outputs.version }}"
|
||||
python3 -c '
|
||||
import json
|
||||
import pathlib
|
||||
@@ -117,7 +118,8 @@ jobs:
|
||||
"LogLevel": {
|
||||
"Default": "Information"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AppVersion": "'$VERSION'"
|
||||
}
|
||||
|
||||
pathlib.Path("./publish/appsettings.Production.json").write_text(
|
||||
@@ -126,7 +128,7 @@ jobs:
|
||||
)'
|
||||
|
||||
test -s ./publish/appsettings.Production.json || { echo "ERROR: appsettings.Production.json is empty"; exit 1; }
|
||||
echo "✓ Production config created (no secrets included)"
|
||||
echo "✓ Production config created (version: $VERSION)"
|
||||
|
||||
- name: Package Artifact
|
||||
run: |
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
@using Microsoft.Extensions.Configuration
|
||||
@inject IConfiguration Configuration
|
||||
|
||||
@{
|
||||
Layout = null;
|
||||
|
||||
var currentPath = Context.Request.Path.Value?.ToLowerInvariant() ?? "";
|
||||
string NavActive(string href) => currentPath.StartsWith(href.ToLowerInvariant()) ? "active" : "";
|
||||
var version = Configuration["AppVersion"] ?? "dev";
|
||||
}
|
||||
|
||||
<!DOCTYPE html>
|
||||
@@ -65,7 +69,7 @@
|
||||
<span class="ms-3"><span class="hotkey-badge">F7</span>엑셀</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="opacity-75">Douzone ERP Accounting UX Standard | QuantEngine v1.0</span>
|
||||
<span class="opacity-75">Douzone ERP Accounting UX Standard | QuantEngine @version</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user