PoshCode / ModuleBuilder

A PowerShell Module to help scripters write, version, sign, package, and publish.
MIT License
448 stars 53 forks source link

Prerelease Key being stripped #93

Closed mattmcnabb closed 4 years ago

mattmcnabb commented 4 years ago

I have a module with Prerelease = "alpha" set in the manifest. This value is being stripped out on build. I attempted to set this value using the -Prerelease parameter, but I'm still left with an empty value after build.

Jaykul commented 4 years ago

Basically: if you pass -Semver (or manually pass -PreRelease) then it's going to try to update the prerelease with whatever is in your semantic version, and otherwise, it will strip it out. The goal is to have module builder set the whole version (including the pre-release part) in the manifest.

It should work if you either:

If that's not what you're seeing, can you run it in -Verbose mode and paste your output?

mattmcnabb commented 4 years ago

@Jaykul Verbose doesn't give me much:

VERBOSE: Building ModuleName
VERBOSE: Resolve OutputDirectory path: .\Output\
VERBOSE: Output to: C:\Repos\ModuleName\Source\Output
VERBOSE: Cleaning C:\Repos\ModuleName\Source\Output
VERBOSE: Loading module from path 'C:\Users\Matt\Documents\PowerShell\Modules\ModuleName\2.0.0\ModuleName.psd1'.
VERBOSE: Populating RepositorySourceLocation property for module ModuleName.
VERBOSE: Loading module from path 'C:\Users\Matt\Documents\PowerShell\Modules\ModuleName\2.0.0\ModuleName.psm1'.
VERBOSE: Adding .\Classes\Message.ps1
VERBOSE: Adding .\Classes\Service.ps1
VERBOSE: Adding .\Classes\Status.ps1
VERBOSE: Adding .\Private\Test-ModuleConnection.ps1
VERBOSE: Adding .\Public\Connect-ModuleName.ps1
VERBOSE: Adding .\Public\Get-Message.ps1
VERBOSE: Adding .\Public\Get-Service.ps1
VERBOSE: Adding .\Public\Get-Status.ps1

The real problem is not so much getting Build-Module to set the prerelease string, but at the least I'd like it to ignore it if I don't use the -Prerelease parameter. If my source manifest looks like this (truncated for clarity):

@{
    NestedModules     = "ModuleName.psm1"
    ModuleVersion     = "2.0.0"

    PrivateData       = @{
        PSData = @{
            Prerelease   = "-alpha1"
        }
    }
}

And I run:

Build-Module .\Source\ModuleName.psd1

Then the "compiled" manifest looks like:

@{
    NestedModules     = "ModuleName.psm1"
    ModuleVersion     = "2.0.0"

    PrivateData       = @{
        PSData = @{
            Prerelease   = ''
        }
    }
}

I think the only path currently to get a valid manifest with a prerelease value is to set an empty prerelease value in the manifest and include the expected value in the Build-Module command. I think that I should be able to manage my versioning in the manifest file or in the build script, but I shouldn't have to do both. Can Build-Module just ignore the prerelease key if it already has a value?

Jaykul commented 4 years ago

Gotcha. So let me explain how it got the way it is, and then think about how to solve it:

How we got here

The way other tools handle stuff like this is by calling New-ModuleManifest and regenerate the manifest in order to update values in it. I don't want that, because I trim all the useless cruft and totally redundant comments from my manifests, and I don't want anything to put them back. I also put things in the manifest in a specific order so things that need to be changed are close together. Anyway. The point is I have hand-crafted bespoke manifests 😉 and I don't want anyone to break them.

The Metadata commands in the Configuration module include Update-Metadata which supports changing --but not inserting-- values, while being careful to not alter the manifest in an unexpected way.

In Build-Module, we're using that command to update:

Obviously, I always let Build-Module set all of those things in my modules ... 🤔

What can we do?

I don't want to just skip updating it because it already has a value. My original scenario was to always leave the source as something like PreRelease = "FromSource" so that you wouldn't get a "release build" except by properly passing a specific version (which would remove the Prerelease value from the code).

I could just change the Prerelease logic to never set the value to an empty string. I.e. if we calculated it as nothing, we'd just leave it alone. This is easy, but it would also break that original scenario ...

I think I could set the Prerelease only when $Semver is passed or when $PreRelease is explicitly set:

if ($PSCmdlet.ParameterSetName -eq "SemanticVersion" -or $PSBoundParameter.ContainsKey("Prerelease")) {

I believe that would allow both scenarios in a way that wouldn't break things too much.

  1. If you don't want me to change it, use -Version instead of -SemVer and if you don't also pass -Prerelease explicitly, I won't set it.
  2. If you do want me to change it, use -SemVer even if you don't have a prerelease

This is still technically a breaking change, but only for the case where someone is currently passing Version and conditionally passing Prerelease.

Do you think that would be good enough?

mattmcnabb commented 4 years ago

@Jaykul I think I understand now, and yeah, that sounds pretty complex! I'm not sure I'd like to see a breaking change for this, but I'll leave that up to you. But yes, I did expect to be able to conditionally pass a value for prerelease.

For me, I think guidance would be the best solution - how do you manage this in a build and release? I've typically just managed versioning directly in my source manifest, but I'm open to implementing that in the pipeline instead. I think I just need a clear picture of how to do that. In other words - serve me 👐

Seriously though, I think the best bet for me might be to leave the manifest alone and always manage versioning in either the build.psd1 directly, or maybe insert versioning values from build variables? Is this something like what you've done?

EDIT: I think I just re-read and fully understood - I'll go with option 1 for now and use -Version instead of -SemVer.

Jaykul commented 4 years ago

Ok, I'll make that change then.

For what it's worth: both in open source and at work I have slowly adopted a mainline git workflow based on GitHub flow, and I currently do all my versioning based on gitversion -- except when I'm really lazy and don't have anything automatic at all (*cough*).

Basically:

  1. We tag master with the last version we released
  2. Gitversion increments the version in a branch based on configuration (e.g. "fix" branches increment the 3rd digit "patch", everything else is a "feature" which increments the minor version)
    • GitVersion also automatically marks builds from branches as -ci releases (and numbers them) or based on the branch name, like -ci.4 or -featureILove0004
    • GitVersion also automatically marks builds from PRs as -pr releases (and numbers them based on the PR number)
  3. We do QA from PR builds
  4. When we merge, we use a keyword in the commit message like "+semver:breaking" or "+semver:feature" or "+semver:fix" to control the actual version number of the released commit, if necessary.
  5. We tag master after the build
mattmcnabb commented 4 years ago

@Jaykul thanks, I'll check those out!