+++ /dev/null
-#!/usr/bin/python3
-##############################################################################
-# #
-# Pakfire - The IPFire package management system #
-# Copyright (C) 2021 Pakfire development team #
-# #
-# This program is free software: you can redistribute it and/or modify #
-# it under the terms of the GNU General Public License as published by #
-# the Free Software Foundation, either version 3 of the License, or #
-# (at your option) any later version. #
-# #
-# This program is distributed in the hope that it will be useful, #
-# but WITHOUT ANY WARRANTY; without even the implied warranty of #
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
-# GNU General Public License for more details. #
-# #
-# You should have received a copy of the GNU General Public License #
-# along with this program. If not, see <http://www.gnu.org/licenses/>. #
-# #
-###############################################################################
-
-import argparse
-import os.path
-import sys
-import tempfile
-import uuid
-
-import pakfire
-import pakfire.errors
-import pakfire.logger
-from pakfire.constants import CONFIG_DISTRO_DIR
-from pakfire.i18n import _
-
-class Cli(object):
- def parse_cli(self):
- parser = argparse.ArgumentParser(
- description = _("Pakfire builder command line interface"),
- )
- subparsers = parser.add_subparsers()
-
- # Add arguments
- parser.add_argument("--arch", "-a", nargs="?",
- help=_("Run pakfire for the given architecture"))
- parser.add_argument("--debug", action="store_true",
- help=_("Enable debug mode"))
- parser.add_argument("--distro", nargs="?", default="ipfire3", # XXX for now
- help=_("Choose the distribution configuration to use for build"))
-
- # Enable/disable repositories
- parser.add_argument("--disable-repo", nargs="*", metavar="REPO",
- help=_("Disable a repository"), default=[])
- parser.add_argument("--enable-repo", nargs="*", metavar="REPO",
- help=_("Enable a repository"), default=[])
-
- # build
- build = subparsers.add_parser("build", help=_("Build one or more packages"))
- build.add_argument("package", nargs="+",
- help=_("Give name of at least one package to build"))
- build.set_defaults(func=self._build)
- build.add_argument("--id", type=uuid.UUID, dest="build_id",
- help=_("Build ID"))
- build.add_argument("--resultdir", nargs="?",
- help=_("Path were the output files should be copied to"))
- build.add_argument("--non-interactive", action="store_true",
- help=_("Disables running a shell in case the build fails"))
- build.add_argument("--skip-install-test", action="store_true",
- help=_("Do not perform the install test"))
- build.add_argument("--disable-ccache", action="store_true",
- help=_("Disable ccache"))
- build.add_argument("--disable-snapshot", action="store_true",
- help=_("Disable using snapshots"))
- build.add_argument("--disable-tests", action="store_true",
- help=_("Disable running tests"))
-
- # clean
- clean = subparsers.add_parser("clean", help=_("Cleanup all temporary files"))
- clean.set_defaults(func=self._clean)
-
- # dist
- dist = subparsers.add_parser("dist", help=_("Generate a source package"))
- dist.add_argument("package", nargs="+", help=_("Give name(s) of a package(s)"))
- dist.set_defaults(func=self._dist)
-
- dist.add_argument("--resultdir", nargs="?",
- help=_("Path were the output files should be copied to"))
-
- # extract
- extract = subparsers.add_parser("extract", help=_("Extract an archive"))
- extract.add_argument("archive", nargs=1, help=_("The path to the archive"))
- extract.add_argument("--path", help=_("Extract the archive into this path"))
- extract.set_defaults(func=self._extract)
-
- # image
- image = subparsers.add_parser("image", help=_("Commands to manipulate images"))
- image_subparsers = image.add_subparsers()
-
- # image create
- image_create = image_subparsers.add_parser("create", help=_("Create a new image"))
- image_create.set_defaults(func=self._image_create)
- image_create.add_argument("type", help=_("Image Type"))
- image_create.add_argument("path", help=_("Image Path"), type=argparse.FileType("w"))
-
- # info
- info = subparsers.add_parser("info",
- help=_("Print some information about the given package(s)"))
- info.add_argument("--filelist", action="store_true",
- help=_("Show filelist"))
- info.add_argument("package", nargs="+",
- help=_("Give at least the name of one package."))
- info.set_defaults(func=self._info)
-
- # provides
- provides = subparsers.add_parser("provides",
- help=_("Get a list of packages that provide a given file or feature"))
- provides.add_argument("pattern", nargs="+",
- help=_("File or feature to search for"))
- provides.set_defaults(func=self._provides)
-
- # requires
- requires = subparsers.add_parser("requires",
- help=_("Get a list of packages that require a given file or feature"))
- requires.add_argument("pattern", nargs="+",
- help=_("File or feature to search for"))
- requires.set_defaults(func=self._requires)
-
- # repolist
- repolist = subparsers.add_parser("repolist",
- help=_("List all currently enabled repositories"))
- repolist.set_defaults(func=self._repolist)
-
- # Repo
- repo = subparsers.add_parser("repo",
- help=_("Deal with repositories"))
-
- repo_subparsers = repo.add_subparsers()
-
- # repo compose
- repo_compose = repo_subparsers.add_parser("compose",
- help=_("Create a new repository"))
- repo_compose.add_argument("path",
- help=_("The path to the repository"),
- )
- repo_compose.add_argument("file", nargs="+",
- help=_("Files to be added to this repository")
- )
- repo_compose.add_argument("--key",
- help=_("Key used to sign archives"),
- )
- repo_compose.set_defaults(func=self._repo_compose)
-
- # search
- search = subparsers.add_parser("search", help=_("Search for a given pattern"))
- search.add_argument("pattern", nargs="+",
- help=_("A pattern to search for"))
- search.set_defaults(func=self._search)
-
- # shell
- shell = subparsers.add_parser("shell", help=_("Go into a build shell"))
- shell.add_argument("--disable-snapshot", action="store_true",
- help=_("Disable using snapshots"))
- shell.add_argument("--install", action="append", default=[],
- help=_("Install additional packages"),
- )
- shell.set_defaults(func=self._shell)
-
- args = parser.parse_args()
-
- # Disable snapshots when repositories are enabled/disabled
- if "disable_snapshot" in args:
- args.disable_snapshot = args.disable_snapshot or \
- (args.enable_repo or args.disable_repo)
-
- # Print usage if no action was given
- if not "func" in args:
- parser.print_usage()
- sys.exit(2)
-
- return args
-
- def pakfire(self, ns):
- # Find distro configuration file
- conf = os.path.join(CONFIG_DISTRO_DIR, "%s.conf" % ns.distro)
-
- # Setup logger
- logger = pakfire.logger.setup(
- "pakfire.builder.cli",
- syslog_identifier="pakfire-builder",
- enable_console=True,
- debug=ns.debug,
- )
-
- # Create a new Pakfire instance
- with open(conf) as c:
- p = pakfire.Pakfire(arch=ns.arch, conf=c, logger=logger.log)
-
- # Enable/disable repositories
- for repo in p.repos:
- if repo.name in ns.enable_repo:
- repo.enabled = True
-
- if repo.name in ns.disable_repo:
- repo.enabled = False
-
- return p
-
- def __call__(self):
- # Parse command line arguments
- args = self.parse_cli()
-
- # Call function
- try:
- ret = args.func(args)
-
- # Catch invalid inputs
- except ValueError as e:
- sys.stderr.write("%s\n" % e)
- ret = 2
-
- # Return with exit code
- sys.exit(ret or 0)
-
- def _clean(self, ns):
- """
- Cleans up
- """
- self.pakfire(ns).clean()
-
- def _build(self, ns):
- """
- Builds one or more packages
- """
- # Initialise a builder instance and build these packages
- p = self.pakfire(ns)
-
- # Create a temporary directory if we need to call dist
- with tempfile.TemporaryDirectory(prefix="pakfire-builder-") as tmp:
- packages = []
-
- # Run dist on packages where necessary
- for path in ns.package:
- if path.endswith(".nm"):
- path = p.dist(path, tmp)
-
- packages.append(path)
-
- # Run build
- for package in packages:
- p.build(
- package,
- interactive=not ns.non_interactive,
- build_id="%s" % ns.build_id if ns.build_id else None,
- disable_ccache=ns.disable_ccache,
- disable_snapshot=ns.disable_snapshot,
- disable_tests=ns.disable_tests,
- )
-
- def _dist(self, ns):
- # Get the packages from the command line options
- pkgs = []
-
- for pkg in ns.package:
- # Check, if we got a regular file
- if os.path.exists(pkg):
- pkg = os.path.abspath(pkg)
- pkgs.append(pkg)
-
- else:
- raise FileNotFoundError(pkg)
-
- # Put packages to where the user said or our
- # current working directory.
- resultdir = ns.resultdir or os.getcwd()
-
- p = self.pakfire(ns)
-
- for pkg in pkgs:
- p.dist(pkg, resultdir)
-
- def _extract(self, ns):
- # Launch Pakfire
- p = self.pakfire(ns)
-
- # Open the archive
- archive = p.open(ns.archive[0])
-
- # Extract the payload
- archive.extract(path=ns.path)
-
- # Some common functions
-
- def _print_packages(self, packages, unique=True, long=True, **kwargs):
- """
- This is a helper function that prints the result of a search
- """
- # Make all packages only appear once in the result
- if unique:
- packages = set(packages)
-
- # Walk through all packages in order
- for package in sorted(packages):
- # Dump all metadata
- s = package.dump(long=long, **kwargs)
-
- # Print the result
- print(s)
-
- def _search_packages(self, func, *args, **kwargs):
- """
- This is a helper function that performs a search for packages
- """
- packages = []
-
- # Search and store the results
- for arg in args:
- packages += func(arg, **kwargs)
-
- return packages
-
- def _info(self, ns):
- """
- Shows information about a certain package
- """
- p = self.pakfire(ns)
-
- archives = []
- patterns = []
-
- # Find all archives
- for package in ns.package:
- print(package)
- try:
- archive = p.open(package)
- archives.append(archive)
-
- # If we could not open the file, we will search for the pattern later
- except FileNotFoundError:
- patterns.append(package)
-
- packages = []
-
- # Perform the pattern search
- if patterns:
- packages += self._search_packages(p.search, *patterns, name_only=True)
-
- # Append all packages found in the archives
- if archives:
- packages += (a.get_package() for a in archives)
-
- # Print the result
- self._print_packages(packages)
-
- def _requires(self, ns):
- """
- Searches for all packages that have a certain requirement
- """
- p = self.pakfire(ns)
-
- # Search for packages
- packages = self._search_packages(p.whatrequires, *ns.pattern)
-
- # Print packages
- self._print_packages(packages)
-
- def _provides(self, ns):
- """
- Searches for all packages that have a certain provides
- """
- p = self.pakfire(ns)
-
- # Search for packages
- packages = self._search_packages(p.whatprovides, *ns.pattern)
-
- # Print packages
- self._print_packages(packages)
-
- def _search(self, ns):
- """
- Searches for packages that match the search query
- """
- p = self.pakfire(ns)
-
- # Search for packages
- packages = self._search_packages(p.search, *ns.pattern)
-
- # Print packages
- self._print_packages(packages, long=False)
-
- def _repolist(self, ns):
- """
- List all repositories
- """
- p = self.pakfire(ns)
-
- FORMAT = " %-20s %8s %12s %12s "
- title = FORMAT % (_("Repository"), _("Enabled"), _("Priority"), _("Packages"))
- print(title)
- print("=" * len(title)) # spacing line
-
- for repo in p.repos:
- print(FORMAT % (repo.name, repo.enabled, repo.priority, len(repo)))
-
- def _repo_compose(self, ns):
- """
- Composes a repository
- """
- p = self.pakfire(ns)
- p.repo_compose(ns.path, files=ns.file)
-
- def _shell(self, ns):
- """
- Open a shell in a build environment
- """
- p = self.pakfire(ns)
-
- try:
- p.shell(disable_snapshot=ns.disable_snapshot, install=ns.install)
-
- # Exit program with the shell's exit code
- except pakfire.errors.CommandExecutionError as e:
- return e.args[0]
-
- # Images
-
- def _image_create(self, ns):
- """
- Creates a new image
- """
- # Setup Pakfire
- p = self.pakfire(ns)
-
- # Run mkimage()
- p.mkimage(type=ns.type, path=ns.path)
-
-
-if __name__ == "__main__":
- c = Cli()
- c()