- Improve setup scripts and CLI functionality
- Report bugs and suggest features
- Improve documentation
- Review pull requests
For productivity helpers, utility functions, and shell shortcuts, contribute to osa-scripts:
- Share useful shell functions you've created
- Build helpers for common development tasks
- Improve existing community scripts
Thank you for your interest in contributing to OSA! This guide explains our code standards, development practices, and how to ensure your changes pass our test suite.
All code must be written for zsh only. We do not support bash, sh, or other shell variants. This allows us to use zsh-specific features and maintain consistency across the codebase.
- No bash-only syntax - Don't use
[[parameter expansion with arithmetic (use((...))in zsh) - No POSIX-only features - Use zsh arrays, associative arrays, and other zsh features freely
- Always use
/usr/bin/env zshshebang - Never#!/bin/bashor#!/bin/sh
All shell scripts must use the .zsh extension:
- ✅
setup.zsh,install.zsh,helpers.zsh - ✅
run-tests.zsh,cleanup.zsh - ❌
setup.sh,install.sh(don't do this)
This makes it immediately clear what language/environment the file uses.
Use this template for new shell scripts:
#!/usr/bin/env zsh
# script-name.zsh - Brief description of what this script does
#
# Usage:
# ./script-name.zsh [options]
#
# Options:
# -h, --help Show help message
#
set -e
# Get script directory
SCRIPT_DIR="$(cd "$(dirname "${(%):-%x}")" && pwd)"
# Color codes
COLOR_RED=$'\033[0;31m'
COLOR_GREEN=$'\033[0;32m'
COLOR_RESET=$'\033[0m'
# Main function
main() {
echo "Script starting..."
# Your code here
}
# Run main if script is executed directly
[[ "${(%):-%x}" == "${0}" ]] && main "$@"- Functions: Use lowercase with hyphens:
setup-symlinks,validate-installation - Variables: Use UPPERCASE for constants:
OSA_REPO_PATH,COLOR_RESET - Variables: Use lowercase with underscores for locals:
backup_suffix,temp_dir - Filenames: Use lowercase with hyphens:
install-mise.zsh,cleanup-symlinks.zsh
- Indentation: Use 2 spaces (not tabs)
- Line length: Keep lines under 100 characters when possible
- Comments: Use comments to explain why, not what the code does
- Error handling: Always use
set -eor check return codes - Safety checks: Validate paths, permissions, and user input before destructive operations
# ✅ Good: Explicit safety checks
remove_osa_files() {
local home_dir="$1"
# Safety check: never delete if HOME is root
if [[ "$home_dir" == "/" ]]; then
echo "Error: HOME is /" >&2
return 1
fi
# Safety check: verify path exists before deleting
if [[ -L "$home_dir/.osa" ]]; then
rm -f "$home_dir/.osa"
echo "✓ Removed OSA symlink"
fi
}
# ❌ Bad: No validation
remove_osa_files() {
rm -rf "$HOME/.osa" # Dangerous!
}- Function headers: Document what the function does, parameters, and return values
- Complex logic: Explain non-obvious decisions
- TODO markers: Use
# TODO: descriptionfor future work
# Calculate backup filename with timestamp
# This ensures unique names if cleanup runs multiple times
get_backup_filename() {
local original="$1"
# Use seconds since epoch for uniqueness
echo "${original}.backup.$(date +%s)"
}All code changes should include tests. We use BATS (Bash Automated Testing System) for shell script testing.
Test files use the .bats extension and should follow this structure:
#!/usr/bin/env bats
# tests/test_feature.bats - Description of what's tested
load helpers # Load shared helpers and mocks
setup() {
setup_test_env # Initialize test environment
}
teardown() {
teardown_test_env # Clean up test environment
}
# Group related tests with comments
# Feature Group
# ============
@test "descriptive test name" {
# Arrange: Set up test data
local input="test value"
# Act: Call the function
local result=$(some_function "$input")
# Assert: Verify the result
[[ "$result" == "expected" ]]
}# Run all tests
./tests/run-tests.zsh
# Run specific test file
bats tests/test_cli_args.bats
# Run with verbose output
bats --verbose tests/
# Watch mode (re-run on changes)
make test-watchWe provide mocks to prevent destructive operations. Use them to test dangerous functions safely:
@test "should remove OSA files safely" {
# Arrange: Mock rm to log calls instead of deleting
alias rm=mock_rm
# Act: Call function that removes files
clean_all "true"
# Assert: Verify correct files were targeted
assert_mock_called "MOCK_RM.*~/.osa"
assert_mock_not_called "MOCK_RM.*home"
}See tests/helpers.zsh for available mocks and assertions.
- Critical functions: Must have tests (e.g.,
clean_all,run_component) - Safety checks: Must be tested (e.g., HOME validation, symlink verification)
- Argument parsing: Must test flag combinations and error cases
- Integration: Test how components work together
Before submitting a pull request, ensure:
- ✅ All code uses
.zshextension and zsh syntax - ✅ Scripts include shebang:
#!/usr/bin/env zsh - ✅ All changes have corresponding tests
- ✅ Tests pass:
./tests/run-tests.zsh - ✅ Scripts are syntactically valid:
bash -n script.zsh - ✅ Documentation is updated (README, docs/, comments)
- ✅ No secrets or credentials in code (use constructors)
- ✅ Safety checks are in place for destructive operations
-
Create a feature branch
git checkout -b feature/my-feature
-
Make changes following code standards
- Use
.zshextension - Include comments for complex logic
- Add safety checks for destructive operations
- Use
-
Write/update tests
# Create test file: tests/test_my_feature.bats # Run tests to verify ./tests/run-tests.zsh
-
Run the test suite
make test # Run all tests make lint # Check syntax
-
Commit with clear messages
git commit -m "feat: add --unsafe flag to skip prompts" -
Push and create pull request
git push origin feature/my-feature
-
GitHub Actions will run tests automatically
- Tests must pass before merging
- Workflow runs:
.github/workflows/test.yml
The CLI uses a component registration system. When adding new setup functionality:
- Create a setup script in
src/setup/new-feature.zsh - Register the component in
osa-cli.zsh:register_component "feature-name" "Human-readable description" "macos,linux" "src/setup/new-feature.zsh"
- Use the sourcing pattern - scripts are sourced, not executed
- Respect
OSA_VERBOSEflag for output control - Support
OSA_DRY_RUNto show what would happen
- Use
set -eto exit on errors - Provide clear error messages
- Return appropriate exit codes (0=success, 1=failure)
- Don't trap errors silently
OSA prioritizes safety through:
- Confirmation prompts for destructive operations (can be skipped with
--unsafe) - Symlink-based architecture - real config is read-only
- Backup files with timestamps before deletion
- Path validation - ensure we never escape the OSA scope
- Dry-run support - see what would happen before committing
- Check existing issues: GitHub Issues
- Review similar code: Look at existing setup scripts in
src/setup/ - Read the architecture docs: See
docs/for system design
When your PR is reviewed:
- Syntax check: Does code follow zsh standards and our style guide?
- Functionality: Does code do what it claims to do?
- Safety: Are there sufficient safety checks? Could this break user systems?
- Tests: Are all changes tested? Do tests verify the code works?
- Documentation: Is documentation updated? Are comments clear?
If anything is unclear, please ask! We'd rather clarify the standards than reject good contributions.