Skip to content

release mac test

release mac test #30

Workflow file for this run

name: Build PyCron Video Alarm
on:
push:
branches: [ "**" ]
pull_request:
branches: [ "**" ]
jobs:
check-trigger:
runs-on: ubuntu-latest
outputs:
should_build: ${{ steps.check.outputs.should_build }}
is_release: ${{ steps.check.outputs.is_release }}
build_timestamp: ${{ steps.check.outputs.build_timestamp }}
steps:
- name: Check commit/PR message
id: check
env:
COMMIT_MSG: ${{ github.event.head_commit.message }}
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
MSG=$(echo "${COMMIT_MSG}${PR_TITLE}" | tr '[:upper:]' '[:lower:]')
# Generate a single unified timestamp (Eastern Time)
export TZ='America/New_York'
TS=$(date +'%Y-%m-%d_%H%M')
echo "build_timestamp=$TS" >> $GITHUB_OUTPUT
if [[ "$MSG" == release* ]]; then
echo "should_build=true" >> $GITHUB_OUTPUT
echo "is_release=true" >> $GITHUB_OUTPUT
elif [[ "$MSG" == build* ]]; then
echo "should_build=true" >> $GITHUB_OUTPUT
echo "is_release=false" >> $GITHUB_OUTPUT
else
echo "should_build=false" >> $GITHUB_OUTPUT
echo "is_release=false" >> $GITHUB_OUTPUT
fi
build-linux:
runs-on: ubuntu-latest
needs: check-trigger
if: needs.check-trigger.outputs.should_build == 'true'
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.11
uses: actions/setup-python@v4
with:
python-version: "3.11"
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y python3-tk libxcb-cursor0
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller
- name: Build with PyInstaller
run: |
TS="${{ needs.check-trigger.outputs.build_timestamp }}"
BUILD_DATE=$(echo "$TS" | sed 's/_/ /' | sed 's/\(.*\)\(..\)/\1:\2/')
echo "Built: ${BUILD_DATE} EST" > version.txt
echo "Archive: ${TS}_PyCronVideoAlarm_Linux" >> version.txt
pyinstaller --noconfirm --onefile --windowed \
--name "PyCronVideoAlarm_Linux" \
--icon "icons/alarm_icon7.ico" \
--add-data "src:src" \
--add-data "icons/alarm_icon7.ico:." \
--add-data "icons/alarm_icon7.png:." \
--add-data "version.txt:." \
--add-data "LICENSE:." \
--hidden-import=crontab \
--hidden-import=jeepney \
--exclude-module=cv2 \
--exclude-module=numpy \
--exclude-module=scipy \
--exclude-module=pandas \
--exclude-module=matplotlib \
--exclude-module=sounddevice \
src/main.py
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: PyCronVideoAlarm_Linux
path: dist/PyCronVideoAlarm_Linux
build-windows:
runs-on: windows-latest
needs: check-trigger
if: needs.check-trigger.outputs.should_build == 'true'
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.11
uses: actions/setup-python@v4
with:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller
- name: Build with PyInstaller
run: |
$ts = "${{ needs.check-trigger.outputs.build_timestamp }}"
$tz = [System.TimeZoneInfo]::FindSystemTimeZoneById("Eastern Standard Time")
$est = [System.TimeZoneInfo]::ConvertTimeFromUtc((Get-Date).ToUniversalTime(), $tz)
"Built: $($est.ToString('yyyy-MM-dd HH:mm')) EST" | Out-File -FilePath version.txt -Encoding utf8
"Archive: ${ts}_PyCronVideoAlarm_Windows.exe" | Add-Content -Path version.txt -Encoding utf8
pyinstaller --noconfirm --onefile --windowed `
--name "PyCronVideoAlarm_Windows" `
--icon="icons/alarm_icon7.ico" `
--add-data "src;src" `
--add-data "icons/alarm_icon7.ico;." `
--add-data "icons/alarm_icon7.png;." `
--add-data "bin;bin" `
--add-data "version.txt;." `
--add-data "LICENSE;." `
--hidden-import=pycaw `
--hidden-import=comtypes `
--hidden-import=comtypes.stream `
--exclude-module=cv2 `
--exclude-module=numpy `
--exclude-module=scipy `
--exclude-module=pandas `
--exclude-module=sounddevice `
src/main.py
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: PyCronVideoAlarm_Windows
path: dist/PyCronVideoAlarm_Windows.exe
build-macos:
runs-on: macos-latest
needs: check-trigger
if: needs.check-trigger.outputs.should_build == 'true'
steps:
- uses: actions/checkout@v3
- name: Install Rosetta 2
run: softwareupdate --install-rosetta --agree-to-license || true
- name: Set up Python 3.11 (x86_64 for cross-platform compatibility)
uses: actions/setup-python@v5
with:
python-version: "3.11"
architecture: x64
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller
- name: Generate macOS .icns file
run: |
mkdir -p build_tmp/icon.iconset
sips -z 16 16 icons/alarm_icon7.png --out build_tmp/icon.iconset/icon_16x16.png
sips -z 32 32 icons/alarm_icon7.png --out build_tmp/icon.iconset/icon_16x16@2x.png
sips -z 32 32 icons/alarm_icon7.png --out build_tmp/icon.iconset/icon_32x32.png
sips -z 64 64 icons/alarm_icon7.png --out build_tmp/icon.iconset/icon_32x32@2x.png
sips -z 128 128 icons/alarm_icon7.png --out build_tmp/icon.iconset/icon_128x128.png
sips -z 256 256 icons/alarm_icon7.png --out build_tmp/icon.iconset/icon_128x128@2x.png
sips -z 256 256 icons/alarm_icon7.png --out build_tmp/icon.iconset/icon_256x256.png
sips -z 512 512 icons/alarm_icon7.png --out build_tmp/icon.iconset/icon_256x256@2x.png
sips -z 512 512 icons/alarm_icon7.png --out build_tmp/icon.iconset/icon_512x512.png
sips -z 1024 1024 icons/alarm_icon7.png --out build_tmp/icon.iconset/icon_512x512@2x.png
iconutil -c icns build_tmp/icon.iconset
mv build_tmp/icon.icns icons/alarm_icon7.icns
rm -rf build_tmp
- name: Build with PyInstaller (Universal)
run: |
TS="${{ needs.check-trigger.outputs.build_timestamp }}"
BUILD_DATE=$(date +'%Y-%m-%d %H:%M EST')
echo "Built: $BUILD_DATE" > version.txt
echo "Archive: ${TS}_PyCronVideoAlarm_macOS" >> version.txt
pyinstaller --noconfirm --onefile --windowed \
--name "PyCronVideoAlarm" \
--icon "icons/alarm_icon7.icns" \
--osx-bundle-identifier "com.juke32.pycronvideoalarm" \
--target-arch x86_64 \
--add-data "src:src" \
--add-data "icons/alarm_icon7.icns:." \
--add-data "icons/alarm_icon7.png:." \
--add-data "version.txt:." \
--add-data "LICENSE:." \
--exclude-module=cv2 \
--exclude-module=numpy \
--exclude-module=scipy \
--exclude-module=pandas \
--exclude-module=matplotlib \
--exclude-module=sounddevice \
src/main.py
- name: Inject macOS Permissions into Info.plist
run: |
PLIST="dist/PyCronVideoAlarm.app/Contents/Info.plist"
if [ -f "$PLIST" ]; then
plutil -insert NSAppleEventsUsageDescription -string "PyCron Video Alarm needs to control the system to execute alarm sequences." "$PLIST"
plutil -insert NSCalendarsUsageDescription -string "PyCron Video Alarm needs to schedule alarms." "$PLIST"
plutil -insert NSRemindersUsageDescription -string "PyCron Video Alarm needs to integrate with reminders." "$PLIST"
plutil -insert NSDesktopFolderUsageDescription -string "PyCron Video Alarm needs to access desktop media for your alarms." "$PLIST"
plutil -insert NSDocumentsFolderUsageDescription -string "PyCron Video Alarm needs to access documents media for your alarms." "$PLIST"
plutil -insert NSDownloadsFolderUsageDescription -string "PyCron Video Alarm needs to access downloaded media for your alarms." "$PLIST"
plutil -insert NSMicrophoneUsageDescription -string "PyCron Video Alarm occasionally requires audio capture if custom actions require it." "$PLIST"
plutil -insert NSScreenCaptureDescription -string "PyCron Video Alarm requires screen capture to perform screenshot sequences." "$PLIST"
echo "Permissions successfully added"
else
echo "Error: Info.plist not found!"
exit 1
fi
- name: Ad-hoc Code Sign
run: |
xattr -cr dist/PyCronVideoAlarm.app
codesign --force --deep --sign - dist/PyCronVideoAlarm.app
- name: Create macOS DMG (Universal)
run: |
cd dist
hdiutil create -volname "PyCronVideoAlarm" -srcfolder "PyCronVideoAlarm.app" -ov -format UDZO "PyCronVideoAlarm_macOS.dmg"
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: PyCronVideoAlarm_macOS
path: dist/PyCronVideoAlarm_macOS.dmg
create-release:
runs-on: ubuntu-latest
needs: [check-trigger, build-linux, build-windows, build-macos]
if: needs.check-trigger.outputs.is_release == 'true'
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: release_artifacts
- name: Create Multi-Platform Portable Bundle
run: |
mkdir -p SuperPortable/portablevideoalarm
# Intelligent Media Finder: Checks root and subfolders, case-insensitive
# 1. Look for sequences
if [ -d sequences ]; then cp -r sequences SuperPortable/portablevideoalarm/;
elif [ -d PortableVideoAlarm/sequences ]; then cp -r PortableVideoAlarm/sequences SuperPortable/portablevideoalarm/;
elif [ -d portablevideoalarm/sequences ]; then cp -r portablevideoalarm/sequences SuperPortable/portablevideoalarm/;
else mkdir -p SuperPortable/portablevideoalarm/sequences; fi
# 2. Look for audio
if [ -d audio ]; then cp -r audio SuperPortable/portablevideoalarm/;
elif [ -d PortableVideoAlarm/audio ]; then cp -r PortableVideoAlarm/audio SuperPortable/portablevideoalarm/;
elif [ -d portablevideoalarm/audio ]; then cp -r portablevideoalarm/audio SuperPortable/portablevideoalarm/;
else mkdir -p SuperPortable/portablevideoalarm/audio; fi
# 3. Look for video
if [ -d video ]; then cp -r video SuperPortable/portablevideoalarm/;
elif [ -d PortableVideoAlarm/video ]; then cp -r PortableVideoAlarm/video SuperPortable/portablevideoalarm/;
elif [ -d portablevideoalarm/video ]; then cp -r portablevideoalarm/video SuperPortable/portablevideoalarm/;
else mkdir -p SuperPortable/portablevideoalarm/video; fi
# Copy executables into the unified folder
cp release_artifacts/PyCronVideoAlarm_Linux/PyCronVideoAlarm_Linux SuperPortable/portablevideoalarm/
cp release_artifacts/PyCronVideoAlarm_Windows/PyCronVideoAlarm_Windows.exe SuperPortable/portablevideoalarm/
cp release_artifacts/PyCronVideoAlarm_macOS/PyCronVideoAlarm_macOS.dmg SuperPortable/portablevideoalarm/
# Include LICENSE
cp LICENSE SuperPortable/portablevideoalarm/
# Create a helper README for the portable pack
echo "PyCron Video Alarm - Super Portable Pack" > SuperPortable/portablevideoalarm/README_Portable.txt
echo "==========================================" >> SuperPortable/portablevideoalarm/README_Portable.txt
echo "This folder contains executables for Linux, Windows, and macOS." >> SuperPortable/portablevideoalarm/README_Portable.txt
echo "" >> SuperPortable/portablevideoalarm/README_Portable.txt
echo "1. Run the version for your OS." >> SuperPortable/portablevideoalarm/README_Portable.txt
echo "2. Your settings, logs, and media will be stored in this shared folder." >> SuperPortable/portablevideoalarm/README_Portable.txt
cd SuperPortable
zip -r ../PyCronVideoAlarm_MultiPlatform_Portable.zip portablevideoalarm
- name: Prepare Assets for Release
run: |
mkdir staging
cp release_artifacts/PyCronVideoAlarm_Linux/PyCronVideoAlarm_Linux staging/
cp release_artifacts/PyCronVideoAlarm_Windows/PyCronVideoAlarm_Windows.exe staging/
cp release_artifacts/PyCronVideoAlarm_macOS/PyCronVideoAlarm_macOS.dmg staging/
cp PyCronVideoAlarm_MultiPlatform_Portable.zip staging/
- name: Generate Release Info
id: release_info
run: |
# Use slash instead of underscore for the user-facing date
DATE_DISPLAY=$(date +'%Y-%m-%d %H:%M EST')
DATE_STR="${{ needs.check-trigger.outputs.build_timestamp }}"
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
MSG="${{ github.event.head_commit.message }}"
# Strip the first word "release" and any leading colon/space/dash
CLEAN_MSG=$(echo "$MSG" | sed -E 's/^release[:[:space:]-]*//i' | tr -d '\n' | tr -s ' ')
echo "tag=v${DATE_STR}" >> $GITHUB_OUTPUT
echo "title=${DATE_STR} ${CLEAN_MSG}" >> $GITHUB_OUTPUT
echo "date_display=${DATE_DISPLAY}" >> $GITHUB_OUTPUT
echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.release_info.outputs.tag }}
name: ${{ steps.release_info.outputs.title }}
files: |
staging/PyCronVideoAlarm_Linux
staging/PyCronVideoAlarm_Windows.exe
staging/PyCronVideoAlarm_macOS.dmg
staging/PyCronVideoAlarm_MultiPlatform_Portable.zip
body: |
Automated Multi-Platform Release
**Release Date:** ${{ steps.release_info.outputs.date_display }}
**Commit Hash:** ${{ steps.release_info.outputs.short_sha }}
Included Files:
- **PyCronVideoAlarm_Linux**: Linux standalone binary
- **PyCronVideoAlarm_Windows.exe**: Windows standalone executable
- **PyCronVideoAlarm_macOS.dmg**: macOS Universal Binary – works on Intel and Apple Silicon (M1/M2/M3)
- **PyCronVideoAlarm_MultiPlatform_Portable.zip**: All-in-one pack with shared media directories. (download this if this is your first time using the app)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}