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
10 changes: 10 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
labels: ["dependencies"]
commit-message:
prefix: "chore(ci)"
4 changes: 4 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ jobs:
- uses: actions/checkout@v4
- name: Toolchain
run: swift --version
- name: Lint
run: |
brew install swiftlint --quiet
swiftlint --strict
- name: Build (release)
run: swift build -c release
- name: Test
Expand Down
31 changes: 31 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,37 @@ jobs:
--title "MLX Control ${VERSION}" \
--notes "${NOTES:-See CHANGELOG.md}"

# ── Homebrew Cask 자동 업데이트 ──
# homebrew-mlxcontrol 탭 레포가 있을 때 활성화. 없으면 이 스텝은 스킵됨.
- name: Update Homebrew Cask
env:
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} # repo 쓰기 권한 PAT
if: env.GH_TOKEN != ''
run: |
VERSION="${GITHUB_REF_NAME#v}"
DMG="MLXControl-${VERSION}.dmg"
SHA=$(shasum -a 256 "$DMG" | awk '{print $1}')

# homebrew-mlxcontrol 레포 클론 → cask 파일 수정 → PR
gh repo clone wonsss/homebrew-mlxcontrol /tmp/tap
cd /tmp/tap
sed -i '' \
-e "s|version \".*\"|version \"${VERSION}\"|" \
-e "s|sha256 \".*\"|sha256 \"${SHA}\"|" \
Casks/mlxcontrol.rb
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
BRANCH="bump-v${VERSION}"
git checkout -b "$BRANCH"
git add Casks/mlxcontrol.rb
git commit -m "chore: bump mlxcontrol to v${VERSION}"
git push origin "$BRANCH"
gh pr create \
--repo wonsss/homebrew-mlxcontrol \
--title "chore: bump mlxcontrol to v${VERSION}" \
--body "Auto-updated by release workflow. SHA256: \`${SHA}\`" \
--base main --head "$BRANCH"

# ── 키체인 정리 ──
- name: Cleanup keychain
if: always()
Expand Down
17 changes: 13 additions & 4 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
# SwiftLint config (local dev). Install: brew install swiftlint — then run: swiftlint
# SwiftLint config. Install: brew install swiftlint — then run: swiftlint
included:
- Sources
- Tests
disabled_rules:
- todo
- trailing_comma
- switch_case_alignment # SwiftUI inline switch expressions trigger false positives
- implicit_optional_initialization # = nil is explicit intent here, not redundancy
opt_in_rules:
- empty_count
line_length:
warning: 120
error: 180
warning: 130
error: 200
ignores_comments: true
ignores_interpolated_strings: true
identifier_name:
min_length: 1
type_body_length:
warning: 600
file_length:
warning: 1000
warning: 1400
error: 2000
cyclomatic_complexity:
warning: 15
error: 25
13 changes: 7 additions & 6 deletions Sources/MLXControl/MLXControlApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ enum Config {
"/opt/homebrew/bin/" + name,
"/usr/local/bin/" + name,
]
for c in candidates { if FileManager.default.isExecutableFile(atPath: c) { return c } }
for c in candidates where FileManager.default.isExecutableFile(atPath: c) { return c }
let paths = ProcessInfo.processInfo.environment["PATH"]?
.split(separator: ":").map(String.init) ?? []
return paths.map { $0 + "/" + name }
Expand Down Expand Up @@ -88,7 +88,7 @@ func ramFeasibility(modelBytes: Int?, freeGB: Double) -> RAMFeasibility? {
guard let b = modelBytes, freeGB > 0 else { return nil }
let modelGB = Double(b) / 1_073_741_824
if modelGB < freeGB * 0.8 { return .ok }
if modelGB < freeGB { return .warn }
if modelGB < freeGB { return .warn }
return .insufficient
}

Expand Down Expand Up @@ -180,7 +180,6 @@ enum MoveToApplications {
try fm.copyItem(atPath: src, toPath: dest)
} catch {
// On copy failure, retry via Process (no AppleScript string injection)

let rm = Process(); rm.executableURL = URL(fileURLWithPath: "/bin/rm")
rm.arguments = ["-rf", dest]
try? rm.run(); rm.waitUntilExit()
Expand Down Expand Up @@ -671,7 +670,8 @@ final class ServerController {

let alert = NSAlert()
alert.messageText = "Delete model"
alert.informativeText = "\(repo)\n\nReclaims about \(folderSize(dir))\nPermanently removes it from the disk cache."
let sizeStr = folderSize(dir)
alert.informativeText = "\(repo)\n\nReclaims about \(sizeStr)\nPermanently removes it from the disk cache."
alert.alertStyle = .warning
alert.addButton(withTitle: "Delete")
alert.addButton(withTitle: "Cancel")
Expand Down Expand Up @@ -781,7 +781,7 @@ final class ServerController {
}

private static func runSearch(_ q: String) async -> [ModelHit] {
var comps = URLComponents(string: "https://huggingface.co/api/models")!
guard var comps = URLComponents(string: "https://huggingface.co/api/models") else { return [] }
comps.queryItems = [
.init(name: "author", value: "mlx-community"),
.init(name: "search", value: q),
Expand Down Expand Up @@ -864,7 +864,8 @@ struct Sparkline: View {
Path { p in
p.move(to: CGPoint(x: 0, y: h))
pts.forEach { p.addLine(to: $0) }
p.addLine(to: CGPoint(x: pts.last!.x, y: h)); p.closeSubpath()
if let last = pts.last { p.addLine(to: CGPoint(x: last.x, y: h)) }
p.closeSubpath()
}.fill(.green.opacity(0.15))
Path { p in
p.move(to: pts[0]); pts.dropFirst().forEach { p.addLine(to: $0) }
Expand Down
Loading