Skip to content

generate release from changelog #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
v1.1.0:
- generate release from changelog (@mateus.mello)

v1.0.5
- Fix isSetNewVersion function logic (@esequiel.virtuoso)

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ check: modcache imagedev
$(run) go test -tags unit -timeout 20s -race -coverprofile=$(cov) ./...

check-integration: imagedev start-env
$(runcompose) --entrypoint "./hack/check-integration.sh" semantic-release
$(runcompose) --entrypoint "./hack/check-integration.sh" semantic-release $(testfile) $(testname)

coverage: modcache check
$(run) go tool cover -html=$(cov) -o=$(covhtml)
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ semantic-release:

```

Also, you can add `-changelog "true"` argument to set semantic-release to get most recent version from CHANGELOG.md file instead of get it from gitlab tags.

If your project is a Python project you can add the flag `-setup-py true` to update the release version in this file too.

Note: The version must be placed in a variable called `__version__` as follows:
Expand Down
Binary file removed cmd/semantic-release/semantic-release
Binary file not shown.
8 changes: 5 additions & 3 deletions cmd/semantic-release/semantic-release.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ func main() {
username := upgradeVersionCmd.String("username", "", "Git username. (required)")
password := upgradeVersionCmd.String("password", "", "Git password. (required)")
logLevel := upgradeVersionCmd.String("log-level", "debug", "Log level.")
var useChangelogFile bool
upgradeVersionCmd.BoolVar(&useChangelogFile, "changelog", false, "Use changelog file to generate input version. (default false)")

if len(os.Args) < 2 {
printWelcomeMessage()
Expand All @@ -67,7 +69,7 @@ func main() {
case "up":
logger.Info(colorYellow + "\nSemantic Version just started the process...\n\n" + colorReset)

semantic := newSemantic(logger, upgradeVersionCmd, gitHost, groupName, projectName, username, password, upgradePyFile)
semantic := newSemantic(logger, upgradeVersionCmd, gitHost, groupName, projectName, username, password, upgradePyFile, useChangelogFile)

if err := semantic.GenerateNewRelease(); err != nil {
logger.Error(err.Error())
Expand Down Expand Up @@ -180,7 +182,7 @@ func printCommitMessageExample() {
fmt.Println("\n\tNote: The maximum number of characters is 150. If the commit subject exceeds it, it will be cut, keeping only the first 150 characters.")
}

func newSemantic(logger *log.Log, upgradeVersionCmd *flag.FlagSet, gitHost, groupName, projectName, username, password *string, upgradePyFile *bool) *semantic.Semantic {
func newSemantic(logger *log.Log, upgradeVersionCmd *flag.FlagSet, gitHost, groupName, projectName, username, password *string, upgradePyFile *bool, changelog bool) *semantic.Semantic {

validateIncomingParams(logger, upgradeVersionCmd, gitHost, groupName, projectName, username, password, upgradePyFile)

Expand All @@ -197,5 +199,5 @@ func newSemantic(logger *log.Log, upgradeVersionCmd *flag.FlagSet, gitHost, grou

versionControl := v.NewVersionControl(logger, timer.PrintElapsedTime)

return semantic.New(logger, repositoryRootPath, addFilesToUpgradeList(upgradePyFile, repositoryRootPath), repoVersionControl, filesVersionControl, versionControl)
return semantic.New(logger, repositoryRootPath, addFilesToUpgradeList(upgradePyFile, repositoryRootPath), repoVersionControl, filesVersionControl, versionControl, changelog)
}
45 changes: 45 additions & 0 deletions src/git/git.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package git

import (
"bufio"
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
Expand Down Expand Up @@ -117,6 +119,49 @@ func (g *GitVersioning) GetCurrentVersion() string {
return g.mostRecentTag
}

func (g *GitVersioning) GetChangelog() (*os.File, error) {

changelog, err := os.Open(filepath.Join(g.destinationDirectory, "CHANGELOG"))
if err != nil {
return nil, fmt.Errorf("error to open file in GetChangelog / %w", err)
}

return changelog, nil
}

func (g *GitVersioning) GetChangelogChanges() (string, error) {
changelog, err := os.Open(filepath.Join(g.destinationDirectory, "CHANGELOG"))
if err != nil {
return "", err
}

scanner := bufio.NewScanner(changelog)
// optionally, resize scanner's capacity for lines over 64K, see next example
change := ""
for scanner.Scan() {
line := scanner.Text()
finishedNewChanges := "## v"
if strings.Contains(line, finishedNewChanges) {
break
}

if isValidChangeMessage(line) {
if change != "" {
return "", fmt.Errorf("more than one change in the release / actual change: %s / another found: %s", change, line)
}
change = line
}
}

return change, nil
}

func isValidChangeMessage(message string) bool {
//regex validate this message: "type: [fix], message: testing message"
r := regexp.MustCompile(`type:\s\[[A-Za-z0-9]+\], message:\s[A-Za-z0-9]+`)
return r.FindString(message) != ""
}

func (g *GitVersioning) UpgradeRemoteRepository(newVersion string) error {
// consider 1.0.0 as the start tag of a repository when it does not have tags yet
if newVersion == "0.1.0" || newVersion == "0.0.1" {
Expand Down
78 changes: 78 additions & 0 deletions src/git/git_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,3 +364,81 @@ func TestNewGitGetCurrentVersionFromRepoWithDisorderedTags(t *testing.T) {
func getDestinationDirectory(repo string) string {
return fmt.Sprintf("%s/%s", os.Getenv("HOME"), repo)
}

// func TestGetChangelogWorks(t *testing.T) {
// f := getValidSetup()
// defer f.cleanLocalRepo(t)
// f.gitLabVersioning.url = greatNumbersTagProject
// f.gitLabVersioning.destinationDirectory = fmt.Sprintf("%s/%s", os.Getenv("HOME"), "get-changelog")
// repo, err := f.newGitService()
// file, err := os.Create(fmt.Sprintf("%s/%s/%s", os.Getenv("HOME"), "get-changelog", "CHANGELOG"))
// if err != nil {
// fmt.Println("Error to create file", err)
// return
// }
// defer file.Close()
// tests.AssertNoError(t, err)
// fmt.Fprintf(file, "type [fix], message: testing message")

// result, err := repo.GetChangelog()
// tests.AssertNil(t, err)
// tests.AssertNotNil(t, result)
// }

// func TestGetChangelogChangesWorks(t *testing.T) {
// f := getValidSetup()
// f.gitLabVersioning.url = greatNumbersTagProject
// f.gitLabVersioning.destinationDirectory = fmt.Sprintf("%s/%s", os.Getenv("HOME"), "update-changelog")
// repo, err := f.newGitService()
// file, err := os.Create(fmt.Sprintf("%s/%s/%s", os.Getenv("HOME"), "update-changelog", "CHANGELOG"))
// if err != nil {
// fmt.Println("Error to create file", err)
// return
// }
// defer file.Close()
// tests.AssertNoError(t, err)
// changelogSample := getChangelogSample()
// fmt.Fprintf(file, changelogSample)

// result, err := repo.GetChangelogChanges()
// tests.AssertNil(t, err)
// tests.AssertNotNil(t, result)
// tests.AssertEqualValues(t, "type: [fix], message: testing message", result)
// f.cleanLocalRepo(t)
// }

// func TestGetChangelogChangesError(t *testing.T) {
// f := getValidSetup()
// f.gitLabVersioning.url = greatNumbersTagProject
// f.gitLabVersioning.destinationDirectory = fmt.Sprintf("%s/%s", os.Getenv("HOME"), "update-changelog")
// repo, err := f.newGitService()
// file, err := os.Create(fmt.Sprintf("%s/%s/%s", os.Getenv("HOME"), "update-changelog", "CHANGELOG"))
// if err != nil {
// fmt.Println("Error to create file", err)
// return
// }
// defer file.Close()
// tests.AssertNoError(t, err)
// changelogSample := getChangelogSampleWithTwoChanges()
// fmt.Fprintf(file, changelogSample)

// result, err := repo.GetChangelogChanges()
// tests.AssertEmpty(t, result)
// tests.AssertNotNil(t, err)
// f.cleanLocalRepo(t)
// }

// func getChangelogSample() string {
// return `type: [fix], message: testing message

