Gentoo Package Deployment Script

Introduction

So you wanna make your life easier when emerging a gentoo package? Good news, so did I. I’m going to start this off with, as with many things I do, it’s not always the best way. I would say the backtracking option is very likely something you can and should take out. Read up on that and see if it makes sense for you. For me, this is something I cobbled together. It has some weak points and places it can be improved. It gets the job done though, and that’s enough for me. That said, I’ve felt like if I continue to just keep this knowledge until it’s perfect, no one can benefit from any of it. Therefore, it is incumbent on you to take responsibility for testing, improving, and revising anything I release. Furthermore, if you want to make recommendations with code examples in the comments, I’m all for it. Help each other out, and together we can make things better. Alright, with that, let’s delve into the code and start making our lives a little easier.

Gentoo? Dear Lord Man. For serial.

If you detected a Southpark vibe there, good on you. Gentoo is not for the faint of heart. It will, however, force you to be better than what you are. You will learn a lot about compiling, systems, kernels, and multiple other things. I started with Calculate Linux (a gentoo based Russian distro). If you’re completely new to linux, don’t let the steep climb throw you. Start with Calculate and learn some of the basics as well as the pitfalls. For my master project, I built out my first Gentoo system. I won’t cover that here, but in an upcoming post, I will go over what and how I did my first build. It took a month because I started with a Dell laptop and had issues with the wifi broadcom drivers. Fun times. Let’s get back to the deployment script though.

Nailed it. Code me.

#!/bin/bash
##Author: Cephas0
##Distro: Gentoo
##Knowledge level needed: Copy and Paste?
##License Brah? No...?

array=($(ls | sed 's/\(Manifest\|metadata.*\|.ebuild\|files\)//g'));

for index in ${!array[@]};do echo "$index ${array[index]}";done

read -p "Enter the pkg to be deployed: " num
pkg=${array[$num]}
current=($(pwd | sed 's+/usr/portage/++g'| sed 's|\(.*\)/.*|\1|'))
emerge -av --autounmask-continue --backtrack=200 =$pkg; dispatch-conf; 
exists=($(find /var/db/pkg | grep $pkg))
if [ -d "$exists" ]; then
   echo "Package has been deployed."
   exit
else 
   emerge -v =$pkg
   echo "Package has been deployed."
fi

Put the alcoholic beverage down and step away from the mouse

What’s all of that Greek?!?! No worries – we’ll go line-by-line through it. For now, if you want to kick it off, put it in your /usr/local/bin which will put it in the system path. Small disclosure: on my personal systems, I run everything as root. It just makes it easier for me. If you’re a sudo person, no problems, just run things as sudo. Make sure you make the script executable.

Code Breakdown

The first line of code is below.

array=($(ls | sed 's/\(Manifest\|metadata.*\|.ebuild\|files\)//g'));

I’ve set up a variable called array. The key to this script is that it assumes you have already found the package you want to install and you are in the correct portage directory.

🔥 Spark Note From the Forge 🔥

But wait. How can I get there? Well let’s use an example. Let’s say I want to install “xclock”. How would I find it?

Well there are two ways I do it; although, full admission, there are more, shall we say, proper ways to get the job done. Regrettably, you poor soul, I’m not very proper.

You could change directory into /usr/portage and run:

find | grep -i xclock

Or you could run something like this:

find /usr/portage | grep -i xclock

And your output should look something like this:



Just make sure you change into the xclock directory before running the deployment script.

Ok, back to it. The first line runs the “ls” command and pipes it through “sed”. This proceeds to filter out unneeded extra things you typically find in the directory that won’t be needed for this process. To that end, we extract the manifest file, any metadata file, and cut off the .ebuild and files from the input that will be shuttled to the array variable.

for index in ${!array[@]};do echo "$index ${array[index]}";done

Ok, so the next line of code is basically a one-liner which is why it has the semicolons. You can put this in a more readable format, but I chose not to because for me it was fairly readable. Just something to keep in mind. We start with a for statement and begin to sort through the array. The nitty-gritty of the “!array[@]” here means that instead of just looping through the array itself and using only the elements in the array (which this code would do shown below)…

array[@]

…we want to go through the slices of the array and use the indices like (0,1,2,3,4).

🔥 Spark Note From the Forge 🔥

Indices? I’m not a programmer!

Don’t sweat it. There’s only one thing to remember: indices start at 0 always. That’s how the system begins its counts. It’s true for all programming, and it’s true here. Here’s one for you LOTR fans: if you think of the “0” as the One Ring, you can think of the rest of the numbers as rings bound to the One Ring which starts at 0.

Geez, what a nerd. Hey, get a room, nerd, with your weird numbers and…stuff!

This is going great. We’re over one of the biggest humps believe it or not. The rest of this line goes through each index number. Then we use “echo” to print out the index number and the array element for each package or element in the array. Simple.

read -p "Enter the pkg to be deployed: " num

If you’ve been reading my other blogs, this will be a familiar one. Skip over it if you have or you know what this is. Otherwise, stay tuned. All this one line does is pauses the script (-p) and takes user input (yours) and then puts that input into a variable (num). Why did I use the variable “num” though? Because the script expects you to put in the index number that was printed.

The script in action

So if we were picking a new virtualbox to install and I wanted the latest package, I would enter the number “4” into the response prompt and press enter.

pkg=${array[$num]}

If you’ve done programming with arrays before, you’ll recognize this move. If not, no worries; I’ll cover it now. All we’re doing is using the array index to select the correct package from the array and putting only that package name into a new variable called “pkg”.

So if we were speaking about the example above and writing pseudo code, it would look somewhat like this:

pkg=${virtualbox-6.1.4[index number 4]}

Ok, so I feel pretty good about the progress we’ve made, and hopefully, if you didn’t know much about bash arrays or indices, you’re picking up some useful ideas for your next script.

current=($(pwd | sed 's+/usr/portage/++g'| sed 's|\(.*\)/.*|\1|'))

The next line is looking for what directory we’re in by basically using the pwd command, which is great, but dumps a lot of extra junk with it. Thankfully we have sed which allows us to clean things up. So if we’re using the virtualbox example above, we’re going to have a string looking like this from pwd:

/usr/portage/app-emulation/virtualbox

But what we really need is only the:

app-emulation

Enter the sed sphere. We pipe the output of pwd to sed and strip the front two tags. Great. That just leaves:

app-emulation/virtualbox

So how to get that last pesky section off? And that, my friends, is why we pipe in the next sed statement of glop. I’ll be completely honest here: I don’t fully remember what it does other than it will pick off the slash and what is after the slash. The how’s and why’s are on the internet and were in my head when I was working on this diligently.

emerge -av --autounmask-continue --backtrack=200 =$pkg; dispatch-conf;

This should look familiar, right? If not, what’s going on is a very questionable use of the emerge command in a script. Basically I’m running emerge, telling it to be verbose, ask, automatically unmask any package requirements (a.k.a. /etc/portage/package.accept_keywords), and backtrack. Why backtrack? The emerge man page puts it best:

–backtrack=COUNT
Specifies an integer number of times to backtrack if dependency calculation fails due to a conflict or an unsatisfied dependency
(default: ´10´).

I’ll leave it there for that. Next we run the “dispatch-conf” command so that we can automatically accept all changes. Side bar here: this will dump an enormous amount of junk into your package files like /etc/portage/package.accept_keywords. If you are strenuous about keeping the amount of comments down and your files clean then, by all means, you can add some code to the script and keep your package.accept_keywords cleaner than mine. I have another script that cleans up that mess for me, but that’s for another post.

exists=($(find /var/db/pkg | grep $pkg))

This line sets up a scenario for the remainder of the script. Namely, we check to see if the package was installed and we set the variable “exists”.

if [ -d "$exists" ]; then
   echo "Package has been deployed."
   exit
else 
   emerge -v =$pkg
   echo "Package has been deployed."
fi

We are in the home stretch now. We start out with an “if else” statement. We check to see if the directory exists (meaning a successful install), and if it does, we end the script and print a success message. If not, we strip all of the options out and automatically try to emerge the package. This is there because of the dispatch-conf option I called earlier.

Wrap up

That’s it for this post. Feels like a lot of ground got covered in a short amount of time. It’s not the most perfect script, but it does get the job done. I won’t promise you it will work all the time. You will have to contend with certain upgrades in a different way such as python and perl. Additionally, dependency conflicts are generally not going to be solved by this. I’ll cover some of these in other posts down the line. For now, enjoy the deployment script and have fun.

Share: