In previous articles, I described how to Install Mercurial 1.9, Dulwich, and Hg-Git on Mac OS X Snow Leopard and Install Mercurial 1.9, Dulwich, and Hg-Git on Mac OS X Lion. For either of those operating systems, we can further extend Mercurial with support for connecting to Subversion repositories. While we could enable the bundled Convert extension, the Hgsubversion extension adds live access to remote networked Subversion repos, so it sounds more interesting.
My personal goal in doing this is to be able to work with the InstaDMG repository. It is hosted on Google Code, uses a Subversion repo, and depends upon Subversion keyword substitution. It presents an interesting challenge.
So, let’s install Hgsubversion to add Subversion support to the Git support we’ve previously set up. We need to take the following additional steps, going beyond what is done in the previous articles. To continue, you must already have installed:
Hgsubversion also needs Subversion to be installed. The Mercurial extension then has two different ways of working with Subversion. The Hgsubversion docs I read indicated that Subvertpy is preferred over the other method — using Subversion’s SWIG bindings — so we’ll install this additional Python module first. The docs also recommend running tests on Hgsubversion before using it on projects, so we will try that, as well.
Start with installing Subvertpy:
easy_install command in Terminal.easy_install again to get hgsubversion. However, version 1.2.1 in PyPi didn’t work for me. So, to get a more current working version, I went to the source repository to get the latest software.That’s it! Hgsubversion has been installed and enabled. Even though we got errors from the tests, we can proceed.
kwdemo. You must be in the directory for the cloned repository and have enabled the Keyword extension.Notice that “Revision: 425” is returned when keywords are expanded, replacing the “$Revision” keyword. This is precisely what we need with InstaDMG, where the revision information is reported when the instadmg.bash script is run.
There may be times when you want to obtain the number of the latest available version — not just the latest installed version — of a software package through automated means. If the vendor or project provides a syndication feed (either RSS or Atom) that describes new releases, then you may be able to parse that data and get the newest release from it.
As an example, let’s examine the RSS feed for Group Logic’s ExtremeZ-IP. Other developers provide RSS/Atom feeds for their releases, but the EZIP feed is a good one to start a demonstration with because it is generally structured well.
We can break apart the EZIP feed with the Universal Feed Parser module for Python, which you must obtain separately.
Update: Because of Mark Pilgrim situation (also described here), the Universal Feed Parser Web site is no longer available. There is an alternative source for the Universal Feed Parser at Google Code, and I have cloned the Universal Feed Parser repository to Bitbucket from there.
As you can see, the “ExtremeZ-IP Latest Releases” feed is automatically recognized as RSS 2.0. I prefer to use HTTPS for fetching these feeds whenever possible, so if the developer has an HTTP feed, I try to see if it also works with HTTPS.
Next, let’s find out where the version numbers are kept in the feed. It looks like they are in the entry title, based on reading the feed in Safari RSS. I can confirm that with the Universal Feed Parser. We’ll want to examine the title of every feed item so we can better handle both current and future entries from the feed. There are more entries to the EZIP than I will print out.
We get Unicode strings as output from the Universal Feed Parser. That’s why the quoted strings are preceded with a “u” character.
I’d like to strip out the version string from the title element in each entry. I’m going to do so by splitting on whitespace and getting the last group of characters from the string. (This doesn’t account for text like “Hot Fix,” as seen in the EZIP feed, but it is still a good enough starting point for my purposes.)
By stripping out the build number after “x” in the version string, you potentially lose some data. In the EZIP feed, there are entries where two consecutive version numbers are the same except for the build number after the “x.” However, depending on your needs, it may still be useful to eliminate that part of the version string, so we’ll do that next.
We really only need the most current or “top” item in the feed, since that should give us the newest release number. The newest version in this particular feed should be in the first entry. That’s “entries[0]” below, because we’re using Python and it zero references the first item in lists.
There, we now have the version number of the most current release, 7.1.1, for the product. We have drawn it straight from the developer’s syndicated feed, so it is as current as the developer makes it.
How could that be useful? The output can be compared against other data, like the currently-installed version. The comparison, in turn, could be made part of a monitoring workflow, so you could get alerts if you fall behind.
If we didn’t strip the build number after the “x,” we would be left with a complex version number. Some Python tools, like distutils, will not currently handle the trailing characters in the version number well.
I have found that you can improve upon distutils’ StrictVersion/LooseVersion version number handling by switching to parse_version in pkg_rsources (“from pkg_resources import parse_version as V”). More coverage of that topic appears in PEP 386. If you are comparing the original version strings from the EZIP feed with similarly complex output from elsewhere, then I would probably use the pkg_resources module.
I stumbled into winning a copy of Beginning Drupal 7 by Todd Tomlinson from Apress and the nice hosts of the Drupal Easy podcast.
The announcement on the podcast had me laughing out loud. (Luckily, I was home with a sick child who was sleeping upstairs.) I hope Mike and Andrew don’t mind that I transcribed the bit where they talked about me (starting at 46:24 into the podcast):
Mike: Anyway, [laughs] so we picked the winners earlier today, and they have not gotten back to me yet, but I’m going to say, I’m going to feel pretty good that they will get back to me so I’m going to announce their names, anyway. Jeremy Reichman, who — get this — his user ID on Drupal.org is 2039. Now, that doesn’t seem that small, but I think we’re up over five hundred thousand right now. Is that correct?
Andrew: Yeah, I believe so. It’s a pretty high number now. My number is 98,079.
Mike: Right. So this guy’s way better than you.
Andrew: Yes.
Mike: Way better. But he’s been a user for over nine — nine and half years — on Drupal.org.
Andrew: Wow.
Mike: So I’m not sure what he needs a Beginning Drupal 7 book for but, you know, I would guess it’s probably for his child that he’s had between the time that he started iwth Drupal and now.
Andrew: Although the could still be using Drupal 2.
Mike: Yeah, that’s true.
Indeed, my user ID is 2039. Indeed, I have been a member on Drupal.org for “9 years 37 weeks,” according to my profile. That’s a long time! The low user ID certainly doesn’t make my account better than someone else’s, but it is kind-of fun to have now that Drupal has become so popular.
One possibility they did not raise in that announcement was that I could just have been lurking on Drupal.org for five or six years — biding my time with my increasingly-creaky UserLand Manila Web site — before I decided to take the plunge with Drupal. This may be why my Certified To Rock score is a mere 3.
But perhaps I will share Beginning Drupal 7 with my children when the book arrives. This is not a bad idea, and I already have precedent for it. Our new arrival needs some technical reading material — and I’m going to have to upgrade to Drupal 7 sometime.
After starting to use Drush, I wanted to switch my Drupal cron jobs over to it. I’d previously been running these jobs the standard way, loading a URL for each of my Drupal sites with curl. That curl command was run by the cron; Site5 allows editing of your crontab directly or through its Backstage Web interface.
I started with the basics. Drush was installed in my home directory, with an alias under ~/bin in my Site5 account. I replaced my curl command one-for-one with the following:
This didn’t work out well. I was getting a huge number of mail messages, indicating problems with the cron jobs. The messages typically contained “tput: No value for $TERM and no -T specified.” Needless to say, this was rather frustrating, so after some trial and error, I modified the command as follows, and that has been working better for me.
The biggest change is that I specify PHP, rather than Drush, directly. This was done so that I could increase the PHP memory limit for the cron job significantly, to 64M, while running Drush. I’m sure that this increase was needed partly due to the number of modules I have installed on my main site (which has an even larger default PHP memory limit in its php.ini). My research indicated that I needed to do this for Drush since it doesn’t access the Drupal sites in the same way loading a page does.
The other noticeable change is that I provide the path to the drush.php file rather than pointing to the Drush alias.
It should come as no surprise that Apple Installer installation packages can contain scripts. These scripts are supposed to conduct important operations during the course of the software installation.
However, when you are the system administrator of more than one Mac, you find that developers sometimes miss a good balance between what you think should be in the installer payload versus what should be in its scripts. The payload of a installer, by definition, are the files and links that should be installed, along with information on where they should be installed as well as how (i.e. ownership, permissions).
Therefore, developers should not need to run scripts that create or delete files — they should be created from the payload itself, and if a file must be deleted during the install then consider that perhaps you’re doing it wrong. Likewise, there should be little need move or copy files, because as many copies as desired can be installed from the paylod. Similarly, the need to change ownership or modify permissions should be taken care of in the payload.
Perhaps I’m being a purist here. I’m certainly accused of that, from time to time. However, this just makes sense to me and I happen to think that many developers are similarly logical people. They just aren’t the kind of logical people who happen to spend effort on software installation, especially the kind that results in a deployment-friendly installer package.
So how do we as administrators verify the quality of the scripts in installers? Is there a way we can quickly peer into them to decide if any of the scripts’ steps will be superfluous or even (gasp!) harmful?
Well, I have a quick suggestion for scanning packaged installers. The following one-liner shell command will search an installer package or metapackage for scripts that have the kinds of steps outlined above.
Note that this will only work for the traditional installer packages; it will not work with Leopard-style flat packages (which are documented so badly by Apple that the best description comes from reverse engineering by Iceberg’s author). The one-liner will currently only find the defined install operations scripts: preflight, preinstall, preupgrade, postinstall, postupgrade, and postflight. (Any other scripts are likely to be called by one of those six.) It assumes those scripts will be shell scripts, currently, even though any of them could be written in other scripting languages installed with Mac OS X, like Python, Perl, or Ruby. It will also not work on the JavaScript-based system and volume requirements portions of the installation.
However, it’s a start. The output displays the offending file and line number, so you can conduct more careful examination of the matches it finds.
I haven’t run this on an exhaustive list of installation packages, but I have already seen at least one installer that produces worrisome output.
Update: I’ve changed the regex for the pre/postflight script so that it is more general that what I originally posted. I’m also having some problems with the snippet working with a certain installer whose scripts I know have cp and chmod commands. So, I may be back to the drawing board with this; comments are welcome.
I have been struggling with the issue of module availability in Python. While the “batteries included” nature of the standard library is great, there are occasionally times when I need to resort to a module that isn’t included with Python.
There are also times where I’m using modules whose status has changed. I expect that to happen more in the eventual transition to Python 2.6 and 3.0, because I’ve used modules that are being deprecated.
So I wondered how I could conditionally import a module if it was available, without stopping the flow of my scripts — and gracefully handle situations where it is missing. And here’s one basic answer: use a “try” block to catch the “ImportError” exception. For example, if I were concerned that DNSPython wasn’t going to be installed on my target system:
try:
import dns.resolver # Import DNSPython
dnspython_available = True
except ImportError:
dnspython_available = False
The “except ImportError” clause could specify a different module to load, or other workarounds entirely. You could map the namespaces in the “try” bock so the rest of your script doesn’t notice the change in module functions, if you have a way to work around the missing module. Perhaps, you could even try to obtain and install the module, at least for temporary use by your script.
Thanks to authors of the article Python modules – how do they work? for the assist. The information under heading 2.11, “Is my module available?” answered my question and has given me something to think about.
I recently took a trip to Seattle, and reminded myself of a useful practice I’d developed a while ago. When I’m traveling, I collect import URLs for that trip in my browser — URLs for my organization’s travel booking/information system, airlines, hotels, maps, conference information, etc. — and put them in Safari’s Bookmarks Bar.
It’s pretty easy to collect them:

The resulting bookmarks can be rearranged, if necessary, in Safari’s bookmarks editor. (It’s the “open book” icon in the Bookmarks Bar.)
The entire group of pages can be opened all at once by clicking-and-holding on the folder in the Bookmarks Bar. When the menu drops down for the folder, choose the last command: “Open in Tabs.” This opens all of the sites bookmarked in the folder in separate tabs in the Safari window. I find that it’s useful to have them all open while I’m traveling to my destination, since that way I can see them even if I can’t get an Internet connection — free Wi-Fi is not always available.
When I’m done with the trip, I can delete the entire folder or just some of the bookmarks it contains. To delete the folder quickly, right-click on its name in the Bookmarks Bar and choose “Delete” from the contextual menu.
I have occasion to write scripts for systems administration, and for years I’ve stored them in a shared CVS repository.
Lately, however, I’ve been interested in distributed version control systems (or DVCS). The concept of DVCSes resonates with me — even though I cannot say I totally grok them yet. Of the DVCSes I’ve read about, Mercurial (aka “Hg”) has caught my eye, particularly because it is:
<
ul>
Anyway, just to put it through its paces, I created my first repository, added a script to it, and began managing. It’s making sense to me and I’m pleased so far with the results, so I’ve got more repositories and more files under revision control already. Of course, I’ve actually revised some of them, so I’ve seen how changesets operate in real life.
I’m finding it simple to use, especially after having skimmed through the Hg tutorial and scanned some of the Distributed revision control with Mercurial on-line and printable book by Bryan O’Sullivan (who was in a good Google Tech Talk on Mercurial).
Overall, I believe that systems administrators — even those who focus on client systems — should become familiar with version control and be able to employ it for any scripts they write as well as configuration files they manage. It just makes sense to me, and after a day with Mercurial, I’m finding that it works well me.
The Sabres won 5-3 over the the Florida Panthers, but rightly overshadowing that was the Panthers’ Zednik, who suffered a cut to his neck. The serious injury and its aftermath stopped play for about twenty minutes until it was decided to continue the game to its conclusion.
I was only listening to the telecast in catches from the kitchen at that point, but a call from my parents at the game brought us back to the Tivo to see what was happening. The replays were unnerving — as another Florida player’s right skate came up into Zednik’s neck as he skated into the corner to Miller’s left — but thankfully we didn’t see much of the blood on the ice. I think my mom said, "It was more blood than I’ve ever seen." The down-the-ice angle we saw of it on television certainly made it look like a profuse amount.
Richard Zednik skated quickly off the ice holding his neck, looking very, very pale, and was practically caught by trainers at the bench; he looked as if he was on the verge of collapse. I don’t know how he made it. They got him out of the bench area and later we were told he had been stabilized and taken to a Buffalo hospital. As of this writing, the postgame show indicated he was in surgery.
The Buffalo fans held a standing ovation when it was announced that Zednik was stable and on the way to the hospital, but up until that, there appeared to be little news in the arena itself. I had tried to let mom know what was going on from the telecast, but I probably called her during this ovation because I had to scrub through video on the Tivo and was still a little behind realtime. I haven’t heard from Aaron and Missy yet; they were in the seventh row for today’s game, but I don’t know what section.
Anyway, the injury was extremely scary and we pray for Zednik’s continued safety and recovery.
Well, the AMP Energy NHL Winter Classic game between the Buffalo Sabres and Pittsburgh Penguins is over. The Penguins won their second straight against the Sabres with a 2-1 shootout victory.
Rewinding a bit, the Sabres were doing really well before Christmas, with two really exciting games against Philadelphia and a nice win streak. The icing on the cake was the shootout win when Miller stuffed Briere. As much as I like Danny, hey, I’ll still root for the home team so it was great to see Miller stop that shot.
Now, the up-and-down Sabres have dropped several games straight, including the home-and-home series against Pittsburgh which culminated today in the Winter Classic. They also lost against the New Jersey Devils during this current streak.
The Winter Classic lived up to the billing in at least one sense: the wintry weather. While we’ve had some mild weather in Western New York of late, it did turn cold with some snowfall today. That was sort-of ideal if you want to televise the first outdoor NHL game in the United States, and have an obvious topic for your telecast. It resulted in some interesting camera views of the game, and a lot — I might say too much — of Zamboni work and ice repair. It certainly didn’t help the flow of the game on television.
On the plus side, the game was carried by NBC in high definition. We went over to Aaron and Missy’s house to watch it on their big set and it really does make a difference. (I’m sure it would have been much harder to figure out which was a puck and which was a snowflake here at home. Although my answer that is that the pucks are all the same and the snowflakes are all unique.) I call it ridiculous that Time Warner Rochester doesn’t carry the HD feed of the Sabres games, given that the team is all of an hour away and has been so popular of late.
Also rating up there on the ridiculousity scale:
All in all, it would have been more satisfying if the Sabres had just won. Sigh.