We recently changed the way we deploy our applications, creating a full build monthly and patch versions as needed in the interim. Patches, however, are problematic. Powerbuilder creates it's own little proprietary DLL-like files called PBDs in whatever directory the source file it is generated from is located. For large projects like ours, hunting down all the changed PBDs, zipping them up, and moving them to the network gets to be rather time consuming.
This little app allows our compile guy to select the system he's working on. It brings up a list of all the PBDs in the project. He selects the ones that have changed, changes the version number (if necessary - the app does it's best to get the next one), clicks deploy, and that's when the magic starts. A new folder on the network is created, ALL of the source code for the app gets archived, and then a second zip containing only the files to be deployed is created. Each app has a file called libs.txt in it's root directory that has a list of all the files that comprise the app.
I'm reasonably happy with this. There's no real security though as it only runs on the compiler machine. At some point in the future I would like to replace my kludgy INI file with an XML config file (problem is I don't know jack about XML). I would also like to make the zip self-extracting (I know how using Archive::Zip, but our client sites do not have Perl installations, so I will need to call the WinZip self-extractor), as well as automatically FTPing the release to our update site. I might even have it automatically e-mail our clients informing them of the update.
Anyhow, without further ado, the code:
The INI looks something like this:#!perl use strict; use warnings; use CGI; use Archive::Zip; use File::Slurp; use File::Copy; # Debugging info $|=1; print "Content-type: text/html\n\n"; use CGI::Carp('fatalsToBrowser'); # Get our configuration my %config = read_ini("AppDeployment.ini"); # Create CGI objects and variables my $request = new CGI; # Get parameters my $action = $request->param("action"); my $system = $request->param("apps"); my $version = $request->param("version"); my @pbds = $request->param("pbds"); # Validate parameters if(!defined($action)) { $action = "Select"; } # Print the page header #print $request->header(); my $JAVASCRIPT=<<END; function SelectAll() { for (var i=0;i<document.deploy.elements.length;i++) { var e = document.deploy.elements[i]; if (e.name == 'pbds') { e.checked = true; } } } function DeselectAll() { for (var i=0;i<document.deploy.elements.length;i++) { var e = document.deploy.elements[i]; if (e.name == 'pbds') { e.checked = false; } } } END print $request->start_html(-title=>"DEVNET, Inc. AppDeployment Agent", -script=>$JAVASCRIPT); # Take action based upon user's selection if($action eq "Select") { print $request->startform(-name=>"deploy", -method=>"POST", -action=>"AppDeployment.pl", -enctype=>"application/x-www-form-urlencoded"); # Parse the list of applications my $apps = $config{SYSTEMS}; my @apps; push @apps, split(",",$apps); # Prompt user for selection of system print $request->h3("Select a System:"); print $request->radio_group(-name=>'apps', -values=>\@apps, -linebreak=>'true'); print $request->p(); print $request->submit(-name=>'action', -value=>'Select'); print " \n"; print $request->defaults("Reset"); # If we previously selected a system, show the list of PBDs if($system ne "") { my $path = "$config{LOCAL_PATH}$system\\libs.txt"; my %library_list = read_libs($path); # Increment version $version = $config{$system}; my($major, $minor, $micro) = split(/\./,$version,3); ++$micro; $version = sprintf("%d.%d.%d",$major,$minor,$micro); $config{$system}=$version; # Prompt user for selection print $request->p(); print $request->h3("Select PBDs to Zip:"); # Give user a list of PBDs to selelct my @libs = sort keys %library_list; print $request->checkbox_group(-name=>'pbds', -values=>\@libs, -linebreak=>'true'); print $request->p(); print $request->button(-name=>'select_all', -value=>'Select All', -onClick=>"javascript:SelectAll(); return false;"); print " \n"; print $request->button(-name=>'deselect_all', -value=>'Deselect All', -onClick=>"javascript:DeselectAll(); return false;"); print $request->p(); print "Version \n"; print $request->textfield(-name=>'version', -default=>$version, -size=>10, -maxlength=>10); print $request->p(); print $request->submit(-name=>'action', -value=>'Deploy'); print " \n"; print $request->reset; } print $request->endform(); } elsif($action eq "Deploy") { my (undef,undef,undef,$mday,$mon,$year) = localtime(time); my $date = sprintf("%02d-%02d-%02d",$mon,$mday,$year + 1900); my $path = "$config{LOCAL_PATH}$system\\libs.txt"; my $base = "$config{ZIP_PATH}$system $version $date"; my %library_list = read_libs($path); my $zip; my $zip_name = "$base\\$system $version $date-patch.zip"; my $source_name = "$base\\$system $version $date-source.zip"; # Make a new directory to put this in mkdir "$base", 0777 or die "Could not create directory $base.\n"; # Save off the changes.txt for this build. copy("$config{LOCAL_PATH}$system\\changes.txt",$base) or warn "Could + not copy changes.txt.\n"; # Archive all source used to make the build $zip = Archive::Zip->new(); foreach (sort keys %library_list) { my $file = $library_list{$_}; $file =~ s/\.pbd/\.pbl/i; # Swap PBD for PBL in the library list $zip->addFile($file) or warn "Can't add file $file to source zip.\ +n"; } # Save the PBR too, and save the zip $zip->addFile("$config{LOCAL_PATH}$system\\$system.pbr") or warn "Ca +n't add file $system.pbr to source zip.\n"; $zip->writeToFileNamed($source_name); # Archive the PBDs that go into the patch $zip = Archive::Zip->new(); foreach (@pbds) { my $file = $library_list{$_}; $zip->addFile($file) or warn "Can't add file $file to deployment z +ip.\n"; } # Get the EXE too! $zip->addFile("$config{LOCAL_PATH}$system\\$system.exe"); $zip->writeToFileNamed($zip_name); # Update the INI write_ini("AppDeployment.ini"); # Give some feedback print $request->h3("Success!"); print "Successfully built deployment package $zip_name.\n"; print $request->p(); } # All done! Print the page and get out. print $request->end_html(); exit; sub read_ini { my @local_ini = read_file(shift); my ($key,$value); my %config; foreach(@local_ini) { chomp; # We should have a usable value now, add it to the hash ($key,$value) = split("=",$_); next unless $value; $config{$key} = $value; } # Give configuration back to caller return %config; } sub write_ini { my $ini = shift; open INI, ">$ini" or die "Could not open configuration file for writ +ing.\n"; foreach(sort keys %config) { print INI "$_=$config{$_}\n"; } close INI; } sub read_libs { my @libs = read_file(shift); my %library_list; # Strip path info for each PBL foreach(@libs) { chomp; next if /^\s*$/; s/;//; s/\.pbl/\.pbd/i; # Swap PBL for PBD in the library list my $dir = $_; my $pbl = $& if (/([\w\.\&])+$/); $pbl =~ s/$&/\L$&/; $library_list{$pbl} = $dir; } return %library_list; }
Any suggestions or improvements always welcome :)Assessor=1.5.499 LOCAL_PATH=d:\pb8\ SYSTEMS=Assessor,Treasurer Treasurer=1.5.200 ZIP_PATH=p:\
MrCromeDome
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Application Deployment With Perl
by vek (Prior) on Apr 03, 2003 at 19:34 UTC | |
by MrCromeDome (Deacon) on Apr 03, 2003 at 19:52 UTC | |
by vek (Prior) on Apr 03, 2003 at 21:29 UTC | |
by buttroast (Scribe) on Apr 12, 2003 at 02:10 UTC | |
by vek (Prior) on Apr 12, 2003 at 19:16 UTC |