Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Sunday, September 15, 2019

Git pre-commit hook to disallow a substring

This took way longer than it should have to figure out, so I wanted to share in case others are trying to do the same thing.

Add this snippet to your Git pre-commit hook to prevent (in this case) the substring FIXME from making it into a committed file. Since the hook includes the substring 'FIXME' you'll need to commit like this:
git add git-hooks/pre-commit
allowfixme=true git commit -m 'Disallow FIXME in committed files.'
Here's the code. Public domain, unless you really need a license, in which case use the MIT license. If you can, I'd appreciate a link, but no worries if not.
# Disallow 'FIXME' from being committed. 'TODO' is for cross-commit notes of
# pending work, but 'FIXME' is for things that need to be updated before
# committing.
# Set allowfixme=true to allow commit anyway, such as if the file contains
# 'FIXME' for a good reason (like this file), or you have some other reason
# to leave it in the repo for now.
if [ "$allowfixme" != "true" ] ; then
  found_fixme=
  for F in $(git diff --cached --name-only) ; do
    C=$(git ls-files -s "$F" | awk '{print $2}')
    if git cat-file blob "$C" | grep -q 'FIXME' ; then
      if [ -z "$found_fixme" ] ; then
        echo "COMMIT REJECTED: Found 'FIXME' in:"
        found_fixme=1
      fi
      echo "  $F"
    fi
  done
  if [ -n "$found_fixme" ] ; then
    echo "Please remove before committing."
    exit 1
  fi
fi

Sunday, March 13, 2016

Bazel and Golang

As a Google software engineer, I use the internal Blaze build system every day. As far as build systems go, it’s really good. So I figured I’d give Bazel a try, since it’s basically the same thing.

Basically.

The part I guess I forgot about was that it isn’t supported and developed by teams of paid engineers all the time. That said, it’s actually not bad. It just feels incomplete after coming from Blaze.

I’m partway through setting it up to build an existing project in Go, which is just barely supported. The documentation is minimal and unclear, but I think I can help out.

Here’s how to take a $GOPATH-type workspace and make it work with Bazel without mucking up all of your project paths, and without symlinking parts of one workspace into another. Because that’s actually what the documentation tells you to do, and it feels wrong to follow it.

First, install Bazel. I’ll wait.

All done? Oh, you don’t like this writing style? Neither do I. I’ll stop now...

Next:
  1. Go to your $GOPATH (the parent of bin/ pkg/ and src/) and clone the Bazel repository:
    git clone github.com/bazelbuild/bazel
  2. Now we need to make some changes under src/:
    cd src/
    ln -s ../bazel/tools .
  3. Write a WORKSPACE file:
    cat >WORKSPACE <
    load("@bazel_tools//tools/build_rules/go:def.bzl", "go_repositories")
    go_repositories()
    EOF
  4. Write a BUILD file:
    cat >BUILD <
    load("@bazel_tools//tools/build_rules/go:def.bzl", "go_prefix")
    go_prefix("")
    EOF

I’m not sure why the tools symlink is necessary; it may be a bug in the Go rules. They can’t seem to find their automatically-added dependency without it.

And that’s it, besides for writing BUILD files for each of your packages. I do have one more tip for that part, though: Write your BUILD files in the parent directories of your packages. If you do this:
src/github.com/me/mypackage/BUILD:
load("@bazel_tools//tools/build_rules/go:def.bzl", "go_library")
go_library(
name = “mypackage”,
srcs = [“mypackage.go”],
visibility = [“//visibility:public”],
)

Then when you use that package from Go, you’ll need to double the ‘mypackage’ part:
src/github.com/me/pkguser/BUILD:
load("@bazel_tools//tools/build_rules/go:def.bzl", “go_binary”)
go_binary(
name = “pkguser”,
srcs = [“pkguser/pkguser.go”],
deps = [“//github.com/me/mypackage”],
)

src/github.com/me/pkguser/pkguser.go:
package pkguser
import (
“github.com/me/mypackage/mypackage” // Redundant, eh?
)

But if you write the BUILD file one level up, the names work out as you would expect:
src/github.com/me/BUILD:
load("@bazel_tools//tools/build_rules/go:def.bzl", "go_library", “go_binary”)
go_library(
name = “mypackage”,
srcs = [“mypackage/mypackage.go”],
)
go_binary(
name = “pkguser”,
srcs = [“pkguser/pkguser.go”],
deps = [“:mypackage”],
)

src/github.com/me/pkguser/pkguser.go:
package pkguser
import (
“github.com/me/mypackage”
)

On the other hand, your BUILD files will be longer mix together multiple unrelated packages if you do it this way, so you’ll need to decide what is best for your own project.

Saturday, February 6, 2016

Prototypes

If you’ve ever visited a Google office and used the bathroom (great way to begin, right?), you probably saw an episode of Testing on the Toilet (or possibly Learning on the Loo) posted at eye-level. This week I saw a post about prototypes. It said there is a simple rule to determine whether your project is a prototype: Whether it will live a short about of time, and then be deleted.


But that’s not an easy way to identify a prototype. “Why not?” you ask (well, probably not, but it fits my writing style). “It sounds simple!”


No, it’s not simple, because it requires you to see into the future. You need to know that your project will only live a couple weeks, and then go away. But how could you know that?


While working on a personal project (I am implementing the Raft consensus algorithm in Go), I discovered a much simpler test for whether a project is a prototype: If you cannot use test driven development, it is a prototype. Think about it for a moment. I’ll leave a break between the paragraphs.


Really, think about it...


When can you write the tests before the actual code? When you understand the solution, which means you have a plan.


When you’re ready to write an implementation.


When can’t you write the tests first? When you are exploring the problem, trying to understand what the solution might look like.


And guess what? If you try to write tests after writing an exploratory solution, the tests are going to suck. You’ve probably done it before. They cover the easy, obvious cases, but you still worry a little about some corner cases that were just too hard to reach. Maybe you were considerate enough to put todo comments on them, but a todo is not the same as a test.

My experience implementing Raft convinced me that I am writing a prototype. I will do it again when I finish, and I may do it again after that. This is how I learn; I write something that kinda works, and then I know enough to write it better. Eventually better is enough to be production code.

Tuesday, April 21, 2009

Termios

So it's apparently been quite a while since my last update... So why the long break? Well... mostly because I forgot to write. Oops.

Here's a quick summary of what's happened since my last update:

  • Took a weeklong vacation to Amsterdam, Holland, with my wife. A very fun adventure, although there were some difficulties coming home. A couple couple of birthday gifts were in the carry-on instead of the checked bag, and were confiscated because they weren't allowed on the plane.
  • Finally finished stripping wallpaper and painting another room in the house. Only a bit more wallpaper left to strip, and a few more rooms to paint, and it'll be time to move on to more expensive projects.
  • My wife has decreed that we are getting a dog. Next week. She had an attack of Florence Nightingale Syndrome at the veterinary office where she works. An American Bulldog in really bad shape came in after being rescued by the local animal shelter. She decided that when he gets better, we're taking him. I met him a couple weeks later, and still am not as excited as her, but I have been wanting a dog, so hopefully everything goes well.
  • And finally, to the topic of this post: Termios.

Occasionally over the last few months, I have tried to figure out how programs like Vim and libraries like Curses and Readline are able to read and act upon individual characters from the terminal without waiting for the user to hit Enter. I tried internet searches, and even browsing the source of the NCurses library, but didn't have any luck.

Then, last night, curiosity struck again, so I made some more internet searches, and I finally hit upon the answer: Termios. If you open the man page for 'termios,' it will describe a large number of options, many of which deal with serial port settings. (In fact, I dealt with it a couple of years ago while writing a JNI serial port interface, but forgot about it shortly afterward.)

If, however, you skip the serial port settings, you will find an option under the 'c_lflag' constants named ICANON. The description is brief: "Enable canonical mode (described below)."

If you are intrigued enough to read below, you will discover that canonical mode is what determines whether characters are available immediately or queued until an end-of-line is encountered. This is exactly the option I was looking for!

So I had finally found my answer. Of course, at this point, I had completely forgotten what I really wanted to do with it when I first started searching. But I still had to play with it, so I wrote this program. It will read input in groups of up to 15 characters, outputting the groups of character codes. (Some keypresses generate multiple character codes, such as the arrow keys or the Delete key.) Press Escape to exit (that character code, 27, will be printed as well).