Skip to content

Instantly share code, notes, and snippets.

@zocker-160
Created July 4, 2022 16:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zocker-160/00da2e644e8b3afc1615e9133bc0ec4b to your computer and use it in GitHub Desktop.
Save zocker-160/00da2e644e8b3afc1615e9133bc0ec4b to your computer and use it in GitHub Desktop.
python script to export flatpak application commands to a simple command in $PATH
#!/usr/bin/env python3
# Python >= 3.5 required.
"""
Author: Jiří Janoušek <janousek.jiri@gmail.com>
To the extent possible under law, author has waived all
copyright and related or neighboring rights to this file.
http://creativecommons.org/publicdomain/zero/1.0/
THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import argparse
import os
import shlex
import sys
from typing import Optional, Iterable
def main(argv) -> int:
parser = argparse.ArgumentParser(
description='Export a command from a flatpak application.',
epilog='Example: `flatpak-export-command.py eu.tiliado.Nuvola nuvolactl --name=nuvola` will let you type'
' `nuvola` instead of `flatpak run --command=nuvolactl eu.tiliado.Nuvola`.')
parser.add_argument('app', help='The flatpak application to export command from.')
parser.add_argument('command', help='The command to export.')
parser.add_argument('-n', '--name',
help='The name under which the command will be exported.')
args = parser.parse_args(argv[1:])
return export_command(args.app, args.command, args.name or args.command)
def get_path_variable() -> str:
return os.getenv('PATH', os.defpath)
def get_path_directories() -> Iterable[str]:
for directory in get_path_variable().split(os.pathsep):
directory = directory.strip()
if directory:
yield directory
def find_path_conflicts(command: str, ignore: Optional[str] = None) -> Iterable[str]:
for directory in get_path_directories():
path = os.path.join(directory, command)
if path != ignore and os.path.exists(path):
yield path
def ask_yes_no(prompt: str) -> bool:
return input(prompt + ' [y/N] ').strip().lower() == 'y'
def make_executable(path: str) -> None:
mode = os.stat(path).st_mode
mode |= (mode & 0o444) >> 2 # copy R bits to X
os.chmod(path, mode)
def export_command(app: str, command: str, name: str) -> int:
bin_directory = os.path.join(os.path.expanduser('~'), 'bin')
destination = os.path.join(bin_directory, name)
flatpak_run = 'flatpak run --command={} {}'.format(shlex.quote(command), shlex.quote(app))
if not ask_yes_no('Do you wish to export "{}" as "{}"?'.format(flatpak_run, destination)):
print('Aborted by the user.', file=sys.stderr)
return 1
if os.path.exists(destination):
print('The file "{}" does already exist. Revise the content and delete it if you wish to continue.'.format(
destination), file=sys.stderr)
return 2
conflicts = tuple(find_path_conflicts(name, ignore=destination))
if conflicts:
print('\nThe file "{}" conflicts with following commands in PATH:\n'.format(destination))
for conflict in conflicts:
print(' -', conflict)
if not ask_yes_no('\nDo you with to continue with these conflicts?'):
print('Aborted by the user.', file=sys.stderr)
return 1
try:
os.makedirs(bin_directory, exist_ok=True)
except OSError as e:
print('Failed to create directory "{}". {}'.format(bin_directory, e), file=sys.stderr)
return 3
wrapper = '#!/bin/sh\nexec {} "$@"\n'.format(flatpak_run)
try:
with open(destination, 'wt') as fh:
fh.write(wrapper)
make_executable(destination)
except OSError as e:
print('Failed to write the wrapper script "{}". {}'.format(destination, e), file=sys.stderr)
return 3
print('\nThe command "{}" of "{}" was exported as "{}".'.format(command, app, destination))
if bin_directory not in get_path_directories():
print('\nThe "{}" directory is not specified in the PATH environment variable. You might want to adjust '
'that:\n'.format(bin_directory))
print(get_path_variable(), bin_directory, sep=os.pathsep)
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment