Search for a small-scale but automated RPM build system

This post is part of a series of older draft posts from a few months ago that I’m just getting around to publishing. Unfortunately, I have yet to find a build system that meets my requirements (see the last paragraph).

At work, we have a handful – currently a really small number – of RPM packages that we need to build and deploy internally for our CentOS server infrastructure. A number of them are just pulled down from specific third-party repositories and rebuilt to have the vendor set as us, and some are internally patched or developed software. We run websites, and on the product side, we’re a Python/Django shop (in fact, probably one of the largest Django apps out there). We don’t deploy our Django apps via RPM, so building and distributing RPMs is definitely not one of our core competencies. In fact, we really only want to do it when we’re testing/deploying a new distro, or when an upstream package is updated.

Last week I pulled a ticket to deploy node.js to one of our build hosts, and we’ve got a few things in the pipeline that also rely on it. I found the puppetlabs-nodejs module on Github that’s supposed to install it on RHEL/CentOS, but it pulls packages from http://patches.fedorapeople.org/oldnode/stable/, and the newest version of nodejs there is 0.6.18, which is quite old. I can’t find any actively maintained sources of newer nodejs packages for RHEL/CentOS (yeah, I know, that’s one down side to the distributions…). However, I did find that nodejs 0.9.5 is being built for Fedora 18/19 in the Fedora build system, is already in the Fedora 18 Testing and Fedora Rawhide repos, but is failing its EL6 builds in their system. The decision I’ve come to is to use the puppetlabs-nodejs module to install it, but try and rebuild the Fedora 18 RPMs under CentOS 5 and 6.

So that’s the background. Now, my current task: to search for an RPM build system for my current job. My core requirements, in no specific order, are:

  • Be relatively easy and quick to use for people who have a specfile or SRPM and want to be able to “ensure => present” the finished RPM on a system. i.e., require as little per-package configuration as possible.
  • Be able to handle rebuilding “all” of our RPMs when we roll out a new distro version. Doesn’t necessarily need to be automatic, but should be relatively simple.
  • Ideally, not need to be running constantly – i.e. something that will cope well with build hosts being VMs that are shut down when they’re not needed.
  • Handle automatically putting successfully built packages into a repository, ideally with some sort of (manual) promotion process from staging to stable.
  • Have minimal external (infrastructure) dependencies that we can’t satisfy with existing systems.

So, the first step was to research existing RPM build systems and how others do this. Here’s a list of what I could find online, though most of these are from distributions and software vendors/projects, not end-user companies that are only building for internal use.

  • Koji is the build system used by Fedora and RedHat. It’s about as full-featured as any can be, and I’m familiar with it from my time at Rutgers University, as it’s used to maintain their CentOS/RHEL packages. It’s based largely on Mock. However, setting up the build server is no trivial task; there are few installations outside of Fedora/RedHat, and it relies on either Kerberos or an SSL CA infrastructure to authenticate machines and clients. So, it’s designed for too large a scale and too much infrastructure for me.
  • PLD Linux has a builder script that seems to automate rpmbuild as well as fetching sources and resolving/building dependencies. I haven’t looked at the script yet, but apparently it’s in PLD’s “rpm-build-tools” package.
  • PLD Linux also has a CVS repository for something called pld-builder.new. The README and ARCHITECTURE files make it sound like a relatively simple mainly-Python system that builds SRPMS and binary packages when requested, and most importantly, seems like a simple system that uses little more than shared filesystem access for communication and coordination.
  • ALT Linux has Sisyphus, which combines repository management and web interface tools, package building and testing tools, and more.
  • The Dries RPM repository uses (or at least used… my reference is quite old) pydar2, “a distributed client/server program which allows you to build multiple spec files on multiple distribution/architecture combinations automatically.” That sounds like it could be what I need, but the last update says that it isn’t finished yet, and that was in 2005.
  • Mandriva Linux has pretty extensive information on their build system on their wiki and a build system theory page, but it seems to be largely a hodgepodge of shell scripts and cronjobs, and is likely not a candidate for use by anyone other than its designers.
  • Argeo provides the SLC framework which has a “RPM Factory” component, but I can’t seem to find much more than a wiki page, and can’t tell if it’s a build automation system or just handles mocking packages and putting them in a repo on a single host.
  • Dag Wieers’ repositories use (or used) a set of python scripts called DAR, “Dynamic Apt Repository builder”. They’re on github but are listed as “old” and haven’t been updated in at least 2 years. The features sound quite interesting, and though it’s based on the Apt repo format, it might provide some good ideas for implementing a similar system.

Update four months later: I’ve yet to find a build system that meets my requirements above. For the moment I’m only managing ~20 packages, so my “build system” is a single shell script that reads in some environment variables and runs through using mock to build them in the correct order (including pushing the finished RPMs back into the local repository that mock reads from) and then pushing the finished packages to our internal repository. Maybe when I have some spare time, I’ll consider a project to either make a slightly better (but simple) RPM build system based on Python, or get our Jenkins install to handle this for me.

CentOS/Fedora Install on SuperMicro Servers via IPMI Card KVM Over IP

I recently had to setup Fedora 16 to test something on a SuperMicro 6015T-TV 1U “dual” server. This is a 1U enclosure with two separate servers in it – based on the SuperMicro X7DBT motherboards – each with an AOC-SIMSO+ IPMI and KVM-over-IP management card. Every time I tried different options for the kernel parameters (I was using Cobbler), I could get the OS to boot, but once Anaconda started, I’d lose image (“No Signal”) on the IP KVM, and the serial console would go quiet. It took me about a dozen tries before I found a mailing list reference to the “nomodeset” option. This did the trick perfectly, and kept Anaconda working.

Linux Memory Usage and Disk Caching

I recently added some Cacti-based graphing to a number of Linux-based servers prior to rolling out a new service. When I was looking over the performance graphs of the initial testing, I noticed that memory usage on our rsyslog server was near 98%. Looking at top(1), I saw numbers that agreed, though processor usage was around 99% idle, and no process appeared to be using more than 1% of memory. It took me a minute or two to open my eyes and see past the panic of memory usage, and finally look at the complete output from free(1):

             total       used       free     shared    buffers     cached
Mem:       8171508    8032632     138876          0     162084    7253716
-/+ buffers/cache:     616832    7554676
Swap:      4192956        152    4192804

The pertinent part is the last column: “cached”. It slipped my mind that while rsyslog is writing vast amounts of data to disk, which may or may not ever be read back, the kernel is using free memory to cache as much of that as it reliably can. Hence the difference between what the kernel and userland tools call “free”, and what most human beings (or at least sysadmins) would consider “free” – or, more correctly, “available for use”.

When I get a chance, maybe I’ll submit patches to the Cacti Memory Usage Percent (SNMP) template to either graph cache separately, or remove it from the total.

Interestingly, I also found a somewhat cute page entitled “Help! Linux ate my RAM!” at http://www.linuxatemyram.com/.

Netgear ReadyNAS 1100 Bug causes NFS Failure after reboot – workaround

At work, we have a Netgear ReadyNAS 1100. It’s a 4TB NAS appliance, a cute little 1U half-depth Linux box with 4 SATA disks, a RAID controller, and some firmware to do a whole bunch of fancy stuff (mainly geared towards consumers and very small shops – all configuration is point-and-click web UI). We’ve been using it for storing archived log data and low-priority backups. At the beginning of this problem, it was running RAIDiator 4.1.6 firmware since December of last year (over 6 months), and hadn’t had any configuration changes in at least 4 months.

