Files
QuantEngineByItz/.gitea/workflows/prepare-release.yml
T
kjh2064 4b29fafcff fix: Use KST (Asia/Seoul) instead of UTC for release date component
User caught this directly: it's already 2026-07-12 in Korea, but
Run #2008's release was tagged quant_20260711.3.7150737 -- the wrong
date.

Confirmed: UTC was still 2026-07-11 16:2x when KST was already
2026-07-12 01:2x (9-hour offset). prepare-release.yml computed
TODAY via `TZ=UTC date +%Y%m%d`, which is only "correct" if the team
operates on UTC -- but this project's production server logs,
deployment cadence, and team are all Korea Standard Time. Any release
cut between midnight and 9am KST would silently tag itself with
yesterday's date.

Fixed by using `TZ=Asia/Seoul date +%Y%m%d` instead.
2026-07-12 01:29:14 +09:00

204 lines
7.7 KiB
YAML

name: Prepare Release
on:
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'
jobs:
build-and-release:
name: Build & Create Release
runs-on: ubuntu-latest
timeout-minutes: 30
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
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: |
VERSION_INPUT="${{ github.event.inputs.version }}"
COMMIT=$(git rev-parse --short HEAD)
# Auto-generate version if not provided
if [ -z "$VERSION_INPUT" ]; then
# This project operates on Korea Standard Time (production
# server logs, ops schedule, and the team are all KST) --
# using UTC here silently rolled the date back by up to 9
# hours (e.g. 2026-07-12 01:xx KST is still 2026-07-11 16:xx
# UTC), so a release cut right after midnight KST would tag
# itself with yesterday's date.
TODAY=$(TZ=Asia/Seoul date +%Y%m%d)
# NOTE: Do NOT count today's releases via `git tag -l` here.
# actions/checkout@v4 defaults to a shallow, single-branch
# clone that does not fetch any tags, so every job container
# sees zero local tags regardless of how many releases exist
# -- this is exactly why every release tonight came out as
# "quant_20260711.1.*" (three of them: b7591fb, 6ab270f,
# e49922e, all claiming to be deploy #1). Query the actual
# Gitea Releases API instead, which reflects real state.
# Sequence number resets to 0 on each new date -- the first
# release of a day is quant_YYYYMMDD.0.hash, the second .1, etc.
RELEASES_TODAY=$(curl -sf --connect-timeout 10 --max-time 30 \
-H "Authorization: token ${GITEA_TOKEN}" \
"https://gitea.taxbaik.com/api/v1/repos/${{ github.repository }}/tags?limit=50" \
| jq -r --arg prefix "quant_${TODAY}." '[.[] | select(.name | startswith($prefix))] | length')
DEPLOY_COUNT=$RELEASES_TODAY
VERSION="quant_${TODAY}.${DEPLOY_COUNT}.${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 Production Config
run: |
mkdir -p ./publish
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"
}
}
}
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 (no secrets included)"
- 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: 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"
notification:
name: Release Notification
runs-on: ubuntu-latest
if: always()
needs: 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 "════════════════════════════════════════"