Skip to content
Merged
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
75 changes: 75 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Bug report
description: Report a reproducible problem with TypeSwitch
title: "[Bug] "
labels:
- bug
body:
- type: markdown
attributes:
value: |
感谢反馈。请勿提交私人 App 名称、用户名或文件路径。
Thanks for helping improve TypeSwitch. Do not include private app names, usernames, or file paths.

- type: textarea
id: problem
attributes:
label: 问题描述 / Problem description
description: 请说明发生了什么,以及你观察到的实际行为。
validations:
required: true

- type: textarea
id: reproduction_steps
attributes:
label: 复现步骤 / Steps to reproduce
placeholder: |
1. 打开……
2. 选择……
3. 观察到……
validations:
required: true

- type: textarea
id: expected_behavior
attributes:
label: 预期行为 / Expected behavior
description: 请说明你原本期望 TypeSwitch 如何运行。
validations:
required: true

- type: dropdown
id: installation_method
attributes:
label: 安装方式 / Installation method
options:
- Homebrew
- GitHub Release 手动安装 / Manual GitHub Release install
- 从源码构建 / Built from source
- 不确定 / Not sure
validations:
required: true

- type: dropdown
id: reproducibility
attributes:
label: 是否稳定复现 / Reproducibility
options:
- 每次 / Every time
- 偶尔 / Sometimes
- 一次 / Once
validations:
required: true

- type: textarea
id: diagnostics
attributes:
label: 诊断信息 / Diagnostics
description: 可选。粘贴与问题相关的错误信息或诊断内容,并先移除隐私数据。

- type: checkboxes
id: privacy_confirmation
attributes:
label: 隐私确认 / Privacy confirmation
options:
- label: 我已检查以上内容,不包含私人 App、用户名或文件路径。
required: true
2 changes: 2 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
blank_issues_enabled: false
contact_links: []
43 changes: 43 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Feature request
description: Suggest an improvement for TypeSwitch
title: "[Feature] "
labels:
- enhancement
body:
- type: markdown
attributes:
value: |
感谢你的建议。请描述实际使用场景,并避免提交隐私信息。
Thanks for your suggestion. Describe the real use case and avoid sharing private information.

- type: textarea
id: use_case
attributes:
label: 使用场景 / Use case
description: 请说明你在什么情况下需要这个功能,以及它要解决的问题。
validations:
required: true

- type: textarea
id: current_workaround
attributes:
label: 当前绕行方式 / Current workaround
description: 请说明你现在如何处理这个需求;如果没有,请填写“无”。
validations:
required: true

- type: textarea
id: desired_behavior
attributes:
label: 期望行为 / Desired behavior
description: 请从用户角度说明期望 TypeSwitch 如何运行。
validations:
required: true

- type: checkboxes
id: privacy_confirmation
attributes:
label: 隐私确认 / Privacy confirmation
options:
- label: 我已检查以上内容,不包含私人 App、用户名或文件路径。
required: true
18 changes: 18 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
## Summary

<!-- What changed and why? -->

## Testing

<!-- List the checks you ran, or explain why testing is not applicable. -->

## Changelog

Select exactly one:

- [ ] Updated both Chinese and English entries under `Unreleased` in `CHANGELOG.md`.
- [ ] No changelog entry is needed. Reason: <!-- Explain why. -->

## Privacy

- [ ] This change does not add telemetry or upload user data, or the reason this is not applicable is explained above.
11 changes: 11 additions & 0 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ concurrency:
cancel-in-progress: true

jobs:
release-scripts:
name: Release Scripts
runs-on: macos-26

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Test release scripts
run: script/test_release_scripts.sh

format:
name: Format
runs-on: macos-26
Expand Down
146 changes: 100 additions & 46 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,28 @@ on:
type: string

permissions:
contents: write
id-token: write
attestations: write
artifact-metadata: write
contents: read

concurrency:
group: release-${{ github.repository }}-${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref_name }}
cancel-in-progress: false

jobs:
build:
name: Build and Release
publish:
name: Publish GitHub Release
runs-on: macos-26

permissions:
contents: write
id-token: write
attestations: write
artifact-metadata: write
outputs:
tag: ${{ steps.release_tag.outputs.tag }}
version: ${{ steps.release_tag.outputs.version }}
commit: ${{ steps.release_tag.outputs.commit }}
asset_name: ${{ steps.release_artifact.outputs.asset_name }}
sha256: ${{ steps.release_artifact.outputs.sha256 }}

steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down Expand Up @@ -105,18 +117,12 @@ jobs:
- name: Check publishing secrets
env:
SPARKLE_ED_PRIVATE_KEY: ${{ secrets.SPARKLE_ED_PRIVATE_KEY }}
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
run: |
if [[ -z "$SPARKLE_ED_PRIVATE_KEY" ]]; then
echo "::error title=Missing Sparkle signing key::Set the SPARKLE_ED_PRIVATE_KEY repository secret before publishing a release."
exit 1
fi

if [[ -z "$HOMEBREW_TAP_TOKEN" ]]; then
echo "::error title=Missing Homebrew tap token::Set the HOMEBREW_TAP_TOKEN repository secret before publishing a release."
exit 1
fi

- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
Expand Down Expand Up @@ -250,11 +256,16 @@ jobs:
cat release-notes.html

- name: Generate Checksums
id: release_artifact
run: |
SHA256=$(shasum -a 256 TypeSwitch-macOS-universal.zip | awk '{print $1}')
echo "asset_name=TypeSwitch-macOS-universal.zip" >> "$GITHUB_OUTPUT"
echo "sha256=$SHA256" >> "$GITHUB_OUTPUT"

{
echo "### SHA-256 Checksums"
echo '```'
shasum -a 256 TypeSwitch-macOS-universal.zip
echo "$SHA256 TypeSwitch-macOS-universal.zip"
echo '```'
} > checksums.txt

Expand Down Expand Up @@ -305,6 +316,14 @@ jobs:

cp DerivedData/SparkleFeed/appcast.xml appcast.xml

- name: Verify local release artifacts
env:
RELEASE_TAG: ${{ steps.release_tag.outputs.tag }}
run: script/verify_release.sh local "$RELEASE_TAG" .

- name: Test release scripts
run: script/test_release_scripts.sh

- name: Attest release artifact
uses: actions/attest@v4
with:
Expand All @@ -321,51 +340,65 @@ jobs:
draft: false
prerelease: false
generate_release_notes: true
overwrite_files: true
tag_name: ${{ steps.release_tag.outputs.tag }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Record publish failure
if: ${{ failure() }}
run: |
{
echo "### Release distribution status"
echo
echo '- Status: `publish_failed`'
echo "- Tag: \`${{ steps.release_tag.outputs.tag || github.ref_name }}\`"
echo "- Details: The publish job did not complete successfully."
} >> "$GITHUB_STEP_SUMMARY"

sync_homebrew:
name: Sync Homebrew cask
needs: publish
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout release scripts
uses: actions/checkout@v4
with:
ref: ${{ needs.publish.outputs.commit }}

- name: Download published release asset
env:
ASSET_NAME: ${{ needs.publish.outputs.asset_name }}
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ needs.publish.outputs.tag }}
run: |
mkdir -p release-assets
gh release download "$RELEASE_TAG" \
--repo "$GITHUB_REPOSITORY" \
--dir release-assets \
--pattern "$ASSET_NAME"

ACTUAL_SHA256=$(sha256sum "release-assets/$ASSET_NAME" | awk '{print $1}')
if [[ "$ACTUAL_SHA256" != "${{ needs.publish.outputs.sha256 }}" ]]; then
echo "::error title=Published asset checksum mismatch::Expected ${{ needs.publish.outputs.sha256 }}, got ${ACTUAL_SHA256}."
exit 1
fi

- name: Checkout Homebrew tap
uses: actions/checkout@v4
with:
repository: ygsgdbd/homebrew-tap
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
path: homebrew-tap