Last week, I had to power off the unit and remove the power cables for some rack maintenance. I went through the usual full shutdown procedure in the web UI, and also told it to `fsck` the volumes, as that hadn’t been done in quite some time. Unfortunately, when the unit came back up, even after I could log into the web UI, I couldn’t mount the NFS shares on my backup server. I kept getting messages like:

[root@backup-server ~]# mount -a
mount: mount to NFS server 'css-readynas' failed: RPC Error: Program not registered.

so my next step was to check RPC status of the readynas, from the client. That was also a bit of a surprise:

[root@backup-server ~]# rpcinfo -p css-readynas                        
   program vers proto   port                                              
    100000    2   tcp    111  portmapper                                  
    100000    2   udp    111  portmapper                                  
    100011    1   udp    620  rquotad                                     
    100011    2   udp    620  rquotad                                     
    100011    1   tcp    623  rquotad                                     
    100011    2   tcp    623  rquotad                                     
    100024    1   udp  32765  status                                      
    100024    1   tcp  32765  status                                      
    100005    1   udp   2051  mountd                                      
    100005    1   tcp   3006  mountd                                      
    100005    2   udp   2051  mountd                                      
    100005    2   tcp   3006  mountd                                      
    100005    3   udp   2051  mountd                                      
    100005    3   tcp   3006  mountd

Somewhere in that list is supposed to be nfsd, listening on port 2049. Next, I did an nmap (port scan) of the readynas:

[root@backup-server ~]# nmap -sU -p2047-2050 css-readynas             
 
