├── scripts ├── smart-common-loader.sh ├── generate-standalone-scripts.sh ├── tabs.toml ├── system-setup │ ├── dev-setup.sh │ ├── tab_data.toml │ ├── system-cleanup.sh │ ├── remove-animations.sh │ └── fix-finder.sh ├── applications-setup │ ├── developer-tools │ │ ├── zed.sh │ │ ├── sublime.sh │ │ ├── vscode.sh │ │ ├── vscodium.sh │ │ ├── githubdesktop.sh │ │ ├── jetbrains-toolbox.sh │ │ └── neovim.sh │ ├── browsers │ │ ├── chromium.sh │ │ ├── zen-browser.sh │ │ ├── brave.sh │ │ ├── vivaldi.sh │ │ ├── firefox.sh │ │ ├── waterfox.sh │ │ ├── librewolf.sh │ │ ├── thorium.sh │ │ └── google-chrome.sh │ ├── communication-apps │ │ ├── slack-setup.sh │ │ ├── signal-setup.sh │ │ ├── discord-setup.sh │ │ ├── whatsapp-setup.sh │ │ ├── telegram-setup.sh │ │ └── thunderbird-setup.sh │ ├── android-debloat.sh │ ├── kitty-setup.sh │ ├── alacritty-setup.sh │ ├── fastfetch-setup.sh │ ├── zsh-setup.sh │ └── tab_data.toml └── common-script.sh ├── run-gui.sh ├── MacUtilGUI ├── logo.png ├── MacUtilGUI.icns ├── Assets │ └── MacUtilGUI.icns ├── Models │ ├── ScriptCategory.fs │ └── ScriptInfo.fs ├── MacUtilGUI.entitlements ├── ViewModels │ ├── ViewModelBase.fs │ └── MainWindowViewModel.fs ├── Program.fs ├── Info.plist ├── Views │ ├── MainWindow.axaml.fs │ └── MainWindow.axaml ├── app.manifest ├── create_icon_from_logo.sh ├── README.md ├── MacUtilGUI.fsproj ├── build_universal.sh ├── TTY_FIXES.md ├── DEPLOYMENT.md ├── App.axaml ├── sign_macos.sh ├── deploy_macos.sh └── Services │ └── ScriptService.fs ├── .github ├── dependabot.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature-reqests.yml │ └── bug-reports.yml ├── workflows │ ├── typos.yml │ ├── shellcheck.yml │ ├── fsharp.yml │ ├── bashisms.yml │ ├── Release.yml │ └── issue-slash-commands.yaml ├── release.yml ├── SECURITY.md ├── PULL_REQUEST_TEMPLATE.md ├── CONTRIBUTING.md └── CODE_OF_CONDUCT.md ├── .vscode └── tasks.json ├── LICENSE ├── README.md └── .gitignore /scripts/smart-common-loader.sh: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scripts/generate-standalone-scripts.sh: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /run-gui.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "$(dirname "$0")/MacUtilGUI" || exit 1 3 | dotnet run 4 | -------------------------------------------------------------------------------- /MacUtilGUI/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisTitusTech/macutil/HEAD/MacUtilGUI/logo.png -------------------------------------------------------------------------------- /scripts/tabs.toml: -------------------------------------------------------------------------------- 1 | directories = [ 2 | "applications-setup", 3 | "system-setup", 4 | ] 5 | -------------------------------------------------------------------------------- /MacUtilGUI/MacUtilGUI.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisTitusTech/macutil/HEAD/MacUtilGUI/MacUtilGUI.icns -------------------------------------------------------------------------------- /MacUtilGUI/Assets/MacUtilGUI.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisTitusTech/macutil/HEAD/MacUtilGUI/Assets/MacUtilGUI.icns -------------------------------------------------------------------------------- /MacUtilGUI/Models/ScriptCategory.fs: -------------------------------------------------------------------------------- 1 | namespace MacUtilGUI.Models 2 | 3 | type ScriptCategory = 4 | { Name: string 5 | Scripts: ScriptInfo list } 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: macutil documentation 4 | url: https://chris-titus-docs.github.io/macutil-docs/ 5 | about: check out the docs 6 | -------------------------------------------------------------------------------- /MacUtilGUI/Models/ScriptInfo.fs: -------------------------------------------------------------------------------- 1 | namespace MacUtilGUI.Models 2 | 3 | type ScriptInfo = 4 | { Name: string 5 | Description: string 6 | Script: string 7 | TaskList: string 8 | Category: string 9 | FullPath: string } 10 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Build MacUtil GUI", 6 | "type": "shell", 7 | "command": "dotnet", 8 | "args": [ 9 | "build" 10 | ], 11 | "group": "build", 12 | "problemMatcher": [ 13 | "$tsc" 14 | ] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /.github/workflows/typos.yml: -------------------------------------------------------------------------------- 1 | name: Check for typos 2 | 3 | on: 4 | [push, pull_request, workflow_dispatch] 5 | 6 | jobs: 7 | check-typos: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v5 12 | - run: git fetch origin ${{ github.base_ref }} 13 | 14 | - name: Run spellcheck 15 | uses: crate-ci/typos@v1.34.0 16 | -------------------------------------------------------------------------------- /MacUtilGUI/MacUtilGUI.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-jit 6 | 7 | com.apple.security.automation.apple-events 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /scripts/system-setup/dev-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # shellcheck disable=SC2086 3 | 4 | . ../common-script.sh 5 | 6 | installDepend() { 7 | ## Check for dependencies. 8 | DEPENDENCIES='tree multitail tealdeer unzip cmake make jq fd ripgrep automake autoconf rustup python pipx' 9 | printf "%b\n" "${YELLOW}Installing dependencies...${RC}" 10 | brew install $DEPENDENCIES 11 | } 12 | 13 | checkEnv 14 | installDepend -------------------------------------------------------------------------------- /MacUtilGUI/ViewModels/ViewModelBase.fs: -------------------------------------------------------------------------------- 1 | namespace MacUtilGUI.ViewModels 2 | 3 | open System.ComponentModel 4 | 5 | type ViewModelBase() = 6 | let propertyChanged = Event() 7 | 8 | interface INotifyPropertyChanged with 9 | [] 10 | member _.PropertyChanged = propertyChanged.Publish 11 | 12 | member this.OnPropertyChanged(propertyName: string) = 13 | propertyChanged.Trigger(this, PropertyChangedEventArgs(propertyName)) 14 | -------------------------------------------------------------------------------- /scripts/applications-setup/developer-tools/zed.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installZed() { 6 | if ! brewprogram_exists zed; then 7 | printf "%b\n" "${CYAN}Installing Zed.${RC}" 8 | brew install --cask zed 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Zed. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Zed installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Zed is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installZed -------------------------------------------------------------------------------- /scripts/applications-setup/browsers/chromium.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installChromium() { 6 | if ! brewprogram_exists chromium; then 7 | printf "%b\n" "${YELLOW}Installing Chromium...${RC}" 8 | brew install --cask chromium 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Chromium Browser. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Chromium Browser installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Chromium Browser is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installChromium -------------------------------------------------------------------------------- /scripts/applications-setup/communication-apps/slack-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installSlack() { 6 | if ! brewprogram_exists slack; then 7 | printf "%b\n" "${YELLOW}Installing Slack...${RC}" 8 | brew install --cask slack 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Slack. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Slack installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Slack is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installSlack -------------------------------------------------------------------------------- /scripts/applications-setup/communication-apps/signal-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installSignal() { 6 | if ! brewprogram_exists signal; then 7 | printf "%b\n" "${YELLOW}Installing Signal...${RC}" 8 | brew install --cask signal 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Signal. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Signal installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Signal is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installSignal -------------------------------------------------------------------------------- /scripts/applications-setup/browsers/zen-browser.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installZenBrowser() { 6 | if ! brewprogram_exists zen; then 7 | printf "%b\n" "${YELLOW}Installing Zen Browser...${RC}" 8 | brew install --cask zen 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Zen Browser. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Zen Browser installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Zen Browser is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installZenBrowser -------------------------------------------------------------------------------- /scripts/applications-setup/communication-apps/discord-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installDiscord() { 6 | if ! brewprogram_exists discord; then 7 | printf "%b\n" "${YELLOW}Installing Discord...${RC}" 8 | brew install --cask discord 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Discord. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Discord installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Discord is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installDiscord -------------------------------------------------------------------------------- /scripts/applications-setup/browsers/brave.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installBrave() { 6 | if ! brewprogram_exists brave-browser; then 7 | printf "%b\n" "${YELLOW}Installing Brave...${RC}" 8 | brew install --cask brave-browser 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Brave Browser. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Brave Browser installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Brave Browser is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installBrave -------------------------------------------------------------------------------- /scripts/applications-setup/browsers/vivaldi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installVivaldi() { 6 | if ! brewprogram_exists vivaldi; then 7 | printf "%b\n" "${YELLOW}Installing Vivaldi...${RC}" 8 | brew install --cask vivaldi 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Vivaldi Browser. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Vivaldi Browser installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Vivaldi Browser is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installVivaldi -------------------------------------------------------------------------------- /scripts/applications-setup/communication-apps/whatsapp-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installWhatsApp() { 6 | if ! brewprogram_exists whatsapp; then 7 | printf "%b\n" "${YELLOW}Installing WhatsApp...${RC}" 8 | brew install --cask whatsapp 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install WhatsApp. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}WhatsApp installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}WhatsApp is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installWhatsApp -------------------------------------------------------------------------------- /scripts/applications-setup/developer-tools/sublime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installSublime() { 6 | if ! brewprogram_exists sublime-text; then 7 | printf "%b\n" "${YELLOW}Installing Sublime...${RC}" 8 | brew install --cask sublime-text 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Sublime. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Sublime installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Sublime is already installed.${RC}" 16 | fi 17 | 18 | } 19 | 20 | checkEnv 21 | installSublime -------------------------------------------------------------------------------- /scripts/applications-setup/developer-tools/vscode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installVsCode() { 6 | if ! brewprogram_exists visual-studio-code; then 7 | printf "%b\n" "${YELLOW}Installing VS Code..${RC}." 8 | brew install --cask visual-studio-code 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install VS Code. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}VS Code installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}VS Code is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installVsCode -------------------------------------------------------------------------------- /scripts/applications-setup/developer-tools/vscodium.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installVsCodium() { 6 | if ! brewprogram_exists vscodium; then 7 | printf "%b\n" "${YELLOW}Installing VS Codium...${RC}" 8 | brew install --cask vscodium 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install VS Codium. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}VS Codium installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}VS Codium is already installed.${RC}" 16 | fi 17 | 18 | } 19 | 20 | checkEnv 21 | installVsCodium -------------------------------------------------------------------------------- /scripts/applications-setup/browsers/firefox.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installFirefox() { 6 | if ! brewprogram_exists firefox; then 7 | printf "%b\n" "${YELLOW}Installing Mozilla Firefox...${RC}" 8 | brew install --cask firefox 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Firefox Browser. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Firefox Browser installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Firefox Browser is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installFirefox -------------------------------------------------------------------------------- /scripts/applications-setup/browsers/waterfox.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installWaterfox() { 6 | if ! brewprogram_exists waterfox; then 7 | printf "%b\n" "${YELLOW}Installing waterfox...${RC}" 8 | brew install --cask waterfox 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Waterfox Browser. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Waterfox Browser installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Waterfox is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installWaterfox 21 | -------------------------------------------------------------------------------- /scripts/applications-setup/browsers/librewolf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installLibreWolf() { 6 | if ! brewprogram_exists librewolf; then 7 | printf "%b\n" "${YELLOW}Installing Librewolf...${RC}" 8 | brew install --cask librewolf 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install LibreWolf Browser. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}LibreWolf Browser installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}LibreWolf Browser is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installLibreWolf -------------------------------------------------------------------------------- /scripts/applications-setup/communication-apps/telegram-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installTelegram() { 6 | if ! brewprogram_exists telegram-desktop; then 7 | printf "%b\n" "${YELLOW}Installing Telegram...${RC}" 8 | brew install --cask telegram-desktop 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Telegram. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Telegram installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Telegram is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installTelegram -------------------------------------------------------------------------------- /scripts/applications-setup/browsers/thorium.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installThorium() { 6 | if ! brewprogram_exists alex313031-thorium; then 7 | printf "%b\n" "${YELLOW}Installing Thorium Browser...${RC}" 8 | brew install --cask alex313031-thorium 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Thorium Browser. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Thorium Browser installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Thorium Browser is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installThorium -------------------------------------------------------------------------------- /scripts/applications-setup/communication-apps/thunderbird-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installThunderBird() { 6 | if ! brewprogram_exists thunderbird; then 7 | printf "%b\n" "${YELLOW}Installing Thunderbird...${RC}" 8 | brew install --cask thunderbird 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Thunderbird. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Thunderbird installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Thunderbird is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installThunderBird -------------------------------------------------------------------------------- /scripts/applications-setup/developer-tools/githubdesktop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installGithubDesktop() { 6 | if ! brewprogram_exists github; then 7 | printf "%b\n" "${YELLOW}Installing Github Desktop...${RC}" 8 | brew install --cask github 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Github Desktop. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Github Desktop installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Github Desktop is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installGithubDesktop -------------------------------------------------------------------------------- /scripts/applications-setup/browsers/google-chrome.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installChrome() { 6 | if ! brewprogram_exists google-chrome; then 7 | printf "%b\n" "${YELLOW}Installing Google Chrome...${RC}" 8 | brew install --cask google-chrome 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Google Chrome Browser. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Google Chrome Browser installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Google Chrome Browser is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installChrome -------------------------------------------------------------------------------- /scripts/applications-setup/developer-tools/jetbrains-toolbox.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installJetBrainsToolBox() { 6 | if ! brewprogram_exists jetbrains-toolbox; then 7 | printf "%b\n" "${YELLOW}Installing Jetbrains Toolbox...${RC}" 8 | brew install --cask jetbrains-toolbox 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Jetbrains Toolbox. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Successfully installed Jetbrains Toolbox.${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Jetbrains toolbox is already installed.${RC}" 16 | fi 17 | } 18 | 19 | checkEnv 20 | installJetBrainsToolBox -------------------------------------------------------------------------------- /.github/workflows/shellcheck.yml: -------------------------------------------------------------------------------- 1 | name: Script Checks 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - '**/*.sh' 7 | workflow_dispatch: 8 | 9 | permissions: 10 | contents: read 11 | pull-requests: write 12 | 13 | jobs: 14 | shellcheck: 15 | name: Shellcheck 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout sources 19 | uses: actions/checkout@v5 20 | 21 | - name: Run ShellCheck 22 | uses: reviewdog/action-shellcheck@v1 23 | with: 24 | shellcheck_flags: '--source-path=${{ github.workspace }}/.shellcheckrc' 25 | reviewdog_flags: '-fail-level=any' 26 | 27 | shfmt: 28 | name: Shell Fomatting 29 | runs-on: ubuntu-latest 30 | needs: shellcheck 31 | steps: 32 | - name: Checkout sources 33 | uses: actions/checkout@v5 34 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | categories: 3 | - title: '🚀 Features' 4 | labels: 5 | - 'enhancement' 6 | - title: '🐛 Bug Fixes' 7 | labels: 8 | - 'bug' 9 | - title: '⚙️ Refactoring' 10 | labels: 11 | - 'refactor' 12 | - title: '🧩 UI/UX' 13 | labels: 14 | - 'UI/UX' 15 | - title: '📚 Documentation' 16 | labels: 17 | - 'documentation' 18 | - title: '🔒 Security' 19 | labels: 20 | - 'security' 21 | - title: '🧰 GitHub Actions' 22 | labels: 23 | - 'github_actions' 24 | - title: '🦀 Rust' 25 | labels: 26 | - 'rust' 27 | - title: '📃 Scripting' 28 | labels: 29 | - 'script' 30 | - title: 'Other Changes' 31 | labels: 32 | - "*" 33 | exclude: 34 | labels: 35 | - 'skip-changelog' 36 | -------------------------------------------------------------------------------- /scripts/applications-setup/android-debloat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../common-script.sh 4 | 5 | install_adb() { 6 | brew install android-platform-tools 7 | } 8 | 9 | install_universal_android_debloater() { 10 | if ! command_exists uad; then 11 | printf "%b\n" "${YELLOW}Installing Universal Android Debloater...${RC}." 12 | curl -sSLo "${HOME}/uad" "https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/releases/latest/download/uad-ng-macos" 13 | "$ESCALATION_TOOL" chmod +x "${HOME}/uad" 14 | "$ESCALATION_TOOL" mv "${HOME}/uad" /usr/local/bin/uad 15 | else 16 | printf "%b\n" "${GREEN}Universal Android Debloater is already installed. Run 'uad' command to execute.${RC}" 17 | fi 18 | } 19 | 20 | checkEnv 21 | install_adb 22 | install_universal_android_debloater 23 | -------------------------------------------------------------------------------- /scripts/system-setup/tab_data.toml: -------------------------------------------------------------------------------- 1 | name = "System Setup" 2 | 3 | [[data]] 4 | name = "System Tools" 5 | 6 | [[data.entries]] 7 | name = "Development Setup" 8 | description = "This script is designed to handle the installation of various development dependencies and tools across different Linux distributions" 9 | script = "dev-setup.sh" 10 | 11 | [[data.entries]] 12 | name = "Full System Cleanup" 13 | description = "This script is designed to remove unnecessary packages, clean old cache files, remove temporary files, and to empty the trash." 14 | script = "system-cleanup.sh" 15 | 16 | [[data.entries]] 17 | name = "Fix Finder" 18 | description = "Finder optimizations for people without a mental disorder." 19 | script = "fix-finder.sh" 20 | 21 | [[data.entries]] 22 | name = "Remove Animations" 23 | description = "This script is designed to remove snap packages and the snapd service from your system" 24 | script = "remove-animations.sh" -------------------------------------------------------------------------------- /MacUtilGUI/Program.fs: -------------------------------------------------------------------------------- 1 | namespace MacUtilGUI 2 | 3 | open System 4 | open Avalonia 5 | open Avalonia.Controls.ApplicationLifetimes 6 | open Avalonia.Markup.Xaml 7 | open MacUtilGUI.Views 8 | open MacUtilGUI.ViewModels 9 | 10 | type App() = 11 | inherit Application() 12 | 13 | override this.Initialize() = AvaloniaXamlLoader.Load(this) 14 | 15 | override this.OnFrameworkInitializationCompleted() = 16 | match this.ApplicationLifetime with 17 | | :? IClassicDesktopStyleApplicationLifetime as desktop -> 18 | desktop.MainWindow <- MainWindow(DataContext = MainWindowViewModel()) 19 | | _ -> () 20 | 21 | base.OnFrameworkInitializationCompleted() 22 | 23 | module Program = 24 | 25 | [] 26 | let buildAvaloniaApp () = 27 | AppBuilder.Configure().UsePlatformDetect().LogToTrace() 28 | 29 | [] 30 | let main argv = 31 | buildAvaloniaApp().StartWithClassicDesktopLifetime(argv) 32 | -------------------------------------------------------------------------------- /scripts/system-setup/system-cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../common-script.sh 4 | 5 | cleanup_system() { 6 | printf "%b\n" "${YELLOW}Performing system cleanup...${RC}" 7 | # Fix Missions control to NEVER rearrange spaces 8 | printf "%b\n" "${CYAN}Fixing Mission Control to never rearrange spaces...${RC}" 9 | $ESCALATION_TOOL defaults write com.apple.dock mru-spaces -bool false 10 | 11 | # Apple Intelligence Crap 12 | $ESCALATION_TOOL defaults write com.apple.CloudSubscriptionFeatures.optIn "545129924" -bool "false" 13 | 14 | # Empty Trash 15 | printf "%b\n" "${CYAN}Emptying Trash...${RC}" 16 | $ESCALATION_TOOL rm -rf ~/.Trash/* 17 | 18 | # Remove old log files 19 | printf "%b\n" "${CYAN}Removing old log files...${RC}" 20 | find /var/log -type f -name "*.log" -mtime +30 -exec $ESCALATION_TOOL rm -f {} \; 21 | find /var/log -type f -name "*.old" -mtime +30 -exec $ESCALATION_TOOL rm -f {} \; 22 | find /var/log -type f -name "*.err" -mtime +30 -exec $ESCALATION_TOOL rm -f {} \; 23 | 24 | } 25 | 26 | checkEnv 27 | cleanup_system -------------------------------------------------------------------------------- /scripts/applications-setup/developer-tools/neovim.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../../common-script.sh 4 | 5 | installNeovim() { 6 | print_info "Setting up Neovim..." 7 | 8 | # Install Neovim and dependencies with brew 9 | print_info "Installing Neovim and dependencies..." 10 | brew install neovim ripgrep fzf 11 | 12 | # Backup existing config if it exists 13 | if [ -d "$HOME/.config/nvim" ] && [ ! -d "$HOME/.config/nvim-backup" ]; then 14 | print_info "Backing up existing Neovim config..." 15 | cp -r "$HOME/.config/nvim" "$HOME/.config/nvim-backup" 16 | fi 17 | 18 | # Clear existing config 19 | rm -rf "$HOME/.config/nvim" 20 | mkdir -p "$HOME/.config/nvim" 21 | 22 | # Clone Titus kickstart config directly to .config/nvim 23 | print_info "Applying Titus Kickstart config..." 24 | git clone --depth 1 https://github.com/ChrisTitusTech/neovim.git /tmp/neovim 25 | cp -r /tmp/neovim/titus-kickstart/* "$HOME/.config/nvim/" 26 | rm -rf /tmp/neovim 27 | print_success "Neovim setup completed." 28 | } 29 | 30 | checkEnv 31 | installNeovim -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Chris Titus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /scripts/applications-setup/kitty-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../common-script.sh 4 | 5 | installKitty() { 6 | if ! brewprogram_exists kitty; then 7 | brew install --cask kitty 8 | if [ $? -ne 0 ]; then 9 | printf "%b\n" "${RED}Failed to install Kitty. Please check your Homebrew installation or try again later.${RC}" 10 | exit 1 11 | fi 12 | printf "%b\n" "${GREEN}Kitty installed successfully!${RC}" 13 | else 14 | printf "%b\n" "${GREEN}Kitty is already installed.${RC}" 15 | fi 16 | } 17 | 18 | setupKittyConfig() { 19 | printf "%b\n" "${YELLOW}Copying Kitty configuration files...${RC}" 20 | if [ -d "${HOME}/.config/kitty" ] && [ ! -d "${HOME}/.config/kitty-bak" ]; then 21 | cp -r "${HOME}/.config/kitty" "${HOME}/.config/kitty-bak" 22 | fi 23 | mkdir -p "${HOME}/.config/kitty/" 24 | curl -sSLo "${HOME}/.config/kitty/kitty.conf" https://github.com/ChrisTitusTech/dwm-titus/raw/main/config/kitty/kitty.conf 25 | curl -sSLo "${HOME}/.config/kitty/nord.conf" https://github.com/ChrisTitusTech/dwm-titus/raw/main/config/kitty/nord.conf 26 | } 27 | 28 | checkEnv 29 | installKitty 30 | setupKittyConfig -------------------------------------------------------------------------------- /scripts/applications-setup/alacritty-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../common-script.sh 4 | 5 | installAlacritty() { 6 | if ! brewprogram_exists alacritty; then 7 | printf "%b\n" "${YELLOW}Installing Alacritty...${RC}" 8 | brew install --cask alacritty 9 | else 10 | printf "%b\n" "${GREEN}Alacritty is already installed.${RC}" 11 | fi 12 | } 13 | 14 | setupAlacrittyConfig() { 15 | printf "%b\n" "${YELLOW}Copying alacritty config files...${RC}" 16 | if [ -d "${HOME}/.config/alacritty" ] && [ ! -d "${HOME}/.config/alacritty-bak" ]; then 17 | cp -r "${HOME}/.config/alacritty" "${HOME}/.config/alacritty-bak" 18 | fi 19 | mkdir -p "${HOME}/.config/alacritty/" 20 | curl -sSLo "${HOME}/.config/alacritty/alacritty.toml" "https://github.com/ChrisTitusTech/dwm-titus/raw/main/config/alacritty/alacritty.toml" 21 | curl -sSLo "${HOME}/.config/alacritty/keybinds.toml" "https://github.com/ChrisTitusTech/dwm-titus/raw/main/config/alacritty/keybinds.toml" 22 | curl -sSLo "${HOME}/.config/alacritty/nordic.toml" "https://github.com/ChrisTitusTech/dwm-titus/raw/main/config/alacritty/nordic.toml" 23 | printf "%b\n" "${GREEN}Alacritty configuration files copied.${RC}" 24 | } 25 | 26 | checkEnv 27 | installAlacritty 28 | setupAlacrittyConfig -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | It is recommended that you use the stable branch as it's tested and used by most. The dev branch may contain bleeding-edge commits that are not well tested and are not meant to be used in production environments.
6 | Version tags lower than the [latest stable release](https://github.com/ChrisTitusTech/macutil/releases/latest) are **not** supported. 7 | 8 | | Branch | Supported | 9 | | ------- | ---------------------- | 10 | | Stable | :white_check_mark: YES | 11 | | Dev | :x: NO | 12 | 13 | | Version | Supported | 14 | | -------------------------------------------------- | ---------------------- | 15 | | [![LATEST](https://img.shields.io/github/v/release/ChrisTitusTech/macutil?color=%230567ff&label=Latest&style=for-the-badge)](https://github.com/ChrisTitusTech/macutil/releases/latest) | :white_check_mark: YES | 16 | | Below LATEST | :x: NO | 17 | | Above LATEST | :x: NO | 18 | 19 | ## Reporting a Vulnerability 20 | 21 | If you have any reason to believe there are security vulnerabilities in macutil, fill out the [report form](https://github.com/christitustech/macutil/security/advisories/new) or e-mail [contact@christitus.com](mailto:contact@christitus.com). 22 | -------------------------------------------------------------------------------- /.github/workflows/fsharp.yml: -------------------------------------------------------------------------------- 1 | name: F# Checks 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: ["main"] 7 | paths: 8 | - '**/*.fs' 9 | - '**/*.fsproj' 10 | - 'MacUtilGUI/**' 11 | pull_request: 12 | branches: ["main"] 13 | paths: 14 | - '**/*.fs' 15 | - '**/*.fsproj' 16 | - 'MacUtilGUI/**' 17 | 18 | jobs: 19 | lints: 20 | name: F# Build and Format Check 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - name: Checkout sources 25 | uses: actions/checkout@v5 26 | 27 | - name: Setup .NET 28 | uses: actions/setup-dotnet@v4 29 | with: 30 | dotnet-version: '9.0.x' 31 | 32 | - name: Cache .NET packages 33 | uses: actions/cache@v4 34 | with: 35 | path: ~/.nuget/packages 36 | key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} 37 | restore-keys: ${{ runner.os }}-nuget- 38 | 39 | - name: Restore dependencies 40 | run: dotnet restore MacUtilGUI/MacUtilGUI.fsproj 41 | 42 | - name: Install Fantomas 43 | run: | 44 | dotnet tool install -g fantomas 45 | echo "$HOME/.dotnet/tools" >> $GITHUB_PATH 46 | 47 | - name: Build project 48 | run: dotnet build MacUtilGUI/MacUtilGUI.fsproj --configuration Release --no-restore 49 | 50 | - name: Check F# formatting 51 | run: fantomas --check MacUtilGUI/ 52 | -------------------------------------------------------------------------------- /MacUtilGUI/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIconFile 6 | MacUtilGUI.icns 7 | CFBundleIdentifier 8 | com.macutil.gui 9 | CFBundleName 10 | MacUtil 11 | CFBundleDisplayName 12 | MacUtil GUI 13 | CFBundleVersion 14 | 0.2.1 15 | CFBundleShortVersionString 16 | 0.2.1 17 | LSMinimumSystemVersion 18 | 10.15 19 | CFBundleExecutable 20 | MacUtilGUI 21 | CFBundleInfoDictionaryVersion 22 | 6.0 23 | CFBundlePackageType 24 | APPL 25 | CFBundleSignature 26 | ???? 27 | NSHighResolutionCapable 28 | 29 | NSPrincipalClass 30 | NSApplication 31 | LSApplicationCategoryType 32 | public.app-category.utilities 33 | NSRequiresAquaSystemAppearance 34 | 35 | NSHumanReadableCopyright 36 | Copyright © 2025 MacUtil. All rights reserved. 37 | 38 | 39 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | ## Type of Change 7 | - [ ] New feature 8 | - [ ] Bug fix 9 | - [ ] Documentation update 10 | - [ ] Refactoring 11 | - [ ] Hotfix 12 | - [ ] Security patch 13 | - [ ] UI/UX improvement 14 | 15 | ## Description 16 | 17 | 18 | ## Testing 19 | 20 | 21 | ## Impact 22 | 23 | 24 | ## Issues / other PRs related 25 | 26 | - Resolves # 27 | 28 | ## Additional Information 29 | 30 | 31 | ## Checklist 32 | - [ ] My code adheres to the coding and style guidelines of the project. 33 | - [ ] I have performed a self-review of my own code. 34 | - [ ] I have commented my code, particularly in hard-to-understand areas. 35 | - [ ] I have made corresponding changes to the documentation. 36 | - [ ] My changes generate no errors/warnings/merge conflicts. 37 | -------------------------------------------------------------------------------- /.github/workflows/bashisms.yml: -------------------------------------------------------------------------------- 1 | name: Check for bashisms 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - 'core/tabs/**/*.sh' 7 | merge_group: 8 | workflow_dispatch: 9 | 10 | jobs: 11 | check-bashisms: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v5 16 | - run: git fetch origin ${{ github.base_ref }} 17 | 18 | - name: Install devscripts 19 | run: sudo apt-get update && sudo apt-get install -y devscripts 20 | 21 | - name: Get changed .sh files (PR only) 22 | id: changed-sh-files 23 | if: github.event_name == 'pull_request' 24 | uses: tj-actions/changed-files@v46 25 | with: 26 | files: '**/*.sh' 27 | 28 | - name: Get all .sh files (if workflow dispatched) 29 | id: sh-files 30 | if: github.event_name != 'pull_request' 31 | run: | 32 | files=$(find . -type f -name "*.sh" | tr '\n' ' ') 33 | echo "files=${files:-none}" >> $GITHUB_ENV 34 | 35 | - name: Set FILES for bashism check 36 | id: set-files 37 | run: | 38 | if [[ "${{ steps.changed-sh-files.outputs.any_changed }}" == 'true' ]]; then 39 | echo "FILES=${{ steps.changed-sh-files.outputs.all_changed_files }}" >> $GITHUB_ENV 40 | else 41 | echo "FILES=${{ env.files }}" >> $GITHUB_ENV 42 | fi 43 | 44 | - name: Check for bashisms 45 | run: | 46 | IFS=' ' read -r -a file_array <<< "$FILES" 47 | checkbashisms "${file_array[@]}" 48 | -------------------------------------------------------------------------------- /MacUtilGUI/Views/MainWindow.axaml.fs: -------------------------------------------------------------------------------- 1 | namespace MacUtilGUI.Views 2 | 3 | open System.Windows.Input 4 | open Avalonia 5 | open Avalonia.Controls 6 | open Avalonia.Input 7 | open Avalonia.Markup.Xaml 8 | open Avalonia.Interactivity 9 | open MacUtilGUI.ViewModels 10 | open MacUtilGUI.Models 11 | 12 | type MainWindow() as this = 13 | inherit Window() 14 | 15 | do this.InitializeComponent() 16 | 17 | member private this.InitializeComponent() = AvaloniaXamlLoader.Load(this) 18 | 19 | member private this.OnCloseButtonClick(sender: obj, e: RoutedEventArgs) = 20 | this.Close() 21 | 22 | member private this.OnMinimizeButtonClick(sender: obj, e: RoutedEventArgs) = 23 | this.WindowState <- WindowState.Minimized 24 | 25 | member private this.OnMaximizeButtonClick(sender: obj, e: RoutedEventArgs) = 26 | this.WindowState <- 27 | if this.WindowState = WindowState.FullScreen then 28 | WindowState.Normal 29 | else 30 | WindowState.FullScreen 31 | 32 | member private this.OnScriptButtonClick(sender: obj, e: RoutedEventArgs) = 33 | match sender with 34 | | :? Button as button -> 35 | match button.Tag with 36 | | :? ScriptInfo as script -> 37 | match this.DataContext with 38 | | :? MainWindowViewModel as vm -> (vm.SelectScriptCommand :> ICommand).Execute(script) 39 | | _ -> () 40 | | _ -> () 41 | | _ -> () 42 | 43 | member private this.TitleBar_PointerPressed(sender: obj, e: PointerPressedEventArgs) = 44 | this.BeginMoveDrag(e) 45 | -------------------------------------------------------------------------------- /MacUtilGUI/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | true 30 | PerMonitorV2 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chris Titus Tech's MacOS Utility 2 | 3 | [![Version](https://img.shields.io/github/v/release/ChrisTitusTech/macutil?color=%230567ff&label=Latest%20Release&style=for-the-badge)](https://github.com/ChrisTitusTech/macutil/releases/latest) 4 | ![GitHub Downloads (specific asset, all releases)](https://img.shields.io/github/downloads/ChrisTitusTech/macutil/macutil?label=Total%20Downloads&style=for-the-badge) 5 | 6 | 7 | > [!NOTE] 8 | > Since the project is still in active development, you may encounter some issues. Please consider [submitting feedback](https://github.com/ChrisTitusTech/macutil/issues) if you do. 9 | 10 | # HEAVY DEVELOPMENT 11 | 12 | - Look at creating or installing a package into /Applications or other methods like copying to ~/.local/bin - _Update: Waiting on Apple Developer License Approval_ 13 | 14 | # Installation 15 | 16 | Currently, macutil is download only and doesn't have a developer license. You can download the latest release from the [Releases page](https://github.com/ChrisTitusTech/macutil/releases/latest). 17 | 18 | ## 💖 Support 19 | 20 | If you find macutil helpful, please consider giving it a ⭐️ to show your support! 21 | 22 | ## 🛠 Contributing 23 | 24 | We welcome contributions from the community! Before you start, please review our [Contributing Guidelines](.github/CONTRIBUTING.md) to understand how to make the most effective and efficient contributions. 25 | 26 | ## 🏅 Thanks to All Contributors 27 | 28 | Thank you to everyone who has contributed to the development of macutil. Your efforts are greatly appreciated, and you're helping make this tool better for everyone! 29 | 30 | [![Contributors](https://contrib.rocks/image?repo=ChrisTitusTech/macutil)](https://github.com/ChrisTitusTech/macutil/graphs/contributors) 31 | 32 | ## 📜 Contributor Milestones 33 | 34 | - 2025/07/21: Claude Sonnet 4 makes boilerplate code for the project. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-reqests.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest a new feature or improvement to help us enhance this project. 3 | title: "[Feature Request]: " 4 | labels: ["enhancement"] 5 | assignees: [] 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thank you for suggesting a feature or enhancement! Please provide as much detail as possible to help us understand your request. Note that submitting a feature request does not guarantee implementation. 11 | - type: textarea 12 | id: related-problem 13 | attributes: 14 | label: Is your feature request related to a problem? Please describe. 15 | description: | 16 | Provide a clear and concise description of the problem, if applicable. 17 | placeholder: I'm always frustrated when ... 18 | - type: textarea 19 | id: proposed-solution 20 | attributes: 21 | label: Describe the solution you'd like 22 | description: | 23 | Provide a clear and concise description of what you want to happen. 24 | placeholder: Explain your proposed feature or enhancement in detail. 25 | validations: 26 | required: true 27 | - type: textarea 28 | id: alternatives 29 | attributes: 30 | label: Describe alternatives you've considered 31 | description: | 32 | Provide a clear and concise description of any alternative solutions or features you've considered. 33 | placeholder: Explain any other approaches or solutions you've thought about. 34 | - type: textarea 35 | id: additional-context 36 | attributes: 37 | label: Additional context 38 | description: | 39 | Add any other context or screenshots about the feature request here. 40 | placeholder: Provide additional information, screenshots, or references to explain your request further. 41 | - type: checkboxes 42 | id: checklist 43 | attributes: 44 | label: Checklist 45 | options: 46 | - label: I checked for duplicate issues. 47 | - label: I checked already existing discussions. 48 | - label: This feature is not included in the roadmap. 49 | required: true 50 | -------------------------------------------------------------------------------- /MacUtilGUI/create_icon_from_logo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script to create macOS app icon from logo.png 4 | # This script creates an iconset and converts it to .icns format 5 | 6 | echo "🎨 Creating macOS app icon from logo.png..." 7 | 8 | # Check if logo.png exists 9 | if [ ! -f "logo.png" ]; then 10 | echo "❌ Error: logo.png not found in current directory" 11 | exit 1 12 | fi 13 | 14 | # Create iconset directory 15 | echo "📁 Creating iconset directory..." 16 | rm -rf MacUtilGUI.iconset 17 | mkdir MacUtilGUI.iconset 18 | 19 | # Define icon sizes needed for macOS 20 | declare -a sizes=(16 32 128 256 512) 21 | declare -a retina_sizes=(32 64 256 512 1024) 22 | 23 | echo "🔄 Generating icon sizes..." 24 | 25 | # Generate standard resolution icons 26 | for i in "${!sizes[@]}"; do 27 | size=${sizes[$i]} 28 | echo " Creating ${size}x${size} icon..." 29 | sips -z $size $size logo.png --out MacUtilGUI.iconset/icon_${size}x${size}.png >/dev/null 2>&1 30 | done 31 | 32 | # Generate retina (@2x) resolution icons 33 | for i in "${!sizes[@]}"; do 34 | size=${sizes[$i]} 35 | retina_size=${retina_sizes[$i]} 36 | echo " Creating ${size}x${size}@2x icon (${retina_size}x${retina_size})..." 37 | sips -z $retina_size $retina_size logo.png --out MacUtilGUI.iconset/icon_${size}x${size}@2x.png >/dev/null 2>&1 38 | done 39 | 40 | # Verify iconset contents 41 | echo "📋 Generated icon files:" 42 | ls -la MacUtilGUI.iconset/ 43 | 44 | # Convert iconset to icns format 45 | echo "🔨 Converting iconset to .icns format..." 46 | if command -v iconutil >/dev/null 2>&1; then 47 | iconutil -c icns MacUtilGUI.iconset -o MacUtilGUI.icns 48 | if [ $? -eq 0 ]; then 49 | echo "✅ Successfully created MacUtilGUI.icns" 50 | 51 | # Show file info 52 | echo "📊 Icon file info:" 53 | ls -lh MacUtilGUI.icns 54 | 55 | # Cleanup iconset directory 56 | rm -rf MacUtilGUI.iconset 57 | echo "🧹 Cleaned up temporary iconset directory" 58 | 59 | echo "🎉 App icon creation complete!" 60 | echo "📝 The MacUtilGUI.icns file is ready to use for your macOS app bundle." 61 | else 62 | echo "❌ Error: Failed to convert iconset to icns format" 63 | exit 1 64 | fi 65 | else 66 | echo "❌ Error: iconutil command not found (required for icns conversion)" 67 | echo "Please run this script on macOS with Xcode command line tools installed" 68 | exit 1 69 | fi 70 | -------------------------------------------------------------------------------- /scripts/common-script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | # shellcheck disable=SC2034 4 | 5 | RC='' 6 | RED='' 7 | YELLOW='' 8 | CYAN='' 9 | GREEN='' 10 | 11 | command_exists() { 12 | for cmd in "$@"; do 13 | command -v "$cmd" >/dev/null 2>&1 || return 1 14 | done 15 | return 0 16 | } 17 | 18 | brewprogram_exists() { 19 | for cmd in "$@"; do 20 | brew list "$cmd" >/dev/null 2>&1 || return 1 21 | done 22 | return 0 23 | } 24 | 25 | setup_askpass() { 26 | # Create a temporary askpass helper script 27 | ASKPASS_SCRIPT="/tmp/macutil_askpass_$$" 28 | cat > "$ASKPASS_SCRIPT" << 'EOF' 29 | #!/bin/sh 30 | osascript -e 'display dialog "Administrator password required for MacUtil setup:" default answer "" with hidden answer' -e 'text returned of result' 2>/dev/null 31 | EOF 32 | chmod +x "$ASKPASS_SCRIPT" 33 | export SUDO_ASKPASS="$ASKPASS_SCRIPT" 34 | } 35 | 36 | cleanup_askpass() { 37 | # Clean up the temporary askpass script 38 | if [ -n "$ASKPASS_SCRIPT" ] && [ -f "$ASKPASS_SCRIPT" ]; then 39 | rm -f "$ASKPASS_SCRIPT" 40 | fi 41 | } 42 | 43 | checkPackageManager() { 44 | ## Check if brew is installed 45 | if command_exists "brew"; then 46 | printf "%b\n" "${GREEN}Homebrew is installed${RC}" 47 | else 48 | printf "%b\n" "${RED}Homebrew is not installed${RC}" 49 | printf "%b\n" "${YELLOW}Installing Homebrew...${RC}" 50 | 51 | # Setup askpass helper for automated password handling 52 | setup_askpass 53 | 54 | # Use sudo with askpass for non-interactive installation 55 | SUDO_ASKPASS="$ASKPASS_SCRIPT" sudo -A /bin/bash -c "NONINTERACTIVE=1 $(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 56 | install_result=$? 57 | 58 | # Cleanup askpass helper 59 | cleanup_askpass 60 | 61 | if [ $install_result -ne 0 ]; then 62 | printf "%b\n" "${RED}Failed to install Homebrew${RC}" 63 | exit 1 64 | fi 65 | 66 | # Add Homebrew to PATH for the current session 67 | if [ -f "/opt/homebrew/bin/brew" ]; then 68 | eval "$(/opt/homebrew/bin/brew shellenv)" 69 | elif [ -f "/usr/local/bin/brew" ]; then 70 | eval "$(/usr/local/bin/brew shellenv)" 71 | fi 72 | trap cleanup_askpass EXIT INT TERM 73 | fi 74 | } 75 | 76 | checkCurrentDirectoryWritable() { 77 | ## Check if the current directory is writable. 78 | GITPATH="$(dirname "$(realpath "$0")")" 79 | if [ ! -w "$GITPATH" ]; then 80 | printf "%b\n" "${RED}Can't write to $GITPATH${RC}" 81 | exit 1 82 | fi 83 | } 84 | 85 | checkEnv() { 86 | checkPackageManager 87 | } 88 | -------------------------------------------------------------------------------- /scripts/applications-setup/fastfetch-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../common-script.sh 4 | 5 | installFastfetch() { 6 | if ! command_exists fastfetch; then 7 | printf "%b\n" "${YELLOW}Installing Fastfetch...${RC}" 8 | brew install fastfetch 9 | if [ $? -ne 0 ]; then 10 | printf "%b\n" "${RED}Failed to install Fastfetch. Please check your Homebrew installation or try again later.${RC}" 11 | exit 1 12 | fi 13 | printf "%b\n" "${GREEN}Fastfetch installed successfully!${RC}" 14 | else 15 | printf "%b\n" "${GREEN}Fastfetch is already installed.${RC}" 16 | fi 17 | } 18 | 19 | setupFastfetchConfig() { 20 | printf "%b\n" "${YELLOW}Copying Fastfetch config files...${RC}" 21 | if [ -d "${HOME}/.config/fastfetch" ] && [ ! -d "${HOME}/.config/fastfetch-bak" ]; then 22 | cp -r "${HOME}/.config/fastfetch" "${HOME}/.config/fastfetch-bak" 23 | fi 24 | mkdir -p "${HOME}/.config/fastfetch/" 25 | curl -sSLo "${HOME}/.config/fastfetch/config.jsonc" https://raw.githubusercontent.com/ChrisTitusTech/mybash/main/config.jsonc 26 | } 27 | 28 | setupFastfetchShell() { 29 | printf "%b\n" "${YELLOW}Configuring shell integration...${RC}" 30 | 31 | current_shell=$(basename "$SHELL") 32 | rc_file="" 33 | 34 | case "$current_shell" in 35 | "bash") 36 | rc_file="$HOME/.bashrc" 37 | ;; 38 | "zsh") 39 | rc_file="$HOME/.zshrc" 40 | ;; 41 | "fish") 42 | rc_file="$HOME/.config/fish/config.fish" 43 | ;; 44 | "nu") 45 | rc_file="$HOME/.config/nushell/config.nu" 46 | ;; 47 | *) 48 | printf "%b\n" "${RED}$current_shell is not supported. Update your shell configuration manually.${RC}" 49 | ;; 50 | esac 51 | 52 | if [ ! -f "$rc_file" ]; then 53 | printf "%b\n" "${RED}Shell config file $rc_file not found${RC}" 54 | else 55 | if grep -q "fastfetch" "$rc_file"; then 56 | printf "%b\n" "${YELLOW}Fastfetch is already configured in $rc_file${RC}" 57 | return 0 58 | else 59 | printf "%b" "${GREEN}Would you like to add fastfetch to $rc_file? [y/N] ${RC}" 60 | read -r response 61 | if [ "$response" = "y" ] || [ "$response" = "Y" ]; then 62 | printf "\n# Run fastfetch on shell initialization\nfastfetch\n" >>"$rc_file" 63 | printf "%b\n" "${GREEN}Added fastfetch to $rc_file${RC}" 64 | else 65 | printf "%b\n" "${YELLOW}Skipped adding fastfetch to shell config${RC}" 66 | fi 67 | fi 68 | fi 69 | 70 | } 71 | 72 | checkEnv 73 | installFastfetch 74 | setupFastfetchConfig 75 | setupFastfetchShell 76 | -------------------------------------------------------------------------------- /scripts/applications-setup/zsh-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ../common-script.sh 4 | 5 | backupZshConfig() { 6 | printf "%b\n" "${YELLOW}Backing up existing Zsh configuration...${RC}" 7 | 8 | # Backup existing .zshrc if it exists 9 | if [ -f "$HOME/.zshrc" ] && [ ! -f "$HOME/.zshrc-backup" ]; then 10 | cp "$HOME/.zshrc" "$HOME/.zshrc-backup" 11 | printf "%b\n" "${GREEN}Existing .zshrc backed up to .zshrc-backup.${RC}" 12 | fi 13 | 14 | # Backup existing .config/zsh if it exists 15 | if [ -d "$HOME/.config/zsh" ] && [ ! -d "$HOME/.config/zsh-backup" ]; then 16 | cp -r "$HOME/.config/zsh" "$HOME/.config/zsh-backup" 17 | printf "%b\n" "${GREEN}Existing Zsh config backed up to .config/zsh-backup.${RC}" 18 | fi 19 | } 20 | 21 | installZshDepend() { 22 | # List of dependencies 23 | DEPENDENCIES="zsh-autocomplete bat tree multitail fastfetch wget unzip fontconfig starship fzf zoxide" 24 | 25 | printf "%b\n" "${CYAN}Installing dependencies...${RC}" 26 | for package in $DEPENDENCIES; do 27 | if brew list "$package" >/dev/null 2>&1; then 28 | printf "%b\n" "${GREEN}$package is already installed, skipping...${RC}" 29 | else 30 | printf "%b\n" "${CYAN}Installing $package...${RC}" 31 | if ! brew install "$package"; then 32 | printf "%b\n" "${RED}Failed to install $package. Please check your brew installation.${RC}" 33 | exit 1 34 | fi 35 | fi 36 | done 37 | 38 | # List of cask dependencies 39 | CASK_DEPENDENCIES="kitty ghostty font-fira-code-nerd-font" 40 | 41 | printf "%b\n" "${CYAN}Installing cask dependencies...${RC}" 42 | for cask in $CASK_DEPENDENCIES; do 43 | if brew list --cask "$cask" >/dev/null 2>&1; then 44 | printf "%b\n" "${GREEN}$cask is already installed, skipping...${RC}" 45 | else 46 | printf "%b\n" "${CYAN}Installing $cask...${RC}" 47 | if ! brew install --cask "$cask"; then 48 | printf "%b\n" "${RED}Failed to install $cask. Please check your brew installation.${RC}" 49 | exit 1 50 | fi 51 | fi 52 | done 53 | 54 | # Complete fzf installation 55 | if [ -e ~/.fzf/install ]; then 56 | ~/.fzf/install --all 57 | fi 58 | } 59 | 60 | 61 | # Function to setup zsh configuration 62 | setupZshConfig() { 63 | printf "%b\n" "${YELLOW}Setting up Zsh configuration...${RC}" 64 | 65 | wget https://raw.githubusercontent.com/ChrisTitusTech/maczsh/refs/heads/main/.zshrc -O "$HOME/.zshrc" 66 | 67 | # Ensure .zshrc is sourced 68 | if [ ! -f "$HOME/.zshrc" ]; then 69 | printf "%b\n" "${RED}Zsh configuration file not found!${RC}" 70 | exit 1 71 | fi 72 | 73 | printf "%b\n" "${GREEN}Zsh configuration has been set up successfully. Restart Shell.${RC}" 74 | } 75 | 76 | checkEnv 77 | backupZshConfig 78 | installZshDepend 79 | setupZshConfig -------------------------------------------------------------------------------- /MacUtilGUI/README.md: -------------------------------------------------------------------------------- 1 | # MacUtil GUI 2 | 3 | A cross-platform GUI application built with F# and Avalonia UI to run the shell scripts from the MacUtil project. 4 | 5 | ## Features 6 | 7 | - **Script Browser**: Browse and categorize scripts from the `scripts/` directory 8 | - **Script Details**: View detailed information about each script including description, category, and file path 9 | - **One-Click Execution**: Run scripts with a single button click 10 | - **Cross-Platform**: Works on macOS, Windows, and Linux 11 | - **Modern UI**: Built with Avalonia UI using the Fluent theme 12 | 13 | ## Project Structure 14 | 15 | ``` 16 | MacUtilGUI/ 17 | ├── MacUtilGUI.fsproj # Project file 18 | ├── App.axaml # Application XAML 19 | ├── Program.fs # Application entry point 20 | ├── Models/ 21 | │ ├── ScriptInfo.fs # Script information model 22 | │ └── ScriptCategory.fs # Script category model 23 | ├── Services/ 24 | │ └── ScriptService.fs # Script loading and execution service 25 | ├── ViewModels/ 26 | │ ├── ViewModelBase.fs # Base view model class 27 | │ └── MainWindowViewModel.fs # Main window view model 28 | └── Views/ 29 | ├── MainWindow.axaml # Main window XAML 30 | └── MainWindow.axaml.fs # Main window code-behind 31 | ``` 32 | 33 | ## Prerequisites 34 | 35 | - .NET 9.0 SDK or later 36 | - F# support 37 | 38 | ## Building and Running 39 | 40 | 1. Navigate to the MacUtilGUI directory: 41 | ```bash 42 | cd MacUtilGUI 43 | ``` 44 | 45 | 2. Restore dependencies: 46 | ```bash 47 | dotnet restore 48 | ``` 49 | 50 | 3. Build the project: 51 | ```bash 52 | dotnet build 53 | ``` 54 | 55 | 4. Run the application: 56 | ```bash 57 | dotnet run 58 | ``` 59 | 60 | ## How It Works 61 | 62 | 1. **Script Discovery**: The application scans the `../scripts/` directory for TOML configuration files and shell scripts 63 | 2. **TOML Parsing**: Uses the `tab_data.toml` files to organize scripts into categories with descriptions 64 | 3. **Dynamic Loading**: Scripts are organized by category and loaded dynamically from the file system 65 | 4. **Execution**: When a script is selected and "Run Script" is clicked, it executes the shell script in a new terminal process 66 | 67 | ## Configuration 68 | 69 | The application reads configuration from: 70 | - `scripts/tabs.toml` - Main configuration listing script directories 71 | - `scripts/*/tab_data.toml` - Category-specific script configurations 72 | 73 | ## Dependencies 74 | 75 | - **Avalonia**: Cross-platform .NET UI framework 76 | - **Tomlyn**: TOML parsing library for .NET 77 | 78 | ## Extending 79 | 80 | To add new script categories: 81 | 1. Create a new directory under `scripts/` 82 | 2. Add the directory name to `scripts/tabs.toml` 83 | 3. Create a `tab_data.toml` file in the new directory with script definitions 84 | 4. The GUI will automatically discover and display the new scripts 85 | 86 | ## Troubleshooting 87 | 88 | - Ensure the `scripts/` directory is one level up from the executable location 89 | - Check that shell scripts have execute permissions (`chmod +x script.sh`) 90 | - Verify TOML files are properly formatted 91 | -------------------------------------------------------------------------------- /scripts/system-setup/remove-animations.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../common-script.sh 4 | 5 | removeAnimations() { 6 | printf "%b\n" "${YELLOW}Reducing motion and animations on macOS...${RC}" 7 | 8 | # Reduce motion in Accessibility settings (most effective) 9 | printf "%b\n" "${CYAN}Setting reduce motion preference...${RC}" 10 | $ESCALATION_TOOL defaults write com.apple.universalaccess reduceMotion -bool true 11 | 12 | # Disable window animations 13 | printf "%b\n" "${CYAN}Disabling window animations...${RC}" 14 | $ESCALATION_TOOL defaults write NSGlobalDomain NSAutomaticWindowAnimationsEnabled -bool false 15 | 16 | # Speed up window resize animations 17 | printf "%b\n" "${CYAN}Speeding up window resize animations...${RC}" 18 | $ESCALATION_TOOL defaults write NSGlobalDomain NSWindowResizeTime -float 0.001 19 | 20 | # Disable smooth scrolling 21 | printf "%b\n" "${CYAN}Disabling smooth scrolling...${RC}" 22 | $ESCALATION_TOOL defaults write NSGlobalDomain NSScrollAnimationEnabled -bool false 23 | 24 | # Disable animation when opening and closing windows 25 | printf "%b\n" "${CYAN}Disabling window open/close animations...${RC}" 26 | $ESCALATION_TOOL defaults write NSGlobalDomain NSAutomaticWindowAnimationsEnabled -bool false 27 | 28 | # Disable animation when opening a Quick Look window 29 | printf "%b\n" "${CYAN}Disabling Quick Look animations...${RC}" 30 | $ESCALATION_TOOL defaults write -g QLPanelAnimationDuration -float 0 31 | 32 | # Disable animation when opening the Info window in Finder 33 | printf "%b\n" "${CYAN}Disabling Finder Info window animations...${RC}" 34 | $ESCALATION_TOOL defaults write com.apple.finder DisableAllAnimations -bool true 35 | 36 | # Speed up Mission Control animations 37 | printf "%b\n" "${CYAN}Speeding up Mission Control animations...${RC}" 38 | $ESCALATION_TOOL defaults write com.apple.dock expose-animation-duration -float 0.1 39 | $ESCALATION_TOOL defaults write com.apple.dock expose-group-apps -bool true 40 | 41 | # Speed up Launchpad animations 42 | printf "%b\n" "${CYAN}Speeding up Launchpad animations...${RC}" 43 | $ESCALATION_TOOL defaults write com.apple.dock springboard-show-duration -float 0.1 44 | $ESCALATION_TOOL defaults write com.apple.dock springboard-hide-duration -float 0.1 45 | 46 | # Disable dock hiding animation 47 | printf "%b\n" "${CYAN}Disabling dock hiding animations...${RC}" 48 | $ESCALATION_TOOL defaults write com.apple.dock autohide-time-modifier -float 0 49 | $ESCALATION_TOOL defaults write com.apple.dock autohide-delay -float 0 50 | 51 | # Disable animations in Mail.app 52 | printf "%b\n" "${CYAN}Disabling Mail animations...${RC}" 53 | $ESCALATION_TOOL defaults write com.apple.mail DisableReplyAnimations -bool true 54 | $ESCALATION_TOOL defaults write com.apple.mail DisableSendAnimations -bool true 55 | 56 | # Disable zoom animation when focusing on text input fields 57 | printf "%b\n" "${CYAN}Disabling text field zoom animations...${RC}" 58 | $ESCALATION_TOOL defaults write NSGlobalDomain NSTextShowsControlCharacters -bool true 59 | 60 | printf "%b\n" "${GREEN}Motion and animations have been reduced.${RC}" 61 | $ESCALATION_TOOL killall Dock 62 | printf "%b\n" "${YELLOW}Dock Restarted.${RC}" 63 | } 64 | 65 | checkEnv 66 | removeAnimations -------------------------------------------------------------------------------- /MacUtilGUI/MacUtilGUI.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WinExe 5 | net9.0 6 | disable 7 | true 8 | app.manifest 9 | true 10 | 11 | 12 | true 13 | true 14 | true 15 | link 16 | true 17 | true 18 | true 19 | true 20 | embedded 21 | 22 | 23 | true 24 | osx-x64;osx-arm64 25 | 26 | 27 | MacUtil 28 | MacUtil GUI 29 | com.macutil.gui 30 | 0.2.1 31 | 0.2.1 32 | APPL 33 | ???? 34 | MacUtilGUI 35 | MacUtilGUI.icns 36 | NSApplication 37 | true 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /.github/workflows/Release.yml: -------------------------------------------------------------------------------- 1 | name: macutil Release 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | permissions: 7 | contents: write 8 | packages: write 9 | 10 | jobs: 11 | macutil_build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v5 16 | 17 | - name: Setup .NET 18 | uses: actions/setup-dotnet@v4 19 | with: 20 | dotnet-version: '9.0.x' 21 | 22 | - name: Cache .NET packages 23 | uses: actions/cache@v4 24 | with: 25 | path: ~/.nuget/packages 26 | key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} 27 | restore-keys: ${{ runner.os }}-nuget- 28 | 29 | - name: Restore dependencies 30 | run: dotnet restore MacUtilGUI/MacUtilGUI.fsproj 31 | 32 | - name: Build and publish macOS x64 33 | run: | 34 | dotnet publish MacUtilGUI/MacUtilGUI.fsproj \ 35 | --configuration Release \ 36 | --runtime osx-x64 \ 37 | --self-contained true \ 38 | --output ./build/osx-x64 \ 39 | /p:PublishSingleFile=true \ 40 | /p:IncludeNativeLibrariesForSelfExtract=true 41 | 42 | - name: Build and publish macOS ARM64 43 | run: | 44 | dotnet publish MacUtilGUI/MacUtilGUI.fsproj \ 45 | --configuration Release \ 46 | --runtime osx-arm64 \ 47 | --self-contained true \ 48 | --output ./build/osx-arm64 \ 49 | /p:PublishSingleFile=true \ 50 | /p:IncludeNativeLibrariesForSelfExtract=true 51 | 52 | - name: Rename binaries 53 | run: | 54 | mv ./build/osx-x64/MacUtilGUI ./build/osx-x64/macutil-macos-x64 55 | mv ./build/osx-arm64/MacUtilGUI ./build/osx-arm64/macutil-macos-arm64 56 | 57 | - name: Extract Version 58 | id: extract_version 59 | run: | 60 | # Fetch all tags 61 | git fetch --tags 62 | 63 | # Get the latest tag, default to 0.0.0 if no tags exist 64 | latest_tag=$(git tag --sort=-version:refname | head -n1) 65 | if [ -z "$latest_tag" ]; then 66 | latest_tag="0.0.0" 67 | fi 68 | 69 | # Parse the latest tag (assuming format X.Y.Z) 70 | IFS='.' read -ra VERSION_PARTS <<< "$latest_tag" 71 | major=${VERSION_PARTS[0]:-0} 72 | minor=${VERSION_PARTS[1]:-0} 73 | patch=${VERSION_PARTS[2]:-0} 74 | 75 | # Increment minor version by 0.1 (increment minor, reset patch to 0) 76 | new_minor=$((minor + 1)) 77 | version="${major}.${new_minor}.0" 78 | 79 | echo "Previous version: $latest_tag" 80 | echo "New version: $version" 81 | echo "version=$version" >> $GITHUB_ENV 82 | shell: bash 83 | 84 | - name: Create and Upload Release 85 | id: create_release 86 | uses: softprops/action-gh-release@v2 87 | with: 88 | tag_name: ${{ env.version }} 89 | name: Pre-Release ${{ env.version }} 90 | body: | 91 | ![GitHub Downloads (specific asset, specific tag)](https://img.shields.io/github/downloads/ChrisTitusTech/macutil/${{ env.version }}/macutil-macos-x64) 92 | ![GitHub Downloads (specific asset, specific tag)](https://img.shields.io/github/downloads/ChrisTitusTech/macutil/${{ env.version }}/macutil-macos-arm64) 93 | 94 | append_body: true 95 | generate_release_notes: true 96 | files: | 97 | ./build/osx-x64/macutil-macos-x64 98 | ./build/osx-arm64/macutil-macos-arm64 99 | prerelease: true 100 | env: 101 | version: ${{ env.version }} 102 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 103 | -------------------------------------------------------------------------------- /scripts/system-setup/fix-finder.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | . ../common-script.sh 4 | 5 | fixfinder () { 6 | printf "%b\n" "${YELLOW}Applying global theme settings for Finder...${RC}" 7 | 8 | # Set the default Finder view to list view 9 | printf "%b\n" "${CYAN}Setting default Finder view to list view...${RC}" 10 | $ESCALATION_TOOL defaults write com.apple.finder FXPreferredViewStyle -string "Nlsv" 11 | 12 | # Configure list view settings for all folders 13 | printf "%b\n" "${CYAN}Configuring list view settings for all folders...${RC}" 14 | # Set default list view settings for new folders 15 | $ESCALATION_TOOL defaults write com.apple.finder FK_StandardViewSettings -dict-add ListViewSettings '{ "columns" = ( { "ascending" = 1; "identifier" = "name"; "visible" = 1; "width" = 300; }, { "ascending" = 0; "identifier" = "dateModified"; "visible" = 1; "width" = 181; }, { "ascending" = 0; "identifier" = "size"; "visible" = 1; "width" = 97; } ); "iconSize" = 16; "showIconPreview" = 0; "sortColumn" = "name"; "textSize" = 12; "useRelativeDates" = 1; }' 16 | 17 | # Clear existing folder view settings to force use of default settings 18 | printf "%b\n" "${CYAN}Clearing existing folder view settings...${RC}" 19 | $ESCALATION_TOOL defaults delete com.apple.finder FXInfoPanesExpanded 2>/dev/null || true 20 | $ESCALATION_TOOL defaults delete com.apple.finder FXDesktopVolumePositions 2>/dev/null || true 21 | 22 | # Set list view for all view types 23 | printf "%b\n" "${CYAN}Setting list view for all folder types...${RC}" 24 | $ESCALATION_TOOL defaults write com.apple.finder FK_StandardViewSettings -dict-add ExtendedListViewSettings '{ "columns" = ( { "ascending" = 1; "identifier" = "name"; "visible" = 1; "width" = 300; }, { "ascending" = 0; "identifier" = "dateModified"; "visible" = 1; "width" = 181; }, { "ascending" = 0; "identifier" = "size"; "visible" = 1; "width" = 97; } ); "iconSize" = 16; "showIconPreview" = 0; "sortColumn" = "name"; "textSize" = 12; "useRelativeDates" = 1; }' 25 | 26 | # Sets default search scope to the current folder 27 | printf "%b\n" "${CYAN}Setting default search scope to the current folder...${RC}" 28 | $ESCALATION_TOOL defaults write com.apple.finder FXDefaultSearchScope -string "SCcf" 29 | 30 | # Remove trash items older than 30 days 31 | printf "%b\n" "${CYAN}Removing trash items older than 30 days...${RC}" 32 | $ESCALATION_TOOL defaults write com.apple.finder "FXRemoveOldTrashItems" -bool "true" 33 | 34 | # Remove .DS_Store files to reset folder view settings 35 | printf "%b\n" "${CYAN}Removing .DS_Store files to reset folder view settings...${RC}" 36 | find ~ -name ".DS_Store" -type f -delete 2>/dev/null || true 37 | 38 | # Show all filename extensions 39 | printf "%b\n" "${CYAN}Showing all filename extensions in Finder...${RC}" 40 | $ESCALATION_TOOL defaults write NSGlobalDomain AppleShowAllExtensions -bool true 41 | 42 | # Set the sidebar icon size to small 43 | printf "%b\n" "${CYAN}Setting sidebar icon size to small...${RC}" 44 | $ESCALATION_TOOL defaults write NSGlobalDomain NSTableViewDefaultSizeMode -int 1 45 | 46 | # Show status bar in Finder 47 | printf "%b\n" "${CYAN}Showing status bar in Finder...${RC}" 48 | $ESCALATION_TOOL defaults write com.apple.finder ShowStatusBar -bool true 49 | 50 | # Show path bar in Finder 51 | printf "%b\n" "${CYAN}Showing path bar in Finder...${RC}" 52 | $ESCALATION_TOOL defaults write com.apple.finder ShowPathbar -bool true 53 | 54 | # Clean up Finder's sidebar 55 | printf "%b\n" "${CYAN}Cleaning up Finder's sidebar...${RC}" 56 | $ESCALATION_TOOL defaults write com.apple.finder SidebarDevicesSectionDisclosedState -bool true 57 | $ESCALATION_TOOL defaults write com.apple.finder SidebarPlacesSectionDisclosedState -bool true 58 | $ESCALATION_TOOL defaults write com.apple.finder SidebarShowingiCloudDesktop -bool false 59 | 60 | # Restart Finder to apply changes 61 | printf "%b\n" "${GREEN}Finder has been restarted and settings have been applied.${RC}" 62 | $ESCALATION_TOOL killall Finder 63 | } 64 | 65 | checkEnv 66 | fixfinder -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines for macutil 2 | 3 | Thank you for considering contributing to macutil! We appreciate your effort in helping improve this project. To ensure that your contributions align with the goals and quality standards of macutil, please follow these guidelines: 4 | 5 | ## 1. **Install .NET**: 6 | 7 | Make sure you have .NET installed on your machine. You can install it by following the instructions at [dotnet.microsoft.com](https://dotnet.microsoft.com/en-us/download). 8 | 9 | ## 2. **Fork and Clone the repo** 10 | 11 | 1. Make a fork of the repo in GitHub 12 | 2. Clone the fork 13 | ```bash 14 | git clone https://github.com/YOUR_USERNAME_HERE/macutil.git 15 | cd macutil 16 | ``` 17 | 18 | ## 3. Make your changes 19 | 20 | - **Edit the files you want to change**: Make your changes to the relevant files. 21 | - **Test your changes**: Run `dotnet run` to test your modifications in a local environment and ensure everything works as expected. 22 | 23 | ## 4. Understand the existing code 24 | 25 | - **Have a clear reason**: Don’t change the way things are done without a valid reason. If you propose an alteration, be prepared to explain why it’s necessary and how it improves the project. 26 | - **Respect existing conventions**: Changes should align with the existing code style, design patterns, and overall project philosophy. If you want to introduce a new way of doing things, justify it clearly. 27 | 28 | ## 5. Learn from Past Pull Requests (PRs) 29 | 30 | - **Check merged PRs**: Reviewing merged pull requests can give you an idea of what kind of contributions are accepted and how they are implemented. 31 | - **Study rejected PRs**: This is especially important as it helps you avoid making similar mistakes or proposing changes that have already been considered and declined. 32 | 33 | ## 6. Write Clean, Descriptive Commit Messages 34 | 35 | - **Be descriptive**: Your commit messages should clearly describe what the change does and why it was made. 36 | - **Use the imperative mood**: For example, "Add feature X" or "Fix bug in Y", rather than "Added feature X" or "Fixed bug in Y". 37 | - **Keep commits clean**: Avoid committing a change and then immediately following it with a fix for that change. Instead, amend your commit or squash it if needed. 38 | 39 | ## 7. Keep Your Pull Requests (PRs) Small and Focused 40 | 41 | - **Make small, targeted PRs**: Focus on one feature or fix per pull request. This makes it easier to review and increases the likelihood of acceptance. 42 | - **Avoid combining unrelated changes**: PRs that tackle multiple unrelated issues are harder to review and might be rejected because of a single problem. 43 | 44 | ## 8. Understand and Test the Code You Write 45 | 46 | - **Review your code**: Before submitting your changes, take the time to review your code for readability, efficiency and performance. Consider how your changes affect the project. 47 | - **Avoid using LLMs**: Don't submit AI-generated code without reviewing and testing it first. Ensure that any code you submit is thoroughly understood and meets the project's standards. 48 | - **Testing Requirements**: Failure to conduct testing after multiple requests may result in the closure of your Pull Request. 49 | 50 | ## 9. Code Review and Feedback 51 | 52 | - **Expect feedback**: PRs will undergo code review. Be open to feedback and willing to make adjustments as needed. 53 | - **Participate in reviews**: If you feel comfortable, review other contributors' PRs as well. Peer review is a great way to learn and ensure high-quality contributions. 54 | 55 | ## 10. Contributing Is More Than Just Code 56 | 57 | - **Test the tool**: Running tests and providing feedback on how the tool works in different environments is a valuable contribution. 58 | - **Write well-formed issues**: Clearly describe bugs or problems you encounter, providing as much detail as possible, including steps to reproduce the issue. 59 | - **Propose reasonable feature requests**: When suggesting new features, ensure they fit within the scope, style, and design of the project. Provide clear reasoning and use cases. 60 | 61 | ## 11. Documentation 62 | 63 | - **Update the documentation**: If your change affects the functionality, please update the relevant documentation files to reflect this. 64 | 65 | ## 12. License 66 | 67 | - **Agree to the license**: By contributing to macutil, you agree that your contributions will be licensed under the project's MIT license. 68 | 69 | We appreciate your contributions and look forward to collaborating with you to make macutil better! 70 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-reports.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report a bug or issue to help us improve. 3 | title: "[Bug]: " 4 | labels: ["bug"] 5 | assignees: [] 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thank you for helping us improve! Please provide as much detail as possible to ensure we can address the issue effectively. 11 | - type: dropdown 12 | id: distribution 13 | attributes: 14 | label: Distribution 15 | multiple: false 16 | description: Select the Linux distribution you are using. 17 | options: 18 | - Arch 19 | - Ubuntu 20 | - Fedora 21 | - Debian 22 | - openSUSE 23 | - Other 24 | validations: 25 | required: true 26 | - type: input 27 | id: specify-distribution 28 | attributes: 29 | label: Specify Distribution (if "Other" selected) 30 | description: Enter the name of your Linux distribution. 31 | placeholder: e.g., Manjaro, Pop!_OS 32 | - type: dropdown 33 | id: de-wm 34 | attributes: 35 | label: Desktop Environment / Window Manager 36 | multiple: false 37 | description: Select your desktop environment or window manager. 38 | options: 39 | - GNOME 40 | - KDE Plasma 41 | - XFCE 42 | - Hyprland 43 | - i3 44 | - Other 45 | validations: 46 | required: true 47 | - type: input 48 | id: specify-de-wm 49 | attributes: 50 | label: Specify Desktop Environment/Window Manager (if "Other" selected) 51 | description: Enter the name of your desktop environment or window manager. 52 | placeholder: e.g., LXQt, Openbox 53 | - type: dropdown 54 | id: windowing-system 55 | attributes: 56 | label: Windowing System 57 | multiple: false 58 | description: Specify whether you are using X11 or Wayland. 59 | options: 60 | - X11 61 | - Wayland 62 | validations: 63 | required: true 64 | - type: input 65 | id: macutil-version 66 | attributes: 67 | label: macutil Version 68 | description: macutil version (found above the list within macutil). 69 | validations: 70 | required: true 71 | - type: dropdown 72 | id: branch 73 | attributes: 74 | label: Branch 75 | multiple: false 76 | description: Specify the branch of the project you are using. 77 | options: 78 | - main 79 | - prerelease 80 | - stable 81 | - other 82 | validations: 83 | required: true 84 | - type: input 85 | id: specify-branch 86 | attributes: 87 | label: Specify Branch (if "Other" selected) 88 | description: Enter the branch name. 89 | placeholder: e.g., feature/new-feature 90 | - type: textarea 91 | id: describe-bug 92 | attributes: 93 | label: Describe the bug 94 | description: | 95 | Provide a clear and concise description of what the bug is. 96 | placeholder: Describe the issue in detail. 97 | validations: 98 | required: true 99 | - type: textarea 100 | id: reproduce-steps 101 | attributes: 102 | label: Steps to reproduce 103 | description: Steps to reproduce the behavior. 104 | placeholder: | 105 | 1. Go to '...' 106 | 2. Click on '...' 107 | 3. Scroll down to '...' 108 | 4. See error 109 | validations: 110 | required: true 111 | - type: textarea 112 | id: expected-behavior 113 | attributes: 114 | label: Expected behavior 115 | description: | 116 | A clear and concise description of what you expected to happen. 117 | placeholder: Explain the expected outcome. 118 | validations: 119 | required: true 120 | - type: textarea 121 | id: additional-context 122 | attributes: 123 | label: Additional context 124 | description: | 125 | Add any other context or information about the problem here. 126 | placeholder: Include any related logs, error messages, or configurations. 127 | - type: textarea 128 | id: screenshots 129 | attributes: 130 | label: Screenshots 131 | description: | 132 | If applicable, add screenshots to help explain your problem. Provide links or attach images in the comments after submitting the issue. 133 | - type: checkboxes 134 | id: checklist 135 | attributes: 136 | label: Checklist 137 | options: 138 | - label: I checked for duplicate issues. 139 | - label: I checked existing discussions. 140 | - label: This issue is not included in the roadmap. 141 | - label: This issue is present on both stable and development branches. 142 | required: true 143 | 144 | -------------------------------------------------------------------------------- /MacUtilGUI/build_universal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Quick Universal App Builder for MacUtil GUI 4 | # This script creates only the universal app bundle without individual architecture builds 5 | 6 | echo "🚀 Building Universal MacUtil GUI App..." 7 | echo 8 | 9 | # Configuration 10 | APP_NAME="MacUtilGUI" 11 | BUNDLE_NAME="MacUtil" 12 | BUNDLE_IDENTIFIER="com.macutil.gui" 13 | VERSION="0.2.0" 14 | COPYRIGHT="Copyright © 2025 CT Tech Group LLC. All rights reserved." 15 | 16 | # Colors for output 17 | RED='\033[0;31m' 18 | GREEN='\033[0;32m' 19 | BLUE='\033[0;34m' 20 | YELLOW='\033[1;33m' 21 | NC='\033[0m' # No Color 22 | 23 | print_status() { 24 | echo -e "${BLUE}ℹ️ $1${NC}" 25 | } 26 | 27 | print_success() { 28 | echo -e "${GREEN}✅ $1${NC}" 29 | } 30 | 31 | print_error() { 32 | echo -e "${RED}❌ $1${NC}" 33 | } 34 | 35 | # Clean previous builds 36 | print_status "Cleaning previous builds..." 37 | dotnet clean -c Release > /dev/null 2>&1 38 | rm -rf ./bin/Release/net9.0/publish/ 39 | rm -rf ./dist/ 40 | mkdir -p ./dist/ 41 | 42 | # Restore packages 43 | print_status "Restoring NuGet packages..." 44 | dotnet restore > /dev/null 45 | 46 | # Build for Intel x64 47 | print_status "Building Intel x64 binary..." 48 | dotnet publish -c Release -r osx-x64 -p:UseAppHost=true --self-contained true -o ./bin/Release/net9.0/publish/osx-x64/ > /dev/null 2>&1 49 | if [ $? -ne 0 ]; then 50 | print_error "Intel x64 build failed" 51 | exit 1 52 | fi 53 | 54 | # Build for Apple Silicon ARM64 55 | print_status "Building Apple Silicon ARM64 binary..." 56 | dotnet publish -c Release -r osx-arm64 -p:UseAppHost=true --self-contained true -o ./bin/Release/net9.0/publish/osx-arm64/ > /dev/null 2>&1 57 | if [ $? -ne 0 ]; then 58 | print_error "Apple Silicon ARM64 build failed" 59 | exit 1 60 | fi 61 | 62 | # Create Universal App Bundle 63 | print_status "Creating Universal App Bundle..." 64 | universal_app="./dist/${BUNDLE_NAME}-Universal.app" 65 | mkdir -p "$universal_app/Contents/MacOS" 66 | mkdir -p "$universal_app/Contents/Resources" 67 | 68 | # Create universal binary using lipo 69 | print_status "Merging binaries with lipo..." 70 | lipo -create \ 71 | "./bin/Release/net9.0/publish/osx-x64/$APP_NAME" \ 72 | "./bin/Release/net9.0/publish/osx-arm64/$APP_NAME" \ 73 | -output "$universal_app/Contents/MacOS/$APP_NAME" 2>/dev/null 74 | 75 | if [ $? -ne 0 ]; then 76 | print_error "Failed to create universal binary with lipo" 77 | exit 1 78 | fi 79 | 80 | # Copy dependencies 81 | rsync -a --exclude="$APP_NAME" "./bin/Release/net9.0/publish/osx-x64/" "$universal_app/Contents/MacOS/" 2>/dev/null 82 | 83 | # Create Info.plist 84 | cat > "$universal_app/Contents/Info.plist" << EOF 85 | 86 | 87 | 88 | 89 | CFBundleIconFile 90 | MacUtilGUI.icns 91 | CFBundleIdentifier 92 | $BUNDLE_IDENTIFIER 93 | CFBundleName 94 | $BUNDLE_NAME 95 | CFBundleDisplayName 96 | MacUtil GUI 97 | CFBundleVersion 98 | $VERSION 99 | CFBundleShortVersionString 100 | $VERSION 101 | LSMinimumSystemVersion 102 | 11.0 103 | CFBundleExecutable 104 | $APP_NAME 105 | CFBundleInfoDictionaryVersion 106 | 6.0 107 | CFBundlePackageType 108 | APPL 109 | CFBundleSignature 110 | MCUT 111 | NSHighResolutionCapable 112 | 113 | NSPrincipalClass 114 | NSApplication 115 | LSApplicationCategoryType 116 | public.app-category.utilities 117 | NSRequiresAquaSystemAppearance 118 | 119 | NSHumanReadableCopyright 120 | $COPYRIGHT 121 | 122 | 123 | EOF 124 | 125 | # Copy icon 126 | if [ -f "MacUtilGUI.icns" ]; then 127 | cp "MacUtilGUI.icns" "$universal_app/Contents/Resources/" 128 | fi 129 | 130 | # Make executable 131 | chmod +x "$universal_app/Contents/MacOS/$APP_NAME" 132 | 133 | # Show results 134 | universal_size=$(du -sh "$universal_app" | cut -f1) 135 | print_success "Universal app created: $universal_app" 136 | print_status "Bundle size: $universal_size" 137 | 138 | # Verify architecture 139 | print_status "Verifying universal binary:" 140 | lipo -info "$universal_app/Contents/MacOS/$APP_NAME" 141 | 142 | echo 143 | print_success "🎉 Universal build complete!" 144 | print_status "📱 Your app will run on both Intel and Apple Silicon Macs" 145 | -------------------------------------------------------------------------------- /MacUtilGUI/TTY_FIXES.md: -------------------------------------------------------------------------------- 1 | # TTY/Non-Interactive Execution Fixes for macOS App Bundle 2 | 3 | ## Problem 4 | When running the MacUtil GUI as a `.app` bundle in macOS, scripts were failing because: 5 | 6 | 1. **No TTY Available**: App bundles don't run in a terminal, so `stdin` is not a TTY 7 | 2. **Interactive Prompts**: Scripts expecting user input would hang or fail 8 | 3. **Homebrew Installation Issues**: Homebrew installer would try to reinstall even when already present 9 | 4. **TTY Detection Failures**: Scripts checking `tty -s` or `[ -t 0 ]` would fail 10 | 11 | ## Solution Implemented 12 | 13 | ### 1. Environment Variables 14 | Added comprehensive environment variable setup for all script execution paths: 15 | 16 | ```bash 17 | export TERM=xterm-256color 18 | export DEBIAN_FRONTEND=noninteractive 19 | export CI=true 20 | export HOMEBREW_NO_ENV_HINTS=1 21 | export HOMEBREW_NO_INSTALL_CLEANUP=1 22 | export HOMEBREW_NO_AUTO_UPDATE=1 23 | export NONINTERACTIVE=1 24 | export FORCE_NONINTERACTIVE=1 25 | ``` 26 | 27 | ### 2. Script Preprocessing Function 28 | Created `preprocessScriptForNonTTY()` that: 29 | 30 | - **Disables TTY checks**: Comments out lines containing `tty -s`, `[ -t 0 ]`, etc. 31 | - **Forces non-interactive mode**: Adds `--force` flag to brew commands 32 | - **Adds safety headers**: Includes environment setup and TTY function override 33 | - **Handles package managers**: Adds `-y` flags to apt commands where needed 34 | 35 | ### 3. TTY Function Override 36 | Added a fake `tty()` function that always returns "not a TTY": 37 | 38 | ```bash 39 | function tty() { 40 | return 1 # Always return "not a TTY" 41 | } 42 | ``` 43 | 44 | ### 4. ProcessStartInfo Environment Setup 45 | Updated all three script execution paths in `ScriptService.fs`: 46 | - Regular script execution 47 | - Elevated (sudo) script execution via osascript 48 | - Async script execution 49 | 50 | ## Files Modified 51 | 52 | ### `/Services/ScriptService.fs` 53 | - Added `preprocessScriptForNonTTY()` function 54 | - Updated all `ProcessStartInfo` instances with environment variables 55 | - Updated osascript command to include environment variable exports 56 | - Applied preprocessing to all script execution paths 57 | 58 | ## Testing the Fixes 59 | 60 | ### Before the Fix 61 | ``` 62 | ERROR: stdin is not a TTY 63 | ERROR: Checking for sudo access... 64 | ERROR: Homebrew installation attempted when already installed 65 | ``` 66 | 67 | ### After the Fix 68 | Scripts should now run smoothly in the `.app` bundle environment without TTY-related errors. 69 | 70 | ## What Each Environment Variable Does 71 | 72 | | Variable | Purpose | 73 | |----------|---------| 74 | | `TERM=xterm-256color` | Provides a valid terminal type | 75 | | `DEBIAN_FRONTEND=noninteractive` | Prevents apt from prompting for input | 76 | | `CI=true` | Signals automated environment to many tools | 77 | | `HOMEBREW_NO_ENV_HINTS=1` | Disables Homebrew environment hints | 78 | | `HOMEBREW_NO_INSTALL_CLEANUP=1` | Skips automatic cleanup | 79 | | `HOMEBREW_NO_AUTO_UPDATE=1` | Prevents automatic updates | 80 | | `NONINTERACTIVE=1` | Custom flag for scripts to detect non-interactive mode | 81 | | `FORCE_NONINTERACTIVE=1` | Additional flag for scripts | 82 | 83 | ## Script Preprocessing Examples 84 | 85 | ### TTY Check Disabling 86 | **Before:** 87 | ```bash 88 | if tty -s; then 89 | echo "Running in terminal" 90 | fi 91 | ``` 92 | 93 | **After:** 94 | ```bash 95 | # TTY check disabled for .app bundle execution: if tty -s; then 96 | # echo "Running in terminal" 97 | # fi 98 | ``` 99 | 100 | ### Brew Command Enhancement 101 | **Before:** 102 | ```bash 103 | brew install package 104 | ``` 105 | 106 | **After:** 107 | ```bash 108 | brew install --quiet package 109 | ``` 110 | 111 | **For update/upgrade commands:** 112 | ```bash 113 | brew update --quiet 114 | brew upgrade --quiet 115 | ``` 116 | 117 | ## Verification Steps 118 | 119 | 1. **Build new app bundle**: `./deploy_macos.sh` 120 | 2. **Test in .app environment**: `open dist/MacUtil-Intel.app` 121 | 3. **Run scripts that previously failed**: Check for TTY-related errors 122 | 4. **Monitor script output**: Should see environment variables being set 123 | 124 | ## Future Considerations 125 | 126 | - **Add more package manager support**: Handle `dnf`, `yum`, `pacman` etc. 127 | - **Enhanced script detection**: Better detection of interactive prompts 128 | - **Logging improvements**: Add debug output for preprocessing steps 129 | - **Custom script flags**: Allow scripts to opt-out of preprocessing if needed 130 | 131 | ## Benefits 132 | 133 | ✅ **No more TTY errors** when running as .app bundle 134 | ✅ **Homebrew installs work correctly** without re-installation attempts 135 | ✅ **Scripts run non-interactively** without hanging on prompts 136 | ✅ **Backward compatibility** - still works in terminal environments 137 | ✅ **Comprehensive coverage** - all execution paths updated 138 | -------------------------------------------------------------------------------- /.github/workflows/issue-slash-commands.yaml: -------------------------------------------------------------------------------- 1 | name: Issue slash commands 2 | 3 | on: 4 | issue_comment: 5 | types: [created, edited] 6 | 7 | jobs: 8 | issueCommands: 9 | # Skip this job if the comment was created/edited on a PR 10 | if: ${{ !github.event.issue.pull_request }} 11 | runs-on: ubuntu-latest 12 | permissions: 13 | issues: write 14 | pull-requests: none 15 | contents: read 16 | 17 | steps: 18 | - run: echo "command=false" >> $GITHUB_ENV 19 | 20 | - name: Check for /label command 21 | id: check_label_command 22 | run: | 23 | if [[ "${{ contains(github.event.comment.body, '/label') }}" == "true" ]]; then 24 | echo "command=true" >> $GITHUB_ENV 25 | LABEL_NAME=$(echo "${{ github.event.comment.body }}" | awk -F"/label" '/\/label/ { match($2, /'\''([^'\'']*)'\''/, arr); if (arr[1] != "") print arr[1] }') 26 | echo "label_command=true" >> $GITHUB_ENV 27 | echo "label_name=${LABEL_NAME}" >> $GITHUB_ENV 28 | else 29 | echo "label_command=false" >> $GITHUB_ENV 30 | fi 31 | 32 | - name: Check for /unlabel command 33 | id: check_unlabel_command 34 | run: | 35 | if [[ "${{ contains(github.event.comment.body, '/unlabel') }}" == "true" ]]; then 36 | echo "command=true" >> $GITHUB_ENV 37 | UNLABEL_NAME=$(echo "${{ github.event.comment.body }}" | awk -F"/unlabel" '/\/unlabel/ { match($2, /'\''([^'\'']*)'\''/, arr); if (arr[1] != "") print arr[1] }') 38 | echo "unlabel_command=true" >> $GITHUB_ENV 39 | echo "unlabel_name=${UNLABEL_NAME}" >> $GITHUB_ENV 40 | else 41 | echo "unlabel_command=false" >> $GITHUB_ENV 42 | fi 43 | 44 | - name: Check for /close command 45 | id: check_close_command 46 | run: | 47 | if [[ "${{ contains(github.event.comment.body, '/close') }}" == "true" ]]; then 48 | echo "command=true" >> $GITHUB_ENV 49 | echo "close_command=true" >> $GITHUB_ENV 50 | echo "reopen_command=false" >> $GITHUB_ENV 51 | else 52 | echo "close_command=false" >> $GITHUB_ENV 53 | fi 54 | 55 | - name: Check for /open or /reopen command 56 | id: check_reopen_command 57 | run: | 58 | if [[ "${{ contains(github.event.comment.body, '/open') }}" == "true" ]] || [[ "${{ contains(github.event.comment.body, '/reopen') }}" == "true" ]]; then 59 | echo "command=true" >> $GITHUB_ENV 60 | echo "reopen_command=true" >> $GITHUB_ENV 61 | echo "close_command=false" >> $GITHUB_ENV 62 | else 63 | echo "reopen_command=false" >> $GITHUB_ENV 64 | fi 65 | 66 | - name: Check if the user is allowed 67 | id: check_user 68 | if: env.command == 'true' 69 | run: | 70 | ALLOWED_USERS=("ChrisTitusTech" "afonsofrancof" "Marterich" "MyDrift-user" "Real-MullaC" "nnyyxxxx" "adamperkowski" "lj3954" "jeevithakannan2" "${{ github.event.issue.user.login }}") 71 | if [[ " ${ALLOWED_USERS[@]} " =~ " ${{ github.event.comment.user.login }} " ]]; then 72 | echo "user=true" >> $GITHUB_ENV 73 | else 74 | exit 0 75 | fi 76 | 77 | - name: Close issue 78 | if: env.close_command == 'true' 79 | env: 80 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 81 | ISSUE_NUMBER: ${{ github.event.issue.number }} 82 | run: | 83 | echo Closing the issue... 84 | if [[ "${{ contains(github.event.comment.body, 'not planned') }}" == "true" ]]; then 85 | gh issue close $ISSUE_NUMBER --repo ${{ github.repository }} --reason 'not planned' 86 | else 87 | gh issue close $ISSUE_NUMBER --repo ${{ github.repository }} 88 | fi 89 | 90 | - name: Reopen issue 91 | if: env.reopen_command == 'true' 92 | env: 93 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 94 | ISSUE_NUMBER: ${{ github.event.issue.number }} 95 | run: | 96 | echo Reopening the issue... 97 | gh issue reopen $ISSUE_NUMBER --repo ${{ github.repository }} 98 | 99 | - name: Label issue 100 | if: env.label_command == 'true' 101 | env: 102 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 103 | ISSUE_NUMBER: ${{ github.event.issue.number }} 104 | run: | 105 | echo Labeling the issue... 106 | gh issue edit $ISSUE_NUMBER --repo ${{ github.repository }} --add-label "${{ env.label_name }}" 107 | 108 | - name: Remove labels 109 | if: env.unlabel_command == 'true' 110 | env: 111 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 112 | ISSUE_NUMBER: ${{ github.event.issue.number }} 113 | run: | 114 | echo Unlabeling the issue... 115 | gh issue edit $ISSUE_NUMBER --repo ${{ github.repository }} --remove-label "${{ env.unlabel_name }}" -------------------------------------------------------------------------------- /MacUtilGUI/ViewModels/MainWindowViewModel.fs: -------------------------------------------------------------------------------- 1 | namespace MacUtilGUI.ViewModels 2 | 3 | open System.Collections.ObjectModel 4 | open System.Windows.Input 5 | open System.Threading.Tasks 6 | open Avalonia.Threading 7 | open MacUtilGUI.Models 8 | open MacUtilGUI.Services 9 | 10 | type RelayCommand(canExecute: obj -> bool, execute: obj -> unit) = 11 | let canExecuteChanged = Event() 12 | 13 | interface ICommand with 14 | [] 15 | member _.CanExecuteChanged = canExecuteChanged.Publish 16 | 17 | member _.CanExecute(parameter) = canExecute parameter 18 | member _.Execute(parameter) = execute parameter 19 | 20 | new(execute: obj -> unit) = RelayCommand((fun _ -> true), execute) 21 | 22 | type MainWindowViewModel() as this = 23 | inherit ViewModelBase() 24 | 25 | let mutable selectedScript: ScriptInfo option = None 26 | let mutable scriptOutput: string = "" 27 | let mutable isScriptRunning: bool = false 28 | let categories = ObservableCollection() 29 | 30 | let selectScriptCommand = 31 | RelayCommand(fun parameter -> 32 | match parameter with 33 | | :? ScriptInfo as script -> 34 | selectedScript <- Some script 35 | scriptOutput <- "" // Clear previous output 36 | this.OnPropertyChanged("SelectedScript") 37 | this.OnPropertyChanged("ScriptOutput") 38 | this.OnPropertyChanged("CanRunScript") 39 | this.OnPropertyChanged("SelectedScriptName") 40 | this.OnPropertyChanged("SelectedScriptDescription") 41 | this.OnPropertyChanged("SelectedScriptCategory") 42 | this.OnPropertyChanged("SelectedScriptFile") 43 | | _ -> ()) 44 | 45 | let runScriptCommand = 46 | RelayCommand(fun _ -> 47 | match selectedScript with 48 | | Some script when not isScriptRunning -> 49 | isScriptRunning <- true 50 | scriptOutput <- "Starting script...\n" 51 | this.OnPropertyChanged("ScriptOutput") 52 | this.OnPropertyChanged("CanRunScript") 53 | this.OnPropertyChanged("IsScriptRunning") 54 | 55 | // Define output and error handlers 56 | let onOutput (line: string) = 57 | Dispatcher.UIThread.InvokeAsync(fun () -> 58 | scriptOutput <- scriptOutput + line + "\n" 59 | this.OnPropertyChanged("ScriptOutput")) 60 | |> ignore 61 | 62 | let onError (line: string) = 63 | Dispatcher.UIThread.InvokeAsync(fun () -> 64 | scriptOutput <- scriptOutput + "[ERROR] " + line + "\n" 65 | this.OnPropertyChanged("ScriptOutput")) 66 | |> ignore 67 | 68 | // Run script asynchronously with real-time output 69 | let scriptTask = ScriptService.runScript script onOutput onError 70 | 71 | scriptTask.ContinueWith(fun (task: Task) -> 72 | Dispatcher.UIThread.InvokeAsync(fun () -> 73 | isScriptRunning <- false 74 | 75 | scriptOutput <- 76 | scriptOutput 77 | + sprintf "\n=== Script completed with exit code: %d ===" task.Result 78 | 79 | this.OnPropertyChanged("ScriptOutput") 80 | this.OnPropertyChanged("CanRunScript") 81 | this.OnPropertyChanged("IsScriptRunning")) 82 | |> ignore) 83 | |> ignore 84 | | _ -> ()) 85 | 86 | do 87 | // Load scripts on initialization 88 | let loadedCategories = ScriptService.loadAllScripts () 89 | 90 | for category in loadedCategories do 91 | categories.Add(category) 92 | 93 | member _.Categories = categories 94 | 95 | member _.SelectedScript = selectedScript 96 | 97 | member _.ScriptOutput = scriptOutput 98 | 99 | member _.SelectedScriptName = 100 | match selectedScript with 101 | | Some script -> script.Name 102 | | None -> "" 103 | 104 | member _.SelectedScriptDescription = 105 | match selectedScript with 106 | | Some script -> script.Description 107 | | None -> "" 108 | 109 | member _.SelectedScriptCategory = 110 | match selectedScript with 111 | | Some script -> script.Category 112 | | None -> "" 113 | 114 | member _.SelectedScriptFile = 115 | match selectedScript with 116 | | Some script -> script.Script 117 | | None -> "" 118 | 119 | member _.CanRunScript = selectedScript.IsSome && not isScriptRunning 120 | 121 | member _.IsScriptRunning = isScriptRunning 122 | 123 | member _.SelectScriptCommand = selectScriptCommand 124 | 125 | member _.RunScriptCommand = runScriptCommand 126 | 127 | member _.Title = "MacUtil GUI - Script Runner" 128 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | contact@christitus.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /MacUtilGUI/DEPLOYMENT.md: -------------------------------------------------------------------------------- 1 | # MacUtil GUI - macOS Deployment Guide 2 | 3 | This guide explains how to build and deploy the MacUtil GUI application for macOS. 4 | 5 | ## 🚀 Quick Start 6 | 7 | ### Building App Bundles 8 | 9 | 1. **Create the app icon** (one-time setup): 10 | ```bash 11 | ./create_icon_from_logo.sh 12 | ``` 13 | This converts your `logo.png` into the required `MacUtilGUI.icns` format. 14 | 15 | 2. **Build macOS app bundles**: 16 | ```bash 17 | ./deploy_macos.sh 18 | ``` 19 | This creates `.app` bundles for both Intel and Apple Silicon Macs. 20 | 21 | ## 📦 What You Get 22 | 23 | After running the deployment script, you'll find in the `./dist/` directory: 24 | 25 | - `MacUtil-Intel.app` - For Intel-based Macs (x64) 26 | - `MacUtil-AppleSilicon.app` - For Apple Silicon Macs (ARM64) 27 | 28 | Each app bundle is **self-contained** and includes all necessary dependencies. 29 | 30 | ## 🔧 Project Configuration 31 | 32 | Your project has been configured with the following macOS-specific properties: 33 | 34 | ### Bundle Information 35 | - **Bundle Name**: MacUtil 36 | - **Display Name**: MacUtil GUI 37 | - **Bundle Identifier**: com.macutil.gui 38 | - **Version**: 1.0.0 39 | - **Category**: Utilities 40 | - **Minimum macOS Version**: 10.15 (Catalina) 41 | 42 | ### Build Features 43 | - ✅ Self-contained deployment 44 | - ✅ Single-file publishing 45 | - ✅ Trimmed assemblies for smaller size 46 | - ✅ Ready-to-run images for faster startup 47 | - ✅ High-resolution display support 48 | - ✅ Custom app icon 49 | 50 | ## 🎯 Testing Your App 51 | 52 | ### Basic Testing 53 | ```bash 54 | # Test the Intel version 55 | open dist/MacUtil-Intel.app 56 | 57 | # Test the Apple Silicon version 58 | open dist/MacUtil-AppleSilicon.app 59 | ``` 60 | 61 | ### On Different Machines 62 | Copy the appropriate `.app` bundle to other Macs and test: 63 | - Intel Macs: Use `MacUtil-Intel.app` 64 | - Apple Silicon Macs: Use `MacUtil-AppleSilicon.app` 65 | 66 | ## 🔐 Code Signing & Distribution 67 | 68 | ### For Testing/Development 69 | The unsigned app bundles work fine for testing and development. Users may see a warning about running unsigned software. 70 | 71 | ### For Public Distribution 72 | 73 | #### 1. Code Signing (Required for macOS 10.15+) 74 | ```bash 75 | # Update the signing identity in sign_macos.sh 76 | # Then run: 77 | ./sign_macos.sh 78 | ``` 79 | 80 | **Prerequisites:** 81 | - Apple Developer Account ($99/year) 82 | - Developer ID Application certificate 83 | - Xcode Command Line Tools 84 | 85 | #### 2. Notarization (Required for macOS 10.15+) 86 | After code signing, notarize your app: 87 | 88 | ```bash 89 | # Create a zip for notarization 90 | ditto -c -k --sequesterRsrc --keepParent dist/MacUtil-Intel.app MacUtil-Intel.zip 91 | 92 | # Submit for notarization 93 | xcrun altool --notarize-app -f MacUtil-Intel.zip \ 94 | --primary-bundle-id com.macutil.gui \ 95 | -u your@apple.id \ 96 | -p @keychain:AC_PASSWORD 97 | 98 | # Wait for approval, then staple the notarization 99 | xcrun stapler staple dist/MacUtil-Intel.app 100 | ``` 101 | 102 | #### 3. Creating a DMG Installer (Optional) 103 | ```bash 104 | # Create a DMG for easy distribution 105 | hdiutil create -volname "MacUtil GUI" -srcfolder dist/MacUtil-Intel.app -ov -format UDZO MacUtil-Intel.dmg 106 | ``` 107 | 108 | ## 📱 App Store Distribution 109 | 110 | For App Store distribution, additional steps are required: 111 | 112 | 1. **App Store Certificates**: Use "3rd Party Mac Developer" certificates 113 | 2. **Sandbox**: Enable App Sandbox with appropriate entitlements 114 | 3. **Provisioning Profile**: Use App Store provisioning profile 115 | 4. **Packaging**: Create a `.pkg` file for submission 116 | 117 | See `sign_macos.sh` for App Store signing templates. 118 | 119 | ## 🗂 File Structure 120 | 121 | ``` 122 | MacUtilGUI/ 123 | ├── MacUtilGUI.fsproj # Project file (updated with macOS config) 124 | ├── Info.plist # Bundle information template 125 | ├── MacUtilGUI.entitlements # Code signing entitlements 126 | ├── MacUtilGUI.icns # App icon 127 | ├── logo.png # Source icon image 128 | ├── create_icon_from_logo.sh # Icon generation script 129 | ├── deploy_macos.sh # Main deployment script 130 | ├── sign_macos.sh # Code signing script 131 | └── dist/ # Output directory 132 | ├── MacUtil-Intel.app/ # Intel app bundle 133 | └── MacUtil-AppleSilicon.app/ # ARM64 app bundle 134 | ``` 135 | 136 | ## 🛠 Customization 137 | 138 | ### Changing App Information 139 | Edit the configuration variables in `deploy_macos.sh`: 140 | ```bash 141 | APP_NAME="MacUtilGUI" 142 | BUNDLE_NAME="MacUtil" 143 | BUNDLE_IDENTIFIER="com.macutil.gui" 144 | VERSION="1.0.0" 145 | ``` 146 | 147 | ### Custom Icon 148 | Replace `logo.png` with your own image and run: 149 | ```bash 150 | ./create_icon_from_logo.sh 151 | ``` 152 | 153 | ### Bundle Properties 154 | Edit `MacUtilGUI.fsproj` to modify bundle properties like: 155 | - `CFBundleName` 156 | - `CFBundleIdentifier` 157 | - `CFBundleVersion` 158 | 159 | ## ❓ Troubleshooting 160 | 161 | ### App Won't Launch 162 | - Check executable permissions: `chmod +x YourApp.app/Contents/MacOS/MacUtilGUI` 163 | - Verify all dependencies are included in the bundle 164 | - Check Console.app for error messages 165 | 166 | ### "App is damaged" Error 167 | - App needs to be code signed and notarized 168 | - Try: `xattr -cr YourApp.app` to remove quarantine attributes 169 | 170 | ### Build Errors 171 | - Ensure .NET 9 is installed 172 | - Run `dotnet restore` before building 173 | - Check that all NuGet packages are restored 174 | 175 | ## 📋 Requirements 176 | 177 | - **Development**: macOS with .NET 9 SDK 178 | - **Target**: macOS 10.15 (Catalina) or later 179 | - **Code Signing**: Xcode Command Line Tools 180 | - **Distribution**: Apple Developer Account (for signing/notarization) 181 | 182 | ## 🔗 Resources 183 | 184 | - [Avalonia macOS Deployment Docs](https://docs.avaloniaui.net/docs/deployment/macOS) 185 | - [Apple Bundle Programming Guide](https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/Introduction/Introduction.html) 186 | - [macOS Code Signing Guide](https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution) 187 | 188 | --- 189 | 190 | ✨ **Your Avalonia F# app is now ready for macOS deployment!** ✨ 191 | -------------------------------------------------------------------------------- /MacUtilGUI/App.axaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 24 | 25 | 28 | 29 | 32 | 33 | 34 | 43 | 44 | 54 | 55 | 56 | 59 | 60 | 66 | 67 | 68 | 73 | 74 | 77 | 78 | 81 | 82 | 87 | 88 | 93 | 94 | 99 | 100 | 104 | 105 | 108 | 109 | 112 | 113 | 116 | 117 | 120 | 121 | 124 | 125 | 130 | 131 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Rust build artifacts 2 | /target 3 | /build 4 | rust/target 5 | rust/build 6 | /build/macutil 7 | 8 | # F# and .NET build artifacts 9 | ## Ignore Visual Studio temporary files, build results, and 10 | ## files generated by popular Visual Studio add-ons. 11 | 12 | # User-specific files 13 | *.rsuser 14 | *.suo 15 | *.user 16 | *.userosscache 17 | *.sln.docstates 18 | 19 | # User-specific files (MonoDevelop/Xamarin Studio) 20 | *.userprefs 21 | 22 | # Build results 23 | [Dd]ebug/ 24 | [Dd]ebugPublic/ 25 | [Rr]elease/ 26 | [Rr]eleases/ 27 | x64/ 28 | x86/ 29 | [Aa][Rr][Mm]/ 30 | [Aa][Rr][Mm]64/ 31 | bld/ 32 | [Bb]in/ 33 | [Oo]bj/ 34 | [Ll]og/ 35 | 36 | # F# specific 37 | *.fsproj.user 38 | *.fsx.lock 39 | 40 | # .NET Core 41 | project.lock.json 42 | project.fragment.lock.json 43 | artifacts/ 44 | 45 | # StyleCop 46 | StyleCopReport.xml 47 | 48 | # Files built by Visual Studio 49 | *_i.c 50 | *_p.c 51 | *_h.h 52 | *.ilk 53 | *.meta 54 | *.obj 55 | *.iobj 56 | *.pch 57 | *.pdb 58 | *.ipdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *_wpftmp.csproj 69 | *.log 70 | *.vspscc 71 | *.vssscc 72 | .builds 73 | *.pidb 74 | *.svclog 75 | *.scc 76 | 77 | # Chutzpah Test files 78 | _Chutzpah* 79 | 80 | # Visual C++ cache files 81 | ipch/ 82 | *.aps 83 | *.ncb 84 | *.opendb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | *.VC.db 89 | *.VC.VC.opendb 90 | 91 | # Visual Studio profiler 92 | *.psess 93 | *.vsp 94 | *.vspx 95 | *.sap 96 | 97 | # Visual Studio Trace Files 98 | *.e2e 99 | 100 | # TFS 2012 Local Workspace 101 | $tf/ 102 | 103 | # Guidance Automation Toolkit 104 | *.gpState 105 | 106 | # ReSharper is a .NET coding add-in 107 | _ReSharper*/ 108 | *.[Rr]e[Ss]harper 109 | *.DotSettings.user 110 | 111 | # JustCode is a .NET coding add-in 112 | .JustCode 113 | 114 | # TeamCity is a build add-in 115 | _TeamCity* 116 | 117 | # DotCover is a Code Coverage Tool 118 | *.dotCover 119 | 120 | # AxoCover is a Code Coverage Tool 121 | .axoCover/* 122 | !.axoCover/settings.json 123 | 124 | # Visual Studio code coverage results 125 | *.coverage 126 | *.coveragexml 127 | 128 | # NCrunch 129 | _NCrunch_* 130 | .*crunch*.local.xml 131 | nCrunchTemp_* 132 | 133 | # MightyMoose 134 | *.mm.* 135 | AutoTest.Net/ 136 | 137 | # Web workbench (sass) 138 | .sass-cache/ 139 | 140 | # Installshield output folder 141 | [Ee]xpress/ 142 | 143 | # DocProject is a documentation generator add-in 144 | DocProject/buildhelp/ 145 | DocProject/Help/*.HxT 146 | DocProject/Help/*.HxC 147 | DocProject/Help/Html2 148 | DocProject/Help/html 149 | 150 | # Click-Once directory 151 | publish/ 152 | 153 | # Publish Web Output 154 | *.[Pp]ublish.xml 155 | *.azurePubxml 156 | # Note: Comment the next line if you want to checkin your web deploy settings, 157 | # but database connection strings (with potential passwords) will be unencrypted 158 | *.pubxml 159 | *.publishproj 160 | 161 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 162 | # checkin your Azure Web App publish settings, but sensitive information contained 163 | # in these files may be unencrypted 164 | *.azurePubxml 165 | # Note: Comment the next line if you want to checkin your web deploy settings, 166 | # but database connection strings (with potential passwords) will be unencrypted 167 | *.pubxml 168 | *.publishproj 169 | 170 | # Microsoft Azure Build Output 171 | csx/ 172 | *.build.csdef 173 | 174 | # Microsoft Azure Emulator 175 | ecf/ 176 | rcf/ 177 | 178 | # Windows Store app package directories and files 179 | AppPackages/ 180 | BundleArtifacts/ 181 | Package.StoreAssociation.xml 182 | _pkginfo.txt 183 | *.appx 184 | 185 | # Visual Studio cache files 186 | # files ending in .cache can be ignored 187 | *.[Cc]ache 188 | # but keep track of directories ending in .cache 189 | !*.[Cc]ache/ 190 | 191 | # Others 192 | ClientBin/ 193 | ~$* 194 | *~ 195 | *.dbmdl 196 | *.dbproj.schemaview 197 | *.jfm 198 | *.pfx 199 | *.publishsettings 200 | orleans.codegen.cs 201 | 202 | # Including strong name files can present a security risk 203 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 204 | #*.snk 205 | 206 | # Since there are multiple workflows, uncomment next line to ignore bower_components 207 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 208 | #bower_components/ 209 | 210 | # RIA/Silverlight projects 211 | Generated_Code/ 212 | 213 | # Backup & report files from converting an old project file 214 | # to a newer Visual Studio version. Backup files are not needed, 215 | # because we have git ;-) 216 | _UpgradeReport_Files/ 217 | Backup*/ 218 | UpgradeLog*.XML 219 | UpgradeLog*.htm 220 | CConvertLog*.txt 221 | 222 | # SQL Server files 223 | *.mdf 224 | *.ldf 225 | *.ndf 226 | 227 | # Business Intelligence projects 228 | *.rdl.data 229 | *.bim.layout 230 | *.bim_*.settings 231 | *.rptproj.rsuser 232 | *- Backup*.rdl 233 | 234 | # Microsoft Fakes 235 | FakesAssemblies/ 236 | 237 | # GhostDoc plugin setting file 238 | *.GhostDoc.xml 239 | 240 | # Node.js Tools for Visual Studio 241 | .ntvs_analysis.dat 242 | node_modules/ 243 | 244 | # Visual Studio 6 build log 245 | *.plg 246 | 247 | # Visual Studio 6 workspace options file 248 | *.opt 249 | 250 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 251 | *.vbw 252 | 253 | # Visual Studio LightSwitch build output 254 | **/*.HTMLClient/GeneratedArtifacts 255 | **/*.DesktopClient/GeneratedArtifacts 256 | **/*.DesktopClient/ModelManifest.xml 257 | **/*.Server/GeneratedArtifacts 258 | **/*.Server/ModelManifest.xml 259 | _Pvt_Extensions 260 | 261 | # Paket dependency manager 262 | .paket/paket.exe 263 | paket-files/ 264 | 265 | # FAKE - F# Make 266 | .fake/ 267 | 268 | # JetBrains Rider 269 | .idea/ 270 | *.sln.iml 271 | 272 | # CodeRush personal settings 273 | .cr/personal 274 | 275 | # Python Tools for Visual Studio (PTVS) 276 | __pycache__/ 277 | *.pyc 278 | 279 | # Cake - Uncomment if you are using it 280 | # tools/** 281 | # !tools/packages.config 282 | 283 | # Tabs Studio 284 | *.tss 285 | 286 | # Telerik's JustMock configuration file 287 | *.jmconfig 288 | 289 | # BizTalk build output 290 | *.btp.cs 291 | *.btm.cs 292 | *.odx.cs 293 | *.xsd.cs 294 | 295 | # OpenCover UI analysis results 296 | OpenCover/ 297 | 298 | # Azure Stream Analytics local run output 299 | ASALocalRun/ 300 | 301 | # MSBuild Binary and Structured Log 302 | *.binlog 303 | 304 | # NVidia Nsight GPU debugger configuration file 305 | *.nvuser 306 | 307 | # MFractors (Xamarin productivity tool) working folder 308 | .mfractor/ 309 | 310 | # Local History for Visual Studio 311 | .localhistory/ 312 | 313 | # BeatPulse healthcheck temp database 314 | healthchecksdb 315 | 316 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 317 | MigrationBackup/ 318 | 319 | # MacUtil GUI specific ignores 320 | MacUtilGUI/bin/ 321 | MacUtilGUI/obj/ 322 | MacUtilGUI/dist/ 323 | MacUtilGUI/publish/ 324 | 325 | # macOS specific files 326 | .DS_Store 327 | .DS_Store? 328 | ._* 329 | .Spotlight-V100 330 | .Trashes 331 | ehthumbs.db 332 | Thumbs.db -------------------------------------------------------------------------------- /scripts/applications-setup/tab_data.toml: -------------------------------------------------------------------------------- 1 | name = "Applications Setup" 2 | 3 | [[data]] 4 | name = "Communication Apps" 5 | 6 | [[data.entries]] 7 | name = "Discord" 8 | description = "Discord is a versatile communication platform for gamers and communities, offering voice, video, and text chat features." 9 | script = "communication-apps/discord-setup.sh" 10 | 11 | [[data.entries]] 12 | name = "Signal" 13 | description = "Signal is a privacy-focused messaging app that provides end-to-end encryption for secure text, voice, and video communication." 14 | script = "communication-apps/signal-setup.sh" 15 | 16 | [[data.entries]] 17 | name = "Slack" 18 | description = "Slack is a collaboration platform designed for team communication, featuring channels, direct messaging, file sharing, and integrations with various productivity tools." 19 | script = "communication-apps/slack-setup.sh" 20 | 21 | [[data.entries]] 22 | name = "Telegram" 23 | description = "Telegram is a cloud-based messaging app known for its speed and security, offering features like group chats, channels, and end-to-end encrypted calls." 24 | script = "communication-apps/telegram-setup.sh" 25 | 26 | [[data.entries]] 27 | name = "Thunderbird" 28 | description = "Thunderbird is a free, open-source email client that offers powerful features like customizable email management, a built-in calendar, and extensive add-ons for enhanced functionality." 29 | script = "communication-apps/thunderbird-setup.sh" 30 | 31 | [[data.entries]] 32 | name = "WhatsApp" 33 | description = "WhatsApp is a popular messaging application that allows users to send text messages, voice messages, make voice and video calls, and share media files." 34 | script = "communication-apps/whatsapp-setup.sh" 35 | 36 | [[data]] 37 | name = "Developer Tools" 38 | 39 | [[data.entries]] 40 | name = "Github Desktop" 41 | description = "GitHub Desktop is a user-friendly application that simplifies the process of managing Git repositories and interacting with GitHub, providing a graphical interface for tasks like committing, branching, and syncing changes." 42 | script = "developer-tools/githubdesktop.sh" 43 | 44 | [[data.entries]] 45 | name = "JetBrains Toolbox" 46 | description = "JetBrains Toolbox is a collection of tools and an app that help developers work with JetBrains products." 47 | script = "developer-tools/jetbrains-toolbox.sh" 48 | 49 | [[data.entries]] 50 | name = "Neovim" 51 | description = "Neovim is a refactor, and sometimes redactor, in the tradition of Vim.\nIt is not a rewrite but a continuation and extension of Vim.\nThis command configures neovim from CTT's neovim configuration.\nhttps://github.com/ChrisTitusTech/neovim" 52 | script = "developer-tools/neovim.sh" 53 | 54 | [[data.entries]] 55 | name = "Sublime Text" 56 | description = "Sublime Text is a fast, lightweight, and customizable text editor known for its simplicity, powerful features, and wide range of plugins for various programming languages." 57 | script = "developer-tools/sublime.sh" 58 | 59 | [[data.entries]] 60 | name = "VS Code" 61 | description = "Visual Studio Code (VS Code) is a lightweight, open-source code editor with built-in support for debugging, version control, and extensions for various programming languages and frameworks." 62 | script = "developer-tools/vscode.sh" 63 | 64 | [[data.entries]] 65 | name = "VS Codium" 66 | description = "VSCodium is a free, open-source version of Visual Studio Code (VS Code) that removes Microsoft-specific telemetry and branding." 67 | script = "developer-tools/vscodium.sh" 68 | 69 | [[data.entries]] 70 | name = "Zed" 71 | description = "Zed is a next-generation code editor written in rust, designed for high-performance collaboration with humans and AI." 72 | script = "developer-tools/zed.sh" 73 | 74 | [[data]] 75 | name = "Web Browsers" 76 | 77 | [[data.entries]] 78 | name = "Brave" 79 | description = "Brave is a free and open-source web browser developed by Brave Software, Inc. based on the Chromium web browser." 80 | script = "browsers/brave.sh" 81 | 82 | [[data.entries]] 83 | name = "Chromium" 84 | description = "Chromium is an open-source web browser project started by Google, to provide the source code for the proprietary Google Chrome browser." 85 | script = "browsers/chromium.sh" 86 | 87 | [[data.entries]] 88 | name = "Google Chrome" 89 | description = "Google Chrome is a fast, secure, and free web browser, built for the modern web." 90 | script = "browsers/google-chrome.sh" 91 | 92 | [[data.entries]] 93 | name = "LibreWolf" 94 | description = "LibreWolf is a fork of Firefox, focused on privacy, security, and freedom." 95 | script = "browsers/librewolf.sh" 96 | 97 | [[data.entries]] 98 | name = "Mozilla Firefox" 99 | description = "Mozilla Firefox is a free and open-source web browser developed by the Mozilla Foundation." 100 | script = "browsers/firefox.sh" 101 | 102 | [[data.entries]] 103 | name = "Zen Browser" 104 | description = "Zen Browser is a privacy-focused web browser designed for enhanced security and a seamless browsing experience." 105 | script = "browsers/zen-browser.sh" 106 | 107 | [[data.entries]] 108 | name = "Thorium" 109 | description = "Thorium is a Chromium-based browser focused on privacy and performance." 110 | script = "browsers/thorium.sh" 111 | 112 | [[data.entries]] 113 | name = "Vivaldi" 114 | description = "Vivaldi is a freeware, cross-platform web browser developed by Vivaldi Technologies." 115 | script = "browsers/vivaldi.sh" 116 | 117 | [[data.entries]] 118 | name = "waterfox" 119 | description = "Waterfox is the privacy-focused web browser engineered to give you speed, control, and peace of mind on the internet." 120 | script = "browsers/waterfox.sh" 121 | 122 | [[data]] 123 | name = "Alacritty" 124 | description = "Alacritty is a modern terminal emulator that comes with sensible defaults, but allows for extensive configuration. By integrating with other applications, rather than reimplementing their functionality, it manages to provide a flexible set of features with high performance. The supported platforms currently consist of BSD, Linux, macOS and Windows. This command installs and configures alacritty terminal emulator." 125 | script = "alacritty-setup.sh" 126 | 127 | [[data]] 128 | name = "Android Debloater" 129 | description = "Universal Android Debloater (UAD) is a tool designed to help users remove bloatware and unnecessary pre-installed applications from Android devices, enhancing performance and user experience." 130 | script = "android-debloat.sh" 131 | 132 | [[data]] 133 | name = "Fastfetch" 134 | description = "Fastfetch is a neofetch-like tool for fetching system information and displaying it prettily. It is written mainly in C, with performance and customizability in mind. This command installs fastfetch and configures from CTT's mybash repository. https://github.com/ChrisTitusTech/mybash" 135 | script = "fastfetch-setup.sh" 136 | 137 | [[data]] 138 | name = "Kitty" 139 | description = "kitty is a free and open-source GPU-accelerated terminal emulator for Linux, macOS, and some BSD distributions, focused on performance and features. kitty is written in a mix of C and Python programming languages. This command installs and configures kitty." 140 | script = "kitty-setup.sh" 141 | 142 | [[data]] 143 | name = "ZSH Prompt" 144 | description = "The Z shell is a Unix shell that can be used as an interactive login shell and as a command interpreter for shell scripting. Zsh is an extended Bourne shell with many improvements, including some features of Bash, ksh, and tcsh. This command installs ZSH prompt and provides basic configuration." 145 | script = "zsh-setup.sh" 146 | -------------------------------------------------------------------------------- /MacUtilGUI/sign_macos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # macOS Code Signing Script for MacUtil GUI 4 | # This script signs your app bundles for distribution outside the App Store 5 | 6 | # Configuration - UPDATE THESE VALUES 7 | DEVELOPER_ID="Developer ID Application: CT Tech Group LLC (8ZHX2A9ALF)" 8 | APP_BUNDLE_PATH="./dist/MacUtil-Universal.app" # Universal app bundle (recommended) 9 | ENTITLEMENTS_FILE="./MacUtilGUI.entitlements" 10 | 11 | # Colors for output 12 | RED='\033[0;31m' 13 | GREEN='\033[0;32m' 14 | BLUE='\033[0;34m' 15 | YELLOW='\033[1;33m' 16 | NC='\033[0m' # No Color 17 | 18 | print_status() { 19 | echo -e "${BLUE}ℹ️ $1${NC}" 20 | } 21 | 22 | print_success() { 23 | echo -e "${GREEN}✅ $1${NC}" 24 | } 25 | 26 | print_warning() { 27 | echo -e "${YELLOW}⚠️ $1${NC}" 28 | } 29 | 30 | print_error() { 31 | echo -e "${RED}❌ $1${NC}" 32 | } 33 | 34 | echo "🔐 MacUtil GUI Code Signing Script" 35 | echo "==================================" 36 | echo 37 | 38 | # Check if app bundle exists 39 | if [ ! -d "$APP_BUNDLE_PATH" ]; then 40 | print_error "App bundle not found at: $APP_BUNDLE_PATH" 41 | print_status "Please run ./deploy_macos.sh first to create the app bundle" 42 | exit 1 43 | fi 44 | 45 | # Check if entitlements file exists 46 | if [ ! -f "$ENTITLEMENTS_FILE" ]; then 47 | print_error "Entitlements file not found at: $ENTITLEMENTS_FILE" 48 | exit 1 49 | fi 50 | 51 | # Check if codesign is available 52 | if ! command -v codesign &> /dev/null; then 53 | print_error "codesign command not found. Please install Xcode Command Line Tools." 54 | exit 1 55 | fi 56 | 57 | print_status "App bundle: $APP_BUNDLE_PATH" 58 | print_status "Entitlements: $ENTITLEMENTS_FILE" 59 | print_status "Developer ID: $DEVELOPER_ID" 60 | echo 61 | 62 | # List available signing identities 63 | print_status "Available signing identities:" 64 | security find-identity -v -p codesigning 65 | echo 66 | 67 | print_warning "IMPORTANT: Make sure you have:" 68 | print_warning "1. A valid Developer ID Application certificate in your Keychain" 69 | print_warning "2. Updated the DEVELOPER_ID variable in this script" 70 | print_warning "3. An Apple Developer account for notarization" 71 | print_warning "4. Set up AC_PASSWORD in keychain with:" 72 | print_warning " security add-generic-password -a 'contact@christitus.com' -s 'AC_PASSWORD' -w" 73 | print_warning " (it will prompt you to enter your app-specific password securely)" 74 | echo 75 | 76 | # Check if AC_PASSWORD is accessible in keychain 77 | print_status "Checking keychain access for AC_PASSWORD..." 78 | if security find-generic-password -a 'contact@christitus.com' -s 'AC_PASSWORD' >/dev/null 2>&1; then 79 | print_success "AC_PASSWORD found in keychain" 80 | else 81 | print_error "AC_PASSWORD not found in keychain or keychain is locked" 82 | print_status "Please ensure the keychain is unlocked and AC_PASSWORD is stored" 83 | print_status "You can test with: security find-generic-password -a 'contact@christitus.com' -s 'AC_PASSWORD'" 84 | fi 85 | echo 86 | 87 | read -p "Do you want to proceed with code signing? (y/N): " -n 1 -r 88 | echo 89 | if [[ ! $REPLY =~ ^[Yy]$ ]]; then 90 | print_status "Code signing cancelled" 91 | exit 0 92 | fi 93 | 94 | # Sign the app bundle with deep signing (signs all nested components) 95 | print_status "Signing the app bundle and all nested components..." 96 | codesign --force --deep --timestamp --options=runtime --entitlements "$ENTITLEMENTS_FILE" --sign "$DEVELOPER_ID" "$APP_BUNDLE_PATH" 97 | 98 | if [ $? -eq 0 ]; then 99 | print_success "App bundle signed successfully!" 100 | 101 | # Verify the signature 102 | print_status "Verifying signature..." 103 | codesign --verify --verbose "$APP_BUNDLE_PATH" 104 | 105 | if [ $? -eq 0 ]; then 106 | print_success "Signature verification passed!" 107 | echo 108 | 109 | # Ask if user wants to proceed with notarization 110 | read -p "Do you want to proceed with notarization? (y/N): " -n 1 -r 111 | echo 112 | if [[ $REPLY =~ ^[Yy]$ ]]; then 113 | print_status "Starting notarization process..." 114 | 115 | # Ensure keychain is unlocked 116 | print_status "Ensuring keychain is unlocked..." 117 | security unlock-keychain ~/Library/Keychains/login.keychain-db 118 | 119 | # Retrieve AC_PASSWORD from keychain (silently) 120 | print_status "Retrieving app-specific password from keychain..." 121 | AC_PASSWORD=$(security find-generic-password -a 'contact@christitus.com' -s 'AC_PASSWORD' -w 2>/dev/null) 122 | if [ $? -ne 0 ] || [ -z "$AC_PASSWORD" ]; then 123 | print_error "Failed to retrieve AC_PASSWORD from keychain" 124 | print_status "Please ensure the keychain is unlocked and AC_PASSWORD is stored correctly" 125 | exit 1 126 | fi 127 | print_success "App-specific password retrieved successfully" 128 | 129 | # Create zip for notarization 130 | ZIP_NAME="MacUtil.zip" 131 | print_status "Creating zip file for notarization: $ZIP_NAME" 132 | if [ -f "$ZIP_NAME" ]; then 133 | rm "$ZIP_NAME" 134 | fi 135 | ditto -c -k --sequesterRsrc --keepParent "$APP_BUNDLE_PATH" "$ZIP_NAME" 136 | 137 | if [ $? -eq 0 ]; then 138 | print_success "Zip file created successfully!" 139 | 140 | # Submit for notarization 141 | print_status "Submitting for notarization (this may take several minutes)..." 142 | print_status "You will see progress updates from Apple's notarization service..." 143 | echo 144 | 145 | # Submit for notarization with real-time output 146 | xcrun notarytool submit "$ZIP_NAME" --apple-id contact@christitus.com --team-id 8ZHX2A9ALF --password "$AC_PASSWORD" --wait 147 | 148 | if [ $? -eq 0 ]; then 149 | 150 | # Staple the notarization 151 | print_status "Stapling notarization to app bundle..." 152 | xcrun stapler staple "$APP_BUNDLE_PATH" 153 | 154 | if [ $? -eq 0 ]; then 155 | print_success "Notarization stapled successfully!" 156 | print_success "App is now ready for distribution! 🚀" 157 | echo 158 | print_status "Next steps:" 159 | echo "1. Test the notarized app on a different Mac" 160 | echo "2. Distribute the app bundle: $APP_BUNDLE_PATH" 161 | else 162 | print_error "Failed to staple notarization" 163 | print_status "The app is notarized but stapling failed. You can distribute it anyway." 164 | fi 165 | else 166 | print_error "Notarization failed!" 167 | print_status "Check your Apple ID credentials and app-specific password" 168 | print_status "You can still distribute the signed app, but users may see security warnings" 169 | fi 170 | 171 | # Clear the password variable for security 172 | unset AC_PASSWORD 173 | 174 | # Clean up zip file 175 | if [ -f "$ZIP_NAME" ]; then 176 | rm "$ZIP_NAME" 177 | print_status "Cleaned up temporary zip file" 178 | fi 179 | else 180 | print_error "Failed to create zip file for notarization" 181 | fi 182 | else 183 | print_status "Skipping notarization" 184 | echo 185 | print_status "🎯 Manual notarization steps (if needed later):" 186 | echo "1. Create a zip: ditto -c -k --sequesterRsrc --keepParent '$APP_BUNDLE_PATH' MacUtil.zip" 187 | echo "2. Submit for notarization: xcrun notarytool submit MacUtil.zip --apple-id contact@christitus.com --team-id 8ZHX2A9ALF --password \$(security find-generic-password -a 'contact@christitus.com' -s 'AC_PASSWORD' -w) --wait" 188 | echo "3. If successful, staple: xcrun stapler staple '$APP_BUNDLE_PATH'" 189 | fi 190 | echo 191 | print_success "Code signing complete! 🔐" 192 | else 193 | print_error "Signature verification failed!" 194 | exit 1 195 | fi 196 | else 197 | print_error "Failed to sign app bundle!" 198 | exit 1 199 | fi 200 | -------------------------------------------------------------------------------- /MacUtilGUI/Views/MainWindow.axaml: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |