{"id":30064934,"url":"https://github.com/rtulke/ship","last_synced_at":"2025-08-08T05:24:26.647Z","repository":{"id":305973687,"uuid":"1024567343","full_name":"rtulke/ship","owner":"rtulke","description":"ship - ships your code to next level a python updater for your application","archived":false,"fork":false,"pushed_at":"2025-07-23T00:15:17.000Z","size":86,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-23T01:12:03.939Z","etag":null,"topics":["application-depl","deployment","deployment-automation","linux","linux-app","manifest","python-based","python3","remote-update-system","remote-upload","software-deployment","unattended-installation","unattended-upgrade","unattended-upgrades","yaml-configuration"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rtulke.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-07-22T23:06:45.000Z","updated_at":"2025-07-23T00:15:21.000Z","dependencies_parsed_at":"2025-07-23T01:12:05.765Z","dependency_job_id":"5da0f636-94e2-4949-944f-e3d1a925a03d","html_url":"https://github.com/rtulke/ship","commit_stats":null,"previous_names":["rtulke/ship"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/rtulke/ship","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtulke%2Fship","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtulke%2Fship/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtulke%2Fship/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtulke%2Fship/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rtulke","download_url":"https://codeload.github.com/rtulke/ship/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtulke%2Fship/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269367396,"owners_count":24405385,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-08T02:00:09.200Z","response_time":72,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["application-depl","deployment","deployment-automation","linux","linux-app","manifest","python-based","python3","remote-update-system","remote-upload","software-deployment","unattended-installation","unattended-upgrade","unattended-upgrades","yaml-configuration"],"created_at":"2025-08-08T05:24:25.926Z","updated_at":"2025-08-08T05:24:26.628Z","avatar_url":"https://github.com/rtulke.png","language":"Python","readme":"# Ship Application Updater\n\n\u003cdiv align=\"right\"\u003e\n  \u003cimg src=\"logo/ship_100.png\" alt=\"ship\" width=\"100\" /\u003e\n\u003c/div\u003e\nship - ships your code to next level a python updater for your application\n\n![Python](https://img.shields.io/badge/python-3.8+-blue.svg)\n![License](https://img.shields.io/badge/license-MIT-green.svg)\n![Status](https://img.shields.io/badge/status-production--ready-brightgreen.svg)\n\nA Python-based application update system supporting Git, HTTP/HTTPS, and SFTP sources. Designed for unattended operation on remote systems with intelligent configuration management and automatic rollback capabilities.\n\n\n## 🚀 Key Features\n\n- **Multiple Update Sources**: Git repositories, HTTP/HTTPS URLs, SFTP servers\n- **Intelligent Configuration Management**: Automatic config file merging without overwriting user settings\n- **Zero-Downtime Updates**: Staged rollouts and canary deployments\n- **Conditional Updates**: If/then logic for situational updates\n- **Automatic Rollbacks**: On failed health checks or service start problems\n- **Security-First**: Checksum verification, file type validation, minimal permissions\n- **Comprehensive Testing**: Post-update health checks with retry logic\n- **Multi-Channel Notifications**: Slack, webhooks, logs\n- **Directory Protection**: User data remains untouched (e.g. `data/`, `images/`, `uploads/`)\n- **Migration Support**: Version-specific upgrade scripts\n\n## 📋 Table of Contents\n\n- [Installation](#installation)\n- [Configuration](#configuration)\n- [Update Manifest](#update-manifest)\n- [Workflow](#workflow)\n- [Linux Service Setup](#linux-service-setup)\n- [Update Sources](#update-sources)\n- [Examples](#examples)\n- [Troubleshooting](#troubleshooting)\n- [Security](#security)\n- [Contributing](#contributing)\n\n## 🛠 Installation\n\n### System Requirements\n\n- **Operating System**: Linux (preferably Debian/Ubuntu), macOS\n- **Python**: 3.8 or higher\n- **Git**: For Git-based updates\n- **systemd**: For automatic execution\n- **Storage**: At least 100MB for backups\n\n### Automatic Installation\n\n```bash\n# Clone repository\ngit clone https://github.com/rtulke/ship.git\ncd ship\n\n# Run installation (as root)\nchmod +x install.sh\nsudo ./install.sh\n```\n\n### Manual Installation\n\n```bash\n# Create ship user\nsudo useradd -r -s /bin/false -d /var/lib/ship ship\n\n# Create directories\nsudo mkdir -p /opt/ship /etc/ship /var/lib/ship /var/log\n\n# Create virtual environment\ncd /opt/ship\npython3 -m venv venv\nsource venv/bin/activate\n\n# Install dependencies\npip install -r requirements.txt\n\n# Copy files\nsudo cp ship /opt/ship/\nsudo cp ship.toml /etc/ship/\nsudo cp *.service *.timer /etc/systemd/system/\n\n# Set permissions\nsudo chown -R ship:ship /var/lib/ship\nsudo chmod +x /opt/ship/ship\nsudo chmod 600 /etc/ship/ship.toml\n\n# Configure systemd\nsudo systemctl daemon-reload\nsudo systemctl enable ship.timer\nsudo systemctl start ship.timer\n```\n\n## ⚙️ Configuration\n\n### Main Configuration (`/etc/ship/ship.toml`)\n\n```toml\n[general]\n# State file for tracking last execution\nstate_file = \"/var/lib/ship/.ship_state.json\"\nbackup_dir = \"/var/lib/ship/backups\"\n\n[general.logging]\nlevel = \"INFO\"\nfile = \"/var/log/ship.log\"\n\n# Git repository as update source\n[sources.main_app]\ntype = \"git\"\nlocal_path = \"/opt/myapp\"\napp_dir = \"/opt/myapp\"\nbranch = \"main\"\n\n# HTTP/HTTPS release source\n[sources.release_archive]\ntype = \"https\"\nurl = \"https://github.com/user/repo/releases/latest/download/release.tar.gz\"\napp_dir = \"/opt/myapp\"\nfilename = \"release.tar.gz\"\nchecksum = \"sha256:abc123...\"\nmetadata_file = \"/var/lib/ship/.http_metadata.json\"\n\n[sources.release_archive.headers]\nAuthorization = \"Bearer YOUR_TOKEN\"\n\n# SFTP source for private deployments\n[sources.sftp_deploy]\ntype = \"sftp\"\nhostname = \"deploy.example.com\"\nusername = \"deployer\"\nkey_filename = \"/home/ship/.ssh/id_rsa\"\nremote_path = \"/releases/myapp-latest.tar.gz\"\napp_dir = \"/opt/myapp\"\nmetadata_file = \"/var/lib/ship/.sftp_metadata.json\"\n```\n\n### Systemd Timer Configuration\n\n```ini\n# /etc/systemd/system/ship.timer\n[Unit]\nDescription=Run Application Updater Daily\nRequires=ship.service\n\n[Timer]\n# Run daily at 6:00 AM\nOnCalendar=*-*-* 06:00:00\n# Run on boot if missed\nPersistent=true\n# Random delay up to 30 minutes\nRandomizedDelaySec=1800\n\n[Install]\nWantedBy=timers.target\n```\n\n## 📄 Update Manifest\n\nThe update manifest (`update-manifest.yaml`) precisely defines how updates are applied:\n\n### Example Manifest\n\n```yaml\nversion: \"1.2.3\"\nrelease_date: \"2025-01-20\"\ndescription: \"Bug fixes and new features\"\n\n# File-specific rules\nfiles:\n  # Configuration files - intelligent merging\n  \"config.toml\":\n    action: \"merge_toml\"\n    merge_strategy: \"preserve_user\"\n    backup: true\n\n  \"config/*.toml\":\n    action: \"merge_toml\"\n    merge_strategy: \"preserve_user\"\n\n  # Application code - always replace\n  \"src/**/*.py\":\n    action: \"replace\"\n\n  \"requirements.txt\":\n    action: \"replace\"\n\n  # Sensitive files - never touch\n  \".env\":\n    action: \"skip\"\n\n  \"secrets/*\":\n    action: \"skip\"\n\n# Directory protection\ndirectories:\n  # User data - always preserve\n  \"data\":\n    preserve: true\n    description: \"User data\"\n\n  \"images\":\n    preserve: true\n    description: \"Uploaded images\"\n\n  \"logs\":\n    preserve: true\n    cleanup_old: true\n    keep_days: 30\n\n  # Cache - can be cleared\n  \"cache\":\n    preserve: false\n\n# Update hooks\nhooks:\n  pre_update:\n    - \"systemctl stop myapp.service\"\n    - \"python3 scripts/pre_update_check.py\"\n\n  post_update:\n    - \"pip install -r requirements.txt\"\n    - \"python3 scripts/migrate.py\"\n    - \"systemctl start myapp.service\"\n    - \"python3 scripts/health_check.py\"\n\n  rollback:\n    - \"systemctl stop myapp.service\"\n    - \"systemctl start myapp.service\"\n\n# System requirements\nrequirements:\n  min_python_version: \"3.8\"\n  min_disk_space_mb: 100\n  required_services:\n    - \"postgresql\"\n  environment_checks:\n    - name: \"database_connectivity\"\n      command: \"python3 scripts/check_db.py\"\n\n# Automatic rollback triggers\nrollback:\n  strategy: \"full_backup\"\n  keep_backups: 5\n  auto_rollback_on:\n    - \"health_check_fail\"\n    - \"service_start_fail\"\n\n# Post-update tests\npost_update_tests:\n  - name: \"Import test\"\n    command: \"python3 -c 'import myapp; print(\\\"OK\\\")'\"\n    timeout: 30\n\n  - name: \"Service health\"\n    command: \"curl -f http://localhost:8000/health\"\n    timeout: 30\n    retry_count: 3\n    retry_delay: 5\n\n# Notifications\nnotifications:\n  on_success:\n    - type: \"webhook\"\n      url: \"https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK\"\n      message: \"✅ MyApp successfully updated to {version}\"\n\n  on_failure:\n    - type: \"webhook\"\n      url: \"https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK\"\n      message: \"❌ MyApp update failed: {error}\"\n\n# Advanced features\nconditionals:\n  - condition: \"file_exists('/opt/myapp/.maintenance_mode')\"\n    action: \"skip_update\"\n    message: \"System in maintenance mode\"\n\nmigrations:\n  \"1.2.0\":\n    - \"python3 scripts/migrate_database.py\"\n    - \"python3 scripts/update_config_format.py\"\n\ncleanup:\n  remove_files: [\"*.pyc\", \"__pycache__\"]\n  remove_directories: [\"legacy_modules\"]\n  commands:\n    - \"find /opt/myapp -name '*.pyc' -delete\"\n```\n\n## 🔄 Workflow\n\n### Developer Workflow (Git-based)\n\n#### 1. Develop new version\n\n```bash\n# Make code changes\ngit add .\ngit commit -m \"Version 1.2.3: New features and bug fixes\"\n\n# Create/adjust update manifest\ncat \u003e update-manifest.yaml \u003c\u003c EOF\nversion: \"1.2.3\"\ndescription: \"New features and bug fixes\"\n# ... additional configuration\nEOF\n\n# Commit manifest\ngit add update-manifest.yaml\ngit commit -m \"Add update manifest for v1.2.3\"\n```\n\n#### 2. Create release\n\n```bash\n# Using release helper (recommended)\n./release.sh interactive\n\n# Or manually\ngit tag v1.2.3\ngit push origin main --tags\n```\n\n#### 3. System-side update process\n\n```bash\n# Runs automatically daily at 6:00 AM\n# 1. Git fetch origin\n# 2. Compare commits (local vs remote)\n# 3. Find new version v1.2.3\n# 4. Load update manifest\n# 5. Check prerequisites\n# 6. Create backup\n# 7. Apply file rules\n# 8. Run migrations\n# 9. Restart services\n# 10. Perform health checks\n# 11. Send notifications\n```\n\n### HTTP/HTTPS Workflow\n\n#### 1. Create release package\n\n```bash\n# Automatically with release helper\n./release.sh http v1.2.3 \"New features\"\n\n# Or manually\nmkdir myapp-v1.2.3\ncp -r src config templates static update-manifest.yaml myapp-v1.2.3/\ntar czf myapp-v1.2.3.tar.gz myapp-v1.2.3/\nsha256sum myapp-v1.2.3.tar.gz \u003e myapp-v1.2.3.tar.gz.sha256\n```\n\n#### 2. Upload to server/GitHub Releases\n\n```bash\n# GitHub Releases API\ncurl -H \"Authorization: token YOUR_TOKEN\" \\\n     -H \"Content-Type: application/octet-stream\" \\\n     --data-binary @myapp-v1.2.3.tar.gz \\\n     \"https://uploads.github.com/repos/user/repo/releases/123/assets?name=myapp-v1.2.3.tar.gz\"\n```\n\n### SFTP Workflow\n\n```bash\n# Using release helper\n./release.sh sftp v1.2.3 \"Hotfix\" deploy.server.com deployer /releases/\n\n# Or manually\nscp myapp-v1.2.3.tar.gz deployer@deploy.server.com:/releases/myapp-latest.tar.gz\n```\n\n## 🐧 Linux Service Setup\n\n### Create Systemd Service\n\n```bash\n# Service file: /etc/systemd/system/ship.service\nsudo tee /etc/systemd/system/ship.service \u003e /dev/null \u003c\u003c 'EOF'\n[Unit]\nDescription=Ship Application Updater Service\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=oneshot\nUser=ship\nGroup=ship\nEnvironment=PYTHONPATH=/opt/ship\nWorkingDirectory=/opt/ship\nExecStart=/opt/ship/venv/bin/python /opt/ship/ship --config /etc/ship/ship.toml\nStandardOutput=journal\nStandardError=journal\n\n# Security Hardening\nPrivateTmp=true\nNoNewPrivileges=true\nProtectSystem=strict\nReadWritePaths=/var/lib/ship /var/log /opt/myapp\nProtectHome=true\nCapabilityBoundingSet=\nSystemCallArchitectures=native\nSystemCallFilter=@system-service\nSystemCallFilter=~@debug @mount @cpu-emulation @obsolete @privileged @reboot @swap @raw-io\n\n[Install]\nWantedBy=multi-user.target\nEOF\n\n# Timer file: /etc/systemd/system/ship.timer\nsudo tee /etc/systemd/system/ship.timer \u003e /dev/null \u003c\u003c 'EOF'\n[Unit]\nDescription=Run Ship Application Updater Daily\nRequires=ship.service\n\n[Timer]\nOnCalendar=*-*-* 06:00:00\nPersistent=true\nRandomizedDelaySec=1800\n\n[Install]\nWantedBy=timers.target\nEOF\n\n# Enable services\nsudo systemctl daemon-reload\nsudo systemctl enable ship.timer\nsudo systemctl start ship.timer\n```\n\n### Service Management\n\n```bash\n# Check status\nsudo systemctl status ship.timer\nsudo systemctl status ship.service\n\n# Show next scheduled run\nsystemctl list-timers ship.timer\n\n# Run manually\nsudo systemctl start ship.service\n\n# Follow logs\nsudo journalctl -u ship.service -f\nsudo tail -f /var/log/ship.log\n\n# Stop/disable service\nsudo systemctl stop ship.timer\nsudo systemctl disable ship.timer\n```\n\n## 🔧 Update Sources\n\n### Git Repository\n\n**Advantages:**\n- Automatic detection of changed files\n- Branch support for different environments\n- Complete version control\n- No separate deployment process\n\n**Setup:**\n```bash\n# Initialize repository\ngit clone https://github.com/user/myapp.git /opt/myapp\ncd /opt/myapp\ngit checkout main\n\n# SSH keys for automatic access\nsudo -u ship ssh-keygen -t rsa -b 4096 -f /home/ship/.ssh/id_rsa\n# Add public key to GitHub/GitLab\n```\n\n### HTTP/HTTPS\n\n**Advantages:**\n- Easy CI/CD integration\n- GitHub Releases support\n- ETag-based change detection\n- Checksum verification\n\n**Setup:**\n```bash\n# Create GitHub Personal Access Token\n# Configure in ship.toml:\n[sources.github_releases.headers]\nAuthorization = \"Bearer ghp_xxxxxxxxxxxxxxxxxxxx\"\n```\n\n### SFTP\n\n**Advantages:**\n- Secure transfer\n- Firewall-friendly\n- Simple server-to-server transfer\n- SSH key-based authentication\n\n**Setup:**\n```bash\n# SSH keys for SFTP access\nsudo -u ship ssh-keygen -t rsa -b 4096 -f /home/ship/.ssh/sftp_key\nsudo -u ship ssh-copy-id -i /home/ship/.ssh/sftp_key deployer@sftp.server.com\n\n# Test SFTP access\nsudo -u ship sftp -i /home/ship/.ssh/sftp_key deployer@sftp.server.com\n```\n\n## 💡 Examples\n\n### Simple Update (Python files only)\n\n```yaml\n# update-manifest.yaml\nversion: \"1.0.1\"\ndescription: \"Bugfix Release\"\n\nfiles:\n  \"*.py\":\n    action: \"replace\"\n  \"src/**/*.py\":\n    action: \"replace\"\n\nhooks:\n  post_update:\n    - \"systemctl restart myapp.service\"\n```\n\n### Complex Update with Database Migration\n\n```yaml\nversion: \"2.0.0\"\ndescription: \"Major release with DB schema changes\"\n\nfiles:\n  \"config.toml\":\n    action: \"merge_toml\"\n    merge_strategy: \"preserve_user\"\n  \"src/**/*.py\":\n    action: \"replace\"\n\ndirectories:\n  \"data\":\n    preserve: true\n  \"uploads\":\n    preserve: true\n\nrequirements:\n  min_disk_space_mb: 500\n  required_services:\n    - \"postgresql\"\n\nconditionals:\n  - condition: \"current_version \u003c '2.0.0'\"\n    action: \"require_manual_intervention\"\n    message: \"Major version upgrade - manual steps required\"\n    manual_steps:\n      - \"Create database backup\"\n      - \"Review breaking changes in CHANGELOG.md\"\n\nmigrations:\n  \"2.0.0\":\n    - \"python3 scripts/migrate_database_v2.py\"\n    - \"python3 scripts/rebuild_search_index.py\"\n\nhooks:\n  pre_update:\n    - \"systemctl stop myapp.service\"\n    - \"python3 scripts/backup_database.py\"\n  \n  post_update:\n    - \"python3 scripts/migrate_database.py\"\n    - \"systemctl start myapp.service\"\n\npost_update_tests:\n  - name: \"Database Migration\"\n    command: \"python3 scripts/verify_db_schema.py\"\n    timeout: 120\n\n  - name: \"API Health\"\n    command: \"curl -f http://localhost:8000/api/health\"\n    retry_count: 5\n    retry_delay: 10\n\nrollback:\n  auto_rollback_on:\n    - \"health_check_fail\"\n    - \"service_start_fail\"\n\nnotifications:\n  on_success:\n    - type: \"webhook\"\n      url: \"https://hooks.slack.com/services/...\"\n      message: \"🎉 MyApp v{version} successfully deployed!\"\n  \n  on_failure:\n    - type: \"webhook\" \n      url: \"https://hooks.slack.com/services/...\"\n      message: \"🚨 MyApp update v{version} failed: {error}\"\n```\n\n### Configuration-Only Update\n\n```yaml\nversion: \"1.1.1\"\ndescription: \"Configuration update without code changes\"\n\nfiles:\n  \"config/app.toml\":\n    action: \"merge_toml\"\n    merge_strategy: \"update_only\"  # Only add new keys\n  \n  \"config/features.json\":\n    action: \"replace\"\n\nhooks:\n  post_update:\n    - \"systemctl reload myapp.service\"  # Reload instead of restart\n\npost_update_tests:\n  - name: \"Config Validation\"\n    command: \"python3 -c 'import myapp.config; myapp.config.validate()'\"\n```\n\n## 🛠 CLI Usage\n\n### Basic Commands\n\n```bash\n# Update check without applying\nsudo -u ship /opt/ship/venv/bin/python /opt/ship/ship --check-only\n\n# Forced update (bypasses \"once daily\" rule)\nsudo -u ship /opt/ship/venv/bin/python /opt/ship/ship --force\n\n# Specific update sources\nsudo -u ship /opt/ship/venv/bin/python /opt/ship/ship --sources main_app config_repo\n\n# Rollback to last backup\nsudo -u ship /opt/ship/venv/bin/python /opt/ship/ship --rollback /var/lib/ship/backups/backup_pre_update_1.2.3\n\n# Manifest validation\nsudo -u ship /opt/ship/venv/bin/python /opt/ship/ship --test-manifest /path/to/update-manifest.yaml\n\n# Check rollout eligibility\nsudo -u ship /opt/ship/venv/bin/python /opt/ship/ship --check-rollout /path/to/update-manifest.yaml\n```\n\n### Release Management\n\n```bash\n# Interactive release process\n./release.sh interactive\n\n# Create Git release\n./release.sh git v1.2.3 \"New features and bug fixes\"\n\n# Create HTTP release package\n./release.sh http v1.2.3 \"Hotfix for critical bug\"\n\n# SFTP upload\n./release.sh sftp v1.2.3 \"Emergency fix\" deploy.server.com deployer /releases/\n\n# Create manifest only\n./release.sh manifest v1.2.3\n```\n\n## 🚨 Troubleshooting\n\n### Common Issues\n\n#### 1. Permission Errors\n\n```bash\n# Problem: Permission denied\n# Solution: Check permissions\nsudo chown -R ship:ship /var/lib/ship\nsudo chmod +x /opt/ship/ship\nsudo chmod 600 /etc/ship/ship.toml\n```\n\n#### 2. Git Authentication Failed\n\n```bash\n# Problem: Git fetch failed\n# Solution: Check SSH keys\nsudo -u ship ssh -T git@github.com\nsudo -u ship git -C /opt/myapp fetch origin\n```\n\n#### 3. Service Won't Start After Update\n\n```bash\n# Problem: Service failed to start\n# Solution: Automatic rollback should trigger\n# Manual rollback if needed:\nsudo systemctl start app-rollback.service\n\n# Or specific backup:\nsudo -u ship /opt/ship/venv/bin/python /opt/ship/ship --rollback /var/lib/ship/backups/backup_pre_update_1.2.2\n```\n\n#### 4. Update Hangs / Timeout\n\n```bash\n# Problem: Update process hangs\n# Solution: Kill process and rollback\nsudo pkill -f \"ship\"\nsudo systemctl start app-rollback.service\n\n# Check logs:\nsudo journalctl -u ship.service --since \"1 hour ago\"\n```\n\n#### 5. Disk Space Issues\n\n```bash\n# Problem: Insufficient disk space\n# Solution: Clean old backups\nsudo find /var/lib/ship/backups -type d -mtime +30 -exec rm -rf {} \\;\n\n# Rotate logs:\nsudo logrotate -f /etc/logrotate.d/ship\n```\n\n### Debug Mode\n\n```bash\n# Enable detailed logging\n# In /etc/ship/ship.toml:\n[general.logging]\nlevel = \"DEBUG\"\n\n# Test individual components:\nsudo -u ship /opt/ship/venv/bin/python /opt/ship/ship --test-manifest update-manifest.yaml\nsudo -u ship /opt/ship/venv/bin/python /opt/ship/ship --check-requirements update-manifest.yaml\n```\n\n### Monitoring\n\n```bash\n# System Health Check\n#!/bin/bash\n# /usr/local/bin/ship-healthcheck.sh\n\necho \"=== Spdater Health Check ===\"\n\n# Timer Status\necho \"Timer Status:\"\nsystemctl is-active ship.timer\n\n# Last Run\necho \"Last successful run:\"\nif [ -f /var/lib/ship/.ship_state.json ]; then\n    cat /var/lib/ship/.ship_state.json | jq -r '.last_run'\nelse\n    echo \"No state file found\"\nfi\n\n# Disk Space\necho \"Backup disk usage:\"\ndu -sh /var/lib/ship/backups/\n\n# Log Errors (last 24h)\necho \"Errors in last 24h:\"\njournalctl -u ship.service --since \"24 hours ago\" | grep -i error | wc -l\n```\n\n## 🔒 Security\n\n### Security Features\n\n- **Minimal Permissions**: Runs as dedicated `ship` user without shell access\n- **Systemd Security**: NoNewPrivileges, ProtectSystem, CapabilityBoundingSet\n- **Checksum Verification**: SHA256 hashes for download integrity\n- **File Type Validation**: Only allowed file types are processed\n- **Size Limits**: Maximum file sizes configurable\n- **Secure Temp**: PrivateTmp for temporary files\n- **System Call Filtering**: Restricted syscalls via SystemCallFilter\n\n### Recommended Security Configuration\n\n```toml\n# In ship.toml\n[sources.main_app.security]\nverify_checksums = true\nallowed_file_types = [\".py\", \".toml\", \".json\", \".sql\", \".md\", \".txt\", \".yaml\"]\nmax_file_size_mb = 50\n\n# Handle privileged files separately\nprivileged_files = [\"scripts/system_config.py\"]\n```\n\n### Network Security\n\n```bash\n# Firewall rules for Git over SSH\nsudo ufw allow out 22 comment \"Git SSH access\"\n\n# For HTTPS updates\nsudo ufw allow out 443 comment \"HTTPS updates\"\n\n# Block incoming connections (outgoing only)\nsudo ufw default deny incoming\nsudo ufw default allow outgoing\n```\n\n## 🤝 Contributing\n\n### Development Setup\n\n```bash\ngit clone https://github.com/rtulke/ship.git\ncd ship\n\n# Development environment\npython3 -m venv dev-env\nsource dev-env/bin/activate\npip install -r requirements.txt\npip install -r requirements-dev.txt\n\n# Pre-commit hooks\npre-commit install\n\n# Run tests\npython -m pytest tests/\npython -m pytest tests/ --cov=ship\n\n# Linting\nflake8 .\nblack .\nisort .\n```\n\n### Testing\n\n```bash\n# Unit tests\npython -m pytest tests/unit/\n\n# Integration tests\npython -m pytest tests/integration/\n\n# End-to-end tests (requires Docker)\npython -m pytest tests/e2e/\n\n# Performance tests\npython -m pytest tests/performance/\n```\n\n### Documentation\n\n```bash\n# Generate documentation\ncd docs/\nmake html\n\n# API documentation\npydoc -w ship\n```\n\n## 📄 License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n\n## 🆘 Support\n\n- **Documentation**: [GitHub Wiki](https://github.com/rtulke/ship/wiki)\n- **Issues**: [GitHub Issues](https://github.com/rtulke/ship/issues)\n- **Discussions**: [GitHub Discussions](https://github.com/rtulke/ship/discussions)\n\n---\n\n**Built with ❤️ for robust, unattended application updates**\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frtulke%2Fship","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frtulke%2Fship","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frtulke%2Fship/lists"}