make tests happy

This commit is contained in:
Hunter Kehoe 2025-07-07 22:47:41 -06:00
parent 1f2c76e63d
commit 650f492d7d
9 changed files with 27 additions and 210 deletions

View file

@ -1440,7 +1440,7 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
* Full [IPv6 support](config.md#ipv6-support) for ntfy and the official ntfy.sh server ([#519](https://github.com/binwiederhier/ntfy/issues/519)/[#1380](https://github.com/binwiederhier/ntfy/pull/1380)/[ansible#4](https://github.com/binwiederhier/ntfy-ansible/pull/4))
* Support `X-Client-IP`, `X-Real-IP`, `Forwarded` headers for [rate limiting](config.md#ip-based-rate-limiting) via `proxy-forwarded-header` and `proxy-trusted-hosts` ([#1360](https://github.com/binwiederhier/ntfy/pull/1360)/[#1252](https://github.com/binwiederhier/ntfy/pull/1252), thanks to [@pixitha](https://github.com/pixitha))
* Add STDIN support for `ntfy publish` ([#1382](https://github.com/binwiederhier/ntfy/pull/1382), thanks to [@srevn](https://github.com/srevn))
* You can now use [Slim-Sprig](https://github.com/go-task/slim-sprig) functions in message/title templates ([#1121](https://github.com/binwiederhier/ntfy/issues/1121), thanks to [@davidatkinsondoyle](https://github.com/davidatkinsondoyle) for reporting and to [@wunter8](https://github.com/wunter8) for implementing)
* You can now use a subset of [Sprig](https://github.com/Masterminds/sprig) functions in message/title templates ([#1121](https://github.com/binwiederhier/ntfy/issues/1121), thanks to [@davidatkinsondoyle](https://github.com/davidatkinsondoyle) for reporting and to [@wunter8](https://github.com/wunter8) for implementing)
**Languages**

View file

@ -1,24 +0,0 @@
# OS Functions
_WARNING:_ These functions can lead to information leakage if not used
appropriately.
_WARNING:_ Some notable implementations of Sprig (such as
[Kubernetes Helm](http://helm.sh)) _do not provide these functions for security
reasons_.
## env
The `env` function reads an environment variable:
```
env "HOME"
```
## expandenv
To substitute environment variables in a string, use `expandenv`:
```
expandenv "Your path is set to $PATH"
```

View file

@ -1,151 +0,0 @@
# Semantic Version Functions
Some version schemes are easily parseable and comparable. Sprig provides functions
for working with [SemVer 2](http://semver.org) versions.
## semver
The `semver` function parses a string into a Semantic Version:
```
$version := semver "1.2.3-alpha.1+123"
```
_If the parser fails, it will cause template execution to halt with an error._
At this point, `$version` is a pointer to a `Version` object with the following
properties:
- `$version.Major`: The major number (`1` above)
- `$version.Minor`: The minor number (`2` above)
- `$version.Patch`: The patch number (`3` above)
- `$version.Prerelease`: The prerelease (`alpha.1` above)
- `$version.Metadata`: The build metadata (`123` above)
- `$version.Original`: The original version as a string
Additionally, you can compare a `Version` to another `version` using the `Compare`
function:
```
semver "1.4.3" | (semver "1.2.3").Compare
```
The above will return `-1`.
The return values are:
- `-1` if the given semver is greater than the semver whose `Compare` method was called
- `1` if the version who's `Compare` function was called is greater.
- `0` if they are the same version
(Note that in SemVer, the `Metadata` field is not compared during version
comparison operations.)
## semverCompare
A more robust comparison function is provided as `semverCompare`. It returns `true` if
the constraint matches, or `false` if it does not match. This version supports version ranges:
- `semverCompare "1.2.3" "1.2.3"` checks for an exact match
- `semverCompare "^1.2.0" "1.2.3"` checks that the major and minor versions match, and that the patch
number of the second version is _greater than or equal to_ the first parameter.
The SemVer functions use the [Masterminds semver library](https://github.com/Masterminds/semver),
from the creators of Sprig.
## Basic Comparisons
There are two elements to the comparisons. First, a comparison string is a list
of space or comma separated AND comparisons. These are then separated by || (OR)
comparisons. For example, `">= 1.2 < 3.0.0 || >= 4.2.3"` is looking for a
comparison that's greater than or equal to 1.2 and less than 3.0.0 or is
greater than or equal to 4.2.3.
The basic comparisons are:
- `=`: equal (aliased to no operator)
- `!=`: not equal
- `>`: greater than
- `<`: less than
- `>=`: greater than or equal to
- `<=`: less than or equal to
_Note, according to the Semantic Version specification pre-releases may not be
API compliant with their release counterpart. It says,_
## Working With Prerelease Versions
Pre-releases, for those not familiar with them, are used for software releases
prior to stable or generally available releases. Examples of prereleases include
development, alpha, beta, and release candidate releases. A prerelease may be
a version such as `1.2.3-beta.1` while the stable release would be `1.2.3`. In the
order of precedence, prereleases come before their associated releases. In this
example `1.2.3-beta.1 < 1.2.3`.
According to the Semantic Version specification prereleases may not be
API compliant with their release counterpart. It says,
> A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version.
SemVer comparisons using constraints without a prerelease comparator will skip
prerelease versions. For example, `>=1.2.3` will skip prereleases when looking
at a list of releases while `>=1.2.3-0` will evaluate and find prereleases.
The reason for the `0` as a pre-release version in the example comparison is
because pre-releases can only contain ASCII alphanumerics and hyphens (along with
`.` separators), per the spec. Sorting happens in ASCII sort order, again per the
spec. The lowest character is a `0` in ASCII sort order
(see an [ASCII Table](http://www.asciitable.com/))
Understanding ASCII sort ordering is important because A-Z comes before a-z. That
means `>=1.2.3-BETA` will return `1.2.3-alpha`. What you might expect from case
sensitivity doesn't apply here. This is due to ASCII sort ordering which is what
the spec specifies.
## Hyphen Range Comparisons
There are multiple methods to handle ranges and the first is hyphens ranges.
These look like:
- `1.2 - 1.4.5` which is equivalent to `>= 1.2 <= 1.4.5`
- `2.3.4 - 4.5` which is equivalent to `>= 2.3.4 <= 4.5`
## Wildcards In Comparisons
The `x`, `X`, and `*` characters can be used as a wildcard character. This works
for all comparison operators. When used on the `=` operator it falls
back to the patch level comparison (see tilde below). For example,
- `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
- `>= 1.2.x` is equivalent to `>= 1.2.0`
- `<= 2.x` is equivalent to `< 3`
- `*` is equivalent to `>= 0.0.0`
## Tilde Range Comparisons (Patch)
The tilde (`~`) comparison operator is for patch level ranges when a minor
version is specified and major level changes when the minor number is missing.
For example,
- `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0`
- `~1` is equivalent to `>= 1, < 2`
- `~2.3` is equivalent to `>= 2.3, < 2.4`
- `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
- `~1.x` is equivalent to `>= 1, < 2`
## Caret Range Comparisons (Major)
The caret (`^`) comparison operator is for major level changes once a stable
(1.0.0) release has occurred. Prior to a 1.0.0 release the minor versions acts
as the API stability level. This is useful when comparisons of API versions as a
major change is API breaking. For example,
- `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0`
- `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0`
- `^2.3` is equivalent to `>= 2.3, < 3`
- `^2.x` is equivalent to `>= 2.0.0, < 3`
- `^0.2.3` is equivalent to `>=0.2.3 <0.3.0`
- `^0.2` is equivalent to `>=0.2.0 <0.3.0`
- `^0.0.3` is equivalent to `>=0.0.3 <0.0.4`
- `^0.0` is equivalent to `>=0.0.0 <0.1.0`
- `^0` is equivalent to `>=0.0.0 <1.0.0`

4
go.mod
View file

@ -32,9 +32,11 @@ require github.com/pkg/errors v0.9.1 // indirect
require (
firebase.google.com/go/v4 v4.16.1
github.com/SherClockHolmes/webpush-go v1.4.0
github.com/google/uuid v1.6.0
github.com/microcosm-cc/bluemonday v1.0.27
github.com/prometheus/client_golang v1.22.0
github.com/stripe/stripe-go/v74 v74.30.0
golang.org/x/text v0.26.0
)
require (
@ -67,7 +69,6 @@ require (
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.14.2 // indirect
github.com/gorilla/css v1.0.1 // indirect
@ -93,7 +94,6 @@ require (
go.opentelemetry.io/otel/trace v1.37.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.26.0 // indirect
google.golang.org/appengine/v2 v2.0.6 // indirect
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect

View file

@ -1133,11 +1133,7 @@ func replaceTemplate(tpl string, source string) (string, error) {
if err := json.Unmarshal([]byte(source), &data); err != nil {
return "", errHTTPBadRequestTemplateMessageNotJSON
}
sprigFuncs := sprig.FuncMap()
// remove unsafe functions
delete(sprigFuncs, "env")
delete(sprigFuncs, "expandenv")
t, err := template.New("").Funcs(sprigFuncs).Parse(tpl)
t, err := template.New("").Funcs(sprig.FuncMap()).Parse(tpl)
if err != nil {
return "", errHTTPBadRequestTemplateInvalid
}

View file

@ -3,16 +3,10 @@ package sprig
import (
"bytes"
"encoding/json"
"math/rand"
"reflect"
"strings"
"time"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
// dfault checks whether `given` is set, and returns default if not set.
//
// This returns `d` if `given` appears not to be set, and `given` otherwise.

View file

@ -11,6 +11,8 @@ import (
"strings"
ttemplate "text/template"
"time"
"golang.org/x/text/cases"
)
// FuncMap produces the function map.
@ -107,7 +109,7 @@ var genericMap = map[string]interface{}{
"trim": strings.TrimSpace,
"upper": strings.ToUpper,
"lower": strings.ToLower,
"title": strings.Title,
"title": cases.Title,
"substr": substring,
// Switch order so that "foo" | repeat 5
"repeat": func(count int, str string) string { return strings.Repeat(str, count) },

View file

@ -39,7 +39,7 @@ func mustPush(list interface{}, v interface{}) ([]interface{}, error) {
return append(nl, v), nil
default:
return nil, fmt.Errorf("Cannot push on type %s", tp)
return nil, fmt.Errorf("cannot push on type %s", tp)
}
}
@ -69,7 +69,7 @@ func mustPrepend(list interface{}, v interface{}) ([]interface{}, error) {
return append([]interface{}{v}, nl...), nil
default:
return nil, fmt.Errorf("Cannot prepend on type %s", tp)
return nil, fmt.Errorf("cannot prepend on type %s", tp)
}
}
@ -113,7 +113,7 @@ func mustChunk(size int, list interface{}) ([][]interface{}, error) {
return nl, nil
default:
return nil, fmt.Errorf("Cannot chunk type %s", tp)
return nil, fmt.Errorf("cannot chunk type %s", tp)
}
}
@ -139,7 +139,7 @@ func mustLast(list interface{}) (interface{}, error) {
return l2.Index(l - 1).Interface(), nil
default:
return nil, fmt.Errorf("Cannot find last on type %s", tp)
return nil, fmt.Errorf("cannot find last on type %s", tp)
}
}
@ -165,7 +165,7 @@ func mustFirst(list interface{}) (interface{}, error) {
return l2.Index(0).Interface(), nil
default:
return nil, fmt.Errorf("Cannot find first on type %s", tp)
return nil, fmt.Errorf("cannot find first on type %s", tp)
}
}
@ -196,7 +196,7 @@ func mustRest(list interface{}) ([]interface{}, error) {
return nl, nil
default:
return nil, fmt.Errorf("Cannot find rest on type %s", tp)
return nil, fmt.Errorf("cannot find rest on type %s", tp)
}
}
@ -227,7 +227,7 @@ func mustInitial(list interface{}) ([]interface{}, error) {
return nl, nil
default:
return nil, fmt.Errorf("Cannot find initial on type %s", tp)
return nil, fmt.Errorf("cannot find initial on type %s", tp)
}
}
@ -267,7 +267,7 @@ func mustReverse(v interface{}) ([]interface{}, error) {
return nl, nil
default:
return nil, fmt.Errorf("Cannot find reverse on type %s", tp)
return nil, fmt.Errorf("cannot find reverse on type %s", tp)
}
}
@ -298,7 +298,7 @@ func mustCompact(list interface{}) ([]interface{}, error) {
return nl, nil
default:
return nil, fmt.Errorf("Cannot compact on type %s", tp)
return nil, fmt.Errorf("cannot compact on type %s", tp)
}
}
@ -329,7 +329,7 @@ func mustUniq(list interface{}) ([]interface{}, error) {
return dest, nil
default:
return nil, fmt.Errorf("Cannot find uniq on type %s", tp)
return nil, fmt.Errorf("cannot find uniq on type %s", tp)
}
}
@ -369,7 +369,7 @@ func mustWithout(list interface{}, omit ...interface{}) ([]interface{}, error) {
return res, nil
default:
return nil, fmt.Errorf("Cannot find without on type %s", tp)
return nil, fmt.Errorf("cannot find without on type %s", tp)
}
}
@ -401,7 +401,7 @@ func mustHas(needle interface{}, haystack interface{}) (bool, error) {
return false, nil
default:
return false, fmt.Errorf("Cannot find has on type %s", tp)
return false, fmt.Errorf("cannot find has on type %s", tp)
}
}
@ -457,7 +457,7 @@ func concat(lists ...interface{}) interface{} {
res = append(res, l2.Index(i).Interface())
}
default:
panic(fmt.Sprintf("Cannot concat type %s as list", tp))
panic(fmt.Sprintf("cannot concat type %s as list", tp))
}
}
return res

View file

@ -101,7 +101,7 @@ func TestToFloat64(t *testing.T) {
if target != toFloat64("102") {
t.Errorf("Expected 102")
}
if 0 != toFloat64("frankie") {
if toFloat64("frankie") != 0 {
t.Errorf("Expected 0")
}
if target != toFloat64(uint16(102)) {
@ -110,10 +110,10 @@ func TestToFloat64(t *testing.T) {
if target != toFloat64(uint64(102)) {
t.Errorf("Expected 102")
}
if 102.1234 != toFloat64(float64(102.1234)) {
if toFloat64(float64(102.1234)) != 102.1234 {
t.Errorf("Expected 102.1234")
}
if 1 != toFloat64(true) {
if toFloat64(true) != 1 {
t.Errorf("Expected 102")
}
}
@ -137,7 +137,7 @@ func TestToInt64(t *testing.T) {
if target != toInt64("102") {
t.Errorf("Expected 102")
}
if 0 != toInt64("frankie") {
if toInt64("frankie") != 0 {
t.Errorf("Expected 0")
}
if target != toInt64(uint16(102)) {
@ -149,7 +149,7 @@ func TestToInt64(t *testing.T) {
if target != toInt64(float64(102.1234)) {
t.Errorf("Expected 102")
}
if 1 != toInt64(true) {
if toInt64(true) != 1 {
t.Errorf("Expected 102")
}
}
@ -174,7 +174,7 @@ func TestToInt(t *testing.T) {
if target != toInt("102") {
t.Errorf("Expected 102")
}
if 0 != toInt("frankie") {
if toInt("frankie") != 0 {
t.Errorf("Expected 0")
}
if target != toInt(uint16(102)) {
@ -186,7 +186,7 @@ func TestToInt(t *testing.T) {
if target != toInt(float64(102.1234)) {
t.Errorf("Expected 102")
}
if 1 != toInt(true) {
if toInt(true) != 1 {
t.Errorf("Expected 102")
}
}