Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cwd #3

Merged
merged 2 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 40 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
# Zapusk

![Zapusk ScreenShot](.imgs/zapusk.png)
![Zapusk Screenshot](.imgs/zapusk.png)

Zapusk is a job runner for desktop environments. It helps you manage background tasks with features like pre-configured job execution, background shell commands, scheduling with cron-like syntax, log tailing, and notifications. It also provides detailed JSON output for easy data manipulation and analysis.
Zapusk is a versatile job runner designed for desktop environments. It simplifies the process of managing background tasks by providing robust features such as pre-configured job execution, background shell command execution, cron-like scheduling, log tailing, and notifications. Zapusk's detailed JSON output also enables powerful data manipulation and analysis when paired with tools like jq.

## Table of Contents

- [Key Features](#key-features)
- [Installation](#installation)
- [Usage](#usage)
- [Basic Commands](#basic-commands)
- [Advanced Usage](#advanced-usage)
- [Configuration](#configuration)
- [Examples](#examples)
- [Contributing](#contributing)
- [License](#license)

## Key Features

Expand All @@ -15,6 +26,7 @@ Zapusk is a job runner for desktop environments. It helps you manage background
- **Job Groups:** Share settings like callbacks and parallelism between jobs.
- **Colored JSON Output:** Easily readable JSON output.
- **Waybar Integration:** Display job statuses and notifications on Waybar.
- **Custom Working Directory:** Run scripts and callbacks in a specified working directory.

## Installation

Expand All @@ -26,8 +38,9 @@ pip install zapusk

## Usage

Zapusk requires `zapusk-server` to be started. Zapusk offers a command-line interface for managing and executing jobs.
Here's a quick reference:
Zapusk requires `zapusk-server` to be started. Zapusk offers a command-line interface for managing and executing jobs. Here's a quick reference:

### Basic Commands

```sh
Usage:
Expand All @@ -51,34 +64,36 @@ Options:
-n --name=<name> Name for a command.
-g --group=<group> Job group to run the command in.
-t --tail Tail logfile immediately.
```

Examples:
### Examples

# Run npm install in the background
zapusk exec "npm i"
```sh
# Run npm install in the background
zapusk exec "npm i"

# Run pytest and tail its log
zapusk exec "pytest -v" -t
# Run pytest and tail its log
zapusk exec "pytest -v" -t

# Schedule a command to run every minute
zapusk exec "ping -c4 google.com" --schedule "*/1 * * * *"
# Schedule a command to run every minute
zapusk exec "ping -c4 google.com" --schedule "*/1 * * * *"

# Run a job defined in ~/.config/zapusk/config.yaml
zapusk run youtube_dl
# Run a job defined in ~/.config/zapusk/config.yaml
zapusk run youtube_dl

# Cancel a job by its ID
zapusk cancel 42
# Cancel a job by its ID
zapusk cancel 42

# See logs for a job by its ID
zapusk tail 42
# See logs for a job by its ID
zapusk tail 42
```

## Example Configuration
## Configuration

Here is an example configuration file for Zapusk. It defines job groups and individual jobs, specifying commands, schedules, and notifications.
Here is an example configuration file for Zapusk. It defines job groups and individual jobs, specifying commands, schedules, notifications, and working directories.

```yaml
# Port server starts on and client call to
# The port the server starts on and the client connects to
port: 9876

# Enable colored JSON output
Expand All @@ -103,6 +118,7 @@ jobs:
id: unsplash
args_command: "zenity --entry --text 'Collection ID'"
command: ~/.bin/jobs/unsplash_dl.sh
cwd: /path/to/working/directory

- name: Sleep
id: sleep
Expand Down Expand Up @@ -132,6 +148,7 @@ jobs:
id: unsplash
args_command: "zenity --entry --text 'Collection ID'"
command: ~/.bin/jobs/unsplash_wallpaper_collection_download.sh
cwd: /path/to/working/directory
on_finish: notify-send -a "Zapusk" "Wallpapers downloaded" --icon kitty
on_fail: notify-send -a "Zapusk" "Wallpaper download failed" --icon kitty
```
Expand Down Expand Up @@ -210,11 +227,12 @@ jobs:
id: sleep
group: sleep
command: ~/.bin/jobs/sleep
cwd: /path/to/working/directory
on_finish: notify-send -a "zapusk" "Job Finished" "{job.name} has finished" --icon kitty
on_fail: notify-send -a "zapusk" "Job Failed" "{job.name} has failed" --icon kitty
```
### Waybar Integration
## Waybar Integration
Zapusk integrates with Waybar to display job statuses and notifications directly on your desktop.
Expand All @@ -230,10 +248,7 @@ Zapusk integrates with Waybar to display job statuses and notifications directly
}
```

## Contribution

We welcome contributions! If you find a bug or have an idea for improvement, please open an issue or submit a pull request on our GitHub repository.

## License

Zapusk is licensed under the MIT License. See the [LICENSE](LICENSE) file for more information.
Zapusk is licensed under the MIT License. See the [LICENSE](LICENSE.md) file for more information.
1 change: 1 addition & 0 deletions config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
- name: Sleep 10 Seconds
id: sleep_10
command: sleep 10
cwd: /var/

- name: Sleep 30 Seconds
group: parallel
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "zapusk"
version = "0.1.0"
version = "0.1.1"
description = ""
authors = ["Anton Shuvalov <[email protected]>"]
readme = "README.md"
Expand Down
2 changes: 2 additions & 0 deletions zapusk/client/__main__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from type_docopt import docopt
import importlib.metadata

Expand Down Expand Up @@ -95,6 +96,7 @@ def main():
name=str(args["--name"]) if args["--name"] else None,
schedule=str(args["--schedule"]) if args["--schedule"] else None,
tail=bool(args["--tail"]),
cwd=os.getcwd(),
)
return

Expand Down
1 change: 1 addition & 0 deletions zapusk/client/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class JobCreateFromConfigPayload(TypedDict):

class JobCreateFromCommandPayload(TypedDict):
command: str
cwd: str
name: NotRequired[Optional[str]]
group_id: NotRequired[Optional[str]]

Expand Down
16 changes: 16 additions & 0 deletions zapusk/client/command.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from requests.exceptions import ConnectionError

from .api_client import ApiClient, ApiClientError
from .output import Output
Expand Down Expand Up @@ -28,3 +29,18 @@ def print_json(self, json_data, one_line=False):

def print_error(self, exception):
self.output.error(exception, colors=self.colors)

def handle_error(self, ex):
if type(ex) ==ApiClientError:
self.print_error(ex)
return

if type(ex) == ConnectionError:
if "Connection refused by Responses" not in str(ex):
self.print_error(ApiClientError("Can not connect to the server. Please start server with `zapusk-server`"))
return

raise ex



8 changes: 6 additions & 2 deletions zapusk/client/command_exec.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from typing import Optional

from zapusk.client.api_client import ApiClientError
Expand All @@ -9,6 +10,7 @@ class CommandExec(Command):
def run(
self,
command: str,
cwd: str,
name: Optional[str] = None,
group_id: Optional[str] = None,
schedule: Optional[str] = None,
Expand All @@ -23,6 +25,7 @@ def run(
"group_id": group_id,
"name": name,
"schedule": schedule,
"cwd": cwd,
}
)

Expand All @@ -35,6 +38,7 @@ def run(
"command": command,
"group_id": group_id,
"name": name,
"cwd": cwd,
}
)

Expand All @@ -44,5 +48,5 @@ def run(

self.print_json(created_job)

except ApiClientError as ex:
self.print_error(ex)
except Exception as ex:
self.handle_error(ex)
30 changes: 26 additions & 4 deletions zapusk/client/command_exec_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
import os
from unittest.mock import call, patch
import responses
from responses import matchers
Expand All @@ -7,6 +8,7 @@


class TestCommandExec(CommandTestCase):

@responses.activate
def test_should_exec_job(self):
responses.post(
Expand All @@ -19,6 +21,7 @@ def test_should_exec_job(self):
"command": "echo 1",
"group_id": "echo",
"name": "Echo",
"cwd": "/home/anton/",
}
)
],
Expand All @@ -28,6 +31,7 @@ def test_should_exec_job(self):
command="echo 1",
group_id="echo",
name="Echo",
cwd="/home/anton/",
)
json_data = json.loads(self.printer.print.call_args[0][0])

Expand All @@ -45,14 +49,19 @@ def test_should_exec_scheduled_job(self):
"command": "echo 1",
"group_id": "echo",
"name": "Echo",
"cwd": "/home/anton/",
"schedule": "*/1 * * * *",
}
)
],
)

self.command_manager.exec.run(
command="echo 1", group_id="echo", name="Echo", schedule="*/1 * * * *"
command="echo 1",
group_id="echo",
name="Echo",
schedule="*/1 * * * *",
cwd="/home/anton/",
)
json_data = json.loads(self.printer.print.call_args[0][0])

Expand All @@ -66,7 +75,10 @@ def test_should_handle_error(self):
json={"error": "ERROR"},
)

