Build & Release #72
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build & Release | |
| on: | |
| workflow_run: | |
| workflows: ["Security & Quality"] | |
| types: [completed] | |
| branches: [master] | |
| permissions: | |
| contents: write | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| jobs: | |
| security-gate: | |
| name: Security Gate | |
| runs-on: ubuntu-latest | |
| if: "github.event.workflow_run.conclusion == 'success' && !startsWith(github.event.workflow_run.head_commit.message, 'chore: bump version')" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22 | |
| - name: Clone core dependency | |
| run: | | |
| git clone https://github.com/CallMeTechie/gatecontrol-client-core.git .core | |
| node -e "const p=require('./package.json'); p.dependencies['@gatecontrol/client-core']='file:.core'; require('fs').writeFileSync('package.json',JSON.stringify(p,null,2)+'\n')" | |
| - run: npm install --no-audit --no-fund | |
| - name: npm audit (high/critical) | |
| run: | | |
| CRITICAL=$(npm audit --json 2>/dev/null | node -e "try{const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));console.log((d.metadata?.vulnerabilities?.critical||0))}catch{console.log(0)}") | |
| if [ "$CRITICAL" -gt 0 ]; then echo "::error::Critical vulnerabilities found"; exit 1; fi | |
| test-gate: | |
| name: Test Gate | |
| runs-on: ubuntu-latest | |
| if: "github.event.workflow_run.conclusion == 'success' && !startsWith(github.event.workflow_run.head_commit.message, 'chore: bump version')" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22 | |
| - run: npm install --ignore-scripts --no-audit --no-fund | |
| - run: npm test | |
| release: | |
| needs: [security-gate, test-gate] | |
| runs-on: windows-latest | |
| # Skip version-bump commits to prevent infinite loop | |
| if: "github.event.workflow_run.conclusion == 'success' && !startsWith(github.event.workflow_run.head_commit.message, 'chore: bump version')" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| - name: Determine version bump | |
| id: bump | |
| shell: bash | |
| env: | |
| COMMIT_MSG: ${{ github.event.head_commit.message }} | |
| run: | | |
| CURRENT=$(node -p "require('./package.json').version") | |
| IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT" | |
| FIRST_LINE=$(echo "$COMMIT_MSG" | head -1) | |
| if echo "$FIRST_LINE" | grep -qE "^feat(\(.+\))?:"; then | |
| MINOR=$((MINOR + 1)) | |
| PATCH=0 | |
| BUMP="minor" | |
| else | |
| PATCH=$((PATCH + 1)) | |
| BUMP="patch" | |
| fi | |
| NEW="${MAJOR}.${MINOR}.${PATCH}" | |
| echo "current=$CURRENT" >> "$GITHUB_OUTPUT" | |
| echo "new=$NEW" >> "$GITHUB_OUTPUT" | |
| echo "bump=$BUMP" >> "$GITHUB_OUTPUT" | |
| echo "tag=v$NEW" >> "$GITHUB_OUTPUT" | |
| echo "Version: $CURRENT → $NEW ($BUMP)" | |
| - name: Check if tag already exists | |
| id: check_tag | |
| shell: bash | |
| env: | |
| TAG: ${{ steps.bump.outputs.tag }} | |
| run: | | |
| if git rev-parse "$TAG" >/dev/null 2>&1; then | |
| echo "exists=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "exists=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Update version in package.json | |
| if: steps.check_tag.outputs.exists == 'false' | |
| shell: bash | |
| env: | |
| NEW_VERSION: ${{ steps.bump.outputs.new }} | |
| run: npm version "$NEW_VERSION" --no-git-tag-version | |
| - name: Update CHANGELOG.md | |
| if: steps.check_tag.outputs.exists == 'false' | |
| shell: bash | |
| env: | |
| NEW_VERSION: ${{ steps.bump.outputs.new }} | |
| BUMP_TYPE: ${{ steps.bump.outputs.bump }} | |
| COMMIT_MSG: ${{ github.event.head_commit.message }} | |
| run: | | |
| FIRST_LINE=$(echo "$COMMIT_MSG" | head -1) | |
| DATE=$(date +%Y-%m-%d) | |
| if echo "$FIRST_LINE" | grep -qE "^feat"; then | |
| SECTION="Features" | |
| ENTRY=$(echo "$FIRST_LINE" | sed 's/^feat[^:]*: //') | |
| elif echo "$FIRST_LINE" | grep -qE "^fix"; then | |
| SECTION="Fixes" | |
| ENTRY=$(echo "$FIRST_LINE" | sed 's/^fix[^:]*: //') | |
| elif echo "$FIRST_LINE" | grep -qE "^docs"; then | |
| SECTION="Dokumentation" | |
| ENTRY=$(echo "$FIRST_LINE" | sed 's/^docs[^:]*: //') | |
| else | |
| SECTION="Änderungen" | |
| ENTRY=$(echo "$FIRST_LINE" | sed 's/^[a-z]*[^:]*: //') | |
| fi | |
| NEW_BLOCK="## [$NEW_VERSION] — $DATE | |
| ### $SECTION | |
| - $ENTRY" | |
| # Remove leading whitespace from heredoc | |
| NEW_BLOCK=$(echo "$NEW_BLOCK" | sed 's/^ //') | |
| if [ -f CHANGELOG.md ]; then | |
| TEMP=$(mktemp) | |
| awk -v block="$NEW_BLOCK" ' | |
| /^# Changelog/ { print; print ""; print block; print ""; print "---"; next } | |
| { print } | |
| ' CHANGELOG.md > "$TEMP" | |
| mv "$TEMP" CHANGELOG.md | |
| fi | |
| - name: Commit version bump | |
| if: steps.check_tag.outputs.exists == 'false' | |
| shell: bash | |
| env: | |
| TAG: ${{ steps.bump.outputs.tag }} | |
| NEW_VERSION: ${{ steps.bump.outputs.new }} | |
| run: | | |
| git config user.name "github-actions" | |
| git config user.email "github-actions@github.com" | |
| git add package.json package-lock.json CHANGELOG.md 2>/dev/null || true | |
| git add package.json | |
| git commit -m "chore: bump version to $NEW_VERSION" | |
| git tag "$TAG" | |
| git push origin master --tags | |
| - name: Clone gatecontrol-client-core (shared dependency) | |
| if: steps.check_tag.outputs.exists == 'false' | |
| shell: bash | |
| run: | | |
| git clone "https://x-access-token:${GH_TOKEN}@github.com/CallMeTechie/gatecontrol-client-core.git" .core | |
| node -e "const p=require('./package.json'); p.dependencies['@gatecontrol/client-core']='file:.core'; require('fs').writeFileSync('package.json',JSON.stringify(p,null,2)+'\n')" | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Install dependencies | |
| if: steps.check_tag.outputs.exists == 'false' | |
| shell: bash | |
| run: npm install --no-audit --no-fund | |
| - name: Build installer | |
| if: steps.check_tag.outputs.exists == 'false' | |
| run: npm run build:installer | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build portable | |
| if: steps.check_tag.outputs.exists == 'false' | |
| shell: bash | |
| run: | | |
| npx electron-builder --win dir --config.win.requestedExecutionLevel=asInvoker | |
| curl -sL https://github.com/electron/rcedit/releases/download/v2.0.0/rcedit-x64.exe -o rcedit.exe | |
| ./rcedit.exe "dist/win-unpacked/GateControl Community Client.exe" --set-requested-execution-level requireAdministrator | |
| echo "Patched with requireAdministrator" | |
| cd dist && powershell Compress-Archive -Path "win-unpacked/*" -DestinationPath "GateControl-Community-Client-Portable.zip" -Force | |
| echo "Portable ZIP created" | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Create GitHub Release | |
| if: steps.check_tag.outputs.exists == 'false' | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ steps.bump.outputs.tag }} | |
| name: GateControl Community Client ${{ steps.bump.outputs.tag }} | |
| generate_release_notes: true | |
| files: | | |
| dist/*.exe | |
| dist/*.zip |