Files
QuantEngineByItz/.gitea/workflows/prepare-release.yml
T
kjh2064 781e04f6e9
Validators (Pushes and Pull Requests) / UI & Storage Validation (push) Failing after 12s
Validators (Pushes and Pull Requests) / Database & Schema Validation (push) Successful in 12s
Validators (Pushes and Pull Requests) / Notify PR Results (push) Has been cancelled
Workflow Lint & Validation / Lint All Workflow Files (push) Has been cancelled
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (push) Failing after 19s
Validators (Pushes and Pull Requests) / WBS & Audit Validations (push) Has been skipped
Validators (Pushes and Pull Requests) / .NET Contracts (push) Has been skipped
Validators (Pushes and Pull Requests) / Calibration & Performance (push) Has been skipped
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (push) Has been skipped
Validators (Pushes and Pull Requests) / CI Workflow Lint (push) Has been cancelled
Validators (Pushes and Pull Requests) / Security & Secrets (push) Has been cancelled
Workflow Lint & Validation / Notify Lint Results (push) Has been cancelled
Workflow Lint & Validation / Validate Secrets Contract (push) Has been cancelled
feat: display deployment version in admin footer
- Add AppVersion to appsettings.Production.json in prepare-release.yml
- Display version in _AdminLayout.cshtml footer via IConfiguration
- Shows deployed version (e.g., v0.1.20260724.165410.7bd491e) for users

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-24 17:06:56 +09:00

