Getting the Git commit id of a Gerrit Change Set

Oddly enough, Gerrit doesn’t report the Git commit id of its Change Sets.

The only way I found to get that piece of information is to use its SSH interface, using the query command.

The syntax is this:

ssh -p 29418 user@gerritHost gerrit query --current-patch-set <ChangeId>

replacing 29418 with the port you normally use to contact Gerrit via SSH.

That will display a number of properties about the Change Set, including the revision parameter which is the extended version of the Git commit id.

You can use the --format JSON option if you want to get a JSON representation of the same data, or you can get the short commit id using for example:

ssh -p 29418 user@gerritHost gerrit query --current-patch-set <ChangeId> \
cut -d':' -f2 | cut -c2-7
Advertisements

sudo asks for password even if NOPASSWD is set in /etc/sudoers

TL;DR when a script is not marked as executable and you try to run it with sudo, you don’t get the usual -bash: myScript.sh: Permission denied message, you are prompted for a password instead!

This one was very frustrating.

What I wanted to do was to make a user (let’s call him bran) able to execute a specific script (let’s call it /home/hodor/calm_down.sh) without having to provide his password, because the script will be executed by an automated tool (Jenkins).

I reached back to my earlier post about sudo, and updated the /etc/sudoers file so that its User privilege specification section looked like this:

root    ALL=(ALL) ALL
bran    ALL=(hodor)  NOPASSWD:  /home/hodor/calm_down.sh *

The last line gives user bran the ability to run /home/hodor/calm_down.sh as user hodor passing it any number of parameters (*) without having to provide his password (NOPASSWD:).

Saved it, su‘ed into bran, ran

bran@laketower:~$ sudo -u hodor /home/hodor/calm_down.sh "it's ok"

aaaaand…

[sudo] password for hodor: 

d’oh.

I checked the syntax in /etc/sudoers, and it was ok.

I checked whether any of the declarations that followed in /etc/sudoers could override the line I set for bran and hodor, none to be found.

Heck, I even put that line as the last line, so no line could override it. Nothing.

After a good hour of googling around and finding nothing, I remembered that the script is in a Git repository for which I just checked out a different branch. As it turned out, the script lost its executable bit.

So I set the executable bit again, as user hodor:

hodor@laketower:~$ chmod +x calm_down.sh
hodor@laketower:~$ logout
root@laketower:~# su - bran
bran@laketower:~$ sudo -u hodor /home/hodor/calm_down.sh "it's ok"
hodor.
bran@laketower:~$

it worked!

I’m sure there’s a legitimate security concern for this behavior, but dang! was this hard to figure out!

Another wallpaper changer for Gnome and Unity

The previous wallpaper changer that I wrote in Python served me well for the last 2 years, but sometimes it would get stuck with some wallpapers: of the 200 pictures I have in my wallpapers folder (mostly taken from the paper wall), some were definitely being shown more often than others. Has the script developed a taste? Probably! 🙂

So this time I decided to put together something very quick, but that does a better job at never showing the same picture twice before all pictures in the folder have been set as desktop background.

It comes as a single bash script, there’s no configuration file to set, it picks pictures from a single folder (whereas the Python version could use several), and it moves files to a folder called shown when setting them as desktop background. Not very elegant, but it gets the job done!

Here it is; you can set your wallpapers folder and the refresh interval at the highlighted lines.

#!/bin/bash
#
# WallpaperChanger.sh
# Copyright 2014 Michele Bonazza michele@michelebonazza.com
#
# A simple script to automatically change your wallpaper in Gnome.
#
# 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/>.

WALLPAPERS_FOLDER=/home/path/to/your/wallpapers
REFRESH_INTERVAL=$((5 * 60)) # change every 5 minutes
MODE="zoom" # one between none, centered, wallpaper, scaled, stretched, zoom, spanned

# Changes the desktop background, and moves it to the "shown" folder so that it's
# not shown again before all wallpapers in the folder have been used.
# arg1 the file name of the file to be set as new background; must be in the
#      current folder
function change_wallpaper() {
  mv $1 shown
  gsettings set org.gnome.desktop.background picture-uri file://$WALLPAPERS_FOLDER/shown/$1
  gsettings set org.gnome.desktop.background picture-options $MODE
}

# Echoes the next wallpaper to be set, picked at random among images in the
# configured folder
function get_next_wallpaper() {
  find . -maxdepth 1 -type f -name "*.png" -o -name "*.jpg" -o -name "*.gif" -o -name "*.jpeg"| shuf -n 1
}

mkdir -p $WALLPAPERS_FOLDER/shown
cd $WALLPAPERS_FOLDER

while true; do
  NEXT_WP=$(get_next_wallpaper)
  
  # have we used all wallpapers?
  if [[ "$NEXT_WP" == "" ]]; then
    # yes, chdir to shown, and move them all back to the parent folder
    cd shown
    # move them to parent folder
    find . -maxdepth 1 -type f -name "*.png" -o -name "*.jpg" -o -name "*.gif" -o -name "*.jpeg" | xargs mv -t ..
    cd ..

    # check again
    NEXT_WP=$(get_next_wallpaper)

    if [[ "$NEXT_WP" == "" ]]; then
      echo "no wallpapers found in $WALLPAPERS_FOLDER, will check again in $REFRESH_INTERVAL seconds..."
      sleep $REFRESH_INTERVAL
      continue
    fi
  fi
  
  echo "changing background to $NEXT_WP"
  change_wallpaper $NEXT_WP
  sleep $REFRESH_INTERVAL
done

As always, I’ve also added this to my pastebin.

Save it as wallpaper_changer.sh, make it executable

chmod +x wallpaper_changer.sh

and add it to your “Startup applications” list, which can be found in Ubuntu’s main menu (the one you use to log out/shut down the computer), or can be brought up from a terminal using

gnome-session-properties

Click “Add”, use whatever name you want and browse to the wallpaper_changer.sh script (wherever you’ve saved it).

Sometimes I found that “Startup applications” doesn’t work: make sure that after having added your script and closed the window you can see an entry called wallpaper_changer.sh.desktop in the output of

ls -l ~/.config/autostart

If it’s not there, remove the entry and try again (I know, I know. The alternative is to fiddle with Upstart or init.d so if you want a GUI, that’s better than nothing!)

You can also change the effect to apply to your wallpapers at line 23 in the script.

Enjoy your new desktops! 🙂

Show/hide hidden files in Mac OS with a keyboard shortcut

I usually need to see all hidden files in Finder, so I used to just have them showing all the time. Being a long time Gnome user (and an Ubuntu one) I learned to love the joy of Ctrl-H to toggle between visible and invisible hidden files, so I thought that Cmd-H would do the same, which it doesn’t.

Seeing hidden files all the time wouldn’t be bad if not for the behated .DS_Store files showing up in every folder and, worst of all, in my Desktop! I try to keep my Desktop clean, no icons at all, just beautiful pictures I get from the nice Kuvva app. I don’t use Desktop shortcuts anyway, Spotlight is my friend.

So, there is a way to add the Ctrl-H behavior to Mac OS, kinda-sorta, and this is the best I could get so far.

Step 1: create a simple shell script
The command to show/hide hidden files is defaults write com.apple.Finder AppleShowAllFiles YES, changing the last word to NO if you want to hide hidden files again.
To make the shortcut behave as a toggle, we just create a “sentinel” hidden file (we created a hidden file for your hidden files so you can hide your hidden files etc..) to save the current state.

Here’s the script

if [[ -f .hiddenindeed ]]; then
	rm .hiddenindeed
	defaults write com.apple.Finder AppleShowAllFiles NO
else
	touch .hiddenindeed
	defaults write com.apple.Finder AppleShowAllFiles YES
fi
killall Finder

As you may have noticed, we kill Finder so it reloads the defaults, otherwise it looks like nothing happens. Finder reloads itself, so it’s safe to kill it. There’s a little gotcha: at least in Lion it looks like it doesn’t save its current state all the time, so run the script only when you don’t care about the currently open windows. Results may vary, and even in Lion as I said the behavior is not 100% consistent.

Step 2: save the script with a .command extension and make it executable
I’m not entirely sure that this is needed in Mac OS, but I’m too lazy to check. Plus, it’s super easy and it’s the same thing you would do in Linux.

To make it executable, just open a terminal and type

chmod a+x your_script_name.command

Of course you need to cd into the same folder as your script, or use the absolute path of your script from whatever folder.

Step 3: choose your preferred keyboard shortcut tool
Ok so this is where you can choose how to launch the script. I strongly prefer the OSX-ified Ubuntu-esque way of just pressing Cmd-H (Cmd being OSX’s Ctrl in most cases) since I never use the shortcut to hide windows, and it’s what comes most natural to me. However, this requires using a third-party tool to launch apps using shortcuts; my weapon of choice is the awesome Better Touch Tool, which I already use to enable 3-finger-click gestures to open links in new background browser tabs (again, this is the equivalent of Gnome’s middle-button click with an external mouse).

There are several alternatives, the most popular being Alfred, I guess. I never felt the need for leaving spotlight thus far, so I don’t know much about how you configure shortcuts for that, but I read it’s one of its features.

Step 3a: using Better Touch Tool
This is very easy: open BTT’s preferences, go to Global (left column, should be selected by default), Keyboard tab, click on the “Add new shortcut” button, click on the Keyboard Shortcut field, press your shortcut of choice and choose “Open Application/File/Script…” from the rightmost menu. Then, select your script.

Step 3b: using Spotlight
You can always type the name of your script in spotlight and select it; the downside to this is that it may never become the Top Hit, or at least it didn’t when I tried, so you’d have to select it with the arrow keys..

Step 4: hide the terminal once the script is done
I don’t want the terminal to stick around even after the script is done, so I have it close automatically when done.

Open a terminal window, go to Terminal/Preferences/Settings tab/Shell sub-tab and change the value for “When the shell exits”. I chose “Close if the shell exited cleanly”, but you can also choose “Close the window”. I like to see error messages if something goes wrong, though..

Fix subtitles offset with python!

[UPDATE – May 25, 2014] I revamped this script, moved it to GitHub, and wrote a new post about it!
[UPDATE – May 19, 2013] Script updated to support Python 3!

One of the most common problems with subtitle files, especially with TV series subtitles, is that they often start all too late because you have a version of the video file containing opening titles (or ‘previously on MyFavoriteSeries’ sequences) and the subtitles don’t account for them, or the other way around.

Of course, once you’ve fixed this offset the subtitles are fine, as the movie is played at the same rate in all versions.

My beloved XBMC has a function to sync subtitles, but it’s more of a fine-tuning thing, you can’t specify a very large offset (last time I checked) and it takes some time to actually reload the subtitles and show you the results.

I developed a small script in python to do just that, as I thought that it would have been quicker to write it than to look for it (and it was… at least the quick&dirty version :D). To use it, just open the subtitles with any text editor you like, look for the first dialog and take note of when that dialog takes place in the movie: your offset is the difference between the time in the movie and the one you found in the file. So if the .srt file states that Renly Baratheon says “Do you swear it?” at 00:02:08,883 but in the .avi file it’s actually at roughly 00:03:43,500, your offset is 3:43,5 - 2:08,883 = 94,617 = 1:34,617. Then, you run the script calling

python subslider.py MySubs.srt offset

and your new subs are in MySubs_offset.srt. That’s it!

You can specify positive offsets –like e.g. +15— for when subtitles should be delayed, or negative offsets –like e.g. -30— in case it’s the movie that should be delayed (and subs anticipated).

Offsets can be specified both with decimal notation (as in +94,617, subs delayed by 94.617 seconds) and with time notation (as in -5:07,324, video delayed by 5 minutes 7 seconds 324 milliseconds). Time notation follows the one used in .srt files, so you get a comma as decimal separator.

Here it is, you can save it to a file named subslider.py and run it with python 2.7 ([Update – May 19, 2013] or python 3!).

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# SubSlider - a simple script to apply offsets to subtitles
#
# Copyright May 2nd 2012 - MB <https://somethingididnotknow.wordpress.com>
#
# 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/>
from __future__ import print_function
from datetime import timedelta, datetime
import os
import re
import sys

class SubSlider:
    """A simple script to apply offsets to subtitles.

    Subtitles can be delayed by specifying a positive offset (e.g. +12 or simply 12), or video can be delayed by specifying a negative offset (e.g. -12)"""

    def __init__(self, argv):
        if len(argv) < 2:
            self.usage()
        else:
            self.first_valid = 0
            self.parse_args(argv)
            self.parse_subs()
            self.fix_file()
            os.remove(self.output_temp)
            print('Success! Offset subs have been written to %s' % os.path.abspath(self.output_subs))

    def usage(self):
        print("""usage: subslider.py [-h] subs_file offset

Applies an offset to a subtitles file

positional arguments:
  subs_file             The input subtitles file, the one to which the offset
                        is to be applied
  offset                The offset to be applied to the input subtitles file.
                        Format is [+/-][MM:]SS[,sss] like +1:23,456 (new subs
                        will be displayed with a delay of 1 minute, 23 seconds,
                        456 milliseconds) or -100 (subs 100
                        seconds earlier) or +12,43 (subs delayed of 12 seconds
                        43 milliseconds)""")
        sys.exit(1)


    def parse_args(self, args):
        error = None
        if not os.path.isfile(args[0]):
            print('%s does not exist' % args[0])
            error = True
        else:
            self.input_subs = args[0]
            self.output_subs = '%s_offset.srt' % os.path.splitext(self.input_subs)[0]
            self.output_temp = '%s_temp.srt' % os.path.splitext(self.input_subs)[0]
        offset_ok = re.match('[\+\-]?(\d{1,2}\:)?\d+(\,\d{1,3})?$', args[1])
        if not offset_ok:
            print('%s is not a valid offset, format is [+/-][MM:]SS[,sss], see help dialog for some examples' % args[1])
            error = True
        else:
            offset = re.search('([\+\-])?((\d{1,2})\:)?(\d+)(\,(\d{1,3}))?', args[1])
            self.direction, self.minutes, self.seconds, self.millis = (offset.group(1), offset.group(3), offset.group(4), offset.group(6))
        if error:
            self.usage()

    def parse_subs(self):
        with open(self.input_subs, 'r') as input:
            with open(self.output_temp, 'w') as output:
                nsafe = lambda s: int(s) if s else 0 
                block = 0
                date_zero = datetime.strptime('00/1/1','%y/%m/%d')
                for line in input:
                    parsed = re.search('(\d{2}:\d{2}:\d{2},\d{3}) \-\-> (\d{2}:\d{2}:\d{2},\d{3})', line)
                    if parsed:
                        block += 1
                        start, end = (self.parse_time(parsed.group(1)), self.parse_time(parsed.group(2)))
                        offset = timedelta(minutes=nsafe(self.minutes), seconds=nsafe(self.seconds), microseconds=nsafe(self.millis) * 1000)
                        if '-' == self.direction:
                            start -= offset
                            end -= offset
                        else:
                            start += offset
                            end += offset
                        offset_start, offset_end = (self.format_time(start), self.format_time(end))
                        if not self.first_valid:
                            if end > date_zero:
                                self.first_valid = block
                                if start < date_zero:
                                    offset_start = '00:00:00,000'
                        output.write('%s --> %s\n' % (offset_start, offset_end))
                    else:
                        output.write(line)

    def fix_file(self):
        with open(self.output_temp, 'r') as input:
            with open(self.output_subs, 'w') as output:
                start_output = False
                for line in input:
                    if re.match('\d+$', line.strip()):
                        block_num = int(line.strip())
                        if block_num >= self.first_valid:
                            if not start_output:
                                start_output = True
                            output.write('%d\r\n' % (block_num - self.first_valid + 1))
                    elif start_output:
                        output.write(line)

    def format_time(self, value):
        formatted = datetime.strftime(value, '%H:%M:%S,%f')
        return formatted[:-3]

    def parse_time(self, time):
        parsed = datetime.strptime(time, '%H:%M:%S,%f')
        return parsed.replace(year=2000)

if __name__ == '__main__':
    SubSlider(sys.argv[1:])

as always, the same script is also on pastebin.

Whenever applying the offset moves some dialogs before 0:00:00,000 I decided to drop them altogether, starting with the first dialog ending after time 0, making it start at time 0 if start is negative.
The renumbering of dialogs (see fix_file) is something that is not needed, at least by VLC (which I used to test the script). You can have dialogs starting at, say, 42 and VLC is fine with that.

I was a little disappointed with the datetime.strptime function, in that it has no built-in support for milliseconds (only microseconds, and even that only on python2.7+!). The whole date/time/datetime system is not as pythonic as it seems at first sight, so I had to do a couple of little ugly things (as in parse_time and format_time).

Add a simple restart Gnome Shell script to your right-click menu

After using the behated Unity for too much time I decided to give Gnome3.2 another chance; last time I tried I couldn’t get my cheap integrated Radeon 3000 graphic card to work with all the rendering gnome-shell requires, so I had to pick one of Gnome “classic” or Unity2D. The latter is a little less ugly, albeit suffering from too much rigidity in what you can customize and what you cannot. I tried to like it, I really did, but working with the left dash bar is painful to me.

So, after installing the latest catalyst drivers (12.1) from ATI I’ve been able to use Gnome3, and after tweaking it hard with several extensions (nice system, by the way, but maybe support for browsers other than firefox would help) I now have a desktop that I kinda like. It’s sad that I used to like Gnome2 better, though.

Not speaking of its slow responsiveness (I admit my GPU is no monster, but my PC is 10 months old, so I’d expect it to run whatever window manager… heck, it runs KDE 4 just fine!), what I find astonishing is its instability. Every now and again, gnome-shell screws up for whatever reason, and all window controls are gone, top bar and the gnome-shell itself as well. Switching to another tty with Ctrl+Alt+F1 and logging in with my user to killall gnome-shell is not helping since the gnome-shell process lies in the other session that I’m therefore forced to kill, restarting the gdm process.

At first I used this little hack from phreaknerd’s blog: you schedule a script for automatic execution that checks whether a “delete me if you want to restart gnome-shell” file exists every 15 seconds (so the routine was like: gnome-shell dies, switch to tty1, login, rm polled file, logout, Ctrl+Alt+F7 back to graphic session, wait for the script to find out).

But then I grew tired of waiting the 7.5 seconds (on average :P) for the script to detect that the control file is missing, and of the whole tty switching in general; since I’m always able to right-click on the desktop (if anything on the empty space left by the gone window decorations) I think a better solution is to add a simple script to ~/.gnome2/nautilus-scripts/ (why is it still called gnome2 anyway) like this:

cd ~/.gnome2/nautilus-scripts/; echo -e '#!/bin/bash'"\n"'gnome-shell --replace' > Restart\ gnome-shell; chmod a+x Restart\ gnome-shell

So now all I have to do is right click somewhere on the desktop, choose Scripts/Restart gnome-shell and I can get back to work! 😀

I really, really, really hope that 3.4 will be a lot more stable though…