summaryrefslogtreecommitdiff
path: root/src/frontend/cli/sisyphus-cli.py
blob: 93928c462462a255a397ad388cb82ef9c836ed80 (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
#!/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.search.start(filter.value, cat, pn, desc, quiet)
    else:
        if not package:
            raise typer.Exit(
                'No search term provided, try: sisyphus search --help')
        else:
            sisyphus.search.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='Do not mark the package as explicitly installed')):
    """
    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 automatically suggested if a binary package is not found, but an ebuild(source) package is found.\n
    The --ebuild option will fall back to binary packages if installation from ebuild(source) package is not required. (Can be used at all times, *SAFELY*).\n
    The --ebuild option will prefer to use binary packages (if available) to satisfy dependencies for the ebuild(source) package, speeding up the installation.\n
    The --oneshot option follows the above rules, however it will not mark the package as explicitly installed. (In Gentoo Linux terms: not added to world set).\n
    The --ebuild and the --oneshot options can be used both independently of each other and/or combined with each other.\n
    \n
    * Examples:\n
        sisyphus install firefox\n
        sisyphus install pidgin --ebuild\n
        sisyphus install xonotic -e\n
        sisyphus install filezilla --oneshot\n
        sisyphus install thunderbird -1\n
        sisyphus install falkon -e -1\n
        sisyphus install opera --ebuild --oneshot\n
        sisyphus install vivaldi --oneshot --ebuild\n
    """
    if ebuild:
        sisyphus.install.start(pkgname, ebuild=True,
                               gfx_ui=False, oneshot=oneshot)
    else:
        sisyphus.install.start(pkgname, ebuild=False,
                               gfx_ui=False, oneshot=oneshot)


@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
    If there are any reverse dependencies, the package or packages will NOT be uninstalled to prevent the system from breaking.\n
    If you are serious about uninstalling the package, it is recommended you uninstall all the reverse dependencies of it as well.\n
    This will not always be possible because the reverse dependency tree may be too long and you may need to uninstall important system packages.\n
    DANGEROUS : The --force option will ignore the reverse dependencies and uninstall the package *UNSAFELY*, without safeguards.\n
    WARNING   : 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.uninstall.start(
            pkgname, depclean=False, gfx_ui=False, unmerge=True)
    else:
        sisyphus.uninstall.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 it's dependencies behind. Those dependencies become orphans if no other package requires them.\n
    A package may also gain extra dependencies, or loose some dependencies. The lost dependencies become orphans if no other package requires them.\n
    In both cases, 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.autoremove.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.update.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 suggested if the ebuild(source) packages need to be upgraded as well, alongside the binary packages.\n
    The --ebuild option will fall back to binary packages if the ebuild(source) packages don't require any upgrade. (Can be used at all times, *SAFELY*).\n
    The --ebuild option will prefer to use binary packages (if available) to satisfy dependencies for the ebuild(source) packages, speeding up the upgrade.\n
    \n
    * Examples:\n
        sisyphus upgrade\n
        sisyphus upgrade --ebuild\n
        sisyphus upgrade -e\n
    """
    if ebuild:
        sisyphus.upgrade.start(ebuild=True, gfx_ui=False)
    else:
        sisyphus.upgrade.start(ebuild=False, gfx_ui=False)


@app.command("spmsync")
def spmsync():
    """
    Sync Sisyphus's package database with Portage's package database.\n
    Sisyphus does not track packages installed directly via Portage in it's package database.\n
    Use this command to synchronize 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 lost or corrupted.\n
    If for some reason Sisyphus's package database is lost or corrupted, it can be resurrected using Portage's package database.\n
    If Portage's package database is corrupted (in this case you're screwed anyway :D), only a partial resurrection will be possible.\n
    If Portage's package database is intact, full resurrection will be possible.\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.mirrors.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.mirrors.setActive(index)


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