Merging upstream version 0.8.13.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
4ccf9dc8f1
commit
bc0f764250
28 changed files with 556 additions and 76 deletions
|
@ -14,7 +14,7 @@ jobs:
|
||||||
docker:
|
docker:
|
||||||
# Specify the version you desire here
|
# Specify the version you desire here
|
||||||
# See: https://circleci.com/developer/images/image/cimg/go
|
# 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
|
# Add steps to the job
|
||||||
# See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps
|
# 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
|
platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v6
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@29694d72cd5e7ef3b09496b39f28a942af47737e
|
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5
|
||||||
with:
|
with:
|
||||||
go-version: 1.24.3
|
go-version: 1.24.x
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@6d4b68b490aef8836e8fb5e50ee7b3bdfa5894f0
|
uses: docker/login-action@6d4b68b490aef8836e8fb5e50ee7b3bdfa5894f0
|
||||||
|
|
9
.github/workflows/docs.yaml
vendored
9
.github/workflows/docs.yaml
vendored
|
@ -7,6 +7,9 @@ permissions:
|
||||||
contents: write
|
contents: write
|
||||||
actions: read
|
actions: read
|
||||||
|
|
||||||
|
env:
|
||||||
|
GO_VERSION: 1.24.x
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -20,9 +23,9 @@ jobs:
|
||||||
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
|
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
|
||||||
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@29694d72cd5e7ef3b09496b39f28a942af47737e
|
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5
|
||||||
with:
|
with:
|
||||||
go-version: "1.24"
|
go-version: ${{ env.GO_VERSION }}
|
||||||
|
|
||||||
- name: Generate Service Config Docs
|
- name: Generate Service Config Docs
|
||||||
run: |
|
run: |
|
||||||
|
@ -31,7 +34,7 @@ jobs:
|
||||||
./generate-service-config-docs.sh
|
./generate-service-config-docs.sh
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
|
uses: actions/setup-python@5db1cf9a59fb97c40a68accab29236f0da7e94db
|
||||||
with:
|
with:
|
||||||
python-version: "3.13.3"
|
python-version: "3.13.3"
|
||||||
cache: "pip"
|
cache: "pip"
|
||||||
|
|
4
.github/workflows/lint.yaml
vendored
4
.github/workflows/lint.yaml
vendored
|
@ -15,7 +15,7 @@ jobs:
|
||||||
uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
|
uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@29694d72cd5e7ef3b09496b39f28a942af47737e
|
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5
|
||||||
with:
|
with:
|
||||||
go-version: "1.24.3"
|
go-version: "1.24.3"
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ jobs:
|
||||||
run: go mod download
|
run: go mod download
|
||||||
|
|
||||||
- name: Run golangci-lint
|
- name: Run golangci-lint
|
||||||
uses: golangci/golangci-lint-action@4d56fa9e3c67fb4afa92b38c99fc7f20f5eeff4e
|
uses: golangci/golangci-lint-action@481777f62fe06de6923fd3a69efd3ba597fe628a
|
||||||
with:
|
with:
|
||||||
args: --timeout=5m --config= # Use default linter settings
|
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:
|
on:
|
||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
paths-ignore:
|
||||||
- main
|
- "docs/*"
|
||||||
|
- ".github/*"
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
2
.github/workflows/release-dev.yaml
vendored
2
.github/workflows/release-dev.yaml
vendored
|
@ -8,6 +8,7 @@ on:
|
||||||
- "v*"
|
- "v*"
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- "docs/*"
|
- "docs/*"
|
||||||
|
- ".github/*"
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
@ -27,6 +28,7 @@ jobs:
|
||||||
uses: ./.github/workflows/build.yaml
|
uses: ./.github/workflows/build.yaml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
needs:
|
needs:
|
||||||
|
- lint
|
||||||
- test
|
- test
|
||||||
with:
|
with:
|
||||||
snapshot: true
|
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
|
uses: ./.github/workflows/build.yaml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
needs:
|
needs:
|
||||||
|
- lint
|
||||||
- test
|
- test
|
||||||
|
|
||||||
renew-docs:
|
update-go-docs:
|
||||||
name: Refresh pkg.go.dev
|
uses: ./.github/workflows/update-go-docs.yaml
|
||||||
needs: build
|
needs:
|
||||||
runs-on: ubuntu-latest
|
- build
|
||||||
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
|
|
||||||
|
|
4
.github/workflows/test.yaml
vendored
4
.github/workflows/test.yaml
vendored
|
@ -15,7 +15,7 @@ jobs:
|
||||||
uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
|
uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@29694d72cd5e7ef3b09496b39f28a942af47737e
|
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5
|
||||||
with:
|
with:
|
||||||
go-version: "1.24.3"
|
go-version: "1.24.3"
|
||||||
|
|
||||||
|
@ -27,6 +27,6 @@ jobs:
|
||||||
go test -v -coverprofile coverage.out -covermode atomic ./...
|
go test -v -coverprofile coverage.out -covermode atomic ./...
|
||||||
|
|
||||||
- name: Upload coverage reports to Codecov
|
- name: Upload coverage reports to Codecov
|
||||||
uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d
|
uses: codecov/codecov-action@15559ed290fa727036809b67ab0f646ffa6c5158
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
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
|
#!/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.
|
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:
|
Example:
|
||||||
```shell
|
|
||||||
$ shoutrrr generate telegram
|
```bash
|
||||||
|
shoutrrr generate telegram
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
Generating URL for telegram using basic generator
|
Generating URL for telegram using basic generator
|
||||||
Enter the configuration values as prompted
|
Enter the configuration values as prompted
|
||||||
|
|
|
@ -6,5 +6,5 @@ The main generator is the reflection based [Basic generator](./basic) that aims
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```bash
|
```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
|
module github.com/nicholas-fedor/shoutrrr
|
||||||
|
|
||||||
go 1.24.2
|
go 1.24.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fatih/color v1.18.0
|
github.com/fatih/color v1.18.0
|
||||||
|
@ -14,7 +14,7 @@ require (
|
||||||
)
|
)
|
||||||
|
|
||||||
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
|
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-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||||
github.com/google/go-cmp v0.7.0 // 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/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.9.0 // indirect
|
github.com/sagikazarmark/locafero v0.9.0 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/afero v1.14.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/spf13/pflag v1.0.6 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
golang.org/x/text v0.25.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
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // 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.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU=
|
||||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
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/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 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=
|
||||||
|
@ -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/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 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/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4=
|
||||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
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 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/jarcoal/httpmock v1.4.0 h1:BvhqnH0JAYbNudL2GMJKgOHe2CtKlzJ/5rWKyp+hc2k=
|
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/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 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
|
||||||
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
|
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.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk=
|
||||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
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 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
github.com/spf13/pflag v1.0.6 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/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 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
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 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
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 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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
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 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
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 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
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.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
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 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
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=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|
|
@ -12,7 +12,10 @@ builds:
|
||||||
- arm
|
- arm
|
||||||
- arm64
|
- arm64
|
||||||
ldflags:
|
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:
|
archives:
|
||||||
- id: default # Unique ID for this archive configuration
|
- 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.
|
// 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 {
|
func (config *Config) setURL(resolver types.ConfigQueryResolver, url *url.URL) error {
|
||||||
config.Host = url.Host
|
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
|
return ErrInvalidHost
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Path = strings.Trim(url.Path, "/")
|
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
|
return ErrNoPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ var httpClient = &http.Client{Timeout: defaultTime}
|
||||||
// Service sends notifications to Lark.
|
// Service sends notifications to Lark.
|
||||||
type Service struct {
|
type Service struct {
|
||||||
standard.Standard
|
standard.Standard
|
||||||
config *Config
|
Config *Config
|
||||||
pkr format.PropKeyResolver
|
pkr format.PropKeyResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ func (service *Service) Send(message string, params *types.Params) error {
|
||||||
return ErrLargeMessage
|
return ErrLargeMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
config := *service.config
|
config := *service.Config
|
||||||
if err := service.pkr.UpdateConfigFromParams(&config, params); err != nil {
|
if err := service.pkr.UpdateConfigFromParams(&config, params); err != nil {
|
||||||
return fmt.Errorf("updating params: %w", err)
|
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.
|
// Initialize configures the service with a URL and logger.
|
||||||
func (service *Service) Initialize(configURL *url.URL, logger types.StdLogger) error {
|
func (service *Service) Initialize(configURL *url.URL, logger types.StdLogger) error {
|
||||||
service.SetLogger(logger)
|
service.SetLogger(logger)
|
||||||
service.config = &Config{}
|
service.Config = &Config{}
|
||||||
service.pkr = format.NewPropKeyResolver(service.config)
|
service.pkr = format.NewPropKeyResolver(service.Config)
|
||||||
|
|
||||||
return service.config.SetURL(configURL)
|
return service.Config.SetURL(configURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the service identifier.
|
// GetID returns the service identifier.
|
||||||
|
@ -174,8 +174,8 @@ func (service *Service) handleResponse(resp *http.Response) error {
|
||||||
|
|
||||||
service.Logf(
|
service.Logf(
|
||||||
"Notification sent successfully to %s/%s",
|
"Notification sent successfully to %s/%s",
|
||||||
service.config.Host,
|
service.Config.Host,
|
||||||
service.config.Path,
|
service.Config.Path,
|
||||||
)
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -88,14 +88,14 @@ var _ = ginkgo.Describe("Lark Test", func() {
|
||||||
data[i] = "0123456789"
|
data[i] = "0123456789"
|
||||||
}
|
}
|
||||||
message := strings.Join(data, "")
|
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))
|
gomega.Expect(service.Send(message, nil)).To(gomega.MatchError(ErrLargeMessage))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
ginkgo.When("an invalid param is passed", func() {
|
ginkgo.When("an invalid param is passed", func() {
|
||||||
ginkgo.It("should fail to send messages", 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(
|
gomega.Expect(
|
||||||
service.Send("test message", &types.Params{"invalid": "value"}),
|
service.Send("test message", &types.Params{"invalid": "value"}),
|
||||||
).To(gomega.MatchError(gomega.ContainSubstring("not a valid config key: invalid")))
|
).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
|
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
|
return ErrMissingHostParameter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,5 +14,11 @@ func DocsURL(path string) string {
|
||||||
path = path[1:]
|
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() {
|
ginkgo.It("should return the expected URL", func() {
|
||||||
expectedBase := fmt.Sprintf(
|
expectedBase := fmt.Sprintf(
|
||||||
`https://nicholas-fedor.github.io/shoutrrr/%s/`,
|
`https://nicholas-fedor.github.io/shoutrrr/%s/`,
|
||||||
meta.DocsVersion,
|
meta.GetVersion(),
|
||||||
)
|
)
|
||||||
gomega.Expect(util.DocsURL(``)).To(gomega.Equal(expectedBase))
|
gomega.Expect(util.DocsURL(``)).To(gomega.Equal(expectedBase))
|
||||||
gomega.Expect(util.DocsURL(`services/logger`)).
|
gomega.Expect(util.DocsURL(`services/logger`)).
|
||||||
|
|
|
@ -50,7 +50,7 @@ func NewSender(logger types.StdLogger, serviceURLs ...string) (*router.ServiceRo
|
||||||
return sr, nil
|
return sr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version returns the current shoutrrr version.
|
// Version returns the current Shoutrrr version.
|
||||||
func Version() string {
|
func Version() string {
|
||||||
return meta.Version
|
return meta.Version
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,9 @@ func printDocs(docFormat string, services []string) cmd.Result {
|
||||||
// Initialize the service to populate Config
|
// Initialize the service to populate Config
|
||||||
dummyURL, _ := url.Parse(scheme + "://dummy@dummy.com")
|
dummyURL, _ := url.Parse(scheme + "://dummy@dummy.com")
|
||||||
if err := service.Initialize(dummyURL, logger); err != nil {
|
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)
|
config := format.GetServiceConfig(service)
|
||||||
|
|
|
@ -16,7 +16,6 @@ import (
|
||||||
|
|
||||||
var cobraCmd = &cobra.Command{
|
var cobraCmd = &cobra.Command{
|
||||||
Use: "shoutrrr",
|
Use: "shoutrrr",
|
||||||
Version: meta.Version,
|
|
||||||
Short: "Shoutrrr CLI",
|
Short: "Shoutrrr CLI",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +25,8 @@ func init() {
|
||||||
cobraCmd.AddCommand(generate.Cmd)
|
cobraCmd.AddCommand(generate.Cmd)
|
||||||
cobraCmd.AddCommand(send.Cmd)
|
cobraCmd.AddCommand(send.Cmd)
|
||||||
cobraCmd.AddCommand(docs.Cmd)
|
cobraCmd.AddCommand(docs.Cmd)
|
||||||
|
|
||||||
|
cobraCmd.Version = meta.GetMetaStr()
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue