Skip to content

Commit

Permalink
Issue ThinkUpLLC#475: Script to add GPL license to source file header…
Browse files Browse the repository at this point in the history
…s, can be used as a git pre-commit hook

* Replaced use of os.rename to shutil.move to avoid this error on Windows/Cygwin: http://bytes.com/topic/python/answers/41652-errno-18-invalid-cross-device-link-using-os-rename
* Added lic_header.py to .gitignore
* Added usage instructions to README.md
  • Loading branch information
amygdala authored and ginatrapani committed Dec 17, 2010
1 parent 996eacb commit f2924b3
Show file tree
Hide file tree
Showing 4 changed files with 268 additions and 2 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Expand Up @@ -11,5 +11,6 @@ tests/config.tests.inc.php
extras/cron/config
extras/scripts/autodeploy-conf
extras/scripts/migratedb-conf
extras/scripts/lic_header.py
webapp/test_installer/*
build/*
build/*
32 changes: 31 additions & 1 deletion extras/scripts/README.md
Expand Up @@ -36,4 +36,34 @@ Iterates through all database migration files (including any new ones you're tes
* Edit `migratedb-conf` to match your settings
* Run `migratedb` script from thinkup root directory

Example: `./extras/scripts/migratedb`
Example: `./extras/scripts/migratedb`

## lic_header.py (pre-commit hook script for adding the license header to files)

How to test:

0. Install Python if necessary. (If you're using Windows, this script has been tested using Cygwin with Python and git installed.)

1. Copy lic_header.sample.py to a new file lic_header.py, e.g.
cd extras/scripts; cp lic_header.sample.py lic_header.py

2. Edit lic_header.py and change the THINKUP_HOME variable appropriately

3. make lic_header.py executable, e.g.
chmod 755 lic_header.py

4. Test the script by editing some existing php files, e.g. under webapps/_lib, to remove their headers.
You can run the script like this from the thinkup home dir:
% extras/scripts/lic_header.py

(It has a few options; call it with --help to see them)

5. To test the pre-commit hook part, create a file under the .git directory named .git/hooks/pre-commit, containing the following two lines:

#!/bin/sh
./extras/scripts/lic_header.py

Make this file executable (important). It will now run whenever you do a commit. If there were files updated by the script, the commit should not go through.

Windows/Cygwin troubleshooting: If you get a "fatal error - unable to remap same address as parent", here's how to fix:
http://www.mylifestartingup.com/2009/04/fatal-error-unable-to-remap-to-same.html
16 changes: 16 additions & 0 deletions extras/scripts/header_content.txt
@@ -0,0 +1,16 @@
*
* LICENSE:
*
* This file is part of ThinkUp (http://thinkupapp.com).
*
* ThinkUp is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any
* later version.
*
* ThinkUp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with ThinkUp. If not, see
* <http://www.gnu.org/licenses/>.
*
219 changes: 219 additions & 0 deletions extras/scripts/lic_header.sample.py
@@ -0,0 +1,219 @@
#! /usr/bin/env python

# Copy this script to lic_header.py and edit the THINKUP_HOME variable appropriately.

# This script updates the license/copyright information for all .php files (except those in the 'excludedir' list)
# which do not have license information added already.
# The script will indicate which files were modified, so that you can check them.
# This script is intended to be used either in standalone mode, or as a git pre-commit hook.
# ...
# #!/bin/sh
# ./extras/scripts/lic_header.py


import os, getopt, sys
import subprocess
import shutil

#change this to the full path to your ThinkUp installation. E.g.,
# "/home/username/yourpathto/ThinkUp" or
# "C:/yourpathto/ThinkUp"
THINKUP_HOME = None

excludedir = ["/webapp/_lib/extlib", "/.git", "/webapp/_lib/view/compiled_view"]
ignorefns = ["config.sample.inc.php", "config.inc.php"]
license_line = "http://www.gnu.org/licenses/gpl.html"
header_fname = "/extras/scripts/header_content.txt"

verbose = False
quiet = False
backup = False
suffix = ".php"
made_mods = False

help_message = '''
Usage: lic_header.py [options]
-b When modifying files, save "~" backup of original
-v Verbose output
-q Suppress all output
-l <file> Specify license header content
--ltext <file> Specify license header content
--help This help
'''


def update_source(filename, shortfn, copyright):
global backup
global made_mods
global ignorefns

if (shortfn in ignorefns):
return

utfstr = chr(0xef)+chr(0xbb)+chr(0xbf)
fdata = file(filename,"r+").read()
# arghh
has_cp = (fdata.find("LICENSE") > 0) and (fdata.find("This file is part of ThinkUp") > 0)
# print "has_cp: %s" % has_cp
if not has_cp:
made_mods = True
if not quiet:
print >> sys.stderr, "updating "+filename
isUTF = False
nl = get_copyright_namelist(filename, False)
filename_str = filename.replace(THINKUP_HOME, "ThinkUp")
copyright_str = "* " + filename_str + "\n*\n* Copyright (c) 2009-2010 " + nl +"\n"

phpHeader = ""
if (fdata.startswith(utfstr)):
isUTF = True
fdata = fdata[3:]
if (fdata.startswith("<?php\n")):
fdata = fdata[6:]
phpHeader = "<?php\n"

tempfn = "/tmp" + shortfn
nla = get_copyright_namelist(filename, True)
nlc = get_copyright_namelist(filename, False)
fdata = phpHeader + "/**\n*\n" + copyright_str + copyright + build_docblock(nla, nlc) + fdata
if (isUTF):
file(tempfn,"w").write(utfstr+fdata)
else:
file(tempfn,"w").write(fdata)
if backup:
#os.rename(filename, filename+"~") can cause "Invalid cross-device link using os.rename error in Cygwin
shutil.move(filename, filename+"~")
#os.rename(tempfn, filename)
shutil.move(tempfn, filename)

def recursive_traversal(dir, copyright):
# print "in recursive_traversal"
global excludedir
global suffix
global verbose

fns = os.listdir(dir)
if verbose:
print "listing "+dir
for fn in fns:
fullfn = os.path.join(dir,fn)
# if verbose:
# print "got filename: " + fullfn
if (fullfn in [THINKUP_HOME+subdir for subdir in excludedir]):
if not quiet:
print >> sys.stderr, "Excluding: %s" % fullfn
continue
if (os.path.isdir(fullfn)):
recursive_traversal(fullfn, copyright)
else:
if (fullfn.endswith(suffix)):
if verbose:
print "checking whether source needs updating:" + fullfn
update_source(fullfn, fn, copyright)


def build_docblock(nla, nlc):
if nla is not "":
nla += "\n"
docblock = ("%s" + build_docblock_lic_copy(nlc) +"*/\n") % (nla,)
return docblock

def build_docblock_lic_copy(nl):
global license_line
return "* @license %s\n* @copyright 2009-2010 %s\n" % (license_line, nl)

def get_copyright_namelist(filename, emailsp):

#run a git command
if(emailsp):
sub = subprocess.Popen(['git', 'shortlog', '-se', '--numbered', 'HEAD', filename],
stdout=subprocess.PIPE)
else:
sub = subprocess.Popen(['git', 'shortlog', '-s', '--numbered', 'HEAD', filename],
stdout=subprocess.PIPE)
sub.wait()
oput = sub.stdout.readlines()

# print "output: %s" % oput
clist= [numstrip(i, emailsp) for i in oput]
if len(clist) > 0:
if (emailsp):
return '\n'.join(clist)
else:
return ', '.join(clist)
else:
return ""

def numstrip(line, emailsp):
nl = line.strip().split('\t')[1]
if (emailsp):
nl = nl.replace("@", "[at]")
nl = nl.replace(".", "[dot]")
nl = "* @author " + nl
return nl


class Usage(Exception):
def __init__(self, msg):
self.msg = msg


def main(argv=None):

global verbose
global backup
global header_fname
global THINKUP_HOME

if THINKUP_HOME is None:
print >> sys.stderr, "Edit this script to set THINKUP_HOME"
return 2

hfname = THINKUP_HOME + header_fname

if argv is None:
argv = sys.argv

try:
try:
opts, args = getopt.getopt(argv[1:], "l:vqb", ["help", "ltext="])
except getopt.error, msg:
raise Usage(msg)

# option processing
for option, value in opts:
# print "option %s, value %s" % (option, value)
if option == "-v":
verbose = True
print "setting verbose to true"
if option == "-q":
quiet = True
print "setting quiet to true"
if option == "-b":
backup = True
print "setting backup to true"
if option in ("-h", "--help"):
raise Usage(help_message)
if option in ("-l", "--ltext"):
hfname = value

if verbose:
print "ThinkUp directory: %s" % (THINKUP_HOME,)
print "header file name: %s" % hfname

cright = file(hfname,"r+").read()
recursive_traversal(THINKUP_HOME, cright)
if made_mods:
return 1
else:
return 0

except Usage, err:
print >> sys.stderr, sys.argv[0].split("/")[-1] + ": " + str(err.msg)
print >> sys.stderr, "\t for help use --help"
return 2


if __name__ == "__main__":
sys.exit(main())

0 comments on commit f2924b3

Please sign in to comment.