Skip to content

yevhen/Nake

Repository files navigation

Nake

Nake is a magic task runner tool for .NET Core. It's a hybrid of Shovel and Rake. The DSL for defining tasks is uniquely minimal and it's just plain (naked) C# code! Nake is built on top of the latest Roslyn release so you can use all of the latest C# features in you scripts and even more.

Gitter Build status NuGet

How to install

Nake is built as dotnet tool. You can install it either globally of locally (recommended):

PM> dotnet tool install Nake

Syntax cheatsheet

#r "System                          //
#r "System.Core"                    //
#r "System.Data"                    //   #reference assemblies from the netstandard BCL   
#r "System.Xml"                     //       (these are referenced by default)
#r "System.Xml.Linq"                //    
#r "Microsoft.CSharp"               //

#r "System.IO, Version=4.0..."      //  you can reference assembly by its full name
#r "./Packages/nunit.dll"           //        or by using relative path
#r "/usr/bin/SDK/Orleans.dll"       //            or by absolute path
                                        
#r "nuget: Streamstone, 2.0.0"      //  Nake is using dotnetscript dependency resolution
#r "nuget: Newtonsoft.Json"         //  so referencing nuget packages is fully supported
#r "nuget: Orleankka, 2.*"          //  https://github.com/filipw/dotnet-script#nuget-packages-1

#load "Other.csx"                   //      #load code from other script files
#load "Build/Another.csx"           //  (both absolute and relative paths are fine)

using System;                       //
using System.IO;                    //      standard C# namespace imports
using System.Linq;                  //     (these are imported by default)
using System.Text;                  //  
using System.Threading.Tasks;       //    
using System.Collections.Generic;   //  

using static System.IO.Path;        //    C# "using static members" feature 
using static System.Console;        //      will make you scripts more terse

WriteLine("Are you ready? Y/N:");   //      any code you put on the script level 
if (ReadLine() == "N")              //  will run before any of the tasks are executed
    Exit("See you soon ...");       //      (useful for one-off initialization)

var greeting = "Hello";             //   you can override any script-level variables 
var who = "world";                  //  with the values passed from the command line

/// Prints greeting                 //  this F#-style summary is shown in task listing
[Nake] void Welcome()               //  [Nake] makes method runnable from command line
{                                       
    Write($"{greeting},{who}!");    //  forget ugly string.Format & string concatenation 
}                                   //   with built-in support for string interpolation

[Nake] void Tell(
    string what = "Hello",          //     for parameterized tasks you can supply
    string whom = "world",          //     arguments directly from the command line
    int times = 1,                  //          (string, int, boolean and 
    DayOfWeek when,                 //         enum arguments are supported)
    bool quiet = false              //  + switch-like syntax for booleans (eg, --quiet)
)
{
    var emphasis = quiet ? "" : "!";
    for (; times > 0; times--)
	    WriteLine($"{what}, {whom} on {when}{emphasis}");
}                                   

[Step] void Clean()                  //     Steps are tasks with 'run once' semantics      
{                                    //     (foundation of any build automation tool)
    Delete($"{OutputPath}/*.*");	
}                                   

[Step] void Build(string cfg = "Debug")
{					                    
    Clean();                        //  unlike popular tools, there is no special syntax
    -------                         //     for specifying task (step) dependencies
    MSBuild("Nake.sln", cfg);       //    (it's just plain old C# method invocation)
}                                       
                                       
[Step] void Test()
{					                    
    Clean();                        //     you have complete control over decision,
    Build();                        //  when and in what order dependent steps should run
    -------                         //      (and Nake makes sure of run-once behavior)
    NUnit($"{OutputPath}/*.Tests.dll");   
}

[Step] void Publish(bool beta = false)
{					                    
    Test();                         //   sometimes, you need to execute the same step but with
    Build("Release");               //  different args. Unlike other build automation tools
    ------                          //  there is no special syntax to force step to run again, 
    Nuget("Nake.nuspec", beta);     //       you just invoke it with different arguments!
}                                       

var apiKey = "$NugetKey$";          //      $var$ is the shortcut syntax for getting 
Push(apiKey, "{PackagePath}");      //          value of environment variable

const string ApiKey = "$APIKEY$";   //     environment variables defined in constant scopes
void Push(string key = "$APIKEY$")  //       evaluated once at compile-time (ie, inlined)

var ApiKey = "$APIKEY$";            //     environment variables defined in variable scopes
Write("$APIKEY$");                  //       evaluated at invocation-time (dynamic)

Write("$NakeStartupDirectory$");    //       these special environment variables
Write("$NakeWorkingDirectory$");    //        are automatically created by Nake

var root = "$NakeScriptDirectory$"; //   this is how you can get script directory inlined
Write(Location.NakeScriptDirectory) //   alternatively call this method from Nake.Utility

Write("{{esc}}");                   //  will simply print {esc} (no string interpolation)
Write("$$esc$$");                   //  will simply print $esc$ (no env variable inlining)

