Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: git

  • Git Flow

    Git Flow

    Late as always to these git things.

    In the ever increasing madness to the method of making code easier to do, we come to the automation of git-flow. This is a library of git subcommands that helps automate some parts of the flow to make working with it a lot easier if you’re using Vincent Driessen’s model.

    This model is not bad, if a little confusing at first:

    The git-flow model

    Jeff Kreeftmeijer already has a good primer for it so these are just my quick notes to add in things that should have been obvious. The basic concept for the model is that you have two main branches, master (the ‘current’ version) and develop (where you’re working on the next version).

    Install git-flow

    You have to install it to use it. I use Homebrew so it was brew install git-flow and that was that.

    Add it to your repo

    Again, you have to add it to have it. git flow init is run inside the repo and you’re good, except you may not be.

    Since I’m starting from an existing repository, which was up to date in master, I made a new branch: git checkout -b develop

    Then I ran git flow init which is not the same as it would be on a new repo:

    $ git flow init
    Which branch should be used for bringing forth production releases?
       - REL_3.3
       - REL_3.3.1
       - REL_3.3.2
       - master
    Branch name for production releases: [master] master
    
    Which branch should be used for integration of the "next release"?
       - REL_3.3
       - REL_3.3.1
       - REL_3.3.2
    Branch name for "next release" development: [master] develop
    
    How to name your supporting branch prefixes?
    Feature branches? [feature/]
    Release branches? [release/]
    Hotfix branches? [hotfix/]
    Support branches? [support/]
    Version tag prefix? [] REL_
    

    Should you happen to do as everyone says and ‘accept the defaults’ you get this:

    Branch name for "next release" development: [master]
    Production and integration branches should differ.
    

    Also if you don’t have a develop branch, it punts you out.

    Branch name for "next release" development: [master] develop
    Local branch 'develop' does not exist.
    

    So a couple ooops along the way. It’s not

    Using A Feature

    First you make a feature:

    $ git flow feature start utility_1.0.3
    Switched to a new branch 'feature/utility_1.0.3'
    
    Summary of actions:
    - A new branch 'feature/utility_1.0.3' was created, based on 'develop'
    - You are now on branch 'feature/utility_1.0.3'
    
    Now, start committing on your feature. When done, use:
    
         git flow feature finish utility_1.0.3
    

    Eventually you’ll be done and use that finish command, which merges it all back into ‘develop’ and changes you back to that branch. Which is good for reasons we get to in a moment. Since I work on multiple computers, I do this at the end of my workday:

    $ git push --all
    Total 0 (delta 0), reused 0 (delta 0)
    To ipstenu@example.com:/home/ipstenu/repositories/theme-wordpress.git
     * [new branch]      develop -> develop
     * [new branch]      feature/utility_1.0.3 -> feature/utility_1.0.3
    

    So now when I do my pull later on the other computer, it’ll be easily usable. Provided I remember to install git-flow and init it on my other laptop. But it’s a little more complicated than just that.

    Releasing a Release

    You can only run this from the develop branch, which makes the previous command pretty awesome, right? So now we’ll do this: git flow release start RELEASE

    Once we’re good to go, it’s git flow release finish RELEASE

    What About Two People?

    This is where we want to use publishing. Technically I’m collaborating with myself across two computers, so I always publish my feature to my remote server so it can be used by my other self: git flow feature publish MYFEATURE

    To pull the feature down, git flow feature pull origin MYFEATURE is just as logical. And that’s actually how I handled pulling the feature utility_1.0.3 onto my work laptop.

    $ git checkout master
    Switched to branch 'master'
    Your branch is up-to-date with 'origin/master'.
    $ git branch develop
    $ git flow init
    [snip .. same as before]
    $ git flow feature pull origin utility_1.0.3
    Created local branch feature/utility_1.0.3 based on origin's feature/utility_1.0.3.
    

    There’s a good cheat sheet for help about this called Git Flow Cheatsheet.

    Now there’s nothing but to do it.

  • Stick a Fork In It

    Stick a Fork In It

    So you’ve forked a repository from someone and they happen to be using git. This is great and with git (and GitHub) this is so easy and so simple. Heck, on GitHub, they want you to press that fork button. And this is all wonderful except for two things.

    1. You can’t search a fork on GitHub.
    2. Merging back into your fork is confusing.

    The first issue drives me nuts.

    Sorry, forked repositories are not currently searchable.  You could try searching the parent repository.

    That it says “currently” gives me some hope, but it’s one of the most annoying aspects of a fork on GitHub.

    The second issue is bigger than just GitHub.

    Sometimes when you fork, you never want to go back, and that’s sensible. You’ve decided to go a different way. That’s how most of us view a fork, after all, because we’re used to repositories being silo’d and stand alone (like with SVN). But with git, you can actually send your fork repo as a pull request to the original for them to merge in your changes. And the reverse is also true, so if you and another dev have a fundamental difference on something you can’t hack with an add-on, you have options that don’t involve reading every line of code and copy/pasting.

    Yes, I did that before.

    Thankfully you won’t have to. You can follow three steps to do this.

    Add the Upstream

    Technically you should do this any time you make a fork, but if you use GitHub you probably forgot. After all, GitHub has that nice ‘Pull Request’ button for you, which takes care of it. They want you to cross contribute and, bless them, they make it quite easy to do so.

    GitHub's Create Pull Request banner

    Instead, you’ll want to manually tell your repository that yes, Virginia, there is an upstream. This is the parent repository and it’s one command:

    git remote add upstream ORIGINALREPO
    

    On GitHub it looks like this:

    git remote add upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git
    

    Simple. Done.

    Fetch the Upstream

    Now you want to fetch the upstream repository so your clone of the repository has the code it will need to merge.

    git fetch upstream
    

    Yeah, it’s that simple. It’s pretty much making a branch and fetching its changes. At this point, you’ve not made any changes to your own code.

    Merge with Upstream

    This works best on master to master, but I’ll bet you can also set a branch and merge that way.

    git merge upstream/master -m "Merging from upstream"
    

    If there aren’t any commit differences, it fastforwards. Otherwise you get a merge done safely, your changes stick.

    But I don’t use Git on the CLI!

    According to Hermes Pique, you can do it this way:

    1. Open your fork on GitHub.
    2. Click on Pull Requests.
    3. Click on New Pull Request. By default, GitHub will compare the original with your fork, and there shouldn’t be anything to compare if you didn’t make any changes.
    4. Click on switching the base. Now GitHub will compare your fork with the original, and you should see all the latest changes.
    5. Click on Click to create a pull request for this comparison and assign a predictable name to your pull request (e.g., Update from original).
    6. Click on Send pull request.
    7. Scroll down and click Merge pull request and finally Confirm merge. If your fork didn’t have any changes, you will be able to merge it automatically.

    Me? I like the CLI.

  • Git The Master Race

    Git The Master Race

    I was working on a project, a side project for fun with a friend, and she expressed a slight worry. We weren’t using any versioning on our theme or mu-plugins code. Or anything else for that matter. There were three of us. We thought about using GitHub until I pointed out I had git on my server, where I was hosting it all.

    In many ways I don’t use git like a ‘normal’ person, or so I’ve been told. I don’t have public git repo of everything because some of it is just private. I don’t even have a public GUI interface for people to file issues on my private code. Public code? Sure, and I use GitHub for it. Personal stuff is not up there, and I don’t care to make it.

    What I do instead is, since I do have Git installed on this server, is allow each user account to make a git repository folder off their home folder. Everyone has ~/repositories/ and in there they can store their code!

    Here are the ones for mine:

    ~/repositories/ipstenu-code.git/
    ~/repositories/ipstenu-mu-plugins.git/
    ~/repositories/mothra.git/
    ~/repositories/scripts.git/
    ~/repositories/slides.git/
    

    The two that start ‘ipstenu’ are for the website ipstenu.org and all it’s children. Mothra is some server scripts I want to keep backed up, scripts are user scripts, and slides are my slides.

    But how do I get there, and how do I keep everything synced up? First is the basic setup for git:

    cd ~/repositories/
    mkdir projectname.git
    cd projectname.git
    git --bare init
    

    From here it’s a simple git clone, too:

    git clone user@example.com:/home/user/repositories/projectname.git
    

    But… I have that project named ipstenu-mu-plugins. When I’ve checked in code, do I manually copy the files over? Of course not.

    When you go into that git folder where I ran the init, you see this:

    [~/repositories/ipstenu-mu-plugins.git]# ls
    ./  ../  branches/  config  description  HEAD  hooks/  index  info/  objects/  refs/
    

    If you go into the hooks folder, you get a list of files:

    ipstenu@ipstenu.org [~/repositories/ipstenu-mu-plugins.git/hooks]# ls
    ./                      commit-msg.sample   pre-applypatch.sample      pre-push.sample
    ../                     post-update         pre-commit.sample          pre-rebase.sample
    applypatch-msg.sample  post-update.sample  prepare-commit-msg.sample  update.sample
    

    You’ll notice I only have one file that isn’t a .sample: post-update

    That’s my secret sauce.

    #!/bin/sh
    export GIT_WORK_TREE=~/public_html/wp-content/mu-plugins
    git checkout -f master
    

    This just uses git checkout, forces the master branch, and dumps the files where I want them. I set it up for each repo when I’m ready to start deploying, and then my dev process is this.

    1. Pull master and all remote branches to make sure I’m up to date
    2. Make a new branch to develop
    3. Push the new branch to origin when I’m sure it’s right
    4. Merge the new branch into master
    5. Push master

    And in that moment, the push will copy my files live.

    Since I don’t ever do dev work in master, this is safe for me. It’s five steps, but I actually have a script to run that first one every single day, so that’s almost step zero. When I work with other people, I always make a backup local branch of master as ‘last known good’ just so I can roll back fast if I’ve been phenomenally stupid. This is usually a date like “GOOD_20141117”, which I know is different from my actual release labels, that are always “REL_1.2.3” style. If it’s a weird thing, like when I was folding Ninja Forms in my site, it was “NINJAFORMS_1.0” because I wanted to be clear what that dev branch was for.

    The Master Race - Doctor Who

    By the way, if you need to set up a shared repository, you should read Kovshenin’s “How To Create a Remote Shared Git Repository”. I don’t, since everyone logs in using SSH keys to the account (so on my shared project, everyone uses ssh projectaccount@projectdomain.com) since they actually need that access anyway.

  • Personal Version Control With Git

    Personal Version Control With Git

    Git RelationshipsOne thing I am personally bad at is version control. Oh, don’t get me wrong, I’m great decent at it for code at work, but I still have a tendency to cowboy code. Bad me.

    Part of why is that I don’t want to have my stuff public. At the same time, I don’t want to pay Github while I’m learning, and I want a GUI. Why not overcomplicate my life! I’ll add on one more, I want a website where I can look at my changes all pretty like. Last time I did this via SVN, hated it, never used it, and walked away. This time I decided to use Git, and once I started using it via the command line, I realized how freaking awesome this was. And since I’m using CLI, I ran git config --global color.ui true to get pretty colors.

    I should note that I use Coda2 by Panic and I love it. But. It doesn’t have an easy way to properly branch and tag in SVN or Git, which is something I’m used to having. And my master plan is this. Master will have the ‘live’ real code. The branches are releases. When a branch is ready to go live, I merge it with master.

    So let’s get started.

    Stage One: Install Git

    I followed Gits official directions for installing Git on my server, so once you’ve got things like that up and running, it’ll be time for the webpage.

    Cart comes after horse, though. I had a hell of a time getting my own SVN crap up and running, but git was way easier. There was far less to mess with to install, since I’d already done it before. With Git, I don’t need to mess with svnserve or daemons, since git isn’t the ‘server’ where my code is stored, it’s just another repository/clone. It breaks my head sometimes, but this is a case where it’s going to be easy. Install any Git dependencies (on CentOS that means I ran yum -y install zlib-devel openssl-devel cpio expat-devel gettext-devel ) and then install from command line:

    cd /usr/local/src
    wget http://git-core.googlecode.com/files/git-1.8.2.2.tar.gz
    tar xvzf git-1.8.2.2.tar.gz
    rm git-1.8.2.2.tar.gz
    cd git-1.8.2.2
    
    ./configure
    make
    make install
    

    Done, and now I’m on 1.8.2.2. Upgrading is as easy as doing the wget again.

    Stage Two: Make Repositories

    At this point, the official git directions say you should make a git account, but after some pondering, I decided to put my repos in /home/myuser/gitrepos/ so I can easily add my repositories to my local computer. Everything is locked down via RSA keys anyway, so I can secure connect from my computers and everything is basic and simple and safe. If I want to make more repositories for other accounts, I can do it as a one-by-one basis. Right now I have no need for group stuff, this is a me repository after all. If I did, I’d use git’s directions for making a git user instead.

    I made projects for my domains (ipstenu and jfo) and then one for my server (mothra). I hooked into them with Coda2, which is my app of choice, and I tested that I could get and push files. I’ll come back to this in a second.

    Stage Three: Webify it!

    Next up was webifying things. I want to do this, even though I’m putting it behind a password, because I’m visual and being able to see what I’ve got going on is helpful to me. I went with GitList because it looked nice. The drawback to these things is that if I used a shared repository (say /home/gituser/gitrepos/ for example) then I’d have to web-alias to the gituser instead of having it hosted as ‘Ipstenu’ … which is okay. If I was really making a full blown shared group, I’d probably buy gitstenu.org and point it there.

    My Gitlist

    Awesomesauce! It works! Except for this error Unnamed repository; edit this file ‘description’ to name the repository. That’s easily fixed on the server by editing the description file.

    Those initial repositories are for my non-public files, actually. That’s where I keep copies of the obscure shell scripts I run, stuff that isn’t web accessible. After I got those set up, I made repositories for my themes and my mu-plugins. This gave me folders like ipstenu-mu-plugins and jfogenesis-wp because I named the theme ‘jfogenesis’ for WordPress, MediaWiki, and ZenPhoto. Still this let me progress to …

    Stage Four: Control

    The whole reason I did all the rest of that was so I could do this, which seems redundant, but bear with me.

    1. I checked out all the code locally, edit it, and push it back up to the repos on my server
    2. On the webserver, I check out the code with a simple git clone to folder2 (trust me)
    3. switch folder and folder2 (you know how to move things)
    4. Checkout the new code with a git pull. If I want to checkout a specific version, it’s git checkout -b mysite-REL_1.0 origin/REL_1.0(It’s worth mentioning that I made a branch for everything at the start as REL_1.0 because reasons.)

    And that’s it.

    Stage Five: Updates

    Here’s my current process.

    Locally, I start work on my new version so I make a new branch: git checkout -b REL_2.0

    Edit my files and add and remove them as needed:

    git rm <filename>
    git add .
    

    Check them in when I’m done with the files: git commit -m "Editing foo and bar, replacing baz."

    When I’m ready with it to be tested, I push it: git push origin REL_2.0

    Everything tests well on the dev server(Just pretend there is one.)? Okay! Let’s merge!

    git checkout master
    git merge REL_2.0
    git push origin master

    Summary

    Since this is just me running stuff, it’s pretty easy to keep track of everything. Having the website makes it easy for me to see what’s I last did and what’s changed:

    Diff Screen

    Again, yes, I put the git website behind password protection. I may change this later, if I determine I never put anything secret up there. But right now, this is good for me. After a couple days of using it, I liked it a lot. Enough that, unlike my foray into SVN, I kept using it. It was easy for me to keep my code organized and up to date. If I had to update ‘live’ I could still use branches easily and roll things back painlessly. That’s pretty darn cool.

  • Deploying With Git

    Deploying With Git

    gitI’ve been banging my head on this for a while. It really did take me a year and reading lots of things to begin to understand that I was totally wrong. As with many things, I have to sit down and use them for a while to understand what I’m doing wrong, and what I need to learn. I finally had my git breakthrough. It’s very possible (no, likely) that I got some of this wrong, but I feel like I now understand more about git and how it should be used, and that made me more confident in what I’m doing with it.

    Speaking as a non-developer (hey, sometimes I am!), I just want a command line upgrade for my stuff. This code also lacks a WordPress-esque click to upgrade, so I have to do a three step tango to download, unpack, copy, delete, in order to upgrade.(By the way, more software should have one-click upgrades like that, it would make life easier for everyone. I do know that the backend support is non-trivial, so I would love to see a third-party act as a deployment hub, much like GitHub is a repository hub.) The more steps I have, the more apt I am to make an error. So in the interests of reducing my errors and my overhead, I wanted to find a faster and safer way to deploy.(My previous job was all about deployment. We had a lot of complicated scripts to take our code, compile it, compress it, move it to a staging site, and then email that it was ready. From there, we had more scripts to ‘move to test’ and ‘move to prod’ which made sense.)

    Since I already thing that automating and simplifying deployment is good, and all I want to do is get one version, the ‘good’ version, of code and be able to easily update it. One or two lines is best. Simple, reliable, and easy to use. That’s what I want.

    Recently, Ryan Hellyer pointed out git archive, which he claims is faster than clone. I’d believe it if I could get it to work. When I tried using HTTPS, I got this: fatal: Operation not supported by protocol.. So I tried using ssh and got Could not resolve hostname… instead. Basically I had all these problems. Turns out github turned off ‘git archive -remote’ so I’m dead in the water there for any code hosted there, which is most of my code.

    I kicked various permutations of this around for a couple afternoons before finally throwing my hands up, yet again, and looking into something else, including Capistrano, which Mark Jaquith uses in WP Stack. It’s something I’m personally interested in for work related reasons. Capistrano is a Ruby app, and vulnerability fears aside, it’s not very user friendly. At my old job, we used ant a lot to deploy, though there don’t seem to be ant tasks yet for Git. The problem with both of those is that they require you to pull down the whole hunk ‘o code and I’m trying to avoid that in this use case. Keep it simple, stupid. Adding more layers of code and complication onto a project that doesn’t need it is bad.

    Finally I went back to git and re-read how the whole distributed deployment works. I know how to clone a repository, which essentially gets me ‘trunk.’ And I know that a pull does a fetch followed by a merge, in case I’d done any edits, and it saves my edits. Hence merge, and why I dig it for dev. At length it occurred to me that what I wanted was to check out the git repo without downloading the code at first. Well I know how to do that:

    $ git clone --no-hardlinks --no-checkout https://github.com/wp-cli/wp-cli.git wp-cli
    Cloning into 'wp-cli'...
    remote: Counting objects: 10464, done.
    remote: Compressing objects: 100% (3896/3896), done.
    remote: Total 10464 (delta 6635), reused 10265 (delta 6471)
    Receiving objects: 100% (10464/10464), 1.20 MiB | 1.04 MiB/s, done.
    Resolving deltas: 100% (6635/6635), done.
    

    That brings down just a .git folder, which is small. And from there, I know how to get a list of tags:

    $ git tag -l
    v0.3.0
    [...]
    v0.8.0
    v0.9.0
    

    And now I can check out version 8!

    $ git checkout v0.8.0
    Note: checking out 'v0.8.0'.
    
    You are in 'detached HEAD' state. You can look around, make experimental
    changes and commit them, and you can discard any commits you make in this
    state without impacting any branches by performing another checkout.
    
    If you want to create a new branch to retain commits you create, you may
    do so (now or later) by using -b with the checkout command again. Example:
    
      git checkout -b new_branch_name
    
    HEAD is now at 8acc57d... set version to 0.8.0
    

    Well damn it, that was simple. But I don’t want to be in a detached head state, as that means it’s a little weird to update. I mean, I could do it with a switch back to master, a pull, and a checkout again, but then I thought about local branches. Even though I’m never making changes to core code (ever), let’s be smart.

    Linus Torvalds
    Linus Torvalds flipping Nvidia the bird
    One codebase I use has the master branch as their current version, which is cool. Then there’s a 1.4.5 branch where they’re working on everything new, so when a new version comes out, I can git pull and be done.(In this moment, I kind of started to get how you should be using git. In SVN, trunk is where you develop and you check into tags (for WordPress at least) to push finished versions. In git, you make your own branch, develop there, and merge back into master when you’re ready to release. Commence head desking.)

    One conundrum was that there are tags and branches, and people use them as they see fit. While some of the code I use defines branches so I can check out a branch, others just use tags, which are treeish. Thankfully, you can make your own branch off a tag, which is what I did.

    I tried it again with a non-Github slice of code: Mediawiki.(Mediawiki, compared to wp-cli, is huge, and took a while to run on my laptop. It was a lot faster on my server. 419,236 objects vs 10464. I’m just saying someone needs to rethink the whole ‘Clone is faster!’ argument, since it’s slow now, or slow later, when downloading large files. Large files is large.) Now we have a new issue. MediaWiki’s .git folder is 228.96 MiB… Interestingly, my MediaWiki install is about 155MiB in and of itself, and diskspace is cheap. If it’s not, you’ve got the wrong host. Still, it’s a drawback and I’m not really fond of it. Running repack makes it a little smaller. Running garbage collection made it way smaller, but it’s not recommended. This, however, is recommended:

    git repack -a -d --depth=1 --window=1

    It doesn’t make it super small, but hey, it worked.

    Speaking of worked, since the whole process worked twice, I decided to move one of my installs (after making a backup!) over to this new workflow. This was a little odd, but for Mediawiki it went like this:

    git clone --no-hardlinks --no-checkout https://gerrit.wikimedia.org/r/p/mediawiki/core.git wiki2
    mv wiki2/.git wiki/
    rmdir wiki2
    cd wiki
    git reset --hard HEAD
    

    Now we’re cloning the repo, moving our files, resetting where HEAD is, and I’m ready to set up my install to use the latest tag, and this time I’m going to make a branch (mysite-1.20.3) based on the tag (1.20.3):

    git checkout -b mysite-1.20.3 1.20.3
    

    And this works great.

    The drawback to pulling a specific tag is that when I want to update to a new tag (1.20.4 let’s say), I have to update everything and then checkout the new tag in order to pull down the files. Now, unlike svn, I’m not making a full copy of my base code with every branch or tag, it’s all handled by head files, so there’s no harm keeping these older versions. If I want to delete them, it’s a simple git branch -D mysite-1.20.3 call and I’m done. No code changes (save themes and .htaccess), no merging needed. And if there’s a problem, I can switch back really fast to the old version with git checkout mysite-1.20.3. The annoyance is that I just want to stay on the 1.20 branch, don’t I? Update the minors as they come, just like the WP minor-release updater only updates changed files.

    Thus, I asked myself if there was a better way and, in the case of MediaWiki, there is! In world of doing_it_right(), MediaWiki has branches and tags(So does WP if you looked at trac.), and they use branches called ‘REL’. If you’re not sure what branches your repo uses, type git remote show origin and it will list everything. There I see REL1_20 and since I’m using version 1.20.3 here, I surmised that I can actually do this instead:

    git checkout -b mysite-REL1_20 origin/REL1_20 
    

    This checks out my branch and says “This branch follows along with REL1_20.” so when I want to update my branch it’s two commands:

    git fetch --all
    git pull
    

    The fetch downloads the changesets and the pull applies it. It looks like this in the real world (where I’m using REL1_21 since I wanted to test some functionality on the alpha version):

    $ git fetch --all
    Fetching origin
    remote: Counting objects: 30, done
    remote: Finding sources: 100% (14/14)
    remote: Getting sizes: 100% (17/17)
    remote: Total 14 (delta 10), reused 12 (delta 10)
    Unpacking objects: 100% (14/14), done.
    From https://gerrit.wikimedia.org/r/p/mediawiki/core
       61a26ee..fb1220d  REL1_21    -&gt; origin/REL1_21
       80347b9..431bb0a  master     -&gt; origin/master
    $ git pull
    Updating 61a26ee..fb1220d
    Fast-forward
     includes/actions/HistoryAction.php | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    

    This doesn’t work on all the repos, as not everyone follows the same code practices. Like one repo I use only uses tags. Still, it’s enough to get me fumbling through to success in a way that doesn’t terrify me, since it’s easy to flip back and forth between versions.

    Fine. I’m sold. git’s becoming a badass. The only thing left is to protect myself with .htaccess:

     # SVN and GIT protection
     RewriteRule ^(.*/)?(\.svn|\.git)/ - [F,L]
     ErrorDocument 403 &quot;Access Forbidden&quot;
    

    Now no one can look at my svn or git files.

    And to figure out how to get unrelated instances of git in subfolders to all update(Mediawiki lets you install extensions via git, but then you don’t have a fast/easy way to update…):

    #!/bin/sh
     
    for i in `find ./ -maxdepth 1 -mindepth 1 -type d`; do
            cd $i
            git pull
            cd ../
    done
    

    And I call that via ./git.sh which lives in /wiki/extensions and works great.

    From here out, if I wanted to script things, it’s pretty trivial, since it’s a series of simple if/else checks, and I’m off to the races. I still wish every app had a WordPress-esque updater (and plugin installer, hello!) but I feel confident now that I can use git to get to where my own updates are faster.

  • Command Line WP

    Command Line WP

    In my new job, there’s a lot of command line work to be done. DreamHost has a mess of scripts I’m getting my comfort with, but also they’ve got this cool thing implemented on a lot of their servers called wp-cli, and that’s what I’m going to talk about today.

    wp-cli is a command line interface to do a lot of sneaky snazzy WP things. While most people are running towards the GUI world of pretty UI and clicks, some of us really like command lines because they’re fast. I mean, I wrote my own command line upgrader just because I wanted to (and was having PHP permissions woes at the time). Command line is just something server people are always going to like, much like people who love driving a stick-shift, or who want to hand-make their own dough for pies. We like to have that extra level on control.

    Aside from being control freaks, however, we CLI jockeys are also insanely lazy. If we can get everything done without having to touch the mouse, or lift our hands from the keyboard, we’re happy. If we can automate things so that all the WordPress installs on a server are magically upgraded in one fell swoop, we’re ecstatic. We all dream of being that guy who walks into a room, presses three keys, and saves the day. (I’ve been that guy, but remember he comes at a cost. I’ve also been the guy who presses three keys and reboots the money trading servers in the middle of our busiest time. Read twice, press enter once.)

    Where wp-cli takes off is in the speed you can perform basic tasks. Typing wp or wp help will get you a list of commands. If you try to run any command outside the WP folders, you’ll get a nice error message. Using Multisite, you have a cool advantage of installing a plugin once and updating it for all your sites. But in cases where you need to have things separated, wp-cli fills the void by letting you script updates. Imagine just writing a simple shell script to upgrade your plugins on all sites?

    Some of you perked up. There are a lot of cases where you don’t want to run Multisite (separate users, special code, whatever), and updating multiple sites under those custom installs really can be a pill. wp core update can be easily scripted to run off a list of your installs, or to just trawl through your directories, look for WP, and update when it’s there.

    Installing

    If you want to install this just on your own account, the directions for installing are on the wp-cli page. But me, I wanted it on my server for all my accounts. Obviously my DreamHost server has it, but this site is still on LiquidWeb (for myriad reasons, one of which is the same as why I didn’t bank at the company where I worked, old habits).

    Their directions are, via git, to install in ~/git/wp-cli, which I don’t want. I decided to put it in /usr/share/wp-cli/ and to do this I just su’d into my root account. Otherwise I could do it all prefacing with sudo, but I’m dangerous like this.

    My first hurdle was the issues I’ve had on git before, simply put the damn thing times out. The fix was so stupidly simple, once I really read into how git works. All I had to do was tell it ‘Use https.’

    git config --global url."https://".insteadOf git://
    

    Suddenly my commands started working and I was able to run the install directions (modified a little):

    git clone --recursive git://github.com/wp-cli/wp-cli.git /usr/share/wp-cli/
    cd /usr/share/wp-cli/
    utils/dev-build
    

    This ran without a hitch. And any update for this, since I’m only ever using trunk, will be as easy as git pull now and then.

    Now on my server, when I try to run certain commands I git this:

    Fatal error: Out of memory (allocated 38797312) (tried to allocate 17 bytes) in /home/userID/public_html/wp-includes/widgets.php on line 635
    

    Two important things to note.

    1. This only happened on one of my accounts.
    2. 37 Megs is a real weird amount of memory.

    I happen to know I usually allocate 64M for my PHP processes, but even bumping this up to 128 didn’t change the fact that at 37M, everything crapped out. I happen to work with one of the leads on wp-cli, and Mike (aka GetSource) let me bounce ideas off him. He offered to help with any questions, but I learn best by doing, so once I sorted out the basics, and was still stumped, I appealed to his greater familiarity. By the time I logged off to clear my head and get dinner, we decided it had to be user permissions. After all, every account on the box used the same PHP instance. Every account had the same rights, etc. It had to be something funny about the profiles, which I’d buy since this server has some accounts that are 15 years old, and it’s the oldest one having the most issues.

    When I picked it back up a day later, I learned something surprising. At first I could get this to run every time on a site that has bbPress, but then I discovered any time I ran a big search (like wp theme status on my multisite, which worked for all other commands), it would also fail. So clearly there’s a memory shenanigan running around here. I tested with and without APC, switched back and forth between PHP handlers (fCGI, suPHP, DSO), and I tried bumping the memory all the way to 128M. Nothing would get me past the weird 37M. In desperation, I changed my php Memory limit to -1. This means no limit. And now it failed on 32M. As I started testing various possibilities, I came across a moment where I set-faulted (this would be from APC cli, don’t use it) and finally grabbed Alex Rabe’s WP Memory Usage, which tells me I was using “Memory : 8.3 of 128 MByte” on the plugins page, so I know WP knows it can have 128M. This lead me down a path of ‘What PHP is CLI using?’ After digging around and verifying it was the same, I started looking at how I’d locked down my server.

    This is where I started banging my head on my keyboard.

    Shell Fork Bomb Protection is a cool thing, in that it stops people from running rough-shod over your server. Of course they way it does this is by restricting the processes you can run via shell. And wp-cli is, say it with me, shell. Once I turned it off, everything worked. I’m not sure if this is something I’d want to do for every server, but since I can count, on one hand, the number of people with access to mine, and I know their passwords are secure, I’m okay with it.