diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0e77622 --- /dev/null +++ b/.github/workflows/release.yml @@ -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 diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..36e49ea --- /dev/null +++ b/.github/workflows/validate.yml @@ -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 diff --git a/EurKey-macOS.bundle/Contents/Info.plist b/EurKey-macOS.bundle/Contents/Info.plist index 64dc700..7678cb4 100644 --- a/EurKey-macOS.bundle/Contents/Info.plist +++ b/EurKey-macOS.bundle/Contents/Info.plist @@ -7,7 +7,40 @@ CFBundleName EurKEY-macOS CFBundleVersion - + 2026.03.03 + KLInfo_EurKEY v1.2 + + TICapsLockLanguageSwitchCapable + + TISIconIsTemplate + + TISInputSourceID + de.felixfoertsch.keyboardlayout.EurKEY-macOS.eurkeyv1.2 + TISIntendedLanguage + en + + KLInfo_EurKEY v1.3 + + TICapsLockLanguageSwitchCapable + + TISIconIsTemplate + + TISInputSourceID + de.felixfoertsch.keyboardlayout.EurKEY-macOS.eurkeyv1.3 + TISIntendedLanguage + en + + KLInfo_EurKEY v1.4 + + TICapsLockLanguageSwitchCapable + + TISIconIsTemplate + + TISInputSourceID + de.felixfoertsch.keyboardlayout.EurKEY-macOS.eurkeyv1.4 + TISIntendedLanguage + en + KLInfo_EurKEY v2.0 TICapsLockLanguageSwitchCapable diff --git a/EurKey-macOS.bundle/Contents/version.plist b/EurKey-macOS.bundle/Contents/version.plist index 3cd2431..833d6e7 100644 --- a/EurKey-macOS.bundle/Contents/version.plist +++ b/EurKey-macOS.bundle/Contents/version.plist @@ -3,10 +3,10 @@ BuildVersion - + 2026.03.03 ProjectName EurKEY-macOS SourceVersion - + 2026.03.03 diff --git a/scripts/build-bundle.sh b/scripts/build-bundle.sh new file mode 100755 index 0000000..09c0825 --- /dev/null +++ b/scripts/build-bundle.sh @@ -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' + + + + + CFBundleIdentifier +PLIST_HEADER + +echo " ${BUNDLE_ID}" >> "${CONTENTS_DIR}/Info.plist" + +cat >> "${CONTENTS_DIR}/Info.plist" << 'PLIST_NAME' + CFBundleName +PLIST_NAME + +echo " ${BUNDLE_NAME}" >> "${CONTENTS_DIR}/Info.plist" + +cat >> "${CONTENTS_DIR}/Info.plist" << 'PLIST_VERSION' + CFBundleVersion +PLIST_VERSION + +echo " ${VERSION}" >> "${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 + KLInfo_${layout_name} + + TICapsLockLanguageSwitchCapable + + TISIconIsTemplate + + TISInputSourceID + ${source_id} + TISIntendedLanguage + en + +KLINFO_ENTRY +done + +cat >> "${CONTENTS_DIR}/Info.plist" << 'PLIST_FOOTER' + + +PLIST_FOOTER + +echo "Generated Info.plist with ${#VERSIONS[@]} layout entries" + +# --- generate version.plist --- +cat > "${CONTENTS_DIR}/version.plist" << VPLIST + + + + + BuildVersion + ${VERSION} + ProjectName + ${BUNDLE_NAME} + SourceVersion + ${VERSION} + + +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}" diff --git a/scripts/create-dmg.sh b/scripts/create-dmg.sh new file mode 100755 index 0000000..b6a5e8a --- /dev/null +++ b/scripts/create-dmg.sh @@ -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)"