93 lines
4.3 KiB
Python
Executable File
93 lines
4.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Starter im Benutzer-Suchpfad ohne sudo oder Änderungen an der Shell einrichten."""
|
|
import argparse
|
|
from datetime import datetime, timezone
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
import uuid
|
|
|
|
|
|
def install(bin_dir: Path) -> None:
|
|
root = Path(__file__).resolve().parent.parent
|
|
bin_dir = bin_dir.expanduser().absolute()
|
|
bin_dir.mkdir(parents=True, exist_ok=True)
|
|
changed = []
|
|
try:
|
|
for name in ('einvoice', 'jr-einvoice', 'einvoice-ui'):
|
|
source = root / 'bin' / name
|
|
target = bin_dir / name
|
|
if target.is_symlink() and target.resolve() == source:
|
|
print(f'Bereits eingerichtet: {target}', flush=True)
|
|
continue
|
|
backup = None
|
|
if target.exists() or target.is_symlink():
|
|
if target.is_dir():
|
|
raise RuntimeError(f'Ziel ist ein Verzeichnis: {target}')
|
|
stamp = datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ')
|
|
backup = target.with_name(f'{name}.before-einvoice-{stamp}-{uuid.uuid4().hex[:8]}')
|
|
if target.is_symlink():
|
|
backup.symlink_to(os.readlink(target))
|
|
else:
|
|
import shutil
|
|
shutil.copy2(target, backup)
|
|
print(f'Bisheriger Befehl gesichert: {backup}', flush=True)
|
|
temporary = target.with_name(f'.{name}-{uuid.uuid4().hex}.next')
|
|
try:
|
|
temporary.symlink_to(source)
|
|
os.replace(temporary, target)
|
|
finally:
|
|
temporary.unlink(missing_ok=True)
|
|
changed.append((target, backup))
|
|
print(f'Eingerichtet: {target} -> {source}', flush=True)
|
|
# Aus fremdem Verzeichnis die Auflösung der Konsolenstarter prüfen.
|
|
for name in ('einvoice', 'jr-einvoice'):
|
|
subprocess.run([str(bin_dir / name), '--help'], cwd=bin_dir,
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True)
|
|
print('OK: Beide Konsolenbefehle geprüft.', flush=True)
|
|
except BaseException:
|
|
for target, backup in reversed(changed):
|
|
if backup is None:
|
|
target.unlink(missing_ok=True)
|
|
else:
|
|
os.replace(backup, target)
|
|
raise
|
|
if str(bin_dir) not in os.environ.get('PATH', '').split(os.pathsep):
|
|
import shlex
|
|
print(f'Für diese Shell ausführen: export PATH={shlex.quote(str(bin_dir))}:"$PATH"')
|
|
print('Ab jetzt: einvoice --help | einvoice-ui | einvoice --update --status | jr-einvoice --help')
|
|
print('Falls die Shell noch den alten Pfad merkt: rehash (zsh) bzw. hash -r (bash).')
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='einvoice und jr-einvoice im Benutzer-Suchpfad einrichten.')
|
|
parser.add_argument('--bin-dir', type=Path, default=Path.home() / '.local/bin')
|
|
parser.add_argument('--desktop', action='store_true', help='Zusätzlich einen Eintrag im Anwendungsmenü installieren')
|
|
args = parser.parse_args()
|
|
try:
|
|
install(args.bin_dir)
|
|
if args.desktop:
|
|
root = Path(__file__).resolve().parent.parent
|
|
applications = Path(os.environ.get('XDG_DATA_HOME', Path.home() / '.local/share')) / 'applications'
|
|
applications.mkdir(parents=True, exist_ok=True)
|
|
launcher = str(root / 'bin/einvoice-ui').replace('%', '%%')
|
|
for char in ('\\', '"', '`', '$'):
|
|
launcher = launcher.replace(char, '\\' + char)
|
|
entry = applications / 'de.jr-it-services.einvoice.desktop'
|
|
entry.write_text('[Desktop Entry]\nType=Application\nName=JR eInvoice\n'
|
|
'Comment=E-Rechnungen erstellen und als PDF ansehen\n'
|
|
f'Exec="{launcher}"\nIcon={root / "assets/logo.svg"}\n'
|
|
'Terminal=false\nCategories=Office;Finance;\nStartupNotify=true\n', encoding='utf-8')
|
|
print(f'Anwendungsmenü eingerichtet: {entry}')
|
|
except (Exception, KeyboardInterrupt) as exc:
|
|
print(f'FEHLER: {exc}; geänderte Befehle wurden zurückgesetzt.', file=sys.stderr)
|
|
if isinstance(exc, subprocess.CalledProcessError) and exc.stderr:
|
|
print(exc.stderr.strip(), file=sys.stderr)
|
|
return 1
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
raise SystemExit(main())
|