Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 63 additions & 3 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,59 @@ jobs:
uses: gittools/actions/gitversion/execute@v0
with:
useConfigFile: true
- name: Update CHANGELOG
shell: pwsh
run: |
$version = "${{ steps.gitversion.outputs.semVer }}"
$date = Get-Date -Format "yyyy-MM-dd"
$changelogPath = ".\CHANGELOG.md"

Write-Host "Updating CHANGELOG.md with version $version"
$content = Get-Content $changelogPath -Raw
$content = $content -replace '## \[Unreleased\]', "## [$version] - $date"
Set-Content $changelogPath -Value $content -NoNewline

Write-Host "CHANGELOG.md updated successfully"

- name: Extract current version changelog
id: changelog
shell: pwsh
run: |
$version = "${{ steps.gitversion.outputs.semVer }}"
$changelogPath = ".\CHANGELOG.md"

$content = Get-Content $changelogPath -Raw

# Extract content between current version and next version/end
$pattern = "(?s)## \[$version\].*?\n(.*?)(?=\n## \[|$)"
if ($content -match $pattern) {
$changelogContent = $matches[1].Trim()

# Escape for GitHub Actions output
$changelogContent = $changelogContent -replace '%', '%25'
$changelogContent = $changelogContent -replace '\n', '%0A'
$changelogContent = $changelogContent -replace '\r', '%0D'

# Use new GitHub Actions output syntax
"CHANGELOG_CONTENT<<EOF" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
$matches[1].Trim() | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"EOF" | Out-File -FilePath $env:GITHUB_OUTPUT -Append

Write-Host "Extracted changelog for version $version"
} else {
Write-Host "Could not extract changelog content"
"CHANGELOG_CONTENT=No changelog found for this version." | Out-File -FilePath $env:GITHUB_OUTPUT -Append
}

- name: Commit CHANGELOG changes
shell: pwsh
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add CHANGELOG.md
git diff --staged --quiet || git commit -m "Update CHANGELOG for version ${{ steps.gitversion.outputs.semVer }} [skip ci] +semver:none"
git push

- name: publish
shell: pwsh
env:
Expand Down Expand Up @@ -67,13 +120,20 @@ jobs:
## OctopusDeploy Module Release v${{ steps.gitversion.outputs.semVer }}

### Installation
Download the zip file and extract it to your PowerShell modules directory, or install from PowerShell Gallery:
Install from PowerShell Gallery:
```powershell
Install-Module -Name OctopusDeploy
```

### Changes
See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details.
Or download the zip file and extract it to your PowerShell modules directory.

### Changes in this Release

${{ steps.changelog.outputs.CHANGELOG_CONTENT }}

---

