Tomtit.git | lib/ | Tomtit.rakumod


#!raku

unit module Tomtit:ver<0.1.37>;

use File::Directory::Tree;

use YAMLish;

use Sparrow6::Task::Repository;

use Sparrow6::DSL;

my %profiles is Map = (
  'perl' => ( 'set-pause', 'make-dist', 'install', 'test', 'changes', 'release' ),
  'raku' =>  ( 'test', 'install', 'changes', 'release' ),
  'cro' => ( 'cro-yaml' ),
  'git' => ( 'set-git', 'commit', 'push', 'git-branch-delete', 'pull', 'status', 'git-summary', 'git-publish', 'update-branch-list', 'github-url-https-to-ssh' ),
  'gitlab' => ( 'gitlab-run-pipeline' ), 
  'ruby' => ( 'rvm' ),
  'azure' => ( 'az-resources', 'az-account-set' , 'az-kv-show', 'az-sql-server-check-fw' ),
  'ado' => ( 'ado-pipeline-build-list', 'ado-pipeline-build-run' ),
  'hello' => ( 'world' ),
  'yaml' => ( 'yaml-lint' ),
  'tomtit' => ( 'tomtit-pl6-to-raku' ),
  'code' => ( 'search' ),
  'go' => ( 'go-build', 'go-format', 'proto' ),
);

# tom cli initializer

our sub check-if-init ( $dir ) is export {

  if ! ($dir.IO ~~ :d) {
	say "tomtit is not initialized, run tom --init";
	exit(1);
  }

}

our sub init ($dir) is export {

  mkdir $dir;

}
  
our sub load-conf () is export {

  my %conf = Hash.new;

  if $*DISTRO.is-win {
    if "{%*ENV<HOMEDRIVE>}{%*ENV<HOMEPATH>}/tom.yaml".IO ~~ :e {
      %conf = load-yaml(slurp "{%*ENV<HOMEDRIVE>}{%*ENV<HOMEPATH>}/tom.yaml");
    }
  } else {  
    if "{%*ENV<HOME>}/tom.yaml".IO ~~ :e {
      %conf = load-yaml(slurp "{%*ENV<HOME>}/tom.yaml");
    }
  }

  %conf;

}

sub tomtit-usage () is export  {
  say 'usage: tom $action|$options $thing'
}

sub tomtit-help () is export  {
  say q:to/DOC/;
  usage:
    tom $action|$options $thing

  run scenario:
    tom $scenario

  remove scenario:
    tom --remove $scenario

  print out scenario:
    tom --cat $scenario

  install profile:
    tom --profile $profile

  set default ennvironment
    tom --env-set $env

  actions:
	tom --init				# initialize tomtit
    tom --list              # list available scenarios
    tom --profile           # list available profiles
    tom --profile $profile  # list profile scenarios
    tom --last              # what is the last run?
    tom --completion        # install Bash completion
    tom --env-set $env      # set current environment
    tom --env-set           # show current environment
    tom --env-list          # list available environments

  options:
    --env=$env  # run scenario for the given environment
    --verbose   # run scenarios in verbose mode
    --quiet,-q  # run scenrios in less verbose mode
  DOC
}

# clean tomtit internal data
# is useful as with time it might grow

sub tomtit-clean ($dir) is export { 

  say "cleaning $dir/.cache ...";

  if "$dir/.cache/".IO ~~ :e {
    empty-directory "$dir/.cache"
  }

}

sub scenario-last ($dir) is export {
  if "$dir/.cache/history".IO ~~ :e {
    my @history =  "$dir/.cache/history".IO.lines;
    say @history[*-1];
  }
}

sub scenario-run ($dir,$scenario,%args?) is export {

  die "scenario $scenario not found" unless "$dir/$scenario.raku".IO ~~ :e;

  mkdir "$dir/.cache";

  my $fh = open "$dir/.cache/history", :a;
  $fh.print($scenario,"\n");
  $fh.close;

  my $conf-file;

  my $current-env = current-env("{$dir}/env");

  if %args<env> {

    $conf-file = %args<env> eq 'default' ?? "$dir/env/config.raku" !! "$dir/env/config.{%args<env>}.raku";

  } elsif $current-env eq 'default' &&  "$dir/env/config.raku".IO ~~ :e {

    $conf-file =  "$dir/env/config.raku";

  } elsif "$dir/env/config.{$current-env}.raku".IO ~~ :e  {

    $conf-file = "$dir/env/config.{$current-env}.raku";

  }

  if $conf-file && $conf-file.IO ~~ :e {
    say "load configuration from $conf-file";
    if "$dir/env/config.any.raku".IO ~~ :f {
      say "mix in common configuration from $dir/env/config.any.raku";
      set-config((EVALFILE $conf-file),(EVALFILE "$dir/env/config.any.raku"));
    } else {
      set-config(EVALFILE $conf-file);
    }
  } else {
    if "$dir/env/config.any.raku".IO ~~ :f {
      say "mix in common configuration from $dir/env/config.any.raku";
      set-config(%(), EVALFILE "$dir/env/config.any.raku");
    }
  }

  Sparrow6::Task::Repository::Api.new().index-update unless %args<no-index-update>;

  EVALFILE "$dir/$scenario.raku";

}

sub scenario-remove ($dir,$scenario) is export {

  if "$dir/$scenario.raku".IO ~~ :e {
    unlink "$dir/$scenario.raku";
    say "scenario $scenario removed"
  } else {
    say "scenario $scenario not found"
  }

}

sub scenario-cat ($dir,$scenario,%args?) is export {

  if "$dir/$scenario.raku".IO ~~ :e {
    say "[scenario $scenario]";
    my $i=0;
    
    for "$dir/$scenario.raku".IO.lines -> $l {
      $i++;
      say %args<lines> ?? "[$i] $l" !! $l;
    }
  } else {
    say "scenario $scenario not found"
  }

}

sub scenario-edit ($dir,$scenario) is export {

    die "you should set EDITOR ENV to run editor" unless  %*ENV<EDITOR>;

    unless "$dir/$scenario.raku".IO ~~ :e {
      my $confirm = prompt("$dir/$scenario.raku does not exit, do you want to create it? (type Y to confirm): ");
      return unless $confirm eq 'Y';
    }

    shell "{%*ENV<EDITOR>} $dir/$scenario.raku";

}

sub current-env ($dir) {

  my $current;

  if "$dir/current".IO ~~ :e {
    $current = slurp "$dir/current";
  }

  return $current || "default"

}


sub environment-edit ($dir,$env) is export {

    die "you should set EDITOR ENV to run editor" unless  %*ENV<EDITOR>;

    mkdir $dir;

    my $conf-file = ( $env eq 'default' ) ?? "$dir/config.raku" !! "$dir/config.{$env}.raku";

    unless $conf-file.IO ~~ :e {
      my $confirm = prompt("$conf-file does not exit, do you want to create it? (type Y to confirm): ");
      return unless $confirm eq 'Y';
    }

    shell "{%*ENV<EDITOR>} $conf-file";

}

sub environment-list ($dir) is export {

    say "[environments list]";

    mkdir $dir;

    my @list = Array.new;

    my $current = current-env($dir);

    for dir($dir) -> $f {

      next unless "$f".IO ~~ :f;
      next unless $f ~~ /\.raku$/;

      if $f.basename ~~ /config\.(.*)\.raku/ {

        @list.push("$0");

      } else {

        @list.push("default")

      }

    }

    for @list.sort -> $l {
      say $current eq $l ?? "$l *" !! $l
    };

}

sub environment-set ($dir,$env) is export {

  # next lines removes "current" symlink if it exist
  # we don't need this for tomty projects
  # generated by the latest version of Tomty
  # where environment manager no longer
  # uses symlinks

  unlink "$dir/current" if "$dir/current".IO ~~ :e;

  mkdir $dir;

  spurt "$dir/current", $env;

}

sub environment-show ($dir) is export {

  if "$dir/current".IO ~~ :e {

    my $current = "$dir/current".IO.resolve.IO.basename;

      if $current ~~ /config\.(.*)\.raku/ {

        say "current environment: $0"

      } else {

        say "current environment: default"

      }

  } elsif "$dir/config.raku".IO ~~ :e {

    say "default";

  } else {

    say "default environment is not set, create default configuration file (.tom/env/config.raku)
or use tom --set-env \$env to set default environments"
  }
  
}

sub environment-cat ($dir,$env,%args?) is export {

  my $conf-file;

  if $env eq "default" {
    $conf-file = "$dir/config.raku"
  } else {
    $conf-file = "$dir/config.{$env}.raku"
  }

  if "$conf-file".IO ~~ :e {
    say "[environment $env]";
    my $i=0;
    
    for "$conf-file".IO.lines -> $l {
      $i++;
      say %args<lines> ?? "[$i] $l" !! $l;
    }
  } else {
    say "environment $env not found"
  }

}

# scenario-doc function is implemented, but not presented in public API, 
# as there are some issues with compiling Sparrowdo scenarios

sub scenario-doc ($dir,$scenario) is export {

  die "scenario $scenario not found" unless "$dir/$scenario.raku".IO ~~ :e;

  run $*EXECUTABLE, '--doc', "$dir/$scenario.raku";

}

sub scenario-list ($dir) is export {

    my @list = Array.new;

    for dir($dir) -> $f {

      next unless "$f".IO ~~ :f;
      next unless $f ~~ /\.raku$/;
      my $scenario-name = substr($f.basename,0,($f.basename.chars)-5);
      @list.push($scenario-name);

    }

    return @list.sort;

}

sub scenario-list-print ($dir) is export {

    my $current-env = current-env("$dir/env");

    say "[$current-env@scenarios list]";

    my @list = scenario-list($dir);

    say join "\n", @list.sort;

}

multi sub profile-list  is export {

  say "[profiles]";

  for %profiles.keys.sort -> $i {
    say "$i"
  }
}

multi sub profile-list($dir,$profile is copy)  is export {

  my @list;

  if $profile ~~ /^ 'Tomtit-Profile-' \w/ { # Portable profile, installed as Perl6 module

    $profile.=subst('-', '::', :g);

    say "load portable profile $profile as Perl6 module ...";

    require ::($profile);

    my $f = "profile-data"; 

    # profile-data() function is provided by $profile module

    @list = ::($profile ~ '::&profile-data')().keys.sort;
    
  } else {

    unless %profiles{$profile}:exists {
      say "profile $profile does not exist";
      return;
    }

    @list = %profiles{$profile}.sort;

  }

  say "[profile scenarios]";

  for @list -> $s {

    my $installed = "$dir/$s.raku".IO ~~ :f;

    say "$profile\@$s\tinstalled: $installed";

  }

}

sub profile-install ($dir, $profile is copy, %args?) is export {

  my @list;

  my $is-portable = False;

  if $profile ~~ /^ 'Tomtit-Profile-' \w/ { # Portable profile, installed as Raku module

    $is-portable = True;

    $profile.=subst('-', '::', :g);

  } elsif $profile ~~ s/ '@' (.*) // { # Core profile, $profile@scenario form

    my $s1 = $0;

    @list = sort &[cmp], grep { $_ eq $s1 }, %profiles{$profile}

  } else { # Core profile, $profile form

    @list = %profiles{$profile}.sort

  }

  if $is-portable {

    say "load prortable profile $profile as Raku module ...";

    require ::($profile);

    my $f = "profile-data"; 

    # profile-data() function is provided by $profile module

    my %list =  ::($profile ~ '::&profile-data')();

    for %list.keys.sort -> $s {
      say "install $profile\@$s ...";
      my $fh = open "$dir/$s.raku", :w;
      $fh.print(%list{$s});
      $fh.close;
    }

  } else {

    unless %profiles{$profile}:exists {
      say "profile $profile does not exist";
      return;
    }

    if @list.elems == 0 {
      say "no scenarios found ...";
      return;
    }

    for @list -> $s {
      if %?RESOURCES{"profiles/$profile/$s.raku"}.IO ~~ :f {
        say "install $profile\@$s ...";
        my $fh = open "$dir/$s.raku", :w;
        $fh.print(slurp %?RESOURCES{"profiles/$profile/$s.raku"});
        $fh.close;
      } else {
        say "no perl6 resource found for $profile\@$s scenario ... skipping it";
      }
    }

  }
    
}


sub completion-install () is export {

  say "install completion.sh ...";

  my $fh = open %*ENV<HOME> ~ '/.tom_completion.sh' , :w;

  $fh.print(slurp %?RESOURCES{"completion.sh"});

  $fh.close;

  say "to activate completion say: source " ~ %*ENV<HOME> ~ '/.tom_completion.sh';  
    
}