pulumi/pkg/util/buildutil/semver.go
Matt Ellis bb6b492d55 Do not include git information in PyPI version.
Previously, we would include information about what git commit a build
came from in the "local" portion of the PEP-440 version. This was a
problem because PyPI does not allow packages to be upload to the
registry if they contain local parts.

So, for now, we'll just never put in the git commit information in the
generate version. We'll continue to add a dirty tag in the local
part. This will be prevent us from publishing dirty builds to PyPI,
but that's in line with what we want.
2018-06-11 13:01:49 -06:00

97 lines
3.6 KiB
Go

// Copyright 2016-2018, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package buildutil
import (
"bytes"
"fmt"
"io"
"regexp"
"github.com/pkg/errors"
"github.com/pulumi/pulumi/pkg/util/contract"
)
var (
releaseVersionRegex = regexp.MustCompile(
`^v(?P<version>\d+\.\d+\.\d+)(-(?P<time>\d+)-(?P<gitInfo>g[a-z0-9]+))?(?P<dirty>-dirty)?$`)
rcVersionRegex = regexp.MustCompile(
`^v(?P<version>\d+\.\d+\.\d+)-rc(?P<rcN>\d+)(-(?P<time>\d+)-(?P<gitInfo>g[a-z0-9]+))?(?P<dirty>-dirty)?$`)
devVersionRegex = regexp.MustCompile(
`^v(?P<version>\d+\.\d+\.\d+)-dev-(?P<time>\d+)-(?P<gitInfo>g[a-z0-9]+)(?P<dirty>-dirty)?$`)
)
// PyPiVersionFromNpmVersion returns a PEP-440 compliant version for a given semver version. This method does not
// support all possible semver strings, but instead just supports versions that we generate for our node packages.
//
// NOTE: We do not include git information in the generated version (even within the local part, which PEP440 would
// allow) because we publish dev packages to PyPI, which does not allow local parts. Instead, we only add a local part
// when the build is dirty (which has the nice side effect of preventing us from publishing a build from dirty bits).
func PyPiVersionFromNpmVersion(s string) (string, error) {
var b bytes.Buffer
if releaseVersionRegex.MatchString(s) {
capMap := captureToMap(releaseVersionRegex, s)
mustFprintf(&b, "%s", capMap["version"])
writePostBuildAndDirtyInfoToReleaseVersion(&b, capMap)
return b.String(), nil
} else if rcVersionRegex.MatchString(s) {
capMap := captureToMap(rcVersionRegex, s)
mustFprintf(&b, "%src%s", capMap["version"], capMap["rcN"])
writePostBuildAndDirtyInfoToReleaseVersion(&b, capMap)
return b.String(), nil
} else if devVersionRegex.MatchString(s) {
capMap := captureToMap(devVersionRegex, s)
mustFprintf(&b, "%s.dev%s", capMap["version"], capMap["time"])
if capMap["dirty"] != "" {
mustFprintf(&b, "+dirty")
}
return b.String(), nil
}
return "", errors.Errorf("can not parse version string '%s'", s)
}
func captureToMap(r *regexp.Regexp, s string) map[string]string {
matches := r.FindStringSubmatch(s)
capMap := make(map[string]string)
for i, name := range r.SubexpNames() {
if name != "" {
capMap[name] = matches[i]
}
}
return capMap
}
// While the version string for dev builds always contain timestamp and commit information, release and release
// release candidate builds do not. In the case where we do have this information, it is for a build newer than
// the actual release build, and we'll use the PEP-440 .post notation to show this. We also handle adding the dirty
// tag in the local version if we need it.
func writePostBuildAndDirtyInfoToReleaseVersion(w io.Writer, capMap map[string]string) {
if capMap["time"] != "" {
mustFprintf(w, ".post%s", capMap["time"])
if capMap["dirty"] != "" {
mustFprintf(w, "+dirty")
}
} else if capMap["dirty"] != "" {
mustFprintf(w, "+dirty")
}
}
func mustFprintf(w io.Writer, format string, a ...interface{}) {
_, err := fmt.Fprintf(w, format, a...)
contract.AssertNoError(err)
}