#!/bin/bash
###########################################################################
#
# Shell program to release a SourceForge.net project module.
#
# Copyright 2001-2002, William Shotts <bshotts@users.sourceforge.net>.
#
# This program 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.
#
# This program 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.
#
# SourceForge.net is a trademark of VA Software, Inc.
#
# This software is part of the LinuxCommand.org project, a site for
# Linux education and advocacy devoted to helping users of legacy
# operating systems migrate into the future.
#
# You may contact the LinuxCommand.org project at:
#
# http://www.linuxcommand.org
#
# Description:
#
# This is a program to release a module on SourceForge.net. It
# performs the following functions:
#
# 1. If needed, it creates a "description file" which contains a
# short prose description of the module. This description is
# used in the release announcements. (see below)
#
# 2. If needed, it creates a CHANGELOG file.
#
# 3. It invokes an editor (determined from the EDITOR environment
# variable) and allows you to edit the CHANGELOG for the current
# release.
#
# 4. It performs a final CVS commit and tags the CVS tree with a
# release tag that encodes the module's version number and
# release date.
#
# 5. A compressed tar file is created and uploaded to SourceForge.net.
#
# 6. A release announcement is constructed and sent to your
# project's announce mailing list.
#
# The program requires two command arguments. The first is the
# module name which must be a name of a directory in the modules
# directory. Please see the sf-tools.README file for a
# description the expected layout of directories. The second
# argument is the module's version number. The following version
# number formats are supported:
#
# 0.0.0
# 0.0.00
# 0.0word0
# 0.0word00
#
# where "0" is any numeral 0-9 inclusive and
# "word" is any of the following: alpha, beta, pre, test
#
# Usage:
#
# sft_release_module -h | --help | -m module -v version
#
# Options:
#
# -h, --help Display this help message and exit.
# -m module module (directory) name
# -v version version number
#
#
# Revisions:
#
# 02/02/2001 File created by lc_new_script v.2.0.6
# 03/18/2001 Changed name of ftp upload site (0.0.2)
# 03/07/2002 Various cosmetic updates (0.0.3)
#
# $Id: sft_release_module,v 1.3 2002/03/09 16:50:43 bshotts Exp $
#
###########################################################################
###########################################################################
# Constants
###########################################################################
PROGNAME=$(basename $0)
VERSION="0.0.3"
PROJECT_NAME="your_project" # Put your project name here!
MAIL_LIST="${PROJECT_NAME}-announce@lists.sourceforge.net"
# Name of your announce maining list.
# Change this to "" to supress mailing a
# release announcement
MAILER=mail # Name of program to send mail
PROJECT_ROOT=~/$PROJECT_NAME
###########################################################################
# Functions
###########################################################################
function clean_up
{
#####
# Function to remove temporary files and other housekeeping
# No arguments
#####
rm -f ${TEMP_FILE1}
}
function error_exit
{
#####
# Function for exit due to fatal program error
# Accepts 1 argument
# string containing descriptive error message
#####
echo "${PROGNAME}: ${1:-"Unknown Error"}" >&2
clean_up
exit 1
}
function make_temp_files
{
#####
# Function to create temporary files
# No arguments
#####
# Use user's local tmp directory if it exists
if [ -d ~/tmp ]; then
TEMP_DIR=~/tmp
else
TEMP_DIR=/tmp
fi
# Temp file for this script, using paranoid method of creation to
# insure that file name is not predictable. This is for security to
# avoid "tmp race" attacks. If more files are needed, create using
# the same form.
TEMP_FILE1=$(mktemp -q "${TEMP_DIR}/${PROGNAME}.$$.XXXXXX")
if [ "$TEMP_FILE1" = "" ]; then
error_exit "cannot create temp file!"
fi
}
function graceful_exit
{
#####
# Function called for a graceful exit
# No arguments
#####
clean_up
exit
}
function term_exit
{
#####
# Function to perform exit if termination signal is trapped
# No arguments
#####
echo "${PROGNAME}: Terminated"
clean_up
exit
}
function int_exit
{
#####
# Function to perform exit if interrupt signal is trapped
# No arguments
#####
echo "${PROGNAME}: Aborted by user"
clean_up
exit
}
function usage
{
#####
# Function to display usage message (does not exit)
# No arguments
#####
echo "Usage: ${PROGNAME} -h | --help | -m module -v version"
}
function helptext
{
#####
# Function to display help message for program
# No arguments
#####
local tab=$(echo -en "\t\t")
cat <<- -EOF-
${PROGNAME} ver. ${VERSION}
Program to release a SourceForge.net project module.
$(usage)
Options:
-h, --help Display this help message and exit.
-m module module (directory) name
-v version version number
-EOF-
}
function check_version
{
#####
# validates a module version number
# Arguments:
# 1 version number (required)
#####
# Fatal error if required arguments are missing
if [ "$1" = "" ]; then
error_exit "check_version: missing argument 1"
fi
echo $1 | awk '
/^[0-9]\.[0-9]\.([0-9]|[0-9][0-9])$/ { print $0 }
/^[0-9]\.[0-9](alpha|beta|pre|test)([0-9]|[0-9][0-9])$/ { print $0 }
'
} # end of check_version
function release_tag
{
#####
# creates cvs release tag
# Arguments:
# 1 version number (required)
#####
# Fatal error if required arguments are missing
if [ "$1" = "" ]; then
error_exit "release_tag: missing argument 1"
fi
local release_date
local release_version
release_date=$(date +"%Y-%m-%d")
release_version=$(echo $1 | awk '{gsub(/\./, "_"); print $0}')
echo "release_ver_${release_version}--${release_date}"
} # end of release_tag
function create_description
{
#####
# Create module description file
# Arguments:
# none
#####
local desc_file=${module}.DESCRIPTION
if [ ! -f $desc_file ]; then
echo -e "\nThere is no description file for this module."
echo -e "Would you like to create one now? (y/n) -> \c"
read foo
if [ "$foo" = "y" ]; then
cat > ${module}.DESCRIPTION <<- _EOF_
$(underline_string "$module")
SFT: Please edit the module description in the lines above.
SFT: All lines starting with "SFT:" will be ommited during
SFT: processing.
_EOF_
${EDITOR:-"vi"} +3 $desc_file
fi
fi
} # end of create_description
function create_changelog
{
#####
# Create CHANGELOG file
# Arguments:
# none
#####
echo "CHANGELOG not found. Creating..."
echo -e "$(underline_string "CHANGELOG for $module")\n" > CHANGELOG
echo -e "\n\n\$Id\$" >> CHANGELOG
cvs add CHANGELOG
} # end of create_changelog
function edit_changelog
{
#####
# Edit CHANGELOG file
# Arguments:
# none
#####
head --lines=3 CHANGELOG > $TEMP_FILE1
echo -e "$(underline_string "Version $version -- $(date +"%a, %b %d, %Y %r")" "-")\n\n" >> $TEMP_FILE1
tail --lines=+4 CHANGELOG >> $TEMP_FILE1
${EDITOR:-"vi"} +6 $TEMP_FILE1
cp $TEMP_FILE1 CHANGELOG
} # end of edit_changelog
function underline_string
{
#####
# Underline a string with a specified character
# Arguments:
# 1 string to underline (required)
# 2 underline character (optional)
#####
# Fatal error if required arguments are missing
if [ "$1" = "" ]; then
error_exit "underline_string: missing argument 1"
fi
local ulc=${2:-"="}
echo "$1" | awk --assign ulc=$ulc '
{ print $0
for (i = 0; i < length($0); i++)
{ ul = ul ulc
}
print ul
}
'
} # end of underline_string
function update_cvs
{
#####
# Perform last CVS commit and tag
# Arguments:
# none
#####
echo "Performing CVS commit and tag..."
cvs commit -m "final commit before release"
cvs tag "$(release_tag $version)"
} # end of update_cvs
function mail_release_announcement
{
#####
# Construct release announcement and mail it
# Arguments:
# none
#####
local desc_file=../$module.DESCRIPTION
if [ ! -n "$MAIL_LIST" ]; then
return
fi
echo "Mailing release announcement..."
$MAILER -s "$module $version released" $MAIL_LIST <<- _EOF_
$module version $version has been released.
$([ -f "$desc_file" ] && grep -v "SFT:" $desc_file)
$(cat CHANGELOG)
_EOF_
} # end of mail_release_announcment
function upload_module
{
#####
# Package module and upload to SourceForge.net
# Arguments:
# none
#####
# Create compressed archive
echo "Creating compressed archive..."
mv $module ${module}-${version}
tar czvf ${module}-${version}.tar.gz --exclude CVS ${module}-${version}
mv ${module}-${version} $module
# Transfer archive
echo "Transferring archive to SourceForge.net..."
ncftpput -V upload.sourceforge.net /incoming ${module}-${version}.tar.gz
} # end of upload_module
function release_tag
{
#####
# creates cvs release tag
# Arguments:
# 1 version number (required)
#####
# Fatal error if required arguments are missing
if [ "$1" = "" ]; then
error_exit "release_tag: missing argument 1"
fi
local release_date
local release_version
release_date=$(date +"%Y-%m-%d")
release_version=$(echo $1 | awk '{gsub(/\./, "_"); print $0}')
echo "release_ver_${release_version}--${release_date}"
} # end of release_tag
###########################################################################
# Program starts here
###########################################################################
##### Initialization And Setup #####
# Trap TERM, HUP, and INT signals and properly exit
trap term_exit TERM HUP
trap int_exit INT
# Create temporary file(s)
make_temp_files
##### Command Line Processing #####
if [ "$1" = "--help" -o "$1" = "-h" ]; then
helptext
graceful_exit
fi
# Some sanity checks
if [ "$PROJECT_NAME" = "your_project" -o "$PROJECT_NAME" = "" ]; then
echo >&2 "
PROJECT_NAME has not been set. Please edit this
script and assign your project name to the
PROJECT_NAME constant."
error_exit "Configuration error"
fi
if [ ! -d $PROJECT_ROOT ]; then
echo >&2 "
Project directory does not exist.
Perhaps project name (PROJECT_NAME) is misspelled."
error_exit "Configuration error"
fi
module=
version=
while getopts ":hm:v:" opt; do
case $opt in
m ) module=$OPTARG
;;
v ) version=$OPTARG
;;
h ) helptext
graceful_exit
;;
* ) usage
clean_up
exit 1
esac
done
##### Main Logic #####
# check if module exists
cd ${PROJECT_ROOT}/modules || error_exit "cannot cd to modules directory"
if [ "$module" = "" -o "$version" = "" ]; then
usage
exit 1
fi
if [ ! -d $module ]; then
error_exit "no such directory \"$module\""
fi
if [ "$(check_version $version)" = "" ]; then
error_exit "invalid version number format. Please see README."
fi
if [ ! -f "../${module}.DESCRIPTION" ]; then
create_description
fi
# Create CHANGELOG if needed and edit
cd ${PROJECT_ROOT}/modules/${module} || error_exit "cannot change to ${module} directory"
if [ ! -f CHANGELOG ]; then
create_changelog
fi
edit_changelog
# Perform final CVS commit and CVS tag
cd ${PROJECT_ROOT}/modules/${module} || error_exit "cannot change to ${module} directory"
update_cvs
# Construct compressed archive and upload to SourceForge.net
cd ${PROJECT_ROOT}/modules || error_exit "cannot change to modules directory"
upload_module
# Send release announcement to mailing list
cd ${PROJECT_ROOT}/modules/${module} || error_exit "cannot change to ${module} directory"
mail_release_announcement
graceful_exit
syntax highlighted by Code2HTML, v. 0.9.1