Monthly Archives: September 2014

grepping in Powershell

Many unix/linux users are intimately familiar with regular expressions and using them with grep, sed or awk in a pipeline. A typical usage scenario is the following command:

ls | grep -i '.*[0-9].*\.dll' | sort 

So how do we do grep in PowerShell? Well, we can use PowerShell’s operators:

Operator Description
-match Matches its input property agains a regular expression. By default Powershell uses case-insensitive matching. Thats different than .Net’s default behavior with the RegEx class
-imatch Case-insensitive version of Match
-cmatch Case-sensitive version of Match
-like This compares its input against a string that can contain a wildcard. So no regular expressions usable here
-contains Only tells you if the input does or doesn’t contain the exact value you supplied

For our specific example, the PowerShell equivalent is:

Get-ChildItem | where { $_.Name -match ".*[0-9].*\.dll" } | Sort-Object -Property Name

And gives us the following output:

    Directory: C:\Windows\System32

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        20-11-2010     22:29     640512 advapi32.dll
-a---         11-6-2011      3:58     138056 atl100.dll
-a---         14-7-2009      3:14      65024 avicap32.dll
-a---        20-11-2010     22:29      91648 avifil32.dll
-a---         14-7-2009      3:14      10752 bitsprx2.dll
...
...

Another approach is to directly use the RegEx class from .Net. An example:

$matchOptions = [System.Text.RegularExpressions.RegexOptions]::IgnoreCase
$matcher = new-object System.Text.RegularExpressions.Regex (".*[0-9].*\.dll",$matchOptions)
$files  = Get-ChildItem
$files | where { $matcher.IsMatch($_.Name)  }

How to programatically download attachments from YouTrack

In a previous post I showed how to retrieve your bugs, comments and attachments from YouTrack. However, actually downloading each attachment is a little more work. Assume that the variable Url contains a value like "http://xxxxx.myjetbrains.com/youtrack/_persistent/yyyyy.doc?file=xx-xxx&v=0&c=false"

The following raises a HTTP NotFound exception:

var foo = Connection.Get(Url); //YouTrackSharp.Infrastructure.Connection

As does this:

String basePath = new System.Uri(Url).PathAndQuery;
var foo = Connection.Get(basePath );

The following will raise a HTTP NotAuthorized exception because we are not supplying the authentication cookie to the web-server.

byte[] file = new System.Net.WebClient().DownloadData(Url);

I looked at the YouTrackSharp.Infrastructure.Connection class and I didn’t see anything that would let me retrieve the authentication cookie.

Instead of writing code to manually implement the YouTrack REST API, I decided to take a different approach. I decided to modify YouTrackSharp itself to give me access to the authentication cookie.
I removed the NuGet package YouTrackSharp from my solution.
I downloaded the source of YouTrackSharp from GitHub and unzipped it next to my own project.
I included src\YouTrackSharp\YouTrackSharp.csproj into my solution
I modified src\YouTrackSharp\Infrastructure to add the public modifier as follows:

public CookieCollection _authenticationCookie;

I changed my own program to download the file as follows:

HttpWebRequest req = (HttpWebRequest)WebRequest.Create(Url);
req.CookieContainer = new CookieContainer();
req.CookieContainer.Add(Connection._authenticationCookie);
req.Method = "GET";
WebResponse response = req.GetResponse();
System.IO.Stream Stream = response.GetResponseStream();
var fileStream = System.IO.File.Create(String.Format("C:\\tmp\\{0}", Name));
Stream.CopyTo(fileStream);
fileStream.Close();

Exporting your bugs out of YouTrack

If you need to export your bug reports from your YouTrack instance, this is how to get it done using C#. Its a small example that will print all the issues,comments and attachments for each project in your instance.

Don’t forget to enable the REST API in your YourTrack settings page.

Create a new project called YouTrackExport and use NuGet to install the YouTrackSharp package

PM> Install-Package YouTrackSharp
Attempting to resolve dependency 'EasyHttp (≥ 1.6.29.0)'.
Attempting to resolve dependency 'JsonFX (≥ 2.0.1209.2802)'.
Installing 'JsonFx 2.0.1209.2802'.
...
...

The following code will print out the information:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Dynamic;
using YouTrackSharp.Infrastructure;
using YouTrackSharp.Projects;
using YouTrackSharp.Issues;
using YouTrackSharp.Admin;
using System.Net;

namespace YouTrackExport
{
    class Program
    {
        static void Main(string[] args)
        {
            String Username = "xxx";
            String Password = "yyy";
            String Site = "zzz.myjetbrains.com";

            YouTrackSharp.Infrastructure.Connection Connection;
            YouTrackSharp.Projects.ProjectManagement ProjectManager;
            IEnumerable<YouTrackSharp.Projects.Project> Projects;
            YouTrackSharp.Issues.IssueManagement IssueManager;
            IEnumerable<YouTrackSharp.Issues.Issue> Issues;
            IEnumerable<YouTrackSharp.Issues.Comment> Comments;


            Connection = new Connection(Site, 80, false, "youtrack");
            Connection.Authenticate(Username, Password);
                    
            ProjectManager = new ProjectManagement(Connection);
            Projects    = ProjectManager.GetProjects();
            foreach (Project project in Projects)
            {
               
                Console.WriteLine(String.Format("Found project {0} - {1} ", project.ShortName, project.Name));
                IssueManager = new IssueManagement(Connection);
                Issues = IssueManager.GetAllIssuesForProject(project.ShortName); 
                
                //An issue in youtrack can have many fields that we dont know at compile time.
                //Therefore its a .Net DynamicObject
                foreach (dynamic Issue in Issues) 
                {
                    Console.WriteLine(String.Format("\tFound issue {0}", Issue.Id));

                    Comments = IssueManager.GetCommentsForIssue(Issue.Id);
                    foreach (Comment Comment in Comments)
                    {
                        Console.WriteLine(String.Format("\t\tComment: {0} - {1} - {2}", Comment.Author, Comment.Created, Comment.Text));
                    }

                    foreach (dynamic Attachment in Issue.Attachments)
                    {
                        String Name = Attachment.name;
                        String Url = Attachment.url;
                        String Author = Attachment.authorLogin;
                        String Id = Attachment.id;
                        String Group = Attachment.group;
                        long Created = Attachment.created;
                        Console.WriteLine(String.Format("\t\tAttachment: {0} - {1} - {2}", Name, Author, Url));
                    }
                }
            }
        }
    }
}

Some gotcha’s that I encountered

The following code wont work if your YouTrack instance is hosted in the cloud:

var connection = new Connection("http://zzz.myjetbrains.com/youtrack"); 
var connection = new Connection("zzz.myjetbrains.com/youtrack"); 
var connection = new Connection("zzz.myjetbrains.com"); 

The following code will raise a HTTPException: NotFound Not Found exception. You need to to use the ShortName member:

Issues = IssueManager.GetAllIssuesForProject(project.Name);