mirror of
https://github.com/crazy-max/diun.git
synced 2025-12-24 06:28:13 +01:00
Tags sorting support (#645)
Co-authored-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
147
docs/faq.md
147
docs/faq.md
@@ -54,34 +54,34 @@ The title and body of a notification message can be customized for each notifier
|
|||||||
|
|
||||||
Templating is supported with the following fields:
|
Templating is supported with the following fields:
|
||||||
|
|
||||||
| Key | Description |
|
| Key | Description |
|
||||||
|----------------------------------|-------------|
|
|---------------------------------|---------------------------------------------------------------------------------------|
|
||||||
| `.Meta.ID` | App ID: `diun` |
|
| `.Meta.ID` | App ID: `diun` |
|
||||||
| `.Meta.Name` | App Name: `Diun` |
|
| `.Meta.Name` | App Name: `Diun` |
|
||||||
| `.Meta.Desc` | App description: `Docker image update notifier` |
|
| `.Meta.Desc` | App description: `Docker image update notifier` |
|
||||||
| `.Meta.URL` | App repo URL: `https://github.com/crazy-max/diun` |
|
| `.Meta.URL` | App repo URL: `https://github.com/crazy-max/diun` |
|
||||||
| `.Meta.Logo` | App logo URL: `https://raw.githubusercontent.com/crazy-max/diun/master/.res/diun.png` |
|
| `.Meta.Logo` | App logo URL: `https://raw.githubusercontent.com/crazy-max/diun/master/.res/diun.png` |
|
||||||
| `.Meta.Author` | App author: `CrazyMax` |
|
| `.Meta.Author` | App author: `CrazyMax` |
|
||||||
| `.Meta.Version` | App version: `v4.19.0` |
|
| `.Meta.Version` | App version: `v4.19.0` |
|
||||||
| `.Meta.UserAgent` | App user-agent used to talk with registries: `diun/4.19.0 go/1.16 Linux` |
|
| `.Meta.UserAgent` | App user-agent used to talk with registries: `diun/4.19.0 go/1.16 Linux` |
|
||||||
| `.Meta.Hostname` | Hostname |
|
| `.Meta.Hostname` | Hostname |
|
||||||
| `.Entry.Status` | Entry status. Can be `new`, `update`, `unchange`, `skip` or `error` |
|
| `.Entry.Status` | Entry status. Can be `new`, `update`, `unchange`, `skip` or `error` |
|
||||||
| `.Entry.Provider` | [Provider](config/providers.md) used |
|
| `.Entry.Provider` | [Provider](config/providers.md) used |
|
||||||
| `.Entry.Image` | Docker image name. e.g. `docker.io/crazymax/diun:latest` |
|
| `.Entry.Image` | Docker image name. e.g. `docker.io/crazymax/diun:latest` |
|
||||||
| `.Entry.Image.Domain` | Docker image domain. e.g. `docker.io` |
|
| `.Entry.Image.Domain` | Docker image domain. e.g. `docker.io` |
|
||||||
| `.Entry.Image.Path` | Docker image path. e.g. `crazymax/diun` |
|
| `.Entry.Image.Path` | Docker image path. e.g. `crazymax/diun` |
|
||||||
| `.Entry.Image.Tag` | Docker image tag. e.g. `latest` |
|
| `.Entry.Image.Tag` | Docker image tag. e.g. `latest` |
|
||||||
| `.Entry.Image.Digest` | Docker image digest |
|
| `.Entry.Image.Digest` | Docker image digest |
|
||||||
| `.Entry.Image.HubLink` | Docker image hub link (if available). e.g. `https://hub.docker.com/r/crazymax/diun` |
|
| `.Entry.Image.HubLink` | Docker image hub link (if available). e.g. `https://hub.docker.com/r/crazymax/diun` |
|
||||||
| `.Entry.Manifest.Name` | Manifest name. e.g. `docker.io/crazymax/diun` |
|
| `.Entry.Manifest.Name` | Manifest name. e.g. `docker.io/crazymax/diun` |
|
||||||
| `.Entry.Manifest.Tag` | Manifest tag. e.g. `latest` |
|
| `.Entry.Manifest.Tag` | Manifest tag. e.g. `latest` |
|
||||||
| `.Entry.Manifest.MIMEType` | Manifest MIME type. e.g. `application/vnd.docker.distribution.manifest.list.v2+json` |
|
| `.Entry.Manifest.MIMEType` | Manifest MIME type. e.g. `application/vnd.docker.distribution.manifest.list.v2+json` |
|
||||||
| `.Entry.Manifest.Digest` | Manifest digest |
|
| `.Entry.Manifest.Digest` | Manifest digest |
|
||||||
| `.Entry.Manifest.Created` | Manifest created date. e.g. `2021-06-20T12:23:56Z` |
|
| `.Entry.Manifest.Created` | Manifest created date. e.g. `2021-06-20T12:23:56Z` |
|
||||||
| `.Entry.Manifest.DockerVersion` | Version of Docker that was used to build the image. e.g. `20.10.7` |
|
| `.Entry.Manifest.DockerVersion` | Version of Docker that was used to build the image. e.g. `20.10.7` |
|
||||||
| `.Entry.Manifest.Labels` | Image labels |
|
| `.Entry.Manifest.Labels` | Image labels |
|
||||||
| `.Entry.Manifest.Layers` | Image layers |
|
| `.Entry.Manifest.Layers` | Image layers |
|
||||||
| `.Entry.Manifest.Platform` | Platform that the image is runs on. e.g. `linux/amd64` |
|
| `.Entry.Manifest.Platform` | Platform that the image is runs on. e.g. `linux/amd64` |
|
||||||
|
|
||||||
## Authentication against the registry
|
## Authentication against the registry
|
||||||
|
|
||||||
@@ -205,6 +205,97 @@ Or you can tweak the [`schedule` setting](config/watch.md#schedule) with somethi
|
|||||||
!!! warning
|
!!! warning
|
||||||
Also be careful with the `watch_repo` setting as it will fetch manifest for **ALL** tags available for the image.
|
Also be careful with the `watch_repo` setting as it will fetch manifest for **ALL** tags available for the image.
|
||||||
|
|
||||||
|
## Tags sorting when using `watch_repo`
|
||||||
|
|
||||||
|
When you use the `watch_repo` setting, Diun will fetch all tags available for
|
||||||
|
the image. Depending on the registry, order of the tags list can change.
|
||||||
|
|
||||||
|
You can use the `sort_tags` setting available for each provider to use a
|
||||||
|
specific sorting method for the tags list.
|
||||||
|
|
||||||
|
* `default`: do not sort and use the expected tags list from the registry
|
||||||
|
* `reverse`: reverse order for the tags list from the registry
|
||||||
|
* `lexicographical`: sort the tags list lexicographically
|
||||||
|
* `semver`: sort the tags list using semantic versioning
|
||||||
|
|
||||||
|
Given the following list of tags received from the registry:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"0.1.0",
|
||||||
|
"0.4.0",
|
||||||
|
"3.0.0-beta.1",
|
||||||
|
"3.0.0-beta.4",
|
||||||
|
"4",
|
||||||
|
"4.0.0",
|
||||||
|
"4.0.0-beta.1",
|
||||||
|
"4.1.0",
|
||||||
|
"4.1.1",
|
||||||
|
"4.10.0",
|
||||||
|
"4.11.0",
|
||||||
|
"4.20",
|
||||||
|
"4.20.0",
|
||||||
|
"4.20.1",
|
||||||
|
"4.3.0",
|
||||||
|
"4.3.1",
|
||||||
|
"4.9.0",
|
||||||
|
"edge",
|
||||||
|
"latest"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is the result for `reverse`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"latest",
|
||||||
|
"edge",
|
||||||
|
"4.9.0",
|
||||||
|
"4.3.1",
|
||||||
|
"4.3.0",
|
||||||
|
"4.20.1",
|
||||||
|
"4.20.0",
|
||||||
|
"4.20",
|
||||||
|
"4.11.0",
|
||||||
|
"4.10.0",
|
||||||
|
"4.1.1",
|
||||||
|
"4.1.0",
|
||||||
|
"4.0.0-beta.1",
|
||||||
|
"4.0.0",
|
||||||
|
"4",
|
||||||
|
"3.0.0-beta.4",
|
||||||
|
"3.0.0-beta.1",
|
||||||
|
"0.4.0",
|
||||||
|
"0.1.0"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
And for `semver`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"4.20.1",
|
||||||
|
"4.20.0",
|
||||||
|
"4.20",
|
||||||
|
"4.11.0",
|
||||||
|
"4.10.0",
|
||||||
|
"4.9.0",
|
||||||
|
"4.3.1",
|
||||||
|
"4.3.0",
|
||||||
|
"4.1.1",
|
||||||
|
"4.1.0",
|
||||||
|
"4.0.0",
|
||||||
|
"4",
|
||||||
|
"4.0.0-beta.1",
|
||||||
|
"3.0.0-beta.4",
|
||||||
|
"3.0.0-beta.1",
|
||||||
|
"0.4.0",
|
||||||
|
"0.1.0",
|
||||||
|
"edge",
|
||||||
|
"latest"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
## Profiling
|
## Profiling
|
||||||
|
|
||||||
Diun provides a simple way to manage runtime/pprof profiling through the
|
Diun provides a simple way to manage runtime/pprof profiling through the
|
||||||
|
|||||||
@@ -172,13 +172,14 @@ Include created and exited containers too (default `false`).
|
|||||||
|
|
||||||
You can configure more finely the way to analyze the image of your container through Docker labels:
|
You can configure more finely the way to analyze the image of your container through Docker labels:
|
||||||
|
|
||||||
| Name | Default | Description |
|
| Name | Default | Description |
|
||||||
|-------------------------------|---------------|---------------|
|
|---------------------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `diun.enable` | | Set to true to enable image analysis of this container |
|
| `diun.enable` | | Set to true to enable image analysis of this container |
|
||||||
| `diun.regopt` | | [Registry options](../config/regopts.md) name to use |
|
| `diun.regopt` | | [Registry options](../config/regopts.md) name to use |
|
||||||
| `diun.watch_repo` | `false` | Watch all tags of this container image ([be careful](../faq.md#docker-hub-rate-limits) with this setting) |
|
| `diun.watch_repo` | `false` | Watch all tags of this container image ([be careful](../faq.md#docker-hub-rate-limits) with this setting) |
|
||||||
| `diun.notify_on` | `new;update` | Semicolon separated list of status to be notified: `new`, `update`. |
|
| `diun.notify_on` | `new;update` | Semicolon separated list of status to be notified: `new`, `update` |
|
||||||
| `diun.max_tags` | `0` | Maximum number of tags to watch if `diun.watch_repo` enabled. `0` means all of them |
|
| `diun.sort_tags` | `reverse` | [Sort tags method](../faq.md#tags-sorting-when-using-watch_repo) if `diun.watch_repo` enabled. One of `default`, `reverse`, `numerical`, `lexicographical` |
|
||||||
| `diun.include_tags` | | Semicolon separated list of regular expressions to include tags. Can be useful if you enable `diun.watch_repo` |
|
| `diun.max_tags` | `0` | Maximum number of tags to watch if `diun.watch_repo` enabled. `0` means all of them |
|
||||||
| `diun.exclude_tags` | | Semicolon separated list of regular expressions to exclude tags. Can be useful if you enable `diun.watch_repo` |
|
| `diun.include_tags` | | Semicolon separated list of regular expressions to include tags. Can be useful if you enable `diun.watch_repo` |
|
||||||
| `diun.platform` | _automatic_ | Platform to use (e.g. `linux/amd64`) |
|
| `diun.exclude_tags` | | Semicolon separated list of regular expressions to exclude tags. Can be useful if you enable `diun.watch_repo` |
|
||||||
|
| `diun.platform` | _automatic_ | Platform to use (e.g. `linux/amd64`) |
|
||||||
|
|||||||
@@ -106,12 +106,13 @@ List of path patterns with [matching and globbing supporting patterns](https://g
|
|||||||
|
|
||||||
The following annotations can be added as comments before the target instruction to customize the image analysis:
|
The following annotations can be added as comments before the target instruction to customize the image analysis:
|
||||||
|
|
||||||
| Name | Default | Description |
|
| Name | Default | Description |
|
||||||
|-------------------------------|---------------|---------------|
|
|---------------------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `diun.regopt` | | [Registry options](../config/regopts.md) name to use |
|
| `diun.regopt` | | [Registry options](../config/regopts.md) name to use |
|
||||||
| `diun.watch_repo` | `false` | Watch all tags of this image |
|
| `diun.watch_repo` | `false` | Watch all tags of this image |
|
||||||
| `diun.notify_on` | `new;update` | Semicolon separated list of status to be notified: `new`, `update`. |
|
| `diun.notify_on` | `new;update` | Semicolon separated list of status to be notified: `new`, `update` |
|
||||||
| `diun.max_tags` | `0` | Maximum number of tags to watch if `watch_repo` enabled. `0` means all of them |
|
| `diun.sort_tags` | `reverse` | [Sort tags method](../faq.md#tags-sorting-when-using-watch_repo) if `diun.watch_repo` enabled. One of `default`, `reverse`, `numerical`, `lexicographical` |
|
||||||
| `diun.include_tags` | | Semicolon separated list of regular expressions to include tags. Can be useful if you enable `diun.watch_repo` |
|
| `diun.max_tags` | `0` | Maximum number of tags to watch if `watch_repo` enabled. `0` means all of them |
|
||||||
| `diun.exclude_tags` | | Semicolon separated list of regular expressions to exclude tags. Can be useful if you enable `diun.watch_repo` |
|
| `diun.include_tags` | | Semicolon separated list of regular expressions to include tags. Can be useful if you enable `diun.watch_repo` |
|
||||||
| `diun.platform` | _automatic_ | Platform to use (e.g. `linux/amd64`) |
|
| `diun.exclude_tags` | | Semicolon separated list of regular expressions to exclude tags. Can be useful if you enable `diun.watch_repo` |
|
||||||
|
| `diun.platform` | _automatic_ | Platform to use (e.g. `linux/amd64`) |
|
||||||
|
|||||||
@@ -172,15 +172,16 @@ Defines the path to the directory that contains the [configuration files](#yaml-
|
|||||||
|
|
||||||
The configuration file(s) defines a slice of images to analyze with the following fields:
|
The configuration file(s) defines a slice of images to analyze with the following fields:
|
||||||
|
|
||||||
| Name | Default | Description |
|
| Name | Default | Description |
|
||||||
|-------------------------------|----------------------------------|---------------|
|
|--------------------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `name` | `latest` | Docker image name to watch using `registry/path:tag` format. If registry omitted, `docker.io` will be used and if tag omitted, `latest` will be used |
|
| `name` | `latest` | Docker image name to watch using `registry/path:tag` format. If registry omitted, `docker.io` will be used and if tag omitted, `latest` will be used |
|
||||||
| `regopt` | | [Registry options](../config/regopts.md) name to use |
|
| `regopt` | | [Registry options](../config/regopts.md) name to use |
|
||||||
| `watch_repo` | `false` | Watch all tags of this image ([be careful](../faq.md#docker-hub-rate-limits) with this setting) |
|
| `watch_repo` | `false` | Watch all tags of this image ([be careful](../faq.md#docker-hub-rate-limits) with this setting) |
|
||||||
| `notify_on` | `new;update` | Semicolon separated list of status to be notified: `new`, `update`. |
|
| `notify_on` | `new;update` | Semicolon separated list of status to be notified: `new`, `update` |
|
||||||
| `max_tags` | `0` | Maximum number of tags to watch if `watch_repo` enabled. `0` means all of them |
|
| `sort_tags` | `reverse` | [Sort tags method](../faq.md#tags-sorting-when-using-watch_repo) if `diun.watch_repo` enabled. One of `default`, `reverse`, `numerical`, `lexicographical` |
|
||||||
| `include_tags` | | List of regular expressions to include tags. Can be useful if you enable `watch_repo` |
|
| `max_tags` | `0` | Maximum number of tags to watch if `watch_repo` enabled. `0` means all of them |
|
||||||
| `exclude_tags` | | List of regular expressions to exclude tags. Can be useful if you enable `watch_repo` |
|
| `include_tags` | | List of regular expressions to include tags. Can be useful if you enable `watch_repo` |
|
||||||
| `platform.os` | _automatic_ | Operating system to use as custom platform |
|
| `exclude_tags` | | List of regular expressions to exclude tags. Can be useful if you enable `watch_repo` |
|
||||||
| `platform.arch` | _automatic_ | CPU architecture to use as custom platform |
|
| `platform.os` | _automatic_ | Operating system to use as custom platform |
|
||||||
| `platform.variant` | _automatic_ | Variant of the CPU to use as custom platform |
|
| `platform.arch` | _automatic_ | CPU architecture to use as custom platform |
|
||||||
|
| `platform.variant` | _automatic_ | Variant of the CPU to use as custom platform |
|
||||||
|
|||||||
@@ -280,13 +280,14 @@ Enable watch by default. If false, pods that don't have `diun.enable: "true"` an
|
|||||||
|
|
||||||
You can configure more finely the way to analyze the image of your pods through Kubernetes annotations:
|
You can configure more finely the way to analyze the image of your pods through Kubernetes annotations:
|
||||||
|
|
||||||
| Name | Default | Description |
|
| Name | Default | Description |
|
||||||
|-------------------------------|---------------|---------------|
|
|---------------------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `diun.enable` | | Set to true to enable image analysis of this pod |
|
| `diun.enable` | | Set to true to enable image analysis of this pod |
|
||||||
| `diun.regopt` | | [Registry options](../config/regopts.md) name to use |
|
| `diun.regopt` | | [Registry options](../config/regopts.md) name to use |
|
||||||
| `diun.watch_repo` | `false` | Watch all tags of this pod image ([be careful](../faq.md#docker-hub-rate-limits) with this setting) |
|
| `diun.watch_repo` | `false` | Watch all tags of this pod image ([be careful](../faq.md#docker-hub-rate-limits) with this setting) |
|
||||||
| `diun.notify_on` | `new;update` | Semicolon separated list of status to be notified: `new`, `update`. |
|
| `diun.notify_on` | `new;update` | Semicolon separated list of status to be notified: `new`, `update`. |
|
||||||
| `diun.max_tags` | `0` | Maximum number of tags to watch if `diun.watch_repo` enabled. `0` means all of them |
|
| `diun.sort_tags` | `reverse` | [Sort tags method](../faq.md#tags-sorting-when-using-watch_repo) if `diun.watch_repo` enabled. One of `default`, `reverse`, `numerical`, `lexicographical` |
|
||||||
| `diun.include_tags` | | Semicolon separated list of regular expressions to include tags. Can be useful if you enable `diun.watch_repo` |
|
| `diun.max_tags` | `0` | Maximum number of tags to watch if `diun.watch_repo` enabled. `0` means all of them |
|
||||||
| `diun.exclude_tags` | | Semicolon separated list of regular expressions to exclude tags. Can be useful if you enable `diun.watch_repo` |
|
| `diun.include_tags` | | Semicolon separated list of regular expressions to include tags. Can be useful if you enable `diun.watch_repo` |
|
||||||
| `diun.platform` | _automatic_ | Platform to use (e.g. `linux/amd64`) |
|
| `diun.exclude_tags` | | Semicolon separated list of regular expressions to exclude tags. Can be useful if you enable `diun.watch_repo` |
|
||||||
|
| `diun.platform` | _automatic_ | Platform to use (e.g. `linux/amd64`) |
|
||||||
|
|||||||
@@ -174,13 +174,14 @@ Enable watch by default. If false, services that don't have `diun.enable=true` l
|
|||||||
|
|
||||||
You can configure more finely the way to analyze the image of your service through Docker labels:
|
You can configure more finely the way to analyze the image of your service through Docker labels:
|
||||||
|
|
||||||
| Name | Default | Description |
|
| Name | Default | Description |
|
||||||
|-------------------------------|---------------|---------------|
|
|---------------------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `diun.enable` | | Set to true to enable image analysis of this service |
|
| `diun.enable` | | Set to true to enable image analysis of this service |
|
||||||
| `diun.regopt` | | [Registry options](../config/regopts.md) name to use |
|
| `diun.regopt` | | [Registry options](../config/regopts.md) name to use |
|
||||||
| `diun.watch_repo` | `false` | Watch all tags of this service image ([be careful](../faq.md#docker-hub-rate-limits) with this setting) |
|
| `diun.watch_repo` | `false` | Watch all tags of this service image ([be careful](../faq.md#docker-hub-rate-limits) with this setting) |
|
||||||
| `diun.notify_on` | `new;update` | Semicolon separated list of status to be notified: `new`, `update`. |
|
| `diun.notify_on` | `new;update` | Semicolon separated list of status to be notified: `new`, `update`. |
|
||||||
| `diun.max_tags` | `0` | Maximum number of tags to watch if `diun.watch_repo` enabled. `0` means all of them |
|
| `diun.sort_tags` | `reverse` | [Sort tags method](../faq.md#tags-sorting-when-using-watch_repo) if `diun.watch_repo` enabled. One of `default`, `reverse`, `numerical`, `lexicographical` |
|
||||||
| `diun.include_tags` | | Semicolon separated list of regular expressions to include tags. Can be useful if you enable `diun.watch_repo` |
|
| `diun.max_tags` | `0` | Maximum number of tags to watch if `diun.watch_repo` enabled. `0` means all of them |
|
||||||
| `diun.exclude_tags` | | Semicolon separated list of regular expressions to exclude tags. Can be useful if you enable `diun.watch_repo` |
|
| `diun.include_tags` | | Semicolon separated list of regular expressions to include tags. Can be useful if you enable `diun.watch_repo` |
|
||||||
| `diun.platform` | _automatic_ | Platform to use (e.g. `linux/amd64`) |
|
| `diun.exclude_tags` | | Semicolon separated list of regular expressions to exclude tags. Can be useful if you enable `diun.watch_repo` |
|
||||||
|
| `diun.platform` | _automatic_ | Platform to use (e.g. `linux/amd64`) |
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -39,6 +39,7 @@ require (
|
|||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.8.0
|
||||||
github.com/tidwall/pretty v1.2.0
|
github.com/tidwall/pretty v1.2.0
|
||||||
go.etcd.io/bbolt v1.3.6
|
go.etcd.io/bbolt v1.3.6
|
||||||
|
golang.org/x/mod v0.5.1
|
||||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150
|
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150
|
||||||
google.golang.org/grpc v1.48.0
|
google.golang.org/grpc v1.48.0
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -1144,6 +1144,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
|
||||||
|
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ func (di *Diun) createJob(job model.Job) {
|
|||||||
tags, err := job.Registry.Tags(registry.TagsOptions{
|
tags, err := job.Registry.Tags(registry.TagsOptions{
|
||||||
Image: job.RegImage,
|
Image: job.RegImage,
|
||||||
Max: job.Image.MaxTags,
|
Max: job.Image.MaxTags,
|
||||||
|
Sort: job.Image.SortTags,
|
||||||
Include: job.Image.IncludeTags,
|
Include: job.Image.IncludeTags,
|
||||||
Exclude: job.Image.ExcludeTags,
|
Exclude: job.Image.ExcludeTags,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -360,6 +360,7 @@ func TestLoadEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range testCases {
|
for _, tt := range testCases {
|
||||||
|
tt := tt
|
||||||
t.Run(tt.desc, func(t *testing.T) {
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
UnsetEnv("DIUN_")
|
UnsetEnv("DIUN_")
|
||||||
|
|
||||||
@@ -484,6 +485,7 @@ for <code>{{ .Entry.Manifest.Platform }}</code> platform.
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range testCases {
|
for _, tt := range testCases {
|
||||||
|
tt := tt
|
||||||
t.Run(tt.desc, func(t *testing.T) {
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
UnsetEnv("DIUN_")
|
UnsetEnv("DIUN_")
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
|
import "github.com/crazy-max/diun/v4/pkg/registry"
|
||||||
|
|
||||||
// Image holds image configuration
|
// Image holds image configuration
|
||||||
type Image struct {
|
type Image struct {
|
||||||
Name string `yaml:"name,omitempty" json:",omitempty"`
|
Name string `yaml:"name,omitempty" json:",omitempty"`
|
||||||
Platform ImagePlatform `yaml:"platform,omitempty" json:",omitempty"`
|
Platform ImagePlatform `yaml:"platform,omitempty" json:",omitempty"`
|
||||||
RegOpt string `yaml:"regopt,omitempty" json:",omitempty"`
|
RegOpt string `yaml:"regopt,omitempty" json:",omitempty"`
|
||||||
WatchRepo bool `yaml:"watch_repo,omitempty" json:",omitempty"`
|
WatchRepo bool `yaml:"watch_repo,omitempty" json:",omitempty"`
|
||||||
NotifyOn []NotifyOn `yaml:"notify_on,omitempty" json:",omitempty"`
|
NotifyOn []NotifyOn `yaml:"notify_on,omitempty" json:",omitempty"`
|
||||||
MaxTags int `yaml:"max_tags,omitempty" json:",omitempty"`
|
MaxTags int `yaml:"max_tags,omitempty" json:",omitempty"`
|
||||||
IncludeTags []string `yaml:"include_tags,omitempty" json:",omitempty"`
|
SortTags registry.SortTag `yaml:"sort_tags,omitempty" json:",omitempty"`
|
||||||
ExcludeTags []string `yaml:"exclude_tags,omitempty" json:",omitempty"`
|
IncludeTags []string `yaml:"include_tags,omitempty" json:",omitempty"`
|
||||||
HubTpl string `yaml:"hub_tpl,omitempty" json:",omitempty"`
|
ExcludeTags []string `yaml:"exclude_tags,omitempty" json:",omitempty"`
|
||||||
|
HubTpl string `yaml:"hub_tpl,omitempty" json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImagePlatform holds image platform configuration
|
// ImagePlatform holds image platform configuration
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/containerd/containerd/platforms"
|
"github.com/containerd/containerd/platforms"
|
||||||
"github.com/crazy-max/diun/v4/internal/model"
|
"github.com/crazy-max/diun/v4/internal/model"
|
||||||
|
"github.com/crazy-max/diun/v4/pkg/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ValidateImage returns a standard image through Docker labels
|
// ValidateImage returns a standard image through Docker labels
|
||||||
@@ -17,6 +18,7 @@ func ValidateImage(image string, labels map[string]string, watchByDef bool) (img
|
|||||||
img = model.Image{
|
img = model.Image{
|
||||||
Name: image,
|
Name: image,
|
||||||
NotifyOn: model.NotifyOnDefaults,
|
NotifyOn: model.NotifyOnDefaults,
|
||||||
|
SortTags: registry.SortTagReverse,
|
||||||
}
|
}
|
||||||
|
|
||||||
if enableStr, ok := labels["diun.enable"]; ok {
|
if enableStr, ok := labels["diun.enable"]; ok {
|
||||||
@@ -51,6 +53,15 @@ func ValidateImage(image string, labels map[string]string, watchByDef bool) (img
|
|||||||
}
|
}
|
||||||
img.NotifyOn = append(img.NotifyOn, notifyOn)
|
img.NotifyOn = append(img.NotifyOn, notifyOn)
|
||||||
}
|
}
|
||||||
|
case "diun.sort_tags":
|
||||||
|
if value == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
sortTags := registry.SortTag(value)
|
||||||
|
if !sortTags.Valid() {
|
||||||
|
return img, fmt.Errorf("unknown sort tags type %q", value)
|
||||||
|
}
|
||||||
|
img.SortTags = sortTags
|
||||||
case "diun.max_tags":
|
case "diun.max_tags":
|
||||||
if img.MaxTags, err = strconv.Atoi(value); err != nil {
|
if img.MaxTags, err = strconv.Atoi(value); err != nil {
|
||||||
return img, fmt.Errorf("cannot parse %s value of label %s", value, key)
|
return img, fmt.Errorf("cannot parse %s value of label %s", value, key)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/crazy-max/diun/v4/internal/model"
|
"github.com/crazy-max/diun/v4/internal/model"
|
||||||
"github.com/crazy-max/diun/v4/internal/provider/file"
|
"github.com/crazy-max/diun/v4/internal/provider/file"
|
||||||
|
"github.com/crazy-max/diun/v4/pkg/registry"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ var (
|
|||||||
Name: "jfrog-docker-reg2.bintray.io/jfrog/artifactory-oss:4.0.0",
|
Name: "jfrog-docker-reg2.bintray.io/jfrog/artifactory-oss:4.0.0",
|
||||||
RegOpt: "bintrayoptions",
|
RegOpt: "bintrayoptions",
|
||||||
NotifyOn: model.NotifyOnDefaults,
|
NotifyOn: model.NotifyOnDefaults,
|
||||||
|
SortTags: registry.SortTagReverse,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -26,7 +28,8 @@ var (
|
|||||||
NotifyOn: []model.NotifyOn{
|
NotifyOn: []model.NotifyOn{
|
||||||
model.NotifyOnNew,
|
model.NotifyOnNew,
|
||||||
},
|
},
|
||||||
MaxTags: 50,
|
SortTags: registry.SortTagLexicographical,
|
||||||
|
MaxTags: 50,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -37,6 +40,7 @@ var (
|
|||||||
Name: "docker.io/crazymax/nextcloud:latest",
|
Name: "docker.io/crazymax/nextcloud:latest",
|
||||||
RegOpt: "myregistry",
|
RegOpt: "myregistry",
|
||||||
NotifyOn: model.NotifyOnDefaults,
|
NotifyOn: model.NotifyOnDefaults,
|
||||||
|
SortTags: registry.SortTagReverse,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -45,6 +49,7 @@ var (
|
|||||||
Name: "crazymax/swarm-cronjob",
|
Name: "crazymax/swarm-cronjob",
|
||||||
WatchRepo: true,
|
WatchRepo: true,
|
||||||
NotifyOn: model.NotifyOnDefaults,
|
NotifyOn: model.NotifyOnDefaults,
|
||||||
|
SortTags: registry.SortTagSemver,
|
||||||
IncludeTags: []string{
|
IncludeTags: []string{
|
||||||
`^1\.2\..*`,
|
`^1\.2\..*`,
|
||||||
},
|
},
|
||||||
@@ -57,6 +62,7 @@ var (
|
|||||||
WatchRepo: true,
|
WatchRepo: true,
|
||||||
NotifyOn: model.NotifyOnDefaults,
|
NotifyOn: model.NotifyOnDefaults,
|
||||||
MaxTags: 10,
|
MaxTags: 10,
|
||||||
|
SortTags: registry.SortTagReverse,
|
||||||
IncludeTags: []string{
|
IncludeTags: []string{
|
||||||
`^(0|[1-9]\d*)\..*`,
|
`^(0|[1-9]\d*)\..*`,
|
||||||
},
|
},
|
||||||
@@ -68,6 +74,7 @@ var (
|
|||||||
Name: "traefik",
|
Name: "traefik",
|
||||||
WatchRepo: true,
|
WatchRepo: true,
|
||||||
NotifyOn: model.NotifyOnDefaults,
|
NotifyOn: model.NotifyOnDefaults,
|
||||||
|
SortTags: registry.SortTagDefault,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -75,6 +82,7 @@ var (
|
|||||||
Image: model.Image{
|
Image: model.Image{
|
||||||
Name: "alpine",
|
Name: "alpine",
|
||||||
NotifyOn: model.NotifyOnDefaults,
|
NotifyOn: model.NotifyOnDefaults,
|
||||||
|
SortTags: registry.SortTagReverse,
|
||||||
Platform: model.ImagePlatform{
|
Platform: model.ImagePlatform{
|
||||||
OS: "linux",
|
OS: "linux",
|
||||||
Arch: "arm64",
|
Arch: "arm64",
|
||||||
@@ -87,6 +95,7 @@ var (
|
|||||||
Image: model.Image{
|
Image: model.Image{
|
||||||
Name: "docker.io/graylog/graylog:3.2.0",
|
Name: "docker.io/graylog/graylog:3.2.0",
|
||||||
NotifyOn: model.NotifyOnDefaults,
|
NotifyOn: model.NotifyOnDefaults,
|
||||||
|
SortTags: registry.SortTagReverse,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -94,6 +103,7 @@ var (
|
|||||||
Image: model.Image{
|
Image: model.Image{
|
||||||
Name: "jacobalberty/unifi:5.9",
|
Name: "jacobalberty/unifi:5.9",
|
||||||
NotifyOn: model.NotifyOnDefaults,
|
NotifyOn: model.NotifyOnDefaults,
|
||||||
|
SortTags: registry.SortTagReverse,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -102,6 +112,7 @@ var (
|
|||||||
Name: "crazymax/ddns-route53",
|
Name: "crazymax/ddns-route53",
|
||||||
WatchRepo: true,
|
WatchRepo: true,
|
||||||
NotifyOn: model.NotifyOnDefaults,
|
NotifyOn: model.NotifyOnDefaults,
|
||||||
|
SortTags: registry.SortTagReverse,
|
||||||
IncludeTags: []string{
|
IncludeTags: []string{
|
||||||
`^1\..*`,
|
`^1\..*`,
|
||||||
},
|
},
|
||||||
@@ -114,6 +125,7 @@ var (
|
|||||||
Image: model.Image{
|
Image: model.Image{
|
||||||
Name: "quay.io/coreos/hyperkube",
|
Name: "quay.io/coreos/hyperkube",
|
||||||
NotifyOn: model.NotifyOnDefaults,
|
NotifyOn: model.NotifyOnDefaults,
|
||||||
|
SortTags: registry.SortTagReverse,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -121,6 +133,7 @@ var (
|
|||||||
Image: model.Image{
|
Image: model.Image{
|
||||||
Name: "quay.io/coreos/hyperkube:v1.1.7-coreos.1",
|
Name: "quay.io/coreos/hyperkube:v1.1.7-coreos.1",
|
||||||
NotifyOn: model.NotifyOnDefaults,
|
NotifyOn: model.NotifyOnDefaults,
|
||||||
|
SortTags: registry.SortTagReverse,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,5 @@
|
|||||||
watch_repo: true
|
watch_repo: true
|
||||||
notify_on:
|
notify_on:
|
||||||
- new
|
- new
|
||||||
|
sort_tags: lexicographical
|
||||||
max_tags: 50
|
max_tags: 50
|
||||||
|
|||||||
@@ -2,15 +2,18 @@
|
|||||||
regopt: myregistry
|
regopt: myregistry
|
||||||
- name: crazymax/swarm-cronjob
|
- name: crazymax/swarm-cronjob
|
||||||
watch_repo: true
|
watch_repo: true
|
||||||
|
sort_tags: semver
|
||||||
include_tags:
|
include_tags:
|
||||||
- ^1\.2\..*
|
- ^1\.2\..*
|
||||||
- name: docker.io/portainer/portainer
|
- name: docker.io/portainer/portainer
|
||||||
watch_repo: true
|
watch_repo: true
|
||||||
max_tags: 10
|
max_tags: 10
|
||||||
|
sort_tags: reverse
|
||||||
include_tags:
|
include_tags:
|
||||||
- ^(0|[1-9]\d*)\..*
|
- ^(0|[1-9]\d*)\..*
|
||||||
- name: traefik
|
- name: traefik
|
||||||
watch_repo: true
|
watch_repo: true
|
||||||
|
sort_tags: default
|
||||||
- name: alpine
|
- name: alpine
|
||||||
platform:
|
platform:
|
||||||
os: linux
|
os: linux
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/containerd/containerd/platforms"
|
"github.com/containerd/containerd/platforms"
|
||||||
"github.com/crazy-max/diun/v4/internal/model"
|
"github.com/crazy-max/diun/v4/internal/model"
|
||||||
|
"github.com/crazy-max/diun/v4/pkg/registry"
|
||||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
@@ -45,6 +46,17 @@ func (c *Client) listFileImage() []model.Image {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check SortType
|
||||||
|
if item.SortTags == "" {
|
||||||
|
item.SortTags = registry.SortTagReverse
|
||||||
|
}
|
||||||
|
if !item.SortTags.Valid() {
|
||||||
|
c.logger.Error().
|
||||||
|
Str("file", file).
|
||||||
|
Str("img_name", item.Name).
|
||||||
|
Msgf("unknown sort tags type %q", item.SortTags)
|
||||||
|
}
|
||||||
|
|
||||||
// Check Platform
|
// Check Platform
|
||||||
if item.Platform != (model.ImagePlatform{}) {
|
if item.Platform != (model.ImagePlatform{}) {
|
||||||
_, err = platforms.Parse(platforms.Format(ocispecs.Platform{
|
_, err = platforms.Parse(platforms.Format(ocispecs.Platform{
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ func TestParseImage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range testCases {
|
for _, tt := range testCases {
|
||||||
|
tt := tt
|
||||||
t.Run(tt.desc, func(t *testing.T) {
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
img, err := registry.ParseImage(tt.parseOpts)
|
img, err := registry.ParseImage(tt.parseOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -239,6 +240,7 @@ func TestHubLink(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range testCases {
|
for _, tt := range testCases {
|
||||||
|
tt := tt
|
||||||
t.Run(tt.desc, func(t *testing.T) {
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
img, err := registry.ParseImage(tt.parseOpts)
|
img, err := registry.ParseImage(tt.parseOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ func TestParseReference(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range testCases {
|
for _, tt := range testCases {
|
||||||
|
tt := tt
|
||||||
t.Run(tt.input, func(t *testing.T) {
|
t.Run(tt.input, func(t *testing.T) {
|
||||||
ref, err := registry.ParseReference(tt.input)
|
ref, err := registry.ParseReference(tt.input)
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ type Tags struct {
|
|||||||
type TagsOptions struct {
|
type TagsOptions struct {
|
||||||
Image Image
|
Image Image
|
||||||
Max int
|
Max int
|
||||||
|
Sort SortTag
|
||||||
Include []string
|
Include []string
|
||||||
Exclude []string
|
Exclude []string
|
||||||
}
|
}
|
||||||
@@ -43,6 +44,9 @@ func (c *Client) Tags(opts TagsOptions) (*Tags, error) {
|
|||||||
Total: len(tags),
|
Total: len(tags),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort tags
|
||||||
|
tags = SortTags(tags, opts.Sort)
|
||||||
|
|
||||||
// Filter
|
// Filter
|
||||||
for _, tag := range tags {
|
for _, tag := range tags {
|
||||||
if !utl.IsIncluded(tag, opts.Include) {
|
if !utl.IsIncluded(tag, opts.Include) {
|
||||||
@@ -55,12 +59,6 @@ func (c *Client) Tags(opts TagsOptions) (*Tags, error) {
|
|||||||
res.List = append(res.List, tag)
|
res.List = append(res.List, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reverse order (latest tags first)
|
|
||||||
for i := len(res.List)/2 - 1; i >= 0; i-- {
|
|
||||||
opp := len(res.List) - 1 - i
|
|
||||||
res.List[i], res.List[opp] = res.List[opp], res.List[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Max > 0 && len(res.List) >= opts.Max {
|
if opts.Max > 0 && len(res.List) >= opts.Max {
|
||||||
res.List = res.List[:opts.Max]
|
res.List = res.List[:opts.Max]
|
||||||
}
|
}
|
||||||
|
|||||||
79
pkg/registry/tags_sort.go
Normal file
79
pkg/registry/tags_sort.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/mod/semver"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SortTags sorts tags list
|
||||||
|
func SortTags(tags []string, sortTag SortTag) []string {
|
||||||
|
switch sortTag {
|
||||||
|
case SortTagReverse:
|
||||||
|
for i := len(tags)/2 - 1; i >= 0; i-- {
|
||||||
|
opp := len(tags) - 1 - i
|
||||||
|
tags[i], tags[opp] = tags[opp], tags[i]
|
||||||
|
}
|
||||||
|
return tags
|
||||||
|
case SortTagLexicographical:
|
||||||
|
sort.Strings(tags)
|
||||||
|
return tags
|
||||||
|
case SortTagSemver:
|
||||||
|
semverIsh := func(s string) string {
|
||||||
|
if semver.IsValid(s) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
if vt := fmt.Sprintf("v%s", s); semver.IsValid(vt) {
|
||||||
|
return vt
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
sort.Slice(tags, func(i, j int) bool {
|
||||||
|
if c := semver.Compare(semverIsh(tags[i]), semverIsh(tags[j])); c > 0 {
|
||||||
|
return true
|
||||||
|
} else if c < 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return strings.Count(tags[i], ".") > strings.Count(tags[j], ".")
|
||||||
|
})
|
||||||
|
return tags
|
||||||
|
default:
|
||||||
|
return tags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortTag holds sort tag type
|
||||||
|
type SortTag string
|
||||||
|
|
||||||
|
// SortTag constants
|
||||||
|
const (
|
||||||
|
SortTagDefault = SortTag("default")
|
||||||
|
SortTagReverse = SortTag("reverse")
|
||||||
|
SortTagLexicographical = SortTag("lexicographical")
|
||||||
|
SortTagSemver = SortTag("semver")
|
||||||
|
)
|
||||||
|
|
||||||
|
// SortTagTypes is the list of available sort tag types
|
||||||
|
var SortTagTypes = []SortTag{
|
||||||
|
SortTagDefault,
|
||||||
|
SortTagReverse,
|
||||||
|
SortTagLexicographical,
|
||||||
|
SortTagSemver,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid checks sort tag type is valid
|
||||||
|
func (st *SortTag) Valid() bool {
|
||||||
|
return st.OneOf(SortTagTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OneOf checks if sort type is one of the values in the list
|
||||||
|
func (st *SortTag) OneOf(stl []SortTag) bool {
|
||||||
|
for _, n := range stl {
|
||||||
|
if n == *st {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -27,3 +27,207 @@ func TestTags(t *testing.T) {
|
|||||||
assert.True(t, tags.Total > 0)
|
assert.True(t, tags.Total > 0)
|
||||||
assert.True(t, len(tags.List) > 0)
|
assert.True(t, len(tags.List) > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTagsSort(t *testing.T) {
|
||||||
|
repotags := []string{
|
||||||
|
"0.1.0",
|
||||||
|
"0.4.0",
|
||||||
|
"3.0.0-beta.1",
|
||||||
|
"3.0.0-beta.3",
|
||||||
|
"3.0.0-beta.4",
|
||||||
|
"4",
|
||||||
|
"4.0.0",
|
||||||
|
"4.0.0-beta.1",
|
||||||
|
"4.1.0",
|
||||||
|
"4.1.1",
|
||||||
|
"4.10.0",
|
||||||
|
"4.11.0",
|
||||||
|
"4.12.0",
|
||||||
|
"4.13.0",
|
||||||
|
"4.14.0",
|
||||||
|
"4.19.0",
|
||||||
|
"4.2.0",
|
||||||
|
"4.20",
|
||||||
|
"4.20.0",
|
||||||
|
"4.20.1",
|
||||||
|
"4.21",
|
||||||
|
"4.21.0",
|
||||||
|
"4.3.0",
|
||||||
|
"4.3.1",
|
||||||
|
"4.4.0",
|
||||||
|
"4.6.1",
|
||||||
|
"4.7.0",
|
||||||
|
"4.8.0",
|
||||||
|
"4.8.1",
|
||||||
|
"4.9.0",
|
||||||
|
"edge",
|
||||||
|
"latest",
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
sortTag registry.SortTag
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "sort default",
|
||||||
|
sortTag: registry.SortTagDefault,
|
||||||
|
expected: []string{
|
||||||
|
"0.1.0",
|
||||||
|
"0.4.0",
|
||||||
|
"3.0.0-beta.1",
|
||||||
|
"3.0.0-beta.3",
|
||||||
|
"3.0.0-beta.4",
|
||||||
|
"4",
|
||||||
|
"4.0.0",
|
||||||
|
"4.0.0-beta.1",
|
||||||
|
"4.1.0",
|
||||||
|
"4.1.1",
|
||||||
|
"4.10.0",
|
||||||
|
"4.11.0",
|
||||||
|
"4.12.0",
|
||||||
|
"4.13.0",
|
||||||
|
"4.14.0",
|
||||||
|
"4.19.0",
|
||||||
|
"4.2.0",
|
||||||
|
"4.20",
|
||||||
|
"4.20.0",
|
||||||
|
"4.20.1",
|
||||||
|
"4.21",
|
||||||
|
"4.21.0",
|
||||||
|
"4.3.0",
|
||||||
|
"4.3.1",
|
||||||
|
"4.4.0",
|
||||||
|
"4.6.1",
|
||||||
|
"4.7.0",
|
||||||
|
"4.8.0",
|
||||||
|
"4.8.1",
|
||||||
|
"4.9.0",
|
||||||
|
"edge",
|
||||||
|
"latest",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sort lexicographical",
|
||||||
|
sortTag: registry.SortTagLexicographical,
|
||||||
|
expected: []string{
|
||||||
|
"0.1.0",
|
||||||
|
"0.4.0",
|
||||||
|
"3.0.0-beta.1",
|
||||||
|
"3.0.0-beta.3",
|
||||||
|
"3.0.0-beta.4",
|
||||||
|
"4",
|
||||||
|
"4.0.0",
|
||||||
|
"4.0.0-beta.1",
|
||||||
|
"4.1.0",
|
||||||
|
"4.1.1",
|
||||||
|
"4.10.0",
|
||||||
|
"4.11.0",
|
||||||
|
"4.12.0",
|
||||||
|
"4.13.0",
|
||||||
|
"4.14.0",
|
||||||
|
"4.19.0",
|
||||||
|
"4.2.0",
|
||||||
|
"4.20",
|
||||||
|
"4.20.0",
|
||||||
|
"4.20.1",
|
||||||
|
"4.21",
|
||||||
|
"4.21.0",
|
||||||
|
"4.3.0",
|
||||||
|
"4.3.1",
|
||||||
|
"4.4.0",
|
||||||
|
"4.6.1",
|
||||||
|
"4.7.0",
|
||||||
|
"4.8.0",
|
||||||
|
"4.8.1",
|
||||||
|
"4.9.0",
|
||||||
|
"edge",
|
||||||
|
"latest",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sort reverse",
|
||||||
|
sortTag: registry.SortTagReverse,
|
||||||
|
expected: []string{
|
||||||
|
"latest",
|
||||||
|
"edge",
|
||||||
|
"4.9.0",
|
||||||
|
"4.8.1",
|
||||||
|
"4.8.0",
|
||||||
|
"4.7.0",
|
||||||
|
"4.6.1",
|
||||||
|
"4.4.0",
|
||||||
|
"4.3.1",
|
||||||
|
"4.3.0",
|
||||||
|
"4.21.0",
|
||||||
|
"4.21",
|
||||||
|
"4.20.1",
|
||||||
|
"4.20.0",
|
||||||
|
"4.20",
|
||||||
|
"4.2.0",
|
||||||
|
"4.19.0",
|
||||||
|
"4.14.0",
|
||||||
|
"4.13.0",
|
||||||
|
"4.12.0",
|
||||||
|
"4.11.0",
|
||||||
|
"4.10.0",
|
||||||
|
"4.1.1",
|
||||||
|
"4.1.0",
|
||||||
|
"4.0.0-beta.1",
|
||||||
|
"4.0.0",
|
||||||
|
"4",
|
||||||
|
"3.0.0-beta.4",
|
||||||
|
"3.0.0-beta.3",
|
||||||
|
"3.0.0-beta.1",
|
||||||
|
"0.4.0",
|
||||||
|
"0.1.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sort semver",
|
||||||
|
sortTag: registry.SortTagSemver,
|
||||||
|
expected: []string{
|
||||||
|
"4.21.0",
|
||||||
|
"4.21",
|
||||||
|
"4.20.1",
|
||||||
|
"4.20.0",
|
||||||
|
"4.20",
|
||||||
|
"4.19.0",
|
||||||
|
"4.14.0",
|
||||||
|
"4.13.0",
|
||||||
|
"4.12.0",
|
||||||
|
"4.11.0",
|
||||||
|
"4.10.0",
|
||||||
|
"4.9.0",
|
||||||
|
"4.8.1",
|
||||||
|
"4.8.0",
|
||||||
|
"4.7.0",
|
||||||
|
"4.6.1",
|
||||||
|
"4.4.0",
|
||||||
|
"4.3.1",
|
||||||
|
"4.3.0",
|
||||||
|
"4.2.0",
|
||||||
|
"4.1.1",
|
||||||
|
"4.1.0",
|
||||||
|
"4.0.0",
|
||||||
|
"4",
|
||||||
|
"4.0.0-beta.1",
|
||||||
|
"3.0.0-beta.4",
|
||||||
|
"3.0.0-beta.3",
|
||||||
|
"3.0.0-beta.1",
|
||||||
|
"0.4.0",
|
||||||
|
"0.1.0",
|
||||||
|
"edge",
|
||||||
|
"latest",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range testCases {
|
||||||
|
tt := tt
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
tags := registry.SortTags(repotags, tt.sortTag)
|
||||||
|
assert.Equal(t, tt.expected, tags)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user