Mercurial on Yosemite

Mercurial is a great source control system… it is light and easy to use, and versatile enough for a professional environment. When I started my current company I worked extensively on Ubuntu with a local Mercurial server, and so when I switched to OS X (Lion at the time) I brought my same setup with me. Unfortunately, after upgrading to Yosemite, I found some tweaks were necessary. Here is how I got Mercurial setup with SSL on Yosemite for command line use.

1) Install Mercurial from MacPorts

$ sudo port install mercurial

2) Create a repository… I use /var/depot for the config and /var/depot/repos for the actual source.

$ cd /var
$ sudo mkdir depot
$ sudo mkdir depot/repos
$ sudo chown -R _www:_www depot/repos

3) To setup the Mercurial web server we first create some needed files to run the web interface, then we will need to modify apache. To start create hgweb.cgi. Note that I’ve modified the Python path on the first line… the default is /usr/bin/env, which is the wrong version of Python for HG.

#!/opt/local/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
#
# An example hgweb CGI script, edit as necessary
# See also http://mercurial.selenic.com/wiki/PublishingRepositories
# Path to repo or hgweb config to serve (see 'hg help hgweb')
config = "/var/depot/hgweb.config"
from mercurial import demandimport; demandimport.enable()
from mercurial.hgweb import hgweb, wsgicgi
application = hgweb(config)
wsgicgi.launch(application)

4) Create hgweb.config

[collections]
/var/depot/repos = /var/depot/repos

5) Create the hgwebdir.cgi file, and again update the python path:

#!/opt/local/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
#
# An example CGI script to export multiple hgweb repos, edit as necessary
# enable importing on demand to reduce startup time
from mercurial import demandimport; demandimport.enable()
from mercurial.hgweb.hgwebdir_mod import hgwebdir
import mercurial.hgweb.wsgicgi as wsgicgi
application = hgwebdir('hgweb.config')
wsgicgi.launch(application)

6) Create the users file to allow people to submit to your repo. I created the username mike. Repeat for all users you need:

$ htpasswd -m hgusers mike

7) To setup Apache and SSL you need to do a few things. I am assuming you already have Apache started. To make the needed changes, open /etc/apache2/httpd.conf. Make sure the following modules are included (some additional config is necessary if you want your webroot to run out of the user dirs… I put mine in /var/www/ so I skip those steps):

LoadModule authz_core_module libexec/apache2/mod_authz_core.so
LoadModule authz_host_module libexec/apache2/mod_authz_host.so
LoadModule cgi_module libexec/apache2/mod_cgi.so
LoadModule userdir_module libexec/apache2/mod_userdir.so

8) In the <directory> directive for your web root, make sure after “Options” you have “ExecCGI”. Mine looks like this:

Options FollowSymLinks Multiviews ExecCGI

9) Also in that same directive, add a handler for CGI:

AddHandler cgi-script .cgi

10) Finally, make sure near the bottom the following line is uncommented:

Include /private/etc/apache2/extra/httpd-ssl.conf

11) Save that file. One more step to setup Mercurial, then we can configure the SSL certificate and a few other things. Create and open this file: /etc/apache2/other/httpd-hg.conf

ScriptAlias /hg "/var/depot/hgweb.cgi"
<Location /hg>
Options ExecCGI
Order allow,deny
Allow from all
AuthType Basic
AuthName "Your Authentication Prompt Message Goes Here"
AuthUserFile /var/depot/hgusers
Require valid-user
</Location>

12) Now to configure the SSL certificate return to the /etc/apache2/ directory and do the following. This will create a private key, a signing request, and finally a usable (non password locked) public key:

$ openssl genrsa -des3 -out server.key 1024
$ openssl req -new -key server.key -out server.csr
$ cp server.key server.key.orig
$ openssl rsa -in server.key.orig -out server.key
$ openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

13) Now setup Apache to use the certificate you just setup. Edit /etc/apache2/extra/httpd-ssl.conf, and make sure the following are uncommented and pointing to the files we just setup:

SSLEngine on
SSLProtocol all -SSLv2
SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5
SSLCertificateFile '/private/etc/apache2/server.crt'
SSLCertificateKeyFile '/private/etc/apache2/server.key'

14) Ok that is the bulk of the server setup. Now we can configure a repository and try setup our user (mike). Let’s first create a quick repository to use.

$ cd /var/depot/repos
$ sudo mkdir example; cd example
$ sudo hg init

15) This is a quirk that seems to be necessary for Yosemite only. Go into the freshly created .hg directory inside this repo, and create an hgrc file.

$ cd /var/depot/repos/example/.hg
$ sudo nano hgrc

Enter the following:

[web]
allow_push = *

Now make sure this whole directory is owned by _www;
$ cd ../../
$ sudo chown -R _www:_www example

16) Nearly there. Restart apache, and make sure a few things are working.

$ apachectl restart

17) If you have a problem, try apachectl configtest. Now visit https://localhost/hg. It will alert you of a certificate error, again because the cert is self signed, but you can select to trust it. Then it should ask you to login using the name and password we created in step 6. You should be able to get in and see the example repository.

18) The last piece of configuration we need is for the user that will be cloning/pulling/pushing from the command line. Go to your home directory and create an .hgrc file with the following content:

[auth]
default.prefix = https://localhost/hg/
default.username = mike
default.password = <>
default.schemes = https
[ui]
username = mike <[email protected]>
[web]
cacerts = /etc/apache2/server.crt
allow_push = *
push_ssl = true
description = localhost/hg
[hostfingerprints]

19) This won’t work quite yet because our SSL cert is self signed, which will fail most security checks. We can fix this — go to your webroot (/var/www in my case) and follow these steps.

$ hg clone https://localhost/hg/example
$ cd example
$ touch test
$ hg addrem; hg ci -m testing; hg push

This is going to fail with a message something like this:

(configure hostfingerprint XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX or use --insecure to connect insecurely)

20) Copy all the numbers for the fingerprint above, and paste that into your ~/.hgrc file in the [hostfingerprints] section we left blank above:

[hostfingerprints]
localhost = XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX

21) You are done! Mercurial works, the web utility works, and SSL works.

Throw back to 2003

One of my many neuroses is an OCD-like organizing of my computer files, separated by year. I happened to be browsing some of the old folders, in this case from 2003, and I stumbled across this short I had written for a high school english assignment. I found it interesting enough to post. #throwbackthursday.

* * *

I remember the thunder and the rain hammering the roof like an invading army. The creaks and groans of the old wood like nearby battle cries. The windows blocked out the chill air, but were unable to stymie the darkness, which flowed in thick as smoke. The swaying, flickering, light cast by an overhead chandelier sent our huddled shadows dancing across the walls. I remember watching them move, as if they were strangers circling us, preparing to close in. Unbroken by the fearful barking of a dog forgotten outside, or the screech of tires as somebody rushed home, the hypnotizing rhythm of the rain and thunder threatened to consume the four of us entirely. We were usually talkative when we were together, but this evening we all sat silently, as if we somehow anticipated the moment that was about to arrive. What I don’t remember is what we did earlier in the day, or how we arrived in that room on that dark night. Nor can I resolve precisely what came after. That night exists for me only as the exaggerated minutes that preceded her confession.

Emi was looking particularly glazed; I suppose I should have recognized the slight frown on the edges of her mouth and her vacant eyes, and known something was very wrong. Regardless, we would all soon find out. Somewhere outside, lost in the darkness, a tree must have fallen. We all jumped when we heard the crash. I looked from Karen to Sarah, and from Sarah to Emi. It was in this moment as Emi’s expression was thrown into harsh relief, the darkness momentarily suspended by a burst of brightest blue, that I saw written on her face what I had failed to see before. I remember Sarah asked her if everything was all right, but Emi didn’t answer. The rain crashed down harder than ever, and the street lights outside flashed angrily and went out. Perhaps it was because she had planned to tell us, or perhaps something in the darkness of this desolate evening resonated with the weight upon her heart. I can’t say for sure, but I remember her eyes locked to mine, and there was a look of watery supplication I had never seen before. And a darkness, even darker than the cold air that surrounded us. Remembering the emptiness in those eyes still makes me shiver. She had stiffened and started speaking. At first her voice seemed stuck in her throat, but it finally found its courage, and she said she had to tell us something. She hesitated, and I found myself wishing she would not continue, but it was only a moments pause before she did. I have forgotten her exact words, but I remember the images her words summoned as if they were burned into my mind’s eye.

She told us how her boyfriend had called her to his house on that stormy night the previous weekend. Intent upon surprising him, she had arrived early, only to find a surprise of her own: he was there with another girl, sending her out into the cold after a loving embrace, then gesturing for her to depart before Emi arrived. There was a pause in her story, but none of us responded. Her words didn’t seem to settle, rather they hovered uncomfortably in the air between us, the darkness all around them. Emi explained that she had confronted him, and in her temper, she had taken his gun from his study and turned it on him. Apparently they struggled, and as he tried to fight her off, he cut her arm with a kitchen knife. Emi rolled up her sleeve and showed us. A flash of lightning timed itself dramatically and illuminated a jagged swollen cut across her forearm. Karen put her hands over her mouth and made a whimpering sound. Sarah looked like she was crying. I didn’t know what to think. But the rain hammered on, so I just sat and listened. The details are lost… I do not know if I’ve blocked them, or if she never told us, but as the struggle escalated, she shot him. At this point she was trembling all over, and her voice trailed off. So there it was: my best friend had killed someone. With a seeming crescendo, thunder erupted directly overhead and as the lightening retreated, the darkness was set free. But suddenly the darkness did not feel oppressive, it felt like a void. We were no longer surrounded, instead we were isolated afloat in nothingness, our senses paralyzed and blank. Sarah sat perfectly still, dim and distant lights glistening on her wide eyes. Karen turned to me but was unable to speak.

Something else was different between us now. The emptiness was leaking into my body from the surrounding void. I felt scared and lost; I didn’t know what to do. Karen asked Emi if she had called the police. Fighting to maintain her composure, Emi merely shook her head but did not speak. The thoughtful silence returned, and we all sat still, now four strangers in the dark. I saw the glimmer of silent tears running down Emi’s face when she sniffled. She had killed someone. Was she now a different person? Was I? I remember a part of me wanted to comfort her, but another part held me back. I saw Karen, too, looking at her differently. Sarah was looking the other way. The void was growing larger, a separation between us four as we sat there in the darkness, the blood from this dark secret seeping through us.

I don’t think any of us spoke for the rest of the evening; there was nothing to say. I don’t know if I was different or if Emi was different. It was a strange thing, that the actions of a few moments of emotion could change everything for her and for us. A few days later I believe Sarah tipped the police, and Emi was arrested. It’s funny how finite the world is. One moment things seem static: boundaries seem clear and reality is well defined. But then in the time it takes for rain to descend, a moments darkness changes everything. Sometimes I am able to feel bad for her, or at least for the person I knew and cared about before that night. I never found out if she was the same person. If the person I thought I knew was a lie, or if the darkness was never a part of her, but merely exploited her in a moment of weakness. Is evil real, or are we all arbitrary victims of inscrutable circumstance and inexorable darkness?