// ## v1.9.9
// type: [fix], message: v199`
// }

// func getChangelogSampleWithTwoChanges() string {
// return `type: [fix], message: testing message
// type: [feat], message: feat message

// ## v1.9.9
// type: [fix], message: v199`
// }
14 changes: 13 additions & 1 deletion src/semantic/semantic.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type RepositoryVersionControl interface {
GetChangeMessage() string
GetCurrentVersion() string
UpgradeRemoteRepository(newVersion string) error
GetChangelogChanges() (string, error)
}

type VersionControl interface {
Expand Down Expand Up @@ -54,6 +55,7 @@ type Semantic struct {
repoVersionControl RepositoryVersionControl
versionControl VersionControl
filesVersionControl FilesVersionControl
changelog bool
}

func (s *Semantic) GenerateNewRelease() error {
Expand All @@ -65,6 +67,15 @@ func (s *Semantic) GenerateNewRelease() error {
CurrentVersion: s.repoVersionControl.GetCurrentVersion(),
}

if s.changelog {
//TODO if the user pass as parameter to generate version from changelog should set changesInfo.message
newChangeMessage, err := s.repoVersionControl.GetChangelogChanges()
if err != nil {
return fmt.Errorf("error to get changelog message / %w", err)
}
changesInfo.Message = newChangeMessage
}

if s.versionControl.MustSkipVersioning(changesInfo.Message) {
s.log.Info(colorCyan + "Semantic Release has been skiped by commit message tag [skip]" + colorReset)
return nil
Expand Down Expand Up @@ -110,13 +121,14 @@ func (s *Semantic) GenerateNewRelease() error {
return nil
}

func New(log Logger, rootPath string, filesToUpdateVariable interface{}, repoVersionControl RepositoryVersionControl, filesVersionControl FilesVersionControl, versionControl VersionControl) *Semantic {
func New(log Logger, rootPath string, filesToUpdateVariable interface{}, repoVersionControl RepositoryVersionControl, filesVersionControl FilesVersionControl, versionControl VersionControl, changelog bool) *Semantic {
return &Semantic{
log: log,
rootPath: rootPath,
filesToUpdateVariable: filesToUpdateVariable,
repoVersionControl: repoVersionControl,
filesVersionControl: filesVersionControl,
versionControl: versionControl,
changelog: changelog,
}
}
9 changes: 7 additions & 2 deletions src/semantic/semantic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ func (r *RepositoryVersionControlMock) UpgradeRemoteRepository(newVersion string
return r.errUpgradeRemoteRepo
}

func (r *RepositoryVersionControlMock) GetChangelogChanges() (string, error) {
change := ""
return change, nil
}

type VersionControlMock struct {
newVersion string
errGetNewVersion error
Expand Down Expand Up @@ -92,8 +97,8 @@ func (f *fixture) NewSemantic() *semantic.Semantic {
if err != nil {
errors.New("error while getting new log")
}

return semantic.New(logger, f.rootPath, f.filesToUpdateVariable, f.repoVersionMock, f.filesVersionMock, f.versionControlMock)
useChangelog := false
return semantic.New(logger, f.rootPath, f.filesToUpdateVariable, f.repoVersionMock, f.filesVersionMock, f.versionControlMock, useChangelog)
}

type upgradeFilesMock struct {
Expand Down
14 changes: 14 additions & 0 deletions src/version/version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,20 @@ func TestGetNewVersionPatchSuccess(t *testing.T) {
tests.AssertEqualValues(t, "1.0.1", actualVersion)
}

func TestGetNewVersionPatchSuccessLot(t *testing.T) {
f := setup()
testcases := map[string]string{
"type:[fix]": "1.0.0",
}
expected := "1.0.1"
for k, v := range testcases {
actualVersion, actualErr := f.versionControl.GetNewVersion(k, v)
tests.AssertNoError(t, actualErr)
tests.AssertEqualValues(t, expected, actualVersion)
}

}

func TestMustSkipVersioningFalse(t *testing.T) {
f := setup()
actualMustSkip := f.versionControl.MustSkipVersioning("type: [fix]")
Expand Down