289 lines
10 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: Prepare Release
on:
workflow_run:
workflows: ["Validators (Pushes and Pull Requests)"]
types: [completed]
workflow_dispatch:
inputs:
version:
description: 'Release version (auto-generated if empty, e.g. quant_20260711.0.abc1234 for the first deploy that day)'
required: false
type: string
env:
DOTNET_VERSION: '10.0.x'
concurrency:
group: prepare-release-${{ github.event.workflow_run.head_sha || github.sha }}
cancel-in-progress: false
jobs:
upstream-gate:
name: "Upstream CI Success Gate"
runs-on: ubuntu-latest
steps:
- name: Check CI Pipeline Status
run: |
if [ "${{ github.event_name }}" = "workflow_run" ]; then
if [ "${{ github.event.workflow_run.conclusion }}" != "success" ]; then
echo "❌ ERROR: CI pipeline failed — release preparation blocked"
exit 1
fi
echo "✓ CI pipeline succeeded — proceeding to release"
else
echo " Release triggered manually — skipping upstream CI check"
fi
build-and-release:
name: Build & Create Release
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
timeout-minutes: 30
needs: upstream-gate
outputs:
version: ${{ steps.metadata.outputs.version }}
commit: ${{ steps.metadata.outputs.commit }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Generate Metadata
id: metadata
run: |
VERSION_INPUT="${{ github.event.inputs.version }}"
COMMIT=$(git rev-parse --short HEAD)
# Auto-generate version if not provided
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}"
else
VERSION="$VERSION_INPUT"
fi
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "commit=${COMMIT}" >> $GITHUB_OUTPUT
echo "Version: $VERSION"
echo "Commit: $COMMIT"
- name: Restore
run: |
dotnet restore src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj
- name: Build (Release)
run: |
dotnet build src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj \
-c Release \
--no-restore \
-p:ContinuousIntegrationBuild=true
- name: Publish
run: |
dotnet publish src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj \
-c Release \
-o ./publish \
--no-restore \
--no-build
- name: Write Version Text
run: |
echo "${{ steps.metadata.outputs.version }}" > ./publish/version.txt
- name: Write Production Config
run: |
mkdir -p ./publish
VERSION="${{ steps.metadata.outputs.version }}"
python3 -c '
import json
import pathlib
# NOTE: No ConnectionStrings here on purpose. The real DB
# password lives only in /home/kjh2064/.config/quantengine.env
# on the production server and is injected via systemd
# EnvironmentFile (ConnectionStrings__DefaultConnection),
# which overrides this file at runtime. Never bake secrets
# into a build artifact that ends up in a Gitea Release.
config = {
"Logging": {
"LogLevel": {
"Default": "Information"
}
},
"AppVersion": "'$VERSION'"
}
pathlib.Path("./publish/appsettings.Production.json").write_text(
json.dumps(config, ensure_ascii=False, indent=2),
encoding="utf-8"
)'
test -s ./publish/appsettings.Production.json || { echo "ERROR: appsettings.Production.json is empty"; exit 1; }
echo "✓ Production config created (version: $VERSION)"
- name: Package Artifact
run: |
VERSION="${{ steps.metadata.outputs.version }}"
ARTIFACT="quantengine_${VERSION}.tar.gz"
tar -czf "$ARTIFACT" -C ./publish .
echo "artifact=${ARTIFACT}" >> $GITHUB_OUTPUT
echo "✓ Package: $(du -sh $ARTIFACT | cut -f1)"
file "$ARTIFACT"
- name: Generate Artifact Checksum
run: |
VERSION="${{ steps.metadata.outputs.version }}"
ARTIFACT="quantengine_${VERSION}.tar.gz"
sha256sum "$ARTIFACT" | awk '{print $1}' > "${ARTIFACT}.sha256"
echo "✓ Checksum created: ${ARTIFACT}.sha256"
cat "${ARTIFACT}.sha256"
- name: Generate Release Manifest
run: |
VERSION="${{ steps.metadata.outputs.version }}"
COMMIT="${{ steps.metadata.outputs.commit }}"
ARTIFACT="quantengine_${VERSION}.tar.gz"
CHECKSUM=$(cat "${ARTIFACT}.sha256")
python3 - <<PY
import json
import pathlib
payload = {
"version": "${VERSION}",
"commit": "${COMMIT}",
"artifact": "${ARTIFACT}",
"sha256": "${CHECKSUM}",
}
pathlib.Path("${ARTIFACT}.manifest.json").write_text(
json.dumps(payload, ensure_ascii=False, indent=2),
encoding="utf-8",
)
PY
echo "✓ Manifest created"
- name: Validate Release Manifest
run: |
ARTIFACT="quantengine_${{ steps.metadata.outputs.version }}.tar.gz"
MANIFEST="${ARTIFACT}.manifest.json"
python3 - <<PY
import json
import sys
import pathlib
try:
data = json.loads(pathlib.Path("${MANIFEST}").read_text(encoding="utf-8"))
required_fields = ["version", "commit", "artifact", "sha256"]
for field in required_fields:
if field not in data or not data[field]:
print(f"ERROR: Manifest missing or empty '{field}'")
sys.exit(1)
print(f"✓ Manifest validated: {data['version']}")
except Exception as e:
print(f"ERROR: {e}")
sys.exit(1)
PY
- name: Create Git Tag
run: |
VERSION="${{ steps.metadata.outputs.version }}"
COMMIT="${{ steps.metadata.outputs.commit }}"
git config user.name "Gitea Actions"
git config user.email "actions@gitea.local"
git tag -a "$VERSION" -m "Release $VERSION (commit: $COMMIT)" HEAD
echo "✓ Local tag created: $VERSION"
git push origin "$VERSION"
echo "✓ Tag pushed: $VERSION"
- name: Create Gitea Release
env:
VERSION: ${{ steps.metadata.outputs.version }}
COMMIT: ${{ steps.metadata.outputs.commit }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: |
ARTIFACT="quantengine_${VERSION}.tar.gz"
API="https://gitea.taxbaik.com/api/v1"
REPO="kjh2064/QuantEngineByItz"
test -s "$ARTIFACT" || { echo "ERROR: artifact missing: $ARTIFACT"; exit 1; }
echo "Creating release $VERSION via Gitea API..."
RELEASE_JSON=$(curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\":\"${VERSION}\",\"name\":\"Release ${VERSION}\",\"body\":\"Release Version: ${VERSION} | Commit: ${COMMIT}\",\"target_commitish\":\"main\"}" \
"${API}/repos/${REPO}/releases")
RELEASE_ID=$(echo "$RELEASE_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "null" ]; then
echo "ERROR: Failed to create release"
echo "$RELEASE_JSON"
exit 1
fi
echo "✓ Release created: $VERSION (id: $RELEASE_ID)"
echo "Uploading artifact..."
curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: multipart/form-data" \
-F "attachment=@${ARTIFACT}" \
"${API}/repos/${REPO}/releases/${RELEASE_ID}/assets?name=${ARTIFACT}" \
-o /dev/null
echo "✓ Artifact attached: $ARTIFACT"
echo "Uploading checksum..."
curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: multipart/form-data" \
-F "attachment=@${ARTIFACT}.sha256" \
"${API}/repos/${REPO}/releases/${RELEASE_ID}/assets?name=${ARTIFACT}.sha256" \
-o /dev/null
echo "✓ Checksum attached: ${ARTIFACT}.sha256"
echo "Uploading manifest..."
curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: multipart/form-data" \
-F "attachment=@${ARTIFACT}.manifest.json" \
"${API}/repos/${REPO}/releases/${RELEASE_ID}/assets?name=${ARTIFACT}.manifest.json" \
-o /dev/null
echo "✓ Manifest attached: ${ARTIFACT}.manifest.json"
notification:
name: Release Notification
runs-on: ubuntu-latest
if: always()
needs: [upstream-gate, build-and-release]
steps:
- name: Notify Release Ready
if: needs.build-and-release.result == 'success'
run: |
echo "════════════════════════════════════════"
echo "✅ Release Ready for Deployment"
echo "════════════════════════════════════════"
echo "Version: ${{ needs.build-and-release.outputs.version }}"
echo "Commit: ${{ needs.build-and-release.outputs.commit }}"
echo ""
echo "Next: Use deploy-prod.yml to deploy this release"
echo "════════════════════════════════════════"