- name: Update Homebrew cask
- name: Update and push Homebrew cask
env:
ASSET_NAME: TypeSwitch-macOS-universal.zip
RELEASE_TAG: ${{ steps.release_tag.outputs.tag }}
VERSION: ${{ steps.release_tag.outputs.version }}
RELEASE_TAG: ${{ needs.publish.outputs.tag }}
SHA256: ${{ needs.publish.outputs.sha256 }}
run: |
SHA256=$(shasum -a 256 "$ASSET_NAME" | awk '{print $1}')
CASK_PATH="homebrew-tap/Casks/typeswitch.rb"

export RELEASE_TAG VERSION SHA256 CASK_PATH
ruby <<'RUBY'
path = ENV.fetch("CASK_PATH")
release_tag = ENV.fetch("RELEASE_TAG")
version = ENV.fetch("VERSION")
sha256 = ENV.fetch("SHA256")

abort "Homebrew cask does not exist: #{path}" unless File.file?(path)

content = File.read(path)
replacements = {
/^ version ".*"$/ => " version \"#{version}\"",
/^ sha256 ".*"$/ => " sha256 \"#{sha256}\"",
/^ url ".*"$/ => " url \"https://github.com/ygsgdbd/TypeSwitch/releases/download/#{release_tag}/TypeSwitch-macOS-universal.zip\"",
}

replacements.each do |pattern, replacement|
matches = content.scan(pattern).length
abort "Expected exactly one #{pattern.inspect} entry in #{path}, found #{matches}" unless matches == 1

content.sub!(pattern, replacement)
end

File.write(path, content)
RUBY
ruby script/update_homebrew_cask.rb homebrew-tap/Casks/typeswitch.rb "$RELEASE_TAG" "$SHA256"

cd homebrew-tap
git config user.name "github-actions[bot]"
Expand All @@ -379,3 +412,24 @@ jobs:

git commit -m "Update TypeSwitch cask to ${RELEASE_TAG}"
git push

verify_distribution:
name: Verify published distribution
needs: [publish, sync_homebrew]
if: ${{ always() && needs.publish.result == 'success' }}
runs-on: macos-26
permissions:
contents: read
attestations: read
steps:
- name: Checkout release scripts
uses: actions/checkout@v4
with:
ref: ${{ needs.publish.outputs.commit }}

- name: Verify GitHub Release assets and attestation
env:
EXPECTED_SHA256: ${{ needs.publish.outputs.sha256 }}
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ needs.publish.outputs.tag }}
run: script/verify_release.sh remote "$RELEASE_TAG" "$GITHUB_REPOSITORY" "$EXPECTED_SHA256"
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,27 @@

### 🇨🇳 中文

#### 功能

- 新增输入法健康诊断:输入法列表为空、读取失败或自动切换失败时,菜单会显示具体状态,并提供重新加载输入法、重试当前 App 和打开键盘设置等恢复操作。

#### 修复

- 取消启动时的静默更新检查,确保只有用户主动点击“检查更新…”时才会访问 GitHub。
- 修复恢复已忽略 App 时丢失原输入法策略的问题,并为菜单栏图标补充当前 App 状态的 VoiceOver 描述。
- 修复输入法目录加载期间激活 App 时可能漏掉自动切换的问题;目录加载完成后仅补偿处理此前被跳过的当前 App。

### 🇺🇸 English

#### Features

- Added input method health diagnostics. When the input method catalog is empty, fails to load, or an automatic switch fails, the menu now explains the state and offers recovery actions to reload input methods, retry the current app, or open Keyboard Settings.

#### Fixes

- Removed the silent update check at startup so TypeSwitch contacts GitHub for updates only after the user clicks `Check for Updates…`.
- Fixed restored ignored apps losing their previous input method strategy, and added VoiceOver descriptions for the current app state in the menu bar icon.
- Fixed automatic switching being skipped when an app activates while the input method catalog is loading; TypeSwitch now retries only the current app whose activation was deferred.

## v0.9.0

Expand Down
Loading
Loading