Skip to content

Instantly share code, notes, and snippets.

@perillo
Last active November 27, 2015 14:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save perillo/06fe1d3e86f9cbf79b28 to your computer and use it in GitHub Desktop.
Save perillo/06fe1d3e86f9cbf79b28 to your computer and use it in GitHub Desktop.

Proposal: Versioning Go packages

Author: Manlio Perillo manlio.perillo@gmail.com

Last updated: 26 Novemberr 2015

Discussion at https://golang.org/issue/XXXXX.

Abstract

TODO

Background

TODO

Proposal

This proposal describes how to assign a version to a package, and how to require a specific version of a package using a Go tool.

Version numbers follow the Semantic Versioning 2.0 standard (SemVer) standard.

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

Glossary

  • repository: TODO
  • revision: TODO
  • tag: TODO
  • branch: TODO
  • version: TODO
  • SemVer: for this specification purpose, a SemVer is defined by MAJOR.MINOR.PATCH.

How versions are specified inside a repository

Go packages live inside a source code repository (like git or mercurial). All packages inside a repository share the same version.

A version is assigned by using the source control system native method to create a tag for a revision.

A tagged version has a 'v' character before the actual SemVer. The 'v' prefix is REQUIRED.

When the full SemVer is used to tag a revision, that revision MUST be considered immutable, that is, a package source code at the specified revision MUST never be changed by the package author (this is also specified by SemVer). The tag, once created, SHOULD never be deleted.

A full SemVer is assigned by using the source control system native method to create an immutable tag (called tag) for a revision. As an example:

git tag -a v1.2.3

It is also possible to tag a revision using a partial SemVer. In this case it is possible to add more revisions to the source code, as long as the SemVer specification is followed (TODO: this is probably confusing).

A partial SemVer is assigned by using the source control system native method to create a tag (called branch) for a revision. The branch, once created, SHOULD never be deleted.

In a partial SemVer both PATCH and MINOR are optional. As an example:

git branch v1.2
git branch v1

A full SemVer MUST not be used to create a branch.

A partial SemVer MUST not be used to create a tag.

RATIONALE:

  • Enabling support for creating partial versions using a branch is a very useful (and required, by some people) feature, but it is also required in order to keep compatibility with many packages that follow the current advice, by the Go tools, to change the import path when a new incompatible version is released. An example of code hosting that follows this convention is gopkg.in(gopkg.in). The service maps packages to branches in the underlying source code repository, using the go get protocol.

ISSUES:

  • Should this specification recommend the use of signed tags, if available? How should signed tags verified? Will this require a Go specific CA? A centralized authority (of any sort) is against Go tool principles, but without an authority signing tags may be useless.

How versions are specified when using a Go tool

In this section go get is used as the reference tool.

When using go get, the version is specified in the remote import path by appending the character @ followed by the SemVer after the package import path. As an example:

go get golang.org/x/text@1.2.3

Note that no 'v' character is prepended to the version.

TODO: should the syntax be golang.org/x/text.v1.2.3, instead? This has the advantage to be compatible with gopkg.in.

NOTE: the proposed change to how a remote import path must be interpreted by go get does not break compatibility, since the new syntax is a valid import path as recognized by the current go get. Using the alternate syntax will make current tools able to handle some packages following this specification. As an example:

    go get gopkg.in/xmlpath.v2

NOTE: to be precise, gopkg.in is not strictly compatible with this specification, since a version is mapped to either a tag or branch, while this specification forbids the creation of a tag using a partial SemVer and the creation of a branch using a full SemVer.

When downloading a package from a specified remote import path, the following rules MUST be followed:

  • If the version is not specified, then go get will search for the latest revision in the repository. Note the use of revision instead of version. This is the current behavior.

  • If a partial SemVer is specified, then go get will first search for a branch having the specified version. If the branch does not exists, go get will search for the most recent tag matching the partial SemVer. It must report an error if no matching tag exists.

  • If a full SemVer is specified, then go get will search for a tag having the specified version. It must report an error if the tag does not exists.

  • The special latest identifier is equivalent to specify, as the version, the most recent version in the repository. Using latest will allow users to only install released packages.

Compatibility with go get

This specification is designed to be compatible with go get. However go get has a concept of version that is not compatible with the one proposed here. In the following text from go help get:

If no such version exists it retrieves the most recent version of the
package.

version should be replaced with revision.

Another issue is how to support multiple, incompatible, versions of the Go compiler when a package version is also specified in a remote import path.

The proposed solution is to append the @go<ver> string to the searched repository tag or branch. As an example:

go get golang.org/x/text@1.2.3

will first try the tag v1.2.3@go1, then the tag v1.2.3. Note that the '@' character is not allowed in SemVer, so it is safe to use.

Proposed extensions to standard Go tools

  • go version should gain the ability to report the version of an installed package, specified using an import path (so, std, all and recursive packages SHOULD be supported)

  • go api should gain the ability to check API compatibility (and reporting a diff) between a currently installed package and a remote version. The package is specified using an import path (so, std, all and recursive packages SHOULD be supported)

  • A new go tool release should be implemented. The tool will be used to automatically create a new version for the current revision of the current (in the current directory) package.

    The syntax SHOULD be:

    go tool release [version]

    version MUST be a full SemVer.

    If version is omitted, the tool SHOULD create a tag using the following rules:

    • If there is no previous version, it MUST report an error.

    • If the previous version has MAJOR number equal to 0, it MUST NOT advance the MAJOR number, under no circumstances. Instead it SHOULD increase MINOR number if the API is not compatible with the previous one, and it SHOULD increase PATCH number if new compatible API is implemented.

    • Otherwise, the new version MUST follow SemVer

    If version is specified, the tool SHOULD check that it follows SemVer.

Proposed new Go tools

TODO: go tool vendor

@davecheney
Copy link

Thank you for suggesting this. I think you should follow the full proposal process to get an issue number and people can comment on your proposals.

I also have some questions

go get golang.org/x/text@1.2.3

Where is this repository checked out inside $GOPATH ?

How do programs import this package, what is their import declaration ?

How does go get deal with the transitive dependencies of x/text ? Will it try to check out their v1.2.3 tag ?

@perillo
Copy link
Author

perillo commented Nov 27, 2015

@davecheney
Thanks for the comment.

When a version is appended in a remote import path, the package import path and version info is splitted.
This means that the local import path will not have version information. The package is imported without appending the version information.

When downloading transitive dependencies, since no version was specified for them, go get will download the latest revision.

A tool like gb can do things better, since it only have to deal with dependencies of a project.
As an example:
gb vendor fetch all latest

will fetch all dependencies at their latest versions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment