Note

The Funtoo Linux project has transitioned to "Hobby Mode" and this wiki is now read-only.

Funtoo:Metatools/Advanced Usage

From Funtoo
Jump to navigation Jump to search

This page documents how to set up a local development environment that will allow full local testing of any of your customizations, bug fixes or improvements to Funtoo Linux. This includes generating your own meta-repo and kits with your custom changes, as well as getting metro set up for unit tests. We recommend that all Funtoo Linux developers who will be submitting ebuild improvements to use set up this environment for local testing of changes. This setup also allows more involved changes to be tested and validated locally, so that your contributions to Funtoo are as production-ready as possible.

Overview

For our local development setup, we will be using gitolite. Gitolite will make things quite a bit easier by managing git repositories for us. Think of gitolite as your own private GitHub that has no Web user interface (we modify its settings by pushing to its special gitolite-admin repo) and you'll have a pretty good idea of what gitolite does. We will be using the following systems in these examples:

  • repohost - this system will be running gitolite under the repos user account and will house git repositories for meta-repo and kits so that they are stored at a handy central location.
  • ryzen - in these examples, this will be the primary development workstation, which will be used for editing cloned git code as well as generating custom kits. Once generated, the custom meta-repo and kits are pushed up to repohost.
   Note

When you follow this guide, it is certainly possible to have repohost and ryzen be the same computer.

   Important

This document assumes you have basic knowledge of ssh-keygen and how to generate public/private SSH key pairs. If you don't know how to to this, see Generating SSH Keys for quick steps or OpenSSH Key Management, Part 1 for a more detailed introduction. For this article, you'll probably want to generate a private keys without a passphrase, which is more convenient but a much greater security risk if the private key gets compromised, or one with a passphrase but using keychain to manage ssh-agent for you.

Gitolite

Installation

To set up gitolite on your LAN, first choose a system that will be used to house your meta-repo and kits git repositories. You can do this on the same system you will be using for testing (and even development), or you can set it up on a dedicated system. It's actually fine to set this up anywhere on the Internet, as git will use ssh to access this repository, but for the purposes of this article, we're assuming you're setting it up somewhere on your LAN. We will refer to this system as repohost.

On this system, perform the following steps as root:

root # useradd -m repos

The repos user will be a dedicated user account on the system that will have gitolite enabled and will house our git repositories. Now, we are going to su to this new user on repohost and perform gitolite configuration:

root # su repos
user $ git clone https://github.com/sitaramc/gitolite
user $ install -d ~/bin

Now, as the repos user, add the following to the end of your ~/.bashrc file:

   ~/.bashrc
export PATH=$HOME/bin:$PATH

What we're doing is setting up a bin directory where the gitolite command will be installed, which will be in your path, so that you can use it more easily. With this done, perform the following steps:

user $ source ~/.bashrc
user $ gitolite/install -ln

Now, your repos account is almost ready to be used for hosting repositories. The way gitolite works is that it is going to basically take over ssh access to the account, so that when you connect via ssh with git, it will perform its own authentication. For this to work, you will need to enable your own "master key" to access gitolite. To do this, you'll want to decide from which account you'll want to administer gitolite itself. I prefer to use my "drobbins" account on my development workstation ryzen, so I will copy my ssh public key from ~/.ssh/id_rsa.pub to /var/tmp/ryzen-drobbins.pub on the gitolite system, and then perform the following steps to "prime" gitolite with this admin public key:

user $ gitolite setup -pk /var/tmp/ryzen-drobbins.pub

Gitolite will now be initialized to recognize the drobbins remote account as an administrator, which will allow this remote account to clone from repos@repohost:gitolite-admin and push any changes to this special git repository which contains the master configuration for gitolite. This is important because we will be performing the rest of gitolite setup over ssh, using this account.

   Note

OK, gitolite is installed on repohost! From this point forward, we will be using the drobbins (or equivalent) account on your development workstation to configure gitolite remotely.

gitolite-admin Clone

Now that gitolite is ready on repohost, we can do everything else remotely. I am going to use the drobbins account on my development workstation ryzen, and you will use whatever account is associated with the public key you loaded into gitolite. I like storing my development repos in /var/src on ryzen, so I'll go ahead and clone the gitolite-admin repo to that location so it can live along all my other git repos. Feel free to put this git repo wherever you like to store git repos that you develop on:

user $ cd /var/src
user $ git clone repos@repohost:gitolite-admin
user $ cd gitolite-admin

We are now ready to configure gitolite. We'll do this by modifying conf/gitolite.conf in the git repo and adding new ssh public keys to keydir/ as needed. You will see that the initial public key you used to "prime" gitolite already exists in keydir/. Once we change the configuration, and potentially add new public ssh keys that we want to grant access to gitolite-managed repositories, we'll perform a git commit and git push, and if gitolite doesn't complain about our changes, they'll take effect immediately. We'll go through our initial configuration steps below.

gitolite Configuration

Since I will be generating meta-repo and kits on ryzen, this system will need to have permissions to create repositories in gitolite. I would typically do this on ryzen as follows. First, as root:

root # cp /root/.ssh/id_rsa.pub /var/tmp

Then, as my regular drobbins user that I typically use to perform development:

user $ cd /var/src/gitolite-admin
user $ cp /var/tmp/id_rsa.pub keydir/ryzen-root.pub
user $ git add keydir/*
   Note

It's important to change the filename of the public keys you are adding to the gitolite-admin repository. I typically use the format host-user.pub.

Now, we're ready to edit conf/gitolite.conf so that it looks like this:

   gitolite.conf
# Group definitions below, starting with @. This makes it easy to associate multiple ssh keys with a particular person.

@drobbins = ryzen-drobbins
@repomgr = ryzen-root

# To enable read-only access to your meta-repo and kits, use this along with
# commented-out line under wildrepo. You will need to add box1-root.pub and
# box2-root.pub to keydir/ as well. This is good for boxes that will be testing
# your meta-repo and kits only but should not be able to modify them.

#@reporead = box1-root box2-root

# repositories:

# SPECIAL ADMIN REPO BELOW -- modify with care! I've switched over to using the @drobbins group instead of 
# referencing the individual ryzen-drobbins key directly.

repo gitolite-admin
    RW+     =   @drobbins

# AUTO-CREATED (wild) REPOS: gitolite will auto-create repos under wildrepo/ for us
# upon initial clone of any path within, if the repo doesn't already exist.

repo wildrepo/..*
    C       =   @repomgr
    RW+     =   @repomgr @drobbins
# NOTE: to enable read-only access for certain boxes, uncomment this line:
#   R       =   @reporead

merge-scripts

Overview

Funtoo Linux uses "merge scripts" to create its kits. These scripts work by sourcing ebuilds from various overlays, and combining them using special algorithms to yield the kits you use. A meta-repo is also generated, which points to the specific kits generated that are designed to work together.

Required for merge scripts is dev-python/lxml.

The Code

You can find the code that does this on GitHub, housed at https://github.com/funtoo/merge-scripts. The script that does all the heavy-lifting is called merge-all-kits.py. Let's clone it from git, on the machine that will be generating new kits and meta-repo. In our example, this will be as the root user on ryzen:

root # cd /root
root # git clone https://github.com/funtoo/merge-scripts
   Note

While it is possible to use your own custom merge script repository, we recommend starting by using our official merge-scripts repository on GitHub, and progress to using your own fork of merge-scripts only when you need to.

Configuration

Now that merge-scripts is cloned, we will need to create a /root/.merge configuration file. Use the following file as a starting point:

   /root/.merge
[sources]

flora = https://github.com/funtoo/flora
kit-fixups = https://github.com/mygithub_user/kit-fixups
gentoo-staging = https://github.com/funtoo/gentoo-staging

[destinations]

base_url = repos@repohost:wildrepo/staging

[branches]

flora = master
kit-fixups = master
meta-repo = master

[work]

source = /var/git/source-trees
destination = /var/git/dest-trees

Sources Section

Let's walk through this configuration file. The [sources] section defines locations of repositories that the merge scripts will use as sources for creating kits and meta-repo. In the above sample config, we are using the official Flora repository from Funtoo, and the official gentoo-staging repository used by Funtoo, but we are using our own fork of https://github.com/funtoo/kit-fixups, which will allow us to add new ebuilds that will appear in kits, such as bug fixes to existing ebuilds in kits, as well as security fixes. For a core Funtoo Linux developer, this is a good way to start. If you are more interested in contributing third-party ebuilds, then you may instead choose to create your own fork of https://github.com/funtoo/flora, and use our standard kit-fixups repository. Or, you could choose to create forks of both. The recommended best practice is to use our upstream repos when possible, and fork only those repos you want to customize. This way, you'll ensure that you have the most up-to-date versions of ebuilds in those unforked repos.

Branches Section

The [branches] section is used to define the default branches that are used by the merge-scripts. In general, sticking with master is fine, but if you need the flexibility, you can point the merge scripts to a particular feature branch to use instead, for example.

Work Section

The [work] section is used to define paths where the merge-scripts will do their work. The source and destination settings above are good defaults, and define where the merge-scripts will clone source repositories and destination (written to) repositories, such as kits and meta-repo. These are kept in two separate hierarchies so they don't get mixed up.

Generating New Kits

With this all configured, you are ready to generate new kits. These kits will be generated as root on your development system, and will be stored on repohost. Here are the steps you'd perform:

root # cd /root/merge-scripts
root # bin/merge-all-kits push

Before starting the script for the first time you should configure your git user.name and user.email variables.

root # git config --global user.email "you@example.com"
root # git config --global user.name "Your Name"

merge-all-kits will proceed to create new kits and meta-repo, and will push them up to repos@repohost:wildrepo/staging/meta-repo, repos@repohost:wildrepo/staging/core-kit, etc. This process can take quite a while but has been optimized to run quickly on multi-core systems.

Using New Kits

Now that the new meta-repo and kits are created, here's how you'll use them on an existing Funtoo system, instead of your official Funtoo meta-repo and kits. First, we'll want to modify /etc/ego.conf as follows:

   /etc/ego.conf
[global]

sync_user = root
sync_base_url = repos@repohost:wildrepo/staging/{repo}

# Yes, you are supposed to have a literal "{repo}", above. Ego recognizes this special pattern.

# You can have whatever [kits] section you want, below...

You will want to make sure that whatever system is connecting to repohost is permitted by gitolite, and has its /root/.ssh/id_rsa.pub file stored and committed to the gitolite-admin repository's keydir/ and is referenced in conf/gitolite.conf. Otherwise, gitolite will not allow this system to connect. Of course, if you are configuring this on the system that's running merge-all-kits.py, it already has RW permissions to these repositories. Otherwise, you will want to make sure that gitolite has the system's keys as part of its @reporead group.

   Note

We change the sync_user to root to force ego sync to use /root/.ssh/id_rsa for authentication. By default, ego sync will attempt to use the portage user which does not have a private key installed, and thus will not be able to authenticate with gitolite. Rather than mess with the portage user and give it a proper home directory and ssh key pair, it's easier just to not drop perms to portage in the first place.

Now, let's move the current meta-repo out of the way -- you can also simply delete the existing meta-repo. And then we'll re-run ego sync:

root # cd /var/git
root # mv meta-repo meta-repo.official
root # ego sync

Ego will now sync your custom repository. If you type emerge -auDN @world, ego will now be using your custom kits, rather than the official Funtoo ones. This means that you can perform a variety of things you couldn't before. You can now add your own custom ebuilds to your fork of kit-fixups, and merge-all-kits.py will automatically incorporate these changes into your own custom kits. This will allow you to locally test any changes before submitting them as pull requests to Funtoo. You will also be able to maintain your own meta-repo and kits with your own local modifications, and have your systems use these meta-repo/kits instead of the official Funtoo ones.

Changing Repo Definitions

Now that you have generated your first meta-repo, let's look at the key file that defines what overlays and repositories are used to create meta-repo, as well as what kit branches exist, and which ones are prime and which are not. You will want to turn your attention to the kit-fixups/modules/fixups/foundations.py file, which can be viewed here on GitHub]. Let's take a look at various sections of this file:

   foundations.py (python source code) - Top of foundations.py file
#!/usr/bin/python3

from enum import Enum


class KitStabilityRating(Enum):
	PRIME = 0  # Kit is enterprise-quality
	NEAR_PRIME = 1  # Kit is approaching enterprise-quality
	BETA = 2  # Kit is in beta
	ALPHA = 3  # Kit is in alpha
	DEV = 4  # Kit is newly created and in active development
	CURRENT = 10  # Kit follows Gentoo currrent
	DEPRECATED = 11  # Kit is deprecated/retired


def KitRatingString(kit_enum):
	if kit_enum is KitStabilityRating.PRIME:
		return "prime"
	elif kit_enum is KitStabilityRating.NEAR_PRIME:
		return "near-prime"
	elif kit_enum is KitStabilityRating.BETA:
		return "beta"
	elif kit_enum is KitStabilityRating.ALPHA:
		return "alpha"
	elif kit_enum is KitStabilityRating.DEV:
		return "dev"
	elif kit_enum is KitStabilityRating.CURRENT:
		return "current"
	elif kit_enum is KitStabilityRating.DEPRECATED:
		return "deprecated"

At the top of the file, we define an enumeration of the different levels of stability that can exist for a particular kit. Kits marked with a stability rating of KitStabilityRating.PRIME will be tagged in the meta-repo JSON as being a "prime" kit and suitable for production use.

As you scroll down further, you will see some code like this:

   foundations.py (python source code) - foundations.py kit_groups
class KitFoundation:

	kit_groups = {
		'prime': [
			{'name': 'core-kit', 'branch': '1.0-prime', 'source': 'gentoo_prime_protected', 'default': True},
			{'name': 'core-kit', 'branch': '1.1-prime', 'source': 'gentoo_prime_mk3_protected', 'stability': KitStabilityRating.DEPRECATED},
			{'name': 'core-kit', 'branch': '1.2-prime', 'source': 'gentoo_prime_mk4_protected', 'stability': KitStabilityRating.BETA},
			{'name': 'core-hw-kit', 'branch': 'master', 'source': 'funtoo_current', 'default': True},
			{'name': 'security-kit', 'branch': '1.0-prime', 'source': 'gentoo_prime_protected', 'default': True},
			{'name': 'security-kit', 'branch': '1.1-prime', 'source': 'gentoo_prime_mk3_protected', 'stability': KitStabilityRating.DEPRECATED},
			{'name': 'security-kit', 'branch': '1.2-prime', 'source': 'gentoo_prime_mk4_protected', 'stability': KitStabilityRating.BETA},
			{'name': 'xorg-kit', 'branch': '1.17-prime', 'source': 'funtoo_prime_xorg', 'default': False, 'stability': KitStabilityRating.PRIME},
			{'name': 'xorg-kit', 'branch': '1.19-prime', 'source': 'funtoo_mk2_prime', 'default': True, 'stability': KitStabilityRating.PRIME},  # MK2
			{'name': 'gnome-kit', 'branch': '3.20-prime', 'source': 'funtoo_prime_gnome', 'default': True},
			{'name': 'gnome-kit', 'branch': '3.26-prime', 'source': 'funtoo_mk4_prime', 'default': False, 'stability': KitStabilityRating.DEV},
			{'name': 'kde-kit', 'branch': '5.10-prime', 'source': 'funtoo_mk3_prime', 'default': False, 'stability': KitStabilityRating.DEPRECATED},
			{'name': 'kde-kit', 'branch': '5.11-prime', 'source': 'funtoo_prime_kde', 'stability': KitStabilityRating.DEPRECATED},
			{'name': 'kde-kit', 'branch': '5.12-prime', 'source': 'funtoo_prime_kde_late', 'default': True, 'stability': KitStabilityRating.PRIME},
			{'name': 'media-kit', 'branch': '1.0-prime', 'source': 'funtoo_prime_media', 'default': False, 'stability': KitStabilityRating.DEPRECATED},
			{'name': 'media-kit', 'branch': '1.1-prime', 'source': 'funtoo_mk3_prime', 'default': True, 'stability': KitStabilityRating.PRIME},  # MK3
			{'name': 'media-kit', 'branch': '1.2-prime', 'source': 'funtoo_mk4_prime', 'stability': KitStabilityRating.BETA},
			{'name': 'perl-kit', 'branch': '5.24-prime', 'source': 'funtoo_prime_perl', 'default': True},
			{'name': 'perl-kit', 'branch': '5.26-prime', 'source': 'funtoo_mk3_prime', 'default': False, 'stability': KitStabilityRating.DEV},
			{'name': 'python-modules-kit', 'branch': 'master', 'source': 'funtoo_current', 'default': True, 'stability': KitStabilityRating.PRIME},

Here, we see the beginning of the definition of the KitFoundation class, which contains the kit_groups class variable. This variable, as you can see, defines a bunch of kits -- their name, the branch, a stability value that defines the stability rating of the kit, and other values. Note that if a kit is marked as default, it is assumed to have a kit stability rating of PRIME. You will also see that there is a source key, which defines the group of repositories and overlays that are used to create this kit.

Here are three important things to take away from this part of code: First, this is the official master definition of what kits exist, their stability rating, and what overlays/repositories are used to generate each kit. The next thing to take away from this code is that it is certainly possible to modify these settings for your custom meta-repo so that they are different from the Funtoo defaults.

Here is the third and most important thing about kit_groups -- the order that they are defined determines the order which the merge scripts try to match packages. The merge-scripts/package-sets files will be processed in order that the kits are defined in kit_groups, so core-kit first, security-kit second, etc. Once a catpkg has been added to a kit, it will not be added to any successive kits.

Moving right along, you will see a section of the KitFoundation class that looks like this:


   foundations.py (python source code) - foundations.py python_kit_settings
python_kit_settings = {
		#	branch / primary python / alternate python / python mask (if any)
		'master': {
			"primary": "python3_6",
			"alternate": "python2_7",
			"mask": None
		},
		'3.4-prime': {
			"primary": "python3_4",
			"alternate": "python2_7",
			"mask": ">=dev-lang/python-3.5"
		},
		'3.6-prime': {
			"primary": "python3_6",
			"alternate": "python2_7",
			"mask": ">=dev-lang/python-3.7"
		},
		'3.6.3-prime': {
			"primary": "python3_6",
			"alternate": "python2_7",
			"mask": ">=dev-lang/python-3.7"
		}
	}

This section is used to define special settings for python-kit. Specifically, for each branch of python-kit, it defines the primary version of python that we will attempt to use to satisfy python-single USE dependencies, followed by an alternate value to use if the ebuild does not support the first. We can also specify a mask to be injected into each python-kit to mask certain versions of python that should be masked due to lack of support in that kit.