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
|
## Project Structure
|
||||||
|
|
||||||
* `bin/`: Contains executable scripts that should be available in the shell's `PATH`.
|
* `bin/`: Contains executable scripts symlinked to `~/bin/`. Subdirectories like `macos/`, `restic/`, and `setup/` are included.
|
||||||
* `dotfiles/`: Contains configuration files (dotfiles) to be symlinked into the home directory.
|
* `dotfiles/`: Contains configuration files (dotfiles) symlinked to 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.
|
* `dotfile_overlays/`: Each directory within is symlinked to the home directory, allowing for modular or git-submodule-based configurations.
|
||||||
* `install.sh`: The main installation script that sets up the environment, symlinks dotfiles, and installs packages.
|
* `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
|
## Notes on Security Issues
|
||||||
|
|
||||||
@@ -43,18 +49,20 @@ If making changes that affects how the user installs the tools, update
|
|||||||
### Adding a new dotfile
|
### Adding a new dotfile
|
||||||
|
|
||||||
1. Place the new dotfile in the `dotfiles/` directory.
|
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/`
|
### 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`).
|
2. Ensure the script is executable (`chmod +x`).
|
||||||
|
|
||||||
### Adding a new package
|
### Adding a new package
|
||||||
|
|
||||||
1. Identify the appropriate package list in the `packages/` directory (e.g., `packages/cli`, `packages/kali`).
|
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.
|
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
|
### Platform-specific changes
|
||||||
|
|
||||||
|
|||||||
15
Brewfile
15
Brewfile
@@ -66,9 +66,7 @@ brew "zlib"
|
|||||||
brew "zsh-syntax-highlighting"
|
brew "zsh-syntax-highlighting"
|
||||||
brew "sass/sass/migrator"
|
brew "sass/sass/migrator"
|
||||||
brew "sass/sass/sass"
|
brew "sass/sass/sass"
|
||||||
cask "claude-code"
|
|
||||||
cask "codeql"
|
cask "codeql"
|
||||||
cask "cryptomator"
|
|
||||||
cask "cyberduck"
|
cask "cyberduck"
|
||||||
cask "font-fira-code-nerd-font"
|
cask "font-fira-code-nerd-font"
|
||||||
cask "font-fira-mono-nerd-font"
|
cask "font-fira-mono-nerd-font"
|
||||||
@@ -77,7 +75,6 @@ cask "font-hack-nerd-font"
|
|||||||
cask "font-inconsolata-nerd-font"
|
cask "font-inconsolata-nerd-font"
|
||||||
cask "font-symbols-only-nerd-font"
|
cask "font-symbols-only-nerd-font"
|
||||||
cask "font-terminess-ttf-nerd-font"
|
cask "font-terminess-ttf-nerd-font"
|
||||||
cask "gcloud-cli"
|
|
||||||
cask "ghidra"
|
cask "ghidra"
|
||||||
cask "gimp"
|
cask "gimp"
|
||||||
cask "github"
|
cask "github"
|
||||||
@@ -85,7 +82,6 @@ cask "iterm2"
|
|||||||
cask "macfuse"
|
cask "macfuse"
|
||||||
cask "meld"
|
cask "meld"
|
||||||
cask "mitmproxy"
|
cask "mitmproxy"
|
||||||
cask "orbstack"
|
|
||||||
cask "raycast"
|
cask "raycast"
|
||||||
cask "rectangle"
|
cask "rectangle"
|
||||||
cask "scroll-reverser"
|
cask "scroll-reverser"
|
||||||
@@ -93,9 +89,18 @@ cask "temurin"
|
|||||||
cask "veracrypt"
|
cask "veracrypt"
|
||||||
cask "zulu@17"
|
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
|
# non-corp
|
||||||
if ENV['USER'] != "davidtomaschik"
|
if !is_corp?
|
||||||
brew "bazel"
|
brew "bazel"
|
||||||
brew "openssh"
|
brew "openssh"
|
||||||
|
cask "claude-code"
|
||||||
|
cask "cryptomator"
|
||||||
|
cask "gcloud-cli"
|
||||||
cask "google-cloud-sdk"
|
cask "google-cloud-sdk"
|
||||||
|
cask "orbstack"
|
||||||
end
|
end
|
||||||
|
|||||||
40
README.md
40
README.md
@@ -1,16 +1,24 @@
|
|||||||
### About ###
|
### About ###
|
||||||
|
|
||||||
This is a repository of configuration files that I like to have on all the
|
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"
|
machines that I use. For new systems, you can bootstrap by running the
|
||||||
and get most things setup the way I like them.
|
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,
|
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
|
options to install just parts of it, such as on a machine where I only have a
|
||||||
user account but no root.
|
user account but no root.
|
||||||
|
|
||||||
This now uses [git-crypt](https://github.com/AGWA/git-crypt) to protect
|
This environment supports using `dotfile_overlays/` or `local_dotfiles/` to
|
||||||
`private_dotfiles` for things I don't want to splash all over the internet. :)
|
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.
|
I still wouldn't check in anything terribly sensitive, like private keys.
|
||||||
|
|
||||||
### Usefulness ###
|
### Usefulness ###
|
||||||
@@ -44,16 +52,30 @@ sudo apt-get install xbindkeys xdotool
|
|||||||
After installation, the functionality will be enabled automatically on your
|
After installation, the functionality will be enabled automatically on your
|
||||||
next login.
|
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
|
BASEDIR: Where the skel framework is installed. Defaults to $HOME/.skel
|
||||||
MINIMAL: Don't do things that require git clones or installation of anything
|
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
|
INSTALL_KEYS: Install GnuPG and SSH keys. SSH keys are placed in
|
||||||
authorized_keys. (Defaults to 1, installs keys.)
|
authorized_keys. (Defaults to 1, installs keys.)
|
||||||
TRUST_ALL_KEYS: Allow all keys to be used for SSH login, versus a small subset.
|
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.
|
VERBOSE: Enable verbose output during installation. (Defaults to 0)
|
||||||
(Defaults to opposite of $MINIMAL.)
|
SAVE: Save the install options to ${BASEDIR}/.installed-prefs
|
||||||
SAVE: Save the install options to ${BASEDIR}/installed-prefs
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### TODO ###
|
### 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
|
install_dotfiles
|
||||||
link_directory_contents "${BASEDIR}/bin" "${HOME}/bin" ""
|
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
|
[[ "$INSTALL_KEYS" = 1 ]] && install_keys
|
||||||
save_prefs
|
save_prefs
|
||||||
cleanup
|
cleanup
|
||||||
|
|||||||
Reference in New Issue
Block a user