mirror of
https://github.com/felixfoertsch/EurKEY-macOS.git
synced 2026-04-16 06:28:28 +02:00
add build, DMG creation scripts, CI/CD workflows
- build-bundle.sh: regenerates Info.plist with KLInfo entries for all 4 layout versions, sets CalVer bundle version, validates plists - create-dmg.sh: packages bundle into a DMG with drag-and-drop symlink to /Library/Keyboard Layouts/ - release.yml: GitHub Actions workflow that validates, builds, creates DMG, publishes GitHub Release on CalVer tag push - validate.yml: CI validation on push/PR for layout and script changes - Info.plist now declares all 4 layouts (was only v2.0) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
51
.github/workflows/release.yml
vendored
Normal file
51
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '[0-9][0-9][0-9][0-9].[0-9][0-9].[0-9][0-9]*'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: extract version from tag
|
||||
id: version
|
||||
run: echo "version=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: validate layouts
|
||||
run: python3 scripts/validate_layouts.py
|
||||
|
||||
- name: build bundle
|
||||
run: bash scripts/build-bundle.sh --version "${{ steps.version.outputs.version }}"
|
||||
|
||||
- name: create DMG
|
||||
run: bash scripts/create-dmg.sh --version "${{ steps.version.outputs.version }}"
|
||||
|
||||
- name: create GitHub release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
name: EurKEY-macOS ${{ steps.version.outputs.version }}
|
||||
files: build/EurKEY-macOS-${{ steps.version.outputs.version }}.dmg
|
||||
body: |
|
||||
## Installation
|
||||
|
||||
1. Download `EurKEY-macOS-${{ steps.version.outputs.version }}.dmg`
|
||||
2. Open the DMG
|
||||
3. Drag `EurKey-macOS.bundle` to the `Install Here (Keyboard Layouts)` folder
|
||||
4. Log out and back in (or restart)
|
||||
5. System Settings → Keyboard → Input Sources → Add → Select EurKEY
|
||||
|
||||
## Included layouts
|
||||
|
||||
- **EurKEY v1.2** — legacy version
|
||||
- **EurKEY v1.3** — official spec implementation
|
||||
- **EurKEY v1.4** — v1.3 with ẞ on Caps+§
|
||||
- **EurKEY v2.0** — custom edition
|
||||
generate_release_notes: true
|
||||
27
.github/workflows/validate.yml
vendored
Normal file
27
.github/workflows/validate.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Validate Layouts
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths:
|
||||
- 'EurKey-macOS.bundle/**'
|
||||
- 'scripts/**'
|
||||
- 'spec/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'EurKey-macOS.bundle/**'
|
||||
- 'scripts/**'
|
||||
- 'spec/**'
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: validate layouts
|
||||
run: python3 scripts/validate_layouts.py
|
||||
|
||||
- name: build bundle
|
||||
run: bash scripts/build-bundle.sh
|
||||
@@ -7,7 +7,40 @@
|
||||
<key>CFBundleName</key>
|
||||
<string>EurKEY-macOS</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string></string>
|
||||
<string>2026.03.03</string>
|
||||
<key>KLInfo_EurKEY v1.2</key>
|
||||
<dict>
|
||||
<key>TICapsLockLanguageSwitchCapable</key>
|
||||
<false/>
|
||||
<key>TISIconIsTemplate</key>
|
||||
<true/>
|
||||
<key>TISInputSourceID</key>
|
||||
<string>de.felixfoertsch.keyboardlayout.EurKEY-macOS.eurkeyv1.2</string>
|
||||
<key>TISIntendedLanguage</key>
|
||||
<string>en</string>
|
||||
</dict>
|
||||
<key>KLInfo_EurKEY v1.3</key>
|
||||
<dict>
|
||||
<key>TICapsLockLanguageSwitchCapable</key>
|
||||
<false/>
|
||||
<key>TISIconIsTemplate</key>
|
||||
<true/>
|
||||
<key>TISInputSourceID</key>
|
||||
<string>de.felixfoertsch.keyboardlayout.EurKEY-macOS.eurkeyv1.3</string>
|
||||
<key>TISIntendedLanguage</key>
|
||||
<string>en</string>
|
||||
</dict>
|
||||
<key>KLInfo_EurKEY v1.4</key>
|
||||
<dict>
|
||||
<key>TICapsLockLanguageSwitchCapable</key>
|
||||
<false/>
|
||||
<key>TISIconIsTemplate</key>
|
||||
<true/>
|
||||
<key>TISInputSourceID</key>
|
||||
<string>de.felixfoertsch.keyboardlayout.EurKEY-macOS.eurkeyv1.4</string>
|
||||
<key>TISIntendedLanguage</key>
|
||||
<string>en</string>
|
||||
</dict>
|
||||
<key>KLInfo_EurKEY v2.0</key>
|
||||
<dict>
|
||||
<key>TICapsLockLanguageSwitchCapable</key>
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildVersion</key>
|
||||
<string></string>
|
||||
<string>2026.03.03</string>
|
||||
<key>ProjectName</key>
|
||||
<string>EurKEY-macOS</string>
|
||||
<key>SourceVersion</key>
|
||||
<string></string>
|
||||
<string>2026.03.03</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
145
scripts/build-bundle.sh
Executable file
145
scripts/build-bundle.sh
Executable file
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build and validate the EurKEY-macOS keyboard layout bundle.
|
||||
#
|
||||
# Regenerates Info.plist with correct KLInfo entries for all layout versions,
|
||||
# sets the bundle version, and validates the bundle structure.
|
||||
#
|
||||
# Usage: bash scripts/build-bundle.sh [--version YYYY.MM.DD]
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
BUNDLE_DIR="${PROJECT_DIR}/EurKey-macOS.bundle"
|
||||
CONTENTS_DIR="${BUNDLE_DIR}/Contents"
|
||||
RESOURCES_DIR="${CONTENTS_DIR}/Resources"
|
||||
|
||||
# parse arguments
|
||||
VERSION="${1:-$(date +%Y.%m.%d)}"
|
||||
if [[ "${1:-}" == "--version" ]]; then
|
||||
VERSION="${2:?missing version argument}"
|
||||
fi
|
||||
|
||||
BUNDLE_ID="de.felixfoertsch.keyboardlayout.EurKEY-macOS"
|
||||
BUNDLE_NAME="EurKEY-macOS"
|
||||
|
||||
# layout versions to include
|
||||
VERSIONS=("v1.2" "v1.3" "v1.4" "v2.0")
|
||||
|
||||
echo "Building ${BUNDLE_NAME} ${VERSION}"
|
||||
echo "Bundle: ${BUNDLE_DIR}"
|
||||
echo
|
||||
|
||||
# --- validate that all required files exist ---
|
||||
errors=0
|
||||
for ver in "${VERSIONS[@]}"; do
|
||||
keylayout="${RESOURCES_DIR}/EurKEY ${ver}.keylayout"
|
||||
icns="${RESOURCES_DIR}/EurKEY ${ver}.icns"
|
||||
if [[ ! -f "${keylayout}" ]]; then
|
||||
echo "ERROR: missing ${keylayout}"
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
if [[ ! -f "${icns}" ]]; then
|
||||
echo "ERROR: missing ${icns}"
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
for lang in en de es; do
|
||||
strings="${RESOURCES_DIR}/${lang}.lproj/InfoPlist.strings"
|
||||
if [[ ! -f "${strings}" ]]; then
|
||||
echo "ERROR: missing ${strings}"
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $errors -gt 0 ]]; then
|
||||
echo "FAILED: ${errors} missing file(s)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- generate Info.plist ---
|
||||
cat > "${CONTENTS_DIR}/Info.plist" << 'PLIST_HEADER'
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
PLIST_HEADER
|
||||
|
||||
echo " <string>${BUNDLE_ID}</string>" >> "${CONTENTS_DIR}/Info.plist"
|
||||
|
||||
cat >> "${CONTENTS_DIR}/Info.plist" << 'PLIST_NAME'
|
||||
<key>CFBundleName</key>
|
||||
PLIST_NAME
|
||||
|
||||
echo " <string>${BUNDLE_NAME}</string>" >> "${CONTENTS_DIR}/Info.plist"
|
||||
|
||||
cat >> "${CONTENTS_DIR}/Info.plist" << 'PLIST_VERSION'
|
||||
<key>CFBundleVersion</key>
|
||||
PLIST_VERSION
|
||||
|
||||
echo " <string>${VERSION}</string>" >> "${CONTENTS_DIR}/Info.plist"
|
||||
|
||||
# add KLInfo for each version
|
||||
for ver in "${VERSIONS[@]}"; do
|
||||
layout_name="EurKEY ${ver}"
|
||||
# generate input source ID: bundle id + layout name with spaces removed, lowercased
|
||||
source_id_suffix=$(echo "eurkey${ver}" | tr '[:upper:]' '[:lower:]' | tr -d ' ')
|
||||
source_id="${BUNDLE_ID}.${source_id_suffix}"
|
||||
|
||||
cat >> "${CONTENTS_DIR}/Info.plist" << KLINFO_ENTRY
|
||||
<key>KLInfo_${layout_name}</key>
|
||||
<dict>
|
||||
<key>TICapsLockLanguageSwitchCapable</key>
|
||||
<false/>
|
||||
<key>TISIconIsTemplate</key>
|
||||
<true/>
|
||||
<key>TISInputSourceID</key>
|
||||
<string>${source_id}</string>
|
||||
<key>TISIntendedLanguage</key>
|
||||
<string>en</string>
|
||||
</dict>
|
||||
KLINFO_ENTRY
|
||||
done
|
||||
|
||||
cat >> "${CONTENTS_DIR}/Info.plist" << 'PLIST_FOOTER'
|
||||
</dict>
|
||||
</plist>
|
||||
PLIST_FOOTER
|
||||
|
||||
echo "Generated Info.plist with ${#VERSIONS[@]} layout entries"
|
||||
|
||||
# --- generate version.plist ---
|
||||
cat > "${CONTENTS_DIR}/version.plist" << VPLIST
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildVersion</key>
|
||||
<string>${VERSION}</string>
|
||||
<key>ProjectName</key>
|
||||
<string>${BUNDLE_NAME}</string>
|
||||
<key>SourceVersion</key>
|
||||
<string>${VERSION}</string>
|
||||
</dict>
|
||||
</plist>
|
||||
VPLIST
|
||||
|
||||
echo "Generated version.plist (${VERSION})"
|
||||
|
||||
# --- validate with plutil ---
|
||||
if command -v plutil &> /dev/null; then
|
||||
plutil -lint "${CONTENTS_DIR}/Info.plist" || exit 1
|
||||
plutil -lint "${CONTENTS_DIR}/version.plist" || exit 1
|
||||
echo "plist validation passed"
|
||||
fi
|
||||
|
||||
# --- run layout validation ---
|
||||
if [[ -f "${SCRIPT_DIR}/validate_layouts.py" ]]; then
|
||||
echo
|
||||
python3 "${SCRIPT_DIR}/validate_layouts.py" || exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Bundle build complete: ${BUNDLE_DIR}"
|
||||
echo "Version: ${VERSION}"
|
||||
63
scripts/create-dmg.sh
Executable file
63
scripts/create-dmg.sh
Executable file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
# Create a .dmg installer for EurKEY-macOS keyboard layouts.
|
||||
#
|
||||
# The DMG contains the keyboard layout bundle and a symlink to
|
||||
# /Library/Keyboard Layouts/ for drag-and-drop installation.
|
||||
#
|
||||
# Usage: bash scripts/create-dmg.sh [--version YYYY.MM.DD]
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
BUNDLE_DIR="${PROJECT_DIR}/EurKey-macOS.bundle"
|
||||
BUILD_DIR="${PROJECT_DIR}/build"
|
||||
|
||||
# parse arguments
|
||||
VERSION="${1:-$(date +%Y.%m.%d)}"
|
||||
if [[ "${1:-}" == "--version" ]]; then
|
||||
VERSION="${2:?missing version argument}"
|
||||
fi
|
||||
|
||||
DMG_NAME="EurKEY-macOS-${VERSION}"
|
||||
DMG_PATH="${BUILD_DIR}/${DMG_NAME}.dmg"
|
||||
STAGING_DIR="${BUILD_DIR}/dmg-staging"
|
||||
|
||||
echo "Creating DMG: ${DMG_NAME}"
|
||||
|
||||
# --- ensure bundle is built ---
|
||||
if [[ ! -f "${BUNDLE_DIR}/Contents/Info.plist" ]]; then
|
||||
echo "ERROR: bundle not found at ${BUNDLE_DIR}"
|
||||
echo "Run scripts/build-bundle.sh first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- prepare staging directory ---
|
||||
rm -rf "${STAGING_DIR}"
|
||||
mkdir -p "${STAGING_DIR}"
|
||||
|
||||
# copy the bundle
|
||||
cp -R "${BUNDLE_DIR}" "${STAGING_DIR}/"
|
||||
|
||||
# create symlink to installation target
|
||||
ln -s "/Library/Keyboard Layouts" "${STAGING_DIR}/Install Here (Keyboard Layouts)"
|
||||
|
||||
echo "Staged files:"
|
||||
ls -la "${STAGING_DIR}/"
|
||||
|
||||
# --- create DMG ---
|
||||
mkdir -p "${BUILD_DIR}"
|
||||
rm -f "${DMG_PATH}"
|
||||
|
||||
hdiutil create \
|
||||
-volname "${DMG_NAME}" \
|
||||
-srcfolder "${STAGING_DIR}" \
|
||||
-ov \
|
||||
-format UDZO \
|
||||
"${DMG_PATH}"
|
||||
|
||||
# --- clean up ---
|
||||
rm -rf "${STAGING_DIR}"
|
||||
|
||||
echo
|
||||
echo "DMG created: ${DMG_PATH}"
|
||||
echo "Size: $(du -h "${DMG_PATH}" | cut -f1)"
|
||||
Reference in New Issue
Block a user