self.command_manager.exec.run(command="echo 1")
self.command_manager.exec.run(
command="echo 1",
cwd="/home/anton/",
)
args = self.printer.print.call_args[0]
message = json.loads(args[0])

Expand All @@ -82,13 +94,23 @@ def test_should_tail_job(self):
responses.get(
"http://example.com/jobs/1",
status=200,
json={"data": {"id": 1, "log": "/var/tail.log"}},
json={
"data": {
"id": 1,
"log": "/var/tail.log",
"cwd": "/home/anton/",
},
},
)

with patch(
"zapusk.client.command_tail.tail", return_value=["log line 1", "log line 2"]
):
self.command_manager.exec.run(command="echo 1", tail=True)
self.command_manager.exec.run(
command="echo 1",
tail=True,
cwd="/home/anton/",
)

log_line1 = self.printer.print.call_args_list[0]
log_line2 = self.printer.print.call_args_list[1]
Expand Down
4 changes: 2 additions & 2 deletions zapusk/client/command_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ def run(
self.print_json(jobs)
return

except ApiClientError as ex:
self.print_error(ex)
except Exception as ex:
self.handle_error(ex)
7 changes: 7 additions & 0 deletions zapusk/models/job.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
import os
from typing import Optional

from .id_field import IdField
Expand Down Expand Up @@ -64,6 +65,7 @@ def from_config(group_config: JobGroup, config: JobConfig):
name=config.name,
on_finish=config.on_finish,
on_fail=config.on_fail,
cwd=config.cwd,
)

group_config: JobGroup
Expand All @@ -86,6 +88,11 @@ def from_config(group_config: JobGroup, config: JobConfig):
job_group id
"""

cwd: str = field(default_factory=lambda: os.environ["HOME"])
"""
current working dir
"""

job_config_id: Optional[str] = None
"""
job_config id
Expand Down
8 changes: 7 additions & 1 deletion zapusk/models/job_config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from dataclasses import dataclass
from dataclasses import dataclass, field
import os
from typing import Optional

from .base_model import BaseModel
Expand All @@ -21,6 +22,11 @@ class JobConfig(BaseModel):
shell command for the job
"""

cwd: str = field(default_factory=lambda: os.environ["HOME"])
"""
current working dir
"""

group: str = "default"
"""
Group id to run job in
Expand Down
Loading
Loading