Cmd($"docker build -f {spec} .");   //        you can use Shell.Cmd to execute 
Cmd($"echo {title}");               //         commands via shell interpreter ...

r = await Run($"docker images");    //      and you can get the result of the execution
Write(result.StandardOutput);       //    by using functionality provided by MedallionShell

Run("app") < new FileInfo("input")  //   this includes fancy composable piping and redirects

await $"docker rm {cid} .";         //      or simply await the cli string to get it executed
                                    //    this is the the most convenient way to execute commands

await $@"docker logs --tail 10 \    //      backslash (\) as line continuation symbol
         {container}";              //        could be used with verbatim strings

await "app 'quoted arg'"            //   use ' quote for arguments that contain spaces
await "app 'quoted '' quote'"       //       '' quote to insert a single quote

await $"app '{path}'"               //   you may quote interpolations that may contain space
await $"app {path}"                 //     but Nake will do it automatically for you ;)
await $"app {arg}\\;;;"             //   and MedallionShell will properly escape the arguments

class Azure                         //  namespace declarations cannot be used with scripts,
{                                   //  but could be easily emulated with class declarations
    class Queue                     //     and you can nest them infinitely as you like
    {    
        [Task] void Clean()         //     then from the command line you would invoke
        {}                          //   this task by its class path (ie, azure queue clean)
    }
}

[Nake] void Default() => Build();   //          running Nake without any options 
                                    //       will cause it to run the "default" task

Command line reference

General syntax is: Nake [options ...] [VAR=VALUE ...] [task ...]

> Nake -f "Nake.csx" Log=1 build    //       set Log environment variable to 1 and
                                    //      then run Build() task from Nake.csx file 
                                        
> Nake Log=1 build                  //  equivalent to the above as Nake will automatically try 
                                    //   to use Nake.csx file if present in current directory

Options:

   -?  --help             Display help message and exit
   -v  --version          Display the program version and exit
   -q  --quiet            Do not echo informational messages to standard output
   -s  --silent           Same as --quiet but also suppresses user generated log messages
   -f  --nakefile FILE    Use FILE as Nake project file
   -d  --directory DIR    Use DIR as current directory
   -t  --trace            Enables full stack traces in error reporting + task execution trace
       --debug            Enables full script debugging in Visual Studio
   -T  --tasks [PATTERN]  Display tasks with descriptions matching optional PATTERN and exit
   -r  --reset-cache      Resets compilation output cache

Invoking tasks

General syntax for invoking tasks and passing arguments is similar to the normal C# method invocation syntax, except is used instead of , to separate task arguments, and = is used instead of : for specifying named argument values. Also, boolean arguments support special -- switch syntax.

> Nake build                          //  run Build task with default arg values
> Nake build Release                  //  or with first positional argument set to 'Release'
> Nake build cfg=Release              //  or with named argument 'cfg' set to 'Release'
> Nake build Release outDir="/c/Tmp"  //  you can mix positional and named arguments
> Nake build ; test                   //  or invoke multiple tasks within a same session
> Nake build `; test                  //  also escape ';' when running in PowerShell console 
> Nake publish --beta                 //  invoke Publish task with 'beta' arg set to 'true'

Included utility reference

Out-of-the box Nake includes a lot of useful convinient utility functions to help you with:

  • running external tools, such as command-line commands or MSBuild
  • selecting and transforming file system paths (globber)
  • casual file system tasks, such as copying, moving, deleting files/folders
  • logging messages to console
  • working with environment variables
  • controlling Nake's runner
  • etc

Check out table below for reference on using utility library:

Class Functions
Shell Executing programs and shell commands
Session Controlling current Nake's runner session
Log Logging messages to console
Env Working with environment variables
FS File-system tasks, such as copy/move/del/mkdir/etc
FileSet File path selection and transformation (globber)
Color Printing to console in color
Location Current directory and special paths (working, startup)

Also, see 'by use-case' reference on wiki.

Tips & tricks

class Azure
{                                       
    StorageAccount account;
    
    static Azure()                  //  this will run once before any of the 
    {                               //  tasks in this namespace are executed
        account = Init();           //  (useful for one-off initialization)
    }
}  

Backlog

  • Check the open issues

Contributing

Make sure you follow the coding guidelines which exists on a project (either as R# formatting settings or .editorconfig). Nothing else)

Samples and Documentation

Have a look at Nake.csx. It's a Nake file used to build and publish Nake itself (ye, we're eating our own dog food).

Community

General discussion group could be found here. Also, for news you can follow Nake's official twitter account (or my account for that matter). The twitter's hashtag is #naketool.

Credits

  • Thanks to everyone in the Roslyn compiler team for making this happen
  • Thanks to everyone in the dotnet-script team
  • Thanks to MedallionShell community for the great project
  • Special thanks to Anton Martynenko for giving me an idea and steering Nake's DSL in the right direction
  • Hugs and kisses to my lovely Valery for being so patient and supportive, and for feeding me food and beer, while I was sitting in my cage working on Nake, instead of spending that time with her

License

Apache 2 License