diff --git a/Docs/Publish-GitHubReleaseAsset.md b/Docs/Publish-GitHubReleaseAsset.md index 65a1860..f213fc5 100644 --- a/Docs/Publish-GitHubReleaseAsset.md +++ b/Docs/Publish-GitHubReleaseAsset.md @@ -14,9 +14,9 @@ Publishes a release asset to GitHub. ``` Publish-GitHubReleaseAsset [-ProjectPath] [-GitHubUsername] [-GitHubRepositoryName] - [-GitHubAccessToken] [-IsPreRelease] [[-Version] ] [[-TagName] ] [[-TagTemplate] ] - [[-ReleaseName] ] [-IncludeProjectNameInTag] [[-ZipPath] ] [-ProgressAction ] [-WhatIf] [-Confirm] - [] + [-GitHubAccessToken] [-IsPreRelease] [[-Version] ] [[-TagName] ] + [[-TagTemplate] ] [[-ReleaseName] ] [-IncludeProjectNameInTag] [[-ZipPath] ] + [-ProgressAction ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -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 @@ -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 @@ -149,7 +137,7 @@ Accept wildcard characters: False ``` ### -TagName -Explicit tag name to use. If omitted, defaults to `v`, `-v` when `-IncludeProjectNameInTag` is specified, or the value produced by `-TagTemplate`. +{{ Fill TagName Description }} ```yaml Type: String @@ -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 @@ -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 `-v` to prevent tag collisions across packages in the same repository. +{{ Fill IncludeProjectNameInTag Description }} ```yaml Type: SwitchParameter @@ -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. @@ -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/..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 -``` diff --git a/Docs/Set-ProjectVersion.md b/Docs/Set-ProjectVersion.md index 71309c7..2b34fec 100644 --- a/Docs/Set-ProjectVersion.md +++ b/Docs/Set-ProjectVersion.md @@ -21,6 +21,7 @@ Set-ProjectVersion [[-VersionType] ] [[-NewVersion] ] [[-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. diff --git a/PSPublishModule.psd1 b/PSPublishModule.psd1 index f8b8988..0a806eb 100644 --- a/PSPublishModule.psd1 +++ b/PSPublishModule.psd1 @@ -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' diff --git a/Private/Get-CurrentVersionFromCsProj.ps1 b/Private/Get-CurrentVersionFromCsProj.ps1 index 6a0df4b..6cddd4b 100644 --- a/Private/Get-CurrentVersionFromCsProj.ps1 +++ b/Private/Get-CurrentVersionFromCsProj.ps1 @@ -12,12 +12,33 @@ function Get-CurrentVersionFromCsProj { try { $content = Get-Content -Path $ProjectFile -Raw + # Prefer VersionPrefix if present if ($content -match '([\d\.]+)<\/VersionPrefix>') { return $matches[1] } + # Then prefer Version (SDK-style projects) + if ($content -match '([\d\.]+)<\/Version>') { + return $matches[1] + } + # PackageVersion is also common when packing + if ($content -match '([\d\.]+)<\/PackageVersion>') { + return $matches[1] + } + # Fall back to AssemblyVersion + if ($content -match '([\d\.]+)<\/AssemblyVersion>') { + return $matches[1] + } + # Fall back to FileVersion + if ($content -match '([\d\.]+)<\/FileVersion>') { + return $matches[1] + } + # Finally, consider InformationalVersion if it's numeric-only + if ($content -match '([\d\.]+)<\/InformationalVersion>') { + return $matches[1] + } return $null } catch { Write-Warning "Error reading project file $ProjectFile`: $_" return $null } -} \ No newline at end of file +} diff --git a/Private/Update-VersionInCsProj.ps1 b/Private/Update-VersionInCsProj.ps1 index cade1ce..177fe66 100644 --- a/Private/Update-VersionInCsProj.ps1 +++ b/Private/Update-VersionInCsProj.ps1 @@ -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. @@ -32,7 +32,14 @@ try { $content = Get-Content -Path $ProjectFile -Raw - $newContent = $content -replace '[\d\.]+<\/VersionPrefix>', "$Version" + $newContent = $content + # Update Version-related tags if present + $newContent = $newContent -replace '[\d\.]+<\/Version>', "$Version" + $newContent = $newContent -replace '[\d\.]+<\/VersionPrefix>', "$Version" + $newContent = $newContent -replace '[\d\.]+<\/PackageVersion>', "$Version" + $newContent = $newContent -replace '[\d\.]+<\/AssemblyVersion>', "$Version" + $newContent = $newContent -replace '[\d\.]+<\/FileVersion>', "$Version" + $newContent = $newContent -replace '[\d\.]+<\/InformationalVersion>', "$Version" if ($content -eq $newContent) { Write-Verbose "No version change needed for $ProjectFile" @@ -48,4 +55,4 @@ Write-Error "Error updating project file $ProjectFile`: $_" return $false } -} \ No newline at end of file +} diff --git a/Public/Get-PowerShellAssemblyMetaData.ps1 b/Public/Get-PowerShellAssemblyMetaData.ps1 index 221babd..c2d6684 100644 --- a/Public/Get-PowerShellAssemblyMetaData.ps1 +++ b/Public/Get-PowerShellAssemblyMetaData.ps1 @@ -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*') { @@ -118,4 +160,4 @@ $context.Dispose() } } -} \ No newline at end of file +}