Adding upstream version 0.8.13.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
c0c4addb85
commit
6819b9812e
28 changed files with 556 additions and 76 deletions
|
@ -14,7 +14,7 @@ jobs:
|
|||
docker:
|
||||
# Specify the version you desire here
|
||||
# See: https://circleci.com/developer/images/image/cimg/go
|
||||
- image: cimg/go:1.24.2@sha256:cd027ede83e11c7b1002dfff3f4975fbf0124c5028df4c63da571c30db88fb3c
|
||||
- image: cimg/go:1.24.3@sha256:5f7cdf218958c02c0da1356a3a2a8d1394c80206322d0790b968443f6875a59e
|
||||
|
||||
# Add steps to the job
|
||||
# See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps
|
||||
|
|
4
.github/workflows/build.yaml
vendored
4
.github/workflows/build.yaml
vendored
|
@ -33,9 +33,9 @@ jobs:
|
|||
platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v6
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@29694d72cd5e7ef3b09496b39f28a942af47737e
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5
|
||||
with:
|
||||
go-version: 1.24.3
|
||||
go-version: 1.24.x
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@6d4b68b490aef8836e8fb5e50ee7b3bdfa5894f0
|
||||
|
|
9
.github/workflows/docs.yaml
vendored
9
.github/workflows/docs.yaml
vendored
|
@ -7,6 +7,9 @@ permissions:
|
|||
contents: write
|
||||
actions: read
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.24.x
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -20,9 +23,9 @@ jobs:
|
|||
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@29694d72cd5e7ef3b09496b39f28a942af47737e
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Generate Service Config Docs
|
||||
run: |
|
||||
|
@ -31,7 +34,7 @@ jobs:
|
|||
./generate-service-config-docs.sh
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
|
||||
uses: actions/setup-python@5db1cf9a59fb97c40a68accab29236f0da7e94db
|
||||
with:
|
||||
python-version: "3.13.3"
|
||||
cache: "pip"
|
||||
|
|
4
.github/workflows/lint.yaml
vendored
4
.github/workflows/lint.yaml
vendored
|
@ -15,7 +15,7 @@ jobs:
|
|||
uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@29694d72cd5e7ef3b09496b39f28a942af47737e
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5
|
||||
with:
|
||||
go-version: "1.24.3"
|
||||
|
||||
|
@ -23,7 +23,7 @@ jobs:
|
|||
run: go mod download
|
||||
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@4d56fa9e3c67fb4afa92b38c99fc7f20f5eeff4e
|
||||
uses: golangci/golangci-lint-action@481777f62fe06de6923fd3a69efd3ba597fe628a
|
||||
with:
|
||||
args: --timeout=5m --config= # Use default linter settings
|
||||
|
||||
|
|
5
.github/workflows/pull-request.yaml
vendored
5
.github/workflows/pull-request.yaml
vendored
|
@ -3,8 +3,9 @@ name: Pull Request
|
|||
on:
|
||||
workflow_dispatch: {}
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- "docs/*"
|
||||
- ".github/*"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
|
2
.github/workflows/release-dev.yaml
vendored
2
.github/workflows/release-dev.yaml
vendored
|
@ -8,6 +8,7 @@ on:
|
|||
- "v*"
|
||||
paths-ignore:
|
||||
- "docs/*"
|
||||
- ".github/*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
@ -27,6 +28,7 @@ jobs:
|
|||
uses: ./.github/workflows/build.yaml
|
||||
secrets: inherit
|
||||
needs:
|
||||
- lint
|
||||
- test
|
||||
with:
|
||||
snapshot: true
|
||||
|
|
15
.github/workflows/release-production.yaml
vendored
15
.github/workflows/release-production.yaml
vendored
|
@ -23,15 +23,10 @@ jobs:
|
|||
uses: ./.github/workflows/build.yaml
|
||||
secrets: inherit
|
||||
needs:
|
||||
- lint
|
||||
- test
|
||||
|
||||
renew-docs:
|
||||
name: Refresh pkg.go.dev
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Pull new module version
|
||||
uses: nicholas-fedor/go-proxy-pull-action@ad5d0f8b44e5478055cf78227eb300d2b02786f2
|
||||
with:
|
||||
goproxy: https://proxy.golang.org
|
||||
import_path: github.com/nicholas-fedor/shoutrrr
|
||||
update-go-docs:
|
||||
uses: ./.github/workflows/update-go-docs.yaml
|
||||
needs:
|
||||
- build
|
||||
|
|
4
.github/workflows/test.yaml
vendored
4
.github/workflows/test.yaml
vendored
|
@ -15,7 +15,7 @@ jobs:
|
|||
uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@29694d72cd5e7ef3b09496b39f28a942af47737e
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5
|
||||
with:
|
||||
go-version: "1.24.3"
|
||||
|
||||
|
@ -27,6 +27,6 @@ jobs:
|
|||
go test -v -coverprofile coverage.out -covermode atomic ./...
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d
|
||||
uses: codecov/codecov-action@15559ed290fa727036809b67ab0f646ffa6c5158
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
|
19
.github/workflows/update-go-docs.yaml
vendored
Normal file
19
.github/workflows/update-go-docs.yaml
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
name: Update pkg.go.dev
|
||||
|
||||
on:
|
||||
- workflow_dispatch
|
||||
- workflow_call
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update-go-docs:
|
||||
name: Update pkg.go.dev
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Pull new module version
|
||||
uses: nicholas-fedor/go-proxy-pull-action@ad5d0f8b44e5478055cf78227eb300d2b02786f2
|
||||
with:
|
||||
goproxy: https://proxy.golang.org
|
||||
import_path: github.com/nicholas-fedor/shoutrrr
|
8
build.sh
8
build.sh
|
@ -1,3 +1,9 @@
|
|||
#!/bin/sh
|
||||
|
||||
go build -o shoutrrr/ ./shoutrrr
|
||||
# Get Git information
|
||||
VERSION=$(git describe --tags --always --dirty 2>/dev/null || echo "unknown")
|
||||
COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
||||
DATE=$(git log -1 --format=%cd --date=iso | date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "unknown")
|
||||
|
||||
# Build with ldflags
|
||||
go build -ldflags "-s -w -X github.com/nicholas-fedor/shoutrrr/internal/meta.Version=$VERSION -X github.com/nicholas-fedor/shoutrrr/internal/meta.Commit=$COMMIT -X github.com/nicholas-fedor/shoutrrr/internal/meta.Date=$DATE" -o shoutrrr ./shoutrrr
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
The basic generator looks at the `key:""`, `desc:""` and `default:""` tags on service configuration structs and uses them to ask the user to fill in their corresponding values.
|
||||
|
||||
Example:
|
||||
```shell
|
||||
$ shoutrrr generate telegram
|
||||
|
||||
```bash
|
||||
shoutrrr generate telegram
|
||||
```
|
||||
|
||||
```yaml
|
||||
Generating URL for telegram using basic generator
|
||||
Enter the configuration values as prompted
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
# Generators
|
||||
|
||||
Generators are used to create service configurations via the command line.
|
||||
Generators are used to create service configurations via the command line.
|
||||
The main generator is the reflection based [Basic generator](./basic) that aims to be able to generator configurations for all the core services via a set of simple questions.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
$ shoutrrr generate [OPTIONS] -g <GENERATOR> <SERVICE>
|
||||
```
|
||||
shoutrrr generate [OPTIONS] -g <GENERATOR> <SERVICE>
|
||||
```
|
||||
|
|
10
go.mod
10
go.mod
|
@ -1,6 +1,6 @@
|
|||
module github.com/nicholas-fedor/shoutrrr
|
||||
|
||||
go 1.24.2
|
||||
go 1.24.3
|
||||
|
||||
require (
|
||||
github.com/fatih/color v1.18.0
|
||||
|
@ -14,7 +14,7 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.7.0 // indirect
|
||||
golang.org/x/net v0.40.0
|
||||
)
|
||||
|
||||
|
@ -24,21 +24,21 @@ require (
|
|||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
|
||||
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/sagikazarmark/locafero v0.9.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.14.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/cast v1.8.0 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/tools v0.32.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
24
go.sum
24
go.sum
|
@ -1,5 +1,5 @@
|
|||
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||
cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU=
|
||||
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
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=
|
||||
|
@ -17,8 +17,8 @@ github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIx
|
|||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
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/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4=
|
||||
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jarcoal/httpmock v1.4.0 h1:BvhqnH0JAYbNudL2GMJKgOHe2CtKlzJ/5rWKyp+hc2k=
|
||||
|
@ -52,8 +52,8 @@ github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9yS
|
|||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
|
||||
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk=
|
||||
github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
|
@ -68,25 +68,17 @@ go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
|||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
|
@ -12,7 +12,10 @@ builds:
|
|||
- arm
|
||||
- arm64
|
||||
ldflags:
|
||||
- -s -w -X github.com/nicholas-fedor/shoutrrr/internal/meta.Version={{ .Version }}
|
||||
- -s -w
|
||||
- -X github.com/nicholas-fedor/shoutrrr/internal/meta.Version={{ .Version }}
|
||||
- -X github.com/nicholas-fedor/shoutrrr/internal/meta.Commit={{.Commit}}
|
||||
- -X github.com/nicholas-fedor/shoutrrr/internal/meta.Date={{.Date}}
|
||||
|
||||
archives:
|
||||
- id: default # Unique ID for this archive configuration
|
||||
|
|
3
internal/meta/doc.go
Normal file
3
internal/meta/doc.go
Normal file
|
@ -0,0 +1,3 @@
|
|||
// Package meta provides functionality to parse and manage metadata information
|
||||
// for Shoutrrr using Go's debug.ReadBuildInfo and GoReleaser build flags.
|
||||
package meta
|
161
internal/meta/meta.go
Normal file
161
internal/meta/meta.go
Normal file
|
@ -0,0 +1,161 @@
|
|||
package meta
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Constants for repeated string values.
|
||||
const (
|
||||
devVersion = "dev"
|
||||
unknownValue = "unknown"
|
||||
trueValue = "true"
|
||||
commitSHALength = 7 // Length to shorten Git commit SHA
|
||||
)
|
||||
|
||||
// These values are populated by GoReleaser during release builds.
|
||||
var (
|
||||
// Version is the Shoutrrr version (e.g., "v0.0.1").
|
||||
Version = devVersion
|
||||
// Commit is the Git commit SHA (e.g., "abc123").
|
||||
Commit = unknownValue
|
||||
// Date is the build or commit timestamp in RFC3339 format (e.g., "2025-05-07T00:00:00Z").
|
||||
Date = unknownValue
|
||||
)
|
||||
|
||||
// Info holds version information for Shoutrrr.
|
||||
type Info struct {
|
||||
Version string
|
||||
Commit string
|
||||
Date string
|
||||
}
|
||||
|
||||
// GetMetaStr returns the formatted version string, including commit info only if available.
|
||||
func GetMetaStr() string {
|
||||
version := GetVersion()
|
||||
date := GetDate()
|
||||
commit := GetCommit()
|
||||
|
||||
if commit == unknownValue {
|
||||
return fmt.Sprintf("%s (Built on %s)", version, date)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s (Built on %s from Git SHA %s)", version, date, commit)
|
||||
}
|
||||
|
||||
// GetVersion returns the version string, using debug.ReadBuildInfo for source builds
|
||||
// or GoReleaser variables for release builds.
|
||||
func GetVersion() string {
|
||||
version := Version
|
||||
|
||||
// If building from source (not GoReleaser), try to get version from debug.ReadBuildInfo
|
||||
if version == devVersion || version == "" {
|
||||
if info, ok := debug.ReadBuildInfo(); ok {
|
||||
// Get the module version (e.g., v1.1.4 or v1.1.4+dirty)
|
||||
version = info.Main.Version
|
||||
if version == "(devel)" || version == "" {
|
||||
version = devVersion
|
||||
}
|
||||
// Check for dirty state
|
||||
for _, setting := range info.Settings {
|
||||
if setting.Key == "vcs.modified" && setting.Value == trueValue &&
|
||||
version != unknownValue && !contains(version, "+dirty") {
|
||||
version += "+dirty"
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// GoReleaser provides a valid version without 'v' prefix, so add it
|
||||
if version != "" && version != "v" {
|
||||
version = "v" + version
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback default if still unset or invalid
|
||||
if version == "" || version == devVersion || version == "v" {
|
||||
return unknownValue
|
||||
}
|
||||
|
||||
return version
|
||||
}
|
||||
|
||||
// GetCommit returns the commit SHA, using debug.ReadBuildInfo for source builds
|
||||
// or GoReleaser variables for release builds.
|
||||
func GetCommit() string {
|
||||
// Return Commit if set by GoReleaser (non-empty and not "unknown")
|
||||
if Commit != unknownValue && Commit != "" {
|
||||
if len(Commit) >= commitSHALength {
|
||||
return Commit[:commitSHALength]
|
||||
}
|
||||
|
||||
return Commit
|
||||
}
|
||||
|
||||
// Try to get commit from debug.ReadBuildInfo for source builds
|
||||
if info, ok := debug.ReadBuildInfo(); ok {
|
||||
for _, setting := range info.Settings {
|
||||
if setting.Key == "vcs.revision" && setting.Value != "" {
|
||||
if len(setting.Value) >= commitSHALength {
|
||||
return setting.Value[:commitSHALength]
|
||||
}
|
||||
|
||||
return setting.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to unknown if no commit is found
|
||||
return unknownValue
|
||||
}
|
||||
|
||||
// GetDate returns the build or commit date, using debug.ReadBuildInfo for source builds
|
||||
// or GoReleaser variables for release builds.
|
||||
func GetDate() string {
|
||||
date := Date
|
||||
|
||||
// If building from source (not GoReleaser), try to get date from debug.ReadBuildInfo
|
||||
if date == unknownValue || date == "" {
|
||||
if info, ok := debug.ReadBuildInfo(); ok {
|
||||
for _, setting := range info.Settings {
|
||||
if setting.Key == "vcs.time" {
|
||||
if t, err := time.Parse(time.RFC3339, setting.Value); err == nil {
|
||||
return t.Format("2006-01-02") // Shorten to YYYY-MM-DD
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fallback to current date if no VCS time is available
|
||||
return time.Now().UTC().Format("2006-01-02")
|
||||
}
|
||||
|
||||
// Shorten date if provided by GoReleaser
|
||||
if date != "" && date != unknownValue {
|
||||
if t, err := time.Parse(time.RFC3339, date); err == nil {
|
||||
return t.Format("2006-01-02") // Shorten to YYYY-MM-DD
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to current date if date is invalid
|
||||
return time.Now().UTC().Format("2006-01-02")
|
||||
}
|
||||
|
||||
// GetMetaInfo returns version information by combining GetVersion, GetCommit, and GetDate.
|
||||
func GetMetaInfo() Info {
|
||||
return Info{
|
||||
Version: GetVersion(),
|
||||
Commit: GetCommit(),
|
||||
Date: GetDate(),
|
||||
}
|
||||
}
|
||||
|
||||
// contains checks if a string contains a substring.
|
||||
func contains(s, substr string) bool {
|
||||
for i := 0; i <= len(s)-len(substr); i++ {
|
||||
if s[i:i+len(substr)] == substr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
282
internal/meta/meta_test.go
Normal file
282
internal/meta/meta_test.go
Normal file
|
@ -0,0 +1,282 @@
|
|||
package meta
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGetVersionInfo(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setVars func()
|
||||
expect Info
|
||||
partialMatch bool
|
||||
}{
|
||||
{
|
||||
name: "GoReleaser build",
|
||||
setVars: func() {
|
||||
Version = "0.0.1"
|
||||
Commit = "abc123456789"
|
||||
Date = "2025-05-07T00:00:00Z"
|
||||
},
|
||||
expect: Info{
|
||||
Version: "v0.0.1",
|
||||
Commit: "abc1234",
|
||||
Date: "2025-05-07",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Source build with default values",
|
||||
setVars: func() {
|
||||
Version = devVersion
|
||||
Commit = unknownValue
|
||||
Date = unknownValue
|
||||
},
|
||||
expect: Info{
|
||||
Version: unknownValue,
|
||||
Commit: unknownValue,
|
||||
Date: time.Now().UTC().Format("2006-01-02"),
|
||||
},
|
||||
partialMatch: true,
|
||||
},
|
||||
{
|
||||
name: "Source build with empty values",
|
||||
setVars: func() {
|
||||
Version = ""
|
||||
Commit = ""
|
||||
Date = ""
|
||||
},
|
||||
expect: Info{
|
||||
Version: unknownValue,
|
||||
Commit: unknownValue,
|
||||
Date: time.Now().UTC().Format("2006-01-02"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Invalid GoReleaser version",
|
||||
setVars: func() {
|
||||
Version = "v"
|
||||
Commit = ""
|
||||
Date = ""
|
||||
},
|
||||
expect: Info{
|
||||
Version: unknownValue,
|
||||
Commit: unknownValue,
|
||||
Date: time.Now().UTC().Format("2006-01-02"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.setVars()
|
||||
|
||||
info := GetMetaInfo()
|
||||
|
||||
if !tt.partialMatch {
|
||||
if info.Version != tt.expect.Version {
|
||||
t.Errorf("Version = %q, want %q", info.Version, tt.expect.Version)
|
||||
}
|
||||
|
||||
if info.Commit != tt.expect.Commit {
|
||||
t.Errorf("Commit = %q, want %q", info.Commit, tt.expect.Commit)
|
||||
}
|
||||
|
||||
// Validate Date format (YYYY-MM-DD) instead of exact match
|
||||
if !regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`).MatchString(info.Date) {
|
||||
t.Errorf("Date = %q, want valid YYYY-MM-DD format", info.Date)
|
||||
}
|
||||
} else if info.Version != tt.expect.Version && !strings.Contains(info.Version, "+dirty") {
|
||||
t.Errorf("Version = %q, want %q or dirty variant", info.Version, tt.expect.Version)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetVersionInfo_VCSData(t *testing.T) {
|
||||
Version = devVersion
|
||||
Commit = unknownValue
|
||||
Date = unknownValue
|
||||
|
||||
info := GetMetaInfo()
|
||||
|
||||
if buildInfo, ok := debug.ReadBuildInfo(); ok {
|
||||
var vcsRevision, vcsTime, vcsModified string
|
||||
|
||||
for _, setting := range buildInfo.Settings {
|
||||
switch setting.Key {
|
||||
case "vcs.revision":
|
||||
vcsRevision = setting.Value
|
||||
case "vcs.time":
|
||||
vcsTime = setting.Value
|
||||
case "vcs.modified":
|
||||
vcsModified = setting.Value
|
||||
}
|
||||
}
|
||||
|
||||
if vcsRevision != "" {
|
||||
expectedCommit := vcsRevision
|
||||
if len(vcsRevision) >= 7 {
|
||||
expectedCommit = vcsRevision[:7]
|
||||
}
|
||||
|
||||
if info.Commit == unknownValue {
|
||||
t.Errorf(
|
||||
"Expected commit %q, got %q; ensure repository has commit history",
|
||||
expectedCommit,
|
||||
info.Commit,
|
||||
)
|
||||
} else if info.Commit != expectedCommit {
|
||||
t.Errorf("Commit = %q, want %q", info.Commit, expectedCommit)
|
||||
}
|
||||
} else {
|
||||
t.Logf("No vcs.revision found; ensure repository has Git metadata to cover commit assignment")
|
||||
}
|
||||
|
||||
if vcsTime != "" {
|
||||
if parsedTime, err := time.Parse(time.RFC3339, vcsTime); err == nil {
|
||||
expectedDate := parsedTime.Format("2006-01-02")
|
||||
if info.Date == unknownValue {
|
||||
t.Errorf(
|
||||
"Expected date %q, got %q; ensure vcs.time is a valid RFC3339 timestamp",
|
||||
expectedDate,
|
||||
info.Date,
|
||||
)
|
||||
} else if info.Date != expectedDate {
|
||||
t.Errorf("Date = %q, want %q", info.Date, expectedDate)
|
||||
}
|
||||
} else {
|
||||
t.Logf("vcs.time %q is invalid; date should be in YYYY-MM-DD format", vcsTime)
|
||||
|
||||
if !regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`).MatchString(info.Date) {
|
||||
t.Errorf("Date = %q, want valid YYYY-MM-DD format", info.Date)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
t.Logf("No vcs.time found; date should be in YYYY-MM-DD format")
|
||||
|
||||
if !regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`).MatchString(info.Date) {
|
||||
t.Errorf("Date = %q, want valid YYYY-MM-DD format", info.Date)
|
||||
}
|
||||
}
|
||||
|
||||
if vcsModified == trueValue && info.Version != unknownValue {
|
||||
if !strings.Contains(info.Version, "+dirty") {
|
||||
t.Errorf(
|
||||
"Expected version to contain '+dirty', got %q; ensure repository has uncommitted changes",
|
||||
info.Version,
|
||||
)
|
||||
}
|
||||
} else if vcsModified != trueValue {
|
||||
t.Logf("Repository is clean (vcs.modified=%q); make uncommitted changes to cover '+dirty' case", vcsModified)
|
||||
}
|
||||
} else {
|
||||
t.Logf("debug.ReadBuildInfo() failed; ensure tests run in a Git repository to cover VCS parsing")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetVersionInfo_InvalidVCSTime(t *testing.T) {
|
||||
Version = devVersion
|
||||
Commit = unknownValue
|
||||
Date = unknownValue
|
||||
|
||||
info := GetMetaInfo()
|
||||
|
||||
if !regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`).MatchString(info.Date) {
|
||||
t.Errorf("Date = %q, want valid YYYY-MM-DD format", info.Date)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMetaStr(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setVars func()
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
name: "With commit (GoReleaser build)",
|
||||
setVars: func() {
|
||||
Version = "0.8.10"
|
||||
Commit = "a6fcf77abcdef"
|
||||
Date = "2025-05-27T00:00:00Z"
|
||||
},
|
||||
expect: "v0.8.10 (Built on 2025-05-27 from Git SHA a6fcf77)",
|
||||
},
|
||||
{
|
||||
name: "Without commit (go install build)",
|
||||
setVars: func() {
|
||||
Version = "0.8.10"
|
||||
Commit = unknownValue
|
||||
Date = unknownValue
|
||||
},
|
||||
expect: "v0.8.10 (Built on " + time.Now().UTC().Format("2006-01-02") + ")",
|
||||
},
|
||||
{
|
||||
name: "Invalid version",
|
||||
setVars: func() {
|
||||
Version = "v"
|
||||
Commit = unknownValue
|
||||
Date = unknownValue
|
||||
},
|
||||
expect: "unknown (Built on " + time.Now().UTC().Format("2006-01-02") + ")",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.setVars()
|
||||
|
||||
result := GetMetaStr()
|
||||
if !strings.HasPrefix(result, strings.Split(tt.expect, " (")[0]) ||
|
||||
!regexp.MustCompile(`\d{4}-\d{2}-\d{2}`).MatchString(result) {
|
||||
t.Errorf("GetMetaStr() = %q, want format like %q", result, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContains(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
s string
|
||||
substr string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "Substring found",
|
||||
s: "v1.0.0+dirty",
|
||||
substr: "+dirty",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Substring not found",
|
||||
s: "v1.0.0",
|
||||
substr: "+dirty",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Empty string",
|
||||
s: "",
|
||||
substr: "+dirty",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Empty substring",
|
||||
s: "v1.0.0",
|
||||
substr: "",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := contains(tt.s, tt.substr)
|
||||
if result != tt.expected {
|
||||
t.Errorf("contains(%q, %q) = %v, want %v", tt.s, tt.substr, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package meta
|
||||
|
||||
// Version of Shoutrrr.
|
||||
const Version = `0.6-dev`
|
||||
|
||||
// DocsVersion is prepended to documentation URLs and usually equals MAJOR.MINOR of Version.
|
||||
const DocsVersion = `dev`
|
|
@ -55,12 +55,18 @@ func (config *Config) SetURL(url *url.URL) error {
|
|||
// It sets the host, path, and query parameters, validating host and path, and returns an error if parsing or validation fails.
|
||||
func (config *Config) setURL(resolver types.ConfigQueryResolver, url *url.URL) error {
|
||||
config.Host = url.Host
|
||||
if config.Host != larkHost && config.Host != feishuHost {
|
||||
// Handle documentation generation or empty host
|
||||
if config.Host == "" || (url.User != nil && url.User.Username() == "dummy") {
|
||||
config.Host = "open.larksuite.com"
|
||||
} else if config.Host != larkHost && config.Host != feishuHost {
|
||||
return ErrInvalidHost
|
||||
}
|
||||
|
||||
config.Path = strings.Trim(url.Path, "/")
|
||||
if config.Path == "" {
|
||||
// Handle documentation generation with empty path
|
||||
if config.Path == "" && (url.User != nil && url.User.Username() == "dummy") {
|
||||
config.Path = "token"
|
||||
} else if config.Path == "" {
|
||||
return ErrNoPath
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ var httpClient = &http.Client{Timeout: defaultTime}
|
|||
// Service sends notifications to Lark.
|
||||
type Service struct {
|
||||
standard.Standard
|
||||
config *Config
|
||||
Config *Config
|
||||
pkr format.PropKeyResolver
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ func (service *Service) Send(message string, params *types.Params) error {
|
|||
return ErrLargeMessage
|
||||
}
|
||||
|
||||
config := *service.config
|
||||
config := *service.Config
|
||||
if err := service.pkr.UpdateConfigFromParams(&config, params); err != nil {
|
||||
return fmt.Errorf("updating params: %w", err)
|
||||
}
|
||||
|
@ -79,10 +79,10 @@ func (service *Service) Send(message string, params *types.Params) error {
|
|||
// Initialize configures the service with a URL and logger.
|
||||
func (service *Service) Initialize(configURL *url.URL, logger types.StdLogger) error {
|
||||
service.SetLogger(logger)
|
||||
service.config = &Config{}
|
||||
service.pkr = format.NewPropKeyResolver(service.config)
|
||||
service.Config = &Config{}
|
||||
service.pkr = format.NewPropKeyResolver(service.Config)
|
||||
|
||||
return service.config.SetURL(configURL)
|
||||
return service.Config.SetURL(configURL)
|
||||
}
|
||||
|
||||
// GetID returns the service identifier.
|
||||
|
@ -174,8 +174,8 @@ func (service *Service) handleResponse(resp *http.Response) error {
|
|||
|
||||
service.Logf(
|
||||
"Notification sent successfully to %s/%s",
|
||||
service.config.Host,
|
||||
service.config.Path,
|
||||
service.Config.Host,
|
||||
service.Config.Path,
|
||||
)
|
||||
|
||||
return nil
|
||||
|
|
|
@ -88,14 +88,14 @@ var _ = ginkgo.Describe("Lark Test", func() {
|
|||
data[i] = "0123456789"
|
||||
}
|
||||
message := strings.Join(data, "")
|
||||
service := Service{config: &Config{Host: larkHost, Path: "token"}}
|
||||
service := Service{Config: &Config{Host: larkHost, Path: "token"}}
|
||||
gomega.Expect(service.Send(message, nil)).To(gomega.MatchError(ErrLargeMessage))
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.When("an invalid param is passed", func() {
|
||||
ginkgo.It("should fail to send messages", func() {
|
||||
service := Service{config: &Config{Host: larkHost, Path: "token"}}
|
||||
service := Service{Config: &Config{Host: larkHost, Path: "token"}}
|
||||
gomega.Expect(
|
||||
service.Send("test message", &types.Params{"invalid": "value"}),
|
||||
).To(gomega.MatchError(gomega.ContainSubstring("not a valid config key: invalid")))
|
||||
|
|
|
@ -119,7 +119,10 @@ func (config *Config) setURL(resolver types.ConfigQueryResolver, url *url.URL) e
|
|||
return err
|
||||
}
|
||||
|
||||
if config.Host == "" {
|
||||
// Allow dummy URL during documentation generation
|
||||
if config.Host == "" && (url.User != nil && url.User.Username() == "dummy") {
|
||||
config.Host = "dummy.webhook.office.com"
|
||||
} else if config.Host == "" {
|
||||
return ErrMissingHostParameter
|
||||
}
|
||||
|
||||
|
|
|
@ -14,5 +14,11 @@ func DocsURL(path string) string {
|
|||
path = path[1:]
|
||||
}
|
||||
|
||||
return fmt.Sprintf("https://nicholas-fedor.github.io/shoutrrr/%s/%s", meta.DocsVersion, path)
|
||||
// Use commit for dev builds, version for releases
|
||||
version := meta.GetVersion()
|
||||
if version == "unknown" || version == "dev" {
|
||||
version = meta.GetCommit()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("https://nicholas-fedor.github.io/shoutrrr/%s/%s", version, path)
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ var _ = ginkgo.Describe("the util package", func() {
|
|||
ginkgo.It("should return the expected URL", func() {
|
||||
expectedBase := fmt.Sprintf(
|
||||
`https://nicholas-fedor.github.io/shoutrrr/%s/`,
|
||||
meta.DocsVersion,
|
||||
meta.GetVersion(),
|
||||
)
|
||||
gomega.Expect(util.DocsURL(``)).To(gomega.Equal(expectedBase))
|
||||
gomega.Expect(util.DocsURL(`services/logger`)).
|
||||
|
|
|
@ -50,7 +50,7 @@ func NewSender(logger types.StdLogger, serviceURLs ...string) (*router.ServiceRo
|
|||
return sr, nil
|
||||
}
|
||||
|
||||
// Version returns the current shoutrrr version.
|
||||
// Version returns the current Shoutrrr version.
|
||||
func Version() string {
|
||||
return meta.Version
|
||||
}
|
||||
|
|
|
@ -75,7 +75,9 @@ func printDocs(docFormat string, services []string) cmd.Result {
|
|||
// Initialize the service to populate Config
|
||||
dummyURL, _ := url.Parse(scheme + "://dummy@dummy.com")
|
||||
if err := service.Initialize(dummyURL, logger); err != nil {
|
||||
return cmd.InvalidUsage(fmt.Sprintf("failed to initialize service %q: %v", scheme, err))
|
||||
return cmd.InvalidUsage(
|
||||
fmt.Sprintf("failed to initialize service %q: %v\n", scheme, err),
|
||||
)
|
||||
}
|
||||
|
||||
config := format.GetServiceConfig(service)
|
||||
|
|
|
@ -15,9 +15,8 @@ import (
|
|||
)
|
||||
|
||||
var cobraCmd = &cobra.Command{
|
||||
Use: "shoutrrr",
|
||||
Version: meta.Version,
|
||||
Short: "Shoutrrr CLI",
|
||||
Use: "shoutrrr",
|
||||
Short: "Shoutrrr CLI",
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -26,6 +25,8 @@ func init() {
|
|||
cobraCmd.AddCommand(generate.Cmd)
|
||||
cobraCmd.AddCommand(send.Cmd)
|
||||
cobraCmd.AddCommand(docs.Cmd)
|
||||
|
||||
cobraCmd.Version = meta.GetMetaStr()
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue