#! perl # Copyright (C) 2012, Parrot Foundation. =head1 NAME tools/release/parrot_github_release.pl - automates the 'parrot.github.com' release process =head1 SYNOPSIS $ perl tools/release/parrot_github_release.pl [OPTIONS] =head1 DESCRIPTION This script automates the packaging of the 'parrot.github.com' repository and the archiving of the previous documentation release into the appropriate 'parrot-docsx' repository. In short, this script automates Section X of the Release Manager Guide (F) as outlined in the Release Parrot Github Guide (F). =head1 OPTIONS =over 4 =item B<--docs>=[/path_to/previous/docs/] The path to the directory which contains the previous documentation release. Specifically, the 'docs/' directory of the previous release of parrot. =item B<-h>, B<--help> Displays this help message and exits. =item B<-v>, B<--version> Displays the version and copyright information and exits. =back =head1 LIMITATIONS 1. As written, this script will execute only on *nix (and related) systems. 2. You must execute this script from the parrot root directory, I, './parrot'; otherwise, it will fail. =head1 NOTES 1. This script assumes you are the Release Manager, working on Section X of the Release Manger Guide, and have, therefore, already cut the new Parrot release. 2. You must use a fully qualified path for the '--docs' option. For example, if the path to the previous version of the documentation is contained in F, you I specify the complete path to the 'docs/' directory and may not use "shell expansion" as the name of your home directory, I you may not use C<~/git-work/parrot/docs/>. To do otherwise means the script will fail. =head1 HISTORY * [2012-03-21] Initial version written by Alvis Yardley * [2012-07-13] Made the script more robust Alvis Yardley =head1 SEE ALSO F F =cut use strict; use warnings; use Getopt::Long; use Pod::Usage; use System::Command; use lib qw( ./lib ); use Parrot::Config; use Cwd; # Switches my $docs; # Path to the previous docs release my $help; # Displays help message my $version; # Displays version and copyright information my $result = GetOptions('docs=s' => \$docs, 'h|help' => \$help, 'v|version' => \$version); my $repos; # Path to where to store, temporarily, the repositories # Catch unrecognized switches pod2usage() unless $result; # Display help message if '-h' was given pod2usage(0) if $help; # Display version and copyright information if '-v' was given version() && exit(0) if $version; # Get temporary directory defined in 'Parrot::Config::Generated.pm' get_repo_directory(); # Get 'docs/' directory if not supplied get_docs_directory() unless $docs; # Test 'docs/' directory to ensure it's a valid 'docs/' directory. tst_docs_directory(); # Get VERSION open my $FH, '<', 'VERSION' or stop("Unable to open 'VERSION' file"); chomp($version = <$FH>); close $FH; # Parse version number my ($major, $minor, $patch); # Quiet perlcritic ($major, $minor, $patch) = ($1, $2, $3) if $version =~ /^(\d+)\.(\d+)\.(\d+)$/; stop("There is some problem with the major or the minor release numbers") unless $major and $minor; # Set to the previous release version if ($minor == 0) { $minor = 11; $major -= 1; } else { $minor -= 1; } # Get the current working directory my $parrot_dir = getcwd(); # Release process get_parrot_github(); get_parrot_docsx(); archive_parrot_docsx(); update_parrot_github(); delete_repos(); exit(0); ########################## # Subroutine definitions # ########################## # Get the temporary directory, contained in '%PConfig', in which to clone # the repos sub get_repo_directory { $repos = $PConfig{tempdir}; if (!defined $repos) { print "\'\$PConfig{\'tempdir\'}\' is undefined. This variable must ", "be defined and defined with a readable and a writeable directory ", "to execute, successfuly, this script.\n"; print "Did you, perhaps, fail to configure parrot?\n"; exit(1); } $repos .= '/'; # Test '$repo' directory to ensure we can read and write to it. my $tstfile = $repos . 'parrot_github_release.out'; my $outstring = "A simple test string: parrot_github_release.out"; open my $OUT, '+>', $tstfile or stop("Unable to open file for output in $repos directory."); print $OUT $outstring; close $OUT or stop("Unable to close file in $repos directory"); open my $IN, '<', $tstfile or stop("Unable to open file for input in $repos directory."); my $instring = <$IN>; close $IN or stop("Unable to close $tstfile"); stop("Unable to read and to write to $repos directory") unless $instring eq $outstring; unlink $tstfile or warn "Unable to delete $tstfile: $!"; } # Get 'docs/' directory sub get_docs_directory { while (1) { print "Please specify the path to the previous documentation release? "; $docs = <>; chomp $docs; last if -d $docs; } $docs .= '/' if $docs =~ /[a-zA-Z0-9]$/; } # Test whether or not we actually have a valid 'docs/' directory. sub tst_docs_directory { my $parrot_dir = getcwd(); my $filename = 'parrothist.pod'; # This one's likely to stick around. $docs .= '/' if $docs =~ /[a-zA-Z0-9]$/; chdir $docs; stop("Unable to access the $docs directory") unless (-f $filename && -s $filename); chdir $parrot_dir; } # Clone a local copy of 'parrot.github.com' sub get_parrot_github { chdir $repos; print "\n== CLONING 'PARROT.GITHUB.COM' ==\n"; system('git', 'clone', 'git@github.com:parrot/parrot.github.com.git') == 0 or stop("Unable to clone 'parrot.github.com'"); chdir $parrot_dir; } # Clone a local copy of 'parrot-docsx' sub get_parrot_docsx { my $parrot_docsx = 'git@github.com:parrot/parrot-docs' . $major . '.git'; chdir $repos; print "\n== CLONING 'PARROT-DOCSX' ==\n"; system('git', 'clone', $parrot_docsx) == 0 or stop("Unable to clone the appropriate 'parrot-docsx' repo."); chdir $parrot_dir; } # Archive the previous documentation release to the 'parrot-docsx' repository sub archive_parrot_docsx { my $parrot_docsx = $repos . 'parrot-docs' . $major . '/'; chdir $parrot_docsx; print "\n== CHECKING OUT GH-PAGES BRANCH ==\n"; system('git', 'checkout', 'gh-pages') == 0 or stop("Unable to switch to the 'gh-pages' branch"); my $previous = $major . '.' . $minor . '.' . $patch; my $copy_to = $parrot_docsx . $previous . '/'; my $copy_from = $docs . '*'; print "\n== MAKING NEW DIRECTORY IN 'PARROT-DOCSX' ==\n"; mkdir($copy_to) or stop("Unable to make new directory in 'parrot-docsx'"); print "\n== COPYING DOCS TO 'PARROT-DOCSX' ==\n"; # Use shell globbing, for convenience. (Should I rewrite this?) system("cp -r --target-directory=$copy_to $copy_from") == 0 or stop("Unable to copy 'docs/' to 'parrot-docsx'"); print "\n== GIT ADD ('PARROT-DOCSX') ==\n"; system('git', 'add', '.') == 0 or stop("Unable to add to 'parrot-docsx'"); print "\n== GIT COMMIT ('PARROT-DOCSX') ==\n"; system('git', 'commit', '-m', "'Archiving documentation release'") == 0 or stop("Unable to commit to 'parrot-docsx'"); print "\n== CHECKING OUT MASTER ==\n"; system('git', 'checkout', 'master') == 0 or stop("Unable to switch to 'master'"); print "\n== PUSHING 'PARROT-DOCSX' ==\n"; system('git', 'push', 'origin', 'gh-pages') == 0 or stop("Unable to push updates to 'parrot-docsx'"); chdir $parrot_dir; } # Update parrot.github.com with present release docs sub update_parrot_github { my $parrot_github = $repos . 'parrot.github.com' . '/'; chdir $parrot_github; my $tmp = $PConfig{tempdir}; print "\n== SAVING KEY 'PARROT.GITHUB.COM' FILES ==\n"; system('cp', "--target-directory=$tmp", 'README.md') == 0 or stop("Unable to save 'README.md'"); system('cp', "--target-directory=$tmp", 'index.html') == 0 or stop("Unable to save 'index.html'"); system('cp', "--target-directory=$tmp", 'releases.html') == 0 or stop("Unable to save 'releases.html'"); print "\n== GIT RM ('PARROT.GITHUB.COM') ==\n"; system('git', 'rm', '-rf', '*') == 0 or stop("Unable to remove files from 'parrot.github.com'"); print "\n== GIT ADD ('PARROT.GITHUB.COM') ==\n"; system('git', 'add', '-A') == 0 or stop("Unable to add to 'parrot.github.com'"); print "\n== GIT COMMIT ('PARROT.GITHUB.COM') ==\n"; system('git', 'commit', '-m', "'Removed files from 'parrot.github.com'") == 0 or stop("Unable to commit to 'parrot.github.com'"); print "\n== RESTORING KEY 'PARROT.GITHUB.COM' FILES ==\n"; system('cp', "$tmp/README.md", '.') == 0 or stop("Unable to restore 'README.md'"); system('cp', "$tmp/index.html", '.') == 0 or stop("Unable to restore 'index.html'"); system('cp', "$tmp/releases.html", '.') == 0 or stop("Unable to restore 'releases.html'"); update_index_html(); update_releases_html(); my $parrot_docs = $parrot_dir . '/' . 'docs/*'; print "\n== COPYING 'DOCS/' TO 'PARROT.GITHUB.COM' ==\n"; # Here I am, relying on the shell, again. system("cp -r $parrot_docs .") == 0 or stop("Unable to copy 'docs/' to 'parrot.github.com'"); print "\n== GIT ADD ('PARROT.GITHUB.COM') ==\n"; system('git', 'add', '.') == 0 or stop("Unable to add to 'parrot.github.com'"); print "\n== GIT COMMIT ('PARROT.GITHUB.COM') ==\n"; system('git', 'commit', '-m', "'Updated 'parrot.github.com'") == 0 or stop("Unable to commit to 'parrot.github.com'"); print "\n== PUSHING ('PARROT.GITHUB.COM') ==\n"; system('git', 'push', 'origin', 'master') == 0 or stop("Unable to push updates to 'parrot.github.com' master"); chdir $parrot_dir; } # Update the link to 'Previous Parrot Documentation Releases' in 'index.html' sub update_index_html { my $buffer = ''; open my $FH, '+<', 'index.html' or stop("Unable to open 'index.html'"); while (<$FH>) { s/$1/$version/ if /Parrot ($major\.$minor\.$patch)-devel - Home/; s/$1/$version/ if /Parrot version ($major\.$minor\.$patch)-devel/; s/$1/$major\.$minor\.$patch/ if /Previous Parrot Documentation Releases \((\d.\d.\d) - 0.1.1\)/; $buffer .= $_; } seek($FH, 0, 0) or stop("Unable to seek start of 'index.html'"); print $FH $buffer or stop("Unable to print out 'index.html'"); truncate($FH, tell($FH)) or stop("Unable to truncate 'index.html'"); close $FH or stop("Unable to close 'index.html'"); } # Update 'releases.html' to point to the newly archived documents in # 'parrot-docsx' sub update_releases_html { my $buffer = ''; my $ul = '
    '; my $li = '
  • Release ' . $major . '.' . $minor . '.' . $patch . '
  • ' . "\n"; open my $FH, '+<', 'releases.html' or stop("Unable to open 'releases.html'"); while (<$FH>) { s/$1/$version/ if /Parrot ($major\.$minor\.$patch)-devel - Home/; s/$1/$version/ if /Parrot version ($major\.$minor\.$patch)-devel/; if (/
      /) { $buffer .= $_; $buffer .= $li; next; # So we don't duplicate the '
        ' tag. } $buffer .= $_; } seek($FH, 0, 0) or stop("Unable to seek to start of 'releases.html'"); print $FH $buffer or stop("Unable to print out 'releases.html'"); truncate($FH, tell($FH)) or stop("Unable to truncate 'releases.html'"); close $FH or stop("Unable to close 'releases.html'"); } # Delete the downloaded repositories sub delete_repos { my $parrot_docsx = $repos . 'parrot-docs' . $major . '/'; my $parrot_github = $repos . 'parrot.github.com' . '/'; print "\n== DELETING PARROT-DOCSX ==\n"; system('rm', '-rf', $parrot_docsx) == 0 or stop("Unable to delete the 'parrot-docsx' repo"); print "\n== DELETING PARROT.GITHUB.COM ==\n"; system('rm', '-rf', $parrot_github) == 0 or stop("Unable to delete the 'parrot.github.com' repo"); } # Customized version of die() for more consistent diagnostics sub stop { if (@_ == 0) { print '[FATAL ERROR] '; if ($!) { print $!; } else { print 'An unrecoverable error has occurred'; } } else { my $msg = shift; print "[ERROR] $msg"; if ($!) { print ": $!"; } } print "!\n"; exit 1; } sub version { (my $version = <