summary refs log blame commit diff stats
path: root/doc/howto-publish-a-release.txt
blob: 38cf07226c21b53ab1c27ee7aaf1fcd3a7fe33c9 (plain) (tree)


















                                                                     
                                                           






                                    
( ) test everything one last time:
( ) * make test
( ) * ./ranger.py [--clean]
( ) * ranger/ext/rifle.py
( ) * make install
( ) make a release commit:
( ) * update the number in the README
( ) * update the number in ranger/__init__.py
( ) * update the version number in ranger/ext/rifle.py
( ) * rebuild the man page with the updated number
( ) * write changelog entry
( ) * think of a witty commit message
( ) * change VERSION in ranger/__init__.py to something with "stable"
( ) * push the commit
( ) build .tar.gz with "make snapshot"
( ) make, make install and test the snapshot one last time
( ) update the website:
( ) * add the new version as ranger-stable.tar.gz
( ) * add the new version as ranger-X.Y.Z.tar.gz
( ) * update both signatures (gpg -u 0x00FB5CDF -sb <file>)
( ) * update the changelog
( ) * update the man page
( ) * rerun boobies.py
( ) announce the update
( ) * to the mailing list
( ) * in the arch linux forum
( ) * write a news entry on savannah
/* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
#!/usr/bin/perl

use strict;
use warnings;

use IPC::Run3;
use Path::Tiny;
use Config::Tiny;
use POSIX qw(strftime);
use Getopt::Long qw/ GetOptions /;

my $version = "leo v0.4.3";

# Options.
my %options = (
    L_SIGN => $ENV{L_SIGN},
    L_GZIP => $ENV{L_GZIP},
    L_ENCRYPT => $ENV{L_ENCRYPT},
    L_SIGNIFY => $ENV{L_SIGNIFY},
);

GetOptions(
    \%options,
    qw{ verbose help version }
) or die "leo: error in command line arguments\n";

# Print version.
print $version, "\n" and exit 0 if $options{version};

# Configuration.

my $config_file = $ENV{XDG_CONFIG_HOME} || "$ENV{HOME}/.config";
$config_file .= "/leo.conf";

my $config = Config::Tiny->new;
$config = Config::Tiny->read( $config_file )
    or die "Cannot read config file: `$config_file'\n";

# Reading config file.

foreach my $key (sort keys $config->{_}->%*) {
    $options{$key} = $config->{_}->{$key};
}

# Die if user is using older config format.
die "leo: old config format detected\n"
    if exists $options{encrypt} or exists $options{sign};

my %profile;

foreach my $prof (sort keys $config->%*) {
    next if $prof eq "_";

    # Set global values to local profiles.
    foreach (qw(L_ENCRYPT L_SIGN L_SIGNIFY L_GZIP)) {
        $profile{$prof}{$_} = $options{$_};
    }

    foreach my $key (sort keys $config->{$prof}->%*) {
        # $profile{$prof} contains config values ($), {exclude}
        # (@), {backup} (@).

        # Set config values.
        if ( length($key) >= 2
             and substr($key, 0, 2) eq "L_") {
            $profile{$prof}{$key} = $config->{$prof}->{$key};
            next;
        }

        push @{ $profile{$prof}{exclude} }, $key and next
            if $config->{$prof}->{$key} eq "exclude";

        push @{ $profile{$prof}{backup} }, $key;
    }
}

my $date = date();
my $backup_dir = $options{backup_dir} || "/tmp/backups";

my @gpg_recipients;
@gpg_recipients = split / /, $options{gpg_recipients}
    if $options{gpg_recipients};

my $gpg_bin = $options{gpg_bin} || "gpg";
warn "[WARN] \$gpg_bin is set to `$gpg_bin'"
    unless $gpg_bin =~ /^(gpg2?)$/;

# Print help.
HelpMessage() and exit 0 if scalar @ARGV == 0 or $options{help};

# Parsing the arguments.
foreach my $prof ( @ARGV ) {
    if ( $profile{ $prof } ) {
        print "--------  $prof";
        print " [Encrypt]" if $profile{$prof}{L_ENCRYPT};
        print " [Sign]"    if $profile{$prof}{L_SIGN};
        print " [Signify]" if $profile{$prof}{L_SIGNIFY};
        print " [gzip]"    if $profile{$prof}{L_GZIP};
        print "\n";

        my $file = "$backup_dir/${prof}/${date}.tar";
        $file .= ".gz" if $profile{$prof}{L_GZIP};

        path("$backup_dir/${prof}")->mkpath; # Create backup directory.
        backup($prof, $file);

        my $is_gpg_req = 1 if $profile{$prof}{L_SIGN} or $profile{$prof}{L_ENCRYPT};
        encrypt_sign($prof, $file) if $is_gpg_req;

        # gpg would've removed the `.tar' file.
        $file = "${file}.gpg" if $is_gpg_req;
        signify($prof, $file) if $profile{$prof}{L_SIGNIFY};
    } else {
        warn "[WARN] leo: no such profile :: `$prof' \n";
    }
}

sub backup {
    my $prof = shift @_;
    my $tar_file = shift @_;

    my @options = ("-C", "/");
    push @options, "-z" if $profile{$prof}{L_GZIP};

    my @backup_paths;
    foreach my $path ($profile{$prof}{backup}->@*) {
        # If it's a directory then walk it upto 1 level.
        if (-d $path) {
            my $iter = path($path)->iterator();
            while ( my $iter_path = $iter->() ) {
                push @backup_paths, path( $iter_path );
            }
        } else {
            push @backup_paths, path( $path );
        }
    }

    # Remove files that are to be excluded.
    foreach my $exclude ($profile{$prof}{exclude}->@*) {
        @backup_paths = grep !/$exclude/, @backup_paths;
    }

    # All paths should be relative to '/'.
    @backup_paths = map { $_->relative('/') } @backup_paths;

    tar_create($tar_file, @options, @backup_paths);
    $? # tar returns 1 on errors.
        ? die "Backup creation failed :: $?\n"
        : print "Backup: $tar_file\n";

    path($tar_file)->chmod(0600);
    print "File was compressed with gzip(1).\n"
        if $profile{$prof}{L_GZIP} and $options{verbose};

    tar_list($tar_file) if $options{verbose};
}

# Encrypt, Sign backups.
sub encrypt_sign {
    my $prof = shift @_;
    my $file = shift @_;

    my @options = ();
    push @options, "--default-key", $options{gpg_fingerprint}
        if $options{gpg_fingerprint};

    if ( $profile{$prof}{L_ENCRYPT} ) {
        push @options, "--encrypt";
        push @options, "--recipient", $options{gpg_fingerprint}
            if $options{gpg_fingerprint};
        push @options, "--recipient", $_
            foreach @gpg_recipients;
    }

    push @options, "--sign" if $profile{$prof}{L_SIGN};
    push @options, "--verbose" if $options{verbose};

    run3 [$gpg_bin, "--yes", "-o", "${file}.gpg", @options, $file];

    $? # We assume non-zero is an error.
        ? die "GPG failed :: $?\n"
        : print "GPG: $file.gpg\n";

    unlink $file or warn "[WARN] Could not delete `$file': $!\n";

    path("$file.gpg")->chmod(0600);
}

sub signify {
    my $prof = shift @_;
    my $file = shift @_;

    my @options = ( "-S",
                    "-s", $options{signify_seckey},
                    "-m", $file,
                    "-x", "${file}.sig",
                );

    run3 ["signify", @options];
    $? # Non-zero exit code is an error.
        ? die "Signify failed :: $?\n"
        : print "Signify: ${file}.sig\n";
}

sub HelpMessage {
    print qq{Backup files to $backup_dir.

Profile:\n};
    foreach my $prof (sort keys %profile) {
        print "    $prof";
        if ($options{verbose}) {
            print " [Encrypt]" if $profile{$prof}{L_ENCRYPT};
            print " [Sign]" if $profile{$prof}{L_SIGN};
            print " [Signify]" if $profile{$prof}{L_SIGNIFY};
            print " [gzip]" if $profile{$prof}{L_GZIP};
            print "\n";

            print "        + $_\n" foreach $profile{$prof}{backup}->@*;
            print "        - $_\n" foreach $profile{$prof}{exclude}->@*;
        }
        print "\n";
    }
    print qq{Options:
    --version [$version]
    --verbose
    --help
};
}

sub tar_create { run3 ["/bin/tar", "cf", @_]; }
sub tar_list { print "\n"; run3 ["/bin/tar", "tvf", @_]; print "\n";}

sub date { return strftime '%FT%T%z', gmtime() }