generate-systemd-timer 0.1.3__tar.gz → 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- generate_systemd_timer-2.0.0/.github/dependabot.yml +20 -0
- generate_systemd_timer-2.0.0/.github/workflows/main.yml +53 -0
- generate_systemd_timer-2.0.0/.github/workflows/tests.yml +37 -0
- generate_systemd_timer-2.0.0/.gitignore +143 -0
- generate_systemd_timer-2.0.0/PKG-INFO +29 -0
- {generate_systemd_timer-0.1.3 → generate_systemd_timer-2.0.0}/README.md +4 -2
- generate_systemd_timer-2.0.0/pyproject.toml +49 -0
- generate_systemd_timer-2.0.0/systemd_generator/__init__.py +197 -0
- generate_systemd_timer-2.0.0/tests/test_generator.py +457 -0
- generate_systemd_timer-2.0.0/uv.lock +455 -0
- generate_systemd_timer-0.1.3/PKG-INFO +0 -37
- generate_systemd_timer-0.1.3/pyproject.toml +0 -28
- generate_systemd_timer-0.1.3/systemd_generator/__init__.py +0 -99
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 2
|
|
3
|
+
|
|
4
|
+
updates:
|
|
5
|
+
- package-ecosystem: pip
|
|
6
|
+
directory: /
|
|
7
|
+
schedule:
|
|
8
|
+
interval: monthly
|
|
9
|
+
groups:
|
|
10
|
+
python-packages:
|
|
11
|
+
patterns:
|
|
12
|
+
- "*"
|
|
13
|
+
- package-ecosystem: github-actions
|
|
14
|
+
directory: /
|
|
15
|
+
schedule:
|
|
16
|
+
interval: monthly
|
|
17
|
+
groups:
|
|
18
|
+
github-actions:
|
|
19
|
+
patterns:
|
|
20
|
+
- "*"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: Publish tags
|
|
2
|
+
on: push
|
|
3
|
+
|
|
4
|
+
jobs:
|
|
5
|
+
build:
|
|
6
|
+
name: build
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
steps:
|
|
9
|
+
- uses: actions/checkout@v6
|
|
10
|
+
- name: Install uv
|
|
11
|
+
uses: astral-sh/setup-uv@v6
|
|
12
|
+
with:
|
|
13
|
+
enable-cache: true
|
|
14
|
+
- name: Build and check
|
|
15
|
+
run: |
|
|
16
|
+
uv build
|
|
17
|
+
uvx twine check dist/*
|
|
18
|
+
- name: Upload build artifact
|
|
19
|
+
uses: actions/upload-artifact@v7
|
|
20
|
+
with:
|
|
21
|
+
name: "builds"
|
|
22
|
+
path: "dist/*"
|
|
23
|
+
if-no-files-found: error
|
|
24
|
+
- name: Check tag name if we're to publish
|
|
25
|
+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
|
|
26
|
+
run: |
|
|
27
|
+
if ! [[ "v$(uv version --short)" == "${GITHUB_REF##*/}" ]]; then
|
|
28
|
+
echo "Tag does not match!"
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
pypi-publish:
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
environment:
|
|
34
|
+
name: pypi
|
|
35
|
+
url: https://pypi.org/p/generate-systemd-timer
|
|
36
|
+
needs: build
|
|
37
|
+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
|
|
38
|
+
permissions:
|
|
39
|
+
id-token: write
|
|
40
|
+
contents: write
|
|
41
|
+
steps:
|
|
42
|
+
- name: "Download build artifacts"
|
|
43
|
+
uses: actions/download-artifact@v8
|
|
44
|
+
with:
|
|
45
|
+
name: "builds"
|
|
46
|
+
path: "dist/"
|
|
47
|
+
- name: Create release on Github
|
|
48
|
+
uses: softprops/action-gh-release@v3
|
|
49
|
+
with:
|
|
50
|
+
files: |
|
|
51
|
+
dist/*
|
|
52
|
+
- name: "Publish to pypi"
|
|
53
|
+
uses: pypa/gh-action-pypi-publish@v1.14.0
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
pull_request:
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
lint:
|
|
8
|
+
name: ruff
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v6
|
|
12
|
+
- name: Install uv
|
|
13
|
+
uses: astral-sh/setup-uv@v6
|
|
14
|
+
with:
|
|
15
|
+
enable-cache: true
|
|
16
|
+
- name: Ruff lint
|
|
17
|
+
run: uvx ruff check .
|
|
18
|
+
- name: Ruff format check
|
|
19
|
+
run: uvx ruff format --check .
|
|
20
|
+
|
|
21
|
+
test:
|
|
22
|
+
name: pytest (py${{ matrix.python-version }})
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
strategy:
|
|
25
|
+
fail-fast: false
|
|
26
|
+
matrix:
|
|
27
|
+
# uv provides every interpreter, so 3.8 works on ubuntu-latest too.
|
|
28
|
+
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v6
|
|
31
|
+
- name: Install uv
|
|
32
|
+
uses: astral-sh/setup-uv@v6
|
|
33
|
+
with:
|
|
34
|
+
enable-cache: true
|
|
35
|
+
python-version: ${{ matrix.python-version }}
|
|
36
|
+
- name: Run pytest
|
|
37
|
+
run: uv run --frozen pytest
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
pip-wheel-metadata/
|
|
24
|
+
share/python-wheels/
|
|
25
|
+
*.egg-info/
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
MANIFEST
|
|
29
|
+
|
|
30
|
+
# PyInstaller
|
|
31
|
+
# Usually these files are written by a python script from a template
|
|
32
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
33
|
+
*.manifest
|
|
34
|
+
*.spec
|
|
35
|
+
|
|
36
|
+
# Installer logs
|
|
37
|
+
pip-log.txt
|
|
38
|
+
pip-delete-this-directory.txt
|
|
39
|
+
|
|
40
|
+
# Unit test / coverage reports
|
|
41
|
+
htmlcov/
|
|
42
|
+
.tox/
|
|
43
|
+
.nox/
|
|
44
|
+
.coverage
|
|
45
|
+
.coverage.*
|
|
46
|
+
.cache
|
|
47
|
+
nosetests.xml
|
|
48
|
+
coverage.xml
|
|
49
|
+
*.cover
|
|
50
|
+
*.py,cover
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
cover/
|
|
54
|
+
|
|
55
|
+
# Translations
|
|
56
|
+
*.mo
|
|
57
|
+
*.pot
|
|
58
|
+
|
|
59
|
+
# Django stuff:
|
|
60
|
+
*.log
|
|
61
|
+
local_settings.py
|
|
62
|
+
db.sqlite3
|
|
63
|
+
db.sqlite3-journal
|
|
64
|
+
|
|
65
|
+
# Flask stuff:
|
|
66
|
+
instance/
|
|
67
|
+
.webassets-cache
|
|
68
|
+
|
|
69
|
+
# Scrapy stuff:
|
|
70
|
+
.scrapy
|
|
71
|
+
|
|
72
|
+
# Sphinx documentation
|
|
73
|
+
docs/_build/
|
|
74
|
+
|
|
75
|
+
# PyBuilder
|
|
76
|
+
.pybuilder/
|
|
77
|
+
target/
|
|
78
|
+
|
|
79
|
+
# Jupyter Notebook
|
|
80
|
+
.ipynb_checkpoints
|
|
81
|
+
|
|
82
|
+
# IPython
|
|
83
|
+
profile_default/
|
|
84
|
+
ipython_config.py
|
|
85
|
+
|
|
86
|
+
# pyenv
|
|
87
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
88
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
89
|
+
# .python-version
|
|
90
|
+
|
|
91
|
+
# pipenv
|
|
92
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
93
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
94
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
95
|
+
# install all needed dependencies.
|
|
96
|
+
#Pipfile.lock
|
|
97
|
+
|
|
98
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
99
|
+
__pypackages__/
|
|
100
|
+
|
|
101
|
+
# Celery stuff
|
|
102
|
+
celerybeat-schedule
|
|
103
|
+
celerybeat.pid
|
|
104
|
+
|
|
105
|
+
# SageMath parsed files
|
|
106
|
+
*.sage.py
|
|
107
|
+
|
|
108
|
+
# Environments
|
|
109
|
+
.env
|
|
110
|
+
.venv
|
|
111
|
+
env/
|
|
112
|
+
venv/
|
|
113
|
+
ENV/
|
|
114
|
+
env.bak/
|
|
115
|
+
venv.bak/
|
|
116
|
+
|
|
117
|
+
# Spyder project settings
|
|
118
|
+
.spyderproject
|
|
119
|
+
.spyproject
|
|
120
|
+
|
|
121
|
+
# Rope project settings
|
|
122
|
+
.ropeproject
|
|
123
|
+
|
|
124
|
+
# mkdocs documentation
|
|
125
|
+
/site
|
|
126
|
+
|
|
127
|
+
# mypy
|
|
128
|
+
.mypy_cache/
|
|
129
|
+
.dmypy.json
|
|
130
|
+
dmypy.json
|
|
131
|
+
|
|
132
|
+
# Pyre type checker
|
|
133
|
+
.pyre/
|
|
134
|
+
|
|
135
|
+
# pytype static type analyzer
|
|
136
|
+
.pytype/
|
|
137
|
+
|
|
138
|
+
# Cython debug symbols
|
|
139
|
+
cython_debug/
|
|
140
|
+
|
|
141
|
+
# static files generated from Django application using `collectstatic`
|
|
142
|
+
media
|
|
143
|
+
static
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: generate-systemd-timer
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Generate a systemd unit.timer and unit.service pair
|
|
5
|
+
Project-URL: Repository, https://github.com/thomwiggers/systemd-timer-generator
|
|
6
|
+
Author-email: Thom Wiggers <thom@thomwiggers.nl>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Requires-Python: >=3.8
|
|
9
|
+
Requires-Dist: jinja2<4,>=3
|
|
10
|
+
Requires-Dist: python-editor<2,>=1.0
|
|
11
|
+
Requires-Dist: setuptools<76.0,>=70.3
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# Systemd generator for timer units
|
|
15
|
+
|
|
16
|
+
Generates systemd `.timer` and `.service` units to more easily add cron-like tasks to your system.
|
|
17
|
+
|
|
18
|
+
After editing the units, the tool can install them for you: copy them into
|
|
19
|
+
`/etc/systemd/system` or `$HOME/.config/systemd/user` (creating the directory if
|
|
20
|
+
needed), run `systemctl daemon-reload`, and enable and start the timer.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
generate-systemd-timer unit-name
|
|
26
|
+
# Now two editors will pop up to allow you to customize
|
|
27
|
+
# Afterwards you'll find unit-name.service and unit-name.timer in the current folder.
|
|
28
|
+
# Finally, you'll be asked whether to install, reload and enable the timer.
|
|
29
|
+
```
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Generates systemd `.timer` and `.service` units to more easily add cron-like tasks to your system.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
or `$HOME/.config/systemd/user`
|
|
5
|
+
After editing the units, the tool can install them for you: copy them into
|
|
6
|
+
`/etc/systemd/system` or `$HOME/.config/systemd/user` (creating the directory if
|
|
7
|
+
needed), run `systemctl daemon-reload`, and enable and start the timer.
|
|
7
8
|
|
|
8
9
|
## Usage
|
|
9
10
|
|
|
@@ -11,4 +12,5 @@ or `$HOME/.config/systemd/user`) and reload systemd using `systemctl daemon-relo
|
|
|
11
12
|
generate-systemd-timer unit-name
|
|
12
13
|
# Now two editors will pop up to allow you to customize
|
|
13
14
|
# Afterwards you'll find unit-name.service and unit-name.timer in the current folder.
|
|
15
|
+
# Finally, you'll be asked whether to install, reload and enable the timer.
|
|
14
16
|
```
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "generate-systemd-timer"
|
|
3
|
+
version = "2.0.0"
|
|
4
|
+
description = "Generate a systemd unit.timer and unit.service pair"
|
|
5
|
+
license = "MIT"
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
requires-python = ">=3.8"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Thom Wiggers", email = "thom@thomwiggers.nl" },
|
|
10
|
+
]
|
|
11
|
+
dependencies = [
|
|
12
|
+
"jinja2>=3,<4",
|
|
13
|
+
"python-editor>=1.0,<2",
|
|
14
|
+
"setuptools>=70.3,<76.0",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.urls]
|
|
18
|
+
Repository = "https://github.com/thomwiggers/systemd-timer-generator"
|
|
19
|
+
|
|
20
|
+
[project.scripts]
|
|
21
|
+
generate-systemd-timer = "systemd_generator:main"
|
|
22
|
+
|
|
23
|
+
[build-system]
|
|
24
|
+
requires = ["hatchling"]
|
|
25
|
+
build-backend = "hatchling.build"
|
|
26
|
+
|
|
27
|
+
[tool.hatch.build.targets.wheel]
|
|
28
|
+
packages = ["systemd_generator"]
|
|
29
|
+
|
|
30
|
+
[dependency-groups]
|
|
31
|
+
dev = [
|
|
32
|
+
"pytest>=8,<9",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[tool.pytest.ini_options]
|
|
36
|
+
testpaths = ["tests"]
|
|
37
|
+
addopts = "-ra"
|
|
38
|
+
|
|
39
|
+
[tool.ruff]
|
|
40
|
+
target-version = "py38"
|
|
41
|
+
|
|
42
|
+
[tool.ruff.lint]
|
|
43
|
+
select = [
|
|
44
|
+
"E", # pycodestyle errors
|
|
45
|
+
"F", # pyflakes
|
|
46
|
+
"I", # isort
|
|
47
|
+
"UP", # pyupgrade
|
|
48
|
+
"B", # flake8-bugbear
|
|
49
|
+
]
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""Generate systemd timer files"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
import editor
|
|
8
|
+
import jinja2
|
|
9
|
+
|
|
10
|
+
USER_UNIT_DIR = os.path.expanduser("~/.config/systemd/user")
|
|
11
|
+
SYSTEM_UNIT_DIR = "/etc/systemd/system"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
TEMPLATE_TIMER = """\
|
|
15
|
+
[Unit]
|
|
16
|
+
Description=Generated timer for {{ service_name }} by {{ script_name }}
|
|
17
|
+
|
|
18
|
+
[Timer]
|
|
19
|
+
#Unit={{ service_name }}.service
|
|
20
|
+
|
|
21
|
+
# Select one of the following
|
|
22
|
+
# OnCalendar: daily / weekly / monthly
|
|
23
|
+
#OnCalendar=
|
|
24
|
+
# OnActiveSec: Time after this timer has been loaded
|
|
25
|
+
#OnActiveSec=
|
|
26
|
+
# OnBootSec: Time relative to boot
|
|
27
|
+
#OnBootSec=
|
|
28
|
+
# OnStartupSec: Time relative to systemd manager start; relevant for user login
|
|
29
|
+
#OnStartupSec=
|
|
30
|
+
# OnUnitActiveSec: Defines it relative to when the to-be-started unit was last activated
|
|
31
|
+
#OnUnitActiveSec=
|
|
32
|
+
# OnUnitInactiveSec: Defines it relative to when the to-be-started unit
|
|
33
|
+
# was last deactivated
|
|
34
|
+
#OnUnitInactiveSec=
|
|
35
|
+
|
|
36
|
+
# Optional settings
|
|
37
|
+
# AccuracySec: Defines the accuracy with which this timer shall elapse
|
|
38
|
+
#AccuracySec=1m
|
|
39
|
+
# RandomizedDelaySec: Defines a randomized delay to be added to the start time
|
|
40
|
+
#RandomizedDelaySec=0
|
|
41
|
+
# Persistent: Also activate if the timer expired while timer was inactive
|
|
42
|
+
# Only relevant for OnCalendar
|
|
43
|
+
#Persistent=false
|
|
44
|
+
|
|
45
|
+
# Probably not necessary
|
|
46
|
+
# OnClockChange: Activate this unit whenever the clock jumps
|
|
47
|
+
#OnClockChange=false
|
|
48
|
+
# OnTimezoneChange: Activate whenever the timezone changes
|
|
49
|
+
#OnTimezoneChange=false
|
|
50
|
+
# WakeSystem: Resume the system from suspend to activate (if supported)
|
|
51
|
+
#WakeSystem=false
|
|
52
|
+
# RemainAfterElapse: Keeps the timer in the service manager once elapsed.
|
|
53
|
+
#RemainAfterElapse=true
|
|
54
|
+
|
|
55
|
+
[Install]
|
|
56
|
+
WantedBy=timers.target
|
|
57
|
+
|
|
58
|
+
# vim : set ft=systemd.timer :
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
TEMPLATE_SERVICE = """\
|
|
62
|
+
[Unit]
|
|
63
|
+
Description=Generated service for {{ service_name }} by {{ script_name }}
|
|
64
|
+
|
|
65
|
+
[Service]
|
|
66
|
+
Type=oneshot
|
|
67
|
+
ExecStart=/bin/true
|
|
68
|
+
|
|
69
|
+
[Install]
|
|
70
|
+
WantedBy=multi-user.target
|
|
71
|
+
|
|
72
|
+
# See https://www.freedesktop.org/software/systemd/man/systemd.service.html#Examples
|
|
73
|
+
|
|
74
|
+
# vim : set ft=systemd.service :
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _get_jinja_environment():
|
|
79
|
+
env = jinja2.Environment()
|
|
80
|
+
return env
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _render_timer(service_name):
|
|
84
|
+
"""Render the systemd.template file"""
|
|
85
|
+
script_name = sys.argv[0]
|
|
86
|
+
template = _get_jinja_environment().from_string(TEMPLATE_TIMER)
|
|
87
|
+
return template.render(
|
|
88
|
+
service_name=service_name,
|
|
89
|
+
script_name=script_name,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _render_service(service_name):
|
|
94
|
+
"""Render the systemd.service file"""
|
|
95
|
+
script_name = sys.argv[0]
|
|
96
|
+
template = _get_jinja_environment().from_string(TEMPLATE_SERVICE)
|
|
97
|
+
return template.render(
|
|
98
|
+
service_name=service_name,
|
|
99
|
+
script_name=script_name,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _prompt_yes_no(question, default=False):
|
|
104
|
+
"""Ask a yes/no question on stdin. Returns the default if non-interactive."""
|
|
105
|
+
if not sys.stdin.isatty():
|
|
106
|
+
return default
|
|
107
|
+
options = "[Y/n]" if default else "[y/N]"
|
|
108
|
+
try:
|
|
109
|
+
answer = input(f"{question} {options} ").strip().lower()
|
|
110
|
+
except EOFError:
|
|
111
|
+
return default
|
|
112
|
+
if not answer:
|
|
113
|
+
return default
|
|
114
|
+
return answer in ("y", "yes")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _run(cmd):
|
|
118
|
+
"""Echo and run a command, returning its exit code."""
|
|
119
|
+
print(f"+ {' '.join(cmd)}")
|
|
120
|
+
return subprocess.call(cmd)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _manual_instructions(service_name, target):
|
|
124
|
+
print(
|
|
125
|
+
"\nTo install manually:\n"
|
|
126
|
+
f" cp {service_name}.service {service_name}.timer {target}/\n"
|
|
127
|
+
f" systemctl daemon-reload\n"
|
|
128
|
+
f" systemctl enable --now {service_name}.timer"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _install(service_name):
|
|
133
|
+
"""Offer to copy the units into place, reload systemd and enable the timer."""
|
|
134
|
+
units = [f"{service_name}.service", f"{service_name}.timer"]
|
|
135
|
+
|
|
136
|
+
user_scope = _prompt_yes_no(
|
|
137
|
+
"Install as a user unit (instead of system-wide)?", default=True
|
|
138
|
+
)
|
|
139
|
+
if user_scope:
|
|
140
|
+
target = USER_UNIT_DIR
|
|
141
|
+
sudo = []
|
|
142
|
+
systemctl = ["systemctl", "--user"]
|
|
143
|
+
else:
|
|
144
|
+
target = SYSTEM_UNIT_DIR
|
|
145
|
+
sudo = ["sudo"]
|
|
146
|
+
systemctl = ["sudo", "systemctl"]
|
|
147
|
+
|
|
148
|
+
if not _prompt_yes_no(f"Copy units into {target} now?", default=True):
|
|
149
|
+
_manual_instructions(service_name, target)
|
|
150
|
+
return
|
|
151
|
+
|
|
152
|
+
if not os.path.isdir(target):
|
|
153
|
+
if _prompt_yes_no(
|
|
154
|
+
f"Directory {target} does not exist. Create it?", default=True
|
|
155
|
+
):
|
|
156
|
+
if _run(sudo + ["mkdir", "-p", target]) != 0:
|
|
157
|
+
print("Failed to create directory; aborting install.", file=sys.stderr)
|
|
158
|
+
return
|
|
159
|
+
else:
|
|
160
|
+
_manual_instructions(service_name, target)
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
for unit in units:
|
|
164
|
+
if _run(sudo + ["cp", unit, os.path.join(target, unit)]) != 0:
|
|
165
|
+
print(f"Failed to copy {unit}; aborting install.", file=sys.stderr)
|
|
166
|
+
return
|
|
167
|
+
|
|
168
|
+
_run(systemctl + ["daemon-reload"])
|
|
169
|
+
|
|
170
|
+
if _prompt_yes_no(f"Enable and start {service_name}.timer now?", default=True):
|
|
171
|
+
_run(systemctl + ["enable", "--now", f"{service_name}.timer"])
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def main():
|
|
175
|
+
if len(sys.argv) != 2:
|
|
176
|
+
print(f"Usage: {sys.argv[0]} <service name>", file=sys.stderr)
|
|
177
|
+
sys.exit(1)
|
|
178
|
+
|
|
179
|
+
service_name = sys.argv[1]
|
|
180
|
+
|
|
181
|
+
with open(f"{service_name}.service", "w") as f:
|
|
182
|
+
f.write(_render_service(service_name))
|
|
183
|
+
editor.edit(filename=f"{service_name}.service")
|
|
184
|
+
with open(f"{service_name}.timer", "w") as f:
|
|
185
|
+
f.write(_render_timer(service_name))
|
|
186
|
+
editor.edit(filename=f"{service_name}.timer")
|
|
187
|
+
|
|
188
|
+
if _prompt_yes_no(
|
|
189
|
+
f"\nGenerated {service_name}.service and {service_name}.timer.\n"
|
|
190
|
+
"Install them now?",
|
|
191
|
+
default=False,
|
|
192
|
+
):
|
|
193
|
+
_install(service_name)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
if __name__ == "__main__":
|
|
197
|
+
main()
|