36 Commits

Author SHA1 Message Date
ef052e9f66 chore(changelog): update changelog for v0.3.1
All checks were successful
Upload Assets / upload-assets (arm64, linux) (release) Successful in 29s
Upload Assets / upload-assets (amd64, linux) (release) Successful in 43s
2025-05-28 12:40:53 +00:00
c53576a700 chore(version): bump version to 0.3.1
All checks were successful
Auto Changelog & Release / detect-version-change (push) Successful in 3s
Auto Changelog & Release / changelog-only (push) Has been skipped
Auto Changelog & Release / release (push) Successful in 8s
2025-05-28 14:40:42 +02:00
3d74ec37e4 chore(changelog): update unreleased changelog 2025-05-28 12:40:16 +00:00
c4855ed3fb style(workflows): fix formatting and whitespace issues
All checks were successful
Auto Changelog & Release / detect-version-change (push) Successful in 3s
Auto Changelog & Release / release (push) Has been skipped
Auto Changelog & Release / changelog-only (push) Successful in 7s
- Standardize quotes in branch matching patterns
- Remove unnecessary trailing whitespace
- Add export of author and committer dates for consistent tags
- Ensure proper environment variable alignment
2025-05-28 14:39:42 +02:00
07ee03b6be chore(version): bump version to 0.3.0
All checks were successful
Auto Changelog & Release / detect-version-change (push) Successful in 3s
Auto Changelog & Release / changelog-only (push) Has been skipped
Auto Changelog & Release / release (push) Successful in 8s
2025-05-28 14:26:02 +02:00
fb2a62d984 docs(readme): expand CLI option descriptions for clarity
- Add detailed explanations for each CLI option in README files
- Improve consistency and formatting for better readability
- Highlight supported features and usage comprehensively
2025-05-28 14:26:02 +02:00
113103f368 feat(cli): add options for user, home, and working directory
- Add `--run-as` option to specify user for system-wide timers
- Add `--home` option to set the HOME environment variable
- Add `--cwd` option to define the working directory
- Update tests to validate new options and behavior
2025-05-28 14:26:02 +02:00
f112002249 chore(changelog): update unreleased changelog 2025-05-26 15:27:10 +00:00
0c1d8be79f chore(workflows): consolidate release sync into upload workflow
All checks were successful
Auto Changelog & Release / detect-version-change (push) Successful in 3s
Auto Changelog & Release / release (push) Has been skipped
Auto Changelog & Release / changelog-only (push) Successful in 6s
- Remove separate workflow for syncing releases to GitHub
- Integrate release sync steps into the upload-assets workflow
- Simplify workflow management and reduce duplication
2025-05-26 17:27:00 +02:00
e3caf0bba9 chore(changelog): update changelog for v0.2.3
All checks were successful
Sync Release to GitHub / sync-github (release) Successful in 6s
Upload Assets / upload-assets (amd64, linux) (release) Successful in 13s
Upload Assets / upload-assets (arm64, linux) (release) Successful in 12s
2025-05-26 15:24:44 +00:00
287cd741b4 chore(version): bump version to 0.2.3
All checks were successful
Auto Changelog & Release / detect-version-change (push) Successful in 2s
Auto Changelog & Release / changelog-only (push) Has been skipped
Auto Changelog & Release / release (push) Successful in 8s
2025-05-26 17:24:28 +02:00
8b29105686 chore(changelog): update unreleased changelog 2025-05-26 15:22:28 +00:00
27c7367ef1 feat(workflows): add GitHub release synchronization workflow
All checks were successful
Auto Changelog & Release / detect-version-change (push) Successful in 2s
Auto Changelog & Release / release (push) Has been skipped
Auto Changelog & Release / changelog-only (push) Successful in 6s
- Introduces a workflow to sync Gitea releases with GitHub
- Supports manual dispatch and reacts to release events
- Utilizes secrets for secure token management
2025-05-26 17:22:17 +02:00
f9cef4b70d chore(changelog): update unreleased changelog 2025-05-22 18:52:03 +00:00
e3a3e61bce docs(readme): update project time badge interval
All checks were successful
Auto Changelog & Release / detect-version-change (push) Successful in 29s
Auto Changelog & Release / release (push) Has been skipped
Auto Changelog & Release / changelog-only (push) Successful in 32s
- Adjusts the project time badge interval from "today" to "any"
2025-05-22 20:50:59 +02:00
bcec1f8f90 chore(changelog): update unreleased changelog 2025-05-22 11:25:35 +00:00
cf483de06b docs: add Englisch README
All checks were successful
Auto Changelog & Release / detect-version-change (push) Successful in 4s
Auto Changelog & Release / release (push) Has been skipped
Auto Changelog & Release / changelog-only (push) Successful in 6s
2025-05-22 13:25:23 +02:00
d9c9eda823 chore(changelog): update changelog for v0.2.2
All checks were successful
Upload Assets / upload-assets (amd64, linux) (release) Successful in 9s
Upload Assets / upload-assets (arm64, linux) (release) Successful in 9s
2025-05-22 08:59:11 +00:00
4737dbb60d chore(version): bump version to 0.2.2
All checks were successful
Auto Changelog & Release / detect-version-change (push) Successful in 3s
Auto Changelog & Release / changelog-only (push) Has been skipped
Auto Changelog & Release / release (push) Successful in 6s
2025-05-22 10:58:59 +02:00
f82249d10f chore(changelog): update changelog for v0.2.1
All checks were successful
Upload Assets / upload-assets (arm64, linux) (release) Successful in 11s
Upload Assets / upload-assets (amd64, linux) (release) Successful in 14s
2025-05-22 08:39:39 +00:00
d46ad2c2b4 chore(version): bump version to 0.2.1
Some checks failed
Auto Changelog & Release / detect-version-change (push) Successful in 3s
Auto Changelog & Release / changelog-only (push) Has been skipped
Auto Changelog & Release / release (push) Failing after 7s
2025-05-22 10:39:24 +02:00
7911a7ed6f chore(changelog): update unreleased changelog 2025-05-22 08:38:13 +00:00
9853f854c9 docs(readme): update installation instructions with script
All checks were successful
Auto Changelog & Release / detect-version-change (push) Successful in 3s
Auto Changelog & Release / release (push) Has been skipped
Auto Changelog & Release / changelog-only (push) Successful in 5s
- Add installation instructions using a shell script for convenience
- Highlight automatic platform detection and binary integrity verification
- Provide guidance for manual inspection and alternative installation options
2025-05-22 10:38:03 +02:00
a2c8ff0f84 chore(changelog): update unreleased changelog 2025-05-22 08:37:28 +00:00
0ca8ed94cc fix(install): enhance checksum validation with detailed comparison
All checks were successful
Auto Changelog & Release / detect-version-change (push) Successful in 4s
Auto Changelog & Release / release (push) Has been skipped
Auto Changelog & Release / changelog-only (push) Successful in 6s
- Replaces `sha256sum` with a detailed checksum comparison using OpenSSL
- Improves error messaging by displaying both expected and actual hashes
2025-05-22 10:37:15 +02:00
227a0426b5 chore(changelog): update unreleased changelog 2025-05-22 08:35:32 +00:00
20d143035e fix(install): ensure compatibility with non-bash shells
All checks were successful
Auto Changelog & Release / detect-version-change (push) Successful in 3s
Auto Changelog & Release / release (push) Has been skipped
Auto Changelog & Release / changelog-only (push) Successful in 5s
- Add conditional to enable fail-safe mode only for bash shells
2025-05-22 10:35:17 +02:00
7b5b855774 chore(changelog): update changelog for v0.2.0
All checks were successful
Upload Assets / upload-assets (arm64, linux) (release) Successful in 15s
Upload Assets / upload-assets (amd64, linux) (release) Successful in 17s
2025-05-22 08:31:49 +00:00
6fc6207da8 chore(version): update to 0.2.0
All checks were successful
Auto Changelog & Release / detect-version-change (push) Successful in 4s
Auto Changelog & Release / changelog-only (push) Has been skipped
Auto Changelog & Release / release (push) Successful in 32s
2025-05-22 10:21:55 +02:00
264b43c9a6 feat(scripts): add installation script for systemd-timer binary
- Introduces a Bash script to install the systemd-timer binary
- Detects OS and architecture to download the appropriate binary
- Verifies the binary using SHA256 checksum for security
- Supports installation with or without sudo privileges
2025-05-22 10:21:24 +02:00
118e4e5a86 feat(workflows): add matrix build and SHA256 generation for releases
- Introduce matrix strategy for building and uploading binaries
  for multiple architectures (amd64, arm64).
- Generate SHA256 checksum files for release binaries.
- Upload both binaries and their corresponding SHA256 files
  as release assets for better integrity verification.
2025-05-22 10:20:07 +02:00
01898a3a8e feat(tasks): add build tasks for amd64 and arm64 targets
- Introduces separate build tasks for amd64 and arm64 architectures
- Enables cross-compilation for better platform support
2025-05-22 10:19:02 +02:00
1a1ad66ab6 chore(changelog): update unreleased changelog 2025-05-21 07:51:29 +00:00
bd71b8ee14 fix(utils): handle file write failures with rollback
All checks were successful
Auto Changelog & Release / detect-version-change (push) Successful in 3s
Auto Changelog & Release / release (push) Has been skipped
Auto Changelog & Release / changelog-only (push) Successful in 5s
- Add error handling and rollback logic for unit file writes
- Prevent partial file writes by removing created files on failure
- Update tests to reflect new return type and error handling
2025-05-21 09:51:16 +02:00
a76417ce1d chore(changelog): update unreleased changelog 2025-05-21 07:21:11 +00:00
a288dbc140 docs(readme): add project time badge
All checks were successful
Auto Changelog & Release / detect-version-change (push) Successful in 4s
Auto Changelog & Release / release (push) Has been skipped
Auto Changelog & Release / changelog-only (push) Successful in 8s
2025-05-21 09:20:54 +02:00
14 changed files with 436 additions and 77 deletions

View File

@@ -4,7 +4,7 @@ on:
push:
branches:
- main
- '**'
- "**"
jobs:
detect-version-change:
@@ -152,7 +152,6 @@ jobs:
echo "changelog_body_path=$BODY" >> $GITHUB_OUTPUT
- name: Commit updated CHANGELOG
run: |
git add CHANGELOG.md
@@ -169,6 +168,8 @@ jobs:
if git rev-parse "v$VERSION" >/dev/null 2>&1; then
echo "Tag v$VERSION already exists, skipping tag creation."
else
export GIT_AUTHOR_DATE="$(date --iso-8601=seconds)"
export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
git tag -a "v$VERSION" -F "${{ steps.generate-changelog.outputs.changelog_body_path }}" --cleanup=verbatim
git push origin "v$VERSION"
fi

View File

@@ -1,38 +1,27 @@
# ========================
# 📦 Upload Assets Template
# ========================
# Dieser Workflow wird automatisch ausgelöst, wenn ein Release
# in Gitea veröffentlicht wurde (event: release.published).
#
# Er dient dem Zweck, Release-Artefakte (wie z. B. Binary-Dateien,
# Changelogs oder Build-Zips) nachträglich mit dem Release zu verknüpfen.
#
# Voraussetzung: Zwei Shell-Skripte liegen im Projekt:
# - .gitea/scripts/get-release-id.sh → ermittelt Release-ID per Tag
# - .gitea/scripts/upload-asset.sh → lädt Datei als Release-Asset hoch
#
# --------------------------------------
name: Upload Assets
on:
release:
types: [published] # Nur bei Veröffentlichung eines Releases (nicht bei Entwürfen)
types: [published]
jobs:
upload-assets:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- target: linux
arch: amd64
- target: linux
arch: arm64
steps:
# 📥 Checke den Stand des Repos aus, exakt auf dem veröffentlichten Tag
# So ist garantiert, dass die Artefakte dem Zustand des Releases entsprechen.
- uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name }} # z. B. "v1.2.3"
fetch-depth: 0 # vollständige Git-Historie (für z. B. git-cliff, logs etc.)
ref: ${{ github.event.release.tag_name }}
fetch-depth: 0
# 🆔 Hole die Release-ID basierend auf dem Tag
# Die ID wird als Umgebungsvariable RELEASE_ID über $GITHUB_ENV verfügbar gemacht.
- name: Get Release ID from tag
run: .gitea/scripts/get-release-id.sh "${{ github.event.release.tag_name }}"
@@ -40,8 +29,28 @@ jobs:
with:
deno-version: v2.x
- name: Build application
run: deno task build
- name: Build ${{ matrix.target }}-${{ matrix.arch }}
run: deno task build:${{ matrix.arch }}
- name: Upload CHANGELOG.md as RELEASE-NOTES.md
run: .gitea/scripts/upload-asset.sh ./dist/systemd-timer systemd-timer
- name: Generate SHA256 for ${{ matrix.target }}-${{ matrix.arch }}
run: |
FILE="./dist/systemd-timer-${{ matrix.target }}-${{ matrix.arch }}"
sha256sum "$FILE" > "$FILE.sha256"
- name: Upload binary for ${{ matrix.target }}-${{ matrix.arch }}
run: .gitea/scripts/upload-asset.sh ./dist/systemd-timer-${{ matrix.target }}-${{ matrix.arch }} systemd-timer-${{ matrix.target }}-${{ matrix.arch }}
- name: Upload SHA256 for ${{ matrix.target }}-${{ matrix.arch }}
run: .gitea/scripts/upload-asset.sh ./dist/systemd-timer-${{ matrix.target }}-${{ matrix.arch }}.sha256 systemd-timer-${{ matrix.target }}-${{ matrix.arch }}.sha256
- name: Run Releases Sync Action
uses: https://git.0xmax42.io/actions/releases-sync@main
with:
gitea_token: ${{ secrets.RELEASE_PUBLISH_TOKEN }}
gitea_url: https://git.0xmax42.io
gitea_owner: maxp
gitea_repo: systemd-timer
tag_name: ${{ github.event.release.tag_name }}
github_token: ${{ secrets.SYNC_GITHUB_TOKEN }}
github_owner: 0xmax42
github_repo: systemd-timer

View File

@@ -2,6 +2,62 @@
All notable changes to this project will be documented in this file.
## [0.3.1](https://git.0xmax42.io/maxp/systemd-timer/compare/v0.2.3..v0.3.1) - 2025-05-28
### 🚀 Features
- *(cli)* Add options for user, home, and working directory - ([113103f](https://git.0xmax42.io/maxp/systemd-timer/commit/113103f368ead3014165cc708f016a04749f59be))
### 📚 Documentation
- *(readme)* Expand CLI option descriptions for clarity - ([fb2a62d](https://git.0xmax42.io/maxp/systemd-timer/commit/fb2a62d984615caa4035fd5c1e8e64d245499e47))
### 🎨 Styling
- *(workflows)* Fix formatting and whitespace issues - ([c4855ed](https://git.0xmax42.io/maxp/systemd-timer/commit/c4855ed3fbc0ada208690f90932710983daef392))
### ⚙️ Miscellaneous Tasks
- *(workflows)* Consolidate release sync into upload workflow - ([0c1d8be](https://git.0xmax42.io/maxp/systemd-timer/commit/0c1d8be79f0cc331db9029beb46384659f465f6e))
## [0.2.3](https://git.0xmax42.io/maxp/systemd-timer/compare/v0.2.2..v0.2.3) - 2025-05-26
### 🚀 Features
- *(workflows)* Add GitHub release synchronization workflow - ([27c7367](https://git.0xmax42.io/maxp/systemd-timer/commit/27c7367ef1799428cc5a491b25036f77b65758af))
### 📚 Documentation
- *(readme)* Update project time badge interval - ([e3a3e61](https://git.0xmax42.io/maxp/systemd-timer/commit/e3a3e61bce0e62c2397bbc5bde3eff81b915c94a))
- Add Englisch README - ([cf483de](https://git.0xmax42.io/maxp/systemd-timer/commit/cf483de06b555599052b1d9f97ee98e9233e5a86))
## [0.2.2](https://git.0xmax42.io/maxp/systemd-timer/compare/v0.2.0..v0.2.2) - 2025-05-22
### 🐛 Bug Fixes
- *(install)* Enhance checksum validation with detailed comparison - ([0ca8ed9](https://git.0xmax42.io/maxp/systemd-timer/commit/0ca8ed94ccc4b9fe4ccac331957f01f852999094))
- *(install)* Ensure compatibility with non-bash shells - ([20d1430](https://git.0xmax42.io/maxp/systemd-timer/commit/20d143035ec6893f680b68dc4a2f6319ca7a5b81))
### 📚 Documentation
- *(readme)* Update installation instructions with script - ([9853f85](https://git.0xmax42.io/maxp/systemd-timer/commit/9853f854c991d87b12cd4fb5e19fce55e7246024))
## [0.2.0](https://git.0xmax42.io/maxp/systemd-timer/compare/v0.1.0..v0.2.0) - 2025-05-22
### 🚀 Features
- *(scripts)* Add installation script for systemd-timer binary - ([264b43c](https://git.0xmax42.io/maxp/systemd-timer/commit/264b43c9a667d344e27cca4ac2f17d7a4a25bffc))
- *(workflows)* Add matrix build and SHA256 generation for releases - ([118e4e5](https://git.0xmax42.io/maxp/systemd-timer/commit/118e4e5a867a42c0d79efcc3b2a4db188affedec))
- *(tasks)* Add build tasks for amd64 and arm64 targets - ([01898a3](https://git.0xmax42.io/maxp/systemd-timer/commit/01898a3a8e094dfbbf981ab6f1cf38d52f60ef5d))
### 🐛 Bug Fixes
- *(utils)* Handle file write failures with rollback - ([bd71b8e](https://git.0xmax42.io/maxp/systemd-timer/commit/bd71b8ee14a1856f1adaaaea198c8467b1a00d24))
### 📚 Documentation
- *(readme)* Add project time badge - ([a288dbc](https://git.0xmax42.io/maxp/systemd-timer/commit/a288dbc140fefbc46745f70cdcd71148802fdabf))
## [0.1.0] - 2025-05-21
### 🚀 Features

107
README.DE.md Normal file
View File

@@ -0,0 +1,107 @@
# systemd-timer
![Project time](https://waka.0xmax42.io/api/badge/0XMax42/interval:any/project:systemd-timer?label=Project%20time)
Ein einfaches CLI-Tool zum schnellen Erzeugen von systemd `.service` und `.timer` Units – als Ersatz oder moderne Ergänzung zu klassischen `cron`-Jobs.
---
## 🚀 Features
- Erzeugt `.service` und `.timer` Dateien per CLI
- Unterstützt `--user` Timer (für `~/.config/systemd/user/`)
- Optionales Logging (`StandardOutput/StandardError`)
- Unterstützt:
- `--calendar`: Zeitplan für den Timer (systemd `OnCalendar`)
- `--exec`: Auszuführendes Kommando (`ExecStart`)
- `--description`: Beschreibung für die Unit
- `--after`: `After=`-Abhängigkeiten in der Service-Unit
- `--environment`: Beliebige `Environment=KEY=VALUE` Einträge
- `--output`: Zielverzeichnis für die generierten Unit-Dateien
- `--run-as`: Setzt `User=` in der Service-Unit (nur systemweite Timer)
- `--home`: Setzt `Environment=HOME=…`
- `--cwd`: Arbeitsverzeichnis des Prozesses (`WorkingDirectory`)
- `--dry-run`: Gibt nur die generierten Inhalte aus, ohne sie zu schreiben
- Getestet und typisiert mit **Deno** + **Cliffy**
---
## 🛠️ Installation
Du kannst `systemd-timer` direkt per Shell-Skript installieren:
```bash
curl -fsSL https://git.0xmax42.io/maxp/systemd-timer/raw/branch/main/scripts/install.sh | sh
```
Das Skript erkennt automatisch deine Plattform (Linux `amd64` oder `arm64`) und installiert die passende Binary nach `/usr/local/bin`, sofern dies erlaubt ist (ggf. mit `sudo`).
**Hinweis:**
- Für die Installation ist eine funktionierende Internetverbindung notwendig.
- Die Integrität der Binary wird mittels SHA256-Prüfsumme verifiziert.
- Du kannst das Skript vor der Ausführung auch manuell inspizieren:
```bash
curl -fsSL https://git.0xmax42.io/maxp/systemd-timer/raw/branch/main/scripts/install.sh -o install.sh
less install.sh
```
Weitere Optionen und manuelle Installationswege findest du unter [`scripts/install.sh`](scripts/install.sh).
---
## 📦 Beispiel
```bash
./systemd-timer create \
--exec "/usr/local/bin/backup.sh" \
--calendar "Mon..Fri 02:00" \
--description "Backup Job" \
--user \
--logfile "/var/log/backup.log"
```
Erzeugt:
- `~/.config/systemd/user/backup.service`
- `~/.config/systemd/user/backup.timer`
Anschließend aktivieren:
```bash
systemctl --user daemon-reload
systemctl --user enable --now backup.timer
```
---
## 🧪 Tests ausführen
```bash
deno task test
```
---
## 🧰 Entwickeln
```bash
deno task start create --exec "/bin/true" --calendar "daily" --dry-run
```
---
## 🔒 Rechte / Flags
Das Tool benötigt beim Ausführen bzw. Kompilieren:
- `--allow-env` (für `$HOME`)
- `--allow-write` (zum Schreiben von `.service`/`.timer`)
Beim Entwickeln wird meist `-A` (allow all) verwendet.
---
## 📝 Lizenz
[MIT License](LICENSE)

View File

@@ -1,39 +1,58 @@
# systemd-timer
Ein einfaches CLI-Tool zum schnellen Erzeugen von systemd `.service` und `.timer` Units – als Ersatz oder moderne Ergänzung zu klassischen `cron`-Jobs.
- ![Project time](https://waka.0xmax42.io/api/badge/0XMax42/interval:any/project:systemd-timer?label=Project%20time)
- [Deutsche Version dieser Readme](README.DE.md)
A simple CLI tool for quickly generating systemd `.service` and `.timer` units — as a replacement or modern supplement to classic `cron` jobs.
---
## 🚀 Features
- Erzeugt `.service` und `.timer` Dateien per CLI
- Unterstützt `--user` Timer (für `~/.config/systemd/user/`)
- Optionales Logging (`StandardOutput/StandardError`)
- Unterstützt:
- `--calendar`
- `--exec`
- `--after`
- `--environment`
- `--output`
- `--dry-run`
- Getestet und typisiert mit Deno + Cliffy
* Generates `.service` and `.timer` files via CLI
* Supports `--user` timers (for `~/.config/systemd/user/`)
* Optional logging (`StandardOutput/StandardError`)
* Supports:
* `--calendar`: Timer schedule (systemd `OnCalendar`)
* `--exec`: Command to execute (`ExecStart`)
* `--description`: Description for the unit
* `--after`: `After=` dependencies in the service unit
* `--environment`: Arbitrary `Environment=KEY=VALUE` entries
* `--output`: Target directory for the generated unit files
* `--run-as`: Sets `User=` in the service unit (only for system-level timers)
* `--home`: Sets `Environment=HOME=…`
* `--cwd`: Working directory for the process (`WorkingDirectory`)
* `--dry-run`: Outputs unit content without writing to disk
* Tested and fully typed with **Deno** + **Cliffy**
---
## 🛠️ Installation
```bash
git clone https://git.0xmax42.io/maxp/systemd-timer.git
cd systemd-timer
deno task build
You can install `systemd-timer` directly via shell script:
# Binary liegt nun unter ./systemd-timer
./systemd-timer --help
```bash
curl -fsSL https://git.0xmax42.io/maxp/systemd-timer/raw/branch/main/scripts/install.sh | sh
```
The script automatically detects your platform (Linux `amd64` or `arm64`) and installs the appropriate binary to `/usr/local/bin`, if permitted (possibly using `sudo`).
**Note:**
* A working internet connection is required for installation.
* The integrity of the binary is verified using a SHA256 checksum.
* You can manually inspect the script before execution:
```bash
curl -fsSL https://git.0xmax42.io/maxp/systemd-timer/raw/branch/main/scripts/install.sh -o install.sh
less install.sh
```
Additional options and manual installation methods are available under [`scripts/install.sh`](scripts/install.sh).
---
## 📦 Beispiel
## 📦 Example
```bash
./systemd-timer create \
@@ -44,11 +63,12 @@ deno task build
--logfile "/var/log/backup.log"
```
Erzeugt:
- `~/.config/systemd/user/backup.service`
- `~/.config/systemd/user/backup.timer`
This creates:
Anschließend aktivieren:
* `~/.config/systemd/user/backup.service`
* `~/.config/systemd/user/backup.timer`
Activate afterwards:
```bash
systemctl --user daemon-reload
@@ -57,7 +77,7 @@ systemctl --user enable --now backup.timer
---
## 🧪 Tests ausführen
## 🧪 Running Tests
```bash
deno task test
@@ -65,7 +85,7 @@ deno task test
---
## 🧰 Entwickeln
## 🧰 Development
```bash
deno task start create --exec "/bin/true" --calendar "daily" --dry-run
@@ -73,17 +93,17 @@ deno task start create --exec "/bin/true" --calendar "daily" --dry-run
---
## 🔒 Rechte / Flags
## 🔒 Permissions / Flags
Das Tool benötigt beim Ausführen bzw. Kompilieren:
The tool requires the following permissions when running or compiling:
- `--allow-env` (für `$HOME`)
- `--allow-write` (zum Schreiben von `.service`/`.timer`)
* `--allow-env` (for `$HOME`)
* `--allow-write` (to write `.service`/`.timer` files)
Beim Entwickeln wird meist `-A` (allow all) verwendet.
During development, usually `-A` (allow all) is used.
---
## 📝 Lizenz
## 📝 License
[MIT License](LICENSE)

View File

@@ -1 +1 @@
0.1.0
0.3.1

View File

@@ -2,7 +2,8 @@
"tasks": {
"start": "deno run -A src/mod.ts",
"test": "deno test -A --coverage **/__tests__/*.test.ts",
"build": "deno compile --include=VERSION --allow-env --allow-write --output dist/systemd-timer src/mod.ts"
"build:amd64": "deno compile --target x86_64-unknown-linux-gnu --include=VERSION --allow-env --allow-write --output dist/systemd-timer-linux-amd64 src/mod.ts",
"build:arm64": "deno compile --target aarch64-unknown-linux-gnu --include=VERSION --allow-env --allow-write --output dist/systemd-timer-linux-arm64 src/mod.ts"
},
"compilerOptions": {},
"fmt": {

63
scripts/install.sh Normal file
View File

@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# Fail-safe bash mode (only if bash is used)
if [ -n "$BASH_VERSION" ]; then
set -euo pipefail
else
set -eu
fi
# === Konfiguration ===
REPO_URL="https://git.0xmax42.io/maxp/systemd-timer/releases/download/latest"
BINARY_NAME="systemd-timer"
INSTALL_PATH="/usr/local/bin"
# === Systemarchitektur erkennen ===
ARCH=$(uname -m)
case "$ARCH" in
x86_64) ARCH="amd64" ;;
aarch64 | arm64) ARCH="arm64" ;;
*) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;;
esac
OS=$(uname -s)
case "$OS" in
Linux) OS="linux" ;;
*) echo "Unsupported OS: $OS" >&2; exit 1 ;;
esac
# === Download-URL zusammensetzen ===
BINARY_FILE="${BINARY_NAME}-${OS}-${ARCH}"
DOWNLOAD_URL="${REPO_URL}/${BINARY_FILE}"
echo "📦 Installing ${BINARY_NAME} for ${OS}/${ARCH}..."
echo "🌐 Downloading from: ${DOWNLOAD_URL}"
# === Binary herunterladen ===
TMP_FILE=$(mktemp)
curl -fsSL "${DOWNLOAD_URL}" -o "${TMP_FILE}"
chmod +x "${TMP_FILE}"
# === SHA256-Check ===
TMP_HASH=$(mktemp)
curl -fsSL "${DOWNLOAD_URL}.sha256" -o "$TMP_HASH"
EXPECTED_HASH=$(cut -d ' ' -f1 "$TMP_HASH")
ACTUAL_HASH=$(openssl dgst -sha256 "$TMP_FILE" | awk '{print $2}')
if [ "$EXPECTED_HASH" != "$ACTUAL_HASH" ]; then
echo "⚠️ Checksum mismatch!"
echo "Expected: $EXPECTED_HASH"
echo "Actual: $ACTUAL_HASH"
exit 1
fi
# === Installation ===
echo "🚀 Installing to ${INSTALL_PATH}/${BINARY_NAME}"
if [ -w "$INSTALL_PATH" ]; then
install -m 755 "${TMP_FILE}" "${INSTALL_PATH}/${BINARY_NAME}"
else
sudo install -m 755 "${TMP_FILE}" "${INSTALL_PATH}/${BINARY_NAME}"
fi
echo "✅ Installation complete: $(command -v ${BINARY_NAME})"
"${BINARY_NAME}" --version || true

View File

@@ -18,6 +18,18 @@ export const createCommand = new Command()
})
.option('--description <desc:string>', 'Beschreibung des Timers')
.option('--user', 'Erstellt die Unit als User-Timer')
.option(
'--run-as <user:string>',
'Führe den systemweiten Timer als bestimmter Benutzer aus (setzt User= in der Service-Unit)',
)
.option(
'--home <path:string>',
'HOME-Variable für den Service setzen',
)
.option(
'--cwd <path:string>',
'Arbeitsverzeichnis (WorkingDirectory) für den Service-Prozess',
)
.option('--output <dir:string>', 'Zielverzeichnis der Unit-Dateien')
.option(
'--after <target:string>',

View File

@@ -1,4 +1,5 @@
import {
assert,
assertStringIncludes,
} from 'https://deno.land/std@0.224.0/assert/mod.ts';
import { TimerOptions } from '../../types/mod.ts';
@@ -53,3 +54,63 @@ Deno.test('generateUnits berücksichtigt environment und logfile', () => {
assertStringIncludes(serviceUnit, 'StandardOutput=append:/var/log/job.log');
assertStringIncludes(serviceUnit, 'StandardError=append:/var/log/job.log');
});
Deno.test('generateUnits berücksichtigt runAs', () => {
const opts: TimerOptions = {
exec: '/bin/true',
calendar: 'daily',
runAs: 'myuser',
};
const { serviceUnit } = generateUnits('job', opts);
assertStringIncludes(serviceUnit, 'User=myuser');
});
Deno.test('generateUnits berücksichtigt home', () => {
const opts: TimerOptions = {
exec: '/bin/true',
calendar: 'daily',
home: '/home/myuser',
};
const { serviceUnit } = generateUnits('job', opts);
assertStringIncludes(serviceUnit, 'Environment=HOME=/home/myuser');
});
Deno.test('generateUnits berücksichtigt cwd', () => {
const opts: TimerOptions = {
exec: '/bin/true',
calendar: 'daily',
cwd: '/srv/app',
};
const { serviceUnit } = generateUnits('job', opts);
assertStringIncludes(serviceUnit, 'WorkingDirectory=/srv/app');
});
Deno.test('generateUnits verwendet default.target bei User-Timern', () => {
const opts: TimerOptions = {
exec: '/bin/true',
calendar: 'daily',
user: true,
};
const { timerUnit } = generateUnits('job', opts);
assertStringIncludes(timerUnit, 'WantedBy=default.target');
});
Deno.test('generateUnits ignoriert runAs bei --user', () => {
const opts = {
exec: '/bin/true',
calendar: 'daily',
user: true,
runAs: 'should-not-appear',
};
const { serviceUnit } = generateUnits('job', opts);
assert(
!serviceUnit.includes('User=should-not-appear'),
'User= sollte bei --user nicht enthalten sein',
);
});

View File

@@ -12,14 +12,20 @@ export async function generateUnitFiles(options: TimerOptions): Promise<void> {
console.log(`\n===== ${name}.timer =====`);
console.log(timerUnit);
} else {
const { servicePath, timerPath } = await writeUnitFiles(
const result = await writeUnitFiles(
name,
serviceUnit,
timerUnit,
options,
);
console.log(`Service unit written to: ${servicePath}`);
console.log(`Timer unit written to: ${timerPath}`);
if (result) {
const { servicePath, timerPath } = result;
console.log(`Service Unit geschrieben in: ${servicePath}`);
console.log(`Timer Unit geschrieben in: ${timerPath}`);
} else {
return;
}
console.log(`\nℹ️ Hinweis:`);
@@ -45,14 +51,18 @@ export function generateUnits(name: string, options: TimerOptions): {
`[Service]`,
`Type=oneshot`,
`ExecStart=${options.exec}`,
...(options.cwd ? [`WorkingDirectory=${options.cwd}`] : []),
...(options.environment?.map((e) => `Environment=${e}`) ?? []),
...(options.home ? [`Environment=HOME=${options.home}`] : []),
...(options.logfile
? [
`StandardOutput=append:${options.logfile}`,
`StandardError=append:${options.logfile}`,
]
: []),
...(options.runAs && !options.user ? [`User=${options.runAs}`] : []),
];
if (options.logfile) {
unitParts.push(`StandardOutput=append:${options.logfile}`);
unitParts.push(`StandardError=append:${options.logfile}`);
}
const serviceUnit = unitParts.join('\n');
const timerParts = [
@@ -64,7 +74,7 @@ export function generateUnits(name: string, options: TimerOptions): {
`Persistent=true`,
``,
`[Install]`,
`WantedBy=timers.target`,
`WantedBy=${options.user ? 'default.target' : 'timers.target'}`,
];
const timerUnit = timerParts.join('\n');

View File

@@ -4,6 +4,9 @@ export interface TimerOptions {
calendar: string;
description?: string;
user?: boolean;
runAs?: string;
home?: string;
cwd?: string;
output?: string;
after?: string[];
environment?: string[];

View File

@@ -24,7 +24,7 @@ Deno.test('writeUnitFiles schreibt .service und .timer korrekt', async () => {
serviceContent,
timerContent,
options as TimerOptions,
);
) as { servicePath: string; timerPath: string };
// Überprüfe Pfade
assertEquals(servicePath, join(tmp, 'test-backup.service'));

View File

@@ -1,4 +1,4 @@
import { ensureDir } from 'https://deno.land/std@0.224.0/fs/mod.ts';
import { ensureDir, exists } from 'https://deno.land/std@0.224.0/fs/mod.ts';
import { join } from 'https://deno.land/std@0.224.0/path/mod.ts';
import { TimerOptions } from '../types/mod.ts';
@@ -7,7 +7,7 @@ export async function writeUnitFiles(
serviceContent: string,
timerContent: string,
options: TimerOptions,
): Promise<{ servicePath: string; timerPath: string }> {
): Promise<{ servicePath: string; timerPath: string } | undefined> {
const basePath = resolveUnitTargetPath(options);
await ensureDir(basePath);
@@ -15,8 +15,24 @@ export async function writeUnitFiles(
const servicePath = join(basePath, `${name}.service`);
const timerPath = join(basePath, `${name}.timer`);
await Deno.writeTextFile(servicePath, serviceContent);
await Deno.writeTextFile(timerPath, timerContent);
try {
await Deno.writeTextFile(servicePath, serviceContent);
await Deno.writeTextFile(timerPath, timerContent);
} catch (error) {
// Rollback: Remove any files that were written
try {
if (await exists(servicePath)) {
await Deno.remove(servicePath);
}
if (await exists(timerPath)) {
await Deno.remove(timerPath);
}
} catch (rollbackError) {
console.error('Rollback fehlgeschlagen:', rollbackError);
}
console.error('Fehler beim Schreiben der Units:', error);
return undefined;
}
return { servicePath, timerPath };
}