1+ name : Build & Release
2+
3+ permissions :
4+ contents : write
5+
6+ on :
7+ push :
8+ tags :
9+ - ' v*'
10+ workflow_dispatch :
11+ inputs :
12+ tag_name :
13+ description : ' Tag to release (optional). If empty, the pushed tag name will be used.'
14+ required : false
15+ default : ' '
16+
17+ jobs :
18+ build :
19+ name : Build (${{ matrix.os }})
20+ runs-on : ${{ matrix.os }}
21+ strategy :
22+ matrix :
23+ include :
24+ - os : windows-latest
25+ target : win
26+ artifact_name : windows-dist
27+ steps :
28+ - name : Checkout repository
29+ uses : actions/checkout@v4
30+
31+ - name : Setup Node.js
32+ uses : actions/setup-node@v4
33+ with :
34+ node-version : ' 22'
35+
36+ - name : Install system dependencies (Windows)
37+ if : matrix.os == 'windows-latest'
38+ run : |
39+ Write-Host "Skipping Npcap installation for CI build"
40+ Write-Host "If packet capture functionality is needed, users should install Npcap manually"
41+ Write-Host "from https://npcap.com/#download"
42+ shell : pwsh
43+
44+ - name : Install dependencies
45+ run : npm ci
46+
47+ - name : Build renderer and TypeScript
48+ run : npm run build:all
49+ env :
50+ VITE_BPTIMER_DB_URL : ${{ secrets.VITE_BPTIMER_DB_URL }}
51+ VITE_BPTIMER_API_KEY : ${{ secrets.VITE_BPTIMER_API_KEY }}
52+
53+ - name : Package (electron-builder)
54+ run : npx electron-builder --${{ matrix.target }} --x64 --publish=never
55+
56+ - name : Upload build artifacts
57+ uses : actions/upload-artifact@v4
58+ with :
59+ name : ${{ matrix.artifact_name }}
60+ path : |
61+ dist_electron/
62+ dist/
63+ retention-days : 1
64+
65+ release :
66+ name : Create Draft Release and Attach Assets
67+ runs-on : ubuntu-latest
68+ needs : build
69+ steps :
70+ - name : Checkout repository
71+ uses : actions/checkout@v4
72+
73+ - name : Download all artifacts
74+ uses : actions/download-artifact@v4
75+ with :
76+ path : ./artifacts
77+
78+ - name : List downloaded artifacts
79+ run : |
80+ echo "Artifact structure:"
81+ find ./artifacts -type f -name "*" | head -20
82+ echo "Total files found:"
83+ find ./artifacts -type f | wc -l
84+
85+ - name : Prepare release assets
86+ run : |
87+ # Create a clean releases directory
88+ mkdir -p release_assets
89+
90+ # Copy and organize artifacts with better naming
91+ if [ -d "./artifacts/windows-dist" ]; then
92+ mkdir -p "release_assets/windows"
93+ cp -r "./artifacts/windows-dist"/* "release_assets/windows/" 2>/dev/null || true
94+ fi
95+
96+ echo "Final release assets structure:"
97+ ls -la release_assets/
98+ find release_assets/ -type f | head -20
99+
100+ - name : Compress release assets into zips
101+ run : |
102+ mkdir -p release_archives
103+ ASSET_DIR="release_assets/windows"
104+ if [ -d "${ASSET_DIR}" ]; then
105+ echo "Creating archive for windows"
106+ if [ -d "${ASSET_DIR}/dist_electron" ]; then
107+ pushd "${ASSET_DIR}/dist_electron" >/dev/null
108+ zip -r "${GITHUB_WORKSPACE:-.}/release_archives/windows.zip" . -x "builder-debug.yml" "*builder-debug.yml" "*.blockmap" "win-unpacked/*" "**/win-unpacked/*" >/dev/null || true
109+ popd >/dev/null
110+ else
111+ pushd "${ASSET_DIR}" >/dev/null
112+ zip -r "${GITHUB_WORKSPACE:-.}/release_archives/windows.zip" . -x "builder-debug.yml" "*builder-debug.yml" "*.blockmap" "win-unpacked/*" "**/win-unpacked/*" >/dev/null || true
113+ popd >/dev/null
114+ fi
115+ ls -la "release_archives/windows.zip" || true
116+ fi
117+ echo "Archives produced:" && ls -la release_archives || true
118+
119+ - name : Determine release tag
120+ id : tag_step
121+ run : |
122+ # If the user supplied a tag_name input, use it.
123+ if [ -n "${{ github.event.inputs.tag_name }}" ]; then
124+ echo "tag=${{ github.event.inputs.tag_name }}" >> $GITHUB_OUTPUT
125+ echo "skip=false" >> $GITHUB_OUTPUT
126+ echo "Using tag: ${{ github.event.inputs.tag_name }}"
127+ exit 0
128+ fi
129+
130+ # If this run was triggered by a tag push, extract the tag name.
131+ if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
132+ TAG_NAME="${GITHUB_REF#refs/tags/}"
133+ echo "tag=${TAG_NAME}" >> $GITHUB_OUTPUT
134+ echo "skip=false" >> $GITHUB_OUTPUT
135+ echo "Using tag from push: ${TAG_NAME}"
136+ exit 0
137+ fi
138+
139+ # Not a tag push and no input provided — skip release creation to avoid attempts to release a branch ref.
140+ echo "tag=" >> $GITHUB_OUTPUT
141+ echo "skip=true" >> $GITHUB_OUTPUT
142+ echo "No tag provided and this is not a tag push (GITHUB_REF=${GITHUB_REF}). Skipping release creation."
143+
144+ - name : Rename archives with tag and generate checksums
145+ if : ${{ steps.tag_step.outputs.skip != 'true' }}
146+ run : |
147+ mkdir -p release_archives_final
148+ TAG=${{ steps.tag_step.outputs.tag }}
149+ REPO_NAME=$(basename $GITHUB_REPOSITORY)
150+ for f in release_archives/*.zip; do
151+ [ -f "$f" ] || continue
152+ OSNAME=$(basename "$f" .zip)
153+ # include architecture (builds are produced for x64)
154+ OUTNAME="${REPO_NAME}-${TAG}-${OSNAME}-x64.zip"
155+ cp "$f" "release_archives_final/${OUTNAME}"
156+ sha256sum "release_archives_final/${OUTNAME}" | awk '{print $1}' > "release_archives_final/${OUTNAME}.sha256"
157+ echo "Produced release_archives_final/${OUTNAME} and checksum"
158+ done
159+ ls -la release_archives_final || true
160+
161+ - name : Create Draft GitHub Release and upload archives
162+ if : ${{ steps.tag_step.outputs.skip != 'true' }}
163+ uses : softprops/action-gh-release@v1
164+ with :
165+ tag_name : ${{ steps.tag_step.outputs.tag }}
166+ name : ${{ steps.tag_step.outputs.tag }}
167+ draft : true
168+ files : |
169+ release_archives_final/*
170+ env :
171+ GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
172+
173+ - name : Done
174+ run : echo "Release created (draft) with artifacts attached."
0 commit comments