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 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) # Count releases for today RELEASES_TODAY=$(git tag -l "quant_${TODAY}.*" | wc -l) 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 "════════════════════════════════════════"