See full [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for all changes.
files: |
OctopusDeploy-${{ steps.gitversion.outputs.semVer }}.zip
draft: false
Expand Down
148 changes: 36 additions & 112 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,128 +5,52 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [2.0.0]

### Added - Configuration as Code Runbook Support 🎉

#### Get-Runbook
- **CaC Runbook Support**: Added ability to retrieve runbooks from Configuration as Code (CaC) projects stored in Git
- **BranchName Parameter**: New optional parameter to specify Git branch (supports both canonical names like `refs/heads/main` and short names like `main`)
- **Name Parameter**: New optional parameter for filtering runbooks by exact name match across all parameter sets
- **Auto-detection**: Automatically detects and uses the default branch for CaC projects when no branch is specified
- **Enhanced Examples**: Added 8+ comprehensive examples demonstrating CaC and traditional runbook retrieval

#### Invoke-RunbookRun
- **CaC Runbook Execution**: Added support for running runbooks directly from Git branches for CaC projects
- **BranchName Parameter**: New optional parameter to specify which Git branch to run from (defaults to project's default branch)
- **Separate Parameter Sets**: Introduced distinct parameter sets (`Runbook` for CaC, `Snapshot` for traditional) for clearer intent
- **Automatic Validation**: Validates project type (CaC vs traditional) and provides clear error messages when wrong parameter set is used
- **Enhanced Tenant Validation**: Improved validation of tenant connections to project/environment before execution
- **Better Error Messages**: Custom error objects with appropriate categories for clearer troubleshooting

#### Documentation
- **Complete Help Rewrite**: Comprehensive comment-based help for both `Get-Runbook` and `Invoke-RunbookRun`
- **Real-world Examples**: Added practical examples covering CaC branches, traditional snapshots, tenanted/untenanted scenarios
- **Parameter Clarity**: Enhanced parameter descriptions explaining when and how to use each parameter
- **Notes Section**: Added detailed notes about CaC detection, branch handling, and tenant modes
- **README & Getting Started**: Updated with extensive CaC runbook examples and usage patterns

#### Infrastructure
- **GitHub Actions Workflow**: Added `release-with-github-publish.yaml` for automated releases
- **Utility Scripts**: Added helper scripts for tenant management and runbook operations
- **Build Enhancements**: Updated build process for better CI/CD integration

### Changed

#### Get-Runbook - BREAKING CHANGES
- **Parameter Name**: Renamed `RunbookID` parameter to `Id` for consistency with other functions
- **Name Matching**: Changed `-Name` parameter from wildcard support to exact match only
- **Default Behavior**: When called without parameters, now returns only non-CaC runbooks with a warning (previously returned all runbooks but couldn't access CaC runbooks)
- **Parameter Sets**: Simplified parameter sets - removed `byName` set, consolidated into `default`, `byProject`, and `byID`
- **Project Parameter**: Changed from accepting array (`ProjectResource[]`) to single object (`ProjectResource`) for clarity

#### Invoke-RunbookRun - BREAKING CHANGES
- **Default Parameter Set**: Changed from `default` to `Runbook` for better clarity
- **Parameter Separation**: `Runbook` parameter now exclusively for CaC projects; must use `RunbookSnapshot` for traditional projects
- **Auto-resolution Removed**: No longer automatically resolves published snapshots when using `Runbook` parameter
- **Error Handling**: Changed from throwing terminating errors to writing non-terminating errors for better pipeline handling
## [Unreleased]

### Improved

#### Error Handling
- Replaced `throw` statements with `Get-CustomError` and `WriteError` for non-terminating errors
- Added specific exception types (ArgumentException, OctopusResourceNotFoundException, InvalidOperationException)
- Improved error categories (InvalidData, InvalidOperation) for better error handling in scripts

#### Validation
- Enhanced tenant deployment mode validation (Tenanted/Untenanted/TenantedOrUntenanted)
- Added validation for tenant connections to project/environment combinations
- Improved CaC project detection with edge case handling

#### User Experience
- Added verbose logging throughout execution flow for better debugging
- Clear warning messages when CaC runbooks won't be returned
- Helpful error messages suggesting correct parameter usage
- Better ShouldProcess messages showing branch information

### Fixed
- **Logic Error in Invoke-RunbookRun**: Corrected inverted CaC validation logic (was treating CaC projects as traditional)
- **Branch Handling**: Fixed default branch selection when multiple branches exist
- **Edge Case**: Properly handles CaC projects where individual runbooks may not be version controlled

### Removed
- **Get-Runbook**: Removed `byName` parameter set (functionality merged into other parameter sets)
- **Get-Runbook**: Removed wildcard support from `-Name` parameter
- **Documentation**: Removed incorrect/broken `.LINK` references from comment-based help

### Dependencies
- **Octopus.Client**: Updated to newer .NET Framework version for better CaC API support
- **Get-Task**: Added support for retrieving tasks from `DeploymentResource` objects.
- **Get-CommonTenantVariable**: Added support for filtering by Environment, made VariableSet optional to retrieve all variable sets, and improved output to include scope and variable set name.
- **Set-CommonTenantVariable**: Enhanced to handle scoped common tenant variables with environment scoping support. Intelligently handles scope conflicts (disjoint, overlapping, equal, contained), supports setting multiple variables at once via hashtable, includes comprehensive verbose logging, and detailed comment-based help with multiple examples.

## [2.0.0] - Configuration as Code Runbook Support 🎉

### Added
- **Get-Runbook**: Configuration as Code (CaC) runbook support with optional `BranchName` parameter for Git-based projects. Auto-detects default branch when not specified.
- **Invoke-RunbookRun**: Execute CaC runbooks directly from Git branches with new `BranchName` parameter. Enhanced tenant and project type validation.

### Changed - BREAKING
- **Get-Runbook**:
- Renamed parameter `RunbookID` → `Id`
- Changed `-Name` from wildcard to exact match only
- Simplified parameter sets (removed `byName`)
- `Project` parameter now accepts single object instead of array
- **Invoke-RunbookRun**:
- Separate parameter sets for CaC (`Runbook`) vs traditional (`RunbookSnapshot`) projects
- No longer auto-resolves published snapshots
- Changed to non-terminating errors for better pipeline handling

### Migration Guide

#### Updating Get-Runbook Calls
```powershell
# Before
Get-Runbook -RunbookID "Runbooks-123" # Parameter renamed
Get-Runbook -Name "Deploy*" # Wildcards no longer supported
Get-Runbook # Didn't warn about missing CaC runbooks

# After
Get-Runbook -Id "Runbooks-123" # Use -Id instead
Get-Runbook -Name "Deploy Application" # Exact match only
Get-Runbook -Project "MyProject" # Add -Project to get CaC runbooks
```

#### Updating Invoke-RunbookRun Calls
```powershell
# Before (auto-resolved to published snapshot)
Invoke-RunbookRun -Runbook "MyRunbook" -Environment Production
# Get-Runbook
Get-Runbook -RunbookID "Runbooks-123" # Before
Get-Runbook -Id "Runbooks-123" # After

# After (explicit parameter sets)
# For CaC runbooks:
Invoke-RunbookRun -Runbook "MyRunbook" -Environment Production -BranchName "main"
# Invoke-RunbookRun (CaC projects)
Invoke-RunbookRun -Runbook $runbook -Environment Production -BranchName "main"

# For traditional runbooks:
$snapshot = Get-RunbookSnapshot -Runbook "MyRunbook" -Latest
# Invoke-RunbookRun (Traditional projects)
$snapshot = Get-RunbookSnapshot -Runbook $runbook -Latest
Invoke-RunbookRun -RunbookSnapshot $snapshot -Environment Production
```

### Technical Details

#### API Methods Used
- `$repo._repository.Projects.GetAllRunbooks($Project, $BranchCanonicalName)` - Retrieve CaC runbooks from specific branch
- `$repo._repository.Runbooks.Run($project, $branch, $slug, $parameters)` - Execute CaC runbook from Git
- `$repo._repository.RunbookRuns.Create($runbookRun)` - Execute traditional snapshot-based runbook

#### Branch Resolution Logic
1. Retrieves all branches via `Get-GitBranch`
2. If `BranchName` specified, matches by name or canonical name
3. If no branch specified, automatically uses default branch
4. Falls back to non-versioned mode for traditional projects
### Improved
- Enhanced error handling with specific exception types and non-terminating errors
- Comprehensive verbose logging for debugging
- Updated comment-based help with extensive examples

---
### Fixed
- Corrected inverted CaC validation logic in Invoke-RunbookRun
- Fixed default branch selection for multiple branches

**Semver Tag**: `+semver:major`
**Related Issues**: DNA-337
**Breaking Changes**: Yes - see migration guide above
**Related Issues**: DNA-337

6 changes: 6 additions & 0 deletions GettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,12 @@ These are library variables with tenant-specific values:
# Get common tenant variables
Get-CommonTenantVariable -VariableSet "Customer Variables" -Tenant "MyTenant"

# Get common tenant variables for a specific environment
Get-CommonTenantVariable -Tenant "MyTenant" -Environment "Production"

# Get all common tenant variables (across all variable sets)
Get-CommonTenantVariable -Tenant "MyTenant"

# Set a single variable
Set-CommonTenantVariable -Tenant "MyTenant" -VariableSet "Customer Variables" -Name "DatabaseServer" -Value "sql.example.com"

Expand Down
25 changes: 25 additions & 0 deletions OctopusDeploy/Private/Get-VariableTemplate.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
function Get-VariableTemplate {
<#
.SYNOPSIS
Gets a variable template from a variable set by name.
.DESCRIPTION
This function retrieves a specific variable template from a LibraryVariableSetResource based on the provided name.
.PARAMETER VariableSet
The LibraryVariableSetResource to search in.
.PARAMETER Name
The name of the template to find.
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[Octopus.Client.Model.LibraryVariableSetResource]$VariableSet,

[Parameter(Mandatory = $true)]
[string]$Name
)

process {
$template = $VariableSet.Templates | Where-Object Name -EQ $Name
return $template
}
}
85 changes: 85 additions & 0 deletions OctopusDeploy/Private/New-TenantCommonVariablePayload.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
function New-TenantCommonVariablePayload {
<#
.SYNOPSIS
Creates a TenantCommonVariablePayload object.
.DESCRIPTION
Helper function to create the payload for updating tenant common variables.
.PARAMETER LibraryVariableSetId
The ID of the library variable set.
.PARAMETER TemplateId
The ID of the variable template.
.PARAMETER Value
The value of the variable. Can be a string or PropertyValueResource.
.PARAMETER Scope
The scope of the variable. Can be CommonVariableScope, ReferenceCollection, or array of environment IDs.
.PARAMETER VariableId
The ID of the existing variable, if updating.
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]$LibraryVariableSetId,

[Parameter(Mandatory = $true)]
[string]$TemplateId,

[Parameter(Mandatory = $true)]
$Value,

[Parameter(Mandatory = $false)]
[bool]$IsSensitive = $false,

[Parameter(Mandatory = $true)]
[AllowEmptyString()]
[AllowNull()]
$Scope,

[Parameter(Mandatory = $false)]
[string]$VariableId
)

process {
# Handle Value
if ($Value -isnot [Octopus.Client.Model.PropertyValueResource]) {
if ([string]::IsNullOrEmpty($Value)) {
return $null
}
$Value = [Octopus.Client.Model.PropertyValueResource]::new($Value, $IsSensitive)
}

# Handle Scope
if ($Scope -isnot [Octopus.Client.Model.TenantVariables.CommonVariableScope]) {
if ($Scope -is [Octopus.Client.Model.ReferenceCollection]) {
$Scope = [Octopus.Client.Model.TenantVariables.CommonVariableScope]::new($Scope)
}
elseif ($Scope -is [System.Collections.IEnumerable] -and $Scope -isnot [string]) {
$collection = [Octopus.Client.Model.ReferenceCollection]::new()
foreach ($id in $Scope) {
$collection.Add($id) | Out-Null
}
$Scope = [Octopus.Client.Model.TenantVariables.CommonVariableScope]::new($collection)
}
else {
# Assume it's a single ID or empty
$collection = [Octopus.Client.Model.ReferenceCollection]::new(@($Scope))
$Scope = [Octopus.Client.Model.TenantVariables.CommonVariableScope]::new($collection)
}
}

$payload = [Octopus.Client.Model.TenantVariables.TenantCommonVariablePayload]::new(
$LibraryVariableSetId,
$TemplateId,
$Value,
$Scope
)

if (-not [string]::IsNullOrEmpty($VariableId)) {
$payload.Id = $VariableId
}
else {
$payload.Id = [string]::Empty
}

return $payload
}
}
8 changes: 7 additions & 1 deletion OctopusDeploy/Public/Add-RoleToMachine.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,13 @@

if ($pscmdlet.ShouldProcess("$($Machine.name)", "$wiMessages$($role -join ', ')")) {
foreach ($_role in $Role) {
$Machine.Roles.Add($_role)
$added = $Machine.Roles.Add($_role)
if ($added){
Write-Verbose "Added role $_role to machine $($Machine.Name)"
}
else {
Write-Verbose "Role $_role already exists on machine $($Machine.Name)"
}
try {
# Modify will return an update MachineResource. Only the last one will be returned to the user
$lastMachineUpdate = $repo._repository.Machines.Modify($Machine)
Expand Down
Loading