Starting Nmap 4.11 ( http://www.insecure.org/nmap/ ) at 2011-06-23 15:21 EDT
Interesting ports on css-readynas.rutgers.edu (172.16.25.108):              
PORT     STATE         SERVICE                                              
2047/udp closed        dls                                                  
2048/udp closed        dls-monitor                                          
2049/udp open|filtered nfs                                                  
2050/udp closed        unknown

Interesting. The port scan shows that something is listening on port 2049. But rpcinfo doesn’t seem to recognize it as a NFS server.

At this point, through FrontView (the web UI) I backed up the configuration and went through a few reboot cycles, with no change. I then started tweaking everything I could that I thought would restart the NFS server – I added and removed allowed hosts for the NFS share, enabled and disabled sync mode, etc. I started searching the ReadyNAS Forum and found a post that reported a very similar problem – upnpd grabbing nfsd port. The user reported that he was getting the same “RPC Error: Program not registered” message, and in daemon.log on the ReadyNAS, found a line including “storage nfsd[1087]: nfssvc: Address already in use”. I remembered that I’d setup the ReadyNAS to forward all logs to our central syslog server and, sure enough, found an identical message that something had already bound to UDP port 2049 when NFS was starting. I tried confirming that upnpd was disabled and rebooting, but that didn’t help. Grepping my logs for “nfs” returned:

daemon.log:Jun 23 12:43:43 css-readynas nfsd[2331]: nfssvc: Address already in use
daemon.log:Jun 23 15:26:33 css-readynas nfsd[2724]: nfssvc: Address already in use
kern.log:Jun 23 11:35:29 css-readynas kernel: Installing knfsd (copyright (C) 1996 okir@monad.swb.de).
kern.log:Jun 23 12:43:43 css-readynas kernel: NFSD: Using /var/lib/nfs/v4recovery as the NFSv4 state recovery directory
kern.log:Jun 23 12:43:43 css-readynas kernel: NFSD: starting 90-second grace period
kern.log:Jun 23 14:56:20 css-readynas kernel: Installing knfsd (copyright (C) 1996 okir@monad.swb.de).
kern.log:Jun 23 15:12:41 css-readynas kernel: NFSD: starting 90-second grace period
kern.log:Jun 23 15:26:33 css-readynas kernel: NFSD: Using /var/lib/nfs/v4recovery as the NFSv4 state recovery directory
kern.log:Jun 23 15:26:33 css-readynas kernel: NFSD: starting 90-second grace period

My next step was a few more reboots, with no change. I then upgraded the RAIDiator firmware to the latest, 4.1.7. Still no luck. I installed the Enable root SSH addon, and that’s when things became very clear:

css-readynas:/var/log# netstat -unlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name   
udp        0      0 0.0.0.0:2049            0.0.0.0:*                           794/snmpd           
udp        0      0 0.0.0.0:514             0.0.0.0:*                           673/syslogd         
udp        0      0 172.16.25.108:137       0.0.0.0:*                           1672/nmbd           
udp        0      0 0.0.0.0:137             0.0.0.0:*                           1672/nmbd           
udp        0      0 172.16.25.108:138       0.0.0.0:*                           1672/nmbd           
udp        0      0 0.0.0.0:138             0.0.0.0:*                           1672/nmbd           
udp        0      0 0.0.0.0:161             0.0.0.0:*                           794/snmpd           
udp        0      0 0.0.0.0:162             0.0.0.0:*                           802/snmptrapd       
udp        0      0 0.0.0.0:2086            0.0.0.0:*                           1565/rpc.mountd     
udp        0      0 127.0.0.1:22081         0.0.0.0:*                           1599/raidard        
udp        0      0 172.16.25.108:22081     0.0.0.0:*                           1599/raidard        
udp        0      0 0.0.0.0:22081           0.0.0.0:*                           1599/raidard        
udp        0      0 0.0.0.0:988             0.0.0.0:*                           819/rpc.rquotad     
udp        0      0 0.0.0.0:5353            0.0.0.0:*                           729/avahi-daemon: r 
udp        0      0 0.0.0.0:111             0.0.0.0:*                           666/portmap         
udp        0      0 0.0.0.0:881             0.0.0.0:*                           1553/rpc.statd      
udp        0      0 0.0.0.0:32765           0.0.0.0:*                           1553/rpc.statd

For some very strange reason, snmpd had bound to port 2049 (the nfs port) instead of 161. That left no port for nfs to bind to.

Solution:

css-readynas:/var/log# /etc/init.d/snmpd stop                                                       
Stopping network management services: snmpd snmptrapd readynas-agent.                               
css-readynas:/var/log# /etc/init.d/nfs-kernel-server start
Exporting directories for NFS kernel daemon...done.
Starting NFS kernel daemon:mount: nfsd already mounted or /proc/fs/nfsd/ busy
mount: according to mtab, nfsd is mounted on /proc/fs/nfsd
 statd nfsd mountd.
css-readynas:/var/log# /etc/init.d/snmpd start
Starting network management services: snmpd snmptrapd readynas-agent.

Stop snmpd, restart nfs-kernel-server so that it grabs port 2049 like it should, and then start snmpd back up. If all went well, nfsd should now be listening on UDP 2049, and snmpd should be listening on UDP 161 like it should. To confirm:

css-readynas:/var/log# netstat -unlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
udp        0      0 0.0.0.0:2049            0.0.0.0:*                           -
udp        0      0 0.0.0.0:514             0.0.0.0:*                           673/syslogd
udp        0      0 172.16.25.108:137       0.0.0.0:*                           1672/nmbd
udp        0      0 0.0.0.0:137             0.0.0.0:*                           1672/nmbd
udp        0      0 172.16.25.108:138       0.0.0.0:*                           1672/nmbd
udp        0      0 0.0.0.0:138             0.0.0.0:*                           1672/nmbd
udp        0      0 0.0.0.0:161             0.0.0.0:*                           26161/snmpd
udp        0      0 0.0.0.0:162             0.0.0.0:*                           26163/snmptrapd
udp        0      0 0.0.0.0:2086            0.0.0.0:*                           1565/rpc.mountd
udp        0      0 127.0.0.1:22081         0.0.0.0:*                           1599/raidard
udp        0      0 172.16.25.108:22081     0.0.0.0:*                           1599/raidard
udp        0      0 0.0.0.0:22081           0.0.0.0:*                           1599/raidard
udp        0      0 0.0.0.0:988             0.0.0.0:*                           819/rpc.rquotad
udp        0      0 0.0.0.0:5353            0.0.0.0:*                           729/avahi-daemon: r
udp        0      0 0.0.0.0:2158            0.0.0.0:*                           -
udp        0      0 0.0.0.0:111             0.0.0.0:*                           666/portmap
udp        0      0 0.0.0.0:2160            0.0.0.0:*                           26161/snmpd
udp        0      0 0.0.0.0:881             0.0.0.0:*                           1553/rpc.statd
udp        0      0 0.0.0.0:32765           0.0.0.0:*                           1553/rpc.statd

All is well. Now to confirm this from the client machine:

[root@backup-server ~]# rpcinfo -p css-readynas
   program vers proto   port
    100000    2   tcp    111  portmapper
    100000    2   udp    111  portmapper
    100011    1   udp    988  rquotad
    100011    2   udp    988  rquotad
    100011    1   tcp    991  rquotad
    100011    2   tcp    991  rquotad
    100024    1   udp  32765  status
    100024    1   tcp  32765  status
    100005    1   udp   2086  mountd
    100005    1   tcp   3131  mountd
    100005    2   udp   2086  mountd
    100005    2   tcp   3131  mountd
    100005    3   udp   2086  mountd
    100005    3   tcp   3131  mountd
    100003    2   udp   2049  nfs
    100003    3   udp   2049  nfs
    100003    4   udp   2049  nfs
    100003    2   tcp   2049  nfs
    100003    3   tcp   2049  nfs
    100003    4   tcp   2049  nfs
    100021    1   udp   2158  nlockmgr
    100021    3   udp   2158  nlockmgr
    100021    4   udp   2158  nlockmgr
    100021    1   tcp   4189  nlockmgr
    100021    3   tcp   4189  nlockmgr
    100021    4   tcp   4189  nlockmgr

Ok, NFS is now there. It all looks good, and re-running mount -a on the client successfully mounts the NFS share.

We’ll see what happens next time I have to reboot the ReadyNAS. In the mean time, I’m going to try to bring this to the attention of Netgear support and hope they do something about it.

Using wireshark to capture packets from a remote host

I spend a fair amount of my time debugging network and service problems on a few racks of Linux servers. Of course, they’re located in a data center (yes, just downstairs, but still not quite as comfortable as my office), and they’re all command-line only – no sense in using up RAM and CPU to run a graphical UI on a box that should just be serving remote clients. I used to go through the arduous task of running a command line tcpdump session on the server until I thought I had enough packets, then SCPing it over to my workstation and opening the file in wireshark (formerly Ethereal). Fortunately, thanks to a post on Rahul Panwar’s Linux Explore blog (which seems to be sadly neglected), I found a much easier way to do it. I’ve summarized that post here, added a little explanation, and also made some useful comments for people working on Red Hat/CentOS and OpenSuSE.

What you need:

  1. Source system (the server you want to capture packets on) that you have SSH access to, with tcpdump installed, and available to your user (either directly, or via sudo without password).
  2. Destination system (where you run graphical Wireshark) with wireshark installed and working, and mkfifo available.

Procedure:

  1. On the destination system, if you haven’t already done so,
    mkfifo /tmp/packet_capture

    This creates a named pipe where the source packet data (via ssh) will be written and Wireshark will read it from. You can use any name or location you want, but /tmp/packet_capture is pretty logical.

  2. On your destination system, open up Wireshark (we do this now, since on many systems it required the root password to start). In the “Capture” menu, select “Options”. In the “Interface” box, type in the path to the FIFO you created (/tmp/packet_capture). You should press the Start button before running the next command – I recommend typing the command in a terminal window, pressing start, then hitting enter in the terminal to run the command.
  3. On the destination system, run
    ssh user@source-hostname "sudo /usr/sbin/tcpdump -s 0 -U -n -w - -i eth0 not port 22" > /tmp/packet_capture

    This will SSH to the source system (source-hostname, either by hostname or IP) as the specified user (user) and execute sudo /usr/sbin/tcpdump. Omit the “sudo” if you don’t need it, though if you do, you’ll need passwordless access. Options passed to tcpdump are: “-s 0″ snarf entire packets, no length limit; “-U” packet-buffered output – write each complete packet to output once it’s captured, rather than waiting for a buffer to fill up; “-n” don’t convert addresses to hostnames; “-w -” write raw packets to STDOUT (which will be passed through the SSH tunnel and become STDOUT of the “ssh” command on the destination machine); “-i eth0″ capture on interface eth0; “not port 22″ a tcpdump filter expression to prevent capturing our own SSH packets (more on this below). The final “> /tmp/packet_capture” redirects the STDOUT of the ssh program (the raw packets from tcpdump on the source machine) to the /tmp/packet_capture FIFO.

  4. When you’re ready to stop the capture, just Ctrl+C the SSH command in the terminal window. Wireshark will automatically stop capturing, and you can save the capture file or play around with it. To capture again, you’ll need to restart the capture in Wireshark and then run the ssh command again.

A note on network usage and tcpdump filters

This is a relatively bandwidth intensive procedure. If you use the “not port 22″ tcpdump filter (shown above) on the source machine, all traffic over eth0 (other than SSH) on that machine will be duplicated within an SSH tunnel. So you have double the traffic, plus the overhead of tunneling all that within SSH to the destination machine. If you’re capturing data from a busy machine this way, you could easily saturate the uplink and wreak all sorts of havoc. As a result, I’d recommend making the tcpdump filter as specific as you can while still retaining the data you need. If you can replace it with a filter for specific ports (i.e. '(port 67 or port 68)' for DHCP) or specific hosts, that should cut down on the amount of data you actually have to pass through the tunnel.

How to Find Network Settings in various operating systems

Since I’m occasionally asked these things, here’s how to find some commonly needed network information in various operating systems – for now, Windows, Mac OS X and Linux, as well as Android and iOS (iPhone/iPad/etc.). My assumption is that the people running BSD, Solaris, etc. (and yes, all of those have visited my blog) know this stuff. I won’t go into descriptions of what these “strange” things are.

First off, I know that most desktop computer users are used to doing everything graphically. If you know what you want to do, the command line is a lot faster. There’s no reason to fear it. Watching a cooking show might be wonderful if you have no idea how to cook a meal, but it’s not very efficient if you just need the list of ingredients.

First off, how to get a command prompt:

  • Windows: For XP and before, Start -> Run -> type “cmd”, click Ok. For Vista, Start -> type “cmd”, click it.
  • Mac OS X:Applications -> Utilities -> Terminal
  • Linux/Unix: Konsole, Xterm, whatever else you use, or just drop to command line/runlevel 3

In the following examples, anything in monospace font should be typed exactly as is at the command prompt. Note: some of this may need to be run as Administrator/root. If you’re using Windows Vista or newer, once “cmd” appears under Programs, right-click it and select Run as Administrator. On Mac or Linux, you may have to run as sudo, and you may have to specify an absolute (full) path.

Default Gateway – on a simple home network, this is the IP address of your router.

  • Windows: route PRINT, look for the line beginning with “Default Gateway:”
  • Mac OS X: route get default, look for the “gateway:” line.
  • Linux: sudo /sbin/route, look for the line beginning with “default”, it will be the in the “Gateway” column. If your system uses iproute2, ip route show.

MAC Address – The (more or less) globally unique address of your computer’s network adapter. Each network adapter (wired, wireless, etc.) has its own. Looks like xx-xx-xx-xx-xx-xx or xx:xx:xx:xx:xx:xx or xxxxxx:xxxxxx where each “x” is a number from 0 to 9 or a letter from a to f.

  • Windows: ipconfig /all, look for the name of your network connection and then the indented line starting with “Physical Address”.
  • Mac OS X: ifconfig, look for your network adapter (en0 is wired ethernet, en1 is your AirPort), the address will be on a line after “ether”.
  • Linux: ifconfig, look for “HWaddr” for the right interface.

WAN (Internet or External) IP Address) – Go to whatismyip.jasonantman.com.

Ping another host – A ping test shows (simple explanation) how long it takes packets to get from your computer to another. (For you Warcraft players, this isn’t the same as the ping times shown in-game, and you can’t ping the realm servers).

  • Windows: ping -t IPaddress, the -t makes it run until you type Control-C to stop it.
  • Everything else: ping IPaddressCtrl-C (or whatever your OS uses) to stop it.

I’ll update this with more when I get time…

Client-side subversion commit message hooks

While I know this isn’t best practice, since we use LDAP-based auth for our Linux boxes (including a sudoers file based on LDAP group membership), we usually do work on some boxes as root (sudo su -). This includes our puppetmaster, where configs are kept in subversion and edited as root. The one problem with this is how to get the username of the actual committer, not root, in subversion messages.

The theory that I came up with is a shell script that finds out who the actual user is, and then tacking this onto the beginning of the subversion commit message (since there’s no real way to do client-side hooks in subversion). While I struggled with subversion’s lack of good client hooks, I came up with a theory based on a script that preloads svn-commit.tmp and then calls the text editor. It’s actually quite simple.

First, in your .bashrc or wherever you setup environment variables, export SVN_EDITOR=/usr/local/bin/svnPreCommitClientHook.sh. This way, every time you run svn commit, instead of calling your text editor with svn-commit.tmp as an argument, the bash script will do what it needs to (commit message preloading) with svn-commit.tmp and then call your editor to finish the message.

