Geek Noise
Rants, rambles, news and notes by Peter Provost
10

Fun with PowerShell and TFS Work Items

Friday, 10 April 2009 06:53 by Peter Provost

It all started with me wanting a better burndown chart for my team. That was relatively simple to solve as we’ve got some pretty good ones floating around inside Microsoft. (Apologies in advance but I can’t share my spreadsheet with you at this time.)

Then came the fun… I wanted to be able to pull any given day’s “work done” and “work remaining” data. But for my own reasons, I don’t want this spreadsheet bound to the OLAP cube, I want to be able to mess with it.

Being the PowerShell junkie that I am I was pretty sure I could do this. I found James Manning’s killer “get-tfs.ps1” script which meant I didn’t have to work out that bit myself.

Using it is simple:

   1: $tfs = get-tfs http://mytfs:8080
   2: $wi = $tfs.wit.GetWorkItem(12345)

As you can see, I get the TFS client object via James’s script, and then I pull a WorkItem from it. From there I can ask the $wi object for all the bits of information in the work item. Piping it to get-member showed me the following methods and properties:

   1:  
   2:    TypeName: Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem
   3:  
   4: Name                   MemberType            Definition                                                                
   5: ----                   ----------            ----------                                                                
   6: FieldChanged           Event                 Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemFieldChangeEve...
   7: Copy                   Method                Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem Copy(Microsof...
   8: Equals                 Method                bool Equals(System.Object obj)                                            
   9: GetHashCode            Method                int GetHashCode()                                                         
  10: GetNextState           Method                string GetNextState(string action)                                        
  11: GetType                Method                type GetType()                                                            
  12: IsValid                Method                bool IsValid()                                                            
  13: MergeToLatest          Method                System.Void MergeToLatest()                                               
  14: Open                   Method                System.Void Open()                                                        
  15: PartialOpen            Method                System.Void PartialOpen()                                                 
  16: Reset                  Method                System.Void Reset()                                                       
  17: Save                   Method                System.Void Save()                                                        
  18: SyncToLatest           Method                System.Void SyncToLatest()                                                
  19: ToString               Method                string ToString()                                                         
  20: Validate               Method                System.Collections.ArrayList Validate()                                   
  21: Item                   ParameterizedProperty System.Object Item(string name) {get;set;}, System.Object Item(Microsof...
  22: AreaId                 Property              System.Int32 AreaId {get;set;}                                            
  23: AreaPath               Property              System.String AreaPath {get;set;}                                         
  24: AttachedFileCount      Property              System.Int32 AttachedFileCount {get;}                                     
  25: Attachments            Property              Microsoft.TeamFoundation.WorkItemTracking.Client.AttachmentCollection A...
  26: ChangedBy              Property              System.String ChangedBy {get;}                                            
  27: ChangedDate            Property              System.DateTime ChangedDate {get;}                                        
  28: CreatedBy              Property              System.String CreatedBy {get;}                                            
  29: CreatedDate            Property              System.DateTime CreatedDate {get;}                                        
  30: Description            Property              System.String Description {get;set;}                                      
  31: DisplayForm            Property              System.String DisplayForm {get;}                                          
  32: ExternalLinkCount      Property              System.Int32 ExternalLinkCount {get;}                                     
  33: Fields                 Property              Microsoft.TeamFoundation.WorkItemTracking.Client.FieldCollection Fields...
  34: History                Property              System.String History {get;set;}                                          
  35: HyperLinkCount         Property              System.Int32 HyperLinkCount {get;}                                        
  36: Id                     Property              System.Int32 Id {get;}                                                    
  37: IsDirty                Property              System.Boolean IsDirty {get;}                                             
  38: IsNew                  Property              System.Boolean IsNew {get;}                                               
  39: IsOpen                 Property              System.Boolean IsOpen {get;}                                              
  40: IsPartialOpen          Property              System.Boolean IsPartialOpen {get;}                                       
  41: IsReadOnly             Property              System.Boolean IsReadOnly {get;}                                          
  42: IsReadOnlyOpen         Property              System.Boolean IsReadOnlyOpen {get;}                                      
  43: IterationId            Property              System.Int32 IterationId {get;set;}                                       
  44: IterationPath          Property              System.String IterationPath {get;set;}                                    
  45: Links                  Property              Microsoft.TeamFoundation.WorkItemTracking.Client.LinkCollection Links {...
  46: NodeName               Property              System.String NodeName {get;}                                             
  47: Project                Property              Microsoft.TeamFoundation.WorkItemTracking.Client.Project Project {get;}   
  48: Reason                 Property              System.String Reason {get;set;}                                           
  49: RelatedLinkCount       Property              System.Int32 RelatedLinkCount {get;}                                      
  50: Rev                    Property              System.Int32 Rev {get;}                                                   
  51: RevisedDate            Property              System.DateTime RevisedDate {get;}                                        
  52: Revision               Property              System.Int32 Revision {get;}                                              
  53: Revisions              Property              Microsoft.TeamFoundation.WorkItemTracking.Client.RevisionCollection Rev...
  54: State                  Property              System.String State {get;set;}                                            
  55: Store                  Property              Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore Store {g...
  56: TemporaryId            Property              System.Int32 TemporaryId {get;}                                           
  57: Title                  Property              System.String Title {get;set;}                                            
  58: Type                   Property              Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemType Type {get;} 
  59: Uri                    Property              System.Uri Uri {get;}                                                     
  60: WorkItemLinkHistory    Property              Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemLinkCollection...
  61: WorkItemLinks          Property              Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemLinkCollection...
  62: GetCompletedWorkByDate ScriptMethod          System.Object GetCompletedWorkByDate();                                   
  63: GetRemainingWorkByDate ScriptMethod          System.Object GetRemainingWorkByDate();                                   

Lots of great stuff there, but what I really wanted was the work done on any given day and not the current work remaining. Hmmm…

After digging around in the object model (with PowerShell of course), I found the answer. The Revisions collection contains the full history of the Work Item. If I enumerated that, looking at the dates, I should be able to get what I want.

A little while later I had it all worked out, setup as a type extension in PowerShell so it automatically works with every WorkItem type I get. Type extensions are added via a special XML file (more info). Here’s what I added to mine:

   1: <Type>
   2:     <Name>Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem</Name>
   3:     <Members>
   4:         <ScriptMethod>
   5:             <Name>GetCompletedWorkByDate</Name>
   6:             <Script>
   1:  
   2:                 $date = [datetime] $args[0]
   3:                 $total = 0
   4:                 $this.Revisions | % {
   5:                     if ($_.Fields['Changed Date'].Value.Date -eq $date) {
   6:                         $work = $_.Fields["Completed Work"]
   7:                         if ($work -ne $null) { $total += ([int] $work.Value) - ([int] $work.OriginalValue) }
   8:                     }
   9:                 }
  10:                 return $total
  11:             
</Script>
   7:         </ScriptMethod>
   8:         <ScriptMethod>
   9:             <Name>GetRemainingWorkByDate</Name>
  10:             <Script>
   1:  
   2:                 $date = [datetime] $args[0]
   3:                 $total = 0
   4:                 $this.Revisions | % {
   5:                     if ($_.Fields['Changed Date'].Value.Date -le $date) {
   6:                         $work = $_.Fields["Remaining Work"]
   7:                         if ($work -ne $null) {
   8:                             $total = ([int] $work.Value)
   9:                         }
  10:                     }
  11:                 }
  12:                 return $total
  13:             
</Script>
  11:         </ScriptMethod>
  12:     </Members>
  13: </Type>

What this does is add two new methods to the .NET type Microsoft.TeamFoundation.WorkItemTracking.Client.Workitem. The first method takes a date and returns the sum of the “Completed Work” field for all revisions that occurred on that date. The second method enumerates the same Revisions collection, but this time it process all revisions up through the given date looking for the last one that says anything about the “Remaining Work” field.

Next step… create a script to wrap all this up into an easy to use command I call get-burndowndata.ps1:

   1: param( 
   2:         $deliverable = $(throw "Must provide deliverable ID"),
   3:         $date = [datetime]::Now.Date 
   4: )
   5:  
   6: $tfs = get-tfs http://mytfs:8080
   7: $deliverable = $tfs.WIT.GetWorkItem($deliverable) 
   8:  
   9: $result = @()
  10:  
  11: $deliverable.WorkItemLinks | ? { $_.LinkType.Name -eq "Child" } | % {
  12:     $feature = $tfs.WIT.GetWorkItem($_.TargetId)
  13:  
  14:     $feature.WorkItemLinks | ? { $_.LinkType.Name -eq "Child" } | % {
  15:         $task = $tfs.WIT.GetWorkItem($_.TargetId)
  16:         $completed = $task.GetCompletedWorkByDate($date)
  17:         $remaining = $task.GetRemainingWorkByDate($date)
  18:  
  19:         $obj = "" | select-object ID, Title, Completed, Remaining 
  20:         $obj.ID = $task.ID
  21:         $obj.Title = $task.Title
  22:         $obj.Completed = $completed
  23:         $obj.Remaining = $remaining
  24:  
  25:         $result += $obj
  26:     }
  27: }
  28:  
  29: $result

Now, getting any given day’s burndown information is as simple as calling that script and opening the result in Excel:

   1: get-burndowndata.ps1 12345 "4/1/2009" | export-csv data_20090401.csv
   2: invoke-item data_20090401.csv

Works like a charm!

Disclaimer: The get-burndowndata.ps1 script makes lots of assumptions about your TFS Work Item struction and relationships . It probably won’t work unmodified for anyone outside of DevDiv.

Currently rated 1.0 by 1 people

  • Currently 1/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
22

Updated PowerShell Syntax File for vim

Monday, 22 September 2008 05:50 by Peter Provost

Thanks to Heath Stewart for sending in some nice updates to the vim syntax file for PowerShell.

Changes in v2.7:

  1. Added begin, process, and end keywords for script blocks
  2. Make foreach and where highlight when used as foreach-object and where-object

The new version is up and available here: http://www.vim.org/scripts/script.php?script_id=1327

Currently rated 1.0 by 1 people

  • Currently 1/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:   ,
Categories:   Technology
Actions:   E-mail | del.icio.us | Permalink | Comments (2) | Comment RSSRSS comment feed
14

The Last vsvars32.ps1 I'll Ever Need

Saturday, 14 June 2008 06:48 by Peter Provost

I know Chris Tavares mentioned this at work one day when I was at P&P, and I'm very excited that he blogged it and made it so easy to do.

The last vsvars32.ps1 I'll ever need

I made one tiny modification, which is to default to 9.0 instead of 8.0 since that is the environment I use, but other than that it seems to work great.

Thanks Chris!

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
25

Windows PowerShell Quick Start

Monday, 25 February 2008 05:45 by Peter Provost

Not sure how I missed this before, but I was looking for something today and this popped up in my search engine.

Channel9 Wiki: Windows PowerShell Quick Start

Perfect!

Technorati Tags:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:  
Categories:   Technology
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed
12

TDD with PowerShell - Mocking Things

Monday, 12 November 2007 02:04 by Peter Provost

Recently, I’ve been dabbling with doing TDD in PowerShell. I think there may be a TDD Framework brewing in my head, but at this point I haven't done enough to figure out what such a framework would look like. Often I find that the functions I’m writing depend heavily on built-in cmdlets and BCL types and there can be issues mocking them out when the underlying .NET type isn’t easy to mock out. This isn’t so much an issue with PS as it is an issue with .NET, but since PS is very close to the edge of the world (where the code touches the user), it can be hard to mock.

That said, as with most dynamic scripting evnironments, you can mock out a lot of things. Suppose you want to test a function that depends on get-childitem. Because functions evaluate before cmdlets, you can actually replace it by defining a function with that name:

function get-childitem() 
{ 
return @()
} 

Now you have a naked stub get-childitem that returns an empty array. All is good so far. Next you need a gci that returns a single FileInfoObject and for your test you want to have the “archive” bit set on this file. You try to create a new replacement function for get-childitem:

function get-childitem() 
{ 
$fi = new-object System.IO.FileInfo(“bogus.txt”) 
$fi.Attributes = [System.IO.FileAttributes]::Archive 
} 

But at this point you discover that you can’t set the Archive bit, because the file doesn’t exist.

This is actually a common problem we run into when we TDD up against another API that isn’t mock friendly. In an OO language like C#, I will typically wrap it up in another class & interface that I own and then mock out the interface for my code. This is do-able in PS, but a little more complicated because you may have to implement a fair amount of extra code to create and return a PSObject that has the interface you expect to find in your calling code. (This code can be a lot smaller, but less clear if you want… I opted for clarity):

function get-childitem() 
{ 
$fi = new-object PSObject 
$getter = { return [System.IO.FileAttributes]::Archive } 
Add-member –inputObject $fi –memberType ScriptProperty –name Attributes –value $getter $secondValue $setter 
Return $fi
} 

The challenge with this approach is the amount of code it takes to mock out all of the parts that you need. For example, if you need the Mode script property that comes built in to PowerShell for DirectoryInfo and FileInfo, then you will have to add them to your mock. If you also need to support the setter for the Attributes property, then you will need to add that (and possibly a Note member to hold the data).

As with all mocking exercises, this gets complicated when interacting with real things and since PowerShell is really about interacting with real things, it is hard.

Enjoy!

Technorati Tags: , , ,

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
18

PowerShell++ (aka PowerShell is better with friends)

Thursday, 18 October 2007 03:10 by Peter Provost

Yes, my love affair with Windows PowerShell continues. I've rediscovered a couple of things that make the PowerShell experience even better.

The most recent one for me is PowerTab. PowerTab takes the extensible Tab-completion feature of PowerShell to rediculous extremes. Imagine Intelli-crack for everything... functions, cmdlets, paths, .NET types and methods, parameters, etc. Truly amazing work. (Here's a video to whet your appetite: PowerTab 0.93 and BDD 2007 teaser)

Another one isn't really about PowerShell per se, as much as it is about redisovering some gems from my past. I've always been a VIM guy (as opposed to an Emacs guy). In fact, I'm the maintainer of the VIM syntax files for PowerShell. But there are lots of other gems in the Unix world that make PowerShell even better.

There are a number of projects on CodePlex and others that are attempting to recreate all the old Unix utils as PowerShell cmdlets, and in some cases they make sense. For example, when you use the unix 'tail' command, it returns you a big ol' string. For PowerShell you would probably prefer to have it return an array of strings to save you a step in your  command pipeline.

But there are things that aren't really about returning objects to you. Things that are more about manipulating files and such. For example, suppose you want to do a global search and replace in a file. Sure you can do this with a few lines of PowerShell script. You could even create a custom function of cmdlet to generalize it for you.

Or you could just use 'sed', the old Unix stream editor. It is simple, amazingly powerful, and works every time. There are a surprising number of useful gems (like 'less' for example) from the Unix world that have been ported over to Win32 and they all work wonderfully with PowerShell. There are two projects on Source Forge that I keep an eye on for these kinds of things:

The final tidbit is the #PowerShell IRC channel on FreeNode.net. Yes... I use IRC. IRC is still the best open and freely available distributed chat system out there. Again, like so many of the old Unix things, it is simple, powerful and works every time. There are lots of very intertesting channels on FreeNode, most of which are about technology and not the newest bad reality TV show. If you're interested in giving IRC a try, check out the Silverex X-Chat client and look for me on Freenode as PProvost.

So all you PowerShell junkies... don't forget your roots. There are still gems to be mined from the olden days.

Technorati Tags: , , ,

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:  
Categories:   Technology
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed
22

Quick-n-Dirty PowerShell Password Generator

Friday, 22 June 2007 16:18 by Peter Provost

I can't even remember when or why I wrote this, but I needed to quickly and easily generate a password for something. I typically use PasswordMinder for this, but when I wrote this I just needed to generate a whole bunch of them for someone else and didn't need to keep them in my password database.

It is a relatively simple script that uses the RNGCryptoServiceProvider class in the System.Security.Cryptography namespace to generate the random sequence. It uses a default set of characters (see the script) and a default password length of 12.

generate-password.ps1:

param( 
[int] $len = 12,
[string] $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_!@#$%^&*()_"
)
$bytes = new-object "System.Byte[]" $len
$rnd = new-object System.Security.Cryptography.RNGCryptoServiceProvider
$rnd.GetBytes($bytes)
$result = ""
for( $i=0; $i -lt $len; $i++ )
{
$result += $chars[ $bytes[$i] % $chars.Length ]	
}
$result

DISCLAIMER: I am not a crypto expert, so this might not be a very good implementation! It is meant to illustrate how you can do this stuff in PowerShell and not anything else.

Currently rated 4.0 by 1 people

  • Currently 4/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:   ,
Categories:   Technology
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed
03

World of Warcraft Addon Updater in PowerShell

Saturday, 3 March 2007 15:48 by Peter Provost

UPDATE: I am now maintaining this script over on my Warcraft Wiki site on pbwiki. Please go there for documentation and the most recent updates.

I couldn't resist. Sometimes I don't even know why I do these things. Last night I decided to start playing with System.Net.WebClient from Windows PowerShell and four hours later I had something close. A few more hours today and now I have a PowerShell script that:

  • Updates Subversion working copies (can be disabled w/ a switch)
  • Can be configured to check for updates from wowinterface.com using a simple data file in your addons folder called addons.ps1 (see the comment header below for a sample of this file) and a nice XML endpoint provided by the guys at wowi (thanks Dolby!)
  • Will check to see if an addon is one of the WowAce family of addons and will try to update it from their using their RSS feed for information.

I know I can add more addon sites to this later, but now that Auctioneer is up on wowi (ID: 4812) I don't know if I have any addons that I use that can't be updated by this script. Perhaps one or two, but that isn't bad given that I have 169 addons in my Addons directory.

This script has a few dependencies for extracting ZIP and RAR files. You will need to get unzip.exe from Info-ZIP and UnRar.exe from RARLabs and put them both on your path. Both are free.

Anyway, here is the script. There is a download at the bottom of this post for those of you who just want to have it.

DISCLAIMER: This script is supplied without warranty or support. If you play WoW and don't know anything about PowerShell, this might be a bit much for you. Take a look at WowAceUpdater or WUU instead.

  1 #########################################################################
  2 # Name: update-addons.ps1
  3 # Version: 1.0
  4 # Author: Peter Provost <peter@provost.org>
  5 #
  6 # Usage: update-addons [-skipSvn]
  7 #
  8 # Remarks: This is a simple powershell script for updating your
  9 #   World of Warcraft addons. It will autodetect SVN working copies
 10 #   and update them. It will look for a file called addons.ps1
 11 #   in your Addons folder to define special locations for downloading
 12 #   addons (see below for a sample addons.ps1). After that it will test
 13 #   if the addon can be updated from http://files.wowace.com. Any addon
 14 #   still unmatched will be skipped.
 15 #
 16 #   This sample addons.ps1 hows how to configure updates from 
 17 #   wowinterface.com:
 18 #
 19 #   $wowiAddons = @{
 20 #     'FlightMap' = 3897;
 21 #     'Clique' = 5108;
 22 #   };
 23 #
 24 #   At this point only wowinterface.com is supported in this data file
 25 #   but more may be added later.
 26 #
 27 #########################################################################
 28 
 29 param (
 30     [switch] $skipSvn
 31 );
 32 
 33 # Configuration - change these as needed
 34 $wowAddonDir = "C:\World of Warcraft\Interface\Addons";
 35 $stateFile = "PSUpdateAddons.state";
 36 
 37 function update-addon($addonSource, $downloadUrl, $remoteVersion, $fileName)
 38 {
 39     if ( $remoteVersion -ne $localVer) {
 40       write-host "$_ : ($addonSource) Update required: Current ver=$localVer, Remote ver=$remoteVersion";
 41 
 42       $tempFilePath = join-path $tempDir $fileName;
 43       downloadextract-addon $downloadUrl $tempFilePath;
 44 
 45       write-host "`tUpdating state file..." -noNewLine;
 46       $remoteVersion > $stateFilePath;
 47       "done." | write-host;
 48     } else {
 49       write-host "$_ : ($addonSource) Addon up-to-date. Skipping.";
 50     }
 51 }
 52 
 53 # Helper function for updating a single addon]
 54 function downloadextract-addon ([string] $uri, [string] $tempFile)
 55 {
 56   write-host "`tDownloading $uri to $($tempFile)..." -noNewLine;
 57   $wc.DownloadFile( $uri, $tempFile );
 58   write-host "done.";
 59 
 60   $ext = [System.IO.Path]::GetExtension($tempFile);
 61   switch ($ext) {
 62     ".rar" {
 63       write-host "`tExtracting RAR Archive..." -noNewLine;
 64       & unrar x -o+ $tempFile $wowAddonDir;
 65     }
 66     ".zip" {
 67       write-host "`tExtracting ZIP Archive..." -noNewLine;
 68       & unzip -o $tempFile -d $wowAddonDir;
 69     }
 70     default { write-host "UNKNOWN EXTENSION TYPE!" }
 71   }
 72   write-host "done.";
 73 
 74   write-host "`tDeleting zip file..." -noNewLine;
 75   remove-item $tempFile;
 76   write-host "done.";
 77 }
 78 
 79 function test-wowaceaddon( [string] $addonName )
 80 {
 81   return ((get-wowaceaddon $addonName)-ne $null)
 82 }
 83 
 84 function get-wowaceaddon( [string] $addonName )
 85 {
 86   $xpath = ("//item[title='ADDON']" -replace 'ADDON', $_.Name);
 87   return $indexXmlDoc.SelectSingleNode($xpath);
 88 }
 89 
 90 # Setup a few things before we get started
 91 $wc = new-object System.Net.WebClient;
 92 $tempDir = join-path (get-content env:\temp) "PsWowUpdater";
 93 if (-not (test-path $tempDir)) { new-item -type directory -path $tempDir; }
 94 
 95 # Load in the WowAce Index file
 96 write-host "Downloading latest.xml from http://files.wowace.com";
 97 $uri = "http://files.wowace.com/latest.xml";
 98 $rssData = $wc.DownloadString($uri);
 99 $indexXmlDoc = [xml] $rssData;
100 
101 # Load in the WOWI config database
102 . (join-path $wowAddonDir "addons.ps1")
103 
104 (get-childitem $wowAddonDir | ? { $_.PSIsContainer }) | % {
105 ## SVN UPDATE
106   if (join-path $_.Fullname ".svn" | test-path ) {
107     if ($skipSvn.isPresent) {
108       write-host "$_ : Skipping SVN working copy";
109     } else {
110       write-host "$_ : Updating SVN working copy";
111       svn up -q $_.Fullname;
112     }
113   }
114 
115 ## WOWINTERFACE.COM
116   elseif ($wowiAddons.Contains($_.Name)) {
117     $stateFilePath = join-path $_.Fullname "PSUpdateAddons.state";
118     $localVer = "";
119     if (test-path $stateFilePath) { $localVer = (get-content $stateFilePath); }
120 
121     $uri = ("http://www.wowinterface.com/patcherXXXX.xml" -replace "XXXX", $wowiAddons[$_.Name]);
122     $wowiXml = [xml] $wc.DownloadString($uri);
123 
124     $downloadUrl = $wowiXml.UpdateUI.Current.UIFileURL;
125     $remoteVersion = $wowiXml.UpdateUI.Current.UIVersion;
126     $fileName = $wowiXml.UpdateUI.Current.UIFile;
127     $addonSource = "WowInterface.com";
128 
129     update-addon $addonSource $downloadUrl $remoteVersion $fileName
130   }
131 
132 ## WOWACE FILES
133   elseif (test-wowaceaddon $_.Name) {
134     $stateFilePath = join-path $_.Fullname "PSUpdateAddons.state";
135     $localVer = "";
136     if (test-path $stateFilePath) { $localVer = (get-content $stateFilePath); }
137 
138     $elt = get-wowaceaddon $_.Name;
139 
140     $downloadUrl = $elt.enclosure.url;
141     $remoteVersion = $elt.version;
142     $fileName = $downloadUrl.Substring($downloadUrl.LastIndexOf("/")+1);
143     $addonSource = "WowAce.com";
144 
145     update-addon $addonSource $downloadUrl $remoteVersion $fileName
146   }
147 
148 ## Unknown addon source
149   else {
150     write-host "$_ : Can't figure this one out. Skipping.";
151   }
152 }

Download update-addons.zip (2KB). And here is my addons.ps1 data file to update my wowi addons. Create a file like that in your Addons folder and the script will find it.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
26

Shifting Powershell Arrays

Monday, 26 February 2007 04:25 by Peter Provost

In one of my recent posts, I showed a newer version of my 'sudo for Powershell' that was all writted in Powershell and left an open question about how to collect the variable arguments at the end.

Wes Haggard again comes through with the answer and points me to a post on the Powershell Blog called PowerShell Tip: How to “shift” arrays…

Using that tip, Wes gave me back a much tighter version of the elevation function:

function elevate
{
$file, [string]$arguments = $args;
$psi = new-object System.Diagnostics.ProcessStartInfo $file;
$psi.Arguments = $arguments;
$psi.Verb = "runas";
[System.Diagnostics.Process]::Start($psi);
}

Killer! Thanks again Wes!

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:  
Categories:   Technology
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed
25

Powershell Sudo (sort of) for Vista UAC -- REDUX

Sunday, 25 February 2007 13:08 by Peter Provost

Okay, after the email from Wes this morning I spent some of the time on the plane today re-doing my UAC privilege elevation stuff... this time in pure Powershell script.

As expected, it is quite simple:

function elevate {
$file = $args[0]; $param = "";
for($i=1;$i -lt $args.Length; $i++) { $param += $args[$i] + " "; }
$psi = new-object "System.Diagnostics.ProcessStartInfo"
$psi.FileName = $file; $psi.Arguments = $param; $psi.Verb = "runas";
[System.Diagnostics.Process]::Start($psi)
}

Now if only I could figure out how to have something like the Ruby "..." catch-all parameter, I could get rid of all that for-loop crap.

UPDATE 2007-02-26: I just posted a newer version of the elevate function that doesn't have the for-loop. Use that one instead.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:   , ,
Categories:   Technology
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed