Skip to content

Commit

Permalink
Add Tasks/Bootstrap sample, amend related
Browse files Browse the repository at this point in the history
  • Loading branch information
nightroman committed Feb 19, 2024
1 parent 17ae0f5 commit cb70d5d
Show file tree
Hide file tree
Showing 12 changed files with 119 additions and 56 deletions.
10 changes: 10 additions & 0 deletions Tasks/Bootstrap/.test.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

task Direct {
($r = .\Project.build.ps1 Build -Configuration Release)
assert ($r -contains 'Building Release')
}

task Engine {
($r = Invoke-Build Build -Configuration Release)
assert ($r -contains 'Building Release')
}
40 changes: 40 additions & 0 deletions Tasks/Bootstrap/Project.build.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<#
.Synopsis
Directly invocable build script with Invoke-Build bootstrapping.
.Example
PS> ./Project.build.ps1 Build
This command invokes the task Build defined in this script.
If Invoke-Build is not available, its module is installed.
Then Invoke-Build is called.
.Example
PS> Invoke-Build Build
This command may be used when Invoke-Build is available.
#>

param(
[Parameter(Position=0)]
$Tasks
,
[ValidateSet('Debug', 'Release')]
[string]$Configuration = 'Release'
)

# bootstrap
if (!$MyInvocation.ScriptName.EndsWith('Invoke-Build.ps1')) {
$ErrorActionPreference = 1
if (!(Get-Command Invoke-Build -ErrorAction 0)) {
Write-Host 'Installing module InvokeBuild...'
Install-Module InvokeBuild -Scope CurrentUser -Force
Import-Module InvokeBuild
}
return Invoke-Build $Tasks $MyInvocation.MyCommand.Path @PSBoundParameters
}

# Synopsis: Build project.
task Build {
"Building $Configuration"
}
31 changes: 31 additions & 0 deletions Tasks/Bootstrap/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Build script with Invoke-Build bootstrapping

In addition to being directly invokable, see [Direct](../Direct), build scripts
may automatically check for the availability of the command `Invoke-Build` and
install its module when needed

```powershell
param(
[Parameter(Position=0)]
[string[]]$Tasks,
#... other script parameters
)
# bootstrap
if (!$MyInvocation.ScriptName.EndsWith('Invoke-Build.ps1')) {
$ErrorActionPreference = 1
if (!(Get-Command Invoke-Build -ErrorAction 0)) {
Write-Host 'Installing module InvokeBuild...'
Install-Module InvokeBuild -Scope CurrentUser -Force
Import-Module InvokeBuild
}
return Invoke-Build $Tasks $MyInvocation.MyCommand.Path @PSBoundParameters
}
# the usual build script
task ...
```

See [Project.build.ps1](Project.build.ps1) for the working example.

See [Direct](../Direct) for some more details about direct calls.
4 changes: 2 additions & 2 deletions Tasks/Direct/.test.ps1
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@

task Direct {
($r = .\my.build.ps1 t1, t2 -Param1 bar -Param2 42)
($r = .\Project.build.ps1 t1, t2 -Param1 bar -Param2 42)
assert ($r -contains 'Param1 = bar')
assert ($r -contains 'Param2 = 42')
}

task Engine {
($r = Invoke-Build t1, t2 my.build.ps1 -Param1 bar -Param2 42)
($r = Invoke-Build t1, t2 -Param1 bar -Param2 42)
assert ($r -contains 'Param1 = bar')
assert ($r -contains 'Param2 = 42')
}
4 changes: 2 additions & 2 deletions Tasks/Direct/my.build.ps1 → Tasks/Direct/Project.build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
parameters are usual build script parameters.
.Example
> ./my.build.ps1
> ./Project.build.ps1
Invoke the default task
.Example
> ./my.build.ps1 t1, t2 -Param1 bar -Param2 42
> ./Project.build.ps1 t1, t2 -Param1 bar -Param2 42
Invoke tasks t1 and t2 with some parameters
.Example
Expand Down
25 changes: 17 additions & 8 deletions Tasks/Direct/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Build scripts are normally invoked by the engine `Invoke-Build`, not directly.
If this is inconvenient then decorate a script to make it directly invokable.
Add `Tasks` as the first parameter and the command redirecting the call:
Add `Tasks` as the first parameter and the code block redirecting the call:

```powershell
param(
Expand Down Expand Up @@ -39,19 +39,28 @@ directory or subdirectory then you may omit the script:
Invoke-Build <tasks> [<parameters>]
```

Note that `Invoke-Build` parameters are not available on direct calls, i.e. you
cannot specify `Safe`, `Summary`, `WhatIf`, etc. When they are needed use the
usual call by `Invoke-Build`.
See [Project.build.ps1](Project.build.ps1) for the working example.

See the script [my.build.ps1](my.build.ps1) for a working example.
## Caveats

## Bootstrap InvokeBuild
Directly invocable build scripts are handy but they have rules and limitations.

Directly invokable scripts may automatically install `InvokeBuild` when needed.
The rules are the extra parameter `$Tasks` and the code block "who calls me".
This code block must be placed immediately after the script parameter block.

Script parameters cannot use `Invoke-Build` features, e.g. `parameter` in
default parameter value expressions.

`Invoke-Build` parameters `Safe`, `Summary`, `WhatIf` are not available on
direct calls.

## Bootstrap

Directly invokable scripts may automatically install the `InvokeBuild` module.

See examples:

- [08-bootstrap/tea.build.ps1](../01-step-by-step-tutorial/08-bootstrap/tea.build.ps1) - straightforward bootstrapping
- [Bootstrap/Project.build.ps1](../Bootstrap/Project.build.ps1) - straightforward bootstrapping
- [Paket/Project.build.ps1](../Paket/Project.build.ps1) - some custom bootstrapping

## Notes
Expand Down
2 changes: 1 addition & 1 deletion Tasks/Paket/.config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"paket": {
"version": "5.249.2",
"version": "8.0.3",
"commands": [
"paket"
]
Expand Down
1 change: 1 addition & 0 deletions Tasks/Paket/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.paket
packages
paket-files
paket.lock
14 changes: 6 additions & 8 deletions Tasks/Paket/Project.build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,22 @@ param(

# Direct call: ensure packages and call the local Invoke-Build

if ([System.IO.Path]::GetFileName($MyInvocation.ScriptName) -ne 'Invoke-Build.ps1') {
$ErrorActionPreference = 'Stop'
if (!$MyInvocation.ScriptName.EndsWith('Invoke-Build.ps1')) {
$ErrorActionPreference = 1
$ib = "$PSScriptRoot/packages/Invoke-Build/tools/Invoke-Build.ps1"

# get packages
if (!(Test-Path -LiteralPath $ib)) {
# restore paket and other local dotnet tools
# restore paket and other tools
dotnet tool restore
if ($LASTEXITCODE) {throw "tool restore exit code: $LASTEXITCODE"}

# install packages (if you keep paket.lock, use restore instead)
# ensure packages
dotnet paket install
if ($LASTEXITCODE) {throw "paket install exit code: $LASTEXITCODE"}
}

# call Invoke-Build
& $ib -Task $Tasks -File $MyInvocation.MyCommand.Path @PSBoundParameters
return
return & $ib $Tasks $MyInvocation.MyCommand.Path @PSBoundParameters
}

# Normal call for tasks, either by local or global Invoke-Build
Expand All @@ -57,5 +55,5 @@ task Build {
# Synopsis: Remove files.
task Clean {
Write-Warning 'This sample removes paket.lock'
remove packages, paket-files, paket.lock
remove .paket, packages, paket-files, paket.lock
}
42 changes: 8 additions & 34 deletions Tasks/Paket/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

The sample script *Project.build.ps1* shows how to use automatic bootstrapping.
The script is designed as directly invokable by PowerShell and it does not
require `Invoke-Build` installed. `Invoke-Build` is restored using `paket`.
require `Invoke-Build` installed. `Invoke-Build` is restored using `paket`,
locally.

The `paket` tool is used as one possible way of getting packages. Instead or in
addition, we could use `PSDepend`, `NuGet.exe`, `Install-Module`, etc.
Expand Down Expand Up @@ -35,53 +36,26 @@ Such a script is designed for scenarios like:
- Packages restored by `paket` and stored locally.
In this sample, it is `Invoke-Build`.
- *paket-files*
- Files generated or restored by `paket.
- Files generated or restored by `paket`.

These directories are usually added to `.gitignore`.

## How to get PowerShell modules by paket

Module entries in *paket.dependencies* should normally use PSGallery source.
Module packages should be downloaded to *packages* (`storage: packages`).
The build script should be designed to import modules from *packages*.

This looks like a ceremony but it has some advantages. This scenario does not
pollute the usual PowerShell module directories and avoids possible module
version issues.

## How to customize package/module management

If `paket` and its *paket.dependencies* is not enough, e.g. you want to install
modules by `Install-Module`, then look at the "bootstrapping" block in
*Project.build.ps1* and add required checks and commands.

For example, just for the `InvokeBuild` module bootstrapping instead of `paket`
we could use this trivial PowerShell code:

```powershell
if (!(Get-Module InvokeBuild -ListAvailable)) {
Install-Module InvokeBuild
Import-Module InvokeBuild
#... other stuff
}
```

## Steps from scratch

To create the dotnet tool manifest *.config/dotnet-tools.json*, invoke:
Create the dotnet tool manifest *.config/dotnet-tools.json*:

dotnet new tool-manifest

To install paket and add its record to the manifest, invoke:
Install paket and add to the manifest:

dotnet tool install paket

To create the paket file *paket.dependencies*, invoke:
Create the paket file *paket.dependencies*:

dotnet paket init

Add Invoke-Build line to *paket.dependencies*:
Add this line to *paket.dependencies*:

nuget Invoke-Build storage: packages

Add the sample build script *Project.build.ps1*.
Use the build script like *Project.build.ps1*.
1 change: 0 additions & 1 deletion Tasks/Paket/paket.dependencies
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
source https://api.nuget.org/v3/index.json

storage: none
framework: netcoreapp3.1, netstandard2.0, netstandard2.1

nuget Invoke-Build storage: packages
1 change: 1 addition & 0 deletions Tasks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- [01-step-by-step-tutorial](01-step-by-step-tutorial) - From "Hello world" to featured script.
- [Attributes](Attributes) How to use custom attributes with task actions.
- [Bootstrap](Bootstrap) How to install the module automatically.
- [Confirm](Confirm) How to use `Confirm-Build` to confirm some tasks.
- [Direct](Direct) How to make build scripts invokable directly.
- [Dynamic](Dynamic) How to use a dynamic script with dynamic tasks.
Expand Down

0 comments on commit cb70d5d

Please sign in to comment.