Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .github/workflows/publish-update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Prepare signed update
on:
workflow_dispatch:
inputs:
version:
description: Stable version for this approved release (for example 1.0.0)
required: true
type: string
publish:
description: Publish only after signed notarized bytes and complete metadata verify
default: false
type: boolean
permissions:
contents: read
concurrency:
group: WindowCleaner-signed-updates
cancel-in-progress: false
jobs:
prepare:
if: (github.repository == 'fernandobelotto/WindowCleaner' && github.ref == 'refs/heads/main') && (github.repository == 'fernandobelotto/WindowCleaner' && github.repository_owner == 'fernandobelotto' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor == 'fernandobelotto' && (github.event_name != 'pull_request' || github.event.pull_request.user.login == 'fernandobelotto')) && github.actor == 'fernandobelotto'
runs-on: macos-26
environment: release
timeout-minutes: 90
permissions:
contents: write
env:
GH_TOKEN: ${{ github.token }}
SIGNING_IDENTITY: ${{ vars.SIGNING_IDENTITY }}
DEVELOPER_ID_P12_BASE64: ${{ secrets.DEVELOPER_ID_P12_BASE64 }}
DEVELOPER_ID_P12_PASSWORD: ${{ secrets.DEVELOPER_ID_P12_PASSWORD }}
NOTARY_PRIVATE_KEY: ${{ secrets.NOTARY_PRIVATE_KEY }}
NOTARY_KEY_ID: ${{ secrets.NOTARY_KEY_ID }}
NOTARY_ISSUER: ${{ secrets.NOTARY_ISSUER }}
SPARKLE_PRIVATE_KEY: ${{ secrets.SPARKLE_PRIVATE_KEY }}
RELEASE_VERSION: ${{ inputs.version }}
RELEASE_BUILD: ${{ github.run_number }}
RELEASE_PUBLISH: ${{ inputs.publish }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Use OpenSSL 3 for Ed25519 verification
shell: bash
run: |
brew list --versions openssl@3 >/dev/null || brew install openssl@3
echo "$(brew --prefix openssl@3)/bin" >> "$GITHUB_PATH"
- name: Verify release policy and signature fixtures
run: python3 -m unittest discover -s Scripts -p 'test_*release*.py' -v
- name: Build, sign, notarize and prepare immutable update
shell: bash
run: |
args=(--version "$RELEASE_VERSION" --build "$RELEASE_BUILD")
if [[ "$RELEASE_PUBLISH" == true ]]; then args+=(--publish); fi
python3 Scripts/release-updates.py "${args[@]}"
- uses: actions/upload-artifact@v4
with:
name: signed-update-${{ inputs.version }}
path: dist/
if-no-files-found: error
44 changes: 44 additions & 0 deletions .github/workflows/updater-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Updater checks
on:
pull_request:
workflow_dispatch:
permissions:
contents: read
# This public repository uses a personal, persistent runner. Only the owner can
# trigger these jobs, and PRs must originate from this same repository. Fork and
# external-contributor PRs cannot use this runner; review/import trusted changes
# into an owner-authored repository branch before requesting required CI.
jobs:
updater:
if: github.repository == 'fernandobelotto/WindowCleaner' && github.repository_owner == 'fernandobelotto' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor == 'fernandobelotto' && (github.event_name != 'pull_request' || github.event.pull_request.user.login == 'fernandobelotto')
runs-on: [self-hosted, macOS, ARM64, ci-build, windowcleaner-build]
timeout-minutes: 90
defaults:
run:
shell: /opt/homebrew/bin/python3 /Users/fernando/ci-tools/serial-shell.py {0}
env:
DEVELOPER_DIR: /Users/fernando/ci-tools/Xcode.app/Contents/Developer
steps:
- name: Use provisioned build tools
run: |
echo /opt/homebrew/bin >> "$GITHUB_PATH"
test -x "$DEVELOPER_DIR/usr/bin/xcodebuild"
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Validate metadata, loopback transport and credential redirects
run: Scripts/test-update-transport.sh
- name: Test updater preferences and safe installation
run: xcodebuild -project WindowCleaner.xcodeproj -scheme WindowCleanerUpdates -destination 'platform=macOS' -skipPackagePluginValidation CODE_SIGNING_ALLOWED=NO test
- name: Build Store variant without self-updater
run: xcodebuild -project WindowCleaner.xcodeproj -scheme WindowCleanerAppStore -configuration Release -derivedDataPath .build/updater-store -skipPackagePluginValidation CODE_SIGNING_ALLOWED=NO build
- name: Verify Store has no Sparkle linkage or framework
run: |
python3 - <<'PY'
import pathlib, plistlib, subprocess
bundle = pathlib.Path('.build/updater-store/Build/Products/Release/WindowCleaner.app')
assert not (bundle / 'Contents/Frameworks/Sparkle.framework').exists()
assert 'Sparkle' not in subprocess.check_output(['otool', '-L', str(bundle / 'Contents/MacOS/WindowCleaner')], text=True)
info = plistlib.loads((bundle / 'Contents/Info.plist').read_bytes())
assert not any(key.startswith('SU') for key in info)
PY
1 change: 1 addition & 0 deletions Scripts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__pycache__/
Loading
Loading