/usr/local/bin/svnPreCommitClientHook.sh:

#!/bin/bash
LOGNAME=`/usr/local/bin/getLogname.py` # script to get user's actual login name, even if using sudo su
echo -e "\nBY: $LOGNAME" > svn-commit.foo
cat svn-commit.tmp >> svn-commit.foo
mv svn-commit.foo svn-commit.tmp
"$EDITOR" svn-commit.tmp

Using this method, running svn commit will pull up your text editor with “BY: username” already inserted in the commit message.

Ignoring SVN directories and save files with grep

Yes, I know I haven’t posted anything useful in a while. I’ve been quite busy at work, and hopefully I’ll post some of my DHCP stuff. In the meantime…

I’m starting a project for the ambulance corps migrating two Linux boxes to a single new server. One of them has been in production for about five years, and (partially due to the “we need it yesterday” nature of emergency services) has quite a bit of cruft laying around. Since almost everything is web-based (well, browser-based, restricted to the LAN only), there are a lot of web apps and PHP scripts that need to be migrated. This is made even more complicated by the switch from SuSE 10.1 (yes, ancient) to CentOS 5.4, and therefore from /srv/www/htdocs to /var/www/html. I could be lazy and symlink it, but I think it’s time to search down and destroy any absolute includes.

The problem with doing this is that, with any scripts in SVN, grepping for a string could potentially return three hits for a file – the actual file, the emacs save file (filename~) and the text file in the .svn directory.

The solution is actually pretty easy. To get rid of the .svn directories, we add --exclude=\*.svn\* to our grep command line (yes, I know, it excludes everything with “.svn” in the path, but that’s acceptably imprecise for my purposes). To get rid of the tilde (save) files, all we need is --exclude=\*~. It’s no problem to string them together as grep --exclude=\*~ --exclude=\*.svn\* -rin "foo bar" *.

To make this even easier, just add to your .bashrc:

export GREP_OPTIONS="--exclude=\*~ --exclude=\*.svn\*"