diff --git a/.github/actions/setup-mdbook/action.yaml b/.github/actions/setup-mdbook/action.yaml new file mode 100644 index 0000000..23e0665 --- /dev/null +++ b/.github/actions/setup-mdbook/action.yaml @@ -0,0 +1,16 @@ +name: "Setup mdbook" +inputs: + version: + description: "mdbook version" + +runs: + using: composite + steps: + - name: Setup mdbook + shell: bash + env: + url: https://github.com/rust-lang/mdbook/releases/download/${{ inputs.version }}/mdbook-${{ inputs.version }}-x86_64-unknown-linux-gnu.tar.gz + run: | + mkdir mdbook + curl -sSL "$url" | tar -xz --directory=./mdbook + echo `pwd`/mdbook >> $GITHUB_PATH diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 5110561..83d07be 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -69,15 +69,6 @@ ': (?.+) # renovate: datasource=(?[a-z-]+) depName=(?[^\\s]+)(?: lookupName=(?[^\\s]+))?(?: versioning=(?[a-z-]+))?(?: extractVersion=(?[^\\s]+))?', ], }, - { - customType: 'regex', - managerFilePatterns: [ - '/.+\\.toml$/' - ], - matchStrings: [ - '= "(?.+)" # renovate: datasource=(?[a-z-]+) depName=(?[^\\s]+)(?: lookupName=(?[^\\s]+))?(?: versioning=(?[a-z-]+))?(?: extractVersion=(?[^\\s]+))?', - ], - } ], postUpdateOptions: [ 'gomodUpdateImportPaths', diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a5f2854..eee89f7 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -2,7 +2,7 @@ name: ci on: push: - branches: [ main ] + branches: [main] pull_request: jobs: @@ -10,68 +10,48 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - - - uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3 - - - name: Run golangci-lint - uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8 - with: - install-mode: none - args: --timeout 5m - - test: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - - - uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3 - - - name: Run tests - run: go test -v -race -coverpkg=./... -coverprofile=coverage.txt ./... - - - name: Upload results to Codecov - uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5 - with: - token: ${{ secrets.CODECOV_TOKEN }} - flags: unit - - test-e2e-forgejo: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: Set up Go uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5 with: go-version-file: go.mod - # We can not use "jobs..services". - # We want to mount the config file, which is only available after "Checkout". - - name: Start Forgejo - working-directory: test/e2e/forgejo - run: docker compose up --wait + - name: Run golangci-lint + uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8 + with: + version: v2.2.1 # renovate: datasource=github-releases depName=golangci/golangci-lint + args: --timeout 5m + + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + + - name: Set up Go + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5 + with: + go-version-file: go.mod - name: Run tests - run: go test -tags e2e_forgejo -v -race -coverpkg=./... -coverprofile=coverage.txt ./test/e2e/forgejo - + run: go test -v -race -coverpkg=./... -coverprofile=coverage.txt ./... - name: Upload results to Codecov - uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5 + uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5 with: token: ${{ secrets.CODECOV_TOKEN }} - flags: e2e go-mod-tidy: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - - uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3 + - name: Set up Go + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5 + with: + go-version-file: go.mod - name: Run go mod tidy run: go mod tidy diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index fcafe52..f9baa0a 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -13,11 +13,13 @@ jobs: id-token: write # To update the deployment status steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: lfs: "true" - - uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3 + - uses: ./.github/actions/setup-mdbook + with: + version: v0.4.51 # renovate: datasource=github-releases depName=rust-lang/mdbook - name: Build Book working-directory: docs @@ -27,7 +29,7 @@ jobs: uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5 - name: Upload artifact - uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4 + uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3 with: # Upload entire repository path: "docs/book" diff --git a/.github/workflows/mirror.yaml b/.github/workflows/mirror.yaml index 940d149..e287aed 100644 --- a/.github/workflows/mirror.yaml +++ b/.github/workflows/mirror.yaml @@ -11,7 +11,7 @@ jobs: REMOTE: mirror steps: - name: Checkout - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: # Need all to fetch all tags so we can push them fetch-depth: 0 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 7da1f8a..6da204d 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -14,16 +14,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - - uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3 - - - name: Prepare ko - run: | - echo "${{ github.token }}" | ko login ghcr.io --username "dummy" --password-stdin - - repo=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]') - echo "KO_DOCKER_REPO=ghcr.io/${repo}" - echo "KO_DOCKER_REPO=ghcr.io/${repo}" >> $GITHUB_ENV + - name: Set up Go + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5 + with: + go-version-file: go.mod + - uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9 - run: ko build --bare --tags ${{ github.ref_name }} github.com/apricote/releaser-pleaser/cmd/rp diff --git a/.github/workflows/releaser-pleaser.yaml b/.github/workflows/releaser-pleaser.yaml index 8d1e62b..aa5097f 100644 --- a/.github/workflows/releaser-pleaser.yaml +++ b/.github/workflows/releaser-pleaser.yaml @@ -2,7 +2,7 @@ name: releaser-pleaser on: push: - branches: [ main ] + branches: [main] # Using pull_request_target to avoid tainting the actual release PR with code from open feature pull requests pull_request_target: types: @@ -17,7 +17,7 @@ concurrency: group: releaser-pleaser cancel-in-progress: true -permissions: { } +permissions: {} jobs: releaser-pleaser: @@ -25,18 +25,23 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: ref: main - - uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3 + - name: Set up Go + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5 + with: + go-version-file: go.mod # Build container image from current commit and replace image ref in `action.yml` # Without this, any new flags in `action.yml` would break the job in this repository until the new # version is released. But a new version can only be released if this job works. + - uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9 - run: ko build --bare --local --platform linux/amd64 --tags ci github.com/apricote/releaser-pleaser/cmd/rp - - run: "sed -i 's|image: .*$|image: docker://ko.local:ci|g' action.yml" + - run: mkdir -p .github/actions/releaser-pleaser + - run: "sed -i 's|image: .*$|image: docker://ghcr.io/apricote/releaser-pleaser:ci|g' action.yml" # Dogfood the action to make sure it works for users. - name: releaser-pleaser diff --git a/.golangci.yaml b/.golangci.yaml index b66f0a5..5af5a17 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -48,10 +48,6 @@ linters: - name: exported disabled: true - gomoddirectives: - replace-allow-list: - - codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 - formatters: enable: - gci diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ba0bc8..1cb192f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,34 +1,5 @@ # Changelog -## [v0.7.1](https://github.com/apricote/releaser-pleaser/releases/tag/v0.7.1) - -### Bug Fixes - -- using code blocks within release-notes (#275) -- no html escaping for changelog template (#277) - -## [v0.7.0](https://github.com/apricote/releaser-pleaser/releases/tag/v0.7.0) - -### Highlights :sparkles: - -#### Update version in `package.json` - -Thanks to @Mattzi it is now possible to use `releaser-pleaser` in Javascript/Node.js projects with a `package.json` file. - -You can enable this with the option `updaters: packagejson` in the GitHub Actions / GitLab CI/CD config. - -All updaters, including the defaults `changelog` and `generic` can now be enabled and disabled through this field. You can find a full list in the [documentation](https://apricote.github.io/releaser-pleaser/reference/updaters.html). - -### Features - -- add updater for package.json (#213) -- highlight breaking changes in release notes (#234) - -### Bug Fixes - -- filter out empty updaters in input (#235) -- **github**: duplicate release pr when process is stopped at wrong moment (#236) - ## [v0.6.1](https://github.com/apricote/releaser-pleaser/releases/tag/v0.6.1) ### Bug Fixes diff --git a/action.yml b/action.yml index 5d25210..225e3cc 100644 --- a/action.yml +++ b/action.yml @@ -14,24 +14,19 @@ inputs: required: false default: ${{ github.token }} extra-files: - description: 'List of files that are scanned for version references by the generic updater.' - required: false - default: "" - updaters: - 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" + description: 'List of files that are scanned for version references.' required: false default: "" # Remember to update docs/reference/github-action.md -outputs: { } +outputs: {} runs: using: 'docker' - image: docker://ghcr.io/apricote/releaser-pleaser:v0.7.1 # x-releaser-pleaser-version + image: docker://ghcr.io/apricote/releaser-pleaser:v0.6.1 # x-releaser-pleaser-version args: - run - --forge=github - --branch=${{ inputs.branch }} - --extra-files="${{ inputs.extra-files }}" - - --updaters="${{ inputs.updaters }}" env: GITHUB_TOKEN: "${{ inputs.token }}" GITHUB_USER: "oauth2" diff --git a/cmd/rp/cmd/root.go b/cmd/rp/cmd/root.go index f2dd180..2799e6d 100644 --- a/cmd/rp/cmd/root.go +++ b/cmd/rp/cmd/root.go @@ -7,23 +7,21 @@ import ( "os/signal" "runtime/debug" "syscall" + "time" + "github.com/lmittmann/tint" "github.com/spf13/cobra" ) -func NewRootCmd() *cobra.Command { - var cmd = &cobra.Command{ - Use: "rp", - Short: "", - Long: ``, - Version: version(), - SilenceUsage: true, // Makes it harder to find the actual error - SilenceErrors: true, // We log manually with slog - } +var logger *slog.Logger - cmd.AddCommand(newRunCommand()) - - return cmd +var rootCmd = &cobra.Command{ + Use: "rp", + Short: "", + Long: ``, + Version: version(), + SilenceUsage: true, // Makes it harder to find the actual error + SilenceErrors: true, // We log manually with slog } func version() string { @@ -68,13 +66,24 @@ func Execute() { // Make sure to stop listening on signals after receiving the first signal to hand control of the signal back // to the runtime. The Go runtime implements a "force shutdown" if the signal is received again. <-ctx.Done() - slog.InfoContext(ctx, "Received shutdown signal, stopping...") + logger.InfoContext(ctx, "Received shutdown signal, stopping...") stop() }() - err := NewRootCmd().ExecuteContext(ctx) + err := rootCmd.ExecuteContext(ctx) if err != nil { - slog.ErrorContext(ctx, err.Error()) + logger.ErrorContext(ctx, err.Error()) os.Exit(1) } } + +func init() { + logger = slog.New( + tint.NewHandler(os.Stderr, &tint.Options{ + Level: slog.LevelDebug, + TimeFormat: time.RFC3339, + }), + ) + + slog.SetDefault(logger) +} diff --git a/cmd/rp/cmd/run.go b/cmd/rp/cmd/run.go index 3d575b0..ec11e24 100644 --- a/cmd/rp/cmd/run.go +++ b/cmd/rp/cmd/run.go @@ -2,8 +2,6 @@ package cmd import ( "fmt" - "log/slog" - "slices" "strings" "github.com/spf13/cobra" @@ -11,130 +9,89 @@ import ( rp "github.com/apricote/releaser-pleaser" "github.com/apricote/releaser-pleaser/internal/commitparser/conventionalcommits" "github.com/apricote/releaser-pleaser/internal/forge" - "github.com/apricote/releaser-pleaser/internal/forge/forgejo" "github.com/apricote/releaser-pleaser/internal/forge/github" "github.com/apricote/releaser-pleaser/internal/forge/gitlab" - "github.com/apricote/releaser-pleaser/internal/log" "github.com/apricote/releaser-pleaser/internal/updater" "github.com/apricote/releaser-pleaser/internal/versioning" ) -func newRunCommand() *cobra.Command { - var ( - flagForge string - flagBranch string - flagOwner string - flagRepo string - flagExtraFiles string - flagUpdaters []string +var runCmd = &cobra.Command{ + Use: "run", + RunE: run, +} - flagAPIURL string - flagAPIToken string - flagUsername string +var ( + flagForge string + flagBranch string + flagOwner string + flagRepo string + flagExtraFiles string +) + +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", "", "") +} + +func run(cmd *cobra.Command, _ []string) error { + ctx := cmd.Context() + + var err error + + logger.DebugContext(ctx, "run called", + "forge", flagForge, + "branch", flagBranch, + "owner", flagOwner, + "repo", flagRepo, ) - var cmd = &cobra.Command{ - Use: "run", - RunE: func(cmd *cobra.Command, _ []string) error { - ctx := cmd.Context() - logger := log.GetLogger(cmd.ErrOrStderr()) + var f forge.Forge - var err error - - logger.DebugContext(ctx, "run called", - "forge", flagForge, - "branch", flagBranch, - "owner", flagOwner, - "repo", flagRepo, - ) - - var f forge.Forge - - forgeOptions := forge.Options{ - Repository: flagRepo, - BaseBranch: flagBranch, - } - - switch flagForge { - case "gitlab": - logger.DebugContext(ctx, "using forge GitLab") - f, err = gitlab.New(logger, &gitlab.Options{ - Options: forgeOptions, - Path: fmt.Sprintf("%s/%s", flagOwner, flagRepo), - }) - if err != nil { - slog.ErrorContext(ctx, "failed to create client", "err", err) - return fmt.Errorf("failed to create gitlab client: %w", err) - } - case "github": - logger.DebugContext(ctx, "using forge GitHub") - f = github.New(logger, &github.Options{ - Options: forgeOptions, - Owner: flagOwner, - Repo: flagRepo, - }) - case "forgejo": - logger.DebugContext(ctx, "using forge Forgejo") - f, err = forgejo.New(logger, &forgejo.Options{ - Options: forgeOptions, - Owner: flagOwner, - Repo: flagRepo, - - APIURL: flagAPIURL, - APIToken: flagAPIToken, - Username: flagUsername, - }) - if err != nil { - logger.ErrorContext(ctx, "failed to create client", "err", err) - return fmt.Errorf("failed to create forgejo client: %w", err) - } - default: - return fmt.Errorf("unknown --forge: %s", flagForge) - } - - extraFiles := parseExtraFiles(flagExtraFiles) - - updaterNames := parseUpdaters(flagUpdaters) - updaters := []updater.Updater{} - for _, name := range updaterNames { - switch name { - 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( - f, - logger, - flagBranch, - conventionalcommits.NewParser(logger), - versioning.SemVer, - extraFiles, - updaters, - ) - - return releaserPleaser.Run(ctx) - }, + forgeOptions := forge.Options{ + Repository: flagRepo, + BaseBranch: flagBranch, } - cmd.PersistentFlags().StringVar(&flagForge, "forge", "", "") - cmd.PersistentFlags().StringVar(&flagBranch, "branch", "main", "") - cmd.PersistentFlags().StringVar(&flagOwner, "owner", "", "") - cmd.PersistentFlags().StringVar(&flagRepo, "repo", "", "") - cmd.PersistentFlags().StringVar(&flagExtraFiles, "extra-files", "", "") - cmd.PersistentFlags().StringSliceVar(&flagUpdaters, "updaters", []string{}, "") + switch flagForge { + case "gitlab": + logger.DebugContext(ctx, "using forge GitLab") + f, err = gitlab.New(logger, &gitlab.Options{ + Options: forgeOptions, + Path: fmt.Sprintf("%s/%s", flagOwner, flagRepo), + }) + if err != nil { + logger.ErrorContext(ctx, "failed to create client", "err", err) + return fmt.Errorf("failed to create gitlab client: %w", err) + } + case "github": + logger.DebugContext(ctx, "using forge GitHub") + f = github.New(logger, &github.Options{ + Options: forgeOptions, + Owner: flagOwner, + Repo: flagRepo, + }) + default: + return fmt.Errorf("unknown --forge: %s", flagForge) + } - cmd.PersistentFlags().StringVar(&flagAPIURL, "api-url", "", "") - cmd.PersistentFlags().StringVar(&flagAPIToken, "api-token", "", "") - cmd.PersistentFlags().StringVar(&flagUsername, "username", "", "") + extraFiles := parseExtraFiles(flagExtraFiles) - return cmd + releaserPleaser := rp.New( + f, + logger, + flagBranch, + conventionalcommits.NewParser(logger), + versioning.SemVer, + extraFiles, + []updater.NewUpdater{updater.Generic}, + ) + + return releaserPleaser.Run(ctx) } func parseExtraFiles(input string) []string { @@ -156,26 +113,3 @@ func parseExtraFiles(input string) []string { return extraFiles } - -func parseUpdaters(input []string) []string { - names := []string{"changelog", "generic"} - - for _, u := range input { - if u == "" { - continue - } - - 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 -} diff --git a/cmd/rp/cmd/run_test.go b/cmd/rp/cmd/run_test.go index 4c6ceff..d4cea7a 100644 --- a/cmd/rp/cmd/run_test.go +++ b/cmd/rp/cmd/run_test.go @@ -57,48 +57,3 @@ 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"}, - }, - { - name: "remove empty entries", - input: []string{""}, - want: []string{"changelog", "generic"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := parseUpdaters(tt.input) - assert.Equal(t, tt.want, got) - }) - } -} diff --git a/cmd/rp/main.go b/cmd/rp/main.go index 734709c..462629b 100644 --- a/cmd/rp/main.go +++ b/cmd/rp/main.go @@ -2,7 +2,6 @@ package main import ( "github.com/apricote/releaser-pleaser/cmd/rp/cmd" - _ "github.com/apricote/releaser-pleaser/internal/log" ) func main() { diff --git a/codecov.yaml b/codecov.yaml deleted file mode 100644 index b52d0c4..0000000 --- a/codecov.yaml +++ /dev/null @@ -1,2 +0,0 @@ -ignore: - - "test" diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 0a22df1..b13bf24 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -25,7 +25,6 @@ - [Pull Request Options](reference/pr-options.md) - [GitHub Action](reference/github-action.md) - [GitLab CI/CD Component](reference/gitlab-cicd-component.md) -- [Updaters](reference/updaters.md) --- diff --git a/docs/guides/release-notes.md b/docs/guides/release-notes.md index 599fb52..d994294 100644 --- a/docs/guides/release-notes.md +++ b/docs/guides/release-notes.md @@ -43,17 +43,17 @@ The release pull request description has text fields where maintainers can add t When you edit the description, make sure to put your desired content into the code blocks named `rp-prefix` and `rp-suffix`. Only the content of these blocks is considered. -> ~~~~rp-prefix +> ```rp-prefix > ### Prefix > > This will be shown as the Prefix. -> ~~~~ +> ``` > -> ~~~~rp-suffix +> ```rp-suffix > ### Suffix > > This will be shown as the Suffix. -> ~~~~ +> ``` To match the style of the auto-generated release notes, you should start any headings at level 3 (`### Title`). diff --git a/docs/guides/updating-arbitrary-files.md b/docs/guides/updating-arbitrary-files.md index e7df9a5..d4b65bf 100644 --- a/docs/guides/updating-arbitrary-files.md +++ b/docs/guides/updating-arbitrary-files.md @@ -10,8 +10,7 @@ In some situations it makes sense to have the current version committed in files ## 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: @@ -29,8 +28,7 @@ You need to tell `releaser-pleaser` which files it should update. This happens t ### 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 jobs: @@ -46,8 +44,7 @@ jobs: ### 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 include: @@ -64,4 +61,3 @@ include: - **Reference** - [GitHub Action](../reference/github-action.md#inputs) - [GitLab CI/CD Component](../reference/gitlab-cicd-component.md#inputs) - - [Updaters](../reference/updaters.md#generic-updater) diff --git a/docs/reference/github-action.md b/docs/reference/github-action.md index c5d595a..eec9789 100644 --- a/docs/reference/github-action.md +++ b/docs/reference/github-action.md @@ -8,20 +8,17 @@ 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 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 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 by the generic updater. | `""` |
version/version.go
deploy/deployment.yaml
| -| `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` | +| 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
| ## Outputs diff --git a/docs/reference/gitlab-cicd-component.md b/docs/reference/gitlab-cicd-component.md index 3080fcf..b22d5b2 100644 --- a/docs/reference/gitlab-cicd-component.md +++ b/docs/reference/gitlab-cicd-component.md @@ -18,11 +18,10 @@ The component does not support floating tags (e.g. The following inputs are supported by the component. -| Input | Description | Default | Example | -|------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------:|---------------------------------------------------------------------:| -| `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 by the generic updater. | `""` |
version/version.go
deploy/deployment.yaml
| -| `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` | -| `needs` | Other jobs the releaser-pleaser job depends on. | `[]` |
- validate-foo
- prepare-bar
| +| Input | Description | Default | Example | +| ---------------------- | :-------------------------------------------------------- | ------: | -------------------------------------------------------------------: | +| `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
| +| `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/docs/reference/glossary.md b/docs/reference/glossary.md index 8966f55..543f970 100644 --- a/docs/reference/glossary.md +++ b/docs/reference/glossary.md @@ -2,21 +2,17 @@ ### 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](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 -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)"]() @@ -28,8 +24,7 @@ In `releaser-pleaser` Markdown is used for most texts. ### 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. @@ -37,9 +32,7 @@ Learn more in the [Pre-releases](../guides/pre-releases.md) guide. ### 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. @@ -51,11 +44,4 @@ Learn more in the [Release Notes customization](../guides/release-notes.md) guid ### 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. - -### 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. \ No newline at end of file +[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. diff --git a/docs/reference/pr-options.md b/docs/reference/pr-options.md index 1b096d4..266bd08 100644 --- a/docs/reference/pr-options.md +++ b/docs/reference/pr-options.md @@ -30,17 +30,17 @@ Any text in code blocks with these languages is being added to the start or end **Examples**: - ~~~~rp-prefix + ```rp-prefix #### Awesome new feature! This text is at the start of the release notes. - ~~~~ + ``` - ~~~~rp-suffix + ```rp-suffix #### Version Compatibility And this at the end. - ~~~~ + ``` ### Status diff --git a/docs/reference/updaters.md b/docs/reference/updaters.md deleted file mode 100644 index 6bafff4..0000000 --- a/docs/reference/updaters.md +++ /dev/null @@ -1,33 +0,0 @@ -# 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. diff --git a/go.mod b/go.mod index 5653239..694111d 100644 --- a/go.mod +++ b/go.mod @@ -1,41 +1,36 @@ module github.com/apricote/releaser-pleaser -go 1.24.0 +go 1.23.2 -toolchain go1.25.5 +toolchain go1.24.5 require ( - codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.2.0 github.com/blang/semver/v4 v4.0.0 - github.com/go-git/go-billy/v5 v5.7.0 - github.com/go-git/go-git/v5 v5.16.4 - github.com/google/go-github/v74 v74.0.0 + github.com/go-git/go-billy/v5 v5.6.2 + github.com/go-git/go-git/v5 v5.16.2 + github.com/google/go-github/v72 v72.0.0 github.com/leodido/go-conventionalcommits v0.12.0 github.com/lmittmann/tint v1.1.2 - github.com/spf13/cobra v1.10.2 - github.com/stretchr/testify v1.11.1 + github.com/spf13/cobra v1.9.1 + github.com/stretchr/testify v1.10.0 github.com/teekennedy/goldmark-markdown v0.5.1 - github.com/yuin/goldmark v1.7.16 - gitlab.com/gitlab-org/api/client-go v0.161.1 + github.com/yuin/goldmark v1.7.12 + gitlab.com/gitlab-org/api/client-go v0.134.0 ) require ( dario.cat/mergo v1.0.1 // indirect - github.com/42wim/httpsig v1.2.3 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.1.6 // indirect github.com/cloudflare/circl v1.6.1 // indirect github.com/cyphar/filepath-securejoin v0.4.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/davidmz/go-pageant v1.0.2 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/go-fed/httpsig v1.1.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.8 // indirect - github.com/hashicorp/go-version v1.7.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect @@ -44,16 +39,13 @@ require ( github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.3.1 // indirect - github.com/spf13/pflag v1.0.9 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - golang.org/x/crypto v0.45.0 // indirect - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/net v0.47.0 // indirect + golang.org/x/crypto v0.37.0 // indirect + golang.org/x/net v0.39.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sys v0.38.0 // indirect + golang.org/x/sys v0.32.0 // indirect golang.org/x/time v0.12.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - -replace codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 => codeberg.org/apricote/forgejo-sdk/forgejo/v2 v2.1.2-0.20250615152743-47d3f0434561 diff --git a/go.sum b/go.sum index 870cb83..96a3479 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,5 @@ -codeberg.org/apricote/forgejo-sdk/forgejo/v2 v2.1.2-0.20250615152743-47d3f0434561 h1:ZFGmrGQ7cd2mbSLrfjrj3COwPKFfKM6sDO/IsrGDW7w= -codeberg.org/apricote/forgejo-sdk/forgejo/v2 v2.1.2-0.20250615152743-47d3f0434561/go.mod h1:2i9GsyawlJtVMO5pTS/Om5uo2O3JN/eCjGWy5v15NGg= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs= -github.com/42wim/httpsig v1.2.3/go.mod h1:nZq9OlYKDrUBhptd77IHx4/sZZD+IxTBADvAPI9G/EM= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= @@ -23,8 +19,6 @@ github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGL github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0= -github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -33,23 +27,21 @@ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= -github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= -github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.7.0 h1:83lBUJhGWhYp0ngzCMSgllhUSuoHP1iEWYjsPl9nwqM= -github.com/go-git/go-billy/v5 v5.7.0/go.mod h1:/1IUejTKH8xipsAcdfcSAlUlo2J7lkYV8GTKxAT/L3E= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.16.4 h1:7ajIEZHZJULcyJebDLo99bGgS0jRrOxzZG4uCk2Yb2Y= -github.com/go-git/go-git/v5 v5.16.4/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= +github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM= +github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-github/v74 v74.0.0 h1:yZcddTUn8DPbj11GxnMrNiAnXH14gNs559AsUpNpPgM= -github.com/google/go-github/v74 v74.0.0/go.mod h1:ubn/YdyftV80VPSI26nSJvaEsTOnsjrxG3o9kJhcyak= +github.com/google/go-github/v72 v72.0.0 h1:FcIO37BLoVPBO9igQQ6tStsv2asG4IPcYFi655PPvBM= +github.com/google/go-github/v72 v72.0.0/go.mod h1:WWtw8GMRiL62mvIquf1kO3onRHeWWKmK01qdCY8c5fg= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= @@ -58,8 +50,6 @@ github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB1 github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= @@ -101,60 +91,50 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= -github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= -github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= -github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= -github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/teekennedy/goldmark-markdown v0.5.1 h1:2lIlJ3AcIwaD1wFl4dflJSJFMhRTKEsEj+asVsu6M/0= github.com/teekennedy/goldmark-markdown v0.5.1/go.mod h1:so260mNSPELuRyynZY18719dRYlD+OSnAovqsyrOMOM= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= -github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE= -github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= -gitlab.com/gitlab-org/api/client-go v0.161.1 h1:XX0EtVGL6cGEdNy9xnJ96CSciIzjCwAVsayItHY1YyU= -gitlab.com/gitlab-org/api/client-go v0.161.1/go.mod h1:YqKcnxyV9OPAL5U99mpwBVEgBPz1PK/3qwqq/3h6bao= +github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY= +github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +gitlab.com/gitlab-org/api/client-go v0.134.0 h1:J4i6qPN5hRLsqatPxVbe9w2C0A3JEItyCQrzsP52S2k= +gitlab.com/gitlab-org/api/client-go v0.134.0/go.mod h1:crkp9sCwMQ8gDwuMLgk11sDT336t6U3kESBT0BGsOBo= go.abhg.dev/goldmark/toc v0.11.0 h1:IRixVy3/yVPKvFBc37EeBPi8XLTXrtH6BYaonSjkF8o= go.abhg.dev/goldmark/toc v0.11.0/go.mod h1:XMFIoI1Sm6dwF9vKzVDOYE/g1o5BmKXghLG8q/wJNww= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= +golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/internal/changelog/changelog.go b/internal/changelog/changelog.go index dda751a..d6386b8 100644 --- a/internal/changelog/changelog.go +++ b/internal/changelog/changelog.go @@ -3,9 +3,9 @@ package changelog import ( "bytes" _ "embed" + "html/template" "log" "log/slog" - "text/template" "github.com/apricote/releaser-pleaser/internal/commitparser" "github.com/apricote/releaser-pleaser/internal/markdown" diff --git a/internal/changelog/changelog.md.tpl b/internal/changelog/changelog.md.tpl index 0a73dd5..50907eb 100644 --- a/internal/changelog/changelog.md.tpl +++ b/internal/changelog/changelog.md.tpl @@ -1,5 +1,5 @@ {{define "entry" -}} -- {{ if .BreakingChange}}**BREAKING**: {{end}}{{ if .Scope }}**{{.Scope}}**: {{end}}{{.Description}} +- {{ if .Scope }}**{{.Scope}}**: {{end}}{{.Description}} {{ end }} {{- if not .Formatting.HideVersionTitle }} diff --git a/internal/changelog/changelog_test.go b/internal/changelog/changelog_test.go index c2bbf71..a969730 100644 --- a/internal/changelog/changelog_test.go +++ b/internal/changelog/changelog_test.go @@ -8,7 +8,6 @@ import ( "github.com/apricote/releaser-pleaser/internal/commitparser" "github.com/apricote/releaser-pleaser/internal/git" - "github.com/apricote/releaser-pleaser/internal/testdata" ) func ptr[T any](input T) *T { @@ -55,23 +54,6 @@ func Test_NewChangelogEntry(t *testing.T) { want: "## [1.0.0](https://example.com/1.0.0)\n\n### Features\n\n- Foobar!\n", wantErr: assert.NoError, }, - { - name: "single breaking change", - args: args{ - analyzedCommits: []commitparser.AnalyzedCommit{ - { - Commit: git.Commit{}, - Type: "feat", - Description: "Foobar!", - BreakingChange: true, - }, - }, - version: "1.0.0", - link: "https://example.com/1.0.0", - }, - want: "## [1.0.0](https://example.com/1.0.0)\n\n### Features\n\n- **BREAKING**: Foobar!\n", - wantErr: assert.NoError, - }, { name: "single fix", args: args{ @@ -144,9 +126,16 @@ func Test_NewChangelogEntry(t *testing.T) { }, version: "1.0.0", link: "https://example.com/1.0.0", - prefix: testdata.MustReadFileString(t, "prefix.txt"), + prefix: "### Breaking Changes", }, - want: testdata.MustReadFileString(t, "changelog-entry-prefix.txt"), + want: `## [1.0.0](https://example.com/1.0.0) + +### Breaking Changes + +### Bug Fixes + +- Foobar! +`, wantErr: assert.NoError, }, { @@ -161,9 +150,18 @@ func Test_NewChangelogEntry(t *testing.T) { }, version: "1.0.0", link: "https://example.com/1.0.0", - suffix: testdata.MustReadFileString(t, "suffix.txt"), + suffix: "### Compatibility\n\nThis version is compatible with flux-compensator v2.2 - v2.9.", }, - want: testdata.MustReadFileString(t, "changelog-entry-suffix.txt"), + want: `## [1.0.0](https://example.com/1.0.0) + +### Bug Fixes + +- Foobar! + +### Compatibility + +This version is compatible with flux-compensator v2.2 - v2.9. +`, wantErr: assert.NoError, }, } diff --git a/internal/forge/forgejo/forgejo.go b/internal/forge/forgejo/forgejo.go deleted file mode 100644 index 9b0c548..0000000 --- a/internal/forge/forgejo/forgejo.go +++ /dev/null @@ -1,529 +0,0 @@ -package forgejo - -import ( - "context" - "fmt" - "log/slog" - "slices" - "strings" - - "codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2" - "github.com/blang/semver/v4" - "github.com/go-git/go-git/v5/plumbing/transport" - "github.com/go-git/go-git/v5/plumbing/transport/http" - - "github.com/apricote/releaser-pleaser/internal/forge" - "github.com/apricote/releaser-pleaser/internal/git" - "github.com/apricote/releaser-pleaser/internal/pointer" - "github.com/apricote/releaser-pleaser/internal/releasepr" -) - -const () - -var _ forge.Forge = &Forgejo{} - -type Forgejo struct { - options *Options - - client *forgejo.Client - log *slog.Logger -} - -func (f *Forgejo) RepoURL() string { - return fmt.Sprintf("%s/%s/%s", f.options.APIURL, f.options.Owner, f.options.Repo) -} - -func (f *Forgejo) CloneURL() string { - return fmt.Sprintf("%s.git", f.RepoURL()) -} - -func (f *Forgejo) ReleaseURL(version string) string { - return fmt.Sprintf("%s/releases/tag/%s", f.RepoURL(), version) -} - -func (f *Forgejo) PullRequestURL(id int) string { - return fmt.Sprintf("%s/pulls/%d", f.RepoURL(), id) -} - -func (f *Forgejo) GitAuth() transport.AuthMethod { - return &http.BasicAuth{ - Username: f.options.Username, - Password: f.options.APIToken, - } -} - -func (f *Forgejo) CommitAuthor(ctx context.Context) (git.Author, error) { - f.log.DebugContext(ctx, "getting commit author from current token user") - - user, _, err := f.client.GetMyUserInfo() - if err != nil { - return git.Author{}, err - } - - // TODO: Same for other forges? - name := user.FullName - if name == "" { - name = user.UserName - } - - return git.Author{ - Name: name, - Email: user.Email, - }, nil -} - -func (f *Forgejo) LatestTags(ctx context.Context) (git.Releases, error) { - f.log.DebugContext(ctx, "listing all tags in forgejo repository") - - tags, err := all(func(listOptions forgejo.ListOptions) ([]*forgejo.Tag, *forgejo.Response, error) { - return f.client.ListRepoTags(f.options.Owner, f.options.Repo, - forgejo.ListRepoTagsOptions{ListOptions: listOptions}, - ) - }) - if err != nil { - return git.Releases{}, err - } - - var releases git.Releases - - for _, fTag := range tags { - tag := &git.Tag{ - Hash: fTag.Commit.SHA, - Name: fTag.Name, - } - - version, err := semver.Parse(strings.TrimPrefix(tag.Name, "v")) - if err != nil { - f.log.WarnContext( - ctx, "unable to parse tag as semver, skipping", - "tag.name", tag.Name, - "tag.hash", tag.Hash, - "error", err, - ) - continue - } - - if releases.Latest == nil { - releases.Latest = tag - } - if len(version.Pre) == 0 { - // Stable version tag - // We return once we have found the latest stable tag, not needed to look at every single tag. - releases.Stable = tag - break - } - } - - return releases, nil -} - -func (f *Forgejo) CommitsSince(ctx context.Context, tag *git.Tag) ([]git.Commit, error) { - var repositoryCommits []*forgejo.Commit - var err error - if tag != nil { - repositoryCommits, err = f.commitsSinceTag(ctx, tag) - } else { - repositoryCommits, err = f.commitsSinceInit(ctx) - } - - if err != nil { - return nil, err - } - - var commits = make([]git.Commit, 0, len(repositoryCommits)) - for _, fCommit := range repositoryCommits { - commit := git.Commit{ - Hash: fCommit.SHA, - Message: fCommit.RepoCommit.Message, - } - commit.PullRequest, err = f.prForCommit(ctx, commit) - if err != nil { - return nil, fmt.Errorf("failed to check for commit pull request: %w", err) - } - - commits = append(commits, commit) - } - - return commits, nil -} - -func (f *Forgejo) commitsSinceTag(_ context.Context, tag *git.Tag) ([]*forgejo.Commit, error) { - head := f.options.BaseBranch - log := f.log.With("base", tag.Hash, "head", head) - log.Debug("comparing commits") - - compare, _, err := f.client.CompareCommits( - f.options.Owner, f.options.Repo, - tag.Hash, head) - if err != nil { - return nil, err - } - - return compare.Commits, nil -} - -func (f *Forgejo) commitsSinceInit(_ context.Context) ([]*forgejo.Commit, error) { - head := f.options.BaseBranch - log := f.log.With("head", head) - log.Debug("listing all commits") - - repositoryCommits, err := all( - func(listOptions forgejo.ListOptions) ([]*forgejo.Commit, *forgejo.Response, error) { - return f.client.ListRepoCommits( - f.options.Owner, f.options.Repo, - forgejo.ListCommitOptions{ - ListOptions: listOptions, - SHA: f.options.BaseBranch, - }) - }) - if err != nil { - return nil, err - } - - return repositoryCommits, nil -} - -func (f *Forgejo) prForCommit(_ context.Context, commit git.Commit) (*git.PullRequest, error) { - // We naively look up the associated PR for each commit through the "List pull requests associated with a commit" - // endpoint. This requires len(commits) requests. - // Using the "List pull requests" endpoint might be faster, as it allows us to fetch 100 arbitrary PRs per request, - // but worst case we need to look up all PRs made in the repository ever. - - f.log.Debug("fetching pull requests associated with commit", "commit.hash", commit.Hash) - - pullRequest, _, err := f.client.GetCommitPullRequest( - f.options.Owner, f.options.Repo, - commit.Hash, - ) - if err != nil { - if strings.HasPrefix(err.Error(), "pull request does not exist") { - return nil, nil - } - - return nil, err - } - - return forgejoPRToPullRequest(pullRequest), nil -} - -func (f *Forgejo) EnsureLabelsExist(_ context.Context, labels []releasepr.Label) error { - f.log.Debug("fetching labels on repo") - fLabels, err := all(func(listOptions forgejo.ListOptions) ([]*forgejo.Label, *forgejo.Response, error) { - return f.client.ListRepoLabels( - f.options.Owner, f.options.Repo, - forgejo.ListLabelsOptions{ListOptions: listOptions}) - }) - if err != nil { - return err - } - - for _, label := range labels { - if !slices.ContainsFunc(fLabels, func(fLabel *forgejo.Label) bool { - return fLabel.Name == label.Name - }) { - f.log.Info("creating label in repository", "label.name", label.Name) - _, _, err = f.client.CreateLabel( - f.options.Owner, f.options.Repo, - forgejo.CreateLabelOption{ - Name: label.Name, - Color: label.Color, - Description: label.Description, - }, - ) - if err != nil { - return err - } - } - } - - return nil -} - -func (f *Forgejo) PullRequestForBranch(_ context.Context, branch string) (*releasepr.ReleasePullRequest, error) { - prs, err := all( - func(listOptions forgejo.ListOptions) ([]*forgejo.PullRequest, *forgejo.Response, error) { - return f.client.ListRepoPullRequests( - f.options.Owner, f.options.Repo, - forgejo.ListPullRequestsOptions{ - ListOptions: listOptions, - State: forgejo.StateOpen, - }, - ) - }, - ) - if err != nil { - return nil, err - } - - for _, pr := range prs { - if pr.Base.Ref == f.options.BaseBranch && pr.Head.Ref == branch { - return forgejoPRToReleasePullRequest(pr), nil - } - } - - return nil, nil -} - -func (f *Forgejo) CreatePullRequest(ctx context.Context, pr *releasepr.ReleasePullRequest) error { - fPR, _, err := f.client.CreatePullRequest( - f.options.Owner, f.options.Repo, - forgejo.CreatePullRequestOption{ - Title: pr.Title, - Head: pr.Head, - Base: f.options.BaseBranch, - Body: pr.Description, - }, - ) - if err != nil { - return err - } - - // TODO: String ID? - pr.ID = int(fPR.ID) - - err = f.SetPullRequestLabels(ctx, pr, []releasepr.Label{}, pr.Labels) - if err != nil { - return err - } - - return nil -} - -func (f *Forgejo) UpdatePullRequest(_ context.Context, pr *releasepr.ReleasePullRequest) error { - _, _, err := f.client.EditPullRequest( - f.options.Owner, f.options.Repo, - int64(pr.ID), forgejo.EditPullRequestOption{ - Title: pr.Title, - Body: pr.Description, - }, - ) - if err != nil { - return err - } - - return nil -} - -func (f *Forgejo) SetPullRequestLabels(_ context.Context, pr *releasepr.ReleasePullRequest, remove, add []releasepr.Label) error { - allLabels, err := all( - func(listOptions forgejo.ListOptions) ([]*forgejo.Label, *forgejo.Response, error) { - return f.client.ListRepoLabels(f.options.Owner, f.options.Repo, forgejo.ListLabelsOptions{ListOptions: listOptions}) - }, - ) - if err != nil { - return err - } - - findLabel := func(labelName string) *forgejo.Label { - for _, fLabel := range allLabels { - if fLabel.Name == labelName { - return fLabel - } - } - - return nil - } - - for _, label := range remove { - fLabel := findLabel(label.Name) - if fLabel == nil { - return fmt.Errorf("unable to remove label %q, not found in API", label.Name) - } - - _, err = f.client.DeleteIssueLabel( - f.options.Owner, f.options.Repo, - int64(pr.ID), fLabel.ID, - ) - if err != nil { - return err - } - } - - addIDs := make([]int64, 0, len(add)) - for _, label := range add { - fLabel := findLabel(label.Name) - if fLabel == nil { - return fmt.Errorf("unable to add label %q, not found in API", label.Name) - } - - addIDs = append(addIDs, fLabel.ID) - } - - _, _, err = f.client.AddIssueLabels( - f.options.Owner, f.options.Repo, - int64(pr.ID), forgejo.IssueLabelsOption{Labels: addIDs}, - ) - if err != nil { - return err - } - - return nil -} - -func (f *Forgejo) ClosePullRequest(_ context.Context, pr *releasepr.ReleasePullRequest) error { - _, _, err := f.client.EditPullRequest( - f.options.Owner, f.options.Repo, - int64(pr.ID), forgejo.EditPullRequestOption{ - State: pointer.Pointer(forgejo.StateClosed), - }, - ) - if err != nil { - return err - } - - return nil -} - -func (f *Forgejo) PendingReleases(_ context.Context, pendingLabel releasepr.Label) ([]*releasepr.ReleasePullRequest, error) { - fPRs, err := all(func(listOptions forgejo.ListOptions) ([]*forgejo.PullRequest, *forgejo.Response, error) { - return f.client.ListRepoPullRequests( - f.options.Owner, f.options.Repo, - forgejo.ListPullRequestsOptions{ - // Filtering by Label ID is possible in the API, but not implemented in the Go SDK. - State: forgejo.StateClosed, - ListOptions: listOptions, - }) - }) - if err != nil { - // "The target couldn't be found." means that the repo does not have pull requests activated. - return nil, err - } - - prs := make([]*releasepr.ReleasePullRequest, 0, len(fPRs)) - - for _, pr := range fPRs { - pending := slices.ContainsFunc(pr.Labels, func(l *forgejo.Label) bool { - return l.Name == pendingLabel.Name - }) - if !pending { - continue - } - - // pr.Merged is always nil :( - if !pr.HasMerged { - // Closed and not merged - continue - } - - prs = append(prs, forgejoPRToReleasePullRequest(pr)) - } - - return prs, nil -} - -func (f *Forgejo) CreateRelease(_ context.Context, commit git.Commit, title, changelog string, preRelease, latest bool) error { - // latest can not be set through the API - - _, _, err := f.client.CreateRelease( - f.options.Owner, f.options.Repo, - forgejo.CreateReleaseOption{ - TagName: title, - Target: commit.Hash, - Title: title, - Note: changelog, - IsPrerelease: preRelease, - }, - ) - if err != nil { - return err - } - - return nil -} - -func all[T any](f func(listOptions forgejo.ListOptions) ([]T, *forgejo.Response, error)) ([]T, error) { - results := make([]T, 0) - page := 1 - - for { - pageResults, resp, err := f(forgejo.ListOptions{Page: page}) - if err != nil { - return nil, err - } - - results = append(results, pageResults...) - - if page == resp.LastPage || resp.LastPage == 0 { - return results, nil - } - page = resp.NextPage - } -} - -func forgejoPRToPullRequest(pr *forgejo.PullRequest) *git.PullRequest { - return &git.PullRequest{ - ID: int(pr.ID), - Title: pr.Title, - Description: pr.Body, - } -} - -func forgejoPRToReleasePullRequest(pr *forgejo.PullRequest) *releasepr.ReleasePullRequest { - labels := make([]releasepr.Label, 0, len(pr.Labels)) - for _, label := range pr.Labels { - labelName := label.Name - if i := slices.IndexFunc(releasepr.KnownLabels, func(label releasepr.Label) bool { - return label.Name == labelName - }); i >= 0 { - labels = append(labels, releasepr.KnownLabels[i]) - } - } - - var releaseCommit *git.Commit - if pr.MergedCommitID != nil { - releaseCommit = &git.Commit{Hash: *pr.MergedCommitID} - } - - return &releasepr.ReleasePullRequest{ - PullRequest: *forgejoPRToPullRequest(pr), - Labels: labels, - - Head: pr.Head.Ref, - ReleaseCommit: releaseCommit, - } -} - -func (g *Options) autodiscover() { - // TODO -} - -func (g *Options) ClientOptions() []forgejo.ClientOption { - options := []forgejo.ClientOption{} - - if g.APIToken != "" { - options = append(options, forgejo.SetToken(g.APIToken)) - } - - return options -} - -type Options struct { - forge.Options - - Owner string - Repo string - - APIURL string - Username string - APIToken string -} - -func New(log *slog.Logger, options *Options) (*Forgejo, error) { - options.autodiscover() - - client, err := forgejo.NewClient(options.APIURL, options.ClientOptions()...) - if err != nil { - return nil, err - } - - client.SetUserAgent("releaser-pleaser") - - f := &Forgejo{ - options: options, - - client: client, - log: log.With("forge", "forgejo"), - } - - return f, nil -} diff --git a/internal/forge/github/github.go b/internal/forge/github/github.go index d01e529..3bff3e6 100644 --- a/internal/forge/github/github.go +++ b/internal/forge/github/github.go @@ -12,7 +12,7 @@ import ( "github.com/blang/semver/v4" "github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-git/go-git/v5/plumbing/transport/http" - "github.com/google/go-github/v74/github" + "github.com/google/go-github/v72/github" "github.com/apricote/releaser-pleaser/internal/forge" "github.com/apricote/releaser-pleaser/internal/git" @@ -296,13 +296,6 @@ func (g *GitHub) PullRequestForBranch(ctx context.Context, branch string) (*rele } func (g *GitHub) CreatePullRequest(ctx context.Context, pr *releasepr.ReleasePullRequest) error { - // If the Pull Request is created without the labels releaser-pleaser will create a new PR in the run. The user may merge both and have duplicate entries in the changelog. - // We try to avoid this situation by checking for a cancelled context first, and then running both API calls without passing along any cancellations. - if ctx.Err() != nil { - return ctx.Err() - } - ctx = context.WithoutCancel(ctx) - ghPR, _, err := g.client.PullRequests.Create( ctx, g.options.Owner, g.options.Repo, &github.NewPullRequest{ @@ -316,6 +309,7 @@ func (g *GitHub) CreatePullRequest(ctx context.Context, pr *releasepr.ReleasePul return err } + // TODO: String ID? pr.ID = ghPR.GetNumber() err = g.SetPullRequestLabels(ctx, pr, []releasepr.Label{}, pr.Labels) diff --git a/internal/git/git.go b/internal/git/git.go index ad5c0a3..d1db11b 100644 --- a/internal/git/git.go +++ b/internal/git/git.go @@ -13,6 +13,8 @@ import ( "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/transport" + + "github.com/apricote/releaser-pleaser/internal/updater" ) const ( @@ -117,7 +119,7 @@ func (r *Repository) Checkout(_ context.Context, branch string) error { return nil } -func (r *Repository) UpdateFile(_ context.Context, path string, create bool, updateHook func(string) (string, error)) error { +func (r *Repository) UpdateFile(_ context.Context, path string, create bool, updaters []updater.Updater) error { worktree, err := r.r.Worktree() if err != nil { return err @@ -139,9 +141,13 @@ func (r *Repository) UpdateFile(_ context.Context, path string, create bool, upd return err } - updatedContent, err := updateHook(string(content)) - if err != nil { - return fmt.Errorf("failed to run update hook on file %s", path) + updatedContent := string(content) + + for _, update := range updaters { + updatedContent, err = update(updatedContent) + if err != nil { + return fmt.Errorf("failed to run updater on file %s", path) + } } err = file.Truncate(0) diff --git a/internal/log/log.go b/internal/log/log.go deleted file mode 100644 index 32219fd..0000000 --- a/internal/log/log.go +++ /dev/null @@ -1,23 +0,0 @@ -package log - -import ( - "io" - "log/slog" - "os" - "time" - - "github.com/lmittmann/tint" -) - -func GetLogger(w io.Writer) *slog.Logger { - return slog.New( - tint.NewHandler(w, &tint.Options{ - Level: slog.LevelDebug, - TimeFormat: time.RFC3339, - }), - ) -} - -func init() { - slog.SetDefault(GetLogger(os.Stderr)) -} diff --git a/internal/releasepr/releasepr.md.tpl b/internal/releasepr/releasepr.md.tpl index 3242c5d..6f74aa0 100644 --- a/internal/releasepr/releasepr.md.tpl +++ b/internal/releasepr/releasepr.md.tpl @@ -15,18 +15,18 @@ If you want to modify the proposed release, add you overrides here. You can lear This will be added to the start of the release notes. -~~~~rp-prefix +```rp-prefix {{- if .Overrides.Prefix }} {{ .Overrides.Prefix }}{{ end }} -~~~~ +``` ### Suffix / End This will be added to the end of the release notes. -~~~~rp-suffix +```rp-suffix {{- if .Overrides.Suffix }} {{ .Overrides.Suffix }}{{ end }} -~~~~ +``` diff --git a/internal/releasepr/releasepr_test.go b/internal/releasepr/releasepr_test.go index 1f1111b..346bacb 100644 --- a/internal/releasepr/releasepr_test.go +++ b/internal/releasepr/releasepr_test.go @@ -6,7 +6,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/apricote/releaser-pleaser/internal/git" - "github.com/apricote/releaser-pleaser/internal/testdata" "github.com/apricote/releaser-pleaser/internal/versioning" ) @@ -38,24 +37,20 @@ func TestReleasePullRequest_GetOverrides(t *testing.T) { name: "prefix in description", pr: ReleasePullRequest{ PullRequest: git.PullRequest{ - Description: testdata.MustReadFileString(t, "description-prefix.txt"), + Description: "```rp-prefix\n## Foo\n\n- Cool thing\n```", }, }, - want: ReleaseOverrides{ - Prefix: testdata.MustReadFileString(t, "prefix.txt"), - }, + want: ReleaseOverrides{Prefix: "## Foo\n\n- Cool thing"}, wantErr: assert.NoError, }, { name: "suffix in description", pr: ReleasePullRequest{ PullRequest: git.PullRequest{ - Description: testdata.MustReadFileString(t, "description-suffix.txt"), + Description: "```rp-suffix\n## Compatibility\n\nNo compatibility guarantees.\n```", }, }, - want: ReleaseOverrides{ - Suffix: testdata.MustReadFileString(t, "suffix.txt"), - }, + want: ReleaseOverrides{Suffix: "## Compatibility\n\nNo compatibility guarantees."}, wantErr: assert.NoError, }, } @@ -85,10 +80,30 @@ func TestReleasePullRequest_ChangelogText(t *testing.T) { wantErr: assert.NoError, }, { - name: "with section", - description: testdata.MustReadFileString(t, "changelog.txt"), - want: testdata.MustReadFileString(t, "changelog-content.txt"), - wantErr: assert.NoError, + name: "with section", + description: `# Foobar + + +This is the changelog + +## Awesome + +### New + +#### Changes + + +Suffix Things +`, + want: `This is the changelog + +## Awesome + +### New + +#### Changes +`, + wantErr: assert.NoError, }, } for _, tt := range tests { @@ -163,17 +178,75 @@ func TestReleasePullRequest_SetDescription(t *testing.T) { name: "no overrides", changelogEntry: `## v1.0.0`, overrides: ReleaseOverrides{}, - want: testdata.MustReadFileString(t, "description-no-overrides.txt"), - wantErr: assert.NoError, + want: ` +## v1.0.0 + + +--- + +
+

