Allows cron job control and command execution on a remote machine using git infrastructure.
👷🏻 Please note, this project is still a work-in-progress 🏗️
On your computer, set up the basic repository
(checkout the example_project
in this repository to see how this should generally look):
- Create a private GitHub repository and clone it.
- Create a
requirements.txt
file, and add thegit-syncer
dependency. - Create a
main.py
file:
from git_syncer import run
from git_syncer.runnables import register
register() # Add boot jobs, cron jobs, and remote commands here 🏋🏻♂️
if __name__ == "__main__":
run()
- Add a
.gitignore
file (tip: use gitignore.io). It must containout/
directory. - Commit and push your changes.
On your remote machine (SSH to it):
- Set up GitHub credentials to clone your new repository.
Highly recommended: instead of providing your own personal GitHub credentials on the remote machine, add an SSH deploy key to your repository . If you choose to do so, check theAllow write access
checkbox. - Clone your new repository to the remote machine.
- Set up a virtual environment for this project and activate it
(using
source <venv_dir>/bin/activate
). cd
to your repository directory.- Install dependencies using
pip install -r requirements.txt
(git-syncer
should be installed). ⚠️ THIS STEP WILL OVERRIDE YOUR EXISTING CRONTAB SETTINGS!⚠️
Activate the syncer using the CLI commandinit-syncer
.
From now on, you can add new cron jobs and execute remote commands on the remote machine using this git repository. For more details, see the usage section.
Note: this tool writes logs to ~/logs/git-syncer/
.
A Runnable
is the basic class that the package uses.
To define your own custom commands, create your own class, inherit Runnable
,
and implement the mandatory abstract methods:
# File: my_runnables.py
from git_syncer.models import Runnable
class HelloWorld(Runnable):
@property
def verbose_name(self) -> str:
return "Hello World"
def run(self) -> str:
return "This runnable was called!"
...and register your runnable in main.py
:
# File: main.py
from git_syncer.runnables import register
from my_runnables import HelloWorld
register(HelloWorld())
Boot jobs will execute once the remote machine turns on.
In order to make a Runnable
into a boot job, set the run_on_boot
property to True
:
class HelloWorld(Runnable):
@property
def run_on_boot(self) -> bool:
return True
...
To create a cron job, inherit the CronJob
class, and fill the expression
property:
from git_syncer.models import CronJob
class MyCronJob(CronJob):
@property
def verbose_name(self) -> str:
return "Ping"
@property
def expression(self) -> str:
# Every 5 minutes
return "*/5 * * * *"
def run(self) -> str:
return "This job runs every 5 minutes"
In order to make a non-cron runnable execute on the remote machine:
- On your local machine, commit an empty file matching your
Runnable
name underexecute
folder (for example, if the runnable class name isGetIP
, commit a file namedexecute/get-ip
). - Push your changes.
- On the next round minute:
- The runnable will execute on the remote device,
- The execution result will be written in a file matching your runnable name (for example, if the runnable
class name is
GetIP
, the result file will be namedexecute/get-ip-result.txt
, and theexecute/get-ip
file will be removed). - The changes will be committed and pushed back to the repository.
- Wait a few seconds and pull your repository. You will see the execution result in the expected result file.
Expert mode: every minute, in order the check if a non-cron Runnable
should be executed, its should_execute
method
is called (matching file names to the runnable class name). In order to execute your runnable based on different logic,
override the should_execute
method:
from typing import Set
from git_syncer.models import Runnable
import random
class HelloWorld(Runnable):
@property
def verbose_name(self) -> str:
return "Hello World"
def run(self) -> str:
return "This runnable was called!"
def should_execute(self, inputs: Set[str]) -> bool:
# Take a look at the base method and implement your own logic.
return random.randint(1, 100) % 5 == 0
👷🏻 TODO