From 74bf5cfb9e3108a48d95e23ca00901cb50cdba2f Mon Sep 17 00:00:00 2001 From: Jason Del Ponte <961963+jasdel@users.noreply.github.com> Date: Tue, 5 Oct 2021 14:21:06 -0700 Subject: [PATCH] Update SDK to use shared tooling for changelog and release handling (#1431) Updates the SDK to use shared tooling with the smithy-go repo for handling changelogs and module releases. Remove internal tooling that's been moved to shared repotools project. --- Makefile | 53 +- codegen/copy_go_codegen.sh | 4 +- internal/repotools/README.md | 73 --- internal/repotools/changelog/annotation.go | 207 ------- internal/repotools/changelog/template.go | 39 -- internal/repotools/changelog/type.go | 152 ----- .../repotools/cmd/annotatestablegen/main.go | 144 ----- .../repotools/cmd/calculaterelease/README.md | 141 ----- .../repotools/cmd/calculaterelease/main.go | 91 --- internal/repotools/cmd/changelog/README.md | 125 ----- internal/repotools/cmd/changelog/create.go | 230 -------- internal/repotools/cmd/changelog/edit.go | 52 -- internal/repotools/cmd/changelog/editor.go | 43 -- internal/repotools/cmd/changelog/list.go | 44 -- internal/repotools/cmd/changelog/main.go | 78 --- internal/repotools/cmd/changelog/remove.go | 58 -- internal/repotools/cmd/changelog/view.go | 44 -- internal/repotools/cmd/eachmodule/main.go | 2 +- internal/repotools/cmd/endpointPrefix/main.go | 2 +- .../repotools/cmd/generatechangelog/main.go | 169 ------ .../cmd/generatechangelog/main_test.go | 58 -- .../cmd/generatechangelog/summary.go | 195 ------- .../cmd/generatechangelog/summary_test.go | 521 ------------------ .../cmd/generatechangelog/template.go | 78 --- internal/repotools/cmd/gomodgen/main.go | 225 -------- internal/repotools/cmd/makerelative/main.go | 252 --------- internal/repotools/cmd/tagrelease/main.go | 76 --- .../repotools/cmd/updatemodulemeta/main.go | 201 ------- .../repotools/cmd/updaterequires/README.md | 26 - internal/repotools/cmd/updaterequires/main.go | 96 ---- internal/repotools/config.go | 74 --- internal/repotools/doc.go | 3 + internal/repotools/editor.go | 37 -- internal/repotools/editor_test.go | 78 --- internal/repotools/git/add.go | 13 - internal/repotools/git/commit.go | 8 - internal/repotools/git/diff.go | 46 -- internal/repotools/git/git.go | 185 ------- internal/repotools/git/git_test.go | 151 ----- internal/repotools/git/tag.go | 9 - internal/repotools/go.mod | 7 +- internal/repotools/go.sum | 7 +- internal/repotools/go_module_metadata.go | 6 - internal/repotools/gomod/diff.go | 71 --- internal/repotools/gomod/diff_test.go | 116 ---- internal/repotools/gomod/module.go | 235 -------- internal/repotools/gomod/module_test.go | 58 -- internal/repotools/gomod/version.go | 76 --- internal/repotools/manifest/manifest.go | 100 ---- internal/repotools/release/calculate.go | 184 ------- internal/repotools/release/calculate_test.go | 75 --- .../repotools/release/manifest_schema.json | 86 --- internal/repotools/release/release.go | 441 --------------- internal/repotools/release/release_test.go | 229 -------- internal/repotools/repo_root.go | 71 --- internal/repotools/semver/semver.go | 429 -------------- internal/repotools/semver/semver_test.go | 197 ------- internal/repotools/util.go | 15 - internal/repotools/uuid.go | 28 - internal/repotools/walk.go | 52 -- 60 files changed, 43 insertions(+), 6523 deletions(-) delete mode 100644 internal/repotools/README.md delete mode 100644 internal/repotools/changelog/annotation.go delete mode 100644 internal/repotools/changelog/template.go delete mode 100644 internal/repotools/changelog/type.go delete mode 100644 internal/repotools/cmd/annotatestablegen/main.go delete mode 100644 internal/repotools/cmd/calculaterelease/README.md delete mode 100644 internal/repotools/cmd/calculaterelease/main.go delete mode 100644 internal/repotools/cmd/changelog/README.md delete mode 100644 internal/repotools/cmd/changelog/create.go delete mode 100644 internal/repotools/cmd/changelog/edit.go delete mode 100644 internal/repotools/cmd/changelog/editor.go delete mode 100644 internal/repotools/cmd/changelog/list.go delete mode 100644 internal/repotools/cmd/changelog/main.go delete mode 100644 internal/repotools/cmd/changelog/remove.go delete mode 100644 internal/repotools/cmd/changelog/view.go delete mode 100644 internal/repotools/cmd/generatechangelog/main.go delete mode 100644 internal/repotools/cmd/generatechangelog/main_test.go delete mode 100644 internal/repotools/cmd/generatechangelog/summary.go delete mode 100644 internal/repotools/cmd/generatechangelog/summary_test.go delete mode 100644 internal/repotools/cmd/generatechangelog/template.go delete mode 100644 internal/repotools/cmd/gomodgen/main.go delete mode 100644 internal/repotools/cmd/makerelative/main.go delete mode 100644 internal/repotools/cmd/tagrelease/main.go delete mode 100644 internal/repotools/cmd/updatemodulemeta/main.go delete mode 100644 internal/repotools/cmd/updaterequires/README.md delete mode 100644 internal/repotools/cmd/updaterequires/main.go delete mode 100644 internal/repotools/config.go create mode 100644 internal/repotools/doc.go delete mode 100644 internal/repotools/editor.go delete mode 100644 internal/repotools/editor_test.go delete mode 100644 internal/repotools/git/add.go delete mode 100644 internal/repotools/git/commit.go delete mode 100644 internal/repotools/git/diff.go delete mode 100644 internal/repotools/git/git.go delete mode 100644 internal/repotools/git/git_test.go delete mode 100644 internal/repotools/git/tag.go delete mode 100644 internal/repotools/go_module_metadata.go delete mode 100644 internal/repotools/gomod/diff.go delete mode 100644 internal/repotools/gomod/diff_test.go delete mode 100644 internal/repotools/gomod/module.go delete mode 100644 internal/repotools/gomod/module_test.go delete mode 100644 internal/repotools/gomod/version.go delete mode 100644 internal/repotools/manifest/manifest.go delete mode 100644 internal/repotools/release/calculate.go delete mode 100644 internal/repotools/release/calculate_test.go delete mode 100644 internal/repotools/release/manifest_schema.json delete mode 100644 internal/repotools/release/release.go delete mode 100644 internal/repotools/release/release_test.go delete mode 100644 internal/repotools/repo_root.go delete mode 100644 internal/repotools/semver/semver.go delete mode 100644 internal/repotools/semver/semver_test.go delete mode 100644 internal/repotools/util.go delete mode 100644 internal/repotools/uuid.go delete mode 100644 internal/repotools/walk.go diff --git a/Makefile b/Makefile index c7d29055cd6..6cafd1ff120 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ EACHMODULE_SKIP_FLAG=-skip="${EACHMODULE_SKIP}" EACHMODULE_FLAGS=${EACHMODULE_CONCURRENCY_FLAG} ${EACHMODULE_FAILFAST_FLAG} ${EACHMODULE_SKIP_FLAG} -# SDK's Core and client packages that are compatable with Go 1.9+. +# SDK's Core and client packages that are compatible with Go 1.9+. SDK_CORE_PKGS=./aws/... ./internal/... SDK_CLIENT_PKGS=./service/... SDK_COMPA_PKGS=${SDK_CORE_PKGS} ${SDK_CLIENT_PKGS} @@ -41,6 +41,20 @@ LICENSE_FILE=$(shell pwd)/LICENSE.txt RELEASE_MANIFEST_FILE ?= RELEASE_CHGLOG_DESC_FILE ?= +REPOTOOLS_VERSION ?= latest +REPOTOOLS_MODULE = github.com/awslabs/aws-go-multi-module-repository-tools +REPOTOOLS_CMD_ANNOTATE_STABLE_GEN = ${REPOTOOLS_MODULE}/cmd/annotatestablegen@${REPOTOOLS_VERSION} +REPOTOOLS_CMD_MAKE_RELATIVE = ${REPOTOOLS_MODULE}/cmd/makerelative@${REPOTOOLS_VERSION} +REPOTOOLS_CMD_CALCULATE_RELEASE = ${REPOTOOLS_MODULE}/cmd/calculaterelease@${REPOTOOLS_VERSION} +REPOTOOLS_CMD_UPDATE_REQUIRES = ${REPOTOOLS_MODULE}/cmd/updaterequires@${REPOTOOLS_VERSION} +REPOTOOLS_CMD_UPDATE_MODULE_METADATA = ${REPOTOOLS_MODULE}/cmd/updatemodulemeta@${REPOTOOLS_VERSION} +REPOTOOLS_CMD_GENERATE_CHANGELOG = ${REPOTOOLS_MODULE}/cmd/generatechangelog@${REPOTOOLS_VERSION} +REPOTOOLS_CMD_CHANGELOG = ${REPOTOOLS_MODULE}/cmd/changelog@${REPOTOOLS_VERSION} +REPOTOOLS_CMD_TAG_RELEASE = ${REPOTOOLS_MODULE}/cmd/tagrelease@${REPOTOOLS_VERSION} + +REPOTOOLS_CALCULATE_RELEASE_VERBOSE ?= false +REPOTOOLS_CALCULATE_RELEASE_VERBOSE_FLAG=-v=${REPOTOOLS_CALCULATE_RELEASE_VERBOSE} + .PHONY: all all: generate unit @@ -74,8 +88,7 @@ smithy-build-%: gen-repo-mod-replace SMITHY_GO_BUILD_API="$(subst smithy-build-,,$@)" ./gradlew clean build -Plog-tests smithy-annotate-stable: - cd ./internal/repotools && \ - go run ./cmd/annotatestablegen + go run ${REPOTOOLS_CMD_ANNOTATE_STABLE_GEN} smithy-clean: cd codegen && ./gradlew clean @@ -96,7 +109,7 @@ gen-config-asserts: gen-repo-mod-replace: @echo "Generating go.mod replace for repo modules" - cd internal/repotools/cmd/makerelative && go run ./ + go run ${REPOTOOLS_CMD_MAKE_RELATIVE} gen-mod-replace-smithy: cd ./internal/repotools/cmd/eachmodule \ @@ -182,12 +195,10 @@ min-go-version-%: "go mod edit -go=${SDK_MIN_GO_VERSION}" update-requires: - cd ./internal/repotools && \ - go run ./cmd/updaterequires + go run ${REPOTOOLS_CMD_UPDATE_REQUIRES} update-module-metadata: - cd ./internal/repotools && \ - go run ./cmd/updatemodulemeta + go run ${REPOTOOLS_CMD_UPDATE_MODULE_METADATA} ################ # Unit Testing # @@ -381,26 +392,27 @@ bench-modules-%: ##################### .PHONY: preview-release pre-release-validation release +ls-changes: + go run ${REPOTOOLS_CMD_CHANGELOG} ls + preview-release: - @cd ./internal/repotools && \ - go run ./cmd/calculaterelease + go run ${REPOTOOLS_CMD_CALCULATE_RELEASE} ${REPOTOOLS_CALCULATE_RELEASE_VERBOSE_FLAG} pre-release-validation: @if [[ -z "${RELEASE_MANIFEST_FILE}" ]]; then \ - echo "RELEASE_MANIFEST_FILE is required to specify the file to write the release manifest" && false; \ - fi + echo "RELEASE_MANIFEST_FILE is required to specify the file to write the release manifest" && false; \ + fi @if [[ -z "${RELEASE_CHGLOG_DESC_FILE}" ]]; then \ echo "RELEASE_CHGLOG_DESC_FILE is required to specify the file to write the release notes" && false; \ fi release: pre-release-validation - cd ./internal/repotools && \ - go run ./cmd/calculaterelease -o ${RELEASE_MANIFEST_FILE} && \ - go run ./cmd/updaterequires -release ${RELEASE_MANIFEST_FILE} && \ - go run ./cmd/updatemodulemeta -release ${RELEASE_MANIFEST_FILE} && \ - go run ./cmd/generatechangelog -release ${RELEASE_MANIFEST_FILE} -o ${RELEASE_CHGLOG_DESC_FILE} && \ - go run ./cmd/changelog rm -all && \ - go run ./cmd/tagrelease -release ${RELEASE_MANIFEST_FILE} + go run ${REPOTOOLS_CMD_CALCULATE_RELEASE} -o ${RELEASE_MANIFEST_FILE} ${REPOTOOLS_CALCULATE_RELEASE_VERBOSE_FLAG} + go run ${REPOTOOLS_CMD_UPDATE_REQUIRES} -release ${RELEASE_MANIFEST_FILE} + go run ${REPOTOOLS_CMD_UPDATE_MODULE_METADATA} -release ${RELEASE_MANIFEST_FILE} + go run ${REPOTOOLS_CMD_GENERATE_CHANGELOG} -release ${RELEASE_MANIFEST_FILE} -o ${RELEASE_CHGLOG_DESC_FILE} + go run ${REPOTOOLS_CMD_CHANGELOG} rm -all + go run ${REPOTOOLS_CMD_TAG_RELEASE} -release ${RELEASE_MANIFEST_FILE} ############## # Repo Tools # @@ -408,8 +420,7 @@ release: pre-release-validation .PHONY: install-repotools install-repotools: - cd ./internal/repotools && \ - go install ./cmd/changelog + go install ${REPOTOOLS_MODULE}/cmd/changelog@${REPOTOOLS_VERSION} ################## # Linting/Verify # diff --git a/codegen/copy_go_codegen.sh b/codegen/copy_go_codegen.sh index b501d71380e..50ddaafb3bf 100755 --- a/codegen/copy_go_codegen.sh +++ b/codegen/copy_go_codegen.sh @@ -5,5 +5,7 @@ set -xe SDK_ROOT=$1 CODGEN_ROOT=$2 +REPOTOOLS_VERSION="${REPOTOOLS_VERSION:-latest}" + cd "$1"/internal/repotools -go run ./cmd/gomodgen -build "$CODGEN_ROOT" +go run github.com/awslabs/aws-go-multi-module-repository-tools/cmd/gomodgen@${REPOTOOLS_VERSION} -build "$CODGEN_ROOT" diff --git a/internal/repotools/README.md b/internal/repotools/README.md deleted file mode 100644 index fdef58e3ea8..00000000000 --- a/internal/repotools/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# Repository Management Tools - -This Go module is a collection of tools that have been written for managing the AWS SDK for Go V2 source repository. -At the time these were written, the Go ecosystem lacked tooling for managing repositories containing multiple Go modules -with at the size and scale of the AWS SDK. With over 274 Go modules in the repository the tooling found here has been -made to manage the lifecycle of managing dependencies (iter-repository and external), managing the releases of new -changes, tag management following Go semver requirements, and production of changelogs. - -## Utilities -The following is a breakdown of some the key utilities found in this module that are used to manage the SDK and handle -the complex release process. - -Commands | Description | README ---- | --- | --- -`changelog` | Create and manage changelog annotations. Annotations are used to document module changes and refining of the next semver version. | [Link][changelog] -`updaterequires` | Manages `go.mod` require entries, allows for easily updating inter-repository module dependencies to their latest tag, and the ability to quickly manage external dependency requirements. | N/A -`updatemodulemeta` | Generates a `go_module_metadata.go` file in each module containing useful runtime metadata like the modules tagged version. | N/A -`generatechangelog` | Uses a release description and associated changelog annotations to produce `CHANGELOG.md` entries for the release in each repository module. In addition, a summarized release statement will be created at the root of the repository. | N/A -`gomodgen` | Copies [smithy-go] codegen build artifacts into the SDK repository and generates a `go.mod` file using the build artifacts `generated.json` description. | N/A -`annotatestablegen` | Generates a release changelog annotation type for **new** [smithy-go] generated modules that are not marked as unstable. | N/A -`calculaterelease` | Detects new and changed Go modules in the repository, associates changelog annotations, and computes the next semver version tag for each module. Produces a release manifest that is used with other utilities to orchestrate a release. | [Link][calculaterelease] -`tagrelease` | Commits pending changes to the working directory, reads the release manifest, and creates the computed tags | N/A -`makerelative` | Used to generate `go.mod` `replace` statements for inter-repository module dependencies. This ensures that when developing on a given Go module it's iter-repository dependencies refer to the cloned repository. | N/A -`eachmodule` | Utility for quickly scripting execution of commands in each module of a repository. | N/A - -# Configuration - -A number of the repository tools, specifically those involved with the dependency management and release have specific -behavior that is driven by the `modman.toml` file found at the root of the git repository. This configuration file is -a [TOML] configuration file. - -## Dependencies -The `dependencies` is a dictionary of key-value pairs that describe **external** dependencies that one or more modules -within the repository may depend on. (External dependencies is defined as the set of Go modules that are not found -within the project git repository.) This section is used to quickly set the version of a dependency modules in the -repository should use. The `updaterequires` tool can be used to update all Go modules require statements for each module -in the repository and update them to the indicated version if they depend on the given external module. - -### Example -```toml -[dependencies] -"github.com/aws/smithy-go" = "v1.4.0" -``` - -This example indicates that repository modules that depend on `github.com/aws/smithy-go` should depend on `v1.4.0` -version of the library. After updating the value in the configuration file, `updaterequires` can be used to update -modules with this information. - -## Modules - -`modules` is a dictionary where the keys are module directories relative to the repository root. Each key maps to a -dictionary of key-value pairs that can configure several properties of a module that affect how or -if a module is handled when performing a release. - -### Example -To configure the module `feature/service/shinything` to not be tagged by the release process: - -```toml -[modules."feature/service/shinything"] -no_tag = true -``` - -For more information on how to configure how modules are released see the [calculaterelease README][calculaterelease]. - -#### Mark a module to be - -**NOTE**: If you wish to create a configuration item for a module located at the root of the repository use -`.` as the key name. - -[calculaterelease]: cmd/calculaterelease/README.md -[changelog]: cmd/changelog/README.md -[smithy-go]: https://github.com/aws/smithy-go -[TOML]: https://toml.io diff --git a/internal/repotools/changelog/annotation.go b/internal/repotools/changelog/annotation.go deleted file mode 100644 index 5d9236f0b7f..00000000000 --- a/internal/repotools/changelog/annotation.go +++ /dev/null @@ -1,207 +0,0 @@ -package changelog - -import ( - "bytes" - "crypto/rand" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - - "github.com/aws/aws-sdk-go-v2/internal/repotools" -) - -const changelogDir = ".changelog" - -// Annotation represents a change to one or more Go modules. -type Annotation struct { - // The unique identifier for this Annotation. - ID string `json:"id" toml:"id" comment:"Annotation Identifier (DO NOT CHANGE)"` - - // Indicates what category of Annotation was made. For example "feature" or "bugfix". - Type ChangeType `json:"type" toml:"type" comment:"Valid Types: announcement, release, feature, bugfix, documentation, or dependency"` - - // Collapse indicates that the change description should collapsed into a single item when summarizing changes across modules - Collapse bool `json:"collapse,omitempty" toml:"collapse" comment:"annotation should collapse as a summary in the CHANGELOG"` - - // A human readable description of this Annotation meant to be included in a CHANGELOG. - Description string `json:"description" toml:"description" comment:"single-line string or markdown list"` - - // The modules this change applies to - Modules []string `json:"modules" toml:"modules" comment:"one or more relative module paths"` -} - -// ValidationError is an error that indicates that one ore more issues are present for an annotation. -type ValidationError struct { - Issues []string -} - -// Error returns the error string -func (v *ValidationError) Error() string { - var sb strings.Builder - sb.WriteString("invalid change annotation:\n") - for _, issue := range v.Issues { - sb.WriteRune('\t') - sb.WriteString(issue) - sb.WriteRune('\n') - } - sb.WriteRune('\n') - return sb.String() -} - -// Validate returns an error if annotation does not mean the minimum requirements. -func Validate(annotation Annotation) error { - var invalid []string - - if len(annotation.ID) == 0 { - invalid = append(invalid, "annotation id is required") - } - - if annotation.Type == UnknownChangeType { - invalid = append(invalid, fmt.Sprintf("invalid change type")) - } - - if len(annotation.Description) == 0 { - invalid = append(invalid, "description is required") - } - - if len(annotation.Modules) < 1 { - invalid = append(invalid, "at least one module is required") - } - - if len(invalid) > 0 { - return &ValidationError{Issues: invalid} - } - - return nil -} - -// WriteAnnotation writes the annotation to changelog metadata directory. -// Path should be the location of the repository root. -func WriteAnnotation(path string, annotation Annotation) (err error) { - dir := filepath.Join(path, changelogDir) - - if err := os.MkdirAll(dir, 0755); err != nil { - return err - } - - marshal, err := json.MarshalIndent(annotation, "", " ") - if err != nil { - return err - } - - name := fmt.Sprintf("%s.json", strings.ReplaceAll(annotation.ID, "-", "")) - - f, err := os.OpenFile(filepath.Join(dir, name), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) - if err != nil { - return err - } - defer func() { - fErr := f.Close() - if err == nil && fErr != nil { - err = fErr - } - }() - - _, err = io.Copy(f, bytes.NewReader(marshal)) - - return err -} - -// RemoveAnnotation removes the annotation from the changelog metadata directory. -func RemoveAnnotation(path string, annotation Annotation) (err error) { - dir := filepath.Join(path, changelogDir) - - name := fmt.Sprintf("%s.json", strings.ReplaceAll(annotation.ID, "-", "")) - - if err := os.Remove(filepath.Join(dir, name)); err != nil && !os.IsNotExist(err) { - return err - } - - return nil -} - -// GetAnnotations returns the list of annotations that are currently present. -// Path should be the location of the repository root. -func GetAnnotations(path string) (annotations []Annotation, err error) { - dir := filepath.Join(path, changelogDir) - - if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) { - return nil, nil - } else if err != nil { - return nil, err - } - - err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - if err != nil || path == dir { - return err - } - - if info.IsDir() { - return filepath.SkipDir - } - - if !strings.HasSuffix(info.Name(), ".json") { - return nil - } - - annotation, err := LoadAnnotationFile(path) - if err != nil { - return err - } - - annotations = append(annotations, annotation) - - return nil - }) - if err != nil { - return nil, err - } - - return annotations, nil -} - -// LoadAnnotationFile loads the annotation file at the given path. -func LoadAnnotationFile(path string) (a Annotation, err error) { - fBytes, err := ioutil.ReadFile(path) - if err != nil { - return Annotation{}, err - } - - if err = json.Unmarshal(fBytes, &a); err != nil { - return Annotation{}, err - } - - return a, nil -} - -// LoadAnnotation loads the annotation id for the given repository path. -func LoadAnnotation(path string, id string) (a Annotation, err error) { - path = filepath.Join(path, changelogDir, strings.ReplaceAll(id, "-", "")+".json") - return LoadAnnotationFile(path) -} - -// GetVersionIncrement returns the highest version increment from a set of annotations. -func GetVersionIncrement(annotations []Annotation) (v SemVerIncrement) { - for _, annotation := range annotations { - vi := annotation.Type.VersionIncrement() - if vi > v { - v = vi - } - } - return v -} - -// NewAnnotation creates a new annotation with a populated identifier. -func NewAnnotation() (Annotation, error) { - var b [16]byte - if _, err := io.ReadFull(rand.Reader, b[:]); err != nil { - return Annotation{}, err - } - return Annotation{ - ID: repotools.UUIDVersion4(b), - }, nil -} diff --git a/internal/repotools/changelog/template.go b/internal/repotools/changelog/template.go deleted file mode 100644 index 3c2005b678e..00000000000 --- a/internal/repotools/changelog/template.go +++ /dev/null @@ -1,39 +0,0 @@ -package changelog - -import ( - "bytes" - "fmt" - - "github.com/pelletier/go-toml" -) - -// TemplateToAnnotation parses the provided filledTemplate into the provided Change. If Change has no ID, TemplateToChange -// will set the ID. -func TemplateToAnnotation(filledTemplate []byte) (annotation Annotation, err error) { - err = toml.Unmarshal(filledTemplate, &annotation) - if err != nil { - return Annotation{}, err - } - - if len(annotation.Modules) == 0 { - return Annotation{}, fmt.Errorf("annotation should include at least one module") - } - - return annotation, nil -} - -// AnnotationToTemplate returns a Change template populated with the given Change's data. -func AnnotationToTemplate(annotation Annotation) ([]byte, error) { - buffer := bytes.NewBuffer(nil) - encoder := toml.NewEncoder(buffer) - - err := encoder.Order(toml.OrderPreserve). - ArraysWithOneElementPerLine(true). - Indentation(" "). - Encode(annotation) - if err != nil { - return nil, err - } - - return buffer.Bytes(), nil -} diff --git a/internal/repotools/changelog/type.go b/internal/repotools/changelog/type.go deleted file mode 100644 index 82cddcc02b8..00000000000 --- a/internal/repotools/changelog/type.go +++ /dev/null @@ -1,152 +0,0 @@ -package changelog - -import ( - "encoding/json" - "fmt" - "strings" -) - -// SemVerIncrement describes how a Annotation should affect a module's version. -type SemVerIncrement int - -const ( - // DefaultBump indicates the the module's version should be incremented using the version selectors default behavior. - DefaultBump SemVerIncrement = iota - - // PatchBump indicates the module's version should be incremented by a patch version bump. - PatchBump - // MinorBump indicates the module's version should be incremented by a minor version bump. - MinorBump - // ReleaseBump indicates the module version should be updated from a pre-release tag. - ReleaseBump -) - -// ChangeType describes the type of change made to a Go module. -type ChangeType int - -// MarshalTOML marshals the ChangeType to a TOML string representation. -func (c ChangeType) MarshalTOML() ([]byte, error) { - return []byte("\"" + c.String() + "\""), nil -} - -// UnmarshalTOML unmarshal i, which must be a string, to it's ChangeType representaiton. -func (c *ChangeType) UnmarshalTOML(i interface{}) error { - v, ok := i.(string) - if !ok { - return fmt.Errorf("expect to unmarshal string, got %T", i) - } - *c = ParseChangeType(v) - return nil -} - -// MarshalJSON marshals the ChangeType to a JSON string representation. -func (c ChangeType) MarshalJSON() ([]byte, error) { - return json.Marshal(c.String()) -} - -// UnmarshalJSON unmarshal bytes, which must be a string, to it's ChangeType representaiton. -func (c *ChangeType) UnmarshalJSON(bytes []byte) error { - var t string - if err := json.Unmarshal(bytes, &t); err != nil { - return err - } - *c = ParseChangeType(t) - return nil -} - -// ChangeType in order from least to most precedence when generating changelog summaries -const ( - UnknownChangeType ChangeType = iota - // DependencyChangeType is a constant change type for a dependency update. - DependencyChangeType - // DocumentationChangeType is a constant change type for an SDK announcement. - DocumentationChangeType - // BugFixChangeType is a constant change type for a bug fix. - BugFixChangeType - // FeatureChangeType is a constant change type for a new feature. - FeatureChangeType - // ReleaseChangeType is a constant change type for a major version updates (from v0 => v1). - ReleaseChangeType - // AnnouncementChangeType is a constant change type for an SDK announcement. - AnnouncementChangeType -) - -// ParseChangeType attempts to parse the given string v into a ChangeType, returning an error if the string is invalid. -func ParseChangeType(v string) ChangeType { - switch { - case strings.EqualFold(FeatureChangeType.String(), v): - return FeatureChangeType - case strings.EqualFold(BugFixChangeType.String(), v): - return BugFixChangeType - case strings.EqualFold(ReleaseChangeType.String(), v): - return ReleaseChangeType - case strings.EqualFold(DependencyChangeType.String(), v): - return DependencyChangeType - case strings.EqualFold(AnnouncementChangeType.String(), v): - return AnnouncementChangeType - case strings.EqualFold(DocumentationChangeType.String(), v): - return DocumentationChangeType - default: - return UnknownChangeType - } -} - -// ChangelogPrefix returns the CHANGELOG header the ChangeType should be grouped under. -func (c ChangeType) ChangelogPrefix() string { - switch c { - case FeatureChangeType: - return "Feature" - case BugFixChangeType: - return "Bug Fix" - case ReleaseChangeType: - return "Release" - case DependencyChangeType: - return "Dependency Update" - case DocumentationChangeType: - return "Documentation" - case AnnouncementChangeType: - return "Announcement" - default: - return "" - } -} - -// VersionIncrement returns the SemVerIncrement corresponding to the given ChangeType. -func (c ChangeType) VersionIncrement() SemVerIncrement { - switch c { - case ReleaseChangeType: - return ReleaseBump - case FeatureChangeType: - return MinorBump - case BugFixChangeType: - return PatchBump - case DependencyChangeType: - return PatchBump - case DocumentationChangeType: - return PatchBump - case AnnouncementChangeType: - fallthrough - default: - return DefaultBump - } -} - -// String returns a string representation of the ChangeType -func (c ChangeType) String() string { - switch c { - case AnnouncementChangeType: - return "announcement" - case ReleaseChangeType: - return "release" - case FeatureChangeType: - return "feature" - case BugFixChangeType: - return "bugfix" - case DocumentationChangeType: - return "documentation" - case DependencyChangeType: - return "dependency" - default: - return "" - } -} diff --git a/internal/repotools/cmd/annotatestablegen/main.go b/internal/repotools/cmd/annotatestablegen/main.go deleted file mode 100644 index 8886d6450a3..00000000000 --- a/internal/repotools/cmd/annotatestablegen/main.go +++ /dev/null @@ -1,144 +0,0 @@ -package main - -import ( - "encoding/json" - "github.com/aws/aws-sdk-go-v2/internal/repotools" - "github.com/aws/aws-sdk-go-v2/internal/repotools/changelog" - "github.com/aws/aws-sdk-go-v2/internal/repotools/git" - "github.com/aws/aws-sdk-go-v2/internal/repotools/gomod" - "github.com/aws/aws-sdk-go-v2/internal/repotools/semver" - "io/ioutil" - "log" - "os" - "path/filepath" - "sort" -) - -const ( - // reservedChangeID is the string "AWSSDK@\xff\xbfGOAUTO\x00" - // Note: future needs can increment the last \x00 value up to \xFF for 256 pseudo-random values for automation - // release notes. - // TODO: centralize these somewhere if we need to extend this past this one UUID. - reservedChangeID = "41575353-444b-40ff-bf47-4f4155544f00" - - description = "New AWS service client module" - - generatedFile = "generated.json" -) - -func main() { - cwd, err := os.Getwd() - if err != nil { - log.Fatalf("failed to get current working directory: %v", err) - } - - repoRoot, err := repotools.FindRepoRoot(cwd) - if err != nil { - log.Fatalf("failed to get git repo root: %v", err) - } - - tags, err := git.Tags(repoRoot) - if err != nil { - log.Fatalf("failed to get git repository tags: %v", err) - } - - moduleTags := git.ParseModuleTags(tags) - - discoverer := gomod.NewDiscoverer(repoRoot) - - if err := discoverer.Discover(); err != nil { - log.Fatalf("failed to discover go modules: %v", err) - } - - modules, err := discoverer.ModulesRel() - if err != nil { - log.Fatalf("failed to get modules relative to repo: %v", err) - } - - var toRelease []string - - for modDir := range modules { - fullPath := filepath.Join(repoRoot, modDir) - - isGenerated, err := isGeneratedModule(fullPath) - if err != nil { - log.Fatalf("failed to determine if module is generated: %v", err) - } - if !isGenerated { - continue - } - - stable, err := isGeneratedModuleStable(fullPath) - if err != nil { - log.Fatalf("failed to determine if generated module is stable: %v", err) - } - - latest, ok := moduleTags.Latest(modDir) - if !ok && stable { - toRelease = append(toRelease, modDir) - } else if ok && stable && len(semver.Prerelease(latest)) > 0 { - toRelease = append(toRelease, modDir) - } - } - - if len(toRelease) == 0 { - log.Printf("[INFO] no generated modules require release annotation") - return - } - - var annotation changelog.Annotation - - annotation, err = changelog.LoadAnnotation(repoRoot, reservedChangeID) - if err != nil && !os.IsNotExist(err) { - log.Fatalf("failed to load module annotation: %v", err) - } - if err != nil { - annotation, err = changelog.NewAnnotation() - if err != nil { - log.Fatalf("failed to generated annotation: %v", err) - } - annotation.ID = reservedChangeID - annotation.Type = changelog.ReleaseChangeType - annotation.Description = description - } else if annotation.Type != changelog.ReleaseChangeType { - log.Fatalf("annotation type does not match the expected type") - } - - sort.Strings(annotation.Modules) - - for _, modDir := range toRelease { - annotation.Modules = repotools.AppendIfNotPresent(annotation.Modules, modDir) - } - - if err := changelog.WriteAnnotation(repoRoot, annotation); err != nil { - log.Fatalf("failed to write annotation: %v", err) - } -} - -type generated struct { - Unstable bool `json:"unstable"` -} - -func isGeneratedModuleStable(dir string) (bool, error) { - bytes, err := ioutil.ReadFile(filepath.Join(dir, generatedFile)) - if err != nil { - return false, err - } - - var generated generated - if err := json.Unmarshal(bytes, &generated); err != nil { - return false, err - } - - return !generated.Unstable, nil -} - -func isGeneratedModule(dir string) (bool, error) { - _, err := os.Stat(filepath.Join(dir, generatedFile)) - if err != nil && os.IsNotExist(err) { - return false, nil - } else if err != nil { - return false, err - } - return true, nil -} diff --git a/internal/repotools/cmd/calculaterelease/README.md b/internal/repotools/cmd/calculaterelease/README.md deleted file mode 100644 index 85f3e83594f..00000000000 --- a/internal/repotools/cmd/calculaterelease/README.md +++ /dev/null @@ -1,141 +0,0 @@ -# Description - -Detects new and changed Go modules in the repository, associates changelog annotations, and computes the next semver -version tag for each module. Produces a release manifest that is used with other utilities to orchestrate a release. - -# Usage - -``` -calculaterelease [-o ] -``` - -# Determining Modules for Release - -The `calculaterelease` traverses the repository to discover Go modules that are present. Using the discovered module -locations the tool determines whether a module is a new module, or an existing module with changes. To do so, `Git` is -first used to retrieve a list of tags for the repository. These tags are filtered and then sorted for each module by -using Go's [module versioning rules][modules-version-numbers]. For modules that have been previously tagged, -[git-diff-tree] is used to determine if changes were made to the module path by comparing the latest tag to the current -repository HEAD. If the module path, excluding child sub-module directories, contains changes to either `*.go` or -`go.mod` the module is considered as having source changes and will be considered for release. New modules that have not -been tagged previously are considered to eligible for release. - -After determining the set of modules that are eligible for release, `calculaterelease` builds a reverse-dependency tree -graph to incrementally mark modules as requiring a version bump if one or more it's dependencies or -transitive-dependencies has been determined to be changed. - -Finally, after determining the complete change set the next module version is chosen by using the change annotations -created using the [changelog] tool to refine and compute the next desired version. See -[here](#determining-the-next-module-version) for a more in-depth description about version selection. After computing -the next version for each module a final pass occurs to filter out modules that are configured to not be tagged for -released. After this is complete a summary manifest of the final set of modules to be released is printed out to -standard output, or the desired output file location. - -**IMPORTANT**: `calculaterelease` does not consider the working directory or index when determining what should be -released, thus all relevant changes MUST be committed before executing the command. - -# Determining the Next Module Version - -Determining the next version of a module is determined by a combination of heuristics, and the information provided by -change annotations to refine the next version of a given module. The precedence of one or more annotations is as follows: -`release > feature > bugfix >= dependency >= documentation >= announcement`. the precedence ordering defines the type -of semantic version bump that will occur, allowing for multiple annotations to be defined safely. - -The following table summarizes a complete set of examples of how module version selection works. - -Module Path | Latest Tag | Next Tag | Annotations | Config | Descriptions ---- | --- | --- | --- | --- | --- -`github.com/aws/aws-sdk-go-v2/foo` | N/A | `foo/v1.0.0-preview` | N/A | N/A | New repository modules with no annotations default to a preview release tag -`github.com/aws/aws-sdk-go-v2/foo` | `foo/v1.0.0-preview` | `foo/v1.0.0-preview.1` | `feature, bugfix` | N/A | All changes to pre-release semver tags will increment an integer separated by a `.` on the pre-release tag. -`github.com/aws/aws-sdk-go-v2/foo` | `foo/v1.0.0-preview.1` | `foo/v1.0.0-rc` | `bugfix` | `{"pre_release": "rc"}` | The `pre_release` config for a module can be used to control the semver pre-release identifier. Annotations that are not `release` do not affect the version increment behavior. -`github.com/aws/aws-sdk-go-v2/foo` | `foo/v1.0.0-rc` | `foo/v1.0.0` | `release` | `{"pre_release": "rc"}` | A release annotation indicates the module's pre-release tag should be removed -`github.com/aws/aws-sdk-go-v2/foo` | `foo/v1.0.0` | `foo/v1.0.1` | N/A | N/A | Modules with changes but no annotations default to patch bump -`github.com/aws/aws-sdk-go-v2/foo` | `foo/v1.0.1` | `foo/v1.0.2` | `bugfix` | N/A | Modules with a bugfix annotation will increment the patch component. -`github.com/aws/aws-sdk-go-v2/foo` | `foo/v1.0.2` | `foo/v1.1.0` | `feature` | N/A | Feature bump will increment the minor version component. -`github.com/aws/aws-sdk-go-v2/foo` | `foo/v1.1.0` | `foo/v1.2.0-alpha` | N/A | `{"pre_release": "alpha"}` | The `pre_release` configuration can be used to mark the a modules next tagged release as a pre-release. Pre-release tags always imply a feature bump when calculating the preview version. -`github.com/aws/aws-sdk-go-v2/foo/v2` | N/A | `foo/v2.0.0-preview` | N/A | N/A | New repository modules with no annotations default to a preview release tag -`github.com/aws/aws-sdk-go-v2/bar` | N/A | N/A | `release` | N/A | New repository modules can be marked with `release` annotation to be immediately tagged with non-pre-release tag. -`github.com/aws/aws-sdk-go-v2/baz` | N/A | N/A | `feature` | `{"no_tag": true}` | Modules that are configured with`no_tag` will not be tagged regardless of whether there are Git changes or annotations. - -# Understanding a Release Manifest - -A [JSON Schema][json-schema] definition is available that provides a description of the release manifest produced by this tool. -You can view the definition [here](../../release/manifest_schema.json). - -# Configuration - -At the repository root one or more keys can be added to the `modules` dictionary in the `modman.toml`. - -```toml -[modules."relative/mod/path"] -no_tag = false # Set to true to indicate that the module should not be tagged for release. Regardless of changes, annoations, or having previously been tagged. -pre_release = "" # Set a semantic version pre-release identifer that will be used in the next release. -``` - -# Examples - -## Preview changes to be released - -By default `calculaterelease` will output the manifest to STDOUT, making it easy to preview changes that are currently -pending release. - -``` -$ calculaterelease -{ - "id": "2021-05-07", - "modules": { - "feature/s3/manager": { - "module_path": "github.com/aws/aws-sdk-go-v2/feature/s3/manager", - "from": "v1.1.4", - "to": "v1.1.5", - "changes": { - "dependency_update": true - } - }, - "service/internal/s3shared": { - "module_path": "github.com/aws/aws-sdk-go-v2/service/internal/s3shared", - "from": "v1.2.3", - "to": "v1.2.4", - "changes": { - "source_change": true - } - }, - "service/s3": { - "module_path": "github.com/aws/aws-sdk-go-v2/service/s3", - "from": "v1.6.0", - "to": "v1.6.1", - "changes": { - "dependency_update": true - } - }, - "service/s3control": { - "module_path": "github.com/aws/aws-sdk-go-v2/service/s3control", - "from": "v1.5.1", - "to": "v1.5.2", - "changes": { - "dependency_update": true - } - } - }, - "tags": [ - "feature/s3/manager/v1.1.5", - "service/internal/s3shared/v1.2.4", - "service/s3/v1.6.1", - "service/s3control/v1.5.2" - ] -} -``` - -## Calculate Release and Write Computation Manifest to File - -``` -$ calculaterelease -o /output/file -``` - -[json-schema]: https://json-schema.org/ - -[changelog]: ../changelog/README.md - -[modules-version-numbers]: https://golang.org/doc/modules/version-numbers - -[git-diff-tree]: https://git-scm.com/docs/git-diff-tree diff --git a/internal/repotools/cmd/calculaterelease/main.go b/internal/repotools/cmd/calculaterelease/main.go deleted file mode 100644 index 4df82493df7..00000000000 --- a/internal/repotools/cmd/calculaterelease/main.go +++ /dev/null @@ -1,91 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "flag" - "fmt" - "io" - "log" - "os" - - "github.com/aws/aws-sdk-go-v2/internal/repotools" - "github.com/aws/aws-sdk-go-v2/internal/repotools/changelog" - "github.com/aws/aws-sdk-go-v2/internal/repotools/git" - "github.com/aws/aws-sdk-go-v2/internal/repotools/gomod" - "github.com/aws/aws-sdk-go-v2/internal/repotools/release" -) - -var outputFile string - -func init() { - flag.StringVar(&outputFile, "o", "", "output file") -} - -func main() { - flag.Parse() - - repoRoot, err := repotools.GetRepoRoot() - if err != nil { - log.Fatalf("failed to get repository root: %v", err) - } - - config, err := repotools.LoadConfig(repoRoot) - if err != nil { - log.Fatalf("failed to load repotools config: %v", err) - } - - discoverer := gomod.NewDiscoverer(repoRoot) - - if err := discoverer.Discover(); err != nil { - log.Fatalf("failed to discover repository modules: %v", err) - } - - tags, err := git.Tags(repoRoot) - if err != nil { - log.Fatalf("failed to get git tags: %v", err) - } - - taggedModules := git.ParseModuleTags(tags) - - annotations, err := changelog.GetAnnotations(repoRoot) - if err != nil { - log.Fatal(err) - } - - modulesForRelease, err := release.Calculate(discoverer, taggedModules, config, annotations) - if err != nil { - log.Fatal(err) - } - - id := release.NextReleaseID(tags) - - manifest, err := release.BuildReleaseManifest(id, modulesForRelease) - if err != nil { - log.Fatal(err) - } - - marshal, err := json.MarshalIndent(manifest, "", " ") - if err != nil { - log.Fatal(err) - } - - if len(outputFile) == 0 { - fmt.Printf("%v\n", string(marshal)) - return - } - - file, err := os.OpenFile(outputFile, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644) - if err != nil { - log.Fatal(err) - } - defer func() { - if err := file.Close(); err != nil { - log.Fatal(err) - } - }() - - if _, err = io.Copy(file, bytes.NewReader(marshal)); err != nil { - log.Fatal(err) - } -} diff --git a/internal/repotools/cmd/changelog/README.md b/internal/repotools/cmd/changelog/README.md deleted file mode 100644 index e3ee102d091..00000000000 --- a/internal/repotools/cmd/changelog/README.md +++ /dev/null @@ -1,125 +0,0 @@ -# Description - -`changelog` is a utility for creating and managing change annotations for a repository. Change annotations are used in -the repository to refine the [semver] increment behavior for modules that have pending changes to be released. -Additionally, these annotations include descriptions that are used to produce `CHANGELOG.md` entries for each module -that has been changed as part of a release. - -# Usage - -``` -changelog create [-c | (-cs -ce )] [-t ] [-d ] [...] - -Options: --c A commit or tag to generate a change annotation for --cs A starting commit or tag for a change annotation, must be used with -ce to compare changes between two trees --ce An ending commit or tag for a change annotation, must be used with -cs to compare changes between two trees --r Declare that the annotation description should be rolled up as a summary when producing summarized CHANGELOG digests --t The change annotation type (release, feature, bugfix, dependency, announcement) --d The description of the change annotation, must be a string or a valid markdown list block --ni Non-Interactive mode - -changelog ls - -changelog edit - -changelog view -``` - -# Examples - -## Create an annotation for modules that were changed in a specific commit - -1. Determine the git commit ID for the change you wish to annotate. - ``` - $ git log --oneline - e22f8f0948 Update API clients from latest models (#1250) - ``` -1. Use the changelog CLI's `create` verb to - ``` - changelog create -c e22f8f0948 - ``` -1. By default, the CLI will prompt you interactively via text editor (vim by default) -1. Adjust the `type`, `description`, and `modules` fields by populating them into the provided TOML template. -1. Once editing is completed save the file and exit the editor - -## Create an annotation for modules that were changed over a commit range - -1. Determine the git commit ID for the change you wish to annotate. - ``` - $ git log --oneline - e22f8f0948 Update API clients from latest models (#1250) - 9b93441d7f service/ec2: Fix generation of number and bool struct members to be pointers (#1195) - ``` -1. Use the changelog CLI's `create` verb to - ``` - $ changelog create -cs 9b93441d7f -ce e22f8f0948 - ``` -1. By default, the CLI will prompt you interactively via text editor (vim by default) -1. Adjust the `type`, `description`, and `modules` fields by populating them into the provided TOML template. -1. Once editing is completed save the file and exit the editor - -## Create an annotation for modules that were changed over a commit range - -1. Determine the git commit ID for the change you wish to annotate. - ``` - $ git log --oneline - e22f8f0948 Update API clients from latest models (#1250) - 9b93441d7f service/ec2: Fix generation of number and bool struct members to be pointers (#1195) - ``` -1. Use the changelog CLI's `create` verb to - ``` - $ changelog create -cs 9b93441d7f -ce e22f8f0948 - ``` -1. By default, the CLI will prompt you interactively via text editor (vim by default) -1. Adjust the `type`, `description`, and `modules` fields by populating them into the provided TOML template. -1. Once editing is completed save the file and exit the editor - -## Create an annotation (non-interactive) - -1. By passing the required annotation parameters and the `-ni` flag to the CLI you can create an annotation without - being prompted interactively using a text editor. - ``` - $ changelog create -ni -type feature -description "addewd new feature foo" service/s3 feature/s3/manager - ``` - -## List Change Annotations - -``` -$ changelog ls -+--------------------------------------+--------+---------+----------+----------------------+ -| ID | TYPE | MODULES | COLLAPSE | DESCRIPTION | -+--------------------------------------+--------+---------+----------+----------------------+ -| 0ba0c6bf-d697-49d1-ac8f-1f6c7f29663e | bugfix | 1 | false | a change description | -+--------------------------------------+--------+---------+----------+----------------------+ -``` - -## View Change Annotation - -``` -$ changelog view 0ba0c6bf-d697-49d1-ac8f-1f6c7f29663e -{ - "id": "0ba0c6bf-d697-49d1-ac8f-1f6c7f29663e", - "type": "bugfix", - "description": "a change description", - "modules": [ - "internal/repotools" - ] -} -``` - -## Remove one or more annotations - -1. Provide one or more annotations identifiers as position arguments the `changelog` - ``` - $ changelog rm - ``` - - -## Remove ALL annotations - -``` -$ changelog rm -all -``` - -[semver]: https://semver.org diff --git a/internal/repotools/cmd/changelog/create.go b/internal/repotools/cmd/changelog/create.go deleted file mode 100644 index a5d6bb6cd65..00000000000 --- a/internal/repotools/cmd/changelog/create.go +++ /dev/null @@ -1,230 +0,0 @@ -package main - -import ( - "errors" - "flag" - "fmt" - "sort" - "strings" - - "github.com/aws/aws-sdk-go-v2/internal/repotools/changelog" - "github.com/aws/aws-sdk-go-v2/internal/repotools/git" - "github.com/aws/aws-sdk-go-v2/internal/repotools/gomod" -) - -const createHelpDoc = `changelog create [-c | (-cs -ce )] [-t ] [-d ] [...] - -Options: --c A commit or tag to generate a change annotation for --cs A starting commit or tag for a change annotation, must be used with -ce to compare changes between two trees --ce An ending commit or tag for a change annotation, must be used with -cs to compare changes between two trees --r Declare that the annotation description should be rolled up as a summary when producing summarized CHANGELOG digests --t The change annotation type (release, feature, bugfix, dependency, announcement) --d The description of the change annotation, must be a string or a valid markdown list block --ni Non-Interactive mode -` - -var createCommand = struct { - Commit string - - CommitStart string - CommitEnd string - - Type ChangeType - - Description string - - Collapse bool - - NonInteractive bool -}{} - -var createFlagSet = func() *flag.FlagSet { - fs := flag.NewFlagSet("create", flag.ExitOnError) - - fs.Usage = func() { - fmt.Fprint(fs.Output(), createHelpDoc) - } - - fs.StringVar(&createCommand.Commit, "c", "", "") - fs.StringVar(&createCommand.CommitStart, "cs", "", "") - fs.StringVar(&createCommand.CommitEnd, "ce", "", "") - fs.Var(&createCommand.Type, "t", "") - fs.StringVar(&createCommand.Description, "d", "", "") - fs.BoolVar(&createCommand.Collapse, "r", false, "") - fs.BoolVar(&createCommand.NonInteractive, "ni", false, "") - - return fs -}() - -func runCreateCommand(args []string, repoRoot string) error { - if err := createFlagSet.Parse(args); err != nil { - return err - } - - discoverer := gomod.NewDiscoverer(repoRoot) - - if err := discoverer.Discover(); err != nil { - return err - } - - modules, err := discoverer.ModulesRel() - if err != nil { - return fmt.Errorf("failed to discover repository go modules: %w", err) - } - - if err := validateCreateCommandArguments(createFlagSet.Args(), modules); err != nil { - return fmt.Errorf("invalid arguments: %w", err) - } - - modulesToAnnotate := make(map[string]struct{}) - - for _, arg := range createFlagSet.Args() { - modulesToAnnotate[arg] = struct{}{} - } - - var commitChanges []string - - if createCommand.Commit != "" { - commitChanges, err = git.Changed(repoRoot, createCommand.Commit) - if err != nil { - return fmt.Errorf("failed to get changed files for commit: %v", err) - } - } else if createCommand.CommitStart != "" && createCommand.CommitEnd != "" { - commitChanges, err = git.Changes(repoRoot, createCommand.CommitStart, createCommand.CommitEnd) - if err != nil { - return fmt.Errorf("failed to get changed files for commit: %v", err) - } - } - - if len(commitChanges) > 0 { - for moduleDir, submodules := range modules { - if isChanged, err := gomod.IsModuleChanged(moduleDir, submodules, commitChanges); err != nil { - return err - } else if isChanged { - modulesToAnnotate[moduleDir] = struct{}{} - } - } - } - - annotation, err := changelog.NewAnnotation() - if err != nil { - return err - } - - annotation.Type = changelog.ChangeType(createCommand.Type) - if annotation.Type == changelog.UnknownChangeType { - annotation.Type = changelog.BugFixChangeType - } - - annotation.Description = createCommand.Description - annotation.Collapse = createCommand.Collapse - - if len(modulesToAnnotate) > 0 { - for moduleDir := range modulesToAnnotate { - annotation.Modules = append(annotation.Modules, moduleDir) - } - } - - sort.Strings(annotation.Modules) - - if createCommand.NonInteractive { - if invalid := validateModules(annotation.Modules, modules); len(invalid) > 0 { - return fmt.Errorf("invalid modules: %v", invalid) - } - - if err = changelog.Validate(annotation); err != nil { - return err - } - } else { - if err = interactiveEdit(&annotation, modules); err != nil { - return err - } - } - - return changelog.WriteAnnotation(repoRoot, annotation) -} - -func interactiveEdit(annotation *changelog.Annotation, modules map[string][]string) error { - var issues []string - - template, err := changelog.AnnotationToTemplate(*annotation) - if err != nil { - return err - } - - filledTemplate, err := editTemplate(template) - if err != nil { - return fmt.Errorf("failed to create change: %v", err) - } - - filledAnnotation, err := changelog.TemplateToAnnotation(filledTemplate) - if err != nil { - return fmt.Errorf("failed to parse annotation: %v", err) - } - - if err = changelog.Validate(filledAnnotation); err != nil { - var ve *changelog.ValidationError - if !errors.As(err, &ve) { - return err - } - issues = append(issues, ve.Issues...) - } - - if invalidModules := validateModules(filledAnnotation.Modules, modules); len(invalidModules) > 0 { - for _, module := range invalidModules { - issues = append(issues, fmt.Sprintf("unknown module: %s", module)) - } - } - - if len(issues) > 0 { - var sb strings.Builder - sb.WriteString("Invalid Template:\n") - for _, issue := range issues { - sb.WriteRune('\t') - sb.WriteString(issue) - sb.WriteRune('\n') - } - return fmt.Errorf(sb.String()) - } - - // Ensure this didn't get swapped / changed - filledAnnotation.ID = annotation.ID - - *annotation = filledAnnotation - - return nil -} - -func validateModules(input []string, modules map[string][]string) (invalid []string) { - for _, module := range input { - if _, ok := modules[module]; !ok { - invalid = append(invalid, module) - } - } - return invalid -} - -func validateCreateCommandArguments(args []string, modules map[string][]string) error { - if createCommand.Commit != "" && (createCommand.CommitStart != "" || createCommand.CommitEnd != "") { - return fmt.Errorf("only -c can not be specified with -cs and -ce") - } - - if (createCommand.CommitStart != "" && createCommand.CommitEnd == "") || - (createCommand.CommitEnd != "" && createCommand.CommitStart == "") { - return fmt.Errorf("-cs must be specified with -ce") - } - - var unknown []string - for _, moduleDir := range args { - if _, ok := modules[moduleDir]; !ok { - unknown = append(unknown, moduleDir) - } - } - - if len(unknown) > 0 { - return fmt.Errorf("unknown modules: %v", unknown) - } - - return nil -} diff --git a/internal/repotools/cmd/changelog/edit.go b/internal/repotools/cmd/changelog/edit.go deleted file mode 100644 index effb224f508..00000000000 --- a/internal/repotools/cmd/changelog/edit.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/aws/aws-sdk-go-v2/internal/repotools/changelog" - "github.com/aws/aws-sdk-go-v2/internal/repotools/gomod" -) - -const editHelpDoc = `changelog edit -` - -var editFlagSet = func() *flag.FlagSet { - fs := flag.NewFlagSet("edit", flag.ExitOnError) - fs.Usage = func() { - fmt.Fprint(fs.Output(), editHelpDoc) - } - return fs -}() - -func runEditCommand(args []string, repoRoot string) error { - if err := editFlagSet.Parse(args); err != nil { - return err - } - - discoverer := gomod.NewDiscoverer(repoRoot) - - if err := discoverer.Discover(); err != nil { - return err - } - - modules, err := discoverer.ModulesRel() - if err != nil { - return fmt.Errorf("failed to discover repository go modules: %w", err) - } - - args = editFlagSet.Args() - if len(args) == 0 || len(args) > 1 { - return fmt.Errorf("expect one annotation id to be provided") - } - - annotation, err := changelog.LoadAnnotation(repoRoot, args[0]) - if err != nil { - return err - } - - if err := interactiveEdit(&annotation, modules); err != nil { - return err - } - - return changelog.WriteAnnotation(repoRoot, annotation) -} diff --git a/internal/repotools/cmd/changelog/editor.go b/internal/repotools/cmd/changelog/editor.go deleted file mode 100644 index f1b63808c29..00000000000 --- a/internal/repotools/cmd/changelog/editor.go +++ /dev/null @@ -1,43 +0,0 @@ -package main - -import ( - "io/ioutil" - "os" - "os/exec" - - "github.com/aws/aws-sdk-go-v2/internal/repotools" -) - -func editTemplate(template []byte) ([]byte, error) { - editor, err := repotools.GetEditorTool() - if err != nil { - return nil, err - } - - f, err := ioutil.TempFile("", "changelog-*.toml") - if err != nil { - return nil, err - } - defer os.Remove(f.Name()) - - _, err = f.Write(template) - if err != nil { - return nil, err - } - - cmd := exec.Command(editor, f.Name()) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err = cmd.Run() - if err != nil { - return nil, err - } - - filledTemplate, err := ioutil.ReadFile(f.Name()) - if err != nil { - return nil, err - } - - return filledTemplate, nil -} diff --git a/internal/repotools/cmd/changelog/list.go b/internal/repotools/cmd/changelog/list.go deleted file mode 100644 index cd9a38540bd..00000000000 --- a/internal/repotools/cmd/changelog/list.go +++ /dev/null @@ -1,44 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/aws/aws-sdk-go-v2/internal/repotools/changelog" - "github.com/olekukonko/tablewriter" - "os" - "strconv" -) - -const listHelpDoc = `changelog ls -` - -var listFlagSet = func() *flag.FlagSet { - fs := flag.NewFlagSet("ls", flag.ExitOnError) - fs.Usage = func() { - fmt.Fprint(fs.Output(), listHelpDoc) - } - return fs -}() - -func runListCommand(args []string, repoRoot string) error { - if err := listFlagSet.Parse(args); err != nil { - return err - } - - annotations, err := changelog.GetAnnotations(repoRoot) - if err != nil { - return err - } - - table := tablewriter.NewWriter(os.Stdout) - - table.SetHeader([]string{"ID", "Type", "Modules", "Collapse", "Description"}) - - for _, annotation := range annotations { - table.Append([]string{annotation.ID, annotation.Type.String(), strconv.Itoa(len(annotation.Modules)), strconv.FormatBool(annotation.Collapse), annotation.Description}) - } - - table.Render() - - return nil -} diff --git a/internal/repotools/cmd/changelog/main.go b/internal/repotools/cmd/changelog/main.go deleted file mode 100644 index 3ddc572c1ef..00000000000 --- a/internal/repotools/cmd/changelog/main.go +++ /dev/null @@ -1,78 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "log" - "os" - "strings" - - "github.com/aws/aws-sdk-go-v2/internal/repotools" - "github.com/aws/aws-sdk-go-v2/internal/repotools/changelog" -) - -// ChangeType is an alias type for changelog.ChangeType to add flag support -type ChangeType changelog.ChangeType - -func (c *ChangeType) String() string { - return changelog.ChangeType(*c).String() -} - -// Set parses c and sets it to the changelog.ChangeType -func (c *ChangeType) Set(s string) error { - ct := changelog.ParseChangeType(s) - *c = ChangeType(ct) - return nil -} - -func init() { - flag.Usage = printHelp -} - -func main() { - flag.Parse() - - repoRoot, err := repotools.GetRepoRoot() - if err != nil { - log.Fatalf("failed to get repository root: %v", err) - } - - arg := flag.Arg(0) - - switch { - case strings.EqualFold(arg, createFlagSet.Name()): - err = runCreateCommand(flag.Args()[1:], repoRoot) - case strings.EqualFold(arg, listFlagSet.Name()): - err = runListCommand(flag.Args()[1:], repoRoot) - case strings.EqualFold(arg, viewFlagSet.Name()): - err = runViewCommand(flag.Args()[1:], repoRoot) - case strings.EqualFold(arg, editFlagSet.Name()): - err = runEditCommand(flag.Args()[1:], repoRoot) - case strings.EqualFold(arg, removeFlagSet.Name()): - err = runRemoveCommand(flag.Args()[1:], repoRoot) - case strings.EqualFold(arg, "help") || len(arg) == 0: - fallthrough - default: - printHelp() - return - } - - if err != nil { - log.Fatal(err) - } -} - -func printHelp() { - var builder strings.Builder - builder.WriteString("Usage:\n\n") - builder.WriteString(createHelpDoc) - builder.WriteRune('\n') - builder.WriteString(listHelpDoc) - builder.WriteRune('\n') - builder.WriteString(editHelpDoc) - builder.WriteRune('\n') - builder.WriteString(viewHelpDoc) - builder.WriteRune('\n') - fmt.Fprint(os.Stderr, builder.String()) - os.Exit(0) -} diff --git a/internal/repotools/cmd/changelog/remove.go b/internal/repotools/cmd/changelog/remove.go deleted file mode 100644 index 1878935c87e..00000000000 --- a/internal/repotools/cmd/changelog/remove.go +++ /dev/null @@ -1,58 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "log" - - "github.com/aws/aws-sdk-go-v2/internal/repotools/changelog" -) - -const removeHelpDoc = `changelog rm (-all | ...) -` - -var removeCommand = struct { - All bool -}{} - -var removeFlagSet = func() *flag.FlagSet { - fs := flag.NewFlagSet("rm", flag.ExitOnError) - fs.Usage = func() { - fmt.Fprint(fs.Output(), editHelpDoc) - } - fs.BoolVar(&removeCommand.All, "all", false, "") - return fs -}() - -func runRemoveCommand(args []string, repoRoot string) error { - if err := removeFlagSet.Parse(args); err != nil { - return err - } - - args = removeFlagSet.Args() - if len(args) == 0 && !removeCommand.All { - return fmt.Errorf("expect either a list ids or -all") - } - - toRemove := make(map[string]struct{}) - for _, id := range args { - toRemove[id] = struct{}{} - } - - annotations, err := changelog.GetAnnotations(repoRoot) - if err != nil { - return err - } - - for _, annotation := range annotations { - _, ok := toRemove[annotation.ID] - if !removeCommand.All && !ok { - continue - } - if err := changelog.RemoveAnnotation(repoRoot, annotation); err != nil { - log.Fatalf("failed to remove annotation: %v", err) - } - } - - return nil -} diff --git a/internal/repotools/cmd/changelog/view.go b/internal/repotools/cmd/changelog/view.go deleted file mode 100644 index 65ce977868c..00000000000 --- a/internal/repotools/cmd/changelog/view.go +++ /dev/null @@ -1,44 +0,0 @@ -package main - -import ( - "encoding/json" - "flag" - "fmt" - "github.com/aws/aws-sdk-go-v2/internal/repotools/changelog" - "os" -) - -const viewHelpDoc = `changelog view -` - -var viewFlagSet = func() *flag.FlagSet { - fs := flag.NewFlagSet("view", flag.ExitOnError) - fs.Usage = func() { - fmt.Fprint(fs.Output(), viewHelpDoc) - } - return fs -}() - -func runViewCommand(args []string, repoRoot string) error { - if err := viewFlagSet.Parse(args); err != nil { - return err - } - - args = viewFlagSet.Args() - - if len(args) == 0 || len(args) > 1 { - return fmt.Errorf("expect one annotation id to be provided") - } - - annotation, err := changelog.LoadAnnotation(repoRoot, args[0]) - if err != nil { - return err - } - - marshal, err := json.MarshalIndent(annotation, "", " ") - if err != nil { - return err - } - _, err = fmt.Fprintf(os.Stdout, "%s\n", marshal) - return err -} diff --git a/internal/repotools/cmd/eachmodule/main.go b/internal/repotools/cmd/eachmodule/main.go index eb3d1ce96ca..4de03793020 100644 --- a/internal/repotools/cmd/eachmodule/main.go +++ b/internal/repotools/cmd/eachmodule/main.go @@ -13,7 +13,7 @@ import ( "strings" "sync" - "github.com/aws/aws-sdk-go-v2/internal/repotools" + repotools "github.com/awslabs/aws-go-multi-module-repository-tools" ) var ( diff --git a/internal/repotools/cmd/endpointPrefix/main.go b/internal/repotools/cmd/endpointPrefix/main.go index db8bf9f1aa8..4055925e8cb 100644 --- a/internal/repotools/cmd/endpointPrefix/main.go +++ b/internal/repotools/cmd/endpointPrefix/main.go @@ -10,7 +10,7 @@ import ( "regexp" "strings" - "github.com/aws/aws-sdk-go-v2/internal/repotools" + repotools "github.com/awslabs/aws-go-multi-module-repository-tools" ) var ( diff --git a/internal/repotools/cmd/generatechangelog/main.go b/internal/repotools/cmd/generatechangelog/main.go deleted file mode 100644 index bf8f98a765c..00000000000 --- a/internal/repotools/cmd/generatechangelog/main.go +++ /dev/null @@ -1,169 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "flag" - "io" - "io/ioutil" - "log" - "os" - "path/filepath" - "sort" - - "github.com/aws/aws-sdk-go-v2/internal/repotools" - "github.com/aws/aws-sdk-go-v2/internal/repotools/changelog" - "github.com/aws/aws-sdk-go-v2/internal/repotools/release" -) - -const changeLogFile = "CHANGELOG.md" - -var releaseManifestFile, summaryNotesFile string - -func init() { - flag.StringVar(&releaseManifestFile, "release", "", "release manifest file") - flag.StringVar(&summaryNotesFile, "o", "", "indicates that a copy of the changelog notes should be written to the target file") -} - -func main() { - flag.Parse() - - if len(releaseManifestFile) == 0 { - log.Fatalln("first argument should be a release manifest file") - } - - manifest, err := loadManifest(releaseManifestFile) - if err != nil { - log.Fatalf("failed to load release manifest file: %v", err) - } - - cwd, err := os.Getwd() - if err != nil { - log.Fatalf("failed to get current working directory: %v", err) - } - - repoRoot, err := repotools.FindRepoRoot(cwd) - if err != nil { - log.Fatalf("failed to get git repository root: %v", err) - } - - annotations, err := changelog.GetAnnotations(repoRoot) - if err != nil { - log.Fatalf("failed to get change annotations: %v", err) - } - - annotations = filterUnreferencedAnnotations(manifest, annotations) - - summary, err := generateSummary(manifest, annotations) - if err != nil { - log.Fatalf("failed to generate summary: %v", err) - } - - if err := writeRepoChangeLogEntry(repoRoot, summary); err != nil { - log.Fatalf("failed to write summary CHANGELOG.md") - } - - for moduleDir, ms := range summary.Modules { - if moduleDir == "." { - // The root module contains the repository changelog - continue - } - if err = writeModuleChangeLog(filepath.Join(repoRoot, moduleDir), ms); err != nil { - log.Fatalf("failed to write module CHANGELOG.md: %v", err) - } - } - - if len(summaryNotesFile) > 0 { - if err := writeSummaryNotes(summaryNotesFile, summary); err != nil { - log.Fatalf("failed to write summary notes: %v", err) - } - } -} - -func copyToTempFile(name string) (io.ReadSeeker, func() error, error) { - if _, err := os.Stat(name); err != nil && os.IsNotExist(err) { - return bytes.NewReader(nil), func() error { - return nil - }, nil - } else if err != nil { - return nil, nil, err - } - - t, err := ioutil.TempFile("", "CHANGELOG-*.md") - if err != nil { - return nil, nil, err - } - cleanup := func() error { - return os.Remove(t.Name()) - } - - f, err := os.Open(name) - if err != nil { - _ = cleanup() - return nil, nil, err - } - defer f.Close() - - _, err = io.Copy(t, f) - if err != nil { - _ = cleanup() - return nil, nil, err - } - - if _, err := t.Seek(0, io.SeekStart); err != nil { - _ = cleanup() - return nil, nil, err - } - - return t, cleanup, nil -} - -func filterUnreferencedAnnotations(manifest release.Manifest, annotations []changelog.Annotation) (filtered []changelog.Annotation) { - referenced := make(map[string]struct{}) - - for _, module := range manifest.Modules { - for _, id := range module.Annotations { - referenced[id] = struct{}{} - } - } - - for _, annotation := range annotations { - if _, ok := referenced[annotation.ID]; ok { - filtered = append(filtered, annotation) - } else { - log.Printf("[WARN] Annotation %v will be ignored as it does not annotate any module changes", annotation.ID) - } - } - - return filtered -} - -func loadManifest(path string) (v release.Manifest, err error) { - fb, err := ioutil.ReadFile(path) - if err != nil { - return release.Manifest{}, err - } - - if err = json.Unmarshal(fb, &v); err != nil { - return release.Manifest{}, err - } - - return v, nil -} - -// sortAnnotations sorts from their highest numerical order to lowest -func sortAnnotations(annotations []changelog.Annotation) { - sort.Slice(annotations, func(i, j int) bool { - if annotations[i].Type < annotations[j].Type { - return false - } - if annotations[i].Type != annotations[j].Type { - return true - } - return annotations[i].Description < annotations[j].Description - }) -} - -func inlineCodeBlock(v string) string { - return "`" + v + "`" -} diff --git a/internal/repotools/cmd/generatechangelog/main_test.go b/internal/repotools/cmd/generatechangelog/main_test.go deleted file mode 100644 index ca48080a439..00000000000 --- a/internal/repotools/cmd/generatechangelog/main_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package main - -import ( - "github.com/aws/aws-sdk-go-v2/internal/repotools/changelog" - "github.com/aws/aws-sdk-go-v2/internal/repotools/release" - "github.com/google/go-cmp/cmp" - "testing" -) - -func Test_sortAnnotations(t *testing.T) { - annotations := []changelog.Annotation{ - {Type: changelog.DependencyChangeType, Description: "b"}, - {Type: changelog.DependencyChangeType, Description: "a"}, - {Type: changelog.DocumentationChangeType, Description: "c"}, - {Type: changelog.BugFixChangeType, Description: "d"}, - {Type: changelog.FeatureChangeType, Description: "e"}, - {Type: changelog.ReleaseChangeType, Description: "f"}, - {Type: changelog.AnnouncementChangeType, Description: "g"}, - } - - want := []changelog.Annotation{ - {Type: changelog.AnnouncementChangeType, Description: "g"}, - {Type: changelog.ReleaseChangeType, Description: "f"}, - {Type: changelog.FeatureChangeType, Description: "e"}, - {Type: changelog.BugFixChangeType, Description: "d"}, - {Type: changelog.DocumentationChangeType, Description: "c"}, - {Type: changelog.DependencyChangeType, Description: "a"}, - {Type: changelog.DependencyChangeType, Description: "b"}, - } - - sortAnnotations(annotations) - - if diff := cmp.Diff(annotations, want); len(diff) > 0 { - t.Error(diff) - } -} - -func Test_filterUnreferencedAnnotations(t *testing.T) { - manifest := release.Manifest{ - Modules: map[string]release.ModuleManifest{ - "foo": { - Annotations: []string{"a", "c"}, - }, - "bar": { - Annotations: []string{"c"}, - }, - }, - } - - annotations := []changelog.Annotation{{ID: "a"}, {ID: "b"}, {ID: "c"}} - wantFiltered := []changelog.Annotation{{ID: "a"}, {ID: "c"}} - - gotFiltered := filterUnreferencedAnnotations(manifest, annotations) - - if diff := cmp.Diff(wantFiltered, gotFiltered); len(diff) > 0 { - t.Error(diff) - } -} diff --git a/internal/repotools/cmd/generatechangelog/summary.go b/internal/repotools/cmd/generatechangelog/summary.go deleted file mode 100644 index 010af284b0c..00000000000 --- a/internal/repotools/cmd/generatechangelog/summary.go +++ /dev/null @@ -1,195 +0,0 @@ -package main - -import ( - "io" - "os" - "path/filepath" - - "github.com/aws/aws-sdk-go-v2/internal/repotools/changelog" - "github.com/aws/aws-sdk-go-v2/internal/repotools/release" -) - -func executeModuleTemplate(wr io.Writer, summary moduleSummary) error { - return moduleChangeLogTemplate.Execute(wr, summary) -} - -func writeModuleChangeLog(path string, summary moduleSummary) error { - changelogPath := filepath.Join(path, changeLogFile) - - t, cleanup, err := copyToTempFile(changelogPath) - if err != nil { - return err - } - defer func() { - cErr := cleanup() - if err == nil && cErr != nil { - err = cErr - } - }() - - f, err := os.OpenFile(changelogPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) - if err != nil { - return err - } - defer func() { - fErr := f.Close() - if err == nil && fErr != nil { - err = fErr - } - }() - - if err = executeModuleTemplate(f, summary); err != nil { - return err - } - - if _, err = io.Copy(f, t); err != nil { - return err - } - - return nil -} - -func executeRepoChangeLogEntryTemplate(wr io.Writer, summary releaseSummary) error { - return repoChangeLogTemplate.ExecuteTemplate(wr, "entry", summary) -} - -func writeRepoChangeLogEntry(path string, summary releaseSummary) (err error) { - changelogPath := filepath.Join(path, changeLogFile) - - t, cleanup, err := copyToTempFile(changelogPath) - if err != nil { - return err - } - defer func() { - cErr := cleanup() - if err == nil && cErr != nil { - err = cErr - } - }() - - f, err := os.OpenFile(changelogPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) - if err != nil { - return err - } - defer func() { - fErr := f.Close() - if err == nil && fErr != nil { - err = fErr - } - }() - - if err = executeRepoChangeLogEntryTemplate(f, summary); err != nil { - return err - } - - if _, err = io.Copy(f, t); err != nil { - return err - } - - return nil -} - -func executeSummaryNotesTemplate(wr io.Writer, summary releaseSummary) error { - return repoChangeLogTemplate.ExecuteTemplate(wr, "summary", summary) -} - -func writeSummaryNotes(path string, summary releaseSummary) (err error) { - f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644) - if err != nil { - return err - } - defer func() { - fErr := f.Close() - if err == nil && fErr != nil { - err = fErr - } - }() - - if err = executeSummaryNotesTemplate(f, summary); err != nil { - return err - } - - return nil -} - -type moduleSummary struct { - ReleaseID string - ModulePath string - Version string - Annotations []changelog.Annotation -} - -type releaseSummary struct { - ReleaseID string - General []changelog.Annotation - Modules map[string]moduleSummary -} - -func (r releaseSummary) IsEmptyReleaseSummary() bool { - if len(r.General) != 0 { - return false - } - - for _, summary := range r.Modules { - if len(summary.Annotations) > 0 { - return false - } - } - - return true -} - -var dependencyBump = changelog.Annotation{ - Type: changelog.DependencyChangeType, - Collapse: true, - Description: "Updated to the latest SDK module versions", -} - -func generateSummary(manifest release.Manifest, annotations []changelog.Annotation) (releaseSummary, error) { - summary := releaseSummary{ - ReleaseID: manifest.ID, - Modules: make(map[string]moduleSummary), - } - - idToAnnotation := make(map[string]changelog.Annotation) - for _, annotation := range annotations { - idToAnnotation[annotation.ID] = annotation - } - - hasDependencyBumps := false - - for modDir, mod := range manifest.Modules { - ms := moduleSummary{ - ReleaseID: manifest.ID, - ModulePath: mod.ModulePath, - Version: mod.To, - } - for _, id := range mod.Annotations { - an, ok := idToAnnotation[id] - if !ok { - continue - } - ms.Annotations = append(ms.Annotations, an) - } - if mod.Changes&release.DependencyUpdate != 0 { - ms.Annotations = append(ms.Annotations, dependencyBump) - hasDependencyBumps = true - } - sortAnnotations(ms.Annotations) - summary.Modules[modDir] = ms - } - - for _, annotation := range annotations { - if annotation.Collapse { - summary.General = append(summary.General, annotation) - } - } - - if hasDependencyBumps { - summary.General = append(summary.General, dependencyBump) - } - - sortAnnotations(summary.General) - - return summary, nil -} diff --git a/internal/repotools/cmd/generatechangelog/summary_test.go b/internal/repotools/cmd/generatechangelog/summary_test.go deleted file mode 100644 index 4aa1a3c12e8..00000000000 --- a/internal/repotools/cmd/generatechangelog/summary_test.go +++ /dev/null @@ -1,521 +0,0 @@ -package main - -import ( - "bytes" - "github.com/aws/aws-sdk-go-v2/internal/repotools/changelog" - "github.com/aws/aws-sdk-go-v2/internal/repotools/release" - "github.com/google/go-cmp/cmp" - "testing" -) - -func Test_executeModuleTemplate(t *testing.T) { - tests := map[string]struct { - summary moduleSummary - wantWr string - wantErr bool - }{ - "no annotations": { - summary: moduleSummary{ - ReleaseID: "2021-05-05", - Version: "v1.0.0", - }, - wantWr: `# v1.0.0 (2021-05-05) - -* No change notes available for this release. - -`, - }, - - "annotations": { - summary: moduleSummary{ - ReleaseID: "2021-05-05", - Version: "v1.0.0", - Annotations: func() (v []changelog.Annotation) { - v = []changelog.Annotation{ - { - Type: changelog.DependencyChangeType, - Description: "Updated foo to a new version.", - }, - { - Type: changelog.DocumentationChangeType, - Description: "Fixed a documentation bug.", - }, - { - Type: changelog.BugFixChangeType, - Description: "Fixed a broken thing.", - }, - { - Type: changelog.FeatureChangeType, - Description: "A new fancy feature.", - }, - { - Type: changelog.ReleaseChangeType, - Description: "New stable version.", - }, - { - Type: changelog.AnnouncementChangeType, - Description: "Something you should know.", - }, - } - sortAnnotations(v) - return v - }(), - }, - wantWr: `# v1.0.0 (2021-05-05) - -* **Announcement**: Something you should know. -* **Release**: New stable version. -* **Feature**: A new fancy feature. -* **Bug Fix**: Fixed a broken thing. -* **Documentation**: Fixed a documentation bug. -* **Dependency Update**: Updated foo to a new version. - -`, - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - wr := bytes.NewBuffer(nil) - err := executeModuleTemplate(wr, tt.summary) - if (err != nil) != tt.wantErr { - t.Errorf("executeModuleTemplate() error = %v, wantErr %v", err, tt.wantErr) - return - } - gotWr := wr.String() - if diff := cmp.Diff(tt.wantWr, gotWr); len(diff) > 0 { - t.Errorf(diff) - } - }) - } -} - -func Test_executeRepoChangeLogEntryTemplate(t *testing.T) { - tests := map[string]struct { - summary releaseSummary - wantWr string - wantErr bool - }{ - "general highlights and module highlights": { - summary: releaseSummary{ - ReleaseID: "2021-05-05", - General: func() (v []changelog.Annotation) { - v = []changelog.Annotation{ - { - Type: changelog.FeatureChangeType, - Collapse: true, - Description: "a", - }, - } - sortAnnotations(v) - return v - }(), - Modules: map[string]moduleSummary{ - ".": { - ReleaseID: "2021-05-05", - ModulePath: "a/b", - Version: "v1.1.0", - Annotations: []changelog.Annotation{ - { - Type: changelog.FeatureChangeType, - Collapse: true, - Description: "a", - }, - { - Type: changelog.FeatureChangeType, - Description: "b", - }, - }, - }, - "c/d": { - ReleaseID: "2021-05-05", - ModulePath: "a/b/c/d", - Version: "v1.1.0", - Annotations: []changelog.Annotation{ - { - Type: changelog.FeatureChangeType, - Collapse: true, - Description: "a", - }, - }, - }, - "e/f": { - ReleaseID: "2021-05-05", - ModulePath: "a/b/e/f", - Version: "v1.0.1", - Annotations: []changelog.Annotation{ - { - Type: changelog.BugFixChangeType, - Description: "c", - }, - }, - }, - }, - }, - wantWr: `# Release (2021-05-05) - -## General Highlights -* **Feature**: a - -## Module Highlights -* ` + "`a/b`" + `: v1.1.0 - * **Feature**: b -* ` + "`a/b/e/f`" + `: [v1.0.1](e/f/CHANGELOG.md#v101-2021-05-05) - * **Bug Fix**: c - -`, - }, - "general highlights only": { - summary: releaseSummary{ - ReleaseID: "2021-05-05", - General: func() (v []changelog.Annotation) { - v = []changelog.Annotation{ - { - Type: changelog.FeatureChangeType, - Collapse: true, - Description: "a", - }, - } - sortAnnotations(v) - return v - }(), - Modules: map[string]moduleSummary{ - ".": { - ReleaseID: "2021-05-05", - ModulePath: "a/b", - Version: "v1.1.0", - Annotations: []changelog.Annotation{ - { - Type: changelog.FeatureChangeType, - Collapse: true, - Description: "a", - }, - }, - }, - }, - }, - wantWr: `# Release (2021-05-05) - -## General Highlights -* **Feature**: a - -`, - }, - "module highlights only": { - summary: releaseSummary{ - ReleaseID: "2021-05-05", - Modules: map[string]moduleSummary{ - "c/d": { - ReleaseID: "2021-05-05", - ModulePath: "a/b/c/d", - Version: "v1.1.0", - Annotations: []changelog.Annotation{ - { - Type: changelog.FeatureChangeType, - Description: "a", - }, - }, - }, - }, - }, - wantWr: `# Release (2021-05-05) - -## Module Highlights -* ` + "`a/b/c/d`" + `: [v1.1.0](c/d/CHANGELOG.md#v110-2021-05-05) - * **Feature**: a - -`, - }, - "no release notes": { - summary: releaseSummary{ - ReleaseID: "2021-05-05", - Modules: map[string]moduleSummary{ - "c/d": { - ReleaseID: "2021-05-05", - ModulePath: "a/b/c/d", - Version: "v1.1.0", - }, - }, - }, - wantWr: `# Release (2021-05-05) - -* No change notes available for this release. - -`, - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - wr := &bytes.Buffer{} - err := executeRepoChangeLogEntryTemplate(wr, tt.summary) - if (err != nil) != tt.wantErr { - t.Errorf("executeRepoChangeLogEntryTemplate() error = %v, wantErr %v", err, tt.wantErr) - return - } - if diff := cmp.Diff(tt.wantWr, wr.String()); len(diff) > 0 { - t.Error(diff) - } - }) - } -} - -func Test_executeSummaryNotesTemplate(t *testing.T) { - tests := map[string]struct { - summary releaseSummary - wantWr string - wantErr bool - }{ - "general highlights and module highlights": { - summary: releaseSummary{ - ReleaseID: "2021-05-05", - General: func() (v []changelog.Annotation) { - v = []changelog.Annotation{ - { - Type: changelog.FeatureChangeType, - Collapse: true, - Description: "a", - }, - } - sortAnnotations(v) - return v - }(), - Modules: map[string]moduleSummary{ - ".": { - ReleaseID: "2021-05-05", - ModulePath: "a/b", - Version: "v1.1.0", - Annotations: []changelog.Annotation{ - { - Type: changelog.FeatureChangeType, - Collapse: true, - Description: "a", - }, - { - Type: changelog.FeatureChangeType, - Description: "b", - }, - }, - }, - "c/d": { - ReleaseID: "2021-05-05", - ModulePath: "a/b/c/d", - Version: "v1.1.0", - Annotations: []changelog.Annotation{ - { - Type: changelog.FeatureChangeType, - Collapse: true, - Description: "a", - }, - }, - }, - "e/f": { - ReleaseID: "2021-05-05", - ModulePath: "a/b/e/f", - Version: "v1.0.1", - Annotations: []changelog.Annotation{ - { - Type: changelog.BugFixChangeType, - Description: "c", - }, - }, - }, - }, - }, - wantWr: `## General Highlights -* **Feature**: a - -## Module Highlights -* ` + "`a/b`" + `: v1.1.0 - * **Feature**: b -* ` + "`a/b/e/f`" + `: [v1.0.1](e/f/CHANGELOG.md#v101-2021-05-05) - * **Bug Fix**: c - -`, - }, - "general highlights only": { - summary: releaseSummary{ - ReleaseID: "2021-05-05", - General: func() (v []changelog.Annotation) { - v = []changelog.Annotation{ - { - Type: changelog.FeatureChangeType, - Collapse: true, - Description: "a", - }, - } - sortAnnotations(v) - return v - }(), - Modules: map[string]moduleSummary{ - ".": { - ReleaseID: "2021-05-05", - ModulePath: "a/b", - Version: "v1.1.0", - Annotations: []changelog.Annotation{ - { - Type: changelog.FeatureChangeType, - Collapse: true, - Description: "a", - }, - }, - }, - }, - }, - wantWr: `## General Highlights -* **Feature**: a - -`, - }, - "module highlights only": { - summary: releaseSummary{ - ReleaseID: "2021-05-05", - Modules: map[string]moduleSummary{ - "c/d": { - ReleaseID: "2021-05-05", - ModulePath: "a/b/c/d", - Version: "v1.1.0", - Annotations: []changelog.Annotation{ - { - Type: changelog.FeatureChangeType, - Description: "a", - }, - }, - }, - }, - }, - wantWr: `## Module Highlights -* ` + "`a/b/c/d`" + `: [v1.1.0](c/d/CHANGELOG.md#v110-2021-05-05) - * **Feature**: a - -`, - }, - "no release notes": { - summary: releaseSummary{ - ReleaseID: "2021-05-05", - Modules: map[string]moduleSummary{ - "c/d": { - ReleaseID: "2021-05-05", - ModulePath: "a/b/c/d", - Version: "v1.1.0", - }, - }, - }, - wantWr: `* No change notes available for this release. - -`, - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - wr := &bytes.Buffer{} - err := executeSummaryNotesTemplate(wr, tt.summary) - if (err != nil) != tt.wantErr { - t.Errorf("executeSummaryNotesTemplate() error = %v, wantErr %v", err, tt.wantErr) - return - } - if diff := cmp.Diff(tt.wantWr, wr.String()); len(diff) > 0 { - t.Error(diff) - } - }) - } -} - -func Test_generateSummary(t *testing.T) { - tests := map[string]struct { - manifest release.Manifest - annotations []changelog.Annotation - want releaseSummary - wantErr bool - }{ - "summary": { - manifest: release.Manifest{ - ID: "2021-05-05", - Modules: map[string]release.ModuleManifest{ - ".": { - ModulePath: "a/b", - From: "v1.0.0", - To: "v1.0.1", - Changes: release.SourceChange, - Annotations: []string{"A"}, - }, - "c/d": { - ModulePath: "a/b/c/d", - From: "v1.0.0", - To: "v1.1.0", - Changes: release.SourceChange | release.DependencyUpdate, - Annotations: []string{"A", "B"}, - }, - }, - }, - annotations: []changelog.Annotation{ - { - ID: "B", - Collapse: true, - Type: changelog.FeatureChangeType, - Description: "B feature", - }, - { - ID: "A", - Type: changelog.AnnouncementChangeType, - Description: "An Announcement", - }, - }, - want: releaseSummary{ - ReleaseID: "2021-05-05", - General: []changelog.Annotation{ - { - ID: "B", - Collapse: true, - Type: changelog.FeatureChangeType, - Description: "B feature", - }, - dependencyBump, - }, - Modules: map[string]moduleSummary{ - ".": { - ReleaseID: "2021-05-05", - ModulePath: "a/b", - Version: "v1.0.1", - Annotations: []changelog.Annotation{ - { - ID: "A", - Type: changelog.AnnouncementChangeType, - Description: "An Announcement", - }, - }, - }, - "c/d": { - ReleaseID: "2021-05-05", - ModulePath: "a/b/c/d", - Version: "v1.1.0", - Annotations: []changelog.Annotation{ - { - ID: "A", - Type: changelog.AnnouncementChangeType, - Description: "An Announcement", - }, - { - ID: "B", - Collapse: true, - Type: changelog.FeatureChangeType, - Description: "B feature", - }, - dependencyBump, - }, - }, - }, - }, - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - got, err := generateSummary(tt.manifest, tt.annotations) - if (err != nil) != tt.wantErr { - t.Errorf("generateSummary() error = %v, wantErr %v", err, tt.wantErr) - return - } - if diff := cmp.Diff(tt.want, got); len(diff) > 0 { - t.Error(diff) - } - }) - } -} diff --git a/internal/repotools/cmd/generatechangelog/template.go b/internal/repotools/cmd/generatechangelog/template.go deleted file mode 100644 index 4f5c78a3a2b..00000000000 --- a/internal/repotools/cmd/generatechangelog/template.go +++ /dev/null @@ -1,78 +0,0 @@ -package main - -import ( - "fmt" - "strings" - "text/template" -) - -var repoChangeLogTemplate = template.Must(template.New("repoChangeLog"). - Funcs(map[string]interface{}{ - "inlineCodeBlock": inlineCodeBlock, - "modulesForHighlight": func(v map[string]moduleSummary) (f map[string]moduleSummary) { - f = make(map[string]moduleSummary) - for modDir, summary := range v { - for i := range summary.Annotations { - if !summary.Annotations[i].Collapse { - f[modDir] = summary - } - } - } - return f - }, - "changeLogLink": func(relModDir, version string, releaseID string) string { - if relModDir == "." { - return version - } - - lv := strings.ReplaceAll(version, ".", "") - lr := strings.ReplaceAll(releaseID, ".", "") - - return fmt.Sprintf("[%s](%s/CHANGELOG.md#%s)", version, relModDir, strings.ToLower(lv+"-"+lr)) - }, - }). - Parse(`{{ define "entry" -}} -# Release ({{ .ReleaseID }}) - -{{ template "summary" . -}} -{{ end }}{{/* template */}} -{{ define "summary" -}} -{{ if (not .IsEmptyReleaseSummary) -}} -{{ if (gt (len .General) 0) -}} -## General Highlights -{{ range $_, $a := .General -}} -* **{{ $a.Type.ChangelogPrefix }}**: {{ $a.Description }} -{{ end }}{{/* range */}} -{{ end -}}{{/* if */ -}} -{{ $mh := (modulesForHighlight .Modules) -}} -{{ if (gt (len $mh) 0) -}} -## Module Highlights -{{ range $name, $mod := $mh -}} -* {{ inlineCodeBlock $mod.ModulePath }}: {{ changeLogLink $name $mod.Version $mod.ReleaseID }} -{{ range $_, $a := $mod.Annotations -}} -{{- if (not $a.Collapse) }} * **{{ $a.Type.ChangelogPrefix }}**: {{ $a.Description }} -{{ end -}}{{/* if */ -}} -{{ end -}}{{/* range */ -}} -{{ end }}{{/* range */}} -{{ end -}}{{/* if */ -}} -{{ else -}}{{/* if */ -}} -* No change notes available for this release. - -{{ end -}}{{/* if * */ -}} -{{ end -}}{{/* define */ -}} -`)) - -var moduleChangeLogTemplate = template.Must(template.New("moduleChangeLog"). - Funcs(map[string]interface{}{ - "inlineCodeBlock": inlineCodeBlock, - }). - Parse(`# {{ .Version }} ({{ .ReleaseID }}) - -{{ if (len .Annotations) -}} -{{- range $_, $a := .Annotations -}} -* **{{ $a.Type.ChangelogPrefix }}**: {{ $a.Description }} -{{ end -}} -{{- else -}} -* No change notes available for this release. -{{ end }} -`)) diff --git a/internal/repotools/cmd/gomodgen/main.go b/internal/repotools/cmd/gomodgen/main.go deleted file mode 100644 index 21d866bc964..00000000000 --- a/internal/repotools/cmd/gomodgen/main.go +++ /dev/null @@ -1,225 +0,0 @@ -package main - -import ( - "errors" - "flag" - "fmt" - "io" - "log" - "os" - "path" - "path/filepath" - "strings" - - "github.com/aws/aws-sdk-go-v2/internal/repotools" - "github.com/aws/aws-sdk-go-v2/internal/repotools/gomod" - "github.com/aws/aws-sdk-go-v2/internal/repotools/manifest" - "golang.org/x/mod/modfile" -) - -const manifestFileName = "generated.json" - -var config = struct { - BuildArtifactPath string -}{} - -func init() { - flag.StringVar(&config.BuildArtifactPath, "build", "", "build artifact path") -} - -func main() { - flag.Parse() - - if len(config.BuildArtifactPath) == 0 { - log.Fatalf("expect build artifact path to be provided") - } - - if stat, err := os.Stat(filepath.Join(config.BuildArtifactPath)); err != nil { - log.Fatalf("failed to stat build artifact path: %v", err) - } else if !stat.IsDir() { - log.Fatalf("build artifact path must be a directory") - } - - repoRoot, err := repotools.GetRepoRoot() - if err != nil { - log.Fatalf("failed to get repository root: %v", err) - } - - rootMod, err := gomod.LoadModuleFile(repoRoot, nil, true) - if err != nil { - log.Fatalf("failed to read repo root go module, %v", err) - } - - rootModulePath, err := gomod.GetModulePath(rootMod) - if err != nil { - log.Fatalf("unable to determine repo root module path, %v", err) - } - - av := manifest.SmithyArtifactPaths{} - if err = filepath.Walk(config.BuildArtifactPath, av.Walk); err != nil { - log.Fatalf("failed to walk build artifacts: %v", err) - } - - if len(av) == 0 { - log.Printf("no build artifacts found: %v", err) - return - } - - if err := copyBuildArtifacts(av, rootModulePath, repoRoot); err != nil { - log.Fatalf("failed to copy build artifacts: %v", err) - } -} - -func copyBuildArtifacts(paths []string, rootModulePath string, repoRoot string) error { - for _, artifactPath := range paths { - buildManifest, err := manifest.LoadManifest(filepath.Join(artifactPath, manifestFileName)) - if err != nil { - return fmt.Errorf("failed to load manifest: %w", err) - } - if !strings.HasPrefix(buildManifest.Module, rootModulePath) { - return fmt.Errorf("%v is not a sub-module of %v", buildManifest.Module, rootModulePath) - } - - moduleRelativePath := strings.TrimPrefix(strings.TrimPrefix(buildManifest.Module, rootModulePath), "/") - if moduleRelativePath == "" { - moduleRelativePath = "." - } - - targetPath := filepath.Join(repoRoot, moduleRelativePath) - if err := prepareTargetDirectory(targetPath, buildManifest); err != nil { - return fmt.Errorf("failed to prepare target directory: %w", err) - } - - if err := copyBuildArtifactToPath(artifactPath, targetPath, buildManifest); err != nil { - return fmt.Errorf("failed to copy build artifact to target: %w", err) - } - - generated, err := generateModuleDefinition(buildManifest) - if err != nil { - return fmt.Errorf("failed to generate go module file: %w", err) - } - - err = gomod.WriteModuleFile(targetPath, generated) - if err != nil { - return fmt.Errorf("failed to write go module file: %w", err) - } - } - return nil -} - -func generateModuleDefinition(m manifest.Manifest) (*modfile.File, error) { - mod := modfile.File{ - Syntax: &modfile.FileSyntax{}, - } - - if err := mod.AddModuleStmt(m.Module); err != nil { - return nil, fmt.Errorf("failed to set module path: %v", err) - } - - if err := mod.AddGoStmt(m.Go); err != nil { - return nil, fmt.Errorf("failed to set Go version: %v", err) - } - - for depPath, depVersion := range m.Dependencies { - depPath := path.Clean(depPath) - - if err := mod.AddRequire(depPath, depVersion); err != nil { - return nil, fmt.Errorf("failed to add dependency %v@%v", depPath, depVersion) - } - } - - return &mod, nil -} - -func prepareTargetDirectory(path string, buildManifest manifest.Manifest) error { - if _, err := os.Stat(path); err != nil && os.IsNotExist(err) { - return os.MkdirAll(path, 0755) - } else if err != nil { - return err - } - - targetManifest, err := manifest.LoadManifest(filepath.Join(path, manifestFileName)) - var notFoundErr *manifest.NoManifestFound - if err != nil && !errors.As(err, ¬FoundErr) { - return err - } - - var ( - targetModule string - cleanupList []string - ) - if err == nil { - targetModule = targetManifest.Module - cleanupList = targetManifest.Files - } else { - log.Printf("[WARN] target directory %v is missing generated.json, will only remove files present in build artifact", path) - if ok, err := gomod.IsGoModPresent(path); err != nil { - return err - } else if ok { - moduleFile, err := gomod.LoadModuleFile(path, nil, true) - if err != nil { - return err - } - targetModule, err = gomod.GetModulePath(moduleFile) - if err != nil { - return err - } - - if targetModule != buildManifest.Module { - return fmt.Errorf("target module %v does not match build artifact %v", targetModule, buildManifest.Module) - } - } - cleanupList = buildManifest.Files - } - - for _, fileName := range cleanupList { - filePath := filepath.Join(path, fileName) - if err := os.Remove(filePath); err != nil && !os.IsNotExist(err) { - return fmt.Errorf("failed to remove %v: %w", filePath, err) - } - } - - return nil -} - -func copyBuildArtifactToPath(source, target string, m manifest.Manifest) error { - for _, fp := range m.Files { - sfp := filepath.Join(source, fp) - tfp := filepath.Join(target, fp) - - if err := copyArtifact(sfp, tfp); err != nil { - return err - } - } - return nil -} - -func copyArtifact(sourcePath, targetPath string) (err error) { - dirs, _ := filepath.Split(targetPath) - if len(dirs) != 0 { - err = os.MkdirAll(dirs, 0755) - if err != nil { - return err - } - } - - sourceFile, err := os.Open(sourcePath) - if err != nil { - return err - } - defer sourceFile.Close() - - targetFile, err := os.OpenFile(targetPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644) - if err != nil { - return err - } - defer func() { - fErr := targetFile.Close() - if fErr != nil && err == nil { - err = fErr - } - }() - - _, err = io.Copy(targetFile, sourceFile) - return err -} diff --git a/internal/repotools/cmd/makerelative/main.go b/internal/repotools/cmd/makerelative/main.go deleted file mode 100644 index 43daf48a351..00000000000 --- a/internal/repotools/cmd/makerelative/main.go +++ /dev/null @@ -1,252 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "io" - "log" - "os" - "path/filepath" - "sort" - "strings" - - "github.com/aws/aws-sdk-go-v2/internal/repotools" - "github.com/aws/aws-sdk-go-v2/internal/repotools/gomod" - "golang.org/x/mod/modfile" -) - -func main() { - repoRoot, err := repotools.GetRepoRoot() - if err != nil { - log.Fatalf("failed to get repository root: %v", err) - } - - registry := NewRegistry() - - discoverer := gomod.NewDiscoverer(repoRoot) - if err = discoverer.Discover(); err != nil { - log.Fatalf("failed to discover modules: %v", err) - } - - // Load Discovered Modules into Registry - var modules []string - - for moduleDir := range discoverer.Modules() { - m := registry.MustLoad(moduleDir) - modules = append(modules, m.Module.Mod.Path) - } - - var modulePath string - for len(modules) > 0 { - modulePath, modules = modules[0], modules[1:] - - err = addRelativeReplaces(repoRoot, modulePath, registry) - if err != nil { - log.Fatal(err) - } - } - - for _, module := range registry.Modules() { - if err := module.Write(); err != nil { - log.Fatal(err) - } - } -} - -// Registry is a map of module path to a module -type Registry struct { - dirToModule map[string]*Module - modulePathToDir map[string]string -} - -// NewRegistry returns a new module registry. -func NewRegistry() *Registry { - return &Registry{ - dirToModule: map[string]*Module{}, - modulePathToDir: map[string]string{}, - } -} - -// Modules returns the modules that were registered. -func (r *Registry) Modules() (m []*Module) { - for _, module := range r.dirToModule { - m = append(m, module) - } - return m -} - -// MustGet retrieves the module identified by the given module path. Panics on failure. -func (r *Registry) MustGet(path string) (string, *Module) { - modulePath, module, err := r.Get(path) - if err != nil { - panic(err) - } - return modulePath, module -} - -// Get retrieves the module identified by the give module path. -func (r *Registry) Get(path string) (string, *Module, error) { - dir, ok := r.modulePathToDir[path] - if !ok { - return "", nil, fmt.Errorf("module not found") - } - - module, ok := r.dirToModule[dir] - if !ok { - return "", nil, fmt.Errorf("module missing or not loaded") - } - - return dir, module, nil -} - -// Has returns whether the given module path is in the registry. -func (r *Registry) Has(path string) bool { - _, ok := r.modulePathToDir[path] - return ok -} - -// MustLoad loads or retrieves the Module from the registry for the given path. Panics on failure. -func (r *Registry) MustLoad(dir string) *Module { - module, err := r.Load(dir) - if err != nil { - panic(err) - } - return module -} - -// Load loads or retrieves the Module from the registry for the given directory path. -func (r *Registry) Load(dir string) (module *Module, err error) { - module, ok := r.dirToModule[dir] - if !ok { - m, err := gomod.LoadModuleFile(dir, nil, false) - if err != nil { - return nil, err - } - module = &Module{File: m} - r.dirToModule[dir] = module - r.modulePathToDir[module.Module.Mod.Path] = dir - } - - return module, nil -} - -// Module is a go.mod file that tracks whether modifications have been made. -type Module struct { - *modfile.File - modified bool -} - -// Write writes any pending changes back to the go.mod -func (m *Module) Write() error { - if !m.modified { - return nil - } - - m.Cleanup() - - mb, err := m.Format() - if err != nil { - log.Fatal(err) - } - - file, err := os.OpenFile(m.Syntax.Name, os.O_WRONLY|os.O_TRUNC, 0) - if err != nil { - return err - } - defer file.Close() - - _, err = io.Copy(file, bytes.NewReader(mb)) - if err != nil { - return err - } - - return nil -} - -// AddReplace replaces oldPath with newPath. -func (m *Module) AddReplace(oldPath, oldVers, newPath, newVers string) error { - m.modified = true - return m.File.AddReplace(oldPath, oldVers, newPath, newVers) -} - -type toReplace struct { - ModulePath string - RelativePath string -} - -// addRelativeReplaces takes the given root and submodule paths and adds go.mod replace directives for any sub modules -// that refer to the given root as a dependency. -func addRelativeReplaces(repoRoot, modulePath string, registry *Registry) error { - modDir, mod := registry.MustGet(modulePath) - - modDirToRoot, err := filepath.Rel(modDir, repoRoot) - if err != nil { - return err - } - - var toDrop []string - for _, replace := range mod.Replace { - if !registry.Has(replace.Old.Path) { - continue - } - toDrop = append(toDrop, replace.Old.Path) - } - - for _, drop := range toDrop { - if err := mod.DropReplace(drop, ""); err != nil { - return err - } - } - - seen := make(map[string]struct{}) - var replaces []toReplace - var toProcess []*modfile.Require - var req *modfile.Require - toProcess = append(toProcess, mod.Require...) - - for len(toProcess) > 0 { - req, toProcess = toProcess[0], toProcess[1:] - - if _, ok := seen[req.Mod.Path]; ok { - continue - } else { - seen[req.Mod.Path] = struct{}{} - } - - if !registry.Has(req.Mod.Path) { - continue - } - - reqDir, reqMod := registry.MustGet(req.Mod.Path) - - reqFromRoot, err := filepath.Rel(repoRoot, reqDir) - if err != nil { - return err - } - - relPathToReq := filepath.Join(modDirToRoot, reqFromRoot) - if !strings.HasSuffix(relPathToReq, string(filepath.Separator)) { - relPathToReq += string(filepath.Separator) - } - - replaces = append(replaces, toReplace{ - ModulePath: req.Mod.Path, - RelativePath: relPathToReq, - }) - - toProcess = append(toProcess, reqMod.Require...) - } - - sort.Slice(replaces, func(i, j int) bool { - return replaces[i].ModulePath < replaces[j].ModulePath - }) - - for _, replace := range replaces { - err = mod.AddReplace(replace.ModulePath, "", replace.RelativePath, "") - if err != nil { - return err - } - } - - return nil -} diff --git a/internal/repotools/cmd/tagrelease/main.go b/internal/repotools/cmd/tagrelease/main.go deleted file mode 100644 index 66042f00d89..00000000000 --- a/internal/repotools/cmd/tagrelease/main.go +++ /dev/null @@ -1,76 +0,0 @@ -package main - -import ( - "encoding/json" - "flag" - "fmt" - "io/ioutil" - "log" - - "github.com/aws/aws-sdk-go-v2/internal/repotools" - "github.com/aws/aws-sdk-go-v2/internal/repotools/git" - "github.com/aws/aws-sdk-go-v2/internal/repotools/release" -) - -var releaseFile string - -func init() { - flag.StringVar(&releaseFile, "release", "", "release manifest file path") -} - -func main() { - flag.Parse() - - if len(releaseFile) == 0 { - log.Fatal("release manifest file path must be provided") - } - - repoRoot, err := repotools.GetRepoRoot() - if err != nil { - log.Fatalf("failed to get repository root: %v", err) - } - - manifest, err := loadManifest(releaseFile) - if err != nil { - log.Fatalf("failed to laod manifest: %v", err) - } - - if len(manifest.Tags) == 0 { - log.Println("[INFO] no modules for release") - return - } - - if err = git.Add(repoRoot, "-A", "."); err != nil { - log.Fatalf("failed to add working directory changes: %v", err) - } - - message := fmt.Sprintf("Release %s", manifest.ID) - - if err = git.Commit(repoRoot, message); err != nil { - log.Fatalf("failed to add working directory changes: %v", err) - } - - for _, tag := range manifest.Tags { - if err := git.Tag(repoRoot, tag, message, "HEAD"); err != nil { - log.Fatalf("failed to create tag %v: %v", tag, err) - } - } - - releaseTag := fmt.Sprintf("release-%s", manifest.ID) - if err = git.Tag(repoRoot, releaseTag, message, "HEAD"); err != nil { - log.Fatalf("failed to create release tag %v: %v", releaseTag, err) - } -} - -func loadManifest(path string) (manifest release.Manifest, err error) { - fBytes, err := ioutil.ReadFile(path) - if err != nil { - return release.Manifest{}, err - } - - if err := json.Unmarshal(fBytes, &manifest); err != nil { - return release.Manifest{}, err - } - - return manifest, nil -} diff --git a/internal/repotools/cmd/updatemodulemeta/main.go b/internal/repotools/cmd/updatemodulemeta/main.go deleted file mode 100644 index 4daff7bc7db..00000000000 --- a/internal/repotools/cmd/updatemodulemeta/main.go +++ /dev/null @@ -1,201 +0,0 @@ -package main - -import ( - "encoding/json" - "flag" - "go/parser" - "go/token" - "io/ioutil" - "log" - "os" - "path/filepath" - "strings" - "text/template" - - "github.com/aws/aws-sdk-go-v2/internal/repotools" - "github.com/aws/aws-sdk-go-v2/internal/repotools/git" - "github.com/aws/aws-sdk-go-v2/internal/repotools/gomod" - "github.com/aws/aws-sdk-go-v2/internal/repotools/release" -) - -const metadataFile = "go_module_metadata.go" - -var metadataTemplate = template.Must(template.New("metadata"). - Parse(`// Code generated by internal/repotools/cmd/updatemodulemeta DO NOT EDIT. - -package {{ .Package }} - -// goModuleVersion is the tagged release for this module -const goModuleVersion = {{ printf "%q" .Version }} -`)) - -var releaseFileName string - -func init() { - flag.StringVar(&releaseFileName, "release", "", "release manifest file path") -} - -func main() { - flag.Parse() - - repoRoot, err := repotools.GetRepoRoot() - if err != nil { - log.Fatalf("failed to get repository root: %v", err) - } - - config, err := repotools.LoadConfig(repoRoot) - if err != nil { - log.Fatalf("failed to load repository config: %v", err) - } - - discoverer := gomod.NewDiscoverer(repoRoot) - - if err = discoverer.Discover(); err != nil { - log.Fatalf("failed to discover modules: %v", err) - } - - tags, err := git.Tags(repoRoot) - if err != nil { - log.Fatalf("failed to retrieve git tags: %v", err) - } - - moduleTags := git.ParseModuleTags(tags) - - if len(releaseFileName) > 0 { - manifest, err := loadManifest(releaseFileName) - if err != nil { - log.Fatalf("failed to load release manifest file: %v", err) - } - for _, tag := range manifest.Tags { - moduleTags.Add(tag) - } - } - - modules, err := discoverer.ModulesRel() - if err != nil { - log.Fatalf("failed to get modules list: %v", err) - } - - for modDir := range modules { - cfg := config.Modules[modDir] - dirPath := filepath.Join(repoRoot, modDir) - if len(cfg.MetadataPackage) > 0 { - pkgRel := filepath.Join(modDir, cfg.MetadataPackage) - if gomod.IsSubmodulePath(pkgRel, modules[modDir]) { - log.Fatalf("%s metadata_package location must not be located in a sub-module", modDir) - } - dirPath = filepath.Join(repoRoot, pkgRel) - } - goPackage, err := getModuleGoPackage(dirPath) - if err != nil { - log.Fatalf("failed to determine module go package: %v", err) - } - if len(goPackage) == 0 { - log.Printf("[WARN] unable to determine go package for %v...skipping", modDir) - continue - } - latest, isTagged := moduleTags.Latest(modDir) - - if cfg, ok := config.Modules[modDir]; (ok && cfg.NoTag) || !isTagged { - latest = "tip" - } - - if err := writeModuleMetadata(dirPath, goPackage, latest); err != nil { - log.Fatalf("failed to write module metadata: %v", err) - } - } -} - -func getModuleGoPackage(dir string) (string, error) { - var inspectFile string - { - metaFile := filepath.Join(dir, metadataFile) - if _, err := os.Stat(metaFile); err == nil { - inspectFile = metaFile - } else if !os.IsNotExist(err) { - return "", err - } - } - if len(inspectFile) == 0 { - err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - if err != nil || path == dir { - return err - } - - if len(inspectFile) > 0 { - return nil - } - - if info.IsDir() { - return filepath.SkipDir - } - - name := info.Name() - if strings.HasSuffix(name, ".go") && !strings.HasSuffix(name, "_test.go") { - inspectFile = path - } - - return nil - }) - if err != nil { - return "", err - } - } - - if len(inspectFile) == 0 { - return "", nil - } - - return readGoPackage(inspectFile) -} - -func readGoPackage(path string) (string, error) { - f, err := os.Open(path) - if err != nil { - return "", err - } - defer f.Close() - - parseFile, err := parser.ParseFile(token.NewFileSet(), f.Name(), f, parser.PackageClauseOnly) - if err != nil { - return "", err - } - - return parseFile.Name.Name, nil -} - -type metadata struct { - Package string - Version string -} - -func writeModuleMetadata(dir string, goPackage string, version string) (err error) { - f, err := os.OpenFile(filepath.Join(dir, metadataFile), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) - if err != nil { - return err - } - defer func() { - fErr := f.Close() - if err == nil && fErr != nil { - err = fErr - } - }() - - return metadataTemplate.Execute(f, metadata{ - Package: goPackage, - Version: strings.TrimPrefix(version, "v"), - }) -} - -func loadManifest(path string) (v release.Manifest, err error) { - fb, err := ioutil.ReadFile(path) - if err != nil { - return release.Manifest{}, err - } - - if err = json.Unmarshal(fb, &v); err != nil { - return release.Manifest{}, err - } - - return v, nil -} diff --git a/internal/repotools/cmd/updaterequires/README.md b/internal/repotools/cmd/updaterequires/README.md deleted file mode 100644 index b49a466a41a..00000000000 --- a/internal/repotools/cmd/updaterequires/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Usage - -``` -updaterequires [-force] [-release ] - -Options: --release Uses next computed version tag information from a release manifest to update module dependencies. --force Force can be used to allow the tool to downgrade a dependency to a lower version. - By default a dependency is only updated if the go.mod recorded version is semantically lower. -``` - -# Description - -`updaterequires` uses the local repositories Git tags to update all inter-repository module dependencies to their latest -tags. Additionally. dependency configuration recorded in root repository directory's `modman.toml` will be used to -update external Go module dependencies to a specific version. - -When executing the utility an additional `-release` argument can be provided by entering a path to a release manifest -(Computed using `calculaterelease`). When provided a release manifest, the tool will project the computed tags onto the -existing repository tags, allowing the tool to update Go Module dependencies with the latest tags being considered -available. - -Lastly in the event that a dependency needs to be forced to a particular version that is lower than what is currently -recorded in the `go.mod`, the `-force` flag can be used. The force flag only applies to external dependencies, and -when enabled will update a dependency to the recorded version indicated in `modman.toml` regardless of the `go.mod` -recorded version being semantically higher or lower. diff --git a/internal/repotools/cmd/updaterequires/main.go b/internal/repotools/cmd/updaterequires/main.go deleted file mode 100644 index 12f6eba8342..00000000000 --- a/internal/repotools/cmd/updaterequires/main.go +++ /dev/null @@ -1,96 +0,0 @@ -package main - -import ( - "encoding/json" - "flag" - "fmt" - "github.com/aws/aws-sdk-go-v2/internal/repotools" - "github.com/aws/aws-sdk-go-v2/internal/repotools/git" - "github.com/aws/aws-sdk-go-v2/internal/repotools/gomod" - "github.com/aws/aws-sdk-go-v2/internal/repotools/release" - "io/ioutil" - "log" -) - -var config = struct { - ReleaseManifestPath string - - Force bool -}{} - -func init() { - flag.StringVar(&config.ReleaseManifestPath, "release", "", "file path to a release manifest containing module tags to be released overlayed") - flag.BoolVar(&config.Force, "force", false, "force module versions regardless of the current recorded version") -} - -func main() { - flag.Parse() - - repoRootPath, err := repotools.GetRepoRoot() - if err != nil { - log.Fatalf("failed to get repository details: %v", err) - } - - tags, err := getRepoTags(repoRootPath) - if err != nil { - log.Fatalf("failed to retrieve git tags: %v", err) - } - - dependencies, err := getDependencies(repoRootPath) - if err != nil { - log.Fatalf("failed to load dependency versions: %v", err) - } - - if len(config.ReleaseManifestPath) > 0 { - if err := applyOverlayTags(config.ReleaseManifestPath, tags); err != nil { - log.Fatalf("failed to apply tag overlay: %v", err) - } - } - - if err := gomod.UpdateRequires(repoRootPath, tags, dependencies, config.Force); err != nil { - log.Fatalf("failed to update module dependencies: %v", err) - } -} - -func applyOverlayTags(path string, tags git.ModuleTags) error { - fBytes, err := ioutil.ReadFile(path) - if err != nil { - return fmt.Errorf("failed to read file: %w", err) - } - - var manifest release.Manifest - - if err := json.Unmarshal(fBytes, &manifest); err != nil { - return err - } - - for _, tag := range manifest.Tags { - if len(tag) == 0 { - continue - } - tags.Add(tag) - } - - return nil -} - -func getDependencies(path string) (map[string]string, error) { - loadConfig, err := repotools.LoadConfig(path) - if err != nil { - return nil, err - } - return loadConfig.Dependencies, nil -} - -func getRepoTags(path string) (git.ModuleTags, error) { - if err := git.Fetch(path); err != nil { - return nil, err - } - - tags, err := git.Tags(path) - if err != nil { - return nil, err - } - - return git.ParseModuleTags(tags), nil -} diff --git a/internal/repotools/config.go b/internal/repotools/config.go deleted file mode 100644 index 9f7aafcf61a..00000000000 --- a/internal/repotools/config.go +++ /dev/null @@ -1,74 +0,0 @@ -package repotools - -import ( - "github.com/pelletier/go-toml" - "io" - "io/ioutil" - "os" - "path/filepath" -) - -const toolingConfigFile = "modman.toml" - -// ModuleConfig is the configuration for the repository module -type ModuleConfig struct { - // Indicates that the given module should not be tagged (released) - NoTag bool `toml:"no_tag"` - - // The semver pre-release string for the module - PreRelease string `toml:"pre_release"` - - // The package alternative location relative to the module where the go_module_metadata.go should be written. - // By default this file is written in the location of the module root where the `go.mod` is located. - MetadataPackage string `toml:"metadata_package"` -} - -// Config is a configuration file for describing how modules and dependencies are managed. -type Config struct { - Modules map[string]ModuleConfig `toml:"modules"` - Dependencies map[string]string `toml:"dependencies"` -} - -// LoadConfig loads the tooling configuration file located in the directory path. -func LoadConfig(path string) (Config, error) { - file, err := os.Open(filepath.Join(path, toolingConfigFile)) - if err != nil && os.IsNotExist(err) { - return Config{}, nil - } else if err != nil { - return Config{}, err - } - defer file.Close() - - return ReadConfig(file) -} - -// ReadConfig reads the tooling configuration from the given reader. -func ReadConfig(reader io.Reader) (c Config, err error) { - all, err := ioutil.ReadAll(reader) - if err != nil { - return Config{}, nil - } - - if err = toml.Unmarshal(all, &c); err != nil { - return Config{}, err - } - - return c, nil -} - -// WriteConfig writes the tooling configuration to the given path. -func WriteConfig(path string, config Config) (err error) { - var f *os.File - f, err = os.OpenFile(filepath.Join(path, toolingConfigFile), os.O_CREATE|os.O_TRUNC, 0644) - if err != nil { - return err - } - defer func() { - fErr := f.Close() - if fErr != nil && err == nil { - err = fErr - } - }() - - return toml.NewEncoder(f).Order(toml.OrderAlphabetical).Encode(config) -} diff --git a/internal/repotools/doc.go b/internal/repotools/doc.go new file mode 100644 index 00000000000..7ba9595c1aa --- /dev/null +++ b/internal/repotools/doc.go @@ -0,0 +1,3 @@ +// Package repotools are a collection of tools used by the SDK development and +// release. +package repotools diff --git a/internal/repotools/editor.go b/internal/repotools/editor.go deleted file mode 100644 index eb2e6b8b2c9..00000000000 --- a/internal/repotools/editor.go +++ /dev/null @@ -1,37 +0,0 @@ -package repotools - -import ( - "fmt" - "os" -) - -const defaultEditor = "vim" - -var allowedEditors = map[string]struct{}{ - "vi": {}, - "vim": {}, - "gvim": {}, - "nvim": {}, - "nano": {}, - "edit": {}, - "gedit": {}, - "emacs": {}, -} - -// GetEditorTool returns the editor tool to use for interactive file edits. -func GetEditorTool() (string, error) { - editor := os.Getenv("VISUAL") - if editor == "" { - editor = os.Getenv("EDITOR") - - if editor == "" { - editor = defaultEditor - } - } - - if _, ok := allowedEditors[editor]; !ok { - return "", fmt.Errorf("unknown editor %q not allowed, %v", editor, allowedEditors) - } - - return editor, nil -} diff --git a/internal/repotools/editor_test.go b/internal/repotools/editor_test.go deleted file mode 100644 index 676cf3954ad..00000000000 --- a/internal/repotools/editor_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package repotools - -import ( - "os" - "strings" - "testing" -) - -func TestGetEditorTool(t *testing.T) { - cases := map[string]struct { - SetupEnv func() - ExpectEditor string - ExpectErr string - }{ - "default": { - ExpectEditor: defaultEditor, - }, - "allowed visual": { - SetupEnv: func() { - os.Setenv("VISUAL", `vi`) - }, - ExpectEditor: `vi`, - }, - "allowed editor": { - SetupEnv: func() { - os.Setenv("EDITOR", `emacs`) - }, - ExpectEditor: `emacs`, - }, - "unknown visual": { - SetupEnv: func() { - os.Setenv("VISUAL", `unknownCmd`) - }, - ExpectErr: `unknown editor "unknownCmd"`, - }, - "unknown editor": { - SetupEnv: func() { - os.Setenv("EDITOR", `unknownCmd`) - }, - ExpectErr: `unknown editor "unknownCmd"`, - }, - } - - for name, c := range cases { - t.Run(name, func(t *testing.T) { - origEnv := os.Environ() - os.Clearenv() - defer func(env []string) { - for _, kv := range env { - n := strings.SplitN(kv, "=", 2) - os.Setenv(n[0], n[1]) - } - }(origEnv) - - if c.SetupEnv != nil { - c.SetupEnv() - } - - editor, err := GetEditorTool() - if len(c.ExpectErr) != 0 { - if err == nil { - t.Fatalf("expect error, got none") - } - if e, a := c.ExpectErr, err.Error(); !strings.Contains(a, e) { - t.Fatalf("expect error to contain %v, got %v", e, a) - } - return - } - if err != nil { - t.Fatalf("expect no error, got %v", err) - } - - if e, a := c.ExpectEditor, editor; e != a { - t.Errorf("expect %v editor, got %v", e, a) - } - }) - } -} diff --git a/internal/repotools/git/add.go b/internal/repotools/git/add.go deleted file mode 100644 index bb65dbb4e23..00000000000 --- a/internal/repotools/git/add.go +++ /dev/null @@ -1,13 +0,0 @@ -package git - -// Add invokes git-add in the specified path passing the provided arguments. -func Add(path string, args ...string) error { - arguments := []string{"add"} - - if len(args) > 0 { - arguments = append(arguments, args...) - } - - _, err := Git(path, arguments...) - return err -} diff --git a/internal/repotools/git/commit.go b/internal/repotools/git/commit.go deleted file mode 100644 index 3b2e2b0edd0..00000000000 --- a/internal/repotools/git/commit.go +++ /dev/null @@ -1,8 +0,0 @@ -package git - -// Commit commits the staged contents in the specified repository path using the provided message. -func Commit(path, message string) error { - arguments := []string{"commit", "-m", message} - _, err := Git(path, arguments...) - return err -} diff --git a/internal/repotools/git/diff.go b/internal/repotools/git/diff.go deleted file mode 100644 index b4adc2f25d9..00000000000 --- a/internal/repotools/git/diff.go +++ /dev/null @@ -1,46 +0,0 @@ -package git - -import ( - "strings" -) - -// Changes outputs a list of files for the repository that have changed at the given paths between from and to commit-like references. -// If no paths are provided then all files that changed between the two tree-ish (commit or tag) references are returned. -func Changes(repository string, from, to string, paths ...string) ([]string, error) { - arguments := []string{"diff-tree", "--name-only", "--no-commit-id", "-r", from, to} - - if len(paths) > 0 { - arguments = append(arguments, paths...) - } - - output, err := Git(repository, arguments...) - if err != nil { - return nil, err - } - return splitOutput(string(output)), nil -} - -// Changed reports the list of files that changed for a specific commit or tag. -func Changed(repository string, commit string, paths ...string) ([]string, error) { - arguments := []string{"diff-tree", "--name-only", "--no-commit-id", "-r", commit} - - if len(paths) > 0 { - arguments = append(arguments, paths...) - } - - output, err := Git(repository, arguments...) - if err != nil { - return nil, err - } - return splitOutput(string(output)), nil -} - -func splitOutput(output string) []string { - split := strings.Split(output, "\n") - if len(split) > 0 { - if len(split[len(split)-1]) == 0 { - split = split[:len(split)-1] - } - } - return split -} diff --git a/internal/repotools/git/git.go b/internal/repotools/git/git.go deleted file mode 100644 index 8675bc59593..00000000000 --- a/internal/repotools/git/git.go +++ /dev/null @@ -1,185 +0,0 @@ -package git - -import ( - "fmt" - "os" - "os/exec" - "path" - "sort" - "strconv" - "strings" - - "github.com/aws/aws-sdk-go-v2/internal/repotools/semver" - "golang.org/x/mod/module" -) - -// LsTree lists the files present in the tree-ish for the repository. An optional set of one or more paths can be -// provided to limit the output file paths. -func LsTree(repository, tree string, path ...string) ([]string, error) { - arguments := []string{"ls-tree", "-r", "--name-only", tree} - if len(path) > 0 { - arguments = append(arguments, path...) - } - - output, err := Git(repository, arguments...) - if err != nil { - return nil, err - } - - return splitOutput(string(output)), nil -} - -// Tags returns a slice of Git tags at the repository located at path -func Tags(path string) ([]string, error) { - output, err := Git(path, "tag", "-l") - if err != nil { - return nil, err - } - return splitOutput(string(output)), nil -} - -// Fetch fetches all objects and refs for the Git repository located at path -func Fetch(path string) error { - _, err := Git(path, "fetch", "--all") - return err -} - -// Git executes the git with the provided arguments. The command is executed in the provided -// directory path. -func Git(path string, arguments ...string) (output []byte, err error) { - cmd := exec.Command("git", arguments...) - if len(path) == 0 { - path, err = os.Getwd() - if err != nil { - return nil, err - } - } - cmd.Dir = path - cmd.Env = append(os.Environ(), "PWD="+path) - - return cmd.Output() -} - -// ToModuleTag converts the relative module path and semver version string to a git tag -// that can be used to identify the module version. -// For example: -// Path: . Version: v1.2.3 => v1.2.3 -// Path: service/s3 Version: v0.2.3 => service/s3/v0.2.3 -// Path: service/s3 Version: v1.2.3 => service/s3/v1.2.3 -// Path: service/s3/v2 Version: v2.2.3 => service/s3/v2.2.3 -// Path: service/s3/v3 Version: v2.2.3 => error -func ToModuleTag(modulePath string, version string) (string, error) { - major := semver.Major(version) - if len(major) == 0 { - return "", fmt.Errorf("invalid semantic version: %v", major) - } - - prefix, pathMajor, ok := module.SplitPathVersion(modulePath) - if !ok { - return "", fmt.Errorf("invalid module path version") - } - - if err := module.CheckPathMajor(version, pathMajor); err != nil { - return "", err - } - - return path.Join(prefix, version), nil -} - -// ModuleTags is a map of module paths to a slice of tagged Go semver versions. -// Root module tags will be placed in the map at ".". Major versions > v1 will be -// added to the map with the semver major version appended to the module path. -// -// Versions will be sorted in the slice from highest to lowest by comparing the values -// following semantic versioning rules. -// -// Example: -// . => ["v1.2.3", "v1.0.0"] -// v2 => ["v2.0.0"] -// sub/module => ["v1.2.3"] -// sub/module/v2 => ["v2.2.3"] -// -type ModuleTags map[string][]string - -// Latest returns the latest tag for the given relative module path. Returns false if -// the module version is not known. -func (r ModuleTags) Latest(module string) (string, bool) { - _, ok := r[module] - if !ok { - return "", false - } - return r[module][0], true -} - -// Add adds the given tag to the ModuleTags -func (r ModuleTags) Add(tag string) bool { - module, version, ok := parseTag(tag) - if !ok { - return false - } - - index := sort.Search(len(r[module]), func(i int) bool { - return semver.Compare(version, r[module][i]) >= 0 - }) - - if index < len(r[module]) && index >= 0 { - if semver.Compare(r[module][index], version) == 0 { - return true - } - } - - r[module] = append(r[module], "") - copy(r[module][index+1:], r[module][index:]) - r[module][index] = version - - return true -} - -// ParseModuleTags parses a list of Git tags into a set of ModuleTags. -// Tags that are not semvar compliant with Go will be ignored. -func ParseModuleTags(tags []string) ModuleTags { - modules := make(map[string][]string) - - for _, tag := range tags { - module, version, ok := parseTag(tag) - if !ok { - continue - } - modules[module] = append(modules[module], version) - } - - for _, versions := range modules { - sort.Slice(versions, func(i, j int) bool { - // We want to sort higher versions first - return semver.Compare(versions[i], versions[j]) > 0 - }) - } - - return modules -} - -func parseTag(tag string) (string, string, bool) { - idx := strings.LastIndex(tag, "/") - - module := "." - version := tag - - if idx != -1 { - module = tag[:idx] - version = tag[idx+1:] - } - - if !semver.IsValid(version) { - return "", "", false - } - - major := semver.Major(version) - - majorInt, _ := strconv.Atoi(major[1:]) - - if majorInt > 1 { - module = path.Join(module, major) - } - - return module, version, true -} diff --git a/internal/repotools/git/git_test.go b/internal/repotools/git/git_test.go deleted file mode 100644 index 131e7b0ae1a..00000000000 --- a/internal/repotools/git/git_test.go +++ /dev/null @@ -1,151 +0,0 @@ -package git_test - -import ( - "github.com/aws/aws-sdk-go-v2/internal/repotools/git" - "github.com/google/go-cmp/cmp" - "strconv" - "testing" -) - -func TestToTag(t *testing.T) { - tests := []struct { - Path string - Version string - Expected string - WantErr bool - }{ - { - Path: ".", - Version: "v1.2.3", - Expected: "v1.2.3", - }, - { - Path: "service/s3", - Version: "v0.1.2", - Expected: "service/s3/v0.1.2", - }, - { - Path: "service/s3", - Version: "v1.2.3", - Expected: "service/s3/v1.2.3", - }, - { - Path: "service/s3/v2", - Version: "v2.3.4", - Expected: "service/s3/v2.3.4", - }, - { - Path: "service/s3/volumetric", - Version: "v1.3.4", - Expected: "service/s3/volumetric/v1.3.4", - }, - { - Path: "service/s3/v2", - Version: "v1.3.4", - WantErr: true, - }, - { - Path: "service/s3/v0", - Version: "v1.3.4", - WantErr: true, - }, - { - Path: "service/s3/v1", - Version: "v1.3.4", - WantErr: true, - }, - } - - for i, tt := range tests { - t.Run(strconv.Itoa(i), func(t *testing.T) { - got, err := git.ToModuleTag(tt.Path, tt.Version) - if (err != nil) != tt.WantErr { - t.Errorf("WantErr=%v, got err=%v", tt.WantErr, err) - } - if tt.Expected != got { - t.Errorf("expect %v, got %v", tt.Expected, got) - } - }) - } -} - -func TestParseTags(t *testing.T) { - tests := map[string]struct { - args []string - want git.ModuleTags - }{ - "standard tags": { - args: []string{ - "v0.2.1", - "v1.0.0", - "v1.3.0", - "v2.0.0", - "feature/ec2/imds/v0.1.0", - "feature/ec2/imds/v1.0.1", - "feature/ec2/imds/v1.0.6", - "feature/ec2/imds/v2.0.0", - }, - want: map[string][]string{ - ".": {"v1.3.0", "v1.0.0", "v0.2.1"}, - "v2": {"v2.0.0"}, - "feature/ec2/imds": {"v1.0.6", "v1.0.1", "v0.1.0"}, - "feature/ec2/imds/v2": {"v2.0.0"}, - }, - }, - "invalid tags": { - args: []string{ - "v0.2.1", - "v1.0.0", - "release-1-2021-04-09", - "v1.3.0", - "1.4.0", - "feature/ec2/imds/v0.1.0", - "feature/ec2/imds/v1.0.1", - "feature/ec2/imds/v1.0.6", - "feature/ec2/imds@v1.2.0", - }, - want: map[string][]string{ - ".": {"v1.3.0", "v1.0.0", "v0.2.1"}, - "feature/ec2/imds": {"v1.0.6", "v1.0.1", "v0.1.0"}, - }, - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - got := git.ParseModuleTags(tt.args) - if diff := cmp.Diff(tt.want, got); len(diff) > 0 { - t.Error(diff) - } - }) - } -} - -func TestModuleTags_Add(t *testing.T) { - tags := []string{ - "v0.2.1", - "v1.0.0", - "v1.3.0", - } - - moduleTags := git.ParseModuleTags(tags) - - for _, tag := range []string{"v0.2.1", "v1.3.0", "v0.2.0", "v1.0.0", "v1.4.0", "v2.0.0", "feature/ec2/imds/v1.0.0"} { - if ok := moduleTags.Add(tag); !ok { - t.Errorf("expect tag to have been inserted") - } - } - - if ok := moduleTags.Add("invalid-tag"); ok { - t.Errorf("expected tag to not be inserted") - } - - want := git.ModuleTags{ - ".": {"v1.4.0", "v1.3.0", "v1.0.0", "v0.2.1", "v0.2.0"}, - "v2": {"v2.0.0"}, - "feature/ec2/imds": {"v1.0.0"}, - } - - if diff := cmp.Diff(want, moduleTags); len(diff) > 0 { - t.Errorf(diff) - } -} diff --git a/internal/repotools/git/tag.go b/internal/repotools/git/tag.go deleted file mode 100644 index 8dc193ce03e..00000000000 --- a/internal/repotools/git/tag.go +++ /dev/null @@ -1,9 +0,0 @@ -package git - -// Tag invokes git-tag is the specified path, creating the given annotated tag at the given commit with the -// provided message. -func Tag(path, tag, message string, commit string) error { - arguments := []string{"tag", "-a", "-m", message, tag, commit} - _, err := Git(path, arguments...) - return err -} diff --git a/internal/repotools/go.mod b/internal/repotools/go.mod index f78ee876f12..0bb54086abe 100644 --- a/internal/repotools/go.mod +++ b/internal/repotools/go.mod @@ -2,9 +2,4 @@ module github.com/aws/aws-sdk-go-v2/internal/repotools go 1.15 -require ( - github.com/google/go-cmp v0.5.6 - github.com/olekukonko/tablewriter v0.0.5 - github.com/pelletier/go-toml v1.9.0 - golang.org/x/mod v0.4.2 -) +require github.com/awslabs/aws-go-multi-module-repository-tools v0.0.0-20210920212330-85c4889f37d1 diff --git a/internal/repotools/go.sum b/internal/repotools/go.sum index 7dd34c066a0..d150547ea26 100644 --- a/internal/repotools/go.sum +++ b/internal/repotools/go.sum @@ -1,14 +1,12 @@ -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/awslabs/aws-go-multi-module-repository-tools v0.0.0-20210920212330-85c4889f37d1 h1:iEumauL7ykXr1p14PK7Nr3dXP/hm0e876wpgG4DKO94= +github.com/awslabs/aws-go-multi-module-repository-tools v0.0.0-20210920212330-85c4889f37d1/go.mod h1:JZn9GHm+RwVZ5FxnzmfPxglZL76gPeG8Ls9hCym9cDk= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pelletier/go-toml v1.9.0 h1:NOd0BRdOKpPf0SxkL3HxSQOG7rNh+4kl6PHcBPFs7Q0= github.com/pelletier/go-toml v1.9.0/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -19,5 +17,4 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/repotools/go_module_metadata.go b/internal/repotools/go_module_metadata.go deleted file mode 100644 index 63c74d11222..00000000000 --- a/internal/repotools/go_module_metadata.go +++ /dev/null @@ -1,6 +0,0 @@ -// Code generated by internal/repotools/cmd/updatemodulemeta DO NOT EDIT. - -package repotools - -// goModuleVersion is the tagged release for this module -const goModuleVersion = "tip" diff --git a/internal/repotools/gomod/diff.go b/internal/repotools/gomod/diff.go deleted file mode 100644 index cedf858ee9d..00000000000 --- a/internal/repotools/gomod/diff.go +++ /dev/null @@ -1,71 +0,0 @@ -package gomod - -import ( - "path" - "strings" -) - -// IsModuleChanged returns whether the given set of changes applies to the module. -// The submodules argument is must be lexicographically sorted list of submodule locations that are located -// under moduleDir. -func IsModuleChanged(moduleDir string, submodules []string, changes []string) (bool, error) { - if moduleDir == "." { - moduleDir = "" - } else { - // Append the path separator to ensure it is used in prefix matches. This ensure that we are looking - // at this specific directory and children rather then any directory that has this moduleDir prefix. - moduleDir += "/" - } - - isChildPathCache := make(map[string]bool) - - hasChanges := false - - for i := 0; i < len(changes) && !hasChanges; i++ { - dir, fileName := path.Split(changes[i]) - - if len(dir) == 0 && moduleDir != "" { - continue - } - - if len(moduleDir) > 0 && !strings.HasPrefix(dir, moduleDir) { - continue - } - - if len(dir) == 0 && (IsGoSource(fileName) || IsGoMod(fileName)) { - hasChanges = true - continue - } else if !(IsGoSource(fileName) || IsGoMod(fileName)) { - continue - } - dir = path.Clean(dir) - - if len(submodules) == 0 { - hasChanges = true - continue - } - - if isChild, ok := isChildPathCache[dir]; !ok { - if IsSubmodulePath(dir, submodules) { - isChildPathCache[dir] = true - } else { - isChildPathCache[dir] = false - hasChanges = true - } - } else if !isChild { - hasChanges = true - } - } - - return hasChanges, nil -} - -// IsGoSource returns whether a given file name is a Go source code file ending in `.go` -func IsGoSource(name string) bool { - return !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") -} - -// IsGoMod returns whether a given file name is `go.mod`. -func IsGoMod(name string) bool { - return name == "go.mod" -} diff --git a/internal/repotools/gomod/diff_test.go b/internal/repotools/gomod/diff_test.go deleted file mode 100644 index 9759d9f6f31..00000000000 --- a/internal/repotools/gomod/diff_test.go +++ /dev/null @@ -1,116 +0,0 @@ -package gomod - -import "testing" - -func TestIsModuleChanged(t *testing.T) { - tests := map[string]struct { - moduleDir string - submodules []string - changes []string - want bool - wantErr bool - }{ - "no submodules": { - moduleDir: ".", - changes: []string{ - "sub3/foo.go", - "sub2/bar.go", - "sub1/baz.go", - "foo.go", - }, - want: true, - }, - "no submodules, no go changes": { - moduleDir: ".", - changes: []string{ - "foo.java", - }, - want: false, - }, - "go.mod considered": { - moduleDir: ".", - changes: []string{ - "go.mod", - }, - want: true, - }, - "repo root with submodules": { - moduleDir: ".", - submodules: []string{"sub1", "sub2"}, - changes: []string{ - "sub3/foo.go", - "sub2/bar.go", - "sub1/baz.go", - "foo.go", - }, - want: true, - }, - "submodule directory, no submodules, no changes": { - moduleDir: "sub1", - submodules: nil, - changes: []string{ - "sub3/foo.go", - "sub2/bar.go", - "foo.go", - }, - }, - "submodule directory, no submodules, changes": { - moduleDir: "sub1", - submodules: nil, - changes: []string{ - "sub3/foo.go", - "sub2/bar.go", - "sub1/bar.go", - "foo.go", - }, - want: true, - }, - "submodule directory, submodules, no changes": { - moduleDir: "sub1", - submodules: []string{"sub1/subsub1", "sub1/subsub2"}, - changes: []string{ - "sub3/foo.go", - "sub2/bar.go", - "sub1/subsub1/foo.go", - "sub1/subsub1/bar.go", - "sub1/subsub2/bar.go", - "foo.go", - }, - }, - "submodule directory, submodules, changes": { - moduleDir: "sub1", - submodules: []string{"sub1/subsub1", "sub1/subsub2"}, - changes: []string{ - "sub3/foo.go", - "sub2/bar.go", - "sub1/subsub1/foo.go", - "sub1/subsub1/bar.go", - "sub1/subsub2/bar.go", - "sub1/notsub/foo.go", - "foo.go", - }, - want: true, - }, - "module with no changes, but shares common prefix with a changed module": { - moduleDir: "foobar", - submodules: []string{"foobar/sub1"}, - changes: []string{ - "foobarbaz/baz.go", - "foobar/sub1/sub1.go", - }, - want: false, - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - got, err := IsModuleChanged(tt.moduleDir, tt.submodules, tt.changes) - if (err != nil) != tt.wantErr { - t.Errorf("IsModuleChanged() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("IsModuleChanged() got = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/internal/repotools/gomod/module.go b/internal/repotools/gomod/module.go deleted file mode 100644 index 3f2ee005787..00000000000 --- a/internal/repotools/gomod/module.go +++ /dev/null @@ -1,235 +0,0 @@ -package gomod - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "sort" - "strings" - - "golang.org/x/mod/modfile" -) - -const ( - goModuleFile = "go.mod" - testDataFolder = "testdata" -) - -// GetModulePath retrieves the module path from the provide file description. -func GetModulePath(file *modfile.File) (string, error) { - if file.Module == nil { - return "", fmt.Errorf("module directive not present") - } - return file.Module.Mod.Path, nil -} - -// LoadModuleFile loads the Go module file located at the provided directory path. -func LoadModuleFile(path string, fix modfile.VersionFixer, lax bool) (*modfile.File, error) { - path = filepath.Join(path, goModuleFile) - - f, err := os.Open(path) - if err != nil { - return nil, err - } - defer f.Close() - - return ReadModule(path, f, fix, lax) -} - -// ReadModule parses the module file bytes from the provided reader. -func ReadModule(path string, f io.Reader, fix modfile.VersionFixer, lax bool) (parse *modfile.File, err error) { - fBytes, err := ioutil.ReadAll(f) - if err != nil { - return nil, err - } - - if lax { - parse, err = modfile.ParseLax(path, fBytes, fix) - } else { - parse, err = modfile.Parse(path, fBytes, fix) - } - if err != nil { - return nil, err - } - - return parse, nil -} - -// WriteModuleFile writes the Go module description to the provided directory path. -func WriteModuleFile(path string, file *modfile.File) (err error) { - modPath := filepath.Join(path, goModuleFile) - - var mf *os.File - mf, err = os.OpenFile(modPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) - if err != nil { - return err - } - defer func() { - fErr := mf.Close() - if fErr != nil && err == nil { - err = fErr - } - }() - - var fb []byte - fb, err = file.Format() - if err != nil { - return err - } - - _, err = io.Copy(mf, bytes.NewReader(fb)) - - return err -} - -// Discoverer is used for discovering all modules and submodules at the provided path. -type Discoverer struct { - path string - modules map[string][]string -} - -// NewDiscoverer constructs a new Discover for the given path. -func NewDiscoverer(path string) *Discoverer { - return &Discoverer{ - path: path, - } -} - -// Root returns the root path of the module discovery. -func (d *Discoverer) Root() string { - return d.path -} - -// Modules returns the modules discovered after executing Discover. -func (d *Discoverer) Modules() (v map[string][]string) { - v = make(map[string][]string) - for modulePath, children := range d.modules { - var c []string - if children != nil { - c := make([]string, 0, len(children)) - copy(c, children) - } - v[modulePath] = c - } - return v -} - -// ModulesRel returns the modules discovered after executing Discover. The returned module directory paths -// will be made relative to the provided base path. -func (d *Discoverer) ModulesRel() (v map[string][]string, err error) { - v = make(map[string][]string) - for modulePath, children := range d.modules { - rel, err := filepath.Rel(d.path, modulePath) - if err != nil { - return nil, err - } - var c []string - if len(children) > 0 { - c = make([]string, 0, len(children)) - for i := range children { - rel, err := filepath.Rel(d.path, children[i]) - if err != nil { - return nil, err - } - c = append(c, rel) - } - } - v[rel] = c - } - return v, nil -} - -// Discover will find all modules starting from the path provided when constructing the Discoverer. -// Does not iterate into testdata folders. -func (d *Discoverer) Discover() error { - d.modules = make(map[string][]string) - - present, err := IsGoModPresent(d.path) - if err != nil { - return err - } - - err = filepath.Walk(d.path, d.walkChildModules(d.path, present)) - if err != nil { - return err - } - - for modulePath := range d.modules { - if len(d.modules) > 0 { - sort.Strings(d.modules[modulePath]) - } - } - - return nil -} - -func (d *Discoverer) walkChildModules(parentPath string, isParentModule bool) func(path string, fs os.FileInfo, err error) error { - if isParentModule { - d.modules[parentPath] = nil - } - - return func(path string, fs os.FileInfo, err error) error { - if err != nil || path == parentPath { - return err - } - - if !fs.IsDir() { - return nil - } - - if fs.Name() == testDataFolder { - return filepath.SkipDir - } - - present, err := IsGoModPresent(path) - if err != nil { - return err - } - - if !present { - return nil - } - - if isParentModule { - d.modules[parentPath] = append(d.modules[parentPath], path) - } - - err = filepath.Walk(path, d.walkChildModules(path, true)) - if err != nil { - return err - } - - return filepath.SkipDir - } -} - -// IsGoModPresent returns whether there is a go.mod file located in the provided directory path -func IsGoModPresent(path string) (bool, error) { - _, err := os.Stat(filepath.Join(path, goModuleFile)) - if err != nil && os.IsNotExist(err) { - return false, nil - } else if err != nil { - return false, err - } - return true, nil -} - -// IsSubmodulePath determines if the given path falls within any of the submodules. Submodules MUST be a -// sorted ascending list of paths. -func IsSubmodulePath(path string, submodules []string) bool { - i := sort.Search(len(submodules), func(i int) bool { - return path <= submodules[i] - }) - - // Search returns where we would insert the given path, so we need to check if the returned index - // module matches our path, or if the previous index entry is a prefix to our current directory since - // nested directory paths would be sorted higher lexicographically - if (i < len(submodules) && path == submodules[i]) || i > 0 && strings.HasPrefix(path, submodules[i-1]) { - return true - } - - return false -} diff --git a/internal/repotools/gomod/module_test.go b/internal/repotools/gomod/module_test.go deleted file mode 100644 index 591e002ce49..00000000000 --- a/internal/repotools/gomod/module_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package gomod - -import ( - "strconv" - "testing" -) - -func TestIsSubmodulePath(t *testing.T) { - tests := []struct { - path string - submodules []string - want bool - }{ - { - path: "foo", - want: false, - }, - { - path: "a/b", - submodules: []string{ - "a", - "b", - }, - want: true, - }, - { - path: "b", - submodules: []string{ - "a", - "c", - }, - want: false, - }, - { - path: "a", - submodules: []string{ - "b", - "c", - }, - want: false, - }, - { - path: "a/b", - submodules: []string{ - "a/b/c", - "c", - }, - want: false, - }, - } - for i, tt := range tests { - t.Run(strconv.Itoa(i), func(t *testing.T) { - if got := IsSubmodulePath(tt.path, tt.submodules); got != tt.want { - t.Errorf("IsSubmodulePath() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/internal/repotools/gomod/version.go b/internal/repotools/gomod/version.go deleted file mode 100644 index 1fd1e5642f2..00000000000 --- a/internal/repotools/gomod/version.go +++ /dev/null @@ -1,76 +0,0 @@ -package gomod - -import ( - "fmt" - "path/filepath" - - "github.com/aws/aws-sdk-go-v2/internal/repotools/git" - "github.com/aws/aws-sdk-go-v2/internal/repotools/semver" - "golang.org/x/mod/modfile" -) - -// UpdateRequires updates all modules discovered starting at repoRootPath using the provided tags and dependencies. -// Using force will update the module required versions regardless whether the target version less the currently -// written version. -func UpdateRequires(repoRootPath string, tags git.ModuleTags, dependencies map[string]string, force bool) error { - discoverer := NewDiscoverer(repoRootPath) - - if err := discoverer.Discover(); err != nil { - return fmt.Errorf("failed to discover repository modules: %v", err) - } - - modules, err := discoverer.ModulesRel() - if err != nil { - return err - } - - repoModules := make(map[string]struct { - ModuleDir string - File *modfile.File - }) - - for moduleDir := range modules { - mod, err := LoadModuleFile(filepath.Join(discoverer.Root(), moduleDir), nil, true) - if err != nil { - return fmt.Errorf("failed to load module file: %w", err) - } - rm := repoModules[mod.Module.Mod.Path] - rm.File = mod - rm.ModuleDir = moduleDir - repoModules[mod.Module.Mod.Path] = rm - } - - for _, mod := range repoModules { - for _, require := range mod.File.Require { - version := require.Mod.Version - if requireMod, ok := repoModules[require.Mod.Path]; ok { - latest, ok := tags.Latest(requireMod.ModuleDir) - if ok { - if force { - version = latest - } else if semver.Compare(latest, version) > 0 { - version = latest - } - } - } else { - dv, ok := dependencies[require.Mod.Path] - if ok { - if force { - version = dv - } else if semver.Compare(dv, version) > 0 { - version = dv - } - } - } - if err := mod.File.AddRequire(require.Mod.Path, version); err != nil { - return err - } - } - - if err = WriteModuleFile(filepath.Join(discoverer.Root(), mod.ModuleDir), mod.File); err != nil { - return fmt.Errorf("failed to write module file: %w", err) - } - } - - return nil -} diff --git a/internal/repotools/manifest/manifest.go b/internal/repotools/manifest/manifest.go deleted file mode 100644 index e3db23aba22..00000000000 --- a/internal/repotools/manifest/manifest.go +++ /dev/null @@ -1,100 +0,0 @@ -package manifest - -import ( - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" -) - -// NoManifestFound is an erro returned when a build manifest file was not found in the designated directory path. -type NoManifestFound struct { - Path string -} - -// Error returns the error description -func (n *NoManifestFound) Error() string { - return "build artifact manifest not found" -} - -// Manifest is a description of a Smithy build artifact produced -// by the github.com/aws/smithy-go plugin. -type Manifest struct { - Module string `json:"module"` - Go string `json:"go"` - Dependencies map[string]string `json:"dependencies"` - Files []string `json:"files"` - Unstable bool `json:"unstable"` -} - -// ValidateManifest validates that the build artifact description -// has the minimum required information to produce a valid Go module description. -func ValidateManifest(manifest Manifest) error { - if len(manifest.Go) == 0 { - return fmt.Errorf("missing Go minimum version") - } - if len(manifest.Module) == 0 { - return fmt.Errorf("missing module path") - } - return nil -} - -// LoadManifest loads the manifest description from the file located at the given path. -func LoadManifest(path string) (Manifest, error) { - mf, err := os.Open(path) - if err != nil && os.IsNotExist(err) { - return Manifest{}, &NoManifestFound{Path: path} - } else if err != nil { - return Manifest{}, fmt.Errorf("failed to open manifest: %w", err) - } - defer mf.Close() - return ReadManifest(mf) -} - -// ReadManifest parses the manifest bytes from the provided reader and returns the manifest description. -func ReadManifest(reader io.Reader) (m Manifest, err error) { - data, err := ioutil.ReadAll(reader) - if err != nil { - return Manifest{}, err - } - if err = json.Unmarshal(data, &m); err != nil { - return Manifest{}, err - } - if err = ValidateManifest(m); err != nil { - return Manifest{}, err - } - return m, nil -} - -// SmithyArtifactPaths is a slice of smithy-go build artifacts. -// See the Walk method which can be used for finding the generated Go -// source code from the Smithy build plugins projection. -type SmithyArtifactPaths []string - -// Walk is a filepath.WalkFunc compatible method that can be used for finding -// smithy-go plugin build artifacts. -func (a *SmithyArtifactPaths) Walk(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - if !info.IsDir() { - return nil - } - - pluginOutput := filepath.Join(path, "go-codegen") - stat, err := os.Stat(pluginOutput) - if err != nil { - return nil - } - - if !stat.IsDir() { - return nil - } - - *a = append(*a, pluginOutput) - - return filepath.SkipDir -} diff --git a/internal/repotools/release/calculate.go b/internal/repotools/release/calculate.go deleted file mode 100644 index 0cb70da2bfd..00000000000 --- a/internal/repotools/release/calculate.go +++ /dev/null @@ -1,184 +0,0 @@ -package release - -import ( - "fmt" - "github.com/aws/aws-sdk-go-v2/internal/repotools" - "github.com/aws/aws-sdk-go-v2/internal/repotools/changelog" - "github.com/aws/aws-sdk-go-v2/internal/repotools/git" - "github.com/aws/aws-sdk-go-v2/internal/repotools/gomod" - "log" - "path" - "path/filepath" - "sort" -) - -// ModuleFinder is a type that -type ModuleFinder interface { - Root() string - - ModulesRel() (map[string][]string, error) -} - -// Calculate calculates the modules to be released and their next versions based on the Git history, previous tags, -// module configuration, and associated changelog annotaitons. -func Calculate(finder ModuleFinder, tags git.ModuleTags, config repotools.Config, annotations []changelog.Annotation) (map[string]*Module, error) { - rootDir := finder.Root() - - repositoryModules, err := finder.ModulesRel() - if err != nil { - log.Fatalf("failed to modules: %v", err) - } - - moduleAnnotations := make(map[string][]changelog.Annotation) - for _, annotation := range annotations { - for _, am := range annotation.Modules { - moduleAnnotations[am] = append(moduleAnnotations[am], annotation) - } - } - - modules := make(map[string]*Module) - var repositoryModuleTombstonePaths []string - - for moduleDir := range tags { - if _, ok := repositoryModules[moduleDir]; !ok { - repositoryModuleTombstonePaths = append(repositoryModuleTombstonePaths, moduleDir) - } - } - - for moduleDir := range repositoryModules { - moduleFile, err := gomod.LoadModuleFile(filepath.Join(rootDir, moduleDir), nil, true) - if err != nil { - return nil, fmt.Errorf("failed to load module file: %w", err) - } - - modulePath, err := gomod.GetModulePath(moduleFile) - if err != nil { - return nil, fmt.Errorf("failed to read module path: %w", err) - } - - var latestVersion string - var hasChanges bool - - latestVersion, ok := tags.Latest(moduleDir) - if ok { - startTag, err := git.ToModuleTag(moduleDir, latestVersion) - if err != nil { - log.Fatalf("failed to convert module path and version to tag: %v", err) - } - - changes, err := git.Changes(finder.Root(), startTag, "HEAD", moduleDir) - if err != nil { - log.Fatalf("failed to get git changes: %v", err) - } - - subModulePaths := repositoryModules[moduleDir] - - ignoredModulePaths := make([]string, 0, len(subModulePaths)+len(repositoryModuleTombstonePaths)) - ignoredModulePaths = append(ignoredModulePaths, subModulePaths...) - - if len(repositoryModuleTombstonePaths) > 0 { - ignoredModulePaths = append(ignoredModulePaths, repositoryModuleTombstonePaths...) - // IsModuleChanged expects the provided list of ignored modules paths to be sorted - sort.Strings(ignoredModulePaths) - } - - hasChanges, err = gomod.IsModuleChanged(moduleDir, ignoredModulePaths, changes) - if err != nil { - return nil, fmt.Errorf("failed to determine module changes: %w", err) - } - - if !hasChanges { - // Check if any of the submodules have been "carved out" of this module since the last tagged release - for _, subModuleDir := range subModulePaths { - if _, ok := tags.Latest(subModuleDir); ok { - continue - } - - treeFiles, err := git.LsTree(rootDir, startTag, subModuleDir) - if err != nil { - return nil, fmt.Errorf("failed to list git tree: %v", err) - } - - carvedOut, err := isModuleCarvedOut(treeFiles, repositoryModules[subModuleDir]) - if err != nil { - return nil, err - } - if carvedOut { - hasChanges = true - break - } - } - } - } - - var changeReason ModuleChange - if hasChanges && len(latestVersion) > 0 { - changeReason |= SourceChange - } else if len(latestVersion) == 0 { - changeReason |= NewModule - } - - modules[modulePath] = &Module{ - File: moduleFile, - RelativeRepoPath: moduleDir, - Latest: latestVersion, - Changes: changeReason, - ChangeAnnotations: moduleAnnotations[moduleDir], - ModuleConfig: config.Modules[moduleDir], - } - } - - if err := CalculateDependencyUpdates(modules); err != nil { - return nil, err - } - - for moduleDir := range modules { - if modules[moduleDir].Changes == 0 || config.Modules[moduleDir].NoTag { - delete(modules, moduleDir) - } - } - - return modules, nil -} - -// isModuleCarvedOut takes a list of files for a (new) submodule directory. The list of files are the files that are located -// in the submodule directory path from the parent's previous tagged release. Returns true the new submodule has been -// carved out of the parent module directory it is located under. This is determined by looking through the file list -// and determining if Go source is present but no `go.mod` file existed. -func isModuleCarvedOut(files []string, subModules []string) (bool, error) { - hasGoSource := false - hasGoMod := false - - isChildPathCache := make(map[string]bool) - - for _, file := range files { - dir, fileName := path.Split(file) - dir = path.Clean(dir) - - isGoMod := gomod.IsGoMod(fileName) - isGoSource := gomod.IsGoSource(fileName) - - if !(isGoMod || isGoSource) { - continue - } - - if isChild, ok := isChildPathCache[dir]; (isChild && ok) || (!ok && gomod.IsSubmodulePath(dir, subModules)) { - isChildPathCache[dir] = true - continue - } else { - isChildPathCache[dir] = false - } - - if isGoSource { - hasGoSource = true - } else if isGoMod { - hasGoMod = true - } - - if hasGoMod && hasGoSource { - break - } - } - - return !hasGoMod && hasGoSource, nil -} diff --git a/internal/repotools/release/calculate_test.go b/internal/repotools/release/calculate_test.go deleted file mode 100644 index c18e16bf166..00000000000 --- a/internal/repotools/release/calculate_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package release - -import ( - "testing" -) - -func Test_isModuleCarvedOut1(t *testing.T) { - tests := map[string]struct { - files []string - subModules []string - want bool - wantErr bool - }{ - "no submodules, has go.mod, has go source": { - files: []string{ - "a/go.mod", - "a/foo.go", - }, - want: false, - }, - "no submodules, no go.mod, has go source": { - files: []string{ - "a/foo.go", - }, - want: true, - }, - "no submodules, no files": { - want: false, - }, - "submodules, no go.mod, no go source": { - files: []string{ - "a/b/go.mod", - "a/b/foo.go", - "a/c/go.mod", - "a/c/bar.go", - }, - subModules: []string{"a/b", "a/c"}, - want: false, - }, - "submodules, has go.mod, no go source": { - files: []string{ - "a/b/go.mod", - "a/b/foo.go", - "a/c/go.mod", - "a/c/bar.go", - "a/go.mod", - }, - subModules: []string{"a/b", "a/c"}, - want: false, - }, - "submodules, no go.mod, has go source": { - files: []string{ - "a/b/go.mod", - "a/b/foo.go", - "a/c/go.mod", - "a/c/bar.go", - "a/foo.go", - }, - subModules: []string{"a/b", "a/c"}, - want: true, - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - got, err := isModuleCarvedOut(tt.files, tt.subModules) - if (err != nil) != tt.wantErr { - t.Errorf("isModuleCarvedOut() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("isModuleCarvedOut() got = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/internal/repotools/release/manifest_schema.json b/internal/repotools/release/manifest_schema.json deleted file mode 100644 index e4805fb5f65..00000000000 --- a/internal/repotools/release/manifest_schema.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft-07/schema", - "type": "object", - "properties": { - "id": { - "type": "string", - "pattern": "^\\d{4}-\\d{2}-\\d{2}(\\.\\d+)?$" - }, - "modules": { - "type": "object", - "patternProperties": { - "^(.*)+$": { - "$ref": "#/$defs/module" - } - } - }, - "tags": { - "type": "array", - "description": "List of Go Module tags for release.", - "items": { - "$ref": "#/$defs/semver" - }, - "uniqueItems": true - } - }, - "$defs": { - "module": { - "required": [ - "module_path", - "to" - ], - "properties": { - "module_path": { - "type": "string", - "description": "The Go Module Path from the go.mod" - }, - "from": { - "$ref": "#/$defs/semver", - "description": "The modules latest version" - }, - "to": { - "$ref": "#/$defs/semver", - "description": "The computed next module version" - }, - "changes": { - "$ref": "#/$defs/moduleChanges", - "description": "The changes detected for this module" - }, - "annotations": { - "type": "array", - "description": "The changelog annotation identifiers that are associated with this module.", - "items": { - "$ref": "#/$defs/annotationId" - }, - "uniqueItems": true - } - } - }, - "semver": { - "type": "string", - "pattern": "^v(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", - "description": "A Go semantic version identifier." - }, - "moduleChanges": { - "type": "object", - "properties": { - "source_change": { - "type": "boolean", - "description": "Changes to the module were detected via Git." - }, - "new_module": { - "type": "boolean", - "description": "Indicates the module is new and hasn't been tagged before." - }, - "dependency_update": { - "type": "boolean", - "description": "Indicates the module has changes due to one or more repository modules that it has a dependency on has been updated." - } - } - }, - "annotationId": { - "type": "string", - "description": "The UUID identifier for a changelog annotation." - } - } -} diff --git a/internal/repotools/release/release.go b/internal/repotools/release/release.go deleted file mode 100644 index ceb4282faf7..00000000000 --- a/internal/repotools/release/release.go +++ /dev/null @@ -1,441 +0,0 @@ -package release - -import ( - "encoding/json" - "fmt" - "sort" - "strconv" - "strings" - "time" - - "github.com/aws/aws-sdk-go-v2/internal/repotools" - "github.com/aws/aws-sdk-go-v2/internal/repotools/changelog" - "github.com/aws/aws-sdk-go-v2/internal/repotools/git" - "github.com/aws/aws-sdk-go-v2/internal/repotools/semver" - "golang.org/x/mod/modfile" - "golang.org/x/mod/module" -) - -// Manifest is a release description of changed modules and their associated tags to be released. -type Manifest struct { - ID string `json:"id"` - Modules map[string]ModuleManifest `json:"modules"` - Tags []string `json:"tags"` -} - -// ModuleManifest describes a changed module for release. -type ModuleManifest struct { - ModulePath string `json:"module_path"` - - From string `json:"from,omitempty"` - To string `json:"to"` - - Changes ModuleChange `json:"changes,omitempty"` - - Annotations Annotations `json:"annotations,omitempty"` -} - -func getNewModuleVersion(pathMajor string, increment changelog.SemVerIncrement, config repotools.ModuleConfig) (nextVersion string) { - if len(pathMajor) == 0 { - nextVersion = "v1.0.0" - } else { - nextVersion = pathMajor + ".0.0" - } - - // Special case, by default new modules will have a pre-release tag, unless we have a corresponding change - // annotation that marks the module for release. - if increment == changelog.ReleaseBump { - return nextVersion - } - - if len(config.PreRelease) > 0 { - nextVersion += "-" + config.PreRelease - } else { - nextVersion += "-preview" - } - - return nextVersion -} - -// CalculateNextVersion calculates the next version for the module. The provided set of annotations must be applicable -// for this specific module. -func CalculateNextVersion(modulePath string, latest string, config repotools.ModuleConfig, annotations []changelog.Annotation) (next string, err error) { - _, pathMajor, ok := module.SplitPathVersion(modulePath) - if !ok { - return "", fmt.Errorf("invalid module path") - } - pathMajor = strings.TrimPrefix(pathMajor, "/") - - increment := changelog.GetVersionIncrement(annotations) - - if len(latest) == 0 { - next = getNewModuleVersion(pathMajor, increment, config) - return next, nil - } - - parsed, ok := semver.Parse(semver.Canonical(latest)) - if !ok { - return "", fmt.Errorf("failed to parse semver: %v, %v", latest, parsed.Err) - } - - if increment == changelog.ReleaseBump { - // Release Bumps are used to elevate pre-release tag versions to released versions - // Examples: - // v1.4.0-preview => v1.4.0 - // v1.4.0-preview.1 => v1.4.0 - - if len(parsed.Prerelease) == 0 { - return "", fmt.Errorf("changelog annotation requests release bump, but latest tag is not a pre-release") - } - parsed.Prerelease = "" - } else if len(parsed.Prerelease) > 0 { - // The existing tag is a pre-release so just increment the pre-release tag number - // Examples: - // v1.4.0-preview => v1.4.0-preview.1 - // v1.4.0-preview.2 => v1.4.0-preview.3 - // v1.4.0-preview => v1.4.0-rc (if different pre-release identifier is configured) - - if err := incrementPrerelease(&parsed.Prerelease, config.PreRelease); err != nil { - return "", err - } - } else if len(parsed.Prerelease) == 0 && len(config.PreRelease) > 0 { - // The latest tag was not a pre-release but module is configured for pre-release - // It is assumed that the target final version is intended to be a minor bump, so we simulate that here - // when constructing the pre-release tag. - // Example: v1.3.5 => v1.4.0-preview - - if err := incrementStrInt(&parsed.Minor); err != nil { - return "", err - } - - parsed.Patch = "0" - - prerelease := config.PreRelease - if !strings.HasPrefix(prerelease, "-") { - prerelease = "-" + prerelease - } - - parsed.Prerelease = prerelease - - } else if increment == changelog.MinorBump { - // Module should be bumped by a minor version - // Example: v1.2.3 => v1.3.0 - - if err := incrementStrInt(&parsed.Minor); err != nil { - return "", err - } - parsed.Patch = "0" - } else { - // Patch Bump - // Example: v1.2.3 => v1.2.4 - if err := incrementStrInt(&parsed.Patch); err != nil { - return "", err - } - } - - next = parsed.String() - - if semver.Compare(next, latest) <= 0 { - return "", fmt.Errorf("computed next version %s is not higher then %s", next, latest) - } - - return next, nil -} - -func incrementStrInt(v *string) error { - if v == nil { - return fmt.Errorf("must be a non-nil pointer") - } - - i, err := strconv.Atoi(*v) - if err != nil { - return err - } - *v = strconv.Itoa(i + 1) - - return nil -} - -func incrementPrerelease(prerelease *string, identifier string) error { - if prerelease == nil { - return fmt.Errorf("must be non-nil pointer") - } - - if !strings.HasSuffix(identifier, "-") { - identifier = "-" + identifier - } - - if len(identifier) > 0 && !strings.HasPrefix(*prerelease, identifier) { - *prerelease = identifier - return nil - } - - index := strings.LastIndex(*prerelease, ".") - if index == -1 { - *prerelease += ".1" - return nil - } - - i, err := strconv.Atoi((*prerelease)[index+1:]) - if err != nil { - return fmt.Errorf("failed to parse pre-release version number: %v", err) - } - *prerelease = (*prerelease)[:index+1] + strconv.Itoa(i+1) - - return nil -} - -// BuildReleaseManifest given a mapping of Go module paths to their Module descriptions, returns a summarized -// manifest for release. -func BuildReleaseManifest(id string, modules map[string]*Module) (rm Manifest, err error) { - rm.ID = id - - rm.Modules = make(map[string]ModuleManifest) - - for modulePath, mod := range modules { - if mod.Changes == 0 || mod.ModuleConfig.NoTag { - continue - } - - nextVersion, err := CalculateNextVersion(modulePath, mod.Latest, mod.ModuleConfig, mod.ChangeAnnotations) - if err != nil { - return Manifest{}, err - } - - mm := ModuleManifest{ - ModulePath: modulePath, - From: mod.Latest, - To: nextVersion, - Changes: mod.Changes, - Annotations: annotationsToIDs(mod.ChangeAnnotations), - } - - rm.Modules[mod.RelativeRepoPath] = mm - - moduleTag, err := git.ToModuleTag(mod.RelativeRepoPath, nextVersion) - if err != nil { - return Manifest{}, err - } - - rm.Tags = append(rm.Tags, moduleTag) - } - - sort.Strings(rm.Tags) - - return rm, nil -} - -// Annotations is a type alias for changelog.Annotation to control how annotations -// are marshaled in a release manifest. -type Annotations []string - -func annotationsToIDs(annotations []changelog.Annotation) []string { - var ids []string - - for _, annotation := range annotations { - ids = append(ids, annotation.ID) - } - - return ids -} - -// Module is a description of a repository Go module and knowledge about it's current release state. -type Module struct { - // The parsed go.mod file - File *modfile.File - - // The modules relative path from the repository root - RelativeRepoPath string - - // The most recent semver tagged release - Latest string - - // The next semver tag to release - Next string - - // The changes for the module - Changes ModuleChange - - // The change note identifiers applicable for this module - ChangeAnnotations []changelog.Annotation - - // The release configuration for this module - ModuleConfig repotools.ModuleConfig -} - -// ModuleChange is a bit field to describe the changes for a module -type ModuleChange uint64 - -// MarshalJSON marshals the chnage bits into a structure JSON object. -func (m ModuleChange) MarshalJSON() ([]byte, error) { - j := moduleChangeJSON{ - SourceChange: m&SourceChange != 0, - NewModule: m&NewModule != 0, - DependencyUpdate: m&DependencyUpdate != 0, - } - - return json.Marshal(j) -} - -// UnmarshalJSON unmarshals the JSON object bytes into the ModuleChange bit-field representation. -func (m *ModuleChange) UnmarshalJSON(bytes []byte) error { - var j moduleChangeJSON - - if err := json.Unmarshal(bytes, &j); err != nil { - return err - } - - if j.SourceChange { - *m |= SourceChange - } - - if j.NewModule { - *m |= NewModule - } - - if j.DependencyUpdate { - *m |= DependencyUpdate - } - - return nil -} - -const ( - // SourceChange indicates that the module has source changes since the last tagged release - SourceChange ModuleChange = 1 << (64 - 1 - iota) - - // NewModule indicates that the module is new and has not been tagged previously - NewModule - - // DependencyUpdate indicates the module has changes due to a dependency bump - DependencyUpdate -) - -type moduleChangeJSON struct { - SourceChange bool `json:"source_change,omitempty"` - NewModule bool `json:"new_module,omitempty"` - DependencyUpdate bool `json:"dependency_update,omitempty"` -} - -// buildInverseDependencyGraph builds an inverse dependency graphs mapping a module path to a slice of -// dependents. -func buildInverseDependencyGraph(modules map[string]*Module) (reverseDepGraph map[string][]string) { - reverseDepGraph = make(map[string][]string) - - for modulePath, mod := range modules { - for _, require := range mod.File.Require { - requireModPath := require.Mod.Path - _, ok := modules[requireModPath] - if !ok { - continue - } - reverseDepGraph[requireModPath] = append(reverseDepGraph[requireModPath], modulePath) - } - } - - return reverseDepGraph -} - -// CalculateDependencyUpdates determines which modules require a dependency update bump -// due to one or more of its direct or indirect dependencies being bumped. This will set -// the DependencyUpdate bit flag on the modules set of changes. -func CalculateDependencyUpdates(modules map[string]*Module) error { - reverseDepGraph := buildInverseDependencyGraph(modules) - - var toVisit []string - for modulePath := range reverseDepGraph { - toVisit = append(toVisit, modulePath) - } - sort.Strings(toVisit) - - var current string - for len(toVisit) > 0 { - current, toVisit = toVisit[0], toVisit[1:] - - m := modules[current] - - if m.Changes == 0 { - continue - } - - dependents := reverseDepGraph[current] - - if m.ModuleConfig.NoTag && len(dependents) > 0 { - return fmt.Errorf("module %v is configured for no releases, but has %d dependents", current, - len(dependents)) - } else if m.ModuleConfig.NoTag { - continue - } - - for _, dependent := range dependents { - dependentModule := modules[dependent] - if dependentModule.Changes&DependencyUpdate != 0 { - continue - } - dependentModule.Changes |= DependencyUpdate - if _, ok := reverseDepGraph[dependent]; ok { - toVisit = repotools.AppendIfNotPresent(toVisit, dependent) - } - } - } - - return nil -} - -var nowTime = time.Now - -// NextReleaseID returns the next release identifier based on current YYYY-MM-DD and whether there are multiple tags -// for the given date. -// For example: -// First Release => YYYY-MM-DD -// Second Same-Day Release => YYYY-MM-DD.2 -func NextReleaseID(tags []string) (next string) { - const releaseTagPrefix = "release-" - const dt = "2006-01-02" - - ct := nowTime().UTC() - - nextTime := time.Date(ct.Year(), ct.Month(), ct.Day(), 0, 0, 0, 0, time.UTC) - - latestNum := 0 - - for _, tag := range tags { - if !strings.HasPrefix(tag, releaseTagPrefix) { - continue - } - tag = strings.TrimPrefix(tag, releaseTagPrefix) - split := strings.SplitN(tag, ".", 2) - - t, err := time.Parse(dt, split[0]) - if err != nil { - continue - } - - if !t.Equal(nextTime) { - continue - } - - if len(split) != 2 { - if latestNum == 0 { - latestNum = 1 - } - continue - } - - i, err := strconv.Atoi(split[1]) - if err != nil { - continue - } - - if i > latestNum { - latestNum = i - } - } - - if latestNum == 0 { - return nextTime.Format(dt) - } - - latestNum++ - return nextTime.Format(dt) + "." + strconv.Itoa(latestNum) -} diff --git a/internal/repotools/release/release_test.go b/internal/repotools/release/release_test.go deleted file mode 100644 index 2c33a33d8fc..00000000000 --- a/internal/repotools/release/release_test.go +++ /dev/null @@ -1,229 +0,0 @@ -package release - -import ( - "github.com/aws/aws-sdk-go-v2/internal/repotools" - "github.com/aws/aws-sdk-go-v2/internal/repotools/changelog" - "testing" - "time" -) - -type mockFinder struct { - RootPath string - Modules map[string][]string -} - -func (m *mockFinder) Root() string { - return m.RootPath -} - -func (m *mockFinder) ModulesRel() (map[string][]string, error) { - return m.Modules, nil -} - -func TestCalculateNextVersion(t *testing.T) { - type args struct { - modulePath string - latest string - config repotools.ModuleConfig - annotations []changelog.Annotation - } - tests := map[string]struct { - args args - wantNext string - wantErr bool - }{ - "new module v1 major": { - args: args{ - modulePath: "github.com/aws/aws-sdk-go-v2/service/shinynew", - }, - wantNext: "v1.0.0-preview", - }, - "new module v1 major with release annotation": { - args: args{ - modulePath: "github.com/aws/aws-sdk-go-v2/service/shinynew", - annotations: []changelog.Annotation{{ - Type: changelog.ReleaseChangeType, - }}, - }, - wantNext: "v1.0.0", - }, - "new module v2 or higher major": { - args: args{ - modulePath: "github.com/aws/aws-sdk-go-v2/service/shinynew/v2", - }, - wantNext: "v2.0.0-preview", - }, - "new module v2 or higher with release annotation": { - args: args{ - modulePath: "github.com/aws/aws-sdk-go-v2/service/shinynew/v2", - annotations: []changelog.Annotation{{ - Type: changelog.ReleaseChangeType, - }}, - }, - wantNext: "v2.0.0", - }, - "existing module version, not pre-release, no annotation": { - args: args{ - modulePath: "github.com/aws/aws-sdk-go-v2/service/existing", - latest: "v1.0.0", - }, - wantNext: "v1.0.1", - }, - "existing module version, not pre-release, with patch semver annotation": { - args: args{ - modulePath: "github.com/aws/aws-sdk-go-v2/service/existing", - latest: "v1.0.0", - annotations: []changelog.Annotation{ - {Type: changelog.BugFixChangeType}, - }, - }, - wantNext: "v1.0.1", - }, - "existing module version, not pre-release, with minor semver annotation": { - args: args{ - modulePath: "github.com/aws/aws-sdk-go-v2/service/existing", - latest: "v1.0.1", - annotations: []changelog.Annotation{ - {Type: changelog.FeatureChangeType}, - }, - }, - wantNext: "v1.1.0", - }, - "existing module version, set for pre-release": { - args: args{ - modulePath: "github.com/aws/aws-sdk-go-v2/service/existing", - latest: "v1.0.1", - config: repotools.ModuleConfig{PreRelease: "rc"}, - }, - wantNext: "v1.1.0-rc", - }, - "existing module preview version": { - args: args{ - modulePath: "github.com/aws/aws-sdk-go-v2/service/existing", - latest: "v1.1.0-preview", - config: repotools.ModuleConfig{PreRelease: "preview"}, - }, - wantNext: "v1.1.0-preview.1", - }, - "existing module preview version, with non-release annotation types": { - args: args{ - modulePath: "github.com/aws/aws-sdk-go-v2/service/existing", - latest: "v1.1.0-preview.1", - config: repotools.ModuleConfig{PreRelease: "preview"}, - annotations: []changelog.Annotation{{ - Type: changelog.FeatureChangeType, - }}, - }, - wantNext: "v1.1.0-preview.2", - }, - "existing module preview version, with new pre-release tag": { - args: args{ - modulePath: "github.com/aws/aws-sdk-go-v2/service/existing", - latest: "v1.1.0-preview.2", - config: repotools.ModuleConfig{PreRelease: "rc"}, - annotations: []changelog.Annotation{{ - Type: changelog.FeatureChangeType, - }}, - }, - wantNext: "v1.1.0-rc", - }, - "existing module preview version, with new invalid pre-release tag": { - args: args{ - modulePath: "github.com/aws/aws-sdk-go-v2/service/existing", - latest: "v1.1.0-rc.5", - config: repotools.ModuleConfig{PreRelease: "alpha"}, - annotations: []changelog.Annotation{{ - Type: changelog.FeatureChangeType, - }}, - }, - wantErr: true, - }, - "existing module preview version, with release annotation": { - args: args{ - modulePath: "github.com/aws/aws-sdk-go-v2/service/existing", - latest: "v1.1.0-rc.5", - annotations: []changelog.Annotation{{ - Type: changelog.ReleaseChangeType, - }}, - }, - wantNext: "v1.1.0", - }, - "invalid latest tag": { - args: args{ - modulePath: "github.com/aws/aws-sdk-go-v2/service/existing", - latest: "1.1.0", - }, - wantErr: true, - }, - "module tag with build metadata": { - args: args{ - modulePath: "github.com/aws/aws-sdk-go-v2/service/existing", - latest: "v1.1.0+build.12345", - }, - wantNext: "v1.1.1", - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - gotNext, err := CalculateNextVersion(tt.args.modulePath, tt.args.latest, tt.args.config, tt.args.annotations) - if (err != nil) != tt.wantErr { - t.Errorf("CalculateNextVersion() error = %v, wantErr %v", err, tt.wantErr) - return - } - if gotNext != tt.wantNext { - t.Errorf("CalculateNextVersion() gotNext = %v, want %v", gotNext, tt.wantNext) - } - }) - } -} - -func TestNextReleaseID(t *testing.T) { - origNowTime := nowTime - defer func() { - nowTime = origNowTime - }() - - type args struct { - tags []string - } - tests := map[string]struct { - args args - nowTime func() time.Time - wantNext string - }{ - "no tags": { - wantNext: "2021-05-06", - }, - "other tags": { - args: args{tags: []string{"v1.2.0", "release/foo/v2"}}, - wantNext: "2021-05-06", - }, - "older tags": { - args: args{tags: []string{"release-2021-05-04", "release-2021-05-04.2"}}, - wantNext: "2021-05-06", - }, - "second release": { - args: args{tags: []string{"release-2021-05-06"}}, - wantNext: "2021-05-06.2", - }, - "third release": { - args: args{tags: []string{"release-2021-05-06", "release-2021-05-06.2"}}, - wantNext: "2021-05-06.3", - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - if tt.nowTime == nil { - nowTime = func() time.Time { - return time.Date(2021, 5, 6, 7, 8, 9, 10, time.UTC) - } - } else { - nowTime = tt.nowTime - } - - if gotNext := NextReleaseID(tt.args.tags); gotNext != tt.wantNext { - t.Errorf("NextReleaseID() = %v, want %v", gotNext, tt.wantNext) - } - }) - } -} diff --git a/internal/repotools/repo_root.go b/internal/repotools/repo_root.go deleted file mode 100644 index b310e7782dd..00000000000 --- a/internal/repotools/repo_root.go +++ /dev/null @@ -1,71 +0,0 @@ -package repotools - -import ( - "fmt" - "os" - "path/filepath" -) - -// GetRepoRoot uses the current working directory to find the repository root. -func GetRepoRoot() (string, error) { - dir, err := os.Getwd() - if err != nil { - return "", fmt.Errorf("failed to get current directory: %w", err) - } - - repoRootPath, err := FindRepoRoot(dir) - if err != nil { - return "", fmt.Errorf("failed to find git repository: %w", err) - } - - return repoRootPath, nil -} - -// FindRepoRoot returns the absolute path to the root directory of the -// repository, or error. If the dir passed in is a relative path it will be -// used relative to the current working directory of the executable. -func FindRepoRoot(dir string) (string, error) { - if len(dir) == 0 { - dir = "." - } - - if !filepath.IsAbs(dir) { - var err error - dir, err = JoinWorkingDirectory(dir) - if err != nil { - return "", err - } - } - - var found bool - for { - if dir == string(filepath.Separator) { - break - } - - _, err := os.Stat(filepath.Join(dir, ".git")) - if err == nil { - found = true - break - } - - dir = filepath.Dir(dir) - } - - if !found { - return "", fmt.Errorf(".git directory not found") - } - - return dir, nil -} - -// JoinWorkingDirectory will return an absolute file system path of the passed -// in dir path with the current working directory. -func JoinWorkingDirectory(dir string) (string, error) { - wd, err := os.Getwd() - if err != nil { - return "", fmt.Errorf("failed to get working directory, %w", err) - } - - return filepath.Join(wd, dir), nil -} diff --git a/internal/repotools/semver/semver.go b/internal/repotools/semver/semver.go deleted file mode 100644 index 84382a469bc..00000000000 --- a/internal/repotools/semver/semver.go +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package semver implements comparison of semantic version strings. -// In this package, semantic version strings must begin with a leading "v", -// as in "v1.0.0". -// -// The general form of a semantic version string accepted by this package is -// -// vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]] -// -// where square brackets indicate optional parts of the syntax; -// MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros; -// PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers -// using only alphanumeric characters and hyphens; and -// all-numeric PRERELEASE identifiers must not have leading zeros. -// -// This package follows Semantic Versioning 2.0.0 (see semver.org) -// with two exceptions. First, it requires the "v" prefix. Second, it recognizes -// vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes) -// as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0. -package semver - -import ( - "sort" - "strings" -) - -// Parsed returns the Parsed form of a semantic version string. -type Parsed struct { - Major string - Minor string - Patch string - short string - Prerelease string - Build string - Err string -} - -func (p Parsed) String() string { - var builder strings.Builder - builder.WriteRune('v') - builder.WriteString(p.Major) - builder.WriteRune('.') - builder.WriteString(p.Minor) - builder.WriteRune('.') - builder.WriteString(p.Patch) - if len(p.Prerelease) > 0 { - builder.WriteString(p.Prerelease) - } - if len(p.Build) > 0 { - builder.WriteString(p.Build) - } - return builder.String() -} - -// IsValid reports whether v is a valid semantic version string. -func IsValid(v string) bool { - _, ok := Parse(v) - return ok -} - -// Canonical returns the canonical formatting of the semantic version v. -// It fills in any missing .MINOR or .PATCH and discards build metadata. -// Two semantic versions compare equal only if their canonical formattings -// are identical strings. -// The canonical invalid semantic version is the empty string. -func Canonical(v string) string { - p, ok := Parse(v) - if !ok { - return "" - } - if p.Build != "" { - return v[:len(v)-len(p.Build)] - } - if p.short != "" { - return v + p.short - } - return v -} - -// Major returns the major version prefix of the semantic version v. -// For example, Major("v2.1.0") == "v2". -// If v is an invalid semantic version string, Major returns the empty string. -func Major(v string) string { - pv, ok := Parse(v) - if !ok { - return "" - } - return v[:1+len(pv.Major)] -} - -// MajorMinor returns the major.minor version prefix of the semantic version v. -// For example, MajorMinor("v2.1.0") == "v2.1". -// If v is an invalid semantic version string, MajorMinor returns the empty string. -func MajorMinor(v string) string { - pv, ok := Parse(v) - if !ok { - return "" - } - i := 1 + len(pv.Major) - if j := i + 1 + len(pv.Minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.Minor { - return v[:j] - } - return v[:i] + "." + pv.Minor -} - -// Prerelease returns the prerelease suffix of the semantic version v. -// For example, Prerelease("v2.1.0-pre+meta") == "-pre". -// If v is an invalid semantic version string, Prerelease returns the empty string. -func Prerelease(v string) string { - pv, ok := Parse(v) - if !ok { - return "" - } - return pv.Prerelease -} - -// Build returns the build suffix of the semantic version v. -// For example, Build("v2.1.0+meta") == "+meta". -// If v is an invalid semantic version string, Build returns the empty string. -func Build(v string) string { - pv, ok := Parse(v) - if !ok { - return "" - } - return pv.Build -} - -// Compare returns an integer comparing two versions according to -// semantic version precedence. -// The result will be 0 if v == w, -1 if v < w, or +1 if v > w. -// -// An invalid semantic version string is considered less than a valid one. -// All invalid semantic version strings compare equal to each other. -func Compare(v, w string) int { - pv, ok1 := Parse(v) - pw, ok2 := Parse(w) - if !ok1 && !ok2 { - return 0 - } - if !ok1 { - return -1 - } - if !ok2 { - return +1 - } - if c := compareInt(pv.Major, pw.Major); c != 0 { - return c - } - if c := compareInt(pv.Minor, pw.Minor); c != 0 { - return c - } - if c := compareInt(pv.Patch, pw.Patch); c != 0 { - return c - } - return comparePrerelease(pv.Prerelease, pw.Prerelease) -} - -// Max canonicalizes its arguments and then returns the version string -// that compares greater. -// -// Deprecated: use Compare instead. In most cases, returning a canonicalized -// version is not expected or desired. -func Max(v, w string) string { - v = Canonical(v) - w = Canonical(w) - if Compare(v, w) > 0 { - return v - } - return w -} - -// ByVersion implements sort.Interface for sorting semantic version strings. -type ByVersion []string - -func (vs ByVersion) Len() int { return len(vs) } -func (vs ByVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] } -func (vs ByVersion) Less(i, j int) bool { - cmp := Compare(vs[i], vs[j]) - if cmp != 0 { - return cmp < 0 - } - return vs[i] < vs[j] -} - -// Sort sorts a list of semantic version strings using ByVersion. -func Sort(list []string) { - sort.Sort(ByVersion(list)) -} - -// Parse parses the string v into it's semver components, returns false if the string is not semver compliant. -func Parse(v string) (p Parsed, ok bool) { - if v == "" || v[0] != 'v' { - p.Err = "missing v prefix" - return - } - p.Major, v, ok = parseInt(v[1:]) - if !ok { - p.Err = "bad major version" - return - } - if v == "" { - p.Minor = "0" - p.Patch = "0" - p.short = ".0.0" - return - } - if v[0] != '.' { - p.Err = "bad minor prefix" - ok = false - return - } - p.Minor, v, ok = parseInt(v[1:]) - if !ok { - p.Err = "bad minor version" - return - } - if v == "" { - p.Patch = "0" - p.short = ".0" - return - } - if v[0] != '.' { - p.Err = "bad patch prefix" - ok = false - return - } - p.Patch, v, ok = parseInt(v[1:]) - if !ok { - p.Err = "bad patch version" - return - } - if len(v) > 0 && v[0] == '-' { - p.Prerelease, v, ok = parsePrerelease(v) - if !ok { - p.Err = "bad prerelease" - return - } - } - if len(v) > 0 && v[0] == '+' { - p.Build, v, ok = parseBuild(v) - if !ok { - p.Err = "bad build" - return - } - } - if v != "" { - p.Err = "junk on end" - ok = false - return - } - ok = true - return -} - -func parseInt(v string) (t, rest string, ok bool) { - if v == "" { - return - } - if v[0] < '0' || '9' < v[0] { - return - } - i := 1 - for i < len(v) && '0' <= v[i] && v[i] <= '9' { - i++ - } - if v[0] == '0' && i != 1 { - return - } - return v[:i], v[i:], true -} - -func parsePrerelease(v string) (t, rest string, ok bool) { - // "A pre-release version MAY be denoted by appending a hyphen and - // a series of dot separated identifiers immediately following the patch version. - // Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. - // Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes." - if v == "" || v[0] != '-' { - return - } - i := 1 - start := 1 - for i < len(v) && v[i] != '+' { - if !isIdentChar(v[i]) && v[i] != '.' { - return - } - if v[i] == '.' { - if start == i || isBadNum(v[start:i]) { - return - } - start = i + 1 - } - i++ - } - if start == i || isBadNum(v[start:i]) { - return - } - return v[:i], v[i:], true -} - -func parseBuild(v string) (t, rest string, ok bool) { - if v == "" || v[0] != '+' { - return - } - i := 1 - start := 1 - for i < len(v) { - if !isIdentChar(v[i]) && v[i] != '.' { - return - } - if v[i] == '.' { - if start == i { - return - } - start = i + 1 - } - i++ - } - if start == i { - return - } - return v[:i], v[i:], true -} - -func isIdentChar(c byte) bool { - return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-' -} - -func isBadNum(v string) bool { - i := 0 - for i < len(v) && '0' <= v[i] && v[i] <= '9' { - i++ - } - return i == len(v) && i > 1 && v[0] == '0' -} - -func isNum(v string) bool { - i := 0 - for i < len(v) && '0' <= v[i] && v[i] <= '9' { - i++ - } - return i == len(v) -} - -func compareInt(x, y string) int { - if x == y { - return 0 - } - if len(x) < len(y) { - return -1 - } - if len(x) > len(y) { - return +1 - } - if x < y { - return -1 - } - - return +1 -} - -func comparePrerelease(x, y string) int { - // "When major, minor, and patch are equal, a pre-release version has - // lower precedence than a normal version. - // Example: 1.0.0-alpha < 1.0.0. - // Precedence for two pre-release versions with the same major, minor, - // and patch version MUST be determined by comparing each dot separated - // identifier from left to right until a difference is found as follows: - // identifiers consisting of only digits are compared numerically and - // identifiers with letters or hyphens are compared lexically in ASCII - // sort order. Numeric identifiers always have lower precedence than - // non-numeric identifiers. A larger set of pre-release fields has a - // higher precedence than a smaller set, if all of the preceding - // identifiers are equal. - // Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < - // 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0." - if x == y { - return 0 - } - if x == "" { - return +1 - } - if y == "" { - return -1 - } - for x != "" && y != "" { - x = x[1:] // skip - or . - y = y[1:] // skip - or . - var dx, dy string - dx, x = nextIdent(x) - dy, y = nextIdent(y) - if dx != dy { - ix := isNum(dx) - iy := isNum(dy) - if ix != iy { - if ix { - return -1 - } - return +1 - } - if ix { - if len(dx) < len(dy) { - return -1 - } - if len(dx) > len(dy) { - return +1 - } - } - if dx < dy { - return -1 - } - return +1 - } - } - if x == "" { - return -1 - } - return +1 -} - -func nextIdent(x string) (dx, rest string) { - i := 0 - for i < len(x) && x[i] != '.' { - i++ - } - return x[:i], x[i:] -} diff --git a/internal/repotools/semver/semver_test.go b/internal/repotools/semver/semver_test.go deleted file mode 100644 index 937e18e00cc..00000000000 --- a/internal/repotools/semver/semver_test.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package semver - -import ( - "math/rand" - "sort" - "strings" - "testing" -) - -var tests = []struct { - in string - out string -}{ - {"bad", ""}, - {"v1-alpha.beta.gamma", ""}, - {"v1-pre", ""}, - {"v1+meta", ""}, - {"v1-pre+meta", ""}, - {"v1.2-pre", ""}, - {"v1.2+meta", ""}, - {"v1.2-pre+meta", ""}, - {"v1.0.0-alpha", "v1.0.0-alpha"}, - {"v1.0.0-alpha.1", "v1.0.0-alpha.1"}, - {"v1.0.0-alpha.beta", "v1.0.0-alpha.beta"}, - {"v1.0.0-beta", "v1.0.0-beta"}, - {"v1.0.0-beta.2", "v1.0.0-beta.2"}, - {"v1.0.0-beta.11", "v1.0.0-beta.11"}, - {"v1.0.0-rc.1", "v1.0.0-rc.1"}, - {"v1", "v1.0.0"}, - {"v1.0", "v1.0.0"}, - {"v1.0.0", "v1.0.0"}, - {"v1.2", "v1.2.0"}, - {"v1.2.0", "v1.2.0"}, - {"v1.2.3-456", "v1.2.3-456"}, - {"v1.2.3-456.789", "v1.2.3-456.789"}, - {"v1.2.3-456-789", "v1.2.3-456-789"}, - {"v1.2.3-456a", "v1.2.3-456a"}, - {"v1.2.3-pre", "v1.2.3-pre"}, - {"v1.2.3-pre+meta", "v1.2.3-pre"}, - {"v1.2.3-pre.1", "v1.2.3-pre.1"}, - {"v1.2.3-zzz", "v1.2.3-zzz"}, - {"v1.2.3", "v1.2.3"}, - {"v1.2.3+meta", "v1.2.3"}, - {"v1.2.3+meta-pre", "v1.2.3"}, - {"v1.2.3+meta-pre.sha.256a", "v1.2.3"}, -} - -func TestIsValid(t *testing.T) { - for _, tt := range tests { - ok := IsValid(tt.in) - if ok != (tt.out != "") { - t.Errorf("IsValid(%q) = %v, want %v", tt.in, ok, !ok) - } - } -} - -func TestCanonical(t *testing.T) { - for _, tt := range tests { - out := Canonical(tt.in) - if out != tt.out { - t.Errorf("Canonical(%q) = %q, want %q", tt.in, out, tt.out) - } - } -} - -func TestMajor(t *testing.T) { - for _, tt := range tests { - out := Major(tt.in) - want := "" - if i := strings.Index(tt.out, "."); i >= 0 { - want = tt.out[:i] - } - if out != want { - t.Errorf("Major(%q) = %q, want %q", tt.in, out, want) - } - } -} - -func TestMajorMinor(t *testing.T) { - for _, tt := range tests { - out := MajorMinor(tt.in) - var want string - if tt.out != "" { - want = tt.in - if i := strings.Index(want, "+"); i >= 0 { - want = want[:i] - } - if i := strings.Index(want, "-"); i >= 0 { - want = want[:i] - } - switch strings.Count(want, ".") { - case 0: - want += ".0" - case 1: - // ok - case 2: - want = want[:strings.LastIndex(want, ".")] - } - } - if out != want { - t.Errorf("MajorMinor(%q) = %q, want %q", tt.in, out, want) - } - } -} - -func TestPrerelease(t *testing.T) { - for _, tt := range tests { - pre := Prerelease(tt.in) - var want string - if tt.out != "" { - if i := strings.Index(tt.out, "-"); i >= 0 { - want = tt.out[i:] - } - } - if pre != want { - t.Errorf("Prerelease(%q) = %q, want %q", tt.in, pre, want) - } - } -} - -func TestBuild(t *testing.T) { - for _, tt := range tests { - build := Build(tt.in) - var want string - if tt.out != "" { - if i := strings.Index(tt.in, "+"); i >= 0 { - want = tt.in[i:] - } - } - if build != want { - t.Errorf("Build(%q) = %q, want %q", tt.in, build, want) - } - } -} - -func TestCompare(t *testing.T) { - for i, ti := range tests { - for j, tj := range tests { - cmp := Compare(ti.in, tj.in) - var want int - if ti.out == tj.out { - want = 0 - } else if i < j { - want = -1 - } else { - want = +1 - } - if cmp != want { - t.Errorf("Compare(%q, %q) = %d, want %d", ti.in, tj.in, cmp, want) - } - } - } -} - -func TestSort(t *testing.T) { - versions := make([]string, len(tests)) - for i, test := range tests { - versions[i] = test.in - } - rand.Shuffle(len(versions), func(i, j int) { versions[i], versions[j] = versions[j], versions[i] }) - Sort(versions) - if !sort.IsSorted(ByVersion(versions)) { - t.Errorf("list is not sorted:\n%s", strings.Join(versions, "\n")) - } -} - -func TestMax(t *testing.T) { - for i, ti := range tests { - for j, tj := range tests { - max := Max(ti.in, tj.in) - want := Canonical(ti.in) - if i < j { - want = Canonical(tj.in) - } - if max != want { - t.Errorf("Max(%q, %q) = %q, want %q", ti.in, tj.in, max, want) - } - } - } -} - -var ( - v1 = "v1.0.0+metadata-dash" - v2 = "v1.0.0+metadata-dash1" -) - -func BenchmarkCompare(b *testing.B) { - for i := 0; i < b.N; i++ { - if Compare(v1, v2) != 0 { - b.Fatalf("bad compare") - } - } -} diff --git a/internal/repotools/util.go b/internal/repotools/util.go deleted file mode 100644 index 3ab861c2987..00000000000 --- a/internal/repotools/util.go +++ /dev/null @@ -1,15 +0,0 @@ -package repotools - -import "sort" - -// AppendIfNotPresent appends value to the slice x if not present. The slice must be sorted in ascending order. -func AppendIfNotPresent(x []string, value string) []string { - i := sort.SearchStrings(x, value) - if i < len(x) && x[i] == value { - return x - } - x = append(x, "") - copy(x[i+1:], x[i:]) - x[i] = value - return x -} diff --git a/internal/repotools/uuid.go b/internal/repotools/uuid.go deleted file mode 100644 index c29d3879a34..00000000000 --- a/internal/repotools/uuid.go +++ /dev/null @@ -1,28 +0,0 @@ -package repotools - -import "encoding/hex" - -// UUIDVersion4 takes an array of 16 (random) bytes and converts it to a UUIDv4 value. -func UUIDVersion4(u [16]byte) string { - // https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29 - const dash = '-' - - // 13th character is "4" - u[6] = (u[6] & 0x0f) | 0x40 // Version 4 - // 17th character is "8", "9", "a", or "b" - u[8] = (u[8] & 0x3f) | 0x80 // Variant is 10 - - var scratch [36]byte - - hex.Encode(scratch[:8], u[0:4]) - scratch[8] = dash - hex.Encode(scratch[9:13], u[4:6]) - scratch[13] = dash - hex.Encode(scratch[14:18], u[6:8]) - scratch[18] = dash - hex.Encode(scratch[19:23], u[8:10]) - scratch[23] = dash - hex.Encode(scratch[24:], u[10:]) - - return string(scratch[:]) -} diff --git a/internal/repotools/walk.go b/internal/repotools/walk.go deleted file mode 100644 index d868c27a781..00000000000 --- a/internal/repotools/walk.go +++ /dev/null @@ -1,52 +0,0 @@ -package repotools - -import ( - "os" - "path/filepath" -) - -// Boots was made for walking the file tree searching for modules. -type Boots struct { - // Directories to skip when iterating. - SkipDirs []string - - modulePaths []string -} - -// Modules returns a slice of module directory absolute paths. -func (b *Boots) Modules() []string { - return b.modulePaths -} - -// Walk is the function to walk folders in the repo searching for go modules. -func (b *Boots) Walk(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - if !info.IsDir() { - return nil - } - - for _, skip := range b.SkipDirs { - if path == skip { - return filepath.SkipDir - } - } - - if !hasGoMod(path) { - return nil - } - - b.modulePaths = append(b.modulePaths, path) - - return nil -} - -func hasGoMod(dir string) bool { - _, err := os.Stat(filepath.Join(dir, "go.mod")) - if err != nil { - return false - } - return true -}