diff --git a/action.yml b/action.yml index 225e3cc..5a3d233 100644 --- a/action.yml +++ b/action.yml @@ -17,6 +17,10 @@ inputs: description: 'List of files that are scanned for version references.' required: false default: "" + update-package-json: + description: 'Update version field in package.json file.' + required: false + default: "false" # Remember to update docs/reference/github-action.md outputs: {} runs: @@ -27,6 +31,7 @@ runs: - --forge=github - --branch=${{ inputs.branch }} - --extra-files="${{ inputs.extra-files }}" + - ${{ inputs.update-package-json == 'true' && '--update-package-json' || '' }} env: GITHUB_TOKEN: "${{ inputs.token }}" GITHUB_USER: "oauth2" diff --git a/cmd/rp/cmd/run.go b/cmd/rp/cmd/run.go index ec11e24..fa48cc0 100644 --- a/cmd/rp/cmd/run.go +++ b/cmd/rp/cmd/run.go @@ -21,21 +21,22 @@ var runCmd = &cobra.Command{ } var ( - flagForge string - flagBranch string - flagOwner string - flagRepo string - flagExtraFiles string + flagForge string + flagBranch string + flagOwner string + flagRepo string + flagExtraFiles string + flagUpdatePackageJson bool ) func init() { rootCmd.AddCommand(runCmd) - runCmd.PersistentFlags().StringVar(&flagForge, "forge", "", "") runCmd.PersistentFlags().StringVar(&flagBranch, "branch", "main", "") runCmd.PersistentFlags().StringVar(&flagOwner, "owner", "", "") runCmd.PersistentFlags().StringVar(&flagRepo, "repo", "", "") runCmd.PersistentFlags().StringVar(&flagExtraFiles, "extra-files", "", "") + runCmd.PersistentFlags().BoolVar(&flagUpdatePackageJson, "update-package-json", false, "") } func run(cmd *cobra.Command, _ []string) error { @@ -48,6 +49,7 @@ func run(cmd *cobra.Command, _ []string) error { "branch", flagBranch, "owner", flagOwner, "repo", flagRepo, + "update-package-json", flagUpdatePackageJson, ) var f forge.Forge @@ -81,6 +83,13 @@ func run(cmd *cobra.Command, _ []string) error { extraFiles := parseExtraFiles(flagExtraFiles) + updaters := []updater.NewUpdater{updater.Generic} + + if flagUpdatePackageJson { + logger.DebugContext(ctx, "package.json updater enabled") + updaters = append(updaters, updater.PackageJson) + } + releaserPleaser := rp.New( f, logger, @@ -88,7 +97,7 @@ func run(cmd *cobra.Command, _ []string) error { conventionalcommits.NewParser(logger), versioning.SemVer, extraFiles, - []updater.NewUpdater{updater.Generic}, + updaters, ) return releaserPleaser.Run(ctx) diff --git a/docs/reference/github-action.md b/docs/reference/github-action.md index eec9789..3a849dc 100644 --- a/docs/reference/github-action.md +++ b/docs/reference/github-action.md @@ -14,11 +14,12 @@ The action does not support floating tags (e.g. `v1`) right now ([#31](https://g The following inputs are supported by the `apricote/releaser-pleaser` GitHub Action. -| Input | Description | Default | Example | -| ------------- | :----------------------------------------------------- | --------------: | -------------------------------------------------------------------: | -| `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}}` | -| `extra-files` | List of files that are scanned for version references. | `""` |
version/version.go
deploy/deployment.yaml |
+| Input | Description | Default | Example |
+| --------------------- | :------------------------------------------------------ | --------------: | -------------------------------------------------------------------: |
+| `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}}` |
+| `extra-files` | List of files that are scanned for version references. | `""` | version/version.go
deploy/deployment.yaml |
+| `update-package-json` | Update version field in package.json file. | `false` | `true` |
## Outputs
diff --git a/docs/reference/gitlab-cicd-component.md b/docs/reference/gitlab-cicd-component.md
index b22d5b2..01a6fc2 100644
--- a/docs/reference/gitlab-cicd-component.md
+++ b/docs/reference/gitlab-cicd-component.md
@@ -23,5 +23,6 @@ The following inputs are supported by the component.
| `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` |
| `extra-files` | List of files that are scanned for version references. | `""` | version/version.go
deploy/deployment.yaml |
+| `update-package-json` | Update version field in package.json file. | `false` | `true` |
| `stage` | Stage the job runs in. Must exists. | `build` | `test` |
| `needs` | Other jobs the releaser-pleaser job depends on. | `[]` | - validate-foo
- prepare-bar |
diff --git a/internal/git/git.go b/internal/git/git.go
index d1db11b..b9c750e 100644
--- a/internal/git/git.go
+++ b/internal/git/git.go
@@ -6,6 +6,7 @@ import (
"io"
"log/slog"
"os"
+ "path/filepath"
"time"
"github.com/go-git/go-git/v5"
@@ -144,7 +145,7 @@ func (r *Repository) UpdateFile(_ context.Context, path string, create bool, upd
updatedContent := string(content)
for _, update := range updaters {
- updatedContent, err = update(updatedContent)
+ updatedContent, err = update(updatedContent, filepath.Base(path))
if err != nil {
return fmt.Errorf("failed to run updater on file %s", path)
}
diff --git a/internal/updater/changelog.go b/internal/updater/changelog.go
index 8bdb9f6..8d6d68c 100644
--- a/internal/updater/changelog.go
+++ b/internal/updater/changelog.go
@@ -15,7 +15,7 @@ var (
)
func Changelog(info ReleaseInfo) Updater {
- return func(content string) (string, error) {
+ return func(content string, filename string) (string, error) {
headerIndex := ChangelogUpdaterHeaderRegex.FindStringIndex(content)
if headerIndex == nil && len(content) != 0 {
return "", fmt.Errorf("unexpected format of CHANGELOG.md, header does not match")
diff --git a/internal/updater/changelog_test.go b/internal/updater/changelog_test.go
index 917cd14..35878b9 100644
--- a/internal/updater/changelog_test.go
+++ b/internal/updater/changelog_test.go
@@ -9,11 +9,12 @@ import (
func TestChangelogUpdater_UpdateContent(t *testing.T) {
tests := []updaterTestCase{
{
- name: "empty file",
- content: "",
- info: ReleaseInfo{ChangelogEntry: "## v1.0.0\n"},
- want: "# Changelog\n\n## v1.0.0\n",
- wantErr: assert.NoError,
+ name: "empty file",
+ content: "",
+ filename: "CHANGELOG.md",
+ info: ReleaseInfo{ChangelogEntry: "## v1.0.0\n"},
+ want: "# Changelog\n\n## v1.0.0\n",
+ wantErr: assert.NoError,
},
{
name: "well-formatted changelog",
@@ -27,7 +28,8 @@ func TestChangelogUpdater_UpdateContent(t *testing.T) {
### Bazuuum
`,
- info: ReleaseInfo{ChangelogEntry: "## v1.0.0\n\n- Version 1, juhu.\n"},
+ filename: "CHANGELOG.md",
+ info: ReleaseInfo{ChangelogEntry: "## v1.0.0\n\n- Version 1, juhu.\n"},
want: `# Changelog
## v1.0.0
@@ -45,11 +47,12 @@ func TestChangelogUpdater_UpdateContent(t *testing.T) {
wantErr: assert.NoError,
},
{
- name: "error on invalid header",
- content: "What even is this file?",
- info: ReleaseInfo{ChangelogEntry: "## v1.0.0\n\n- Version 1, juhu.\n"},
- want: "",
- wantErr: assert.Error,
+ name: "error on invalid header",
+ content: "What even is this file?",
+ filename: "CHANGELOG.md",
+ info: ReleaseInfo{ChangelogEntry: "## v1.0.0\n\n- Version 1, juhu.\n"},
+ want: "",
+ wantErr: assert.Error,
},
}
for _, tt := range tests {
diff --git a/internal/updater/generic.go b/internal/updater/generic.go
index b8d73b0..1883c1a 100644
--- a/internal/updater/generic.go
+++ b/internal/updater/generic.go
@@ -8,7 +8,7 @@ import (
var GenericUpdaterSemVerRegex = regexp.MustCompile(`\d+\.\d+\.\d+(-[\w.]+)?(.*x-releaser-pleaser-version)`)
func Generic(info ReleaseInfo) Updater {
- return func(content string) (string, error) {
+ return func(content string, filename string) (string, error) {
// We strip the "v" prefix to avoid adding/removing it from the users input.
version := strings.TrimPrefix(info.Version, "v")
diff --git a/internal/updater/generic_test.go b/internal/updater/generic_test.go
index e0a8d1d..4cc8952 100644
--- a/internal/updater/generic_test.go
+++ b/internal/updater/generic_test.go
@@ -9,8 +9,9 @@ import (
func TestGenericUpdater_UpdateContent(t *testing.T) {
tests := []updaterTestCase{
{
- name: "single line",
- content: "v1.0.0 // x-releaser-pleaser-version",
+ name: "single line",
+ content: "v1.0.0 // x-releaser-pleaser-version",
+ filename: "version.txt",
info: ReleaseInfo{
Version: "v1.2.0",
},
@@ -18,8 +19,9 @@ func TestGenericUpdater_UpdateContent(t *testing.T) {
wantErr: assert.NoError,
},
{
- name: "multiline line",
- content: "Foooo\n\v1.2.0\nv1.0.0 // x-releaser-pleaser-version\n",
+ name: "multiline line",
+ content: "Foooo\n\v1.2.0\nv1.0.0 // x-releaser-pleaser-version\n",
+ filename: "version.txt",
info: ReleaseInfo{
Version: "v1.2.0",
},
@@ -27,8 +29,9 @@ func TestGenericUpdater_UpdateContent(t *testing.T) {
wantErr: assert.NoError,
},
{
- name: "invalid existing version",
- content: "1.0 // x-releaser-pleaser-version",
+ name: "invalid existing version",
+ content: "1.0 // x-releaser-pleaser-version",
+ filename: "version.txt",
info: ReleaseInfo{
Version: "v1.2.0",
},
@@ -36,8 +39,9 @@ func TestGenericUpdater_UpdateContent(t *testing.T) {
wantErr: assert.NoError,
},
{
- name: "complicated line",
- content: "version: v1.2.0-alpha.1 => Awesome, isnt it? x-releaser-pleaser-version foobar",
+ name: "complicated line",
+ content: "version: v1.2.0-alpha.1 => Awesome, isnt it? x-releaser-pleaser-version foobar",
+ filename: "version.txt",
info: ReleaseInfo{
Version: "v1.2.0",
},
diff --git a/internal/updater/packagejson.go b/internal/updater/packagejson.go
new file mode 100644
index 0000000..58f2cc3
--- /dev/null
+++ b/internal/updater/packagejson.go
@@ -0,0 +1,30 @@
+package updater
+
+import (
+ "regexp"
+ "strings"
+)
+
+// PackageJson creates an updater that modifies the version field in package.json files
+func PackageJson(info ReleaseInfo) Updater {
+ return func(content string, filename string) (string, error) {
+ if filename != "package.json" {
+ return content, nil // No update needed for non-package.json files
+ }
+ // We strip the "v" prefix to match npm versioning convention
+ version := strings.TrimPrefix(info.Version, "v")
+
+ // Regex to match "version": "..." with flexible whitespace and quote styles
+ versionRegex := regexp.MustCompile(`("version"\s*:\s*)"[^"]*"`)
+
+ // Check if the file contains a version field
+ if !versionRegex.MatchString(content) {
+ return content, nil
+ }
+
+ // Replace the version value while preserving the original formatting
+ updatedContent := versionRegex.ReplaceAllString(content, `${1}"`+version+`"`)
+
+ return updatedContent, nil
+ }
+}
diff --git a/internal/updater/packagejson_test.go b/internal/updater/packagejson_test.go
new file mode 100644
index 0000000..4fd196e
--- /dev/null
+++ b/internal/updater/packagejson_test.go
@@ -0,0 +1,70 @@
+package updater
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestPackageJsonUpdater(t *testing.T) {
+ tests := []updaterTestCase{
+ {
+ name: "simple package.json",
+ content: `{"name":"test","version":"1.0.0"}`,
+ filename: "package.json",
+ info: ReleaseInfo{
+ Version: "v2.0.5",
+ },
+ want: `{"name":"test","version":"2.0.5"}`,
+ 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",
+ content: "{\n \"name\": \"test\",\n \"version\": \"1.0.0\",\n \"dependencies\": {\n \"foo\": \"^1.0.0\"\n }\n}",
+ filename: "package.json",
+ info: ReleaseInfo{
+ Version: "v2.0.0",
+ },
+ want: "{\n \"name\": \"test\",\n \"version\": \"2.0.0\",\n \"dependencies\": {\n \"foo\": \"^1.0.0\"\n }\n}",
+ wantErr: assert.NoError,
+ },
+ {
+ name: "invalid json",
+ content: `not json`,
+ filename: "package.json",
+ info: ReleaseInfo{
+ Version: "v2.0.0",
+ },
+ want: `not json`,
+ wantErr: assert.NoError,
+ },
+ {
+ name: "json without version",
+ content: `{"name":"test"}`,
+ filename: "package.json",
+ info: ReleaseInfo{
+ Version: "v2.0.0",
+ },
+ want: `{"name":"test"}`,
+ wantErr: assert.NoError,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ fmt.Println("Running updater test for PackageJson")
+ runUpdaterTest(t, PackageJson, tt)
+ })
+ }
+}
diff --git a/internal/updater/updater.go b/internal/updater/updater.go
index fb773b4..f5fd677 100644
--- a/internal/updater/updater.go
+++ b/internal/updater/updater.go
@@ -5,7 +5,7 @@ type ReleaseInfo struct {
ChangelogEntry string
}
-type Updater func(string) (string, error)
+type Updater func(content string, filename string) (string, error)
type NewUpdater func(ReleaseInfo) Updater
diff --git a/internal/updater/updater_test.go b/internal/updater/updater_test.go
index 0c0c40e..17162ef 100644
--- a/internal/updater/updater_test.go
+++ b/internal/updater/updater_test.go
@@ -8,19 +8,20 @@ import (
)
type updaterTestCase struct {
- name string
- content string
- info ReleaseInfo
- want string
- wantErr assert.ErrorAssertionFunc
+ name string
+ content string
+ filename string
+ info ReleaseInfo
+ want string
+ wantErr assert.ErrorAssertionFunc
}
func runUpdaterTest(t *testing.T, constructor NewUpdater, tt updaterTestCase) {
t.Helper()
- got, err := constructor(tt.info)(tt.content)
- if !tt.wantErr(t, err, fmt.Sprintf("Updater(%v, %v)", tt.content, tt.info)) {
+ got, err := constructor(tt.info)(tt.content, tt.filename)
+ if !tt.wantErr(t, err, fmt.Sprintf("Updater(%v, %v, %v)", tt.content, tt.filename, tt.info)) {
return
}
- assert.Equalf(t, tt.want, got, "Updater(%v, %v)", tt.content, tt.info)
+ assert.Equalf(t, tt.want, got, "Updater(%v, %v, %v)", tt.content, tt.filename, tt.info)
}
diff --git a/templates/run.yml b/templates/run.yml
index c18a330..66037b1 100644
--- a/templates/run.yml
+++ b/templates/run.yml
@@ -12,6 +12,10 @@ spec:
description: 'List of files that are scanned for version references.'
default: ""
+ update-package-json:
+ description: 'Update version field in package.json file.'
+ default: "false"
+
stage:
default: build
description: 'Defines the build stage'
@@ -49,4 +53,5 @@ releaser-pleaser:
rp run \
--forge=gitlab \
--branch=$[[ inputs.branch ]] \
- --extra-files="$[[ inputs.extra-files ]]"
+ --extra-files="$[[ inputs.extra-files ]]" \
+ $([[ inputs.update-package-json == "true" ]] && echo "--update-package-json" || echo "")