mirror of
https://github.com/apricote/releaser-pleaser.git
synced 2026-01-13 13:21:00 +00:00
refactor: let updaters define the files they want to run on (#233)
This change reverses the responsibility for which files the updaters are run on. Now each updater can specify the list of files and wether the files should be created when they do not exist yet. This simplifies the handling of each update in releaserpleaser.go, as we can just iterate over all updaters and call it for each file of that updater. Also update the flags to allow users to easily define which updaters should run.
This commit is contained in:
parent
1e9e0aa5d9
commit
f1aa1a2ef4
20 changed files with 307 additions and 151 deletions
12
action.yml
12
action.yml
|
|
@ -14,15 +14,15 @@ inputs:
|
||||||
required: false
|
required: false
|
||||||
default: ${{ github.token }}
|
default: ${{ github.token }}
|
||||||
extra-files:
|
extra-files:
|
||||||
description: 'List of files that are scanned for version references.'
|
description: 'List of files that are scanned for version references by the generic updater.'
|
||||||
required: false
|
required: false
|
||||||
default: ""
|
default: ""
|
||||||
update-package-json:
|
updaters:
|
||||||
description: 'Update version field in package.json file.'
|
description: "List of updaters that are run. Default updaters can be removed by specifying them as -name. Multiple updaters should be concatenated with a comma. Default Updaters: changelog,generic"
|
||||||
required: false
|
required: false
|
||||||
default: "false"
|
default: ""
|
||||||
# Remember to update docs/reference/github-action.md
|
# Remember to update docs/reference/github-action.md
|
||||||
outputs: {}
|
outputs: { }
|
||||||
runs:
|
runs:
|
||||||
using: 'docker'
|
using: 'docker'
|
||||||
image: docker://ghcr.io/apricote/releaser-pleaser:v0.6.1 # x-releaser-pleaser-version
|
image: docker://ghcr.io/apricote/releaser-pleaser:v0.6.1 # x-releaser-pleaser-version
|
||||||
|
|
@ -31,7 +31,7 @@ runs:
|
||||||
- --forge=github
|
- --forge=github
|
||||||
- --branch=${{ inputs.branch }}
|
- --branch=${{ inputs.branch }}
|
||||||
- --extra-files="${{ inputs.extra-files }}"
|
- --extra-files="${{ inputs.extra-files }}"
|
||||||
- ${{ inputs.update-package-json == 'true' && '--update-package-json' || '' }}
|
- --updaters="${{ inputs.updaters }}"
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: "${{ inputs.token }}"
|
GITHUB_TOKEN: "${{ inputs.token }}"
|
||||||
GITHUB_USER: "oauth2"
|
GITHUB_USER: "oauth2"
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
@ -26,7 +27,7 @@ var (
|
||||||
flagOwner string
|
flagOwner string
|
||||||
flagRepo string
|
flagRepo string
|
||||||
flagExtraFiles string
|
flagExtraFiles string
|
||||||
flagUpdatePackageJson bool
|
flagUpdaters []string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
@ -36,7 +37,7 @@ func init() {
|
||||||
runCmd.PersistentFlags().StringVar(&flagOwner, "owner", "", "")
|
runCmd.PersistentFlags().StringVar(&flagOwner, "owner", "", "")
|
||||||
runCmd.PersistentFlags().StringVar(&flagRepo, "repo", "", "")
|
runCmd.PersistentFlags().StringVar(&flagRepo, "repo", "", "")
|
||||||
runCmd.PersistentFlags().StringVar(&flagExtraFiles, "extra-files", "", "")
|
runCmd.PersistentFlags().StringVar(&flagExtraFiles, "extra-files", "", "")
|
||||||
runCmd.PersistentFlags().BoolVar(&flagUpdatePackageJson, "update-package-json", false, "")
|
runCmd.PersistentFlags().StringSliceVar(&flagUpdaters, "updaters", []string{}, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(cmd *cobra.Command, _ []string) error {
|
func run(cmd *cobra.Command, _ []string) error {
|
||||||
|
|
@ -49,7 +50,6 @@ func run(cmd *cobra.Command, _ []string) error {
|
||||||
"branch", flagBranch,
|
"branch", flagBranch,
|
||||||
"owner", flagOwner,
|
"owner", flagOwner,
|
||||||
"repo", flagRepo,
|
"repo", flagRepo,
|
||||||
"update-package-json", flagUpdatePackageJson,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var f forge.Forge
|
var f forge.Forge
|
||||||
|
|
@ -83,11 +83,19 @@ func run(cmd *cobra.Command, _ []string) error {
|
||||||
|
|
||||||
extraFiles := parseExtraFiles(flagExtraFiles)
|
extraFiles := parseExtraFiles(flagExtraFiles)
|
||||||
|
|
||||||
updaters := []updater.NewUpdater{updater.Generic}
|
updaterNames := parseUpdaters(flagUpdaters)
|
||||||
|
updaters := []updater.Updater{}
|
||||||
if flagUpdatePackageJson {
|
for _, name := range updaterNames {
|
||||||
logger.DebugContext(ctx, "package.json updater enabled")
|
switch name {
|
||||||
updaters = append(updaters, updater.PackageJson)
|
case "generic":
|
||||||
|
updaters = append(updaters, updater.Generic(extraFiles))
|
||||||
|
case "changelog":
|
||||||
|
updaters = append(updaters, updater.Changelog())
|
||||||
|
case "packagejson":
|
||||||
|
updaters = append(updaters, updater.PackageJson())
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown updater: %s", name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
releaserPleaser := rp.New(
|
releaserPleaser := rp.New(
|
||||||
|
|
@ -122,3 +130,22 @@ func parseExtraFiles(input string) []string {
|
||||||
|
|
||||||
return extraFiles
|
return extraFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseUpdaters(input []string) []string {
|
||||||
|
names := []string{"changelog", "generic"}
|
||||||
|
|
||||||
|
for _, u := range input {
|
||||||
|
if strings.HasPrefix(u, "-") {
|
||||||
|
name := u[1:]
|
||||||
|
names = slices.DeleteFunc(names, func(existingName string) bool { return existingName == name })
|
||||||
|
} else {
|
||||||
|
names = append(names, u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we only have unique updaters
|
||||||
|
slices.Sort(names)
|
||||||
|
names = slices.Compact(names)
|
||||||
|
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,3 +57,43 @@ dir/Chart.yaml"`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_parseUpdaters(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []string
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
input: []string{},
|
||||||
|
want: []string{"changelog", "generic"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove defaults",
|
||||||
|
input: []string{"-changelog", "-generic"},
|
||||||
|
want: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove unknown is ignored",
|
||||||
|
input: []string{"-fooo"},
|
||||||
|
want: []string{"changelog", "generic"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add new entry",
|
||||||
|
input: []string{"bar"},
|
||||||
|
want: []string{"bar", "changelog", "generic"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "duplicates are removed",
|
||||||
|
input: []string{"bar", "bar", "changelog"},
|
||||||
|
want: []string{"bar", "changelog", "generic"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := parseUpdaters(tt.input)
|
||||||
|
assert.Equal(t, tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
- [Pull Request Options](reference/pr-options.md)
|
- [Pull Request Options](reference/pr-options.md)
|
||||||
- [GitHub Action](reference/github-action.md)
|
- [GitHub Action](reference/github-action.md)
|
||||||
- [GitLab CI/CD Component](reference/gitlab-cicd-component.md)
|
- [GitLab CI/CD Component](reference/gitlab-cicd-component.md)
|
||||||
|
- [Updaters](reference/updaters.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ In some situations it makes sense to have the current version committed in files
|
||||||
|
|
||||||
## Markers
|
## Markers
|
||||||
|
|
||||||
The line that needs to be updated must have the marker `x-releaser-pleaser-version` somewhere after the version that should be updated.
|
The line that needs to be updated must have the marker
|
||||||
|
`x-releaser-pleaser-version` somewhere after the version that should be updated.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
|
|
@ -28,7 +29,8 @@ You need to tell `releaser-pleaser` which files it should update. This happens t
|
||||||
|
|
||||||
### GitHub Action
|
### GitHub Action
|
||||||
|
|
||||||
In the GitHub Action you can set the `extra-files` input with a list of the files. They need to be formatted as a single multi-line string with one file path per line:
|
In the GitHub Action you can set the
|
||||||
|
`extra-files` input with a list of the files. They need to be formatted as a single multi-line string with one file path per line:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
jobs:
|
jobs:
|
||||||
|
|
@ -44,7 +46,8 @@ jobs:
|
||||||
|
|
||||||
### GitLab CI/CD Component
|
### GitLab CI/CD Component
|
||||||
|
|
||||||
In the GitLab CI/CD Component you can set the `extra-files` input with a list of files. They need to be formatted as a single multi-line string with one file path per line:
|
In the GitLab CI/CD Component you can set the
|
||||||
|
`extra-files` input with a list of files. They need to be formatted as a single multi-line string with one file path per line:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
include:
|
include:
|
||||||
|
|
@ -61,3 +64,4 @@ include:
|
||||||
- **Reference**
|
- **Reference**
|
||||||
- [GitHub Action](../reference/github-action.md#inputs)
|
- [GitHub Action](../reference/github-action.md#inputs)
|
||||||
- [GitLab CI/CD Component](../reference/gitlab-cicd-component.md#inputs)
|
- [GitLab CI/CD Component](../reference/gitlab-cicd-component.md#inputs)
|
||||||
|
- [Updaters](../reference/updaters.md#generic-updater)
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,20 @@ The action is available as `apricote/releaser-pleaser` on GitHub.com.
|
||||||
|
|
||||||
The `apricote/releaser-pleaser` action is released together with `releaser-pleaser` and they share the version number.
|
The `apricote/releaser-pleaser` action is released together with `releaser-pleaser` and they share the version number.
|
||||||
|
|
||||||
The action does not support floating tags (e.g. `v1`) right now ([#31](https://github.com/apricote/releaser-pleaser/issues/31)). You have to use the full version or commit SHA instead: `apricote/releaser-pleaser@v0.2.0`.
|
The action does not support floating tags (e.g.
|
||||||
|
`v1`) right now ([#31](https://github.com/apricote/releaser-pleaser/issues/31)). You have to use the full version or commit SHA instead:
|
||||||
|
`apricote/releaser-pleaser@v0.2.0`.
|
||||||
|
|
||||||
## Inputs
|
## Inputs
|
||||||
|
|
||||||
The following inputs are supported by the `apricote/releaser-pleaser` GitHub Action.
|
The following inputs are supported by the `apricote/releaser-pleaser` GitHub Action.
|
||||||
|
|
||||||
| Input | Description | Default | Example |
|
| Input | Description | Default | Example |
|
||||||
| --------------------- | :------------------------------------------------------ | --------------: | -------------------------------------------------------------------: |
|
|---------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------:|---------------------------------------------------------------------:|
|
||||||
| `branch` | This branch is used as the target for releases. | `main` | `master` |
|
| `branch` | This branch is used as the target for releases. | `main` | `master` |
|
||||||
| `token` | GitHub token for creating and updating release PRs | `$GITHUB_TOKEN` | `${{secrets.RELEASER_PLEASER_TOKEN}}` |
|
| `token` | GitHub token for creating and updating release PRs | `$GITHUB_TOKEN` | `${{secrets.RELEASER_PLEASER_TOKEN}}` |
|
||||||
| `extra-files` | List of files that are scanned for version references. | `""` | <pre><code>version/version.go<br>deploy/deployment.yaml</code></pre> |
|
| `extra-files` | List of files that are scanned for version references by the generic updater. | `""` | <pre><code>version/version.go<br>deploy/deployment.yaml</code></pre> |
|
||||||
| `update-package-json` | Update version field in package.json file. | `false` | `true` |
|
| `updaters` | List of updaters that are run. Default updaters can be removed by specifying them as -name. Multiple updaters should be concatenated with a comma. Default Updaters: changelog,generic | `""` | `-generic,packagejson` |
|
||||||
|
|
||||||
## Outputs
|
## Outputs
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@ The component does not support floating tags (e.g.
|
||||||
The following inputs are supported by the component.
|
The following inputs are supported by the component.
|
||||||
|
|
||||||
| Input | Description | Default | Example |
|
| Input | Description | Default | Example |
|
||||||
| ---------------------- | :-------------------------------------------------------- | ------: | -------------------------------------------------------------------: |
|
|------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------:|---------------------------------------------------------------------:|
|
||||||
| `branch` | This branch is used as the target for releases. | `main` | `master` |
|
| `branch` | This branch is used as the target for releases. | `main` | `master` |
|
||||||
| `token` (**required**) | GitLab access token for creating and updating release PRs | | `$RELEASER_PLEASER_TOKEN` |
|
| `token` (**required**) | GitLab access token for creating and updating release PRs | | `$RELEASER_PLEASER_TOKEN` |
|
||||||
| `extra-files` | List of files that are scanned for version references. | `""` | <pre><code>version/version.go<br>deploy/deployment.yaml</code></pre> |
|
| `extra-files` | List of files that are scanned for version references by the generic updater. | `""` | <pre><code>version/version.go<br>deploy/deployment.yaml</code></pre> |
|
||||||
| `update-package-json` | Update version field in package.json file. | `false` | `true` |
|
| `updaters` | List of updaters that are run. Default updaters can be removed by specifying them as -name. Multiple updaters should be concatenated with a comma. Default Updaters: changelog,generic | `""` | `-generic,packagejson` |
|
||||||
| `stage` | Stage the job runs in. Must exists. | `build` | `test` |
|
| `stage` | Stage the job runs in. Must exists. | `build` | `test` |
|
||||||
| `needs` | Other jobs the releaser-pleaser job depends on. | `[]` | <pre><code>- validate-foo<br>- prepare-bar</code></pre> |
|
| `needs` | Other jobs the releaser-pleaser job depends on. | `[]` | <pre><code>- validate-foo<br>- prepare-bar</code></pre> |
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,21 @@
|
||||||
|
|
||||||
### Changelog
|
### Changelog
|
||||||
|
|
||||||
The Changelog is a file in the repository (`CHANGELOG.md`) that contains the [Release Notes](#release-notes) for every release of that repository. Usually, new releases are added at the top of the file.
|
The Changelog is a file in the repository (
|
||||||
|
`CHANGELOG.md`) that contains the [Release Notes](#release-notes) for every release of that repository. Usually, new releases are added at the top of the file.
|
||||||
|
|
||||||
### Conventional Commits
|
### Conventional Commits
|
||||||
|
|
||||||
[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) is a specification for commit messages. It is the only supported commit message schema in `releaser-pleaser`. Follow the link to learn more.
|
[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) is a specification for commit messages. It is the only supported commit message schema in
|
||||||
|
`releaser-pleaser`. Follow the link to learn more.
|
||||||
|
|
||||||
### Forge
|
### Forge
|
||||||
|
|
||||||
A **forge** is a web-based collaborative software platform for both developing and sharing computer applications.[^wp-forge]
|
A **forge
|
||||||
|
** is a web-based collaborative software platform for both developing and sharing computer applications.[^wp-forge]
|
||||||
|
|
||||||
Right now only **GitHub** is supported. We plan to support **GitLab** in the future ([#4](https://github.com/apricote/releaser-pleaser/issues/4)). For other forges like Forgejo or Gitea, please open an issue and submit a pull request.
|
Right now only **GitHub** is supported. We plan to support **GitLab
|
||||||
|
** in the future ([#4](https://github.com/apricote/releaser-pleaser/issues/4)). For other forges like Forgejo or Gitea, please open an issue and submit a pull request.
|
||||||
|
|
||||||
[^wp-forge]: Quote from [Wikipedia "Forge (software)"](<https://en.wikipedia.org/wiki/Forge_(software)>)
|
[^wp-forge]: Quote from [Wikipedia "Forge (software)"](<https://en.wikipedia.org/wiki/Forge_(software)>)
|
||||||
|
|
||||||
|
|
@ -24,7 +28,8 @@ In `releaser-pleaser` Markdown is used for most texts.
|
||||||
|
|
||||||
### Pre-release
|
### Pre-release
|
||||||
|
|
||||||
Pre-releases are a concept of [SemVer](#semantic-versioning-semver). They follow the normal versioning schema but use a suffix out of `-alpha.X`, `-beta.X` and `-rc.X`.
|
Pre-releases are a concept of [SemVer](#semantic-versioning-semver). They follow the normal versioning schema but use a suffix out of
|
||||||
|
`-alpha.X`, `-beta.X` and `-rc.X`.
|
||||||
|
|
||||||
Pre-releases are not considered "stable" and are usually not recommended for most users.
|
Pre-releases are not considered "stable" and are usually not recommended for most users.
|
||||||
|
|
||||||
|
|
@ -32,7 +37,9 @@ Learn more in the [Pre-releases](../guides/pre-releases.md) guide.
|
||||||
|
|
||||||
### Release Pull Request
|
### Release Pull Request
|
||||||
|
|
||||||
A Release Pull Request is opened by `releaser-pleaser` whenever it finds releasable commits in your project. It proposes a new version number and the Changelog. Once it is merged, `releaser-pleaser` creates a matching release.
|
A Release Pull Request is opened by
|
||||||
|
`releaser-pleaser` whenever it finds releasable commits in your project. It proposes a new version number and the Changelog. Once it is merged,
|
||||||
|
`releaser-pleaser` creates a matching release.
|
||||||
|
|
||||||
Learn more in the [Release Pull Request](../explanation/release-pr.md) explanation.
|
Learn more in the [Release Pull Request](../explanation/release-pr.md) explanation.
|
||||||
|
|
||||||
|
|
@ -44,4 +51,11 @@ Learn more in the [Release Notes customization](../guides/release-notes.md) guid
|
||||||
|
|
||||||
### Semantic Versioning (SemVer)
|
### Semantic Versioning (SemVer)
|
||||||
|
|
||||||
[Semantic Versioning](https://semver.org/) is a specification for version numbers. It is the only supported versioning schema in `releaser-pleaser`. Follow the link to learn more.
|
[Semantic Versioning](https://semver.org/) is a specification for version numbers. It is the only supported versioning schema in
|
||||||
|
`releaser-pleaser`. Follow the link to learn more.
|
||||||
|
|
||||||
|
### Updater
|
||||||
|
|
||||||
|
Updaters can update or create files that will be included in [Release Pull Request](#release-pull-request). Examples of Updaters are
|
||||||
|
`changelog` for `CHANGELOG.md`, `generic` that can update arbitrary files and
|
||||||
|
`packagejson` that knows how to update Node.JS `package.json` files.
|
||||||
33
docs/reference/updaters.md
Normal file
33
docs/reference/updaters.md
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Updaters
|
||||||
|
|
||||||
|
There are different updater for different purposes available.
|
||||||
|
|
||||||
|
They each have a name and may be enabled by default. You can configure which updaters are used through the
|
||||||
|
`updaters` input on GitHub Actions and GitLab CI/CD. This is a comma-delimited list of updaters that should be enabled, for updaters that are enabled by default you can remove them by adding a minus before its name:
|
||||||
|
|
||||||
|
```
|
||||||
|
updaters: -generic,packagejson
|
||||||
|
```
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
- **Name**: `changelog`
|
||||||
|
- **Default**: enabled
|
||||||
|
|
||||||
|
This updater creates the `CHANGELOG.md` file and adds new release notes to it.
|
||||||
|
|
||||||
|
## Generic Updater
|
||||||
|
|
||||||
|
- **Name**: `generic`
|
||||||
|
- **Default**: enabled
|
||||||
|
|
||||||
|
This updater can update any file and only needs a marker on the line. It is enabled by default.
|
||||||
|
|
||||||
|
Learn more about this updater in ["Updating arbitrary files"](../guides/updating-arbitrary-files.md).
|
||||||
|
|
||||||
|
## Node.js `package.json` Updater
|
||||||
|
|
||||||
|
- **Name**: `packagejson`
|
||||||
|
- **Default**: disabled
|
||||||
|
|
||||||
|
This updater can update the `version` field in Node.js `package.json` files. The updater is disabled by default.
|
||||||
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
|
|
@ -14,8 +13,6 @@ import (
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
"github.com/go-git/go-git/v5/plumbing/object"
|
"github.com/go-git/go-git/v5/plumbing/object"
|
||||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||||
|
|
||||||
"github.com/apricote/releaser-pleaser/internal/updater"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -120,7 +117,7 @@ func (r *Repository) Checkout(_ context.Context, branch string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repository) UpdateFile(_ context.Context, path string, create bool, updaters []updater.Updater) error {
|
func (r *Repository) UpdateFile(_ context.Context, path string, create bool, updateHook func(string) (string, error)) error {
|
||||||
worktree, err := r.r.Worktree()
|
worktree, err := r.r.Worktree()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -142,13 +139,9 @@ func (r *Repository) UpdateFile(_ context.Context, path string, create bool, upd
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedContent := string(content)
|
updatedContent, err := updateHook(string(content))
|
||||||
|
|
||||||
for _, update := range updaters {
|
|
||||||
updatedContent, err = update(updatedContent, filepath.Base(path))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to run updater on file %s", path)
|
return fmt.Errorf("failed to run update hook on file %s", path)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = file.Truncate(0)
|
err = file.Truncate(0)
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,23 @@ var (
|
||||||
ChangelogUpdaterHeaderRegex = regexp.MustCompile(`^# Changelog\n`)
|
ChangelogUpdaterHeaderRegex = regexp.MustCompile(`^# Changelog\n`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func Changelog(info ReleaseInfo) Updater {
|
func Changelog() Updater {
|
||||||
return func(content string, filename string) (string, error) {
|
return changelog{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type changelog struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c changelog) Files() []string {
|
||||||
|
return []string{ChangelogFile}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c changelog) CreateNewFiles() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c changelog) Update(info ReleaseInfo) func(content string) (string, error) {
|
||||||
|
return func(content string) (string, error) {
|
||||||
headerIndex := ChangelogUpdaterHeaderRegex.FindStringIndex(content)
|
headerIndex := ChangelogUpdaterHeaderRegex.FindStringIndex(content)
|
||||||
if headerIndex == nil && len(content) != 0 {
|
if headerIndex == nil && len(content) != 0 {
|
||||||
return "", fmt.Errorf("unexpected format of CHANGELOG.md, header does not match")
|
return "", fmt.Errorf("unexpected format of CHANGELOG.md, header does not match")
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,19 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestChangelogUpdater_UpdateContent(t *testing.T) {
|
func TestChangelogUpdater_Files(t *testing.T) {
|
||||||
|
assert.Equal(t, []string{"CHANGELOG.md"}, Changelog().Files())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChangelogUpdater_CreateNewFiles(t *testing.T) {
|
||||||
|
assert.True(t, Changelog().CreateNewFiles())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChangelogUpdater_Update(t *testing.T) {
|
||||||
tests := []updaterTestCase{
|
tests := []updaterTestCase{
|
||||||
{
|
{
|
||||||
name: "empty file",
|
name: "empty file",
|
||||||
content: "",
|
content: "",
|
||||||
filename: "CHANGELOG.md",
|
|
||||||
info: ReleaseInfo{ChangelogEntry: "## v1.0.0\n"},
|
info: ReleaseInfo{ChangelogEntry: "## v1.0.0\n"},
|
||||||
want: "# Changelog\n\n## v1.0.0\n",
|
want: "# Changelog\n\n## v1.0.0\n",
|
||||||
wantErr: assert.NoError,
|
wantErr: assert.NoError,
|
||||||
|
|
@ -28,7 +35,6 @@ func TestChangelogUpdater_UpdateContent(t *testing.T) {
|
||||||
|
|
||||||
### Bazuuum
|
### Bazuuum
|
||||||
`,
|
`,
|
||||||
filename: "CHANGELOG.md",
|
|
||||||
info: ReleaseInfo{ChangelogEntry: "## v1.0.0\n\n- Version 1, juhu.\n"},
|
info: ReleaseInfo{ChangelogEntry: "## v1.0.0\n\n- Version 1, juhu.\n"},
|
||||||
want: `# Changelog
|
want: `# Changelog
|
||||||
|
|
||||||
|
|
@ -49,7 +55,6 @@ func TestChangelogUpdater_UpdateContent(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "error on invalid header",
|
name: "error on invalid header",
|
||||||
content: "What even is this file?",
|
content: "What even is this file?",
|
||||||
filename: "CHANGELOG.md",
|
|
||||||
info: ReleaseInfo{ChangelogEntry: "## v1.0.0\n\n- Version 1, juhu.\n"},
|
info: ReleaseInfo{ChangelogEntry: "## v1.0.0\n\n- Version 1, juhu.\n"},
|
||||||
want: "",
|
want: "",
|
||||||
wantErr: assert.Error,
|
wantErr: assert.Error,
|
||||||
|
|
@ -57,7 +62,7 @@ func TestChangelogUpdater_UpdateContent(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
runUpdaterTest(t, Changelog, tt)
|
runUpdaterTest(t, Changelog(), tt)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,26 @@ import (
|
||||||
|
|
||||||
var GenericUpdaterSemVerRegex = regexp.MustCompile(`\d+\.\d+\.\d+(-[\w.]+)?(.*x-releaser-pleaser-version)`)
|
var GenericUpdaterSemVerRegex = regexp.MustCompile(`\d+\.\d+\.\d+(-[\w.]+)?(.*x-releaser-pleaser-version)`)
|
||||||
|
|
||||||
func Generic(info ReleaseInfo) Updater {
|
func Generic(files []string) Updater {
|
||||||
return func(content string, filename string) (string, error) {
|
return generic{
|
||||||
|
files: files,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type generic struct {
|
||||||
|
files []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g generic) Files() []string {
|
||||||
|
return g.files
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g generic) CreateNewFiles() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g generic) Update(info ReleaseInfo) func(content string) (string, error) {
|
||||||
|
return func(content string) (string, error) {
|
||||||
// We strip the "v" prefix to avoid adding/removing it from the users input.
|
// We strip the "v" prefix to avoid adding/removing it from the users input.
|
||||||
version := strings.TrimPrefix(info.Version, "v")
|
version := strings.TrimPrefix(info.Version, "v")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,19 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenericUpdater_UpdateContent(t *testing.T) {
|
func TestGenericUpdater_Files(t *testing.T) {
|
||||||
|
assert.Equal(t, []string{"foo.bar", "version.txt"}, Generic([]string{"foo.bar", "version.txt"}).Files())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenericUpdater_CreateNewFiles(t *testing.T) {
|
||||||
|
assert.False(t, Generic([]string{}).CreateNewFiles())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenericUpdater_Update(t *testing.T) {
|
||||||
tests := []updaterTestCase{
|
tests := []updaterTestCase{
|
||||||
{
|
{
|
||||||
name: "single line",
|
name: "single line",
|
||||||
content: "v1.0.0 // x-releaser-pleaser-version",
|
content: "v1.0.0 // x-releaser-pleaser-version",
|
||||||
filename: "version.txt",
|
|
||||||
info: ReleaseInfo{
|
info: ReleaseInfo{
|
||||||
Version: "v1.2.0",
|
Version: "v1.2.0",
|
||||||
},
|
},
|
||||||
|
|
@ -21,7 +28,6 @@ func TestGenericUpdater_UpdateContent(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "multiline line",
|
name: "multiline line",
|
||||||
content: "Foooo\n\v1.2.0\nv1.0.0 // x-releaser-pleaser-version\n",
|
content: "Foooo\n\v1.2.0\nv1.0.0 // x-releaser-pleaser-version\n",
|
||||||
filename: "version.txt",
|
|
||||||
info: ReleaseInfo{
|
info: ReleaseInfo{
|
||||||
Version: "v1.2.0",
|
Version: "v1.2.0",
|
||||||
},
|
},
|
||||||
|
|
@ -31,7 +37,6 @@ func TestGenericUpdater_UpdateContent(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "invalid existing version",
|
name: "invalid existing version",
|
||||||
content: "1.0 // x-releaser-pleaser-version",
|
content: "1.0 // x-releaser-pleaser-version",
|
||||||
filename: "version.txt",
|
|
||||||
info: ReleaseInfo{
|
info: ReleaseInfo{
|
||||||
Version: "v1.2.0",
|
Version: "v1.2.0",
|
||||||
},
|
},
|
||||||
|
|
@ -41,7 +46,6 @@ func TestGenericUpdater_UpdateContent(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "complicated line",
|
name: "complicated line",
|
||||||
content: "version: v1.2.0-alpha.1 => Awesome, isnt it? x-releaser-pleaser-version foobar",
|
content: "version: v1.2.0-alpha.1 => Awesome, isnt it? x-releaser-pleaser-version foobar",
|
||||||
filename: "version.txt",
|
|
||||||
info: ReleaseInfo{
|
info: ReleaseInfo{
|
||||||
Version: "v1.2.0",
|
Version: "v1.2.0",
|
||||||
},
|
},
|
||||||
|
|
@ -51,7 +55,7 @@ func TestGenericUpdater_UpdateContent(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
runUpdaterTest(t, Generic, tt)
|
runUpdaterTest(t, Generic([]string{"version.txt"}), tt)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,22 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// PackageJson creates an updater that modifies the version field in package.json files
|
// PackageJson creates an updater that modifies the version field in package.json files
|
||||||
func PackageJson(info ReleaseInfo) Updater {
|
func PackageJson() Updater {
|
||||||
return func(content string, filename string) (string, error) {
|
return packagejson{}
|
||||||
if filename != "package.json" {
|
}
|
||||||
return content, nil // No update needed for non-package.json files
|
|
||||||
}
|
type packagejson struct{}
|
||||||
|
|
||||||
|
func (p packagejson) Files() []string {
|
||||||
|
return []string{"package.json"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p packagejson) CreateNewFiles() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p packagejson) Update(info ReleaseInfo) func(content string) (string, error) {
|
||||||
|
return func(content string) (string, error) {
|
||||||
// We strip the "v" prefix to match npm versioning convention
|
// We strip the "v" prefix to match npm versioning convention
|
||||||
version := strings.TrimPrefix(info.Version, "v")
|
version := strings.TrimPrefix(info.Version, "v")
|
||||||
|
|
||||||
|
|
@ -23,8 +34,6 @@ func PackageJson(info ReleaseInfo) Updater {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace the version value while preserving the original formatting
|
// Replace the version value while preserving the original formatting
|
||||||
updatedContent := versionRegex.ReplaceAllString(content, `${1}"`+version+`"`)
|
return versionRegex.ReplaceAllString(content, `${1}"`+version+`"`), nil
|
||||||
|
|
||||||
return updatedContent, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,33 @@
|
||||||
package updater
|
package updater
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPackageJsonUpdater(t *testing.T) {
|
func TestPackageJsonUpdater_Files(t *testing.T) {
|
||||||
|
assert.Equal(t, []string{"package.json"}, PackageJson().Files())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPackageJsonUpdater_CreateNewFiles(t *testing.T) {
|
||||||
|
assert.False(t, PackageJson().CreateNewFiles())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPackageJsonUpdater_Update(t *testing.T) {
|
||||||
tests := []updaterTestCase{
|
tests := []updaterTestCase{
|
||||||
{
|
{
|
||||||
name: "simple package.json",
|
name: "simple package.json",
|
||||||
content: `{"name":"test","version":"1.0.0"}`,
|
content: `{"name":"test","version":"1.0.0"}`,
|
||||||
filename: "package.json",
|
|
||||||
info: ReleaseInfo{
|
info: ReleaseInfo{
|
||||||
Version: "v2.0.5",
|
Version: "v2.0.5",
|
||||||
},
|
},
|
||||||
want: `{"name":"test","version":"2.0.5"}`,
|
want: `{"name":"test","version":"2.0.5"}`,
|
||||||
wantErr: assert.NoError,
|
wantErr: assert.NoError,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "simple package.json, wrong name",
|
|
||||||
content: `{"name":"test","version":"1.0.0"}`,
|
|
||||||
filename: "nopackage.json",
|
|
||||||
info: ReleaseInfo{
|
|
||||||
Version: "v2.0.5",
|
|
||||||
},
|
|
||||||
want: `{"name":"test","version":"1.0.0"}`,
|
|
||||||
wantErr: assert.NoError,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "complex package.json",
|
name: "complex package.json",
|
||||||
content: "{\n \"name\": \"test\",\n \"version\": \"1.0.0\",\n \"dependencies\": {\n \"foo\": \"^1.0.0\"\n }\n}",
|
content: "{\n \"name\": \"test\",\n \"version\": \"1.0.0\",\n \"dependencies\": {\n \"foo\": \"^1.0.0\"\n }\n}",
|
||||||
filename: "package.json",
|
|
||||||
info: ReleaseInfo{
|
info: ReleaseInfo{
|
||||||
Version: "v2.0.0",
|
Version: "v2.0.0",
|
||||||
},
|
},
|
||||||
|
|
@ -42,7 +37,6 @@ func TestPackageJsonUpdater(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "invalid json",
|
name: "invalid json",
|
||||||
content: `not json`,
|
content: `not json`,
|
||||||
filename: "package.json",
|
|
||||||
info: ReleaseInfo{
|
info: ReleaseInfo{
|
||||||
Version: "v2.0.0",
|
Version: "v2.0.0",
|
||||||
},
|
},
|
||||||
|
|
@ -52,7 +46,6 @@ func TestPackageJsonUpdater(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "json without version",
|
name: "json without version",
|
||||||
content: `{"name":"test"}`,
|
content: `{"name":"test"}`,
|
||||||
filename: "package.json",
|
|
||||||
info: ReleaseInfo{
|
info: ReleaseInfo{
|
||||||
Version: "v2.0.0",
|
Version: "v2.0.0",
|
||||||
},
|
},
|
||||||
|
|
@ -63,8 +56,7 @@ func TestPackageJsonUpdater(t *testing.T) {
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
fmt.Println("Running updater test for PackageJson")
|
runUpdaterTest(t, PackageJson(), tt)
|
||||||
runUpdaterTest(t, PackageJson, tt)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,11 @@ type ReleaseInfo struct {
|
||||||
ChangelogEntry string
|
ChangelogEntry string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Updater func(content string, filename string) (string, error)
|
type Updater interface {
|
||||||
|
Files() []string
|
||||||
|
CreateNewFiles() bool
|
||||||
|
Update(info ReleaseInfo) func(content string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
type NewUpdater func(ReleaseInfo) Updater
|
type NewUpdater func(ReleaseInfo) Updater
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,18 +10,17 @@ import (
|
||||||
type updaterTestCase struct {
|
type updaterTestCase struct {
|
||||||
name string
|
name string
|
||||||
content string
|
content string
|
||||||
filename string
|
|
||||||
info ReleaseInfo
|
info ReleaseInfo
|
||||||
want string
|
want string
|
||||||
wantErr assert.ErrorAssertionFunc
|
wantErr assert.ErrorAssertionFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func runUpdaterTest(t *testing.T, constructor NewUpdater, tt updaterTestCase) {
|
func runUpdaterTest(t *testing.T, u Updater, tt updaterTestCase) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
got, err := constructor(tt.info)(tt.content, tt.filename)
|
got, err := u.Update(tt.info)(tt.content)
|
||||||
if !tt.wantErr(t, err, fmt.Sprintf("Updater(%v, %v, %v)", tt.content, tt.filename, tt.info)) {
|
if !tt.wantErr(t, err, fmt.Sprintf("Updater(%v, %v)", tt.content, tt.info)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
assert.Equalf(t, tt.want, got, "Updater(%v, %v, %v)", tt.content, tt.filename, tt.info)
|
assert.Equalf(t, tt.want, got, "Updater(%v, %v)", tt.content, tt.info)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,10 @@ type ReleaserPleaser struct {
|
||||||
commitParser commitparser.CommitParser
|
commitParser commitparser.CommitParser
|
||||||
versioning versioning.Strategy
|
versioning versioning.Strategy
|
||||||
extraFiles []string
|
extraFiles []string
|
||||||
updaters []updater.NewUpdater
|
updaters []updater.Updater
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(forge forge.Forge, logger *slog.Logger, targetBranch string, commitParser commitparser.CommitParser, versioningStrategy versioning.Strategy, extraFiles []string, updaters []updater.NewUpdater) *ReleaserPleaser {
|
func New(forge forge.Forge, logger *slog.Logger, targetBranch string, commitParser commitparser.CommitParser, versioningStrategy versioning.Strategy, extraFiles []string, updaters []updater.Updater) *ReleaserPleaser {
|
||||||
return &ReleaserPleaser{
|
return &ReleaserPleaser{
|
||||||
forge: forge,
|
forge: forge,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
|
@ -281,16 +281,12 @@ func (rp *ReleaserPleaser) runReconcileReleasePR(ctx context.Context) error {
|
||||||
// Info for updaters
|
// Info for updaters
|
||||||
info := updater.ReleaseInfo{Version: nextVersion, ChangelogEntry: changelogEntry}
|
info := updater.ReleaseInfo{Version: nextVersion, ChangelogEntry: changelogEntry}
|
||||||
|
|
||||||
err = repo.UpdateFile(ctx, updater.ChangelogFile, true, updater.WithInfo(info, updater.Changelog))
|
for _, u := range rp.updaters {
|
||||||
|
for _, file := range u.Files() {
|
||||||
|
err = repo.UpdateFile(ctx, file, u.CreateNewFiles(), u.Update(info))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to update changelog file: %w", err)
|
return fmt.Errorf("failed to run updater %T: %w", u, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, path := range rp.extraFiles {
|
|
||||||
// TODO: Check for missing files
|
|
||||||
err = repo.UpdateFile(ctx, path, false, updater.WithInfo(info, rp.updaters...))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to run file updater: %w", err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@ spec:
|
||||||
description: "GitLab token for creating and updating release MRs."
|
description: "GitLab token for creating and updating release MRs."
|
||||||
|
|
||||||
extra-files:
|
extra-files:
|
||||||
description: 'List of files that are scanned for version references.'
|
description: 'List of files that are scanned for version references by the generic updater.'
|
||||||
default: ""
|
default: ""
|
||||||
|
|
||||||
update-package-json:
|
updaters:
|
||||||
description: 'Update version field in package.json file.'
|
description: "List of updaters that are run. Default updaters can be removed by specifying them as -name. Multiple updaters should be concatenated with a comma. Default Updaters: changelog,generic"
|
||||||
default: "false"
|
default: ""
|
||||||
|
|
||||||
stage:
|
stage:
|
||||||
default: build
|
default: build
|
||||||
|
|
@ -54,4 +54,4 @@ releaser-pleaser:
|
||||||
--forge=gitlab \
|
--forge=gitlab \
|
||||||
--branch=$[[ inputs.branch ]] \
|
--branch=$[[ inputs.branch ]] \
|
||||||
--extra-files="$[[ inputs.extra-files ]]" \
|
--extra-files="$[[ inputs.extra-files ]]" \
|
||||||
$([[ inputs.update-package-json == "true" ]] && echo "--update-package-json" || echo "")
|
--updaters="$[[ inputs.updaters ]]"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue