summaryrefslogtreecommitdiff
path: root/src/frontend/cli/sisyphus-cli.py
blob: 4e7d85694729fa6436d903f6724304f3bec12512 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
#!/usr/bin/python3

import sisyphus
import typer
from typing import List
from enum import Enum
import sys

app = typer.Typer()
mirrorSetup = typer.Typer()
app.add_typer(mirrorSetup, name="mirror",
              help='List/Set the active binhost (binary repository) mirror.')


@app.callback()
def app_callback(ctx: typer.Context):
    """
    Sisyphus is a simple python wrapper around portage, gentoolkit, and portage-utils\n
    which provides an apt-get/yum-alike interface to these commands, to assist newcomers\n
    transitioning from Debian/RedHat-based systems to Gentoo.\n

    Use 'sisyphus COMMAND --help' for detailed usage.
    """
    ctx.info_name = 'sisyphus'


class Filter(str, Enum):
    all = 'all'
    alien = 'alien'
    installed = 'installed'
    available = 'available'
    upgradable = 'upgradable'


@app.command("search")
def search(package: List[str] = typer.Argument(...),
           desc: str = typer.Option(
               '', '--description', '-d', help='Match description.'),
           filter: Filter = typer.Option(
               Filter.all, '--filter', '-f', show_default=True),
           quiet: bool = typer.Option(
               False, '-q', help='Short (one line) output.'),
           ebuild: bool = typer.Option(False, "--ebuild", "-e", help='Search in ebuilds (slower).')):
    """Search for binary and/or ebuild (source) packages.\n
    By default will search for binary packages, using internal database.\n
    The search term can be provided also in the category/name format.\n
    \n
    * Examples:\n
        sisyphus search openbox\n
        sisyphus search x11-wm/openbox\n
    \n
    Using * and ? wildcards is supported. An empty string will match everything (similar to *).\n
    \n
    * Examples:\n
        sisyphus search x11-wm/     # search all packages in the x11-wm category\n
        sisyphus search x11-wm/*    # search all packages in the x11-wm category\n
    \n
    In addition, search can be performed by package description, using the -d (--description) option:\n
    \n
    * Examples:\n
        sisyphus search x11/open -d 'window manager'    # use single or double quotes when the description contains spaces\n
    \n
    Use the -f (--filter) option to select only packages of interest. Possible values:\n
    \n
        all (default)   - search the entire database\n
        alien           - search for installed packages but not available     # (this filter can match packages installed from ebuilds or packages no longer maintained as binaries)\n
        installed       - search in all installed packages\n
        available       - search for available packages but not installed\n
        upgradable      - search for installed packages where installed version is different from available version\n
    \n
    !!! NOTE !!!\n
    \n
    Bash will expand a single * character as current folder listing.\n
    To search for all matching '--filter' packages you need to escape it, surround it with quotes, or just use an empty string.\n
    \n
    * Examples:\n
        sisyphus search * -f installed          # not valid\n
        sisyphus search \* -f alien             # valid\n
        sisyphus search '*' -f available        # valid\n
        sisyphus search '' -f upgradable        # valid\n
    \n
    To search for all (including source) packages, use the --ebuild option.\n
    This is slower since will perform an 'emerge --search' actually.\n
    With this option, more than one package can be provided as search term.\n
    '-d', '-f' and '-q' (quiet) options are ignored in this mode.\n
    """
    if not ebuild:
        if '/' in package[0]:
            cat, pn = package[0].split('/')
        else:
            cat, pn = '', package[0]
        sisyphus.searchdb.start(filter.value, cat, pn, desc, quiet)
    else:
        if not package:
            raise typer.Exit(
                'No search term provided, try: sisyphus search --help')
        else:
            sisyphus.searchdb.estart(package)


@app.command("install")
def install(pkgname: List[str],
            ebuild: bool = typer.Option(
                False, "--ebuild", "-e", help='Install ebuild(source) package if binary package is not found (slower).'),
            oneshot: bool = typer.Option(
                False, "--oneshot", "-1", help='Install the package without marking it as explicitly installed.'),
            nodeps: bool = typer.Option(False, "--nodeps", help='Install the package without retrieving its dependencies.')):
    """
    Install binary and/or ebuild(source) packages.\n
    Binary packages are default, however the --ebuild option can be used to install ebuild(source) packages.\n
    The --ebuild option will be recommended automatically if a binary package is not found but a corresponding ebuild (source) package is available.\n
    The --ebuild option will revert to binary packages when installation from an ebuild (source) package is unnecessary. This option can be safely used at all times.\n
    The --ebuild option will prioritize the use of binary packages (when available) to fulfill dependencies for the ebuild (source) package, thereby accelerating the installation process.\n
    The --oneshot option adheres to the aforementioned rules but does not designate the package as explicitly installed. In Gentoo Linux terminology, this means it is not added to the world set.\n
    The --nodeps option adheres to the aforementioned rules but does not retrieve the package dependecies. In conjunction with the --oneshot option, it can be used to quickly reinstall any already installed *BINARY* package.\n
    The options --ebuild, --oneshot, and --nodeps can be utilized independently or in conjunction with one another, in either their short or long forms.\n
    The sequence of utilizing the --ebuild, --oneshot, and --nodeps options is flexible; they can be applied before or after specifying the package name.\n
    \n
    !!! NOTE !!!\n
    \n
    To minimize the risk of inadvertent input or typographical errors, the --nodeps option is intentionally without a shortened form.\n
    \n
    !!! WARNING !!!\n
    \n
    Unless all required dependencies are already installed, attempting to install an ebuild (source) package using the --ebuild option in conjunction with the --nodeps option is likely to result in failure.\n
    \n
    * Examples:\n
        sisyphus install firefox\n
        sisyphus install pidgin --ebuild\n
        sisyphus install -e xonotic\n
        sisyphus install filezilla --oneshot\n
        sisyphus install -1 thunderbird\n
        sisyphus install falkon -e -1\n
        sisyphus install --nodeps --oneshot opera\n
        sisyphus install -e --nodeps vivaldi\n
    """
    if ebuild:
        sisyphus.pkgadd.start(
            pkgname, ebuild=True, gfx_ui=False, oneshot=oneshot, nodeps=nodeps)
    else:
        sisyphus.pkgadd.start(
            pkgname, ebuild=False, gfx_ui=False, oneshot=oneshot, nodeps=nodeps)


@app.command("uninstall")
def uninstall(pkgname: List[str], force: bool = typer.Option(False, "--force", "-f", help='Ignore the reverse dependencies and force uninstall the package (DANGEROUS)')):
    """
    Uninstall packages *SAFELY* by checking for reverse dependencies.\n
    To maintain system integrity, if there are any reverse dependencies, the package or packages will not be uninstalled to prevent system instability.\n
    If you're set on uninstalling the package or packages, consider removing all of its reverse dependencies first.\n
    However, this may not always be feasible due to the extensive nature of the reverse dependency tree, which could involve critical system packages.\n
    \n
    !!! DANGEROUS !!!\n
    \n
    The --force option will ignore the reverse dependencies and uninstall the package *UNSAFELY*, without safeguards.\n
    \n
    !!! WARNING !!!\n
    \n
    The --force option will break your system if you uninstall important system packages (bootloader, kernel, init).\n
    \n
    * Examples:\n
        sisyphus uninstall firefox              # this will succeed, no package depends on firefox\n
        sisyphus uninstall pulseaudio           # this will fail, many packages depend on pulseaudio\n
        sisyphus uninstall pulseaudio --force   # this will succeed, but the sound may no longer work\n
        sisyphus uninstall openrc -f            # this will succeed, but the system will no longer boot\n
    """
    if force:
        sisyphus.pkgremove.start(
            pkgname, depclean=False, gfx_ui=False, unmerge=True)
    else:
        sisyphus.pkgremove.start(
            pkgname, depclean=True, gfx_ui=False, unmerge=False)


@app.command("autoremove")
def autoremove():
    """
    Uninstall packages which become orphans and which are no longer needed.\n
    Uninstalling a package will usually leave its dependencies behind.\n
    Those dependencies become orphans if no other package requires them.\n
    A package may also gain extra dependencies or lose some dependencies.\n
    The lost dependencies become orphans if no other package requires them.\n
    In either case, the orphan packages are no longer needed and can be safely removed.\n
    Use this option to check the whole dependency tree for orphan packages, and remove them.\n
    \n
    * Examples:\n
        sisyphus autoremove\n
    """
    sisyphus.sysclean.start(gfx_ui=False)


@app.command("autoclean")
def autoclean():
    """
    Clean the binary package cache and the source tarball cache.\n
    \n
    * Examples:\n
        sisyphus autoclean\n
    """
    if sisyphus.checkenv.root():
        sisyphus.purgeenv.cache()
    else:
        sys.exit("\nYou need root permissions to do this, exiting!\n")


@app.command("update")
def update():
    """
    Update the source trees, package configs (USE flags, keywords, masks, etc) and the binary package database.\n
    \n
    * Examples:\n
        sisyphus update\n
    """
    if sisyphus.checkenv.root():
        sisyphus.syncall.start(gfx_ui=False)
    else:
        sys.exit("\nYou need root permissions to do this, exiting!\n")


@app.command("upgrade")
def upgrade(
        ebuild: bool = typer.Option(False, "--ebuild", "-e", help='Upgrade all packages, including ebuild(source) packages (slower)')):
    """
    Upgrade the system using binary and/or ebuild(source) packages.\n
    Binary packages are default, however the --ebuild option can be used to upgrade the ebuild(source) packages as well, alongside the binary packages.\n
    The --ebuild option will be automatically recommended if upgrades are necessary for the ebuild (source) packages, in addition to the binary packages.\n
    The --ebuild option will default to binary packages if upgrades are unnecessary for the ebuild (source) packages. This option is safe to use at all times.\n
    The --ebuild option will prioritize the use of binary packages (when available) to fulfill dependencies for the ebuild (source) packages, thereby accelerating the upgrade process.\n
    \n
    * Examples:\n
        sisyphus upgrade\n
        sisyphus upgrade --ebuild\n
        sisyphus upgrade -e\n
    """
    if ebuild:
        sisyphus.sysupgrade.start(ebuild=True, gfx_ui=False)
    else:
        sisyphus.sysupgrade.start(ebuild=False, gfx_ui=False)


@app.command("spmsync")
def spmsync():
    """
    Sync Sisyphus's package database with Portage's package database.\n
    Sisyphus doesn't monitor packages installed directly via Portage in its package database.\n
    Use this command to align Sisyphus's package database with Portage's package database.\n
    \n
    * Examples:\n
        sisyphus spmsync\n
    """
    sisyphus.syncspm.start()


@app.command("rescue")
def rescue():
    """
    Resurrect Sisyphus's package database if it becomes lost or corrupted.\n
    If, for any reason, Sisyphus's package database becomes lost or corrupted, it can be resurrected using Portage's package database.\n
    In the event of corruption in Portage's package database (in which case, you're in trouble anyway :D), only partial resurrection will be feasible.\n
    With Portage's package database intact, complete resurrection will be achievable.\n
    \n
    * Examples:\n
        sisyphus rescue\n
    """
    sisyphus.recoverdb.start()


class Branch(str, Enum):
    master = 'master'
    next = 'next'


class Remote(str, Enum):
    github = 'github'
    gitlab = 'gitlab'
    pagure = 'pagure'


@app.command("branch")
def branch(branch: Branch = typer.Argument(...), remote: Remote = typer.Option(Remote.gitlab, "--remote", "-r")):
    """
    Switch between the branches of Redcore Linux : 'master' (stable) or 'next' (testing), default branch is 'master' (stable)\n
    Reconfigure the source trees, package configs (USE flags, keywords, masks, etc) and the binhost (binary repository)\n
    Selection of a remote is optional (default is gitlab), but it can be accomplished by using the --remote option.\n
    'BRANCH' can be one of the following : master, next\n
    'REMOTE' can be one of the following : github, gitlab, pagure (default is gitlab)\n
    \n
    * Examples:\n
        sisyphus branch master                  # switch to branch 'master', use default remote (gitlab)\n
        sisyphus branch next                    # switch to branch 'next', use default remote (gitlab)\n
        sisyphus branch master --remote=github  # switch to branch 'master', use github remote\n
        sisyphus branch next --remote=pagure    # switch to branch 'next', use pagure remote\n
    \n
    Sisyphus will automatically pair the selected branch with the correct binhost (binary repository).\n
    However, since no geolocation is ever used, it may select one which is geographically far from you.\n
    If that is inconvenient, you can manually select a binhost (binary repository) closer to your location,\n
    \n
    !!! WARNING !!!\n
    \n
    Branch 'master' must be paired with the stable binhost (binary repository) (odd numbers in 'sisyphus mirror list').\n
    * Examples:\n
        sisyphus mirror set 1\n
        sisyphus mirror set 5\n
    \n
    Branch 'next' must be paired with the testing binhost (binary repository) (even numbers in 'sisyphus mirror list').\n
    * Examples:\n
        sisyphus mirror set 2\n
        sisyphus mirror set 8\n
    """
    sisyphus.setbranch.start(branch.value, remote.value)


@app.command("sysinfo")
def sysinfo():
    """
    Display information about installed core packages and portage configuration.\n
    \n
    * Examples:\n
        sisyphus sysinfo\n
    """
    sisyphus.sysinfo.show()


@mirrorSetup.command("list")
def mirrorlist():
    """
    List available binary package repository mirrors (the active one is marked with *).\n
    \n
    * Examples:\n
        sisyphus mirror list\n
    """
    sisyphus.setmirror.printList()


@mirrorSetup.command("set")
def mirrorset(index: int):
    """
    Change the binary package repository to the selected mirror.\n
    \n
    * Examples:\n
        sisyphus mirror set 2\n
        sisyphus mirror set 5\n
    """
    sisyphus.setmirror.setActive(index)


if __name__ == "__main__":
    if len(sys.argv) > 1 and not '--help' in sys.argv:
        sisyphus.setjobs.start()
    app()