a unix shell command-line program
This shell script
It also renames the previously-installed NetBeans-nightly.app to a name that includes its date.
If someone would like to modify this script for Cygwin and/or Linux, I will post those versions as well. (Should be trivial to do.)
Example:
1 Z% /Applications/NetBeans/get-netbeans-nightly [ Current nightly build is 201001170201 ] [ Latest installed date is 200909011401 ] [ Running subshell that uses 'sudo' -- may need YOUR password (not root's). ] Password: [ Start downloading netbeans-trunk-nightly-201001170201-src ] [ Start downloading netbeans-trunk-nightly-201001170201 ] [ Download the NetBeans Mac installer ] [ Launch the installer; waiting for you to quit it ] [ Disk /Volumes/NetBeans Dev 201001170201 ejected ] [ Wait for add-in downloading and unzipping to finish ] [ Start unzipping netbeans-trunk-nightly-201001170201 ] [ Unzipped netbeans-trunk-nightly-201001170201.zip ] [ Start unzipping netbeans-trunk-nightly-201001170201-src ] [ Unzipped netbeans-trunk-nightly-201001170201-src.zip ] [ Merge the downloaded add-ons into the installed app ] [ Keep NetBeansDev200909011401.app ] [ Rename to NetBeans-nightly.app and make symlink NetBeansDev201001170201.app ] [ Empty the download dir ] [ Removing old versions: NetBeansDev200808201401.app ] [ Quitting ] 2 Z% /Applications/NetBeans/get-netbeans-nightly [ Current nightly build is 201001170201 ] [ Latest installed date is 200909011401 ] [ Current nightly build is already installed ] [ Quitting ] 3 Z%
Here’s the get-netbeans-nightly
program:
#!/bin/zsh # http://Yost.com/computers/netbeans-nightly # See bug list at end. # See notice of copyright and license at end. # REQUIREMENTS: Mac OS X 10.5.x # Mac OS X 10.4 can't do "open -W". # Anyone care to provide a workaround? #------------------------------------------------------------------------------- # Constants commandName=${0##*/} commandDir=${0%/*} if [[ "$commandDir" == "$commandName" ]] ; then commandDir=`pwd` ; fi if [[ ${commandDir##/} == "$commandDir" ]] ; then commandDir=`pwd`/$commandDir ; fi commandDir=$commandDir/ usageText=" Usage: $commandName Get the latest Mac version of the nightly build of NetBeans Java SE edition plus some additional NetBeans packs, then install the app and merge in the additional NetBeans packs. 2008-07-26 Dave@Yost.com " # A parenthesized value assigned to a variable is # a list of space-separated values. # suffixes of zip files to download and unzip, for example: # netbeans-trunk-nightly-200808111401-src suffixesUnzipped=(src) # suffixes of zip files to download and unzip, for example: # netbeans-trunk-nightly-200808111401-javadoc suffixesZipped=(javadoc) # NetBeans packs to extract from netbeans-trunk-nightly-[datetime].zip # For example: (cnd2 ruby2 php1 groovy1) nbPacks=(cnd3) # Local names nbdir=/Applications/NetBeans appname=NetBeans-nightly.app # NetBeans names urlBase=http://bits.netbeans.org/dev/nightly/latest zipUrlBase=$urlBase/zip baseBaseName=netbeans-trunk-nightly appNamePrefix=NetBeansDev # derived names dldir=$nbdir/downloads appNightly=$nbdir/$appname numberOfOldVersionsToKeep=2 suffixes=($suffixesUnzipped $suffixesZipped) #------------------------------------------------------------------------------- main() { setopt ERR_RETURN getOpt $@ setVars if [[ $isGetNetBeansSubshell != true ]] { reportVersions quitIfNotNew requireOpenMinusW execRootSubshell } doSubshellWork } getOpt() { if [[ $# != 0 ]] { echo2 $usageText exit 2 } } setVars() { date=$(getBuildDate) baseName=${baseBaseName}-$date dlbase=$dldir/$baseName appNameAsInstalled="NetBeans Dev $date.app" appNameWithDate=$(rmSpacesFromName $appNameAsInstalled) appdest=$nbdir/$appNameWithDate/Contents/Resources/NetBeans jobs=() } reportVersions() { echo2 "[ Current nightly build is $date ]" cd $nbdir local current=$(echo ${appNamePrefix}*(N) | fmt -1 | tail -1) if [[ $current != "" ]] { local currentDate=$(extractDate $current) echo2 "[ Latest installed date is $currentDate ]" } } quitIfNotNew() { # If $appNameWithDate is not a symlink, the it could be incomplete. if [[ -h $appNameWithDate ]] { echo2 "[ Current nightly build is already installed ]" exit 1 } } requireOpenMinusW() { # Mac OS X 10.4 can't do "open -W". # Anyone care to provide a workaround? if uname -r | grep '^9\.' > /dev/null ; then echo2 "[ Sorry, requires Mac OS X 10.5.x ]" exit 2 else return 0 fi } execRootSubshell() { export isGetNetBeansSubshell=true echo2 "[ Running subshell that uses 'sudo' -- may need YOUR password (not root's). ]" exec sudo -E zsh -c $commandDir$commandName echo2 "${commandName}: can't exec subshell" exit 2 } doSubshellWork() { ensureIsRoot mkdir -p $dldir # Each function (recursively) from now on must do its own cd. startGettingAddIns rmOldStuffFromDownloadDir getAppInstaller runAppInstaller finishGettingAddIns mergeAddIns doAppRenames emptyDownloadDir removeExcessOldVersions } ensureIsRoot() { if [[ $UID != 0 ]] { echo "$commandName bug: subshell must run as root." exit 2 } } startGettingAddIns() { if [[ $#nbPacks != 0 ]] { if [[ -e $dldir/$baseName ]] { mv -f $dldir/$baseName $dldir/tmp # Salvage gutted hierarchy. } getAddInUnzipped "" } for suffix in $suffixesUnzipped ; { getAddInUnzipped $suffix } for suffix in $suffixesZipped ; { getAddInZipped $suffix } } rmOldStuffFromDownloadDir() { #echo2 "[ Remove old stuff from the download dir ]" cd $dldir rmOldStuff rmOldStuff tmp/ } getAppInstaller() { cd $dldir file=${baseName}-javase-macosx.dmg if [[ ! -r $file ]] { mkdir -p tmp cd tmp local what if [[ -e $file ]] then what="Resume downloading" else what=Download fi echo2 "[ $what the NetBeans Mac installer ]" doCurl $urlBase/bundles/${baseName}-javase-macosx.dmg mv $file .. } else { echo2 "[ Already downloaded the NetBeans Mac installer ]" } } runAppInstaller() { cd $nbdir rmbg $appNameAsInstalled open -W ${dlbase}-javase-macosx.dmg echo "[ Launch the installer; waiting for you to quit it ]" open -W /Volumes/NetBeans\ Dev\ $date/NetBeans\ Dev\ $date.mpkg echo "[ $(diskutil eject /Volumes/NetBeans\ Dev\ $date) ]" & if [[ ! -r $appNameAsInstalled ]] { echo2 "[ Install failed ]" # BUG! If you get here, background processes are not killed. exit 2 } rmbg $appNameWithDate mv $appNameAsInstalled $appNameWithDate } finishGettingAddIns() { waitIfThereAreJobs "add-in downloading and unzipping" } mergeAddIns() { if [[ $#suffixes != 0 || $#nbPacks != 0 ]] { cd $nbdir echo2 "[ Merge the downloaded add-ons into the installed app ]" for suffix in $suffixesUnzipped ; { mve ${dlbase}-$suffix $appdest/$suffix } for suffix in $suffixesZipped ; { mve ${dlbase}-${suffix}.zip $appdest/${suffix}.zip } for x in $nbPacks ; { mve $dlbase/netbeans/$x $appdest/$x } } } doAppRenames() { cd $nbdir local oldAppNameWithDate=$( ls -l $nbdir \ | sed -n "s,.* \(${appNamePrefix}.*\) ->.*,\1,p" ) if [[ -n $oldAppNameWithDate ]] { echo2 "[ Keep $oldAppNameWithDate ]" rmbg $oldAppNameWithDate mv $appname $oldAppNameWithDate } else { # There was no symlink so we don't know what to name it. echo2 "[ Delete old $appname ]" rmbg $appname } echo2 "[ Rename to $appname and make symlink $appNameWithDate ]" mv $appNameWithDate $appname ln -s $appname $appNameWithDate } emptyDownloadDir() { echo2 "[ Empty the download dir ]" cd $dldir rmDownloads rmDownloads tmp/ } removeExcessOldVersions() { cd $nbdir local versions="$(echo ${appNamePrefix}*(N) | fmt -1 | grep -v $appNameWithDate)" numberToDelete=$(( $(echo "$versions" | wc -l) - $numberOfOldVersionsToKeep )) if [[ $numberToDelete > 0 ]] { local -a files files=($( echo "$versions" \ | sed -n 1,${numberToDelete}p )) echo2 "[ Removing old versions: $files ]" rmbg $files } } #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - getBuildDate() { curl -l $zipUrlBase/ 2> /dev/null \ | sed -n 's,.*trunk-nightly-\([0-9]*\)\.zip.*,\1,p' } rmSpacesFromName() { echo $1 \ | sed 's/ //g' } extractDate() { echo $1 | sed "s,$appNamePrefix\(.*\).app,\1," } rmOldStuff() { toRemove=($(echo $1${baseBaseName}*(N) | fmt -1 | grep -v $date)) || true for x in $toRemove ; { #echo2 "[ Removing $x ]" rmbg $x } } getAddInUnzipped() { local suffix if [[ -n $1 ]] { suffix=-$1 } else { suffix= } ( trap - INT QUIT TERM ZERR EXIT cd $dldir mkdir -p tmp local zipfile=$baseName$suffix.zip local dest=$baseName$suffix if needsUnzipping $dest ; then if [[ ! -r $zipfile ]] { cd tmp downloadAndUnzipFile $zipfile $dest } else { cd tmp local what if [[ -e $dest ]] then what=Resume else what=Start fi echo2 "[ $what unzipping previously-downloaded $dest ]" unzipFile $zipfile $dest } else echo2 "[ Already downloaded and unzipped $zipfile ]" fi ) & jobs+=$! } getAddInZipped() { local suffix if [[ -n $1 ]] { suffix=-$1 } else { suffix= } ( trap - INT QUIT TERM ZERR EXIT cd $dldir mkdir -p tmp local zipfile=$baseName$suffix.zip if [[ ! -r $zipfile ]] then cd tmp downloadAndUnzipFile $zipfile '' else echo2 "[ Already downloaded $zipfile ]" fi ) & jobs+=$! } needsUnzipping() { if [[ ! -e $1 ]] return 0 if [[ $1 = $baseName ]] { dlbaseNeedsRefreshing } else { false } } dlbaseNeedsRefreshing() { for x in $nbPacks ; { if [[ ! -e $dlbase/netbeans/$x ]] { return 0 } } return 1 } waitIfThereAreJobs() { if [[ -n $jobstates ]] { echo2 "[ Wait for $1 to finish ]" wait } } downloadAndUnzipFile() { zipfile=$1 dest=$2 # If unzip would take stdin, we would # curl | tee $zipfile | unzip --stdin $dest local what if [[ -e $zipfile ]] then what=Resume else what=Start fi if [[ -n $dest ]] then target=$dest else target=$zipfile fi echo2 "[ $what downloading $target ]" if downloadZipFile $zipfile ; then if [[ -n $dest ]] { echo2 "[ Start unzipping $dest ]" unzipFile $zipfile $dest } fi } downloadZipFile() { doCurl $zipUrlBase/$1 if file $1 | grep -i 'zip:.*zip' > /dev/null ; then mv $1 .. else echo2 "**** ERROR problem with $1" return 1 fi } unzipFile() { doUnzip $2 mv $2 .. echo2 "[ Unzipped $1 ]" } doCurl() { local curlQuiet=-s curl --continue-at - -O $curlQuiet $1 result=$? # echo "[ curl result $result $1 ]" return $result } doUnzip() { export zipname=$1 mkdir -p $zipname ( cd $zipname unzip -q -u -o ../../$zipname.zip ) } mve() { if ! mv $1 $2 ; then echo2 "**** ERROR problem with $1" fi } rmDownloads() { toRemove=($1${baseBaseName}*(N)) || true if [[ $#toRemove != 0 ]] { for x in "$toRemove[@]" ; { #echo2 "[ Removing $x ]" rmbg $x } } } rmbg() { for x in $@ ; { if [[ $x != "" ]] { if [[ -d $x ]] { mv $x ${x}-DELETING rm -rf ${x}-DELETING &! } else { rm -f $x } } } return 0 } echo2() { echo 1>&2 "$@" } trap ' print 1>&2 "\n[ Killed ]" ' INT QUIT TERM trap ' print 1>&2 "\n[ Aborting ]" killJobs exit 2 ' ZERR trap ' print 1>&2 "[ Quitting ]" killJobs ' EXIT killJobs() { #print 1>&2 "[ killing $jobs ]" kill $jobs 2> /dev/null } #------------------------------------------------------------------------------- main $@ exit # BUGS # If you quit the installer before it's done, # the script exits, but background curl and/or unzip commands # are not killed as they should be. # Killing with ^C tries to clean up but fails in odd ways. # If anyone wants to fix this, be my guest. #=============================================================================== # Copyright 2008 Dave Yost <Dave@Yost.com> # All rights reserved. # This version is # get-netbeans-nightly 2.2 2010-01-17 # which at time of this publication can be found at: # http://Yost.com/computers/netbeans-nightly # Redistribution and use in the form of source code or derivative data built # from the source code, with or without modification, are permitted provided # that the following conditions are met: # 1. THE USER AGREES THAT THERE IS NO WARRANTY. # 2. If and only if appropriate, the above phrase "This version is" must be # followed by the phrase "a modified form of" or "extracted from" or # "extracted and modified from". # 3. Redistributions of source code must retain this notice intact. # 4. Redistributions in the form of derivative data built from the source # code must reproduce this notice intact in the documentation and/or other # materials provided with the distribution, and each file in the derivative # data must reproduce any Yost.com URI included in the original distribution. # 5. Neither the name of Dave Yost nor the names of its contributors may be # used to endorse or promote products derived from this software without # specific prior written permission. # 6. Written permission by the author is required for redistribution as part # of a commercial product. # This notice comprises all text from "Copyright" above through here.