Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
91 changes: 38 additions & 53 deletions Docs/Publish-GitHubReleaseAsset.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ Publishes a release asset to GitHub.

```
Publish-GitHubReleaseAsset [-ProjectPath] <String> [-GitHubUsername] <String> [-GitHubRepositoryName] <String>
[-GitHubAccessToken] <String> [-IsPreRelease] [[-Version] <String>] [[-TagName] <String>] [[-TagTemplate] <String>]
[[-ReleaseName] <String>] [-IncludeProjectNameInTag] [[-ZipPath] <String>] [-ProgressAction <ActionPreference>] [-WhatIf] [-Confirm]
[<CommonParameters>]
[-GitHubAccessToken] <String> [-IsPreRelease] [[-Version] <String>] [[-TagName] <String>]
[[-TagTemplate] <String>] [[-ReleaseName] <String>] [-IncludeProjectNameInTag] [[-ZipPath] <String>]
[-ProgressAction <ActionPreference>] [-WhatIf] [-Confirm] [<CommonParameters>]
```

## DESCRIPTION
Expand Down Expand Up @@ -44,18 +44,6 @@ Publish-GitHubReleaseAsset -ProjectPath 'C:\Git\OfficeIMO\OfficeIMO.Markdown' -G
Publish-GitHubReleaseAsset -ProjectPath 'C:\Git\OfficeIMO\OfficeIMO.Excel' -GitHubUsername 'EvotecIT' -GitHubRepositoryName 'OfficeIMO' -GitHubAccessToken $Token -Version '1.2.3' -TagName 'OfficeIMO.Excel-v1.2.3'
```

### EXAMPLE 4
```
# Use a custom tag template with placeholders {Project} and {Version}
Publish-GitHubReleaseAsset -ProjectPath 'C:\Git\OfficeIMO\OfficeIMO.Word' -GitHubUsername 'EvotecIT' -GitHubRepositoryName 'OfficeIMO' -GitHubAccessToken $Token -TagTemplate 'officeimo/{Project}/v{Version}'
```

### EXAMPLE 5
```
# Provide a specific path to the asset zip instead of the default
Publish-GitHubReleaseAsset -ProjectPath 'C:\Git\OfficeIMO\OfficeIMO.Excel' -GitHubUsername 'EvotecIT' -GitHubRepositoryName 'OfficeIMO' -GitHubAccessToken $Token -ZipPath 'C:\Git\OfficeIMO\OfficeIMO.Excel\bin\Release\OfficeIMO.Excel.1.2.3.zip'
```

## PARAMETERS

### -ProjectPath
Expand Down Expand Up @@ -134,7 +122,7 @@ Accept wildcard characters: False
```

### -Version
Override the version discovered from the project file (VersionPrefix). Used to locate the zip and for default tag generation.
{{ Fill Version Description }}

```yaml
Type: String
Expand All @@ -149,7 +137,7 @@ Accept wildcard characters: False
```

### -TagName
Explicit tag name to use. If omitted, defaults to `v<Version>`, `<ProjectName>-v<Version>` when `-IncludeProjectNameInTag` is specified, or the value produced by `-TagTemplate`.
{{ Fill TagName Description }}

```yaml
Type: String
Expand All @@ -163,8 +151,8 @@ Accept pipeline input: False
Accept wildcard characters: False
```

### -ReleaseName
Optional release display name. Defaults to the tag name when omitted.
### -TagTemplate
{{ Fill TagTemplate Description }}

```yaml
Type: String
Expand All @@ -178,8 +166,23 @@ Accept pipeline input: False
Accept wildcard characters: False
```

### -ReleaseName
{{ Fill ReleaseName Description }}

```yaml
Type: String
Parameter Sets: (All)
Aliases:

Required: False
Position: 8
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

### -IncludeProjectNameInTag
When set and `-TagName` is not provided, the tag is generated as `<ProjectName>-v<Version>` to prevent tag collisions across packages in the same repository.
{{ Fill IncludeProjectNameInTag Description }}

```yaml
Type: SwitchParameter
Expand All @@ -193,6 +196,21 @@ Accept pipeline input: False
Accept wildcard characters: False
```

### -ZipPath
{{ Fill ZipPath Description }}

```yaml
Type: String
Parameter Sets: (All)
Aliases:

Required: False
Position: 9
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

### -WhatIf
Shows what would happen if the cmdlet runs.
The cmdlet is not run.
Expand Down Expand Up @@ -249,36 +267,3 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable
## NOTES

## RELATED LINKS
### -TagTemplate
Custom tag pattern with placeholders. Supported tokens:
- `{Project}` replaced with the project name (csproj BaseName)
- `{Version}` replaced with the version (from VersionPrefix or `-Version`)

If both `-TagName` and `-TagTemplate` are provided, `-TagName` takes precedence.

```yaml
Type: String
Parameter Sets: (All)
Aliases:

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

### -ZipPath
Use a specific zip file path for the release asset, instead of the default `bin/Release/<Project>.<Version>.zip`. The path must exist.

```yaml
Type: String
Parameter Sets: (All)
Aliases:

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
1 change: 1 addition & 0 deletions Docs/Set-ProjectVersion.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Set-ProjectVersion [[-VersionType] <String>] [[-NewVersion] <String>] [[-ModuleN
## DESCRIPTION
Updates version numbers in C# projects (.csproj), PowerShell modules (.psd1),
and PowerShell build scripts that contain 'Invoke-ModuleBuild'.
For .csproj files, it updates Version, VersionPrefix, PackageVersion, AssemblyVersion, FileVersion, and InformationalVersion when present.
Can increment
version components or set a specific version.

Expand Down
2 changes: 1 addition & 1 deletion PSPublishModule.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
Copyright = '(c) 2011 - 2025 Przemyslaw Klys @ Evotec. All rights reserved.'
Description = 'Simple project allowing preparing, managing, building and publishing modules to PowerShellGallery'
DotNetFrameworkVersion = '4.5.2'
FunctionsToExport = @('Convert-CommandsToList', 'Convert-ProjectEncoding', 'Convert-ProjectLineEnding', 'Export-CertificateForNuGet', 'Get-MissingFunctions', 'Get-ModuleInformation', 'Get-ModuleTestFailures', 'Get-PowerShellAssemblyMetadata', 'Get-PowerShellCompatibility', 'Get-ProjectConsistency', 'Get-ProjectEncoding', 'Get-ProjectLineEnding', 'Get-ProjectVersion', 'Initialize-PortableModule', 'Initialize-PortableScript', 'Initialize-ProjectManager', 'Invoke-DotNetReleaseBuild', 'Invoke-ModuleBuild', 'Invoke-ModuleTestSuite', 'New-ConfigurationArtefact', 'New-ConfigurationBuild', 'New-ConfigurationCommand', 'New-ConfigurationCompatibility', 'New-ConfigurationDocumentation', 'New-ConfigurationExecute', 'New-ConfigurationFileConsistency', 'New-ConfigurationFormat', 'New-ConfigurationImportModule', 'New-ConfigurationInformation', 'New-ConfigurationManifest', 'New-ConfigurationModule', 'New-ConfigurationModuleSkip', 'New-ConfigurationPlaceHolder', 'New-ConfigurationPublish', 'New-ConfigurationTest', 'Publish-GitHubReleaseAsset', 'Publish-NugetPackage', 'Register-Certificate', 'Remove-Comments', 'Remove-ProjectFiles', 'Send-GitHubRelease', 'Set-ProjectVersion')
FunctionsToExport = @('Convert-ProjectEncoding', 'Convert-ProjectLineEnding', 'Export-CertificateForNuGet', 'Get-MissingFunctions', 'Get-ModuleInformation', 'Get-ModuleTestFailures', 'Get-PowerShellAssemblyMetadata', 'Get-PowerShellCompatibility', 'Get-ProjectConsistency', 'Get-ProjectEncoding', 'Get-ProjectLineEnding', 'Get-ProjectVersion', 'Initialize-PortableModule', 'Initialize-PortableScript', 'Initialize-ProjectManager', 'Invoke-DotNetReleaseBuild', 'Invoke-ModuleBuild', 'Invoke-ModuleTestSuite', 'New-ConfigurationArtefact', 'New-ConfigurationBuild', 'New-ConfigurationCommand', 'New-ConfigurationCompatibility', 'New-ConfigurationDocumentation', 'New-ConfigurationExecute', 'New-ConfigurationFileConsistency', 'New-ConfigurationFormat', 'New-ConfigurationImportModule', 'New-ConfigurationInformation', 'New-ConfigurationManifest', 'New-ConfigurationModule', 'New-ConfigurationModuleSkip', 'New-ConfigurationPlaceHolder', 'New-ConfigurationPublish', 'New-ConfigurationTest', 'Publish-GitHubReleaseAsset', 'Publish-NugetPackage', 'Register-Certificate', 'Remove-Comments', 'Remove-ProjectFiles', 'Send-GitHubRelease', 'Set-ProjectVersion')
GUID = 'eb76426a-1992-40a5-82cd-6480f883ef4d'
ModuleVersion = '2.0.25'
PowerShellVersion = '5.1'
Expand Down
23 changes: 22 additions & 1 deletion Private/Get-CurrentVersionFromCsProj.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,33 @@ function Get-CurrentVersionFromCsProj {

try {
$content = Get-Content -Path $ProjectFile -Raw
# Prefer VersionPrefix if present
if ($content -match '<VersionPrefix>([\d\.]+)<\/VersionPrefix>') {
return $matches[1]
}
# Then prefer Version (SDK-style projects)
if ($content -match '<Version>([\d\.]+)<\/Version>') {
return $matches[1]
}
# PackageVersion is also common when packing
if ($content -match '<PackageVersion>([\d\.]+)<\/PackageVersion>') {
return $matches[1]
}
# Fall back to AssemblyVersion
if ($content -match '<AssemblyVersion>([\d\.]+)<\/AssemblyVersion>') {
return $matches[1]
}
# Fall back to FileVersion
if ($content -match '<FileVersion>([\d\.]+)<\/FileVersion>') {
return $matches[1]
}
# Finally, consider InformationalVersion if it's numeric-only
if ($content -match '<InformationalVersion>([\d\.]+)<\/InformationalVersion>') {
return $matches[1]
}
return $null
} catch {
Write-Warning "Error reading project file $ProjectFile`: $_"
return $null
}
}
}
13 changes: 10 additions & 3 deletions Private/Update-VersionInCsProj.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Updates the version in a .csproj file.

.DESCRIPTION
Modifies the VersionPrefix element in a .csproj file with the new version.
Updates Version, VersionPrefix, PackageVersion, AssemblyVersion, FileVersion, and InformationalVersion elements in a .csproj file with the new version (when present).

.PARAMETER ProjectFile
Path to the .csproj file.
Expand Down Expand Up @@ -32,7 +32,14 @@

try {
$content = Get-Content -Path $ProjectFile -Raw
$newContent = $content -replace '<VersionPrefix>[\d\.]+<\/VersionPrefix>', "<VersionPrefix>$Version</VersionPrefix>"
$newContent = $content
# Update Version-related tags if present
$newContent = $newContent -replace '<Version>[\d\.]+<\/Version>', "<Version>$Version</Version>"
$newContent = $newContent -replace '<VersionPrefix>[\d\.]+<\/VersionPrefix>', "<VersionPrefix>$Version</VersionPrefix>"
$newContent = $newContent -replace '<PackageVersion>[\d\.]+<\/PackageVersion>', "<PackageVersion>$Version</PackageVersion>"
$newContent = $newContent -replace '<AssemblyVersion>[\d\.]+<\/AssemblyVersion>', "<AssemblyVersion>$Version</AssemblyVersion>"
$newContent = $newContent -replace '<FileVersion>[\d\.]+<\/FileVersion>', "<FileVersion>$Version</FileVersion>"
$newContent = $newContent -replace '<InformationalVersion>[\d\.]+<\/InformationalVersion>', "<InformationalVersion>$Version</InformationalVersion>"

if ($content -eq $newContent) {
Write-Verbose "No version change needed for $ProjectFile"
Expand All @@ -48,4 +55,4 @@
Write-Error "Error updating project file $ProjectFile`: $_"
return $false
}
}
}
80 changes: 61 additions & 19 deletions Public/Get-PowerShellAssemblyMetaData.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -72,35 +72,77 @@
try {
$context = [System.Reflection.MetadataLoadContext]::new($resolver)

# Load the System.Management.Automation assembly into the context
$smaAssemblyInContext = $context.LoadFromAssemblyPath($smaAssemblyPath)
$cmdletType = $smaAssemblyInContext.GetType('System.Management.Automation.Cmdlet')
$cmdletAttribute = $smaAssemblyInContext.GetType('System.Management.Automation.CmdletAttribute')
$aliasAttribute = $smaAssemblyInContext.GetType('System.Management.Automation.AliasAttribute')

# Load target assembly first
$assembly = $context.LoadFromAssemblyPath($Path)

Write-Verbose -Message "Loaded assembly $($assembly.FullName), $($assembly.Location) searching for cmdlets and aliases"

# Resolve SMA inside the same MetadataLoadContext by name to avoid type identity mismatches
$smaRef = ($assembly.GetReferencedAssemblies() | Where-Object { $_.Name -eq 'System.Management.Automation' } | Select-Object -First 1)
if ($null -ne $smaRef) {
$smaAssemblyInContext = $context.LoadFromAssemblyName($smaRef)
} else {
# Fallback to host SMA path if not referenced explicitly (unusual)
$smaAssemblyInContext = $context.LoadFromAssemblyPath($smaAssemblyPath)
}

$cmdletTypeName = 'System.Management.Automation.Cmdlet'
$pscmdletTypeName = 'System.Management.Automation.PSCmdlet'
$cmdletAttributeName = 'System.Management.Automation.CmdletAttribute'
$aliasAttributeName = 'System.Management.Automation.AliasAttribute'

$cmdletsToExport = [System.Collections.Generic.List[string]]::new()
$aliasesToExport = [System.Collections.Generic.List[string]]::new()
$Types = $assembly.GetTypes()

$Types | Where-Object { $_.IsSubclassOf($cmdletType) } | ForEach-Object {
$cmdletInfo = $_.CustomAttributes | Where-Object { $_.AttributeType -eq $cmdletAttribute }
if (-not $cmdletInfo) { return }
try {
$Types = $assembly.GetTypes()
} catch {
Write-Verbose -Message "Falling back to GetExportedTypes() due to: $($_.Exception.Message)"
$Types = $assembly.GetExportedTypes()
}

$name = "$($cmdletInfo.ConstructorArguments[0].Value)-$($cmdletInfo.ConstructorArguments[1].Value)"
$cmdletsToExport.Add($name)
foreach ($type in $Types) {
# Robust cmdlet detection: prefer attribute FullName match to avoid identity issues
$attr = $type.CustomAttributes | Where-Object { $_.AttributeType.FullName -eq $cmdletAttributeName } | Select-Object -First 1

if (-not $attr) {
# Fallback: walk base types by FullName to detect Cmdlet/PSCmdlet inheritance
$bt = $type.BaseType
$isCmdlet = $false
while ($bt) {
if ($bt.FullName -eq $cmdletTypeName -or $bt.FullName -eq $pscmdletTypeName) { $isCmdlet = $true; break }
$bt = $bt.BaseType
}
if (-not $isCmdlet) { continue }
}

$aliases = $_.CustomAttributes | Where-Object { $_.AttributeType -eq $aliasAttribute }
if (-not $aliases -or -not $aliases.ConstructorArguments.Value) { return }
$aliasesToExport.AddRange([string[]]@($aliases.ConstructorArguments.Value.Value))
# Extract Verb-Noun from attribute where available
if ($attr) {
$verb = $null; $noun = $null
if ($attr.ConstructorArguments.Count -ge 2) {
$verb = [string]$attr.ConstructorArguments[0].Value
$noun = [string]$attr.ConstructorArguments[1].Value
}
if (-not $verb -or -not $noun) {
$verb = [string]($attr.NamedArguments | Where-Object { $_.MemberName -eq 'VerbName' } | Select-Object -ExpandProperty TypedValue -ErrorAction Ignore).Value
$noun = [string]($attr.NamedArguments | Where-Object { $_.MemberName -eq 'NounName' } | Select-Object -ExpandProperty TypedValue -ErrorAction Ignore).Value
}
if ($verb -and $noun) {
$cmdletsToExport.Add("$verb-$noun")
}
}

# Aliases (if any)
$aliasAttrs = $type.CustomAttributes | Where-Object { $_.AttributeType.FullName -eq $aliasAttributeName }
foreach ($aa in $aliasAttrs) {
if ($aa.ConstructorArguments -and $aa.ConstructorArguments.Value) {
$aliasesToExport.AddRange([string[]]@($aa.ConstructorArguments.Value.Value))
}
}
}

[PSCustomObject]@{
CmdletsToExport = $cmdletsToExport
AliasesToExport = $aliasesToExport
CmdletsToExport = ($cmdletsToExport | Select-Object -Unique)
AliasesToExport = ($aliasesToExport | Select-Object -Unique)
}
} catch {
if ($_.Exception.Message -like '*has already been loaded into this MetadataLoadContext*') {
Expand All @@ -118,4 +160,4 @@
$context.Dispose()
}
}
}
}
Loading