diff --git a/.all-contributorsrc b/.all-contributorsrc index 4c07c48..270f462 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -5,6 +5,30 @@ "imageSize": 100, "commit": false, "contributors": [ + { + "login": "piksel", + "name": "nils måsén", + "avatar_url": "https://avatars2.githubusercontent.com/u/807383?v=4", + "profile": "https://piksel.se", + "contributions": [ + "code", + "doc", + "maintenance", + "review" + ] + }, + { + "login": "simskij", + "name": "Simon Aronsson", + "avatar_url": "https://avatars0.githubusercontent.com/u/1596025?v=4", + "profile": "http://simme.dev", + "contributions": [ + "code", + "doc", + "maintenance", + "review" + ] + }, { "login": "Codelica", "name": "James", @@ -273,18 +297,6 @@ "code" ] }, - { - "login": "simskij", - "name": "Simon Aronsson", - "avatar_url": "https://avatars0.githubusercontent.com/u/1596025?v=4", - "profile": "http://simme.dev", - "contributions": [ - "code", - "maintenance", - "review", - "doc" - ] - }, { "login": "Ansem93", "name": "Ansem93", @@ -508,16 +520,6 @@ "doc" ] }, - { - "login": "piksel", - "name": "nils måsén", - "avatar_url": "https://avatars2.githubusercontent.com/u/807383?v=4", - "profile": "https://piksel.se", - "contributions": [ - "doc", - "code" - ] - }, { "login": "arnested", "name": "Arne Jørgensen", @@ -841,6 +843,12 @@ "code" ] }, + { + "login": "andriibratanin", + "name": "Andrii Bratanin", + "avatar_url": "https://avatars.githubusercontent.com/u/20169213?v=4", + "profile": "https://github.com/andriibratanin" + }, { "login": "IAmTamal", "name": "Tamal Das ", @@ -858,6 +866,16 @@ "contributions": [ "doc" ] + }, + { + "login": "nothub", + "name": "Florian Hübner", + "avatar_url": "https://avatars.githubusercontent.com/u/48992448?v=4", + "profile": "http://hub.lol", + "contributions": [ + "doc", + "code" + ] } ], "contributorsPerLine": 7, diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 8f0c724..c479d05 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -44,7 +44,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -55,7 +55,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -69,4 +69,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 9204541..4818e54 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -20,11 +20,11 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: 1.18.x + go-version: 1.20.x - name: Build tplprev run: scripts/build-tplprev.sh - name: Setup python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.10' cache: 'pip' diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 819a554..f6866af 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -18,10 +18,10 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: 1.18.x + go-version: 1.20.x - uses: dominikh/staticcheck-action@ba605356b4b29a60e87ab9404b712f3461e566dc #v1.3.0 with: - version: "2022.1.1" + version: "2023.1.6" install-go: "false" # StaticCheck uses go v1.17 which does not support `any` test: name: Test @@ -29,7 +29,7 @@ jobs: fail-fast: false matrix: go-version: - - 1.18.x + - 1.20.x platform: - macos-latest - windows-latest @@ -43,7 +43,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: 1.18.x + go-version: 1.20.x - name: Run tests run: | go test -v -coverprofile coverage.out -covermode atomic ./... @@ -62,7 +62,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: 1.18.x + go-version: 1.20.x - name: Build uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 #v3 with: diff --git a/.github/workflows/release-dev.yaml b/.github/workflows/release-dev.yaml index df97308..95ee68d 100644 --- a/.github/workflows/release-dev.yaml +++ b/.github/workflows/release-dev.yaml @@ -11,10 +11,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v4 with: - go-version: 1.18 + go-version: 1.20.x - name: Build run: ./build.sh test: @@ -24,7 +26,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: 1.18 + go-version: 1.20.x - name: Test run: go test -v -coverprofile coverage.out -covermode atomic ./... - name: Publish coverage diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b6ef374..370d395 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,9 +2,7 @@ name: Release (Production) on: workflow_dispatch: {} - release: - types: - - created + push: tags: - 'v[0-9]+.[0-9]+.[0-9]+' - '**/v[0-9]+.[0-9]+.[0-9]+' @@ -21,7 +19,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: 1.18.x + go-version: 1.20.x - uses: dominikh/staticcheck-action@ba605356b4b29a60e87ab9404b712f3461e566dc #v1.3.0 with: version: "2022.1.1" @@ -32,7 +30,7 @@ jobs: strategy: matrix: go-version: - - 1.18.x + - 1.20.x platform: - ubuntu-latest - macos-latest @@ -46,7 +44,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: 1.18.x + go-version: 1.20.x - name: Run tests run: | go test ./... -coverprofile coverage.out @@ -59,7 +57,7 @@ jobs: - lint env: CGO_ENABLED: 0 - TAG: ${{ github.event.release.tag_name }} + TAG: ${{ github.ref_name }} steps: - name: Checkout uses: actions/checkout@v4 @@ -68,7 +66,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: 1.18.x + go-version: 1.20.x - name: Login to Docker Hub uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc #v2 with: @@ -93,7 +91,7 @@ jobs: echo '{"experimental": "enabled"}' > ~/.docker/config.json - name: Create manifest for version run: | - export DH_TAG=$(echo $TAG | sed 's/^v*//') + export DH_TAG=$(git tag --points-at HEAD | sed 's/^v*//') docker manifest create \ containrrr/watchtower:$DH_TAG \ containrrr/watchtower:amd64-$DH_TAG \ diff --git a/README.md b/README.md index ba6642d..f550302 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ $ docker run --detach \ containrrr/watchtower ``` +Watchtower is intended to be used in homelabs, media centers, local dev environments, and similar. We do **not** recommend using Watchtower in a commercial or production environment. If that is you, you should be looking into using Kubernetes. If that feels like too big a step for you, please look into solutions like [MicroK8s](https://microk8s.io/) and [k3s](https://k3s.io/) that take away a lot of the toil of running a Kubernetes cluster. + ## Documentation The full documentation is available at https://containrrr.dev/watchtower. @@ -44,75 +46,75 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d + + - - + + - - + + - - + + - - + + - - + - + - + - @@ -162,6 +164,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d + +
nils måsén
nils måsén

💻 📖 🚧 👀
Simon Aronsson
Simon Aronsson

💻 📖 🚧 👀
James
James

⚠️ 🤔
Florian
Florian

👀 📖
Brian DeHamer
Brian DeHamer

💻 🚧
Ross Cadogan
Ross Cadogan

💻
stffabi
stffabi

💻 🚧
Austin
Austin

📖
David Gardner
David Gardner

👀 📖
Austin
Austin

📖
David Gardner
David Gardner

👀 📖
Tanguy ⧓ Herrmann
Tanguy ⧓ Herrmann

💻
Rodrigo Damazio Bovendorp
Rodrigo Damazio Bovendorp

💻 📖
Ryan Kuba
Ryan Kuba

🚇
cnrmck
cnrmck

📖
Harry Walter
Harry Walter

💻
Robotex
Robotex

📖
Gerald Pape
Gerald Pape

📖
Robotex
Robotex

📖
Gerald Pape
Gerald Pape

📖
fomk
fomk

💻
Sven Gottwald
Sven Gottwald

🚇
techknowlogick
techknowlogick

💻
waja
waja

📖
Scott Albertson
Scott Albertson

📖
Jason Huddleston
Jason Huddleston

📖
Napster
Napster

💻
Jason Huddleston
Jason Huddleston

📖
Napster
Napster

💻
Maxim
Maxim

💻 📖
Max Schmitt
Max Schmitt

📖
cron410
cron410

📖
Paulo Henrique
Paulo Henrique

📖
Kaleb Elwert
Kaleb Elwert

📖
Bill Butler
Bill Butler

📖
Mario Tacke
Mario Tacke

💻
Bill Butler
Bill Butler

📖
Mario Tacke
Mario Tacke

💻
Mark Woodbridge
Mark Woodbridge

💻
Simon Aronsson
Simon Aronsson

💻 🚧 👀 📖
Ansem93
Ansem93

📖
Luka Peschke
Luka Peschke

💻 📖
Zois Pagoulatos
Zois Pagoulatos

💻 👀 🚧
Alexandre Menif
Alexandre Menif

💻
Andrey
Andrey

📖
Andrey
Andrey

📖
Armando Lüscher
Armando Lüscher

📖
Ryan Budke
Ryan Budke

📖
Kaloyan Raev
Kaloyan Raev

💻 ⚠️
sixth
sixth

📖
Gina Häußge
Gina Häußge

💻
Max H.
Max H.

💻
Jungkook Park
Jungkook Park

📖
Jungkook Park
Jungkook Park

📖
Jan Kristof Nidzwetzki
Jan Kristof Nidzwetzki

📖
lukas
lukas

💻
Ameya Shenoy
Ameya Shenoy

💻
Raymon de Looff
Raymon de Looff

💻
John Clayton
John Clayton

💻
Germs2004
Germs2004

📖
Lukas Willburger
Lukas Willburger

💻
Lukas Willburger
Lukas Willburger

💻
Oliver Cervera
Oliver Cervera

📖
Victor Moura
Victor Moura

⚠️ 💻 📖
Maximilian Brandau
Maximilian Brandau

💻 ⚠️
Andrew
Andrew

📖
sixcorners
sixcorners

📖
nils måsén
nils måsén

📖 💻
Arne Jørgensen
Arne Jørgensen

⚠️ 👀
guangwu
guangwu

📖
Florian Hübner
Florian Hübner

📖 💻

Andrii Bratanin

📖
diff --git a/cmd/root.go b/cmd/root.go index 90c338c..eef13ce 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -29,18 +29,20 @@ import ( ) var ( - client container.Client - scheduleSpec string - cleanup bool - noRestart bool - monitorOnly bool - enableLabel bool - notifier t.Notifier - timeout time.Duration - lifecycleHooks bool - rollingRestart bool - scope string - labelPrecedence bool + client container.Client + scheduleSpec string + cleanup bool + noRestart bool + noPull bool + monitorOnly bool + enableLabel bool + disableContainers []string + notifier t.Notifier + timeout time.Duration + lifecycleHooks bool + rollingRestart bool + scope string + labelPrecedence bool ) var rootCmd = NewRootCommand() @@ -93,6 +95,7 @@ func PreRun(cmd *cobra.Command, _ []string) { } enableLabel, _ = f.GetBool("label-enable") + disableContainers, _ = f.GetStringSlice("disable-containers") lifecycleHooks, _ = f.GetBool("enable-lifecycle-hooks") rollingRestart, _ = f.GetBool("rolling-restart") scope, _ = f.GetString("scope") @@ -108,7 +111,7 @@ func PreRun(cmd *cobra.Command, _ []string) { log.Fatal(err) } - noPull, _ := f.GetBool("no-pull") + noPull, _ = f.GetBool("no-pull") includeStopped, _ := f.GetBool("include-stopped") includeRestarting, _ := f.GetBool("include-restarting") reviveStopped, _ := f.GetBool("revive-stopped") @@ -120,7 +123,6 @@ func PreRun(cmd *cobra.Command, _ []string) { } client = container.NewClient(container.ClientOptions{ - PullImages: !noPull, IncludeStopped: includeStopped, ReviveStopped: reviveStopped, RemoveVolumes: removeVolumes, @@ -134,7 +136,7 @@ func PreRun(cmd *cobra.Command, _ []string) { // Run is the main execution flow of the command func Run(c *cobra.Command, names []string) { - filter, filterDesc := filters.BuildFilter(names, enableLabel, scope) + filter, filterDesc := filters.BuildFilter(names, disableContainers, enableLabel, scope) runOnce, _ := c.PersistentFlags().GetBool("run-once") enableUpdateAPI, _ := c.PersistentFlags().GetBool("http-api-update") enableMetricsAPI, _ := c.PersistentFlags().GetBool("http-api-metrics") @@ -185,7 +187,7 @@ func Run(c *cobra.Command, names []string) { metrics.RegisterScan(metric) }, updateLock) httpAPI.RegisterFunc(updateHandler.Path, updateHandler.Handle) - // If polling isn't enabled the scheduler is never started and + // If polling isn't enabled the scheduler is never started, and // we need to trigger the startup messages manually. if !unblockHTTPAPI { writeStartupMessage(c, time.Time{}, filterDesc) @@ -365,6 +367,7 @@ func runUpdatesWithNotifications(filter t.Filter) *metrics.Metric { LifecycleHooks: lifecycleHooks, RollingRestart: rollingRestart, LabelPrecedence: labelPrecedence, + NoPull: noPull, } result, err := actions.Update(client, updateParams) if err != nil { diff --git a/dockerfiles/Dockerfile b/dockerfiles/Dockerfile index b6f8676..2fc571d 100644 --- a/dockerfiles/Dockerfile +++ b/dockerfiles/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM alpine:3.18.3 as alpine +FROM --platform=$BUILDPLATFORM alpine:3.19.0 as alpine RUN apk add --no-cache \ ca-certificates \ diff --git a/docs/arguments.md b/docs/arguments.md index a467538..d7ed0b0 100644 --- a/docs/arguments.md +++ b/docs/arguments.md @@ -230,6 +230,19 @@ __Do not__ Monitor and update containers that have `com.centurylinklabs.watchtow no `--label-enable` argument is passed. Note that only one or the other (targeting by enable label) can be used at the same time to target containers. +## Filter by disabling specific container names +Monitor and update containers whose names are not in a given set of names. + +This can be used to exclude specific containers, when setting labels is not an option. +The listed containers will be excluded even if they have the enable filter set to true. + +```text + Argument: --disable-containers, -x +Environment Variable: WATCHTOWER_DISABLE_CONTAINERS + Type: Comma- or space-separated string list + Default: "" +``` + ## Without updating containers Will only monitor for new images, send notifications and invoke the [pre-check/post-check hooks](https://containrrr.dev/watchtower/lifecycle-hooks/), but will __not__ update the @@ -346,6 +359,11 @@ Environment Variable: WATCHTOWER_HTTP_API_PERIODIC_POLLS Update containers that have a `com.centurylinklabs.watchtower.scope` label set with the same value as the given argument. This enables [running multiple instances](https://containrrr.dev/watchtower/running-multiple-instances). +!!! note "Filter by lack of scope" + If you want other instances of watchtower to ignore the scoped containers, set this argument to `none`. + When omitted, watchtower will update all containers regardless of scope. + + ```text Argument: --scope Environment Variable: WATCHTOWER_SCOPE diff --git a/docs/container-selection.md b/docs/container-selection.md index 4b6facd..8327c66 100644 --- a/docs/container-selection.md +++ b/docs/container-selection.md @@ -58,6 +58,7 @@ If instead you want to [only include containers with the enable label](https://c If you wish to create a monitoring scope, you will need to [run multiple instances and set a scope for each of them](https://containrrr.github.io/watchtower/running-multiple-instances). Watchtower filters running containers by testing them against each configured criteria. A container is monitored if all criteria are met. For example: + - If a container's name is on the monitoring name list (not empty `--name` argument) but it is not enabled (_centurylinklabs.watchtower.enable=false_), it won't be monitored; - If a container's name is not on the monitoring name list (not empty `--name` argument), even if it is enabled (_centurylinklabs.watchtower.enable=true_ and `--label-enable` flag is set), it won't be monitored; diff --git a/docs/http-api-mode.md b/docs/http-api-mode.md index 2cf082a..69812bb 100644 --- a/docs/http-api-mode.md +++ b/docs/http-api-mode.md @@ -35,3 +35,11 @@ Notice that there is an environment variable named WATCHTOWER_HTTP_API_TOKEN. To ```bash curl -H "Authorization: Bearer mytoken" localhost:8080/v1/update ``` + +--- + +In order to update only certain images, the image names can be provided as URL query parameters. The following `curl` command would trigger an update for the images `foo/bar` and `foo/baz`: + +```bash +curl -H "Authorization: Bearer mytoken" localhost:8080/v1/update?image=foo/bar,foo/baz +``` diff --git a/docs/notifications.md b/docs/notifications.md index 3042919..d5da4fe 100644 --- a/docs/notifications.md +++ b/docs/notifications.md @@ -24,7 +24,7 @@ system, [logrus](http://github.com/sirupsen/logrus). - `--notification-skip-title` (env. `WATCHTOWER_NOTIFICATION_SKIP_TITLE`): Do not pass the title param to notifications. This will not pass a dynamic title override to notification services. If no title is configured for the service, it will remove the title all together. - `--notification-log-stdout` (env. `WATCHTOWER_NOTIFICATION_LOG_STDOUT`): Enable output from `logger://` shoutrrr service to stdout. -## [shoutrrr](https://github.com/containrrr/shoutrrr) notifications +## [Shoutrrr](https://github.com/containrrr/shoutrrr) notifications To send notifications via shoutrrr, the following command-line options, or their corresponding environment variables, can be set: @@ -57,6 +57,10 @@ outputs timestamp and log level. custom format. i.e., The day of the year has to be 1, the month has to be 2 (february), the hour 3 (or 15 for 24h time) etc. +!!! note "Skipping notifications" + To skip sending notifications that do not contain any information, you can wrap your template with `{{if .}}` and `{{end}}`. + + Example: ```bash diff --git a/docs/private-registries.md b/docs/private-registries.md index 68fbe84..5367a8c 100644 --- a/docs/private-registries.md +++ b/docs/private-registries.md @@ -124,7 +124,7 @@ in a volume that may be mounted onto your watchtower container. 1. Create the Dockerfile (contents below): ```Dockerfile - FROM golang:1.17 + FROM golang:1.20 ENV GO111MODULE off ENV CGO_ENABLED 0 diff --git a/docs/running-multiple-instances.md b/docs/running-multiple-instances.md index 3899095..5a82c80 100644 --- a/docs/running-multiple-instances.md +++ b/docs/running-multiple-instances.md @@ -1,10 +1,11 @@ By default, Watchtower will clean up other instances and won't allow multiple instances running on the same Docker host or swarm. It is possible to override this behavior by defining a [scope](https://containrrr.github.io/watchtower/arguments/#filter_by_scope) to each running instance. -Notice that: -- Multiple instances can't run with the same scope; -- An instance without a scope will clean up other running instances, even if they have a defined scope; +!!! note + - Multiple instances can't run with the same scope; + - An instance without a scope will clean up other running instances, even if they have a defined scope; + - Supplying `none` as the scope will treat `com.centurylinklabs.watchtower.scope=none`, `com.centurylinklabs.watchtower.scope=` and the lack of a `com.centurylinklabs.watchtower.scope` label as the scope `none`. This effectly enables you to run both scoped and unscoped watchtower instances on the same machine. -To define an instance monitoring scope, use the `--scope` argument or the `WATCHTOWER_SCOPE` environment variable on startup and set the _com.centurylinklabs.watchtower.scope_ label with the same value for the containers you want to include in this instance's scope (including the instance itself). +To define an instance monitoring scope, use the `--scope` argument or the `WATCHTOWER_SCOPE` environment variable on startup and set the `com.centurylinklabs.watchtower.scope` label with the same value for the containers you want to include in this instance's scope (including the instance itself). For example, in a Docker Compose config file: @@ -12,16 +13,29 @@ For example, in a Docker Compose config file: version: '3' services: - app-monitored-by-watchtower: + app-with-scope: image: myapps/monitored-by-watchtower - labels: - - "com.centurylinklabs.watchtower.scope=myscope" + labels: [ "com.centurylinklabs.watchtower.scope=myscope" ] - watchtower: + scoped-watchtower: image: containrrr/watchtower - volumes: - - /var/run/docker.sock:/var/run/docker.sock + volumes: [ "/var/run/docker.sock:/var/run/docker.sock" ] command: --interval 30 --scope myscope - labels: - - "com.centurylinklabs.watchtower.scope=myscope" + labels: [ "com.centurylinklabs.watchtower.scope=myscope" ] + + unscoped-app-a: + image: myapps/app-a + + unscoped-app-b: + image: myapps/app-b + labels: [ "com.centurylinklabs.watchtower.scope=none" ] + + unscoped-app-c: + image: myapps/app-b + labels: [ "com.centurylinklabs.watchtower.scope=" ] + + unscoped-watchtower: + image: containrrr/watchtower + volumes: [ "/var/run/docker.sock:/var/run/docker.sock" ] + command: --interval 30 --scope none ``` diff --git a/docs/template-preview.md b/docs/template-preview.md index 3ae4321..3d99ce9 100644 --- a/docs/template-preview.md +++ b/docs/template-preview.md @@ -40,7 +40,7 @@ } #tplprev button { border-radius: 0.1rem; - color: var(--md-typeset-color); + color: var(--md-primary-bg-color); background-color: var(--md-primary-fg-color); flex:1; min-width: 12ch; @@ -78,6 +78,8 @@ flex:1; width:100% } + #result b {color: var(--md-code-hl-special-color)} + #result i {color: var(--md-code-hl-keyword-color)} #tplprev .loading { position: absolute; inset: 0; @@ -90,12 +92,14 @@ -
+
loading wasm...
-