4d2c23221a
User asked why every release tonight had the same "sequence number" (quant_20260711.1.*) despite creating three of them. Confirmed via API: tagsb7591fb,6ab270f, ande49922eall exist for 2026-07-11, all claiming to be deploy #1. Root cause: `actions/checkout@v4` (no fetch-depth/fetch-tags options) does a shallow, tags-less clone by default. Each prepare-release.yml run happens in a brand-new container, so `git tag -l "quant_${TODAY}.*"` always sees zero local tags regardless of how many releases actually exist -- DEPLOY_COUNT was permanently stuck at 0+1=1. Fix: query GET /repos/{repo}/tags via the Gitea API (same token/curl pattern already used elsewhere in this workflow) to count today's actual tags, instead of relying on the job's local, incomplete git state.
196 lines
7.2 KiB
YAML
196 lines
7.2 KiB
YAML
name: Prepare Release
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: 'Release version (auto-generated if empty, e.g. quant_20260711.1.abc1234)'
|
|
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
|
|
TODAY=$(TZ=UTC 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.
|
|
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 + 1))
|
|
|
|
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 "════════════════════════════════════════"
|