PR by releaser-pleaser 🤖

+ +If you want to modify the proposed release, add you overrides here. You can learn more about the options in the docs. + +## Release Notes + +### Prefix / Start + +This will be added to the start of the release notes. + +` + "```" + `rp-prefix +` + "```" + ` + +### Suffix / End + +This will be added to the end of the release notes. + +` + "```" + `rp-suffix +` + "```" + ` + +
+`, + wantErr: assert.NoError, }, { name: "existing overrides", changelogEntry: `## v1.0.0`, overrides: ReleaseOverrides{ - Prefix: testdata.MustReadFileString(t, "prefix.txt"), - Suffix: testdata.MustReadFileString(t, "suffix.txt"), + Prefix: "This release is awesome!", + Suffix: "Fooo", }, - want: testdata.MustReadFileString(t, "description-overrides.txt"), + want: ` +## v1.0.0 + + +--- + +
+

PR by releaser-pleaser 🤖

+ +If you want to modify the proposed release, add you overrides here. You can learn more about the options in the docs. + +## Release Notes + +### Prefix / Start + +This will be added to the start of the release notes. + +` + "```" + `rp-prefix +This release is awesome! +` + "```" + ` + +### Suffix / End + +This will be added to the end of the release notes. + +` + "```" + `rp-suffix +Fooo +` + "```" + ` + +
+`, wantErr: assert.NoError, }, } diff --git a/internal/testdata/changelog-content.txt b/internal/testdata/changelog-content.txt deleted file mode 100644 index 3b95e44..0000000 --- a/internal/testdata/changelog-content.txt +++ /dev/null @@ -1,7 +0,0 @@ -This is the changelog - -## Awesome - -### New - -#### Changes diff --git a/internal/testdata/changelog-entry-prefix.txt b/internal/testdata/changelog-entry-prefix.txt deleted file mode 100644 index 186d04a..0000000 --- a/internal/testdata/changelog-entry-prefix.txt +++ /dev/null @@ -1,19 +0,0 @@ -## [1.0.0](https://example.com/1.0.0) - -## Foo - -- Cool thing - -```go -// Some code example -func IsPositive(number int) error { - if number < 0 { - return fmt.Errorf("number %d is negative", number) - } - return nil -} -``` - -### Bug Fixes - -- Foobar! diff --git a/internal/testdata/changelog-entry-suffix.txt b/internal/testdata/changelog-entry-suffix.txt deleted file mode 100644 index 91bbb05..0000000 --- a/internal/testdata/changelog-entry-suffix.txt +++ /dev/null @@ -1,9 +0,0 @@ -## [1.0.0](https://example.com/1.0.0) - -### Bug Fixes - -- Foobar! - -## Compatibility - -This version is compatible with flux-compensator v2.2 - v2.9. diff --git a/internal/testdata/changelog.txt b/internal/testdata/changelog.txt deleted file mode 100644 index f77bc5a..0000000 --- a/internal/testdata/changelog.txt +++ /dev/null @@ -1,13 +0,0 @@ -# Foobar - - -This is the changelog - -## Awesome - -### New - -#### Changes - - -Suffix Things \ No newline at end of file diff --git a/internal/testdata/description-no-overrides.txt b/internal/testdata/description-no-overrides.txt deleted file mode 100644 index 8a98ae4..0000000 --- a/internal/testdata/description-no-overrides.txt +++ /dev/null @@ -1,28 +0,0 @@ - -## v1.0.0 - - ---- - -
-

PR by releaser-pleaser 🤖

- -If you want to modify the proposed release, add you overrides here. You can learn more about the options in the docs. - -## Release Notes - -### Prefix / Start - -This will be added to the start of the release notes. - -~~~~rp-prefix -~~~~ - -### Suffix / End - -This will be added to the end of the release notes. - -~~~~rp-suffix -~~~~ - -
diff --git a/internal/testdata/description-overrides.txt b/internal/testdata/description-overrides.txt deleted file mode 100644 index 5a1db7e..0000000 --- a/internal/testdata/description-overrides.txt +++ /dev/null @@ -1,44 +0,0 @@ - -## v1.0.0 - - ---- - -
-

PR by releaser-pleaser 🤖

- -If you want to modify the proposed release, add you overrides here. You can learn more about the options in the docs. - -## Release Notes - -### Prefix / Start - -This will be added to the start of the release notes. - -~~~~rp-prefix -## Foo - -- Cool thing - -```go -// Some code example -func IsPositive(number int) error { - if number < 0 { - return fmt.Errorf("number %d is negative", number) - } - return nil -} -``` -~~~~ - -### Suffix / End - -This will be added to the end of the release notes. - -~~~~rp-suffix -## Compatibility - -This version is compatible with flux-compensator v2.2 - v2.9. -~~~~ - -
diff --git a/internal/testdata/description-prefix.txt b/internal/testdata/description-prefix.txt deleted file mode 100644 index 3a30166..0000000 --- a/internal/testdata/description-prefix.txt +++ /dev/null @@ -1,41 +0,0 @@ - -## v1.0.0 - - ---- - -
-

PR by releaser-pleaser 🤖

- -If you want to modify the proposed release, add you overrides here. You can learn more about the options in the docs. - -## Release Notes - -### Prefix / Start - -This will be added to the start of the release notes. - -~~~~rp-prefix -## Foo - -- Cool thing - -```go -// Some code example -func IsPositive(number int) error { - if number < 0 { - return fmt.Errorf("number %d is negative", number) - } - return nil -} -``` -~~~~ - -### Suffix / End - -This will be added to the end of the release notes. - -~~~~rp-suffix -~~~~ - -
diff --git a/internal/testdata/description-suffix.txt b/internal/testdata/description-suffix.txt deleted file mode 100644 index d0a1596..0000000 --- a/internal/testdata/description-suffix.txt +++ /dev/null @@ -1,31 +0,0 @@ - -## v1.0.0 - - ---- - -
-

PR by releaser-pleaser 🤖

- -If you want to modify the proposed release, add you overrides here. You can learn more about the options in the docs. - -## Release Notes - -### Prefix / Start - -This will be added to the start of the release notes. - -~~~~rp-prefix -~~~~ - -### Suffix / End - -This will be added to the end of the release notes. - -~~~~rp-suffix -## Compatibility - -This version is compatible with flux-compensator v2.2 - v2.9. -~~~~ - -
diff --git a/internal/testdata/prefix.txt b/internal/testdata/prefix.txt deleted file mode 100644 index 55271d3..0000000 --- a/internal/testdata/prefix.txt +++ /dev/null @@ -1,13 +0,0 @@ -## Foo - -- Cool thing - -```go -// Some code example -func IsPositive(number int) error { - if number < 0 { - return fmt.Errorf("number %d is negative", number) - } - return nil -} -``` \ No newline at end of file diff --git a/internal/testdata/suffix.txt b/internal/testdata/suffix.txt deleted file mode 100644 index 3fc6656..0000000 --- a/internal/testdata/suffix.txt +++ /dev/null @@ -1,3 +0,0 @@ -## Compatibility - -This version is compatible with flux-compensator v2.2 - v2.9. \ No newline at end of file diff --git a/internal/testdata/testdata.go b/internal/testdata/testdata.go deleted file mode 100644 index 28c87d1..0000000 --- a/internal/testdata/testdata.go +++ /dev/null @@ -1,19 +0,0 @@ -package testdata - -import ( - "embed" - "testing" -) - -//go:embed *.txt -var testdata embed.FS - -func MustReadFileString(t *testing.T, name string) string { - t.Helper() - - content, err := testdata.ReadFile(name) - if err != nil { - t.Fatal(err) - } - return string(content) -} diff --git a/internal/updater/changelog.go b/internal/updater/changelog.go index a7c7506..8bdb9f6 100644 --- a/internal/updater/changelog.go +++ b/internal/updater/changelog.go @@ -14,22 +14,7 @@ var ( ChangelogUpdaterHeaderRegex = regexp.MustCompile(`^# Changelog\n`) ) -func Changelog() Updater { - 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) { +func Changelog(info ReleaseInfo) Updater { return func(content string) (string, error) { headerIndex := ChangelogUpdaterHeaderRegex.FindStringIndex(content) if headerIndex == nil && len(content) != 0 { diff --git a/internal/updater/changelog_test.go b/internal/updater/changelog_test.go index c0becb5..917cd14 100644 --- a/internal/updater/changelog_test.go +++ b/internal/updater/changelog_test.go @@ -6,15 +6,7 @@ import ( "github.com/stretchr/testify/assert" ) -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) { +func TestChangelogUpdater_UpdateContent(t *testing.T) { tests := []updaterTestCase{ { name: "empty file", @@ -62,7 +54,7 @@ func TestChangelogUpdater_Update(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - runUpdaterTest(t, Changelog(), tt) + runUpdaterTest(t, Changelog, tt) }) } } diff --git a/internal/updater/generic.go b/internal/updater/generic.go index 11b21a4..b8d73b0 100644 --- a/internal/updater/generic.go +++ b/internal/updater/generic.go @@ -7,25 +7,7 @@ import ( var GenericUpdaterSemVerRegex = regexp.MustCompile(`\d+\.\d+\.\d+(-[\w.]+)?(.*x-releaser-pleaser-version)`) -func Generic(files []string) Updater { - 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) { +func Generic(info ReleaseInfo) Updater { return func(content 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 7c007a4..e0a8d1d 100644 --- a/internal/updater/generic_test.go +++ b/internal/updater/generic_test.go @@ -6,15 +6,7 @@ import ( "github.com/stretchr/testify/assert" ) -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) { +func TestGenericUpdater_UpdateContent(t *testing.T) { tests := []updaterTestCase{ { name: "single line", @@ -55,7 +47,7 @@ func TestGenericUpdater_Update(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - runUpdaterTest(t, Generic([]string{"version.txt"}), tt) + runUpdaterTest(t, Generic, tt) }) } } diff --git a/internal/updater/packagejson.go b/internal/updater/packagejson.go deleted file mode 100644 index 0fcb8a5..0000000 --- a/internal/updater/packagejson.go +++ /dev/null @@ -1,39 +0,0 @@ -package updater - -import ( - "regexp" - "strings" -) - -// PackageJson creates an updater that modifies the version field in package.json files -func PackageJson() Updater { - return packagejson{} -} - -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 - 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 - return versionRegex.ReplaceAllString(content, `${1}"`+version+`"`), nil - } -} diff --git a/internal/updater/packagejson_test.go b/internal/updater/packagejson_test.go deleted file mode 100644 index 9bff8b7..0000000 --- a/internal/updater/packagejson_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package updater - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -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{ - { - name: "simple package.json", - content: `{"name":"test","version":"1.0.0"}`, - info: ReleaseInfo{ - Version: "v2.0.5", - }, - want: `{"name":"test","version":"2.0.5"}`, - 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}", - 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`, - info: ReleaseInfo{ - Version: "v2.0.0", - }, - want: `not json`, - wantErr: assert.NoError, - }, - { - name: "json without version", - content: `{"name":"test"}`, - info: ReleaseInfo{ - Version: "v2.0.0", - }, - want: `{"name":"test"}`, - wantErr: assert.NoError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - runUpdaterTest(t, PackageJson(), tt) - }) - } -} diff --git a/internal/updater/updater.go b/internal/updater/updater.go index 6e27f37..fb773b4 100644 --- a/internal/updater/updater.go +++ b/internal/updater/updater.go @@ -5,11 +5,7 @@ type ReleaseInfo struct { ChangelogEntry string } -type Updater interface { - Files() []string - CreateNewFiles() bool - Update(info ReleaseInfo) func(content string) (string, error) -} +type Updater func(string) (string, error) type NewUpdater func(ReleaseInfo) Updater diff --git a/internal/updater/updater_test.go b/internal/updater/updater_test.go index 5a90936..0c0c40e 100644 --- a/internal/updater/updater_test.go +++ b/internal/updater/updater_test.go @@ -15,10 +15,10 @@ type updaterTestCase struct { wantErr assert.ErrorAssertionFunc } -func runUpdaterTest(t *testing.T, u Updater, tt updaterTestCase) { +func runUpdaterTest(t *testing.T, constructor NewUpdater, tt updaterTestCase) { t.Helper() - got, err := u.Update(tt.info)(tt.content) + got, err := constructor(tt.info)(tt.content) if !tt.wantErr(t, err, fmt.Sprintf("Updater(%v, %v)", tt.content, tt.info)) { return } diff --git a/mise.toml b/mise.toml deleted file mode 100644 index 4db342a..0000000 --- a/mise.toml +++ /dev/null @@ -1,25 +0,0 @@ -[tools] -go = "1.25.5" -golangci-lint = "2.8.0" -goreleaser = "v2.9.0" -mdbook = "v0.5.2" # renovate: datasource=github-releases depName=rust-lang/mdbook -ko = "v0.18.1" # renovate: datasource=github-releases depName=ko-build/ko - -[settings] -# Experimental features are needed for the Go backend -experimental = true - -[tasks.lint] -run = "golangci-lint run" - -[tasks.test] -run = "go test -v -race ./..." - -[tasks.test-e2e] -run = "go test -tags e2e_forgejo -v -race ./test/e2e/forgejo" - -[tasks.e2e-forgejo-start] -run = "docker compose --project-directory ./test/e2e/forgejo up -d --wait" - -[tasks.e2e-forgejo-stop] -run = "docker compose --project-directory ./test/e2e/forgejo down" diff --git a/releaserpleaser.go b/releaserpleaser.go index b72b85f..a09aefe 100644 --- a/releaserpleaser.go +++ b/releaserpleaser.go @@ -34,10 +34,10 @@ type ReleaserPleaser struct { commitParser commitparser.CommitParser versioning versioning.Strategy extraFiles []string - updaters []updater.Updater + updaters []updater.NewUpdater } -func New(forge forge.Forge, logger *slog.Logger, targetBranch string, commitParser commitparser.CommitParser, versioningStrategy versioning.Strategy, extraFiles []string, updaters []updater.Updater) *ReleaserPleaser { +func New(forge forge.Forge, logger *slog.Logger, targetBranch string, commitParser commitparser.CommitParser, versioningStrategy versioning.Strategy, extraFiles []string, updaters []updater.NewUpdater) *ReleaserPleaser { return &ReleaserPleaser{ forge: forge, logger: logger, @@ -281,12 +281,16 @@ func (rp *ReleaserPleaser) runReconcileReleasePR(ctx context.Context) error { // Info for updaters info := updater.ReleaseInfo{Version: nextVersion, ChangelogEntry: changelogEntry} - for _, u := range rp.updaters { - for _, file := range u.Files() { - err = repo.UpdateFile(ctx, file, u.CreateNewFiles(), u.Update(info)) - if err != nil { - return fmt.Errorf("failed to run updater %T: %w", u, err) - } + err = repo.UpdateFile(ctx, updater.ChangelogFile, true, updater.WithInfo(info, updater.Changelog)) + if err != nil { + return fmt.Errorf("failed to update changelog file: %w", 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) } } diff --git a/templates/run.yml b/templates/run.yml index b8a6513..c18a330 100644 --- a/templates/run.yml +++ b/templates/run.yml @@ -9,11 +9,7 @@ spec: description: "GitLab token for creating and updating release MRs." extra-files: - description: 'List of files that are scanned for version references by the generic updater.' - default: "" - - updaters: - 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" + description: 'List of files that are scanned for version references.' default: "" stage: @@ -44,7 +40,7 @@ releaser-pleaser: resource_group: releaser-pleaser image: - name: ghcr.io/apricote/releaser-pleaser:v0.7.1 # x-releaser-pleaser-version + name: ghcr.io/apricote/releaser-pleaser:v0.6.1 # x-releaser-pleaser-version entrypoint: [ "" ] variables: GITLAB_TOKEN: $[[ inputs.token ]] @@ -53,5 +49,4 @@ releaser-pleaser: rp run \ --forge=gitlab \ --branch=$[[ inputs.branch ]] \ - --extra-files="$[[ inputs.extra-files ]]" \ - --updaters="$[[ inputs.updaters ]]" + --extra-files="$[[ inputs.extra-files ]]" diff --git a/test/e2e/forge.go b/test/e2e/forge.go deleted file mode 100644 index ea55545..0000000 --- a/test/e2e/forge.go +++ /dev/null @@ -1,19 +0,0 @@ -package e2e - -import ( - "context" - "testing" -) - -type TestForge interface { - Init(ctx context.Context, runID string) error - CreateRepo(t *testing.T, opts CreateRepoOpts) (*Repository, error) - - RunArguments() []string -} - -type CreateRepoOpts struct { - Name string - Description string - DefaultBranch string -} diff --git a/test/e2e/forgejo/app.ini b/test/e2e/forgejo/app.ini deleted file mode 100644 index 4a22d2e..0000000 --- a/test/e2e/forgejo/app.ini +++ /dev/null @@ -1,23 +0,0 @@ -WORK_PATH = /data/gitea - -[database] -DB_TYPE = sqlite3 -PATH = /data/gitea/forgejo.db - -[security] -INSTALL_LOCK = true -SECRET_KEY = releaser-pleaser -INTERNAL_TOKEN = releaser-pleaser - -[service] -REGISTER_EMAIL_CONFIRM = false -ENABLE_NOTIFY_MAIL = false -DISABLE_REGISTRATION = true - -[server] -DOMAIN = localhost -HTTP_PORT = 3000 -ROOT_URL = http://localhost:3000/ - -[oauth2] -JWT_SECRET = rTD-FL2n_aBB6v4AOcr5lBvwgZ6PSr3HGZAuNH6nMu8 diff --git a/test/e2e/forgejo/compose.yaml b/test/e2e/forgejo/compose.yaml deleted file mode 100644 index 3f20c3a..0000000 --- a/test/e2e/forgejo/compose.yaml +++ /dev/null @@ -1,16 +0,0 @@ -services: - forgejo: - image: codeberg.org/forgejo/forgejo:11 - ports: - - '3000:3000' - - '222:22' - volumes: - - data:/data/gitea - - ./app.ini:/data/gitea/conf/app.ini:ro - - healthcheck: - test: ["CMD", "curl", "localhost:3000/api/healthz"] - - -volumes: - data: diff --git a/test/e2e/forgejo/forge.go b/test/e2e/forgejo/forge.go deleted file mode 100644 index c631cb8..0000000 --- a/test/e2e/forgejo/forge.go +++ /dev/null @@ -1,113 +0,0 @@ -package forgejo - -import ( - "context" - "fmt" - "log/slog" - "os/exec" - "strings" - "testing" - - "codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2" - - "github.com/apricote/releaser-pleaser/test/e2e" -) - -const ( - TestAPIURL = "http://localhost:3000" - - TestUserNameTemplate = "rp-%s" - TestUserPassword = "releaser-pleaser" - TestUserEmailTemplate = "releaser-pleaser-%s@example.com" - TestTokenName = "rp" - TestTokenScopes = "write:user,write:issue,write:repository" -) - -type TestForge struct { - username string - token string - client *forgejo.Client -} - -func (f *TestForge) Init(ctx context.Context, runID string) error { - if err := f.initUser(ctx, runID); err != nil { - return err - } - if err := f.initClient(ctx); err != nil { - return err - } - - return nil -} - -func (f *TestForge) initUser(ctx context.Context, runID string) error { - f.username = fmt.Sprintf(TestUserNameTemplate, runID) - - //gosec:disable G204 - if output, err := exec.CommandContext(ctx, - "docker", "compose", "exec", "--user=1000", "forgejo", - "forgejo", "admin", "user", "create", - "--username", f.username, - "--password", TestUserPassword, - "--email", fmt.Sprintf(TestUserEmailTemplate, runID), - "--must-change-password=false", - ).CombinedOutput(); err != nil { - slog.Debug("create forgejo user output", "output", output) - return fmt.Errorf("failed to create forgejo user: %w", err) - } - - //gosec:disable G204 - token, err := exec.CommandContext(ctx, - "docker", "compose", "exec", "--user=1000", "forgejo", - "forgejo", "admin", "user", "generate-access-token", - "--username", f.username, - "--token-name", TestTokenName, - "--scopes", TestTokenScopes, - "--raw", - ).Output() - if err != nil { - return fmt.Errorf("failed to create forgejo token: %w", err) - } - - f.token = strings.TrimSpace(string(token)) - - return nil -} - -func (f *TestForge) initClient(ctx context.Context) (err error) { - f.client, err = forgejo.NewClient(TestAPIURL, - forgejo.SetToken(f.token), - forgejo.SetUserAgent("releaser-pleaser-e2e-tests"), - forgejo.SetContext(ctx), - // forgejo.SetDebugMode(), - ) - return err -} - -func (f *TestForge) CreateRepo(t *testing.T, opts e2e.CreateRepoOpts) (*e2e.Repository, error) { - t.Helper() - - repo, _, err := f.client.CreateRepo(forgejo.CreateRepoOption{ - Name: opts.Name, - Description: opts.Description, - DefaultBranch: opts.DefaultBranch, - Readme: "Default", - AutoInit: true, - }) - if err != nil { - return nil, err - } - - return &e2e.Repository{ - Name: repo.Name, - }, nil -} - -func (f *TestForge) RunArguments() []string { - return []string{"--forge=forgejo", - fmt.Sprintf("--owner=%s", f.username), - fmt.Sprintf("--api-url=%s", TestAPIURL), - fmt.Sprintf("--api-token=%s", f.token), - fmt.Sprintf("--username=%s", f.username), - } -} diff --git a/test/e2e/forgejo/forgejo_test.go b/test/e2e/forgejo/forgejo_test.go deleted file mode 100644 index b52504f..0000000 --- a/test/e2e/forgejo/forgejo_test.go +++ /dev/null @@ -1,39 +0,0 @@ -//go:build e2e_forgejo - -package forgejo - -import ( - "context" - "log/slog" - "os" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/apricote/releaser-pleaser/test/e2e" -) - -var ( - f *e2e.Framework -) - -func TestMain(m *testing.M) { - ctx := context.Background() - - var err error - f, err = e2e.NewFramework(ctx, &TestForge{}) - if err != nil { - slog.Error("failed to set up test framework", "err", err) - } - - os.Exit(m.Run()) -} - -func TestCreateRepository(t *testing.T) { - _ = f.NewRepository(t, t.Name()) -} - -func TestRun(t *testing.T) { - repo := f.NewRepository(t, t.Name()) - require.NoError(t, f.Run(t, repo, []string{})) -} diff --git a/test/e2e/framework.go b/test/e2e/framework.go deleted file mode 100644 index 7352f14..0000000 --- a/test/e2e/framework.go +++ /dev/null @@ -1,96 +0,0 @@ -package e2e - -import ( - "bytes" - "context" - "crypto/rand" - "encoding/hex" - "fmt" - "strings" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/apricote/releaser-pleaser/cmd/rp/cmd" -) - -const ( - TestDefaultBranch = "main" -) - -func randomString() string { - randomBytes := make([]byte, 4) - if _, err := rand.Read(randomBytes); err != nil { - panic(err) - } - return hex.EncodeToString(randomBytes) -} - -type Framework struct { - runID string - forge TestForge -} - -func NewFramework(ctx context.Context, forge TestForge) (*Framework, error) { - f := &Framework{ - runID: randomString(), - forge: forge, - } - - err := forge.Init(ctx, f.runID) - if err != nil { - return nil, err - } - - return f, nil -} - -type Repository struct { - Name string -} - -func (f *Framework) NewRepository(t *testing.T, name string) *Repository { - t.Helper() - - r := &Repository{ - Name: fmt.Sprintf("%s-%s-%s", name, f.runID, randomString()), - } - - repo, err := f.forge.CreateRepo(t, CreateRepoOpts{ - Name: r.Name, - Description: name, - DefaultBranch: TestDefaultBranch, - }) - require.NoError(t, err) - require.NotNil(t, repo) - - return r -} - -func (f *Framework) Run(t *testing.T, r *Repository, extraFiles []string) error { - t.Helper() - - ctx := t.Context() - - rootCmd := cmd.NewRootCmd() - rootCmd.SetArgs(append([]string{ - "run", - fmt.Sprintf("--repo=%s", r.Name), - fmt.Sprintf("--extra-files=%q", strings.Join(extraFiles, "\n")), - }, f.forge.RunArguments()...)) - - var stdout, stderr bytes.Buffer - - rootCmd.SetOut(&stdout) - rootCmd.SetErr(&stderr) - - err := rootCmd.ExecuteContext(ctx) - - stdoutString := stdout.String() - stderrString := stderr.String() - - t.Log(stdoutString) - t.Log(stderrString) - - return err -}