Skip to content

fix(tests): skip hardcoded-dev-path regression guard on CI #1

fix(tests): skip hardcoded-dev-path regression guard on CI

fix(tests): skip hardcoded-dev-path regression guard on CI #1

Workflow file for this run

name: Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Tag to release (must already exist)'
required: true
permissions:
contents: write
jobs:
release:
runs-on: macos-15
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Resolve tag
id: tag
env:
INPUT_TAG: ${{ inputs.tag }}
run: |
if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
TAG="$INPUT_TAG"
else
TAG="${GITHUB_REF#refs/tags/}"
fi
# Validate tag format to prevent injection via free-text input
if ! [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "ERROR: tag '$TAG' does not match vMAJOR.MINOR.PATCH pattern" >&2
exit 1
fi
VERSION="${TAG#v}"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode_16.4.app
- name: Show Xcode version
run: xcodebuild -version
- name: Build Release
run: |
xcodebuild -project DevWatchdog.xcodeproj \
-scheme DevWatchdog \
-configuration Release \
-derivedDataPath build \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO \
build
- name: Package .app as ZIP
id: package
env:
VERSION: ${{ steps.tag.outputs.version }}
run: |
APP_PATH="build/Build/Products/Release/DevWatchdog.app"
ASSET="DevWatchdog-${VERSION}.zip"
# ditto preserves resource forks/metadata better than zip for macOS apps
ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "$ASSET"
echo "asset=$ASSET" >> "$GITHUB_OUTPUT"
ls -lh "$ASSET"
shasum -a 256 "$ASSET"
- name: Extract CHANGELOG section
id: notes
env:
VERSION: ${{ steps.tag.outputs.version }}
run: |
NOTES_FILE="RELEASE_NOTES.md"
# Extract the section between "## [VERSION]" and the next "## [" heading
awk -v ver="$VERSION" '
$0 ~ "^## \\[" ver "\\]" { flag=1; next }
flag && /^## \[/ { exit }
flag { print }
' CHANGELOG.md > "$NOTES_FILE"
if [[ ! -s "$NOTES_FILE" ]]; then
echo "WARNING: no CHANGELOG section for [$VERSION] — using placeholder."
printf "Release %s\n\nSee CHANGELOG.md for details." "$VERSION" > "$NOTES_FILE"
fi
# Prepend install hint
{
echo "## Installation"
echo ""
echo "1. Download \`DevWatchdog-${VERSION}.zip\` below"
echo "2. Unzip and move \`DevWatchdog.app\` to \`/Applications\`"
echo "3. First launch: right-click → Open (ad-hoc signed; Gatekeeper requires one-time approval)"
echo ""
echo "---"
echo ""
cat "$NOTES_FILE"
} > "RELEASE_NOTES_FINAL.md"
mv "RELEASE_NOTES_FINAL.md" "$NOTES_FILE"
echo "notes_file=$NOTES_FILE" >> "$GITHUB_OUTPUT"
echo "--- Release notes preview ---"
cat "$NOTES_FILE"
- name: Create / update GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.tag.outputs.tag }}
VERSION: ${{ steps.tag.outputs.version }}
ASSET: ${{ steps.package.outputs.asset }}
NOTES: ${{ steps.notes.outputs.notes_file }}
run: |
# If the release already exists (e.g., re-run), upload the asset instead of failing.
if gh release view "$TAG" >/dev/null 2>&1; then
echo "Release $TAG already exists — uploading asset (clobber)."
gh release upload "$TAG" "$ASSET" --clobber
else
gh release create "$TAG" \
--title "DevWatchdog $TAG" \
--notes-file "$NOTES" \
--generate-notes \
"$ASSET"
fi
- name: Upload build artifact (for workflow re-use)
uses: actions/upload-artifact@v4
with:
name: DevWatchdog-${{ steps.tag.outputs.version }}
path: DevWatchdog-*.zip
retention-days: 30