Friday, May 26, 2017

Install Cygwin in a script

I built an Intellij docker image based on CentOS 7. To allow my colleagues to use it on Windows, I need to help them to setup a X window system. This post describe how to setup Cygwin/X using a script without user intervention.
To run in Windows, the best choice for the script is PowerShell. PowerShell is powerful, but ugly compared to Bash.
$CygwinDir=<where you want to install Cygwin>
$CygwinPkgsDir=<where the cygwin packages are cached>

function Download($uri, $outfile) {
  $webClient = New-Object System.Net.WebClient
  $Webclient.DownloadFile($uri, $outfile)

function Install-CygwinX {
  $setup = "$DownloadDir\setup-x86_64.exe"
  if (!(Test-Path "$setup")) {
    Download `
      -uri "" `
      -outfile "$setup"

  Start-Process "$setup" -ArgumentList "--site """" --root ""$CygwinDir"" --packages xorg-server,xhost --no-admin --local-package-dir ""$CygwinPkgsDir"" --upgrade-also --quiet-mode" -Wait -NoNewWindow
You can run this command to get the full list of command line options:
Downloads\setup-x86_64.exe --help
Most important command line options of Cygwin setup are:
  • --no-admin: Your user won’t have to be administrator
  • --quiet-mode: The script will run without asking anything
  • --packages: the packages you need to run a Cygwin/X. No package selection is needed.
The PowerShell script will download the setup executable and run it automatically. Here are some thing you need to know:
  • Don’t use Invoke-WebRequest because it is too slow. For Cygwin setup, it is not a big problem because setup-x86_64.exe is small, and Cygwin setup will download the rest package. If you download about 200MB file like ‘Docker Toolbox for Windows’, you will see how slow it is.
  • Use Start-Process to start the setup executable.
    • -Wait makes the script wait until Cygwin setup finished
    • -NoNewWindow is important if you run a .bat file. Without it, your .bat file will run in a separate window, just appear then disappear. If anything is wrong, you have no way to see what is the error message.

Install Docker Toolbox for Windows Automatically

Docker Toolbox for Windows have command line arguments which allows you to install it without user’s involvement.
You can run this command in Windows Command Prompt to get those arguments:
> Downloads\DockerToolbox.exe /HELP
The most useful arguments for automation are: /SILENT, /DIR, /COMPONENTS, and /TASKS.
To know what values for /COMPONENTS and /TASKS, you can run this command
> Downloads\DockerToolbox.exe /SAVEINF=docker_toolbox.inf
Dir=C:\Program Files\Docker Toolbox
If VirtualBox is already installed and I don’t want to install Kitematic, I can run this command, or put the command into the script to skip them. The installation will be automated.
> Downloads\DockerToolbox.exe /COMPONENTS=docker,dockermachine,dockercompose,git /TASKS=desktopicon,modifypath,upgradevm

Thursday, May 11, 2017

Build Git RPM on CentOS 7

I want to use core.hooksPath which is supported since Git 2.9, but the default Git version of CentOS 7.3 is still 1.8.3:
$ yum list git
Available Packages
git.x86_64                                       base
I have to compile from the source by myself. It is not hard to find bunch of blogs explain how to do it. For example, However, the method mentioned in those blogs is not what I want, a docker image based on centos:7 with the latest Git version. There are questions I don’t know the answers:
  • How can I clean up those required build tools like gcc? I don’t want a larger image size.
  • How can I install Git manuals? I could not remember all commands and parameters. It will be handy to reference by just typing git help xxx.
I believe the clean way is to build Git in RPM and install Git using RPM in my docker image. Sounds easy, but the first problem I had was where to get .spec to build RPM. The Git source code doesn’t have a RPM spec file. I finally found the spec from Redhat. But it is not easy like Gradle or Maven when you build from a RPM spec. You have to know the tools to pull the dependencies. Finally, my method is actually pretty simple after I figured it out all of steps because I made it in a dockerfile. Here are what I did:
  • I created a docker image git-rpm, which builds Git 2.12.2 in RPMs.
  • When running git-rpm in a container, a yum repository server starts.
  • When I build the docker image, I just simply put the local yum repository for Git, and call yum install -y git.
  • For centos:7, the default configuration turns off the manual installation tsflags=nodocs. I need to turn it on using yum --setopt tsflags='' -y install git.
If you want to get those RPMs from docker and put them to a yum repository, you can run docker cp
FROM centos:7

LABEL name="Latest version Git RPM on CentOS 7" \

RUN yum install -y \
    git rpm-build rpmdevtools make gcc \
    asciidoc xmlto desktop-file-utils emacs expat-devel gettext gnupg2 \
    curl-devel libsecret-devel pcre-devel perl-generators perl openssl-devel \
    zlib-devel pkgconfig bash-completion systemd python libgnome-keyring-devel \
    perl-ExtUtils-MakeMaker \
    createrepo httpd \
    && yum clean all

RUN cd /root \
    && git clone \
    && cd /root/git \
    && git checkout f26 \
    && rpmdev-setuptree \
    && cp /root/git/* /root/rpmbuild/SOURCES \
    && cd /root/rpmbuild/SOURCES \
    && spectool -g git.spec \
    && rpmbuild -bb git.spec 

RUN mkdir /var/lib/git-repo \
    && cp -R /root/rpmbuild/RPMS/* /var/lib/git-repo \
    && createrepo /var/lib/git-repo \
    && ln -s /var/lib/git-repo /var/www/html

CMD [ "/usr/sbin/apachectl", "-DFOREGROUND" ]