Skip to content

Commit

Permalink
Add cwd (#3)
Browse files Browse the repository at this point in the history
* Add `cwd` to Job, JobConfig, and popen calls

* Update readme
  • Loading branch information
A authored Jul 15, 2024
1 parent 962e8bb commit ef88d6a
Show file tree
Hide file tree
Showing 24 changed files with 326 additions and 166 deletions.
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

0 comments on commit ef88d6a

Please sign in to comment.