mirror of
https://github.com/apricote/releaser-pleaser.git
synced 2026-01-13 21:21:03 +00:00
Compare commits
No commits in common. "main" and "v0.6.0" have entirely different histories.
63 changed files with 392 additions and 1832 deletions
16
.github/actions/setup-mdbook/action.yaml
vendored
Normal file
16
.github/actions/setup-mdbook/action.yaml
vendored
Normal file
|
|
@ -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
|
||||||
9
.github/renovate.json5
vendored
9
.github/renovate.json5
vendored
|
|
@ -69,15 +69,6 @@
|
||||||
': (?<currentValue>.+) # renovate: datasource=(?<datasource>[a-z-]+) depName=(?<depName>[^\\s]+)(?: lookupName=(?<packageName>[^\\s]+))?(?: versioning=(?<versioning>[a-z-]+))?(?: extractVersion=(?<extractVersion>[^\\s]+))?',
|
': (?<currentValue>.+) # renovate: datasource=(?<datasource>[a-z-]+) depName=(?<depName>[^\\s]+)(?: lookupName=(?<packageName>[^\\s]+))?(?: versioning=(?<versioning>[a-z-]+))?(?: extractVersion=(?<extractVersion>[^\\s]+))?',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
customType: 'regex',
|
|
||||||
managerFilePatterns: [
|
|
||||||
'/.+\\.toml$/'
|
|
||||||
],
|
|
||||||
matchStrings: [
|
|
||||||
'= "(?<currentValue>.+)" # renovate: datasource=(?<datasource>[a-z-]+) depName=(?<depName>[^\\s]+)(?: lookupName=(?<packageName>[^\\s]+))?(?: versioning=(?<versioning>[a-z-]+))?(?: extractVersion=(?<extractVersion>[^\\s]+))?',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
postUpdateOptions: [
|
postUpdateOptions: [
|
||||||
'gomodUpdateImportPaths',
|
'gomodUpdateImportPaths',
|
||||||
|
|
|
||||||
70
.github/workflows/ci.yaml
vendored
70
.github/workflows/ci.yaml
vendored
|
|
@ -2,7 +2,7 @@ name: ci
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [main]
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
@ -10,68 +10,48 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- 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
|
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
|
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
|
|
||||||
# We can not use "jobs.<job>.services".
|
- name: Run golangci-lint
|
||||||
# We want to mount the config file, which is only available after "Checkout".
|
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8
|
||||||
- name: Start Forgejo
|
with:
|
||||||
working-directory: test/e2e/forgejo
|
version: v2.1.6 # renovate: datasource=github-releases depName=golangci/golangci-lint
|
||||||
run: docker compose up --wait
|
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
|
- 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
|
- name: Upload results to Codecov
|
||||||
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5
|
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
flags: e2e
|
|
||||||
|
|
||||||
go-mod-tidy:
|
go-mod-tidy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- 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
|
- name: Run go mod tidy
|
||||||
run: go mod tidy
|
run: go mod tidy
|
||||||
|
|
|
||||||
8
.github/workflows/docs.yaml
vendored
8
.github/workflows/docs.yaml
vendored
|
|
@ -13,11 +13,13 @@ jobs:
|
||||||
id-token: write # To update the deployment status
|
id-token: write # To update the deployment status
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
lfs: "true"
|
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
|
- name: Build Book
|
||||||
working-directory: docs
|
working-directory: docs
|
||||||
|
|
@ -27,7 +29,7 @@ jobs:
|
||||||
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5
|
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4
|
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3
|
||||||
with:
|
with:
|
||||||
# Upload entire repository
|
# Upload entire repository
|
||||||
path: "docs/book"
|
path: "docs/book"
|
||||||
|
|
|
||||||
2
.github/workflows/mirror.yaml
vendored
2
.github/workflows/mirror.yaml
vendored
|
|
@ -11,7 +11,7 @@ jobs:
|
||||||
REMOTE: mirror
|
REMOTE: mirror
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
# Need all to fetch all tags so we can push them
|
# Need all to fetch all tags so we can push them
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
|
||||||
16
.github/workflows/release.yaml
vendored
16
.github/workflows/release.yaml
vendored
|
|
@ -14,16 +14,12 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- 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
|
||||||
- name: Prepare ko
|
with:
|
||||||
run: |
|
go-version-file: go.mod
|
||||||
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
|
|
||||||
|
|
||||||
|
- uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9
|
||||||
- run: ko build --bare --tags ${{ github.ref_name }} github.com/apricote/releaser-pleaser/cmd/rp
|
- run: ko build --bare --tags ${{ github.ref_name }} github.com/apricote/releaser-pleaser/cmd/rp
|
||||||
|
|
|
||||||
15
.github/workflows/releaser-pleaser.yaml
vendored
15
.github/workflows/releaser-pleaser.yaml
vendored
|
|
@ -2,7 +2,7 @@ name: releaser-pleaser
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [main]
|
||||||
# Using pull_request_target to avoid tainting the actual release PR with code from open feature pull requests
|
# Using pull_request_target to avoid tainting the actual release PR with code from open feature pull requests
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types:
|
types:
|
||||||
|
|
@ -17,7 +17,7 @@ concurrency:
|
||||||
group: releaser-pleaser
|
group: releaser-pleaser
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
permissions: { }
|
permissions: {}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
releaser-pleaser:
|
releaser-pleaser:
|
||||||
|
|
@ -25,18 +25,23 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
ref: main
|
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`
|
# 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
|
# 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.
|
# 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: 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.
|
# Dogfood the action to make sure it works for users.
|
||||||
- name: releaser-pleaser
|
- name: releaser-pleaser
|
||||||
|
|
|
||||||
|
|
@ -48,10 +48,6 @@ linters:
|
||||||
- name: exported
|
- name: exported
|
||||||
disabled: true
|
disabled: true
|
||||||
|
|
||||||
gomoddirectives:
|
|
||||||
replace-allow-list:
|
|
||||||
- codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2
|
|
||||||
|
|
||||||
formatters:
|
formatters:
|
||||||
enable:
|
enable:
|
||||||
- gci
|
- gci
|
||||||
|
|
|
||||||
35
CHANGELOG.md
35
CHANGELOG.md
|
|
@ -1,40 +1,5 @@
|
||||||
# Changelog
|
# 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
|
|
||||||
|
|
||||||
- **gitlab**: support fast-forward merges (#210)
|
|
||||||
|
|
||||||
## [v0.6.0](https://github.com/apricote/releaser-pleaser/releases/tag/v0.6.0)
|
## [v0.6.0](https://github.com/apricote/releaser-pleaser/releases/tag/v0.6.0)
|
||||||
|
|
||||||
### ✨ Highlights
|
### ✨ Highlights
|
||||||
|
|
|
||||||
11
action.yml
11
action.yml
|
|
@ -14,24 +14,19 @@ inputs:
|
||||||
required: false
|
required: false
|
||||||
default: ${{ github.token }}
|
default: ${{ github.token }}
|
||||||
extra-files:
|
extra-files:
|
||||||
description: 'List of files that are scanned for version references by the generic updater.'
|
description: 'List of files that are scanned for version references.'
|
||||||
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"
|
|
||||||
required: false
|
required: false
|
||||||
default: ""
|
default: ""
|
||||||
# Remember to update docs/reference/github-action.md
|
# Remember to update docs/reference/github-action.md
|
||||||
outputs: { }
|
outputs: {}
|
||||||
runs:
|
runs:
|
||||||
using: 'docker'
|
using: 'docker'
|
||||||
image: docker://ghcr.io/apricote/releaser-pleaser:v0.7.1 # x-releaser-pleaser-version
|
image: docker://ghcr.io/apricote/releaser-pleaser:v0.6.0 # x-releaser-pleaser-version
|
||||||
args:
|
args:
|
||||||
- run
|
- run
|
||||||
- --forge=github
|
- --forge=github
|
||||||
- --branch=${{ inputs.branch }}
|
- --branch=${{ inputs.branch }}
|
||||||
- --extra-files="${{ inputs.extra-files }}"
|
- --extra-files="${{ inputs.extra-files }}"
|
||||||
- --updaters="${{ inputs.updaters }}"
|
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: "${{ inputs.token }}"
|
GITHUB_TOKEN: "${{ inputs.token }}"
|
||||||
GITHUB_USER: "oauth2"
|
GITHUB_USER: "oauth2"
|
||||||
|
|
|
||||||
|
|
@ -7,23 +7,21 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lmittmann/tint"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewRootCmd() *cobra.Command {
|
var logger *slog.Logger
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.AddCommand(newRunCommand())
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "rp",
|
||||||
return cmd
|
Short: "",
|
||||||
|
Long: ``,
|
||||||
|
Version: version(),
|
||||||
|
SilenceUsage: true, // Makes it harder to find the actual error
|
||||||
|
SilenceErrors: true, // We log manually with slog
|
||||||
}
|
}
|
||||||
|
|
||||||
func version() string {
|
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
|
// 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.
|
// to the runtime. The Go runtime implements a "force shutdown" if the signal is received again.
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
slog.InfoContext(ctx, "Received shutdown signal, stopping...")
|
logger.InfoContext(ctx, "Received shutdown signal, stopping...")
|
||||||
stop()
|
stop()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := NewRootCmd().ExecuteContext(ctx)
|
err := rootCmd.ExecuteContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.ErrorContext(ctx, err.Error())
|
logger.ErrorContext(ctx, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
logger = slog.New(
|
||||||
|
tint.NewHandler(os.Stderr, &tint.Options{
|
||||||
|
Level: slog.LevelDebug,
|
||||||
|
TimeFormat: time.RFC3339,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
slog.SetDefault(logger)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
|
||||||
"slices"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
@ -11,130 +9,89 @@ import (
|
||||||
rp "github.com/apricote/releaser-pleaser"
|
rp "github.com/apricote/releaser-pleaser"
|
||||||
"github.com/apricote/releaser-pleaser/internal/commitparser/conventionalcommits"
|
"github.com/apricote/releaser-pleaser/internal/commitparser/conventionalcommits"
|
||||||
"github.com/apricote/releaser-pleaser/internal/forge"
|
"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/github"
|
||||||
"github.com/apricote/releaser-pleaser/internal/forge/gitlab"
|
"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/updater"
|
||||||
"github.com/apricote/releaser-pleaser/internal/versioning"
|
"github.com/apricote/releaser-pleaser/internal/versioning"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newRunCommand() *cobra.Command {
|
var runCmd = &cobra.Command{
|
||||||
var (
|
Use: "run",
|
||||||
flagForge string
|
RunE: run,
|
||||||
flagBranch string
|
}
|
||||||
flagOwner string
|
|
||||||
flagRepo string
|
|
||||||
flagExtraFiles string
|
|
||||||
flagUpdaters []string
|
|
||||||
|
|
||||||
flagAPIURL string
|
var (
|
||||||
flagAPIToken string
|
flagForge string
|
||||||
flagUsername 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{
|
var f forge.Forge
|
||||||
Use: "run",
|
|
||||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
|
||||||
ctx := cmd.Context()
|
|
||||||
logger := log.GetLogger(cmd.ErrOrStderr())
|
|
||||||
|
|
||||||
var err error
|
forgeOptions := forge.Options{
|
||||||
|
Repository: flagRepo,
|
||||||
logger.DebugContext(ctx, "run called",
|
BaseBranch: flagBranch,
|
||||||
"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)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.PersistentFlags().StringVar(&flagForge, "forge", "", "")
|
switch flagForge {
|
||||||
cmd.PersistentFlags().StringVar(&flagBranch, "branch", "main", "")
|
case "gitlab":
|
||||||
cmd.PersistentFlags().StringVar(&flagOwner, "owner", "", "")
|
logger.DebugContext(ctx, "using forge GitLab")
|
||||||
cmd.PersistentFlags().StringVar(&flagRepo, "repo", "", "")
|
f, err = gitlab.New(logger, &gitlab.Options{
|
||||||
cmd.PersistentFlags().StringVar(&flagExtraFiles, "extra-files", "", "")
|
Options: forgeOptions,
|
||||||
cmd.PersistentFlags().StringSliceVar(&flagUpdaters, "updaters", []string{}, "")
|
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", "", "")
|
extraFiles := parseExtraFiles(flagExtraFiles)
|
||||||
cmd.PersistentFlags().StringVar(&flagAPIToken, "api-token", "", "")
|
|
||||||
cmd.PersistentFlags().StringVar(&flagUsername, "username", "", "")
|
|
||||||
|
|
||||||
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 {
|
func parseExtraFiles(input string) []string {
|
||||||
|
|
@ -156,26 +113,3 @@ func parseExtraFiles(input string) []string {
|
||||||
|
|
||||||
return extraFiles
|
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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/apricote/releaser-pleaser/cmd/rp/cmd"
|
"github.com/apricote/releaser-pleaser/cmd/rp/cmd"
|
||||||
_ "github.com/apricote/releaser-pleaser/internal/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
ignore:
|
|
||||||
- "test"
|
|
||||||
|
|
@ -25,7 +25,6 @@
|
||||||
- [Pull Request Options](reference/pr-options.md)
|
- [Pull Request Options](reference/pr-options.md)
|
||||||
- [GitHub Action](reference/github-action.md)
|
- [GitHub Action](reference/github-action.md)
|
||||||
- [GitLab CI/CD Component](reference/gitlab-cicd-component.md)
|
- [GitLab CI/CD Component](reference/gitlab-cicd-component.md)
|
||||||
- [Updaters](reference/updaters.md)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
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
|
> ### Prefix
|
||||||
>
|
>
|
||||||
> This will be shown as the Prefix.
|
> This will be shown as the Prefix.
|
||||||
> ~~~~
|
> ```
|
||||||
>
|
>
|
||||||
> ~~~~rp-suffix
|
> ```rp-suffix
|
||||||
> ### Suffix
|
> ### Suffix
|
||||||
>
|
>
|
||||||
> This will be shown as the 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`).
|
To match the style of the auto-generated release notes, you should start any headings at level 3 (`### Title`).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,7 @@ In some situations it makes sense to have the current version committed in files
|
||||||
|
|
||||||
## Markers
|
## Markers
|
||||||
|
|
||||||
The line that needs to be updated must have the marker
|
The line that needs to be updated must have the marker `x-releaser-pleaser-version` somewhere after the version that should be updated.
|
||||||
`x-releaser-pleaser-version` somewhere after the version that should be updated.
|
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
|
|
@ -29,8 +28,7 @@ You need to tell `releaser-pleaser` which files it should update. This happens t
|
||||||
|
|
||||||
### GitHub Action
|
### GitHub Action
|
||||||
|
|
||||||
In the GitHub Action you can set the
|
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:
|
||||||
`extra-files` input with a list of the files. They need to be formatted as a single multi-line string with one file path per line:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
jobs:
|
jobs:
|
||||||
|
|
@ -46,8 +44,7 @@ jobs:
|
||||||
|
|
||||||
### GitLab CI/CD Component
|
### GitLab CI/CD Component
|
||||||
|
|
||||||
In the GitLab CI/CD Component you can set the
|
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:
|
||||||
`extra-files` input with a list of files. They need to be formatted as a single multi-line string with one file path per line:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
include:
|
include:
|
||||||
|
|
@ -64,4 +61,3 @@ include:
|
||||||
- **Reference**
|
- **Reference**
|
||||||
- [GitHub Action](../reference/github-action.md#inputs)
|
- [GitHub Action](../reference/github-action.md#inputs)
|
||||||
- [GitLab CI/CD Component](../reference/gitlab-cicd-component.md#inputs)
|
- [GitLab CI/CD Component](../reference/gitlab-cicd-component.md#inputs)
|
||||||
- [Updaters](../reference/updaters.md#generic-updater)
|
|
||||||
|
|
|
||||||
|
|
@ -8,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 `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.
|
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`.
|
||||||
`v1`) right now ([#31](https://github.com/apricote/releaser-pleaser/issues/31)). You have to use the full version or commit SHA instead:
|
|
||||||
`apricote/releaser-pleaser@v0.2.0`.
|
|
||||||
|
|
||||||
## Inputs
|
## Inputs
|
||||||
|
|
||||||
The following inputs are supported by the `apricote/releaser-pleaser` GitHub Action.
|
The following inputs are supported by the `apricote/releaser-pleaser` GitHub Action.
|
||||||
|
|
||||||
| Input | Description | Default | Example |
|
| Input | Description | Default | Example |
|
||||||
|---------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------:|---------------------------------------------------------------------:|
|
| ------------- | :----------------------------------------------------- | --------------: | -------------------------------------------------------------------: |
|
||||||
| `branch` | This branch is used as the target for releases. | `main` | `master` |
|
| `branch` | This branch is used as the target for releases. | `main` | `master` |
|
||||||
| `token` | GitHub token for creating and updating release PRs | `$GITHUB_TOKEN` | `${{secrets.RELEASER_PLEASER_TOKEN}}` |
|
| `token` | GitHub token for creating and updating release PRs | `$GITHUB_TOKEN` | `${{secrets.RELEASER_PLEASER_TOKEN}}` |
|
||||||
| `extra-files` | List of files that are scanned for version references by the generic updater. | `""` | <pre><code>version/version.go<br>deploy/deployment.yaml</code></pre> |
|
| `extra-files` | List of files that are scanned for version references. | `""` | <pre><code>version/version.go<br>deploy/deployment.yaml</code></pre> |
|
||||||
| `updaters` | List of updaters that are run. Default updaters can be removed by specifying them as -name. Multiple updaters should be concatenated with a comma. Default Updaters: changelog,generic | `""` | `-generic,packagejson` |
|
|
||||||
|
|
||||||
## Outputs
|
## Outputs
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,10 @@ The component does not support floating tags (e.g.
|
||||||
|
|
||||||
The following inputs are supported by the component.
|
The following inputs are supported by the component.
|
||||||
|
|
||||||
| Input | Description | Default | Example |
|
| Input | Description | Default | Example |
|
||||||
|------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------:|---------------------------------------------------------------------:|
|
| ---------------------- | :-------------------------------------------------------- | ------: | -------------------------------------------------------------------: |
|
||||||
| `branch` | This branch is used as the target for releases. | `main` | `master` |
|
| `branch` | This branch is used as the target for releases. | `main` | `master` |
|
||||||
| `token` (**required**) | GitLab access token for creating and updating release PRs | | `$RELEASER_PLEASER_TOKEN` |
|
| `token` (**required**) | GitLab access token for creating and updating release PRs | | `$RELEASER_PLEASER_TOKEN` |
|
||||||
| `extra-files` | List of files that are scanned for version references by the generic updater. | `""` | <pre><code>version/version.go<br>deploy/deployment.yaml</code></pre> |
|
| `extra-files` | List of files that are scanned for version references. | `""` | <pre><code>version/version.go<br>deploy/deployment.yaml</code></pre> |
|
||||||
| `updaters` | List of updaters that are run. Default updaters can be removed by specifying them as -name. Multiple updaters should be concatenated with a comma. Default Updaters: changelog,generic | `""` | `-generic,packagejson` |
|
| `stage` | Stage the job runs in. Must exists. | `build` | `test` |
|
||||||
| `stage` | Stage the job runs in. Must exists. | `build` | `test` |
|
| `needs` | Other jobs the releaser-pleaser job depends on. | `[]` | <pre><code>- validate-foo<br>- prepare-bar</code></pre> |
|
||||||
| `needs` | Other jobs the releaser-pleaser job depends on. | `[]` | <pre><code>- validate-foo<br>- prepare-bar</code></pre> |
|
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,17 @@
|
||||||
|
|
||||||
### Changelog
|
### Changelog
|
||||||
|
|
||||||
The Changelog is a file in the repository (
|
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.
|
||||||
`CHANGELOG.md`) that contains the [Release Notes](#release-notes) for every release of that repository. Usually, new releases are added at the top of the file.
|
|
||||||
|
|
||||||
### Conventional Commits
|
### Conventional Commits
|
||||||
|
|
||||||
[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) is a specification for commit messages. It is the only supported commit message schema in
|
[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.
|
||||||
`releaser-pleaser`. Follow the link to learn more.
|
|
||||||
|
|
||||||
### Forge
|
### Forge
|
||||||
|
|
||||||
A **forge
|
A **forge** is a web-based collaborative software platform for both developing and sharing computer applications.[^wp-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
|
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.
|
||||||
** in the future ([#4](https://github.com/apricote/releaser-pleaser/issues/4)). For other forges like Forgejo or Gitea, please open an issue and submit a pull request.
|
|
||||||
|
|
||||||
[^wp-forge]: Quote from [Wikipedia "Forge (software)"](<https://en.wikipedia.org/wiki/Forge_(software)>)
|
[^wp-forge]: Quote from [Wikipedia "Forge (software)"](<https://en.wikipedia.org/wiki/Forge_(software)>)
|
||||||
|
|
||||||
|
|
@ -28,8 +24,7 @@ In `releaser-pleaser` Markdown is used for most texts.
|
||||||
|
|
||||||
### Pre-release
|
### Pre-release
|
||||||
|
|
||||||
Pre-releases are a concept of [SemVer](#semantic-versioning-semver). They follow the normal versioning schema but use a suffix out of
|
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`.
|
||||||
`-alpha.X`, `-beta.X` and `-rc.X`.
|
|
||||||
|
|
||||||
Pre-releases are not considered "stable" and are usually not recommended for most users.
|
Pre-releases are not considered "stable" and are usually not recommended for most users.
|
||||||
|
|
||||||
|
|
@ -37,9 +32,7 @@ Learn more in the [Pre-releases](../guides/pre-releases.md) guide.
|
||||||
|
|
||||||
### Release Pull Request
|
### Release Pull Request
|
||||||
|
|
||||||
A Release Pull Request is opened by
|
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.
|
||||||
`releaser-pleaser` whenever it finds releasable commits in your project. It proposes a new version number and the Changelog. Once it is merged,
|
|
||||||
`releaser-pleaser` creates a matching release.
|
|
||||||
|
|
||||||
Learn more in the [Release Pull Request](../explanation/release-pr.md) explanation.
|
Learn more in the [Release Pull Request](../explanation/release-pr.md) explanation.
|
||||||
|
|
||||||
|
|
@ -51,11 +44,4 @@ Learn more in the [Release Notes customization](../guides/release-notes.md) guid
|
||||||
|
|
||||||
### Semantic Versioning (SemVer)
|
### Semantic Versioning (SemVer)
|
||||||
|
|
||||||
[Semantic Versioning](https://semver.org/) is a specification for version numbers. It is the only supported versioning schema in
|
[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.
|
||||||
`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.
|
|
||||||
|
|
|
||||||
|
|
@ -30,17 +30,17 @@ Any text in code blocks with these languages is being added to the start or end
|
||||||
|
|
||||||
**Examples**:
|
**Examples**:
|
||||||
|
|
||||||
~~~~rp-prefix
|
```rp-prefix
|
||||||
#### Awesome new feature!
|
#### Awesome new feature!
|
||||||
|
|
||||||
This text is at the start of the release notes.
|
This text is at the start of the release notes.
|
||||||
~~~~
|
```
|
||||||
|
|
||||||
~~~~rp-suffix
|
```rp-suffix
|
||||||
#### Version Compatibility
|
#### Version Compatibility
|
||||||
|
|
||||||
And this at the end.
|
And this at the end.
|
||||||
~~~~
|
```
|
||||||
|
|
||||||
### Status
|
### Status
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
|
||||||
38
go.mod
38
go.mod
|
|
@ -1,41 +1,36 @@
|
||||||
module github.com/apricote/releaser-pleaser
|
module github.com/apricote/releaser-pleaser
|
||||||
|
|
||||||
go 1.24.0
|
go 1.23.2
|
||||||
|
|
||||||
toolchain go1.25.5
|
toolchain go1.24.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.2.0
|
|
||||||
github.com/blang/semver/v4 v4.0.0
|
github.com/blang/semver/v4 v4.0.0
|
||||||
github.com/go-git/go-billy/v5 v5.7.0
|
github.com/go-git/go-billy/v5 v5.6.2
|
||||||
github.com/go-git/go-git/v5 v5.16.4
|
github.com/go-git/go-git/v5 v5.16.2
|
||||||
github.com/google/go-github/v74 v74.0.0
|
github.com/google/go-github/v72 v72.0.0
|
||||||
github.com/leodido/go-conventionalcommits v0.12.0
|
github.com/leodido/go-conventionalcommits v0.12.0
|
||||||
github.com/lmittmann/tint v1.1.2
|
github.com/lmittmann/tint v1.1.2
|
||||||
github.com/spf13/cobra v1.10.2
|
github.com/spf13/cobra v1.9.1
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/teekennedy/goldmark-markdown v0.5.1
|
github.com/teekennedy/goldmark-markdown v0.5.1
|
||||||
github.com/yuin/goldmark v1.7.16
|
github.com/yuin/goldmark v1.7.12
|
||||||
gitlab.com/gitlab-org/api/client-go v0.161.1
|
gitlab.com/gitlab-org/api/client-go v0.130.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.1 // indirect
|
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/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/ProtonMail/go-crypto v1.1.6 // indirect
|
github.com/ProtonMail/go-crypto v1.1.6 // indirect
|
||||||
github.com/cloudflare/circl v1.6.1 // indirect
|
github.com/cloudflare/circl v1.6.1 // indirect
|
||||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.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/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/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.2.0 // 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/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/skeema/knownhosts v1.3.1 // 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
|
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||||
golang.org/x/crypto v0.45.0 // indirect
|
golang.org/x/crypto v0.37.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
golang.org/x/net v0.39.0 // indirect
|
||||||
golang.org/x/net v0.47.0 // indirect
|
|
||||||
golang.org/x/oauth2 v0.30.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
|
golang.org/x/time v0.11.0 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // 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
|
|
||||||
|
|
|
||||||
80
go.sum
80
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 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
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.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||||
|
|
@ -33,33 +27,29 @@ 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/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 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
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 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
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.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
|
||||||
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/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 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-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.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
|
||||||
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/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 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
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.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 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
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/v72 v72.0.0 h1:FcIO37BLoVPBO9igQQ6tStsv2asG4IPcYFi655PPvBM=
|
||||||
github.com/google/go-github/v74 v74.0.0/go.mod h1:ubn/YdyftV80VPSI26nSJvaEsTOnsjrxG3o9kJhcyak=
|
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 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
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.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
|
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||||
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 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||||
|
|
@ -101,62 +91,52 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
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 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
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.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
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/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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
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 h1:2lIlJ3AcIwaD1wFl4dflJSJFMhRTKEsEj+asVsu6M/0=
|
||||||
github.com/teekennedy/goldmark-markdown v0.5.1/go.mod h1:so260mNSPELuRyynZY18719dRYlD+OSnAovqsyrOMOM=
|
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 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
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.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY=
|
||||||
github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
github.com/yuin/goldmark v1.7.12/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.130.1 h1:1xF5C5Zq3sFeNg3PzS2z63oqrxifne3n/OnbI7nptRc=
|
||||||
gitlab.com/gitlab-org/api/client-go v0.161.1/go.mod h1:YqKcnxyV9OPAL5U99mpwBVEgBPz1PK/3qwqq/3h6bao=
|
gitlab.com/gitlab-org/api/client-go v0.130.1/go.mod h1:ZhSxLAWadqP6J9lMh40IAZOlOxBLPRh7yFOXR/bMJWM=
|
||||||
go.abhg.dev/goldmark/toc v0.11.0 h1:IRixVy3/yVPKvFBc37EeBPi8XLTXrtH6BYaonSjkF8o=
|
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.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.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
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 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
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.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
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 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
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-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-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-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-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-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.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
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.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.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||||
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/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
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.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
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.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ package changelog
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/apricote/releaser-pleaser/internal/commitparser"
|
"github.com/apricote/releaser-pleaser/internal/commitparser"
|
||||||
"github.com/apricote/releaser-pleaser/internal/markdown"
|
"github.com/apricote/releaser-pleaser/internal/markdown"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{{define "entry" -}}
|
{{define "entry" -}}
|
||||||
- {{ if .BreakingChange}}**BREAKING**: {{end}}{{ if .Scope }}**{{.Scope}}**: {{end}}{{.Description}}
|
- {{ if .Scope }}**{{.Scope}}**: {{end}}{{.Description}}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{- if not .Formatting.HideVersionTitle }}
|
{{- if not .Formatting.HideVersionTitle }}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import (
|
||||||
|
|
||||||
"github.com/apricote/releaser-pleaser/internal/commitparser"
|
"github.com/apricote/releaser-pleaser/internal/commitparser"
|
||||||
"github.com/apricote/releaser-pleaser/internal/git"
|
"github.com/apricote/releaser-pleaser/internal/git"
|
||||||
"github.com/apricote/releaser-pleaser/internal/testdata"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ptr[T any](input T) *T {
|
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",
|
want: "## [1.0.0](https://example.com/1.0.0)\n\n### Features\n\n- Foobar!\n",
|
||||||
wantErr: assert.NoError,
|
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",
|
name: "single fix",
|
||||||
args: args{
|
args: args{
|
||||||
|
|
@ -144,9 +126,16 @@ func Test_NewChangelogEntry(t *testing.T) {
|
||||||
},
|
},
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
link: "https://example.com/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,
|
wantErr: assert.NoError,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -161,9 +150,18 @@ func Test_NewChangelogEntry(t *testing.T) {
|
||||||
},
|
},
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
link: "https://example.com/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,
|
wantErr: assert.NoError,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"github.com/blang/semver/v4"
|
"github.com/blang/semver/v4"
|
||||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||||
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
"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/forge"
|
||||||
"github.com/apricote/releaser-pleaser/internal/git"
|
"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 {
|
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(
|
ghPR, _, err := g.client.PullRequests.Create(
|
||||||
ctx, g.options.Owner, g.options.Repo,
|
ctx, g.options.Owner, g.options.Repo,
|
||||||
&github.NewPullRequest{
|
&github.NewPullRequest{
|
||||||
|
|
@ -316,6 +309,7 @@ func (g *GitHub) CreatePullRequest(ctx context.Context, pr *releasepr.ReleasePul
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: String ID?
|
||||||
pr.ID = ghPR.GetNumber()
|
pr.ID = ghPR.GetNumber()
|
||||||
|
|
||||||
err = g.SetPullRequestLabels(ctx, pr, []releasepr.Label{}, pr.Labels)
|
err = g.SetPullRequestLabels(ctx, pr, []releasepr.Label{}, pr.Labels)
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,7 @@ func (g *GitLab) prForCommit(ctx context.Context, commit git.Commit) (*git.PullR
|
||||||
var mergeRequest *gitlab.BasicMergeRequest
|
var mergeRequest *gitlab.BasicMergeRequest
|
||||||
for _, mr := range associatedMRs {
|
for _, mr := range associatedMRs {
|
||||||
// We only look for the MR that has this commit set as the "merge/squash commit" => The result of squashing this branch onto main
|
// We only look for the MR that has this commit set as the "merge/squash commit" => The result of squashing this branch onto main
|
||||||
if mr.MergeCommitSHA == commit.Hash || mr.SquashCommitSHA == commit.Hash || mr.SHA == commit.Hash {
|
if mr.MergeCommitSHA == commit.Hash || mr.SquashCommitSHA == commit.Hash {
|
||||||
mergeRequest = mr
|
mergeRequest = mr
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -403,15 +403,12 @@ func gitlabMRToReleasePullRequest(pr *gitlab.BasicMergeRequest) *releasepr.Relea
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit SHA is saved in either [MergeCommitSHA], [SquashCommitSHA] or [SHA] depending on which merge method was used.
|
// Commit SHA is saved in either [MergeCommitSHA] or [SquashCommitSHA] depending on which merge method was used.
|
||||||
var releaseCommit *git.Commit
|
var releaseCommit *git.Commit
|
||||||
switch {
|
if pr.MergeCommitSHA != "" {
|
||||||
case pr.MergeCommitSHA != "":
|
|
||||||
releaseCommit = &git.Commit{Hash: pr.MergeCommitSHA}
|
releaseCommit = &git.Commit{Hash: pr.MergeCommitSHA}
|
||||||
case pr.SquashCommitSHA != "":
|
} else if pr.SquashCommitSHA != "" {
|
||||||
releaseCommit = &git.Commit{Hash: pr.SquashCommitSHA}
|
releaseCommit = &git.Commit{Hash: pr.SquashCommitSHA}
|
||||||
case pr.MergedAt != nil && pr.SHA != "":
|
|
||||||
releaseCommit = &git.Commit{Hash: pr.SHA}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &releasepr.ReleasePullRequest{
|
return &releasepr.ReleasePullRequest{
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ import (
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
"github.com/go-git/go-git/v5/plumbing/object"
|
"github.com/go-git/go-git/v5/plumbing/object"
|
||||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||||
|
|
||||||
|
"github.com/apricote/releaser-pleaser/internal/updater"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -117,7 +119,7 @@ func (r *Repository) Checkout(_ context.Context, branch string) error {
|
||||||
return nil
|
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()
|
worktree, err := r.r.Worktree()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -139,9 +141,13 @@ func (r *Repository) UpdateFile(_ context.Context, path string, create bool, upd
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedContent, err := updateHook(string(content))
|
updatedContent := string(content)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to run update hook on file %s", path)
|
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)
|
err = file.Truncate(0)
|
||||||
|
|
|
||||||
|
|
@ -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))
|
|
||||||
}
|
|
||||||
|
|
@ -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.
|
This will be added to the start of the release notes.
|
||||||
|
|
||||||
~~~~rp-prefix
|
```rp-prefix
|
||||||
{{- if .Overrides.Prefix }}
|
{{- if .Overrides.Prefix }}
|
||||||
{{ .Overrides.Prefix }}{{ end }}
|
{{ .Overrides.Prefix }}{{ end }}
|
||||||
~~~~
|
```
|
||||||
|
|
||||||
### Suffix / End
|
### Suffix / End
|
||||||
|
|
||||||
This will be added to the end of the release notes.
|
This will be added to the end of the release notes.
|
||||||
|
|
||||||
~~~~rp-suffix
|
```rp-suffix
|
||||||
{{- if .Overrides.Suffix }}
|
{{- if .Overrides.Suffix }}
|
||||||
{{ .Overrides.Suffix }}{{ end }}
|
{{ .Overrides.Suffix }}{{ end }}
|
||||||
~~~~
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/apricote/releaser-pleaser/internal/git"
|
"github.com/apricote/releaser-pleaser/internal/git"
|
||||||
"github.com/apricote/releaser-pleaser/internal/testdata"
|
|
||||||
"github.com/apricote/releaser-pleaser/internal/versioning"
|
"github.com/apricote/releaser-pleaser/internal/versioning"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -38,24 +37,20 @@ func TestReleasePullRequest_GetOverrides(t *testing.T) {
|
||||||
name: "prefix in description",
|
name: "prefix in description",
|
||||||
pr: ReleasePullRequest{
|
pr: ReleasePullRequest{
|
||||||
PullRequest: git.PullRequest{
|
PullRequest: git.PullRequest{
|
||||||
Description: testdata.MustReadFileString(t, "description-prefix.txt"),
|
Description: "```rp-prefix\n## Foo\n\n- Cool thing\n```",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: ReleaseOverrides{
|
want: ReleaseOverrides{Prefix: "## Foo\n\n- Cool thing"},
|
||||||
Prefix: testdata.MustReadFileString(t, "prefix.txt"),
|
|
||||||
},
|
|
||||||
wantErr: assert.NoError,
|
wantErr: assert.NoError,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "suffix in description",
|
name: "suffix in description",
|
||||||
pr: ReleasePullRequest{
|
pr: ReleasePullRequest{
|
||||||
PullRequest: git.PullRequest{
|
PullRequest: git.PullRequest{
|
||||||
Description: testdata.MustReadFileString(t, "description-suffix.txt"),
|
Description: "```rp-suffix\n## Compatibility\n\nNo compatibility guarantees.\n```",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: ReleaseOverrides{
|
want: ReleaseOverrides{Suffix: "## Compatibility\n\nNo compatibility guarantees."},
|
||||||
Suffix: testdata.MustReadFileString(t, "suffix.txt"),
|
|
||||||
},
|
|
||||||
wantErr: assert.NoError,
|
wantErr: assert.NoError,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -85,10 +80,30 @@ func TestReleasePullRequest_ChangelogText(t *testing.T) {
|
||||||
wantErr: assert.NoError,
|
wantErr: assert.NoError,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with section",
|
name: "with section",
|
||||||
description: testdata.MustReadFileString(t, "changelog.txt"),
|
description: `# Foobar
|
||||||
want: testdata.MustReadFileString(t, "changelog-content.txt"),
|
|
||||||
wantErr: assert.NoError,
|
<!-- section-start changelog -->
|
||||||
|
This is the changelog
|
||||||
|
|
||||||
|
## Awesome
|
||||||
|
|
||||||
|
### New
|
||||||
|
|
||||||
|
#### Changes
|
||||||
|
<!-- section-end changelog -->
|
||||||
|
|
||||||
|
Suffix Things
|
||||||
|
`,
|
||||||
|
want: `This is the changelog
|
||||||
|
|
||||||
|
## Awesome
|
||||||
|
|
||||||
|
### New
|
||||||
|
|
||||||
|
#### Changes
|
||||||
|
`,
|
||||||
|
wantErr: assert.NoError,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
@ -163,17 +178,75 @@ func TestReleasePullRequest_SetDescription(t *testing.T) {
|
||||||
name: "no overrides",
|
name: "no overrides",
|
||||||
changelogEntry: `## v1.0.0`,
|
changelogEntry: `## v1.0.0`,
|
||||||
overrides: ReleaseOverrides{},
|
overrides: ReleaseOverrides{},
|
||||||
want: testdata.MustReadFileString(t, "description-no-overrides.txt"),
|
want: `<!-- section-start changelog -->
|
||||||
wantErr: assert.NoError,
|
## v1.0.0
|
||||||
|
<!-- section-end changelog -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>PR by <a href="https://github.com/apricote/releaser-pleaser">releaser-pleaser</a> 🤖</h4></summary>
|
||||||
|
|
||||||
|
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
|
||||||
|
` + "```" + `
|
||||||
|
|
||||||
|
</details>
|
||||||
|
`,
|
||||||
|
wantErr: assert.NoError,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "existing overrides",
|
name: "existing overrides",
|
||||||
changelogEntry: `## v1.0.0`,
|
changelogEntry: `## v1.0.0`,
|
||||||
overrides: ReleaseOverrides{
|
overrides: ReleaseOverrides{
|
||||||
Prefix: testdata.MustReadFileString(t, "prefix.txt"),
|
Prefix: "This release is awesome!",
|
||||||
Suffix: testdata.MustReadFileString(t, "suffix.txt"),
|
Suffix: "Fooo",
|
||||||
},
|
},
|
||||||
want: testdata.MustReadFileString(t, "description-overrides.txt"),
|
want: `<!-- section-start changelog -->
|
||||||
|
## v1.0.0
|
||||||
|
<!-- section-end changelog -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h4>PR by <a href="https://github.com/apricote/releaser-pleaser">releaser-pleaser</a> 🤖</h4></summary>
|
||||||
|
|
||||||
|
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
|
||||||
|
` + "```" + `
|
||||||
|
|
||||||
|
</details>
|
||||||
|
`,
|
||||||
wantErr: assert.NoError,
|
wantErr: assert.NoError,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
7
internal/testdata/changelog-content.txt
vendored
7
internal/testdata/changelog-content.txt
vendored
|
|
@ -1,7 +0,0 @@
|
||||||
This is the changelog
|
|
||||||
|
|
||||||
## Awesome
|
|
||||||
|
|
||||||
### New
|
|
||||||
|
|
||||||
#### Changes
|
|
||||||
19
internal/testdata/changelog-entry-prefix.txt
vendored
19
internal/testdata/changelog-entry-prefix.txt
vendored
|
|
@ -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!
|
|
||||||
9
internal/testdata/changelog-entry-suffix.txt
vendored
9
internal/testdata/changelog-entry-suffix.txt
vendored
|
|
@ -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.
|
|
||||||
13
internal/testdata/changelog.txt
vendored
13
internal/testdata/changelog.txt
vendored
|
|
@ -1,13 +0,0 @@
|
||||||
# Foobar
|
|
||||||
|
|
||||||
<!-- section-start changelog -->
|
|
||||||
This is the changelog
|
|
||||||
|
|
||||||
## Awesome
|
|
||||||
|
|
||||||
### New
|
|
||||||
|
|
||||||
#### Changes
|
|
||||||
<!-- section-end changelog -->
|
|
||||||
|
|
||||||
Suffix Things
|
|
||||||
28
internal/testdata/description-no-overrides.txt
vendored
28
internal/testdata/description-no-overrides.txt
vendored
|
|
@ -1,28 +0,0 @@
|
||||||
<!-- section-start changelog -->
|
|
||||||
## v1.0.0
|
|
||||||
<!-- section-end changelog -->
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><h4>PR by <a href="https://github.com/apricote/releaser-pleaser">releaser-pleaser</a> 🤖</h4></summary>
|
|
||||||
|
|
||||||
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
|
|
||||||
~~~~
|
|
||||||
|
|
||||||
</details>
|
|
||||||
44
internal/testdata/description-overrides.txt
vendored
44
internal/testdata/description-overrides.txt
vendored
|
|
@ -1,44 +0,0 @@
|
||||||
<!-- section-start changelog -->
|
|
||||||
## v1.0.0
|
|
||||||
<!-- section-end changelog -->
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><h4>PR by <a href="https://github.com/apricote/releaser-pleaser">releaser-pleaser</a> 🤖</h4></summary>
|
|
||||||
|
|
||||||
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.
|
|
||||||
~~~~
|
|
||||||
|
|
||||||
</details>
|
|
||||||
41
internal/testdata/description-prefix.txt
vendored
41
internal/testdata/description-prefix.txt
vendored
|
|
@ -1,41 +0,0 @@
|
||||||
<!-- section-start changelog -->
|
|
||||||
## v1.0.0
|
|
||||||
<!-- section-end changelog -->
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><h4>PR by <a href="https://github.com/apricote/releaser-pleaser">releaser-pleaser</a> 🤖</h4></summary>
|
|
||||||
|
|
||||||
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
|
|
||||||
~~~~
|
|
||||||
|
|
||||||
</details>
|
|
||||||
31
internal/testdata/description-suffix.txt
vendored
31
internal/testdata/description-suffix.txt
vendored
|
|
@ -1,31 +0,0 @@
|
||||||
<!-- section-start changelog -->
|
|
||||||
## v1.0.0
|
|
||||||
<!-- section-end changelog -->
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><h4>PR by <a href="https://github.com/apricote/releaser-pleaser">releaser-pleaser</a> 🤖</h4></summary>
|
|
||||||
|
|
||||||
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.
|
|
||||||
~~~~
|
|
||||||
|
|
||||||
</details>
|
|
||||||
13
internal/testdata/prefix.txt
vendored
13
internal/testdata/prefix.txt
vendored
|
|
@ -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
|
|
||||||
}
|
|
||||||
```
|
|
||||||
3
internal/testdata/suffix.txt
vendored
3
internal/testdata/suffix.txt
vendored
|
|
@ -1,3 +0,0 @@
|
||||||
## Compatibility
|
|
||||||
|
|
||||||
This version is compatible with flux-compensator v2.2 - v2.9.
|
|
||||||
19
internal/testdata/testdata.go
vendored
19
internal/testdata/testdata.go
vendored
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
@ -14,22 +14,7 @@ var (
|
||||||
ChangelogUpdaterHeaderRegex = regexp.MustCompile(`^# Changelog\n`)
|
ChangelogUpdaterHeaderRegex = regexp.MustCompile(`^# Changelog\n`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func Changelog() Updater {
|
func Changelog(info ReleaseInfo) 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) {
|
|
||||||
return func(content string) (string, error) {
|
return func(content string) (string, error) {
|
||||||
headerIndex := ChangelogUpdaterHeaderRegex.FindStringIndex(content)
|
headerIndex := ChangelogUpdaterHeaderRegex.FindStringIndex(content)
|
||||||
if headerIndex == nil && len(content) != 0 {
|
if headerIndex == nil && len(content) != 0 {
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,7 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestChangelogUpdater_Files(t *testing.T) {
|
func TestChangelogUpdater_UpdateContent(t *testing.T) {
|
||||||
assert.Equal(t, []string{"CHANGELOG.md"}, Changelog().Files())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChangelogUpdater_CreateNewFiles(t *testing.T) {
|
|
||||||
assert.True(t, Changelog().CreateNewFiles())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChangelogUpdater_Update(t *testing.T) {
|
|
||||||
tests := []updaterTestCase{
|
tests := []updaterTestCase{
|
||||||
{
|
{
|
||||||
name: "empty file",
|
name: "empty file",
|
||||||
|
|
@ -62,7 +54,7 @@ func TestChangelogUpdater_Update(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
runUpdaterTest(t, Changelog(), tt)
|
runUpdaterTest(t, Changelog, tt)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,25 +7,7 @@ import (
|
||||||
|
|
||||||
var GenericUpdaterSemVerRegex = regexp.MustCompile(`\d+\.\d+\.\d+(-[\w.]+)?(.*x-releaser-pleaser-version)`)
|
var GenericUpdaterSemVerRegex = regexp.MustCompile(`\d+\.\d+\.\d+(-[\w.]+)?(.*x-releaser-pleaser-version)`)
|
||||||
|
|
||||||
func Generic(files []string) Updater {
|
func Generic(info ReleaseInfo) 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) {
|
|
||||||
return func(content string) (string, error) {
|
return func(content string) (string, error) {
|
||||||
// We strip the "v" prefix to avoid adding/removing it from the users input.
|
// We strip the "v" prefix to avoid adding/removing it from the users input.
|
||||||
version := strings.TrimPrefix(info.Version, "v")
|
version := strings.TrimPrefix(info.Version, "v")
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,7 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenericUpdater_Files(t *testing.T) {
|
func TestGenericUpdater_UpdateContent(t *testing.T) {
|
||||||
assert.Equal(t, []string{"foo.bar", "version.txt"}, Generic([]string{"foo.bar", "version.txt"}).Files())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGenericUpdater_CreateNewFiles(t *testing.T) {
|
|
||||||
assert.False(t, Generic([]string{}).CreateNewFiles())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGenericUpdater_Update(t *testing.T) {
|
|
||||||
tests := []updaterTestCase{
|
tests := []updaterTestCase{
|
||||||
{
|
{
|
||||||
name: "single line",
|
name: "single line",
|
||||||
|
|
@ -55,7 +47,7 @@ func TestGenericUpdater_Update(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
runUpdaterTest(t, Generic([]string{"version.txt"}), tt)
|
runUpdaterTest(t, Generic, tt)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,11 +5,7 @@ type ReleaseInfo struct {
|
||||||
ChangelogEntry string
|
ChangelogEntry string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Updater interface {
|
type Updater func(string) (string, error)
|
||||||
Files() []string
|
|
||||||
CreateNewFiles() bool
|
|
||||||
Update(info ReleaseInfo) func(content string) (string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type NewUpdater func(ReleaseInfo) Updater
|
type NewUpdater func(ReleaseInfo) Updater
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@ type updaterTestCase struct {
|
||||||
wantErr assert.ErrorAssertionFunc
|
wantErr assert.ErrorAssertionFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func runUpdaterTest(t *testing.T, u Updater, tt updaterTestCase) {
|
func runUpdaterTest(t *testing.T, constructor NewUpdater, tt updaterTestCase) {
|
||||||
t.Helper()
|
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)) {
|
if !tt.wantErr(t, err, fmt.Sprintf("Updater(%v, %v)", tt.content, tt.info)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
mise.toml
25
mise.toml
|
|
@ -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"
|
|
||||||
|
|
@ -34,10 +34,10 @@ type ReleaserPleaser struct {
|
||||||
commitParser commitparser.CommitParser
|
commitParser commitparser.CommitParser
|
||||||
versioning versioning.Strategy
|
versioning versioning.Strategy
|
||||||
extraFiles []string
|
extraFiles []string
|
||||||
updaters []updater.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{
|
return &ReleaserPleaser{
|
||||||
forge: forge,
|
forge: forge,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
|
@ -281,12 +281,16 @@ func (rp *ReleaserPleaser) runReconcileReleasePR(ctx context.Context) error {
|
||||||
// Info for updaters
|
// Info for updaters
|
||||||
info := updater.ReleaseInfo{Version: nextVersion, ChangelogEntry: changelogEntry}
|
info := updater.ReleaseInfo{Version: nextVersion, ChangelogEntry: changelogEntry}
|
||||||
|
|
||||||
for _, u := range rp.updaters {
|
err = repo.UpdateFile(ctx, updater.ChangelogFile, true, updater.WithInfo(info, updater.Changelog))
|
||||||
for _, file := range u.Files() {
|
if err != nil {
|
||||||
err = repo.UpdateFile(ctx, file, u.CreateNewFiles(), u.Update(info))
|
return fmt.Errorf("failed to update changelog file: %w", err)
|
||||||
if err != nil {
|
}
|
||||||
return fmt.Errorf("failed to run updater %T: %w", u, err)
|
|
||||||
}
|
for _, path := range rp.extraFiles {
|
||||||
|
// TODO: Check for missing files
|
||||||
|
err = repo.UpdateFile(ctx, path, false, updater.WithInfo(info, rp.updaters...))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to run file updater: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,7 @@ spec:
|
||||||
description: "GitLab token for creating and updating release MRs."
|
description: "GitLab token for creating and updating release MRs."
|
||||||
|
|
||||||
extra-files:
|
extra-files:
|
||||||
description: 'List of files that are scanned for version references by the generic updater.'
|
description: 'List of files that are scanned for version references.'
|
||||||
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"
|
|
||||||
default: ""
|
default: ""
|
||||||
|
|
||||||
stage:
|
stage:
|
||||||
|
|
@ -44,7 +40,7 @@ releaser-pleaser:
|
||||||
resource_group: releaser-pleaser
|
resource_group: releaser-pleaser
|
||||||
|
|
||||||
image:
|
image:
|
||||||
name: ghcr.io/apricote/releaser-pleaser:v0.7.1 # x-releaser-pleaser-version
|
name: ghcr.io/apricote/releaser-pleaser:v0.6.0 # x-releaser-pleaser-version
|
||||||
entrypoint: [ "" ]
|
entrypoint: [ "" ]
|
||||||
variables:
|
variables:
|
||||||
GITLAB_TOKEN: $[[ inputs.token ]]
|
GITLAB_TOKEN: $[[ inputs.token ]]
|
||||||
|
|
@ -53,5 +49,4 @@ releaser-pleaser:
|
||||||
rp run \
|
rp run \
|
||||||
--forge=gitlab \
|
--forge=gitlab \
|
||||||
--branch=$[[ inputs.branch ]] \
|
--branch=$[[ inputs.branch ]] \
|
||||||
--extra-files="$[[ inputs.extra-files ]]" \
|
--extra-files="$[[ inputs.extra-files ]]"
|
||||||
--updaters="$[[ inputs.updaters ]]"
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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:
|
|
||||||
|
|
@ -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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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{}))
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue