Skip to content

Commit

Permalink
Merge pull request #10 from s0ders/ci/remove-double-tag
Browse files Browse the repository at this point in the history
removed tag signing
  • Loading branch information
s0ders authored May 1, 2024
2 parents c7ce78c + 32a40f9 commit 06d0efd
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 134 deletions.
1 change: 0 additions & 1 deletion .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -124,5 +124,4 @@ jobs:
- name: Sign Tag and Push
run: |
git tag -s ${{needs.go-build.outputs.semver}}
git push origin ${{needs.go-build.outputs.semver}}
2 changes: 1 addition & 1 deletion .github/workflows/pull-requests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Pull Request

on:
pull_request:
types: [opened, reopened]
types: [opened, reopened, edited]

jobs:
go-build:
Expand Down
93 changes: 11 additions & 82 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,24 @@ them with the right semver number. This program can be used directly via its CLI
<li><a href="#Motivation">Motivation</a></li>
<li><a href="#Install">Install</a></li>
<li><a href="#Usage">Usage</a></li>
<li><a href="#github-actions">GitHub Actions</a></li>
<li><a href="#release-rules">Release Rules</a></li>
<li><a href="#ci-workflow-examples">CI workflow examples</a></li>
</ul>

## Motivation

This project was built to create a lightweight and simple tool to seamlessly automate the semantic versioning on your
Git repository.
Git repository in a language and CI agnostic way.

Following the UNIX philosophy of "make each program do one thing well", it only handles publishing semver tags to your
Git repository, no package publishing or any other features.

All you need to do is choose a release branch (e.g., `main`) and take care to format commits on that branch by following
the [Conventional Commit](https://www.conventionalcommits.org/en/v1.0.0/) specification, which many IDEs plugins offers
to do seamlessly (e.g., [VSCode](https://marketplace.visualstudio.com/items?itemName=vivaxy.vscode-conventional-commits), [IntelliJ](https://plugins.jetbrains.com/plugin/13389-conventional-commit))
All you need have is an initialized Git repository, a release branch (e.g., `main`) and a formatted commit history on
that branch following the [Conventional Commit](https://www.conventionalcommits.org/en/v1.0.0/) specification. Many IDEs
support plugins to help in formatting your commit messages (e.g., [VSCode](https://marketplace.visualstudio.com/items?itemName=vivaxy.vscode-conventional-commits), [IntelliJ](https://plugins.jetbrains.com/plugin/13389-conventional-commit)).

> [!IMPORTANT]
> `go-semver-release` can only read **annotated** Git tags, so if you plan on only using it in dry-run mode to then use
> its output to tag your repository with an other action, make sure the tag you are pushing is annotated, otherwise the
> program will not be able to detect it during its next execution.
> `go-semver-release` can only read **annotated** Git tags. If at some point you need to manually add a semver tag your
> repository, make sure it is annotated, otherwise the program will not be able to detect it.
## Install

Expand All @@ -52,80 +51,10 @@ $ docker pull soders/go-semver-release:latest
$ docker run --rm soders/go-semver-release --help
```

## Prerequisites

- The commits of the Git repository to version must follow the [Conventional Commit](https://www.conventionalcommits.org/en/v1.0.0/) convention.
- The Git repository must already be initialized (i.e., Git `HEAD` does not point to nothing)

## Usage

The program only supports a local mode of execution. This means that it requires the repository to version to be already
present
The program takes the path of the already present Git repository, computes the next semver, tags the local repository
with it and stops. This mode is a good option security-wise since it lets you use the program without having to
configure any kind of right management because it does not require any access token.

Remote mode example:

Local mode example:

```bash
$ go-semver-release local <REPOSITORY_PATH> --rules-path <PATH> --tag-prefix <PREFIX> \
--release-branch <NAME> --dry-run --verbose
```

> [!TIP]
> You can change your tag prefix during the lifetime of your repository (e.g., going from no prefix to `v`), this will
> **not** affect your semver tags history, the program will still be able to recognize previous semver tags.
For more informations about commands and flags usage as well as the default value, simply run:

```bash
$ go-semver-release <COMMAND> --help
```

## GitHub Actions

### Inputs

The action takes the same parameters as those defined in the <a href="#Usage">usage</a> section. Note that the boolean
flags (e.g., `--dry-run`, `--verbose`) need to be passed as a string inside your YAML work-flow due to how Github
Actions works.

### Outputs

The action generate two outputs
- `SEMVER`, the computed semver or the current one if no new were computed, prefixed with the given `tag-prefix` if any;
- `NEW_RELEASE`, whether a new semver was computed or not.

## Release Rules

Release rules define which commit type will trigger a release, and what type of release (i.e., `minor` or `patch`).

> [!WARNING]
> Release type can only be `minor` or `patch`, `major` is reserved for breaking change only.
By default, the program applies the following release rules:
```json
{
"rules": [
{"type": "feat", "release": "minor"},
{"type": "fix", "release": "patch"},
{"type": "perf", "release": "patch"},
{"type": "revert", "release": "patch"}
]
}
```

You can define custom release rules to suit your needs using a JSON file and by passing it to the program as
bellow.

If a commit type (e.g., `chore`) is not specified in you rule file, it will not trigger any kind of release.
Documentation about the CLI usage can be found [here](docs/usage.md).

The following `type` are supported for release rules: `build`, `chore`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`,
`revert`, `style`, `test`.
## CI workflow examples

## Work in progress
- [ ] Support non-annotated tags
- [ ] Fix local action (Docker volumes)
- [ ] Create /docs/ folder for clarity
This tool is voluntarily agnostic of which CI tool is used with it. Examples of workflows with various CI tools can be found [here](docs/workflows.md).
6 changes: 5 additions & 1 deletion cmd/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ var localCmd = &cobra.Command{
default:
logger.Info().Str("new-version", semver.String()).Bool("new-release", true).Msg("new release found")

err = tag.AddTagToRepository(repository, semver, tagPrefix)
tagOpts := &tag.Options{
Prefix: tagPrefix,
}

err = tag.AddToRepository(repository, semver, tagOpts)
if err != nil {
return err
}
Expand Down
6 changes: 3 additions & 3 deletions cmd/local_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func TestLocalCmd_Release(t *testing.T) {
assert.Equal(expectedOut, actualOut, "localCmd output should be equal")

// Check that the tag was actually created on the repository
exists, err := tag.TagExists(repository, expectedTag)
exists, err := tag.Exists(repository, expectedTag)
assert.NoError(err, "failed to check if tag exists")

assert.Equal(true, exists, "tag should exist")
Expand Down Expand Up @@ -158,7 +158,7 @@ func TestLocalCmd_ReleaseWithDryRun(t *testing.T) {
assert.Equal(expectedOut, actualOut, "localCmd output should be equal")

// Check that the tag was actually created on the repository
exists, err := tag.TagExists(repository, expectedTag)
exists, err := tag.Exists(repository, expectedTag)
assert.NoError(err, "failed to check if tag exists")

assert.Equal(false, exists, "tag should not exist, running in dry-run mode")
Expand Down Expand Up @@ -321,7 +321,7 @@ func TestLocalCmd_CustomRules(t *testing.T) {
assert.Equal(expectedOut, actualOut, "localCmd output should be equal")

// Check that the tag was actually created on the repository
exists, err := tag.TagExists(repository, expectedTag)
exists, err := tag.Exists(repository, expectedTag)
assert.NoError(err, "failed to check if tag exists")

assert.Equal(true, exists, "tag should exist")
Expand Down
64 changes: 64 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
## Usage

The program only supports a local mode of execution. This means that it requires that the repository to version is
already present on the local file system.
The program takes the path of local Git repository, computes the next semver and tags the repository.

This mode is a good option security-wise since it lets you use the program without having to configure any kind of
access management since it does not require any access token.

Example:

```bash
$ go-semver-release local <REPOSITORY_PATH> --rules-path <PATH> --tag-prefix <PREFIX> \
--release-branch <NAME> --dry-run --verbose
```

> [!TIP]
> Tag prefix can be changed during the lifetime of a repository (e.g., going from no prefix to `v`), this will
> **not** affect the semver tags history, the program will still be able to recognize previous semver tags.
For more informations about available flags and their default values, run:

```bash
$ go-semver-release <COMMAND> --help
```

## Tag prefix

## Release branch

## Release rules

Release rules define which commit type will trigger a release, and which type of release (i.e., `minor` or `patch`).

> [!IMPORTANT]
> Release type can only be `minor` or `patch`, `major` is reserved for breaking change only.
The following release rules are applied by default:
```json
{
"rules": [
{"type": "feat", "release": "minor"},
{"type": "fix", "release": "patch"},
{"type": "perf", "release": "patch"},
{"type": "revert", "release": "patch"}
]
}
```

You can define custom release rules to suit your needs using a JSON file and by passing it to the program as
bellow.

```bash
$ go-semver-release local <REPOSITORY_PATH> --rules-path ./path/to/custom/rules.json
```

If a commit type (e.g., `chore`) is not specified in you rule file, it will not trigger any kind of release.

The following `type` are supported for release rules: `build`, `chore`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`,
`revert`, `style`, `test`.

## Dry-run

## Signing tag
File renamed without changes.
44 changes: 31 additions & 13 deletions internal/ci/github_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,41 +13,59 @@ func TestCI_GenerateGitHub(t *testing.T) {
assert := assert.New(t)

outputDir, err := os.MkdirTemp("./", "output-*")
assert.NoError(err, "should create temp directory")
if err != nil {
t.Fatalf("failed to create temporary directory: %s", err)
}

defer func(path string) {
err := os.RemoveAll(outputDir)
assert.NoError(err, "should have been able to remove temporary directory")
err = os.RemoveAll(outputDir)
if err != nil {
t.Fatalf("failed to remove temporary directory: %s", err)
}
}(outputDir)

outputFilePath := filepath.Join(outputDir, "output")

outputFile, err := os.OpenFile(outputFilePath, os.O_RDONLY|os.O_CREATE, 0o666)
assert.NoError(err, "should have been able to create output file")
outputFile, err := os.OpenFile(outputFilePath, os.O_RDONLY|os.O_CREATE, 0o644)
if err != nil {
t.Fatalf("failed to create output file: %s", err)
}

defer func() {
err := outputFile.Close()
assert.NoError(err, "should have been able to close output file")
err = outputFile.Close()
if err != nil {
t.Fatalf("failed to create temporary directory: %s", err)
}
}()

outputPath := filepath.Join(outputDir, "output")

err = os.Setenv("GITHUB_OUTPUT", outputPath)
assert.NoError(err, "should have been able to set GITHUB_OUTPUT")
if err != nil {
t.Fatalf("failed to set GITHUB_OUTPUT env. var.: %s", err)
}

defer func() {
err := os.Unsetenv("GITHUB_OUTPUT")
assert.NoError(err, "should have been able to unset GITHUB_OUTPUT")
err = os.Unsetenv("GITHUB_OUTPUT")
if err != nil {
t.Fatalf("failed unset GITHUB_OUTPUT env. var.: %s", err)
}
}()

version, err := semver.New(1, 2, 3)
assert.NoError(err, "should have been able to create version")
if err != nil {
t.Fatalf("failed to create version: %s", err)
}

err = GenerateGitHubOutput("v", version, true)
assert.NoError(err, "should have been able to generate GitHub output")
if err != nil {
t.Fatalf("failed to create github output: %s", err)
}

writtenOutput, err := os.ReadFile(outputPath)
assert.NoError(err, "should have been able to read output file")
if err != nil {
t.Fatalf("failed to read output file: %s", err)
}

want := "\nSEMVER=v1.2.3\nNEW_RELEASE=true\n"
got := string(writtenOutput)
Expand Down
2 changes: 1 addition & 1 deletion internal/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func (p *Parser) fetchLatestSemverTag(repository *git.Repository) (*object.Tag,
return nil, fmt.Errorf("failed to build new semver: %w", err)
}

return tag.NewTagFromSemver(version, head.Hash()), nil
return tag.NewFromSemver(version, head.Hash()), nil
}

p.logger.Debug().Str("tag", latestTag.Name).Msg("latest semver tag found")
Expand Down
Loading

0 comments on commit 06d0efd

Please sign in to comment.