Merge branch 'master' into AGDNS-3684-fix-tls-status

all: merge changes from master;
This commit is contained in:
Maksim Kazantsev 2026-02-25 18:20:38 +03:00
commit 94ed08dcb3
13 changed files with 485 additions and 133 deletions

View file

@ -9,14 +9,21 @@ The format is based on [*Keep a Changelog*](https://keepachangelog.com/en/1.0.0/
<!--
## [v0.108.0] TBA
## [v0.107.72] - 2026-02-12 (APPROX.)
## [v0.107.73] - 2026-03-02 (APPROX.)
See also the [v0.107.72 GitHub milestone][ms-v0.107.72].
See also the [v0.107.73 GitHub milestone][ms-v0.107.73].
[ms-v0.107.72]: https://github.com/AdguardTeam/AdGuardHome/milestone/107?closed=1
[ms-v0.107.73]: https://github.com/AdguardTeam/AdGuardHome/milestone/108?closed=1
NOTE: Add new changes BELOW THIS COMMENT.
-->
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->
## [v0.107.72] - 2026-02-19
See also the [v0.107.72 GitHub milestone][ms-v0.107.72].
### Security
@ -24,7 +31,7 @@ NOTE: Add new changes BELOW THIS COMMENT.
### Added
- The TLS certificate and key files are now being tracked for updates, which trigger a reload ([#3962]).
- AdGuard Home now tracks the TLS certificate and key files for updates and reloads them after any updates are detected ([#3962]).
- New query parameter `recent` in `GET /control/stats/` defines statistics lookback period in millieseconds. See `openapi/openapi.yaml` for details.
@ -76,10 +83,7 @@ In this release, the schema version has changed from 32 to 33.
[#8237]: https://github.com/AdguardTeam/AdGuardHome/issues/8237
[go-1.25.7]: https://groups.google.com/g/golang-announce/c/K09ubi9FQFk
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->
[ms-v0.107.72]: https://github.com/AdguardTeam/AdGuardHome/milestone/107?closed=1
## [v0.107.71] - 2025-12-08
@ -3482,11 +3486,12 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
[ms-v0.104.2]: https://github.com/AdguardTeam/AdGuardHome/milestone/28?closed=1
<!--
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.72...HEAD
[v0.107.72]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.71...v0.107.72
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.73...HEAD
[v0.107.73]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.72...v0.107.73
-->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.71...HEAD
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.72...HEAD
[v0.107.72]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.71...v0.107.72
[v0.107.71]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.70...v0.107.71
[v0.107.70]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.69...v0.107.70
[v0.107.69]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.68...v0.107.69

View file

@ -5,9 +5,12 @@
'key': 'AHBRTSPECS'
'name': 'AdGuard Home - Build and run tests'
'variables':
# This variable is used to override Docker caching, for example to rerun a
# flaky test suite.
'cacheBuster': '0'
'channel': 'development'
'dockerFrontend': 'adguard/home-js-builder:4.0'
'dockerGo': 'adguard/go-builder:1.25.7--1'
'channel': 'development'
'stages':
- 'Tests':
@ -36,10 +39,8 @@
- 'Test e2e'
'Test frontend':
'docker':
'image': '${bamboo.dockerFrontend}'
'volumes':
'${system.NPM_DIR}': '${bamboo.cacheNpm}'
'final-tasks':
- 'clean'
'key': 'JSTEST'
'other':
'clean-working-dir': true
@ -54,19 +55,18 @@
set -e -f -u -x
make VERBOSE=1 js-deps js-typecheck js-lint js-test
'final-tasks':
- 'clean'
'requirements':
- 'golang': true
docker info
docker build \
--build-arg "BASE_IMAGE=${bamboo.dockerFrontend}" \
--build-arg "CACHE_BUSTER=${bamboo_cacheBuster}" \
--output '.' \
--progress 'plain' \
--target 'tester' \
-f ./docker/frontend.Dockerfile \
.
# TODO(e.burkov): Add the linting stage for markdown docs and shell scripts.
'Test backend':
'docker':
'image': '${bamboo.dockerGo}'
'volumes':
'${system.GO_CACHE_DIR}': '${bamboo.cacheGo}'
'${system.GO_PKG_CACHE_DIR}': '${bamboo.cacheGoPkg}'
'final-tasks':
- 'test-parser':
# The default pattern, '**/test-reports/*.xml', works, so don't set
@ -88,35 +88,21 @@
set -e -f -u -x
# Do not sort the commands below, as they are arranged
# intentionally to keep faster commands first.
make \
GOMAXPROCS=1 \
VERBOSE=1 \
go-deps \
md-lint \
sh-lint \
txt-lint \
go-lint \
;
docker info
make \
TEST_REPORTS_DIR="./test-reports/" \
VERBOSE=1 \
go-test \
;
docker build \
--build-arg "BASE_IMAGE=${bamboo_dockerGo}" \
--build-arg "CACHE_BUSTER=${bamboo_cacheBuster}" \
--output '.' \
--progress 'plain' \
--target 'tester-exporter' \
-f ./docker/ci.Dockerfile \
.
exit_code="$(cat ./test-reports/test-exit-code.txt)"
readonly exit_code
make VERBOSE=1 \
go-fuzz \
go-bench \
;
exit "$exit_code"
'requirements':
- 'golang': true
'Build frontend':
'artifacts':
@ -124,10 +110,6 @@
'pattern': 'build/**'
'shared': true
'required': true
'docker':
'image': '${bamboo.dockerFrontend}'
'volumes':
'${system.NPM_DIR}': '${bamboo.cacheNpm}'
'key': 'BF'
'other':
'clean-working-dir': true
@ -142,11 +124,16 @@
set -e -f -u -x
make\
VERBOSE=1\
js-deps js-build
'requirements':
- 'golang': true
docker info
docker build \
--build-arg "BASE_IMAGE=${bamboo.dockerFrontend}" \
--build-arg "CACHE_BUSTER=${bamboo_cacheBuster}" \
--output '.' \
--progress 'plain' \
--target 'builder-exporter' \
-f ./docker/frontend.Dockerfile \
.
'Artifact':
'artifact-subscriptions':
@ -164,11 +151,6 @@
'pattern': 'dist/AdGuardHome_linux_amd64.tar.gz'
'shared': true
'required': true
'docker':
'image': '${bamboo.dockerGo}'
'volumes':
'${system.GO_CACHE_DIR}': '${bamboo.cacheGo}'
'${system.GO_PKG_CACHE_DIR}': '${bamboo.cacheGoPkg}'
'key': 'ART'
'other':
'clean-working-dir': true
@ -183,26 +165,28 @@
set -e -f -u -x
make\
ARCH="amd64"\
CHANNEL=${bamboo.channel}\
FRONTEND_PREBUILT=1\
OS="windows darwin linux"\
PARALLELISM=1\
SIGN=0\
VERBOSE=2\
build-release
'requirements':
- 'golang': true
docker info
docker build \
--build-arg "ARCH=amd64" \
--build-arg "BASE_IMAGE=${bamboo_dockerGo}" \
--build-arg "BRANCH=${bamboo_planRepository_branchName}" \
--build-arg "CACHE_BUSTER=${bamboo_cacheBuster}" \
--build-arg "CHANNEL=${bamboo_channel}" \
--build-arg "OS=windows darwin linux" \
--build-arg "REVISION=${bamboo_repository_revision_number}" \
--build-arg "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" \
--build-arg "VERSION=${bamboo_buildNumber}" \
--output '.' \
--progress 'plain' \
--target 'builder-exporter' \
-f ./docker/ci.Dockerfile \
.
'Test e2e':
'artifact-subscriptions':
- 'artifact': 'AdGuardHome_linux_amd64'
- 'artifact': 'AdGuardHome frontend'
'docker':
'image': '${bamboo.dockerFrontend}'
'volumes':
'${system.NPM_DIR}': '${bamboo.cacheNpm}'
'key': 'E2ETEST'
'other':
'clean-working-dir': true
@ -217,15 +201,20 @@
set -e -f -u -x
export CI=true
tar -xzf dist/AdGuardHome_linux_amd64.tar.gz -C /tmp
mv /tmp/AdGuardHome/AdGuardHome ./AdGuardHome
make VERBOSE=1 js-deps js-test-e2e
'requirements':
- 'golang': true
docker info
docker build \
--build-arg "BASE_IMAGE=${bamboo.dockerFrontend}" \
--build-arg "CACHE_BUSTER=${bamboo_cacheBuster}" \
--output '.' \
--progress 'plain' \
--target 'e2etester' \
-f ./docker/frontend.Dockerfile \
.
'branches':
'create': 'for-pull-request'

191
docker/ci.Dockerfile Normal file
View file

@ -0,0 +1,191 @@
# syntax=docker/dockerfile:1
# This comment is used to simplify checking local copies of the Dockerfile.
# Bump this number every time a significant change is made to this Dockerfile.
#
# AdGuard-Project-Version: 10
# Dockerfile guidelines:
#
# 1. Make sure that Docker correctly caches layers, on a second build attempt it
# must not run lint / test second time when it's not required.
#
# 2. Use BuildKit to improve the build performance (--mount=type=cache, etc).
#
# 3. Prefer using ARG instead of ENV when appropriate, as ARG does not create a
# layer in the final image. However, be careful with what you use ARG for.
# Also, prefer to give ARGs sensible default values.
#
# 4. Use --output and the export stage if you need to get any output on the host
# machine.
#
# NOTE: Only use --output with FROM scratch.
#
# 5. Use .dockerignore to prevent unnecessary files from being sent to the
# Docker daemon, which can invalidate the cache.
#
# 6. Add a CACHE_BUSTER argument to stages to be able to rerun the stages if
# needed. Keep it in sync with bamboo-specs/bamboo.yaml.
# NOTE: Keep in sync with bamboo-specs/bamboo.yaml.
ARG BASE_IMAGE=adguard/go-builder:1.25.7--1
# The dependencies stage is needed to install packages and tool dependencies.
# This is also where binaries like osslsigncode, which may be required for tests
# in some projects, must be installed.
#
# Use fake BRANCH and REVISION values to both prevent git calls and also not
# ruin the caching with ARGs.
#
# NOTE: Only ADD the files required to install the dependencies.
FROM "$BASE_IMAGE" AS dependencies
ADD Makefile go.mod go.sum /app/
ADD scripts /app/scripts
WORKDIR /app
RUN \
--mount=type=cache,id=gocache,target=/root/.cache/go-build \
--mount=type=cache,id=gopath,target=/go \
<<-'EOF'
set -e -f -u -x
make \
BRANCH='master' \
REVISION='0000000000000000000000000000000000000000' \
VERBOSE=1 \
go-env \
go-deps \
;
EOF
# The linter stage is separated from the tester stage to make catching test
# failures easier.
#
# Use fake BRANCH and REVISION values to both prevent git calls and also not
# ruin the caching with ARGs. IGNORE_NON_REPRODUCIBLE is set to 1 to make this
# stage reproducible even when linters that query external sources fail.
FROM dependencies AS linter
ADD . /app
WORKDIR /app
RUN \
--mount=type=cache,id=gocache,target=/root/.cache/go-build \
--mount=type=cache,id=gopath,target=/go \
<<-'EOF'
set -e -f -u -x
export GOMAXPROCS=2
make \
BRANCH='master' \
IGNORE_NON_REPRODUCIBLE='1' \
REVISION='0000000000000000000000000000000000000000' \
VERBOSE=1 \
go-lint \
md-lint \
sh-lint \
txt-lint \
;
EOF
# The test stage. TEST_REPORTS_DIR is set to create JUnit reports for the
# tester-exporter stage; run with --build-arg TEST_REPORTS_DIR='' if you don't
# need them on your machine.
#
# Use fake BRANCH and REVISION values to both prevent git calls and also not
# ruin the caching with ARGs.
#
# To run the tests:
#
# docker build --target tester -t 'app' .
#
# Projects that have go-bench and/or go-fuzz targets should add them here as
# well.
FROM linter AS tester
ARG CACHE_BUSTER=0
ARG TEST_REPORTS_DIR=/test-reports
RUN \
--mount=type=cache,id=gocache,target=/root/.cache/go-build \
--mount=type=cache,id=gopath,target=/go \
<<-'EOF'
set -e -f -u -x
export GOMAXPROCS=2
make \
BRANCH='master' \
REVISION='0000000000000000000000000000000000000000' \
TEST_REPORTS_DIR="$TEST_REPORTS_DIR" \
VERBOSE=1 \
go-test \
;
exit_code="$(cat "${TEST_REPORTS_DIR}/test-exit-code.txt")"
readonly exit_code
make \
BRANCH='master' \
REVISION='0000000000000000000000000000000000000000' \
VERBOSE=1 \
go-fuzz \
go-bench \
;
exit "$exit_code"
EOF
# tester-exporter exports the test result to the host machine so that it could
# parse and analyze it. This stage should only used in a CI.
#
# It the file test-report.xml, which contains test results in the JUnit format.
#
# Run the following command to export the test result:
#
# docker build \
# --output . \
# --progress plain \
# --target tester-exporter \
# .
FROM scratch AS tester-exporter
ARG CACHE_BUSTER=0
ARG TEST_REPORTS_DIR=/test-reports
COPY --from=tester "$TEST_REPORTS_DIR" "$TEST_REPORTS_DIR"
# The builder stage is used to build release artifacts. Real BRANCH and
# REVISION must be used here.
FROM dependencies AS builder
ARG ARCH=""
ARG BRANCH=master
ARG CACHE_BUSTER=0
ARG CHANNEL=development
ARG DEPLOY_SCRIPT_PATH=not/a/real/path
ARG GPG_KEY_PASSPHRASE
ARG GPG_SECRET_KEY
ARG OS=""
ARG REVISION=0000000000000000000000000000000000000000
ARG SIGNER_API_KEY
ARG SOURCE_DATE_EPOCH=0
ARG VERSION=""
ADD . /app
WORKDIR /app
RUN \
--mount=type=cache,id=gocache,target=/root/.cache/go-build \
--mount=type=cache,id=gopath,target=/go \
<<-'EOF'
set -e -f -u -x
make \
ARCH="${ARCH}" \
BRANCH="${BRANCH}" \
CHANNEL="${CHANNEL}" \
FRONTEND_PREBUILT=1 \
OS="${OS}" \
PARALLELISM=1 \
REVISION="${REVISION}" \
SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH" \
SIGN=0 \
VERBOSE=2 \
VERSION="${VERSION}" \
build-release \
;
EOF
# builder-exporter exports the build artifacts to the host machine so that they
# could be published. This stage should only be used in a CI.
FROM scratch AS builder-exporter
ARG CACHE_BUSTER=0
COPY --from=builder /app/dist /dist

View file

@ -0,0 +1,8 @@
# This comment is used to simplify checking local copies of the file. Bump this
# number every time a significant change is made to this file.
#
# AdGuard-Project-Version: 2
.git
/bin/
/tmp/
/client/

118
docker/frontend.Dockerfile Normal file
View file

@ -0,0 +1,118 @@
# syntax=docker/dockerfile:1
# This comment is used to simplify checking local copies of the Dockerfile.
# Bump this number every time a significant change is made to this Dockerfile.
#
# AdGuard-Project-Version: 11
# Dockerfile guidelines:
#
# 1. Make sure that Docker correctly caches layers, on a second build attempt it
# must not run lint / test second time when it's not required.
#
# 2. Use BuildKit to improve the build performance (--mount=type=cache, etc).
#
# 3. Prefer using ARG instead of ENV when appropriate, as ARG does not create a
# layer in the final image. However, be careful with what you use ARG for.
# Also, prefer to give ARGs sensible default values.
#
# 4. Use --output and the export stage if you need to get any output on the host
# machine.
#
# NOTE: Only use --output with FROM scratch.
#
# 5. Use .dockerignore to prevent unnecessary files from being sent to the
# Docker daemon, which can invalidate the cache.
#
# 6. Add a CACHE_BUSTER argument to stages to be able to rerun the stages if
# needed. Keep it in sync with bamboo-specs/bamboo.yaml.
# NOTE: Keep in sync with bamboo-specs/bamboo.yaml.
ARG BASE_IMAGE=adguard/home-js-builder:4.0
# The dependencies stage is needed to install packages and tool dependencies.
# This is also where binaries like osslsigncode, which may be required for tests
# in some projects, must be installed.
#
# NOTE: Only ADD the files required to install the dependencies.
FROM "$BASE_IMAGE" AS dependencies
ADD Makefile /app/
ADD scripts /app/scripts
ADD client /app/client
WORKDIR /app
RUN \
--mount=type=cache,id=npm-root-cache,target=/root/.npm \
<<-'EOF'
set -e -f -u -x
make \
VERBOSE=1 \
js-deps \
;
EOF
# The linter stage is separated from the tester stage to make catching test
# failures easier.
FROM dependencies AS linter
ARG CACHE_BUSTER=0
ADD . /app
WORKDIR /app
RUN \
--mount=type=cache,id=npm-root-cache,target=/root/.npm \
<<-'EOF'
set -e -f -u -x
make \
VERBOSE=1 \
js-typecheck \
js-lint \
;
EOF
# The test stage.
FROM linter AS tester
ARG CACHE_BUSTER=0
RUN \
--mount=type=cache,id=npm-root-cache,target=/root/.npm \
<<-'EOF'
set -e -f -u -x
make \
VERBOSE=1 \
js-test \
;
EOF
# The e2e test stage.
FROM dependencies AS e2etester
ARG CACHE_BUSTER=0
ADD . /app
WORKDIR /app
RUN \
--mount=type=cache,id=npm-root-cache,target=/root/.npm \
<<-'EOF'
set -e -f -u -x
make \
CI='true' \
VERBOSE=1 \
js-test-e2e \
;
EOF
# The builder stage.
FROM dependencies AS builder
ARG CACHE_BUSTER=0
ADD . /app
WORKDIR /app
RUN \
--mount=type=cache,id=npm-root-cache,target=/root/.npm \
<<-'EOF'
set -e -f -u -x
make \
VERBOSE=1 \
js-build \
;
EOF
# builder-exporter exports the build artifacts to the host machine so that they
# could be published. This stage should only be used in a CI.
FROM scratch AS builder-exporter
ARG CACHE_BUSTER=0
COPY --from=builder /app/build /build

View file

@ -0,0 +1,8 @@
# This comment is used to simplify checking local copies of the file. Bump this
# number every time a significant change is made to this file.
#
# AdGuard-Project-Version: 2
.git
/bin/
/tmp/
/client/node_modules

View file

@ -1,15 +1,12 @@
package ossvc
import (
"bytes"
"context"
"fmt"
"log/slog"
"os"
"path/filepath"
"runtime"
"strconv"
"syscall"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/golibs/errors"
@ -123,51 +120,8 @@ func (m *manager) Status(ctx context.Context, name ServiceName) (status Status,
var _ ReloadManager = (*manager)(nil)
// Reload implements the [ReloadManager] interface for *manager.
//
// TODO(e.burkov): On Windows just don't implement this interface.
func (m *manager) Reload(ctx context.Context, name ServiceName) (err error) {
if runtime.GOOS == "windows" {
return errors.ErrUnsupported
}
nameStr := string(name)
var pid int
pidFile := filepath.Join("/var", "run", nameStr+".pid")
data, err := os.ReadFile(pidFile)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("reading service pid file: %w", err)
}
pid, err = aghos.PIDByCommand(ctx, m.logger, nameStr, os.Getpid())
if err != nil {
return fmt.Errorf("finding process: %w", err)
}
} else {
parts := bytes.SplitN(data, []byte("\n"), 2)
if len(parts) == 0 {
return fmt.Errorf("parsing %q: %w", pidFile, errors.ErrEmptyValue)
}
pidStr := string(bytes.TrimSpace(parts[0]))
pid, err = strconv.Atoi(pidStr)
if err != nil {
return fmt.Errorf("parsing pid from %q: %w", pidFile, err)
}
}
proc, err := os.FindProcess(pid)
if err != nil {
return fmt.Errorf("finding process with pid %d: %w", pid, err)
}
err = proc.Signal(syscall.SIGHUP)
if err != nil {
return fmt.Errorf("sending sighup to process with pid %d: %w", pid, err)
}
return nil
return m.reload(ctx, name)
}
// install installs the service in the service manager.
@ -364,7 +318,7 @@ func (m *manager) runInitdCommand(
}
// emptyInterface is an empty implementation of the [service.Interface], as the
// actual implementation is onlyy needed for the [service.Service.Run] method.
// actual implementation is only needed for the [service.Service.Run] method.
type emptyInterface struct{}
// type check

View file

@ -0,0 +1,61 @@
//go:build unix
package ossvc
import (
"bytes"
"context"
"fmt"
"os"
"path/filepath"
"strconv"
"syscall"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/golibs/errors"
)
// reload is a UNIX platform implementation for the Reload method of
// [ReloadManager] interface for *manager.
func (m *manager) reload(ctx context.Context, name ServiceName) (err error) {
nameStr := string(name)
var pid int
pidFile := filepath.Join("/var", "run", nameStr+".pid")
// #nosec CWE-22 -- The name of the variable is always predictable, it is a
// constant.
data, err := os.ReadFile(pidFile)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("reading service pid file: %w", err)
}
pid, err = aghos.PIDByCommand(ctx, m.logger, nameStr, os.Getpid())
if err != nil {
return fmt.Errorf("finding process: %w", err)
}
} else {
parts := bytes.SplitN(data, []byte("\n"), 2)
if len(parts) == 0 {
return fmt.Errorf("parsing %q: %w", pidFile, errors.ErrEmptyValue)
}
pidStr := string(bytes.TrimSpace(parts[0]))
pid, err = strconv.Atoi(pidStr)
if err != nil {
return fmt.Errorf("parsing pid from %q: %w", pidFile, err)
}
}
proc, err := os.FindProcess(pid)
if err != nil {
return fmt.Errorf("finding process with pid %d: %w", pid, err)
}
err = proc.Signal(syscall.SIGHUP)
if err != nil {
return fmt.Errorf("sending sighup to process with pid %d: %w", pid, err)
}
return nil
}

View file

@ -0,0 +1,15 @@
//go:build windows
package ossvc
import (
"context"
"github.com/AdguardTeam/golibs/errors"
)
// reload is a Windows platform implementation of the Reload method of the
// [ReloadManager] interface for *manager.
func (*manager) reload(context.Context, ServiceName) error {
return errors.ErrUnsupported
}

View file

@ -137,7 +137,7 @@ docker_build_opt_tag() {
set -- \
"$@" \
-f \
./docker/Dockerfile \
./docker/build.Dockerfile \
. \
;

View file

@ -210,6 +210,7 @@ run_linter "$go" tool gocognit --over='10' \
./internal/home/ \
./internal/ipset \
./internal/next/ \
./internal/ossvc/ \
./internal/rdns/ \
./internal/schedule/ \
./internal/stats/ \
@ -257,6 +258,7 @@ run_linter "$go" tool fieldalignment \
./internal/filtering/safesearch/ \
./internal/ipset/ \
./internal/next/... \
./internal/ossvc/ \
./internal/querylog/ \
./internal/rdns/ \
./internal/schedule/ \
@ -290,6 +292,7 @@ run_linter "$go" tool gosec --exclude=G115 --fmt=golint --quiet \
./internal/filtering/safesearch/ \
./internal/ipset/ \
./internal/next/ \
./internal/ossvc/ \
./internal/rdns/ \
./internal/schedule/ \
./internal/stats/ \