mirror of
https://github.com/Matir/skel.git
synced 2026-05-25 13:19:07 -07:00
Updates
This commit is contained in:
6
.Brewfile.ignore
Normal file
6
.Brewfile.ignore
Normal file
@@ -0,0 +1,6 @@
|
||||
# Add package names here to ignore them in Brewfile updates.
|
||||
# One package per line.
|
||||
# Example:
|
||||
# iterm2
|
||||
# wget
|
||||
orbstack
|
||||
1
.githooks/pre-commit
Symbolic link
1
.githooks/pre-commit
Symbolic link
@@ -0,0 +1 @@
|
||||
githooks.sh
|
||||
20
.githooks/pre-commit.d/05-check-brewfile
Executable file
20
.githooks/pre-commit.d/05-check-brewfile
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Check if Brewfile needs updating
|
||||
if [[ "$(uname)" != "Darwin" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# We use the script we just created
|
||||
UPDATE_SCRIPT="bin/macos/update_brewfile"
|
||||
|
||||
if [[ -x "$UPDATE_SCRIPT" ]]; then
|
||||
# Run in dry-run mode and see if there's output
|
||||
DIFF_OUTPUT=$("$UPDATE_SCRIPT" --dry-run 2>/dev/null)
|
||||
if [[ "$DIFF_OUTPUT" == *"Changes detected"* ]]; then
|
||||
echo "⚠️ Brewfile is out of sync with your installed packages."
|
||||
echo " Run '$UPDATE_SCRIPT' to synchronize it."
|
||||
echo ""
|
||||
# We don't fail the commit, just warn.
|
||||
fi
|
||||
fi
|
||||
22
AGENTS.md
22
AGENTS.md
@@ -17,10 +17,16 @@ may also be used at times.
|
||||
|
||||
## Project Structure
|
||||
|
||||
* `bin/`: Contains executable scripts that should be available in the shell's `PATH`.
|
||||
* `dotfiles/`: Contains configuration files (dotfiles) to be symlinked into the home directory.
|
||||
* `packages/`: Contains lists of packages to be installed by the `install.sh` script. Each file in this directory corresponds to a package set.
|
||||
* `install.sh`: The main installation script that sets up the environment, symlinks dotfiles, and installs packages.
|
||||
* `bin/`: Contains executable scripts symlinked to `~/bin/`. Subdirectories like `macos/`, `restic/`, and `setup/` are included.
|
||||
* `dotfiles/`: Contains configuration files (dotfiles) symlinked to the home directory.
|
||||
* `dotfile_overlays/`: Each directory within is symlinked to the home directory, allowing for modular or git-submodule-based configurations.
|
||||
* `local_dotfiles/`: If present, its contents are symlinked to the home directory (ignored by git).
|
||||
* `packages/`: Contains lists of packages (one per line) for different environments or toolsets.
|
||||
* `keys/`: Contains SSH keys (`ssh/`), GPG keys (`gpg/`), and a `known_hosts` file to be installed/merged.
|
||||
* `skeltools/`: Internal utilities used by the installation scripts.
|
||||
* `sysctl/` and `udev/`: Linux system configuration files.
|
||||
* `Brewfile`: Homebrew package list for macOS environments.
|
||||
* `install.sh`: The primary installation script for symlinking and basic setup.
|
||||
|
||||
## Notes on Security Issues
|
||||
|
||||
@@ -43,18 +49,20 @@ If making changes that affects how the user installs the tools, update
|
||||
### Adding a new dotfile
|
||||
|
||||
1. Place the new dotfile in the `dotfiles/` directory.
|
||||
2. The `install.sh` script will automatically symlink it to the home directory.
|
||||
2. Alternatively, use `dotfile_overlays/` if the dotfile belongs to a specific group or submodule.
|
||||
3. The `install.sh` script will automatically symlink it to the home directory.
|
||||
|
||||
### Adding a new script to `bin/`
|
||||
|
||||
1. Add the new script to the `bin/` directory.
|
||||
1. Add the new script to the `bin/` directory (or an appropriate subdirectory).
|
||||
2. Ensure the script is executable (`chmod +x`).
|
||||
|
||||
### Adding a new package
|
||||
|
||||
1. Identify the appropriate package list in the `packages/` directory (e.g., `packages/cli`, `packages/kali`).
|
||||
2. Add the new package name to the list.
|
||||
2. Add the new package name to the list (one per line).
|
||||
3. If a new package set is required, create a new file in the `packages/` directory.
|
||||
4. For macOS-specific packages, also consider adding them to the `Brewfile`.
|
||||
|
||||
### Platform-specific changes
|
||||
|
||||
|
||||
15
Brewfile
15
Brewfile
@@ -66,9 +66,7 @@ brew "zlib"
|
||||
brew "zsh-syntax-highlighting"
|
||||
brew "sass/sass/migrator"
|
||||
brew "sass/sass/sass"
|
||||
cask "claude-code"
|
||||
cask "codeql"
|
||||
cask "cryptomator"
|
||||
cask "cyberduck"
|
||||
cask "font-fira-code-nerd-font"
|
||||
cask "font-fira-mono-nerd-font"
|
||||
@@ -77,7 +75,6 @@ cask "font-hack-nerd-font"
|
||||
cask "font-inconsolata-nerd-font"
|
||||
cask "font-symbols-only-nerd-font"
|
||||
cask "font-terminess-ttf-nerd-font"
|
||||
cask "gcloud-cli"
|
||||
cask "ghidra"
|
||||
cask "gimp"
|
||||
cask "github"
|
||||
@@ -85,7 +82,6 @@ cask "iterm2"
|
||||
cask "macfuse"
|
||||
cask "meld"
|
||||
cask "mitmproxy"
|
||||
cask "orbstack"
|
||||
cask "raycast"
|
||||
cask "rectangle"
|
||||
cask "scroll-reverser"
|
||||
@@ -93,9 +89,18 @@ cask "temurin"
|
||||
cask "veracrypt"
|
||||
cask "zulu@17"
|
||||
|
||||
def is_corp?
|
||||
# Check for MDM enrollment (Enrolled via DEP: Yes)
|
||||
`profiles status -type enrollment 2>/dev/null`.include?("Enrolled via DEP: Yes")
|
||||
end
|
||||
|
||||
# non-corp
|
||||
if ENV['USER'] != "davidtomaschik"
|
||||
if !is_corp?
|
||||
brew "bazel"
|
||||
brew "openssh"
|
||||
cask "claude-code"
|
||||
cask "cryptomator"
|
||||
cask "gcloud-cli"
|
||||
cask "google-cloud-sdk"
|
||||
cask "orbstack"
|
||||
end
|
||||
|
||||
40
README.md
40
README.md
@@ -1,16 +1,24 @@
|
||||
### About ###
|
||||
|
||||
This is a repository of configuration files that I like to have on all the
|
||||
machines that I use. I can just clone the repository and run "repo/setup.sh"
|
||||
and get most things setup the way I like them.
|
||||
machines that I use. For new systems, you can bootstrap by running the
|
||||
included `clone.sh` script:
|
||||
|
||||
```bash
|
||||
curl -L https://raw.githubusercontent.com/Matir/skel/master/clone.sh | bash
|
||||
```
|
||||
|
||||
Alternatively, you can manually clone the repository and run `./install.sh`.
|
||||
|
||||
This started just as dotfiles, but expanded to include SSH keys, GPG keys,
|
||||
packages I like installed, and an ever-growing setup script. There are various
|
||||
and an ever-growing setup script. There are various
|
||||
options to install just parts of it, such as on a machine where I only have a
|
||||
user account but no root.
|
||||
|
||||
This now uses [git-crypt](https://github.com/AGWA/git-crypt) to protect
|
||||
`private_dotfiles` for things I don't want to splash all over the internet. :)
|
||||
This environment supports using `dotfile_overlays/` or `local_dotfiles/` to
|
||||
manage machine-specific or private configurations. You can use
|
||||
[git-crypt](https://github.com/AGWA/git-crypt) on these overlay directories
|
||||
for things you don't want to splash all over the internet. :)
|
||||
I still wouldn't check in anything terribly sensitive, like private keys.
|
||||
|
||||
### Usefulness ###
|
||||
@@ -44,16 +52,30 @@ sudo apt-get install xbindkeys xdotool
|
||||
After installation, the functionality will be enabled automatically on your
|
||||
next login.
|
||||
|
||||
On macOS, you can install the recommended packages using the included `Brewfile`:
|
||||
|
||||
```bash
|
||||
brew bundle install
|
||||
```
|
||||
|
||||
### Packages ###
|
||||
|
||||
The `packages/` directory contains lists of recommended packages. You can
|
||||
manually install a set (e.g., on a Debian-based system) using:
|
||||
|
||||
```bash
|
||||
grep -v "^#" packages/cli | xargs sudo apt-get install -y
|
||||
```
|
||||
|
||||
```
|
||||
BASEDIR: Where the skel framework is installed. Defaults to $HOME/.skel
|
||||
MINIMAL: Don't do things that require git clones or installation of anything
|
||||
not included in my .skel. (Defaults to 0, installs everything.)
|
||||
not included in my .skel. (e.g., skips vim-plug, TPM) (Defaults to 0)
|
||||
INSTALL_KEYS: Install GnuPG and SSH keys. SSH keys are placed in
|
||||
authorized_keys. (Defaults to 1, installs keys.)
|
||||
TRUST_ALL_KEYS: Allow all keys to be used for SSH login, versus a small subset.
|
||||
INSTALL_PKGS: Install common packages, if on a Debian-like system.
|
||||
(Defaults to opposite of $MINIMAL.)
|
||||
SAVE: Save the install options to ${BASEDIR}/installed-prefs
|
||||
VERBOSE: Enable verbose output during installation. (Defaults to 0)
|
||||
SAVE: Save the install options to ${BASEDIR}/.installed-prefs
|
||||
```
|
||||
|
||||
### TODO ###
|
||||
|
||||
156
bin/macos/update_brewfile
Executable file
156
bin/macos/update_brewfile
Executable file
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
import sys
|
||||
import argparse
|
||||
import difflib
|
||||
|
||||
# Regex to match brew/cask/tap/mas lines
|
||||
PKG_RE = re.compile(r'^\s*(brew|cask|tap|mas)\s+["\']([^"\']+)["\'](.*)$')
|
||||
|
||||
def get_repo_root():
|
||||
try:
|
||||
root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'],
|
||||
stderr=subprocess.STDOUT).decode().strip()
|
||||
return root
|
||||
except subprocess.CalledProcessError:
|
||||
return os.getcwd()
|
||||
|
||||
def get_ignore_list(repo_root):
|
||||
ignore = set()
|
||||
paths = [
|
||||
os.path.join(repo_root, '.Brewfile.ignore'),
|
||||
os.path.expanduser('~/.Brewfile.ignore'),
|
||||
os.path.expanduser('~/.config/homebrew/ignore')
|
||||
]
|
||||
for path in paths:
|
||||
if os.path.exists(path):
|
||||
with open(path) as f:
|
||||
for line in f:
|
||||
line = line.split('#')[0].strip()
|
||||
if line:
|
||||
ignore.add(line)
|
||||
return ignore
|
||||
|
||||
def get_current_packages():
|
||||
"""Runs brew bundle dump and returns lines."""
|
||||
try:
|
||||
output = subprocess.check_output(['brew', 'bundle', 'dump', '--file=-'],
|
||||
stderr=subprocess.DEVNULL).decode()
|
||||
return output.splitlines()
|
||||
except subprocess.CalledProcessError:
|
||||
print("Error: 'brew bundle dump' failed. Is homebrew installed?", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
def parse_brewfile(content):
|
||||
"""
|
||||
Parses Brewfile content.
|
||||
Returns:
|
||||
- conditional_pkgs: set of (type, name)
|
||||
- preserved_footer: string (everything from first conditional onwards)
|
||||
"""
|
||||
lines = content.splitlines()
|
||||
conditional_pkgs = set()
|
||||
|
||||
# Find the start of the first conditional block
|
||||
first_conditional_idx = -1
|
||||
in_conditional = 0
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
stripped = line.strip()
|
||||
if stripped.startswith(('if ', 'unless ', 'case ')) and not stripped.endswith('; end'):
|
||||
if first_conditional_idx == -1:
|
||||
# Look back for comments that might belong to this block
|
||||
j = i - 1
|
||||
while j >= 0 and (lines[j].strip().startswith('#') or not lines[j].strip()):
|
||||
j -= 1
|
||||
first_conditional_idx = j + 1
|
||||
in_conditional += 1
|
||||
|
||||
if in_conditional > 0:
|
||||
match = PKG_RE.match(line)
|
||||
if match:
|
||||
conditional_pkgs.add((match.group(1), match.group(2)))
|
||||
|
||||
if stripped == 'end' or stripped.endswith('; end'):
|
||||
in_conditional -= 1
|
||||
|
||||
if first_conditional_idx == -1:
|
||||
return set(), ""
|
||||
|
||||
footer = "\n".join(lines[first_conditional_idx:])
|
||||
return conditional_pkgs, footer
|
||||
|
||||
def main(args):
|
||||
repo_root = get_repo_root()
|
||||
brewfile_path = os.path.join(repo_root, 'Brewfile')
|
||||
|
||||
if not os.path.exists(brewfile_path):
|
||||
print(f"Error: Brewfile not found at {brewfile_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
with open(brewfile_path) as f:
|
||||
old_content = f.read()
|
||||
|
||||
conditional_pkgs, footer = parse_brewfile(old_content)
|
||||
ignore_list = get_ignore_list(repo_root)
|
||||
|
||||
dumped_lines = get_current_packages()
|
||||
|
||||
new_unconditional_lines = []
|
||||
|
||||
for line in dumped_lines:
|
||||
match = PKG_RE.match(line)
|
||||
if match:
|
||||
pkg_type, pkg_name = match.group(1), match.group(2)
|
||||
if pkg_name in ignore_list:
|
||||
continue
|
||||
if (pkg_type, pkg_name) in conditional_pkgs:
|
||||
continue
|
||||
|
||||
# If it's not a package line (e.g. comment from dump), we can skip or keep
|
||||
if line.strip():
|
||||
new_unconditional_lines.append(line)
|
||||
|
||||
# Sort lines by type (tap, brew, cask, mas) then name
|
||||
def sort_key(line):
|
||||
match = PKG_RE.match(line)
|
||||
if not match: return (4, line)
|
||||
order = {'tap': 0, 'brew': 1, 'cask': 2, 'mas': 3}
|
||||
return (order.get(match.group(1), 4), match.group(2))
|
||||
|
||||
new_unconditional_lines.sort(key=sort_key)
|
||||
|
||||
# Build new content
|
||||
new_content = "\n".join(new_unconditional_lines)
|
||||
if footer:
|
||||
if new_unconditional_lines:
|
||||
new_content += "\n\n"
|
||||
new_content += footer.strip() + "\n"
|
||||
else:
|
||||
new_content += "\n"
|
||||
|
||||
if new_content == old_content:
|
||||
print("Brewfile is already up to date.")
|
||||
else:
|
||||
if args.dry_run:
|
||||
print("Changes detected (dry run):")
|
||||
diff = difflib.unified_diff(
|
||||
old_content.splitlines(keepends=True),
|
||||
new_content.splitlines(keepends=True),
|
||||
fromfile='Brewfile (original)',
|
||||
tofile='Brewfile (new)'
|
||||
)
|
||||
sys.stdout.writelines(diff)
|
||||
else:
|
||||
with open(brewfile_path, 'w') as f:
|
||||
f.write(new_content)
|
||||
print("Brewfile updated.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Update Brewfile while preserving conditionals.")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Show changes without applying them.")
|
||||
args = parser.parse_args()
|
||||
main(args)
|
||||
@@ -225,6 +225,13 @@ install_main() {
|
||||
}
|
||||
install_dotfiles
|
||||
link_directory_contents "${BASEDIR}/bin" "${HOME}/bin" ""
|
||||
|
||||
# macOS specific Homebrew bundle installation
|
||||
if [[ "$(uname)" == "Darwin" ]] && have_command brew && [[ -f "${BASEDIR}/Brewfile" ]]; then
|
||||
verbose "Checking Homebrew bundle..."
|
||||
brew bundle install --file="${BASEDIR}/Brewfile" --no-lock
|
||||
fi
|
||||
|
||||
[[ "$INSTALL_KEYS" = 1 ]] && install_keys
|
||||
save_prefs
|
||||
cleanup
|
||||
|
||||
Reference in New Issue
Block a user