CI revamp part 2 (#1875)

* Added Makefile
* Updated README with the new information
* Updated Github Actions
* Added FreeBSD ARM builds
* Prepared to our internal CI
* Improved the auto-update version check
This commit is contained in:
Andrey Meshkov 2020-07-09 19:54:53 +03:00 committed by GitHub
parent ff23d7b6d7
commit 632a47d56f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 472 additions and 313 deletions

View File

@ -1,9 +1,13 @@
name: build name: build
env:
GO_VERSION: 1.14
NODE_VERSION: 13
on: on:
push: push:
branches: branches:
- master - '*'
tags: tags:
- v* - v*
pull_request: pull_request:
@ -26,16 +30,19 @@ jobs:
- -
name: Checkout name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
with:
fetch-depth: 0
- -
name: Set up Go name: Set up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.14 go-version: ${{ env.GO_VERSION }}
- -
name: Set up Node name: Set up Node
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 13 node-version: ${{ env.NODE_VERSION }}
- -
name: Set up Go modules cache name: Set up Go modules cache
uses: actions/cache@v2 uses: actions/cache@v2
@ -58,33 +65,14 @@ jobs:
restore-keys: | restore-keys: |
${{ runner.os }}-node- ${{ runner.os }}-node-
- -
name: Run node build-prod name: Run make ci
shell: bash shell: bash
run: | run: |
npm --prefix client ci make ci
npm --prefix client run build-prod
-
name: Download modules and generate
shell: bash
run: |
go mod download
go generate ./...
-
name: Golangci-lint
if: matrix.os != 'windows-latest'
shell: bash
run: |
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.23.8
golangci-lint --version
golangci-lint run
-
name: Go test
run: |
go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./...
- -
name: Upload coverage name: Upload coverage
uses: codecov/codecov-action@v1 uses: codecov/codecov-action@v1
if: success() if: success() && matrix.os == 'ubuntu-latest'
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.txt file: ./coverage.txt
@ -96,26 +84,18 @@ jobs:
- -
name: Checkout name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- with:
name: Prepare fetch-depth: 0
id: prepare
run: |
if [[ $GITHUB_REF == refs/tags/* ]]; then
echo ::set-output name=tag_name::${GITHUB_REF#refs/tags/}
echo ::set-output name=channel::release
else
echo ::set-output name=channel::none
fi
- -
name: Set up Go name: Set up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.14 go-version: ${{ env.GO_VERSION }}
- -
name: Set up Node name: Set up Node
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 13 node-version: ${{ env.NODE_VERSION }}
- -
name: Set up Go modules cache name: Set up Go modules cache
uses: actions/cache@v2 uses: actions/cache@v2
@ -142,98 +122,30 @@ jobs:
run: | run: |
sudo apt-get -yq --no-install-suggests --no-install-recommends install snapcraft sudo apt-get -yq --no-install-suggests --no-install-recommends install snapcraft
- -
name: Run node build-prod name: Set up GoReleaser
run: | run: |
npm --prefix client ci curl -sfL https://install.goreleaser.com/github.com/goreleaser/goreleaser.sh | BINDIR="$(go env GOPATH)/bin" sh
npm --prefix client run build-prod
- -
name: Snapcraft Login name: Run snapshot build
if: success() && startsWith(github.ref, 'refs/tags/v')
env:
SNAPCRAFT_LOGIN: ${{ secrets.SNAPCRAFT_LOGIN }}
run: | run: |
snapcraft login --with <(echo "$SNAPCRAFT_LOGIN") make snapshot
-
name: GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --parallelism 2
env:
CHANNEL: ${{ steps.prepare.outputs.channel }}
-
name: GitHub Release
uses: softprops/action-gh-release@v1
if: success() && startsWith(github.ref, 'refs/tags/v')
with:
draft: true
files: |
dist/checksums.txt
dist/*.tar.gz
dist/*.zip
name: AdGuard Home ${{ steps.prepare.outputs.tag_name }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
docker: docker:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: test needs: test
steps: steps:
-
name: Prepare
id: prepare
run: |
DOCKER_IMAGE=adguard/adguardhome
DOCKER_PLATFORMS=linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/386,linux/ppc64le
VERSION=edge
CHANNEL=none
if [[ $GITHUB_REF == refs/tags/* ]]; then
VERSION=${GITHUB_REF#refs/tags/v}
CHANNEL=release
fi
TAGS="--tag ${DOCKER_IMAGE}:${VERSION}"
if [[ $VERSION =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
TAGS="$TAGS --tag ${DOCKER_IMAGE}:latest"
fi
echo ::set-output name=docker_image::${DOCKER_IMAGE}
echo ::set-output name=version::${VERSION}
echo ::set-output name=channel::${CHANNEL}
echo ::set-output name=buildx_args::--platform ${DOCKER_PLATFORMS} \
--build-arg VERSION=${VERSION} \
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
--build-arg VCS_REF=${GITHUB_SHA::8} \
${TAGS} \
--file Dockerfile .
- -
name: Set up Docker Buildx name: Set up Docker Buildx
uses: crazy-max/ghaction-docker-buildx@v1 uses: crazy-max/ghaction-docker-buildx@v1
- -
name: Checkout name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
with:
fetch-depth: 0
- -
name: Docker Buildx (build) name: Docker Buildx (build)
run: | run: |
docker buildx build --output "type=image,push=false" ${{ steps.prepare.outputs.buildx_args }} make docker-multi-arch
-
name: Docker Login
if: success() && startsWith(github.ref, 'refs/tags/v')
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: |
echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin
-
name: Docker Buildx (push)
if: success() && startsWith(github.ref, 'refs/tags/v')
run: |
docker buildx build --output "type=image,push=true" ${{ steps.prepare.outputs.buildx_args }}
-
name: Docker Check Manifest
if: always() && startsWith(github.ref, 'refs/tags/v')
run: |
docker run --rm mplatform/mquery ${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.version }}
- -
name: Clear name: Clear
if: always() && startsWith(github.ref, 'refs/tags/v') if: always() && startsWith(github.ref, 'refs/tags/v')
@ -242,7 +154,8 @@ jobs:
notify: notify:
needs: [app, docker] needs: [app, docker]
if: always() # Secrets are not passed to workflows that are triggered by a pull request from a fork
if: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- -

View File

@ -1,44 +0,0 @@
name: gosum
on:
push:
branches:
- 'master'
paths:
- '.github/workflows/gosum.yml'
- 'go.mod'
- 'go.sum'
jobs:
fix:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.14
-
name: Tidy
run: |
rm -f go.sum
go mod tidy
-
name: Set up Git
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config user.name GitHub
git config user.email noreply@github.com
git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git
-
name: Commit and push changes
run: |
git add .
if output=$(git status --porcelain) && [ ! -z "$output" ]; then
git commit --author "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>" --message "Fix go modules"
git push
fi

47
.github/workflows/lint.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: golangci-lint
on:
push:
tags:
- v*
branches:
- '*'
pull_request:
jobs:
golangci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v1
with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: v1.27
eslint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install modules
run: npm --prefix client ci
- name: Run ESLint
run: npm --prefix client run lint
notify:
needs: [golangci,eslint]
# Secrets are not passed to workflows that are triggered by a pull request from a fork
if: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository }}
runs-on: ubuntu-latest
steps:
-
name: Conclusion
uses: technote-space/workflow-conclusion-action@v1
-
name: Send Slack notif
uses: 8398a7/action-slack@v3
with:
status: ${{ env.WORKFLOW_CONCLUSION }}
fields: repo,message,commit,author
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

View File

@ -1,29 +0,0 @@
{
"Vendor": true,
"Test": true,
"Deadline": "2m",
"Sort": ["linter", "severity", "path", "line"],
"Exclude": [
".*generated.*",
"dnsfilter/rule_to_regexp.go"
],
"EnableGC": true,
"Linters": {
"nakedret": {
"Command": "nakedret",
"Pattern": "^(?P<path>.*?\\.go):(?P<line>\\d+)\\s*(?P<message>.*)$"
}
},
"WarnUnmatchedDirective": true,
"EnableAll": true,
"DisableAll": false,
"Disable": [
"maligned",
"goconst",
"vetshadow"
],
"Cyclo": 20,
"LineLength": 200
}

View File

@ -24,23 +24,37 @@ builds:
- amd64 - amd64
- arm - arm
- arm64 - arm64
- mips
- mipsle
- mips64
- mips64le
goarm: goarm:
- 5
- 6 - 6
- 7 - 7
gomips:
- softfloat
ignore: ignore:
- goos: freebsd - goos: freebsd
goarch: arm goarch: mips
- goos: freebsd - goos: freebsd
goarch: arm64 goarch: mipsle
archives: archives:
- -
replacements: # Archive name template.
386: i386 # Defaults:
amd64: x86_64 # - if format is `tar.gz`, `tar.xz`, `gz` or `zip`:
# - `{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}`
# - if format is `binary`:
# - `{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}`
name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}"
wrap_in_directory: "AdGuardHome"
format_overrides: format_overrides:
- goos: windows - goos: windows
format: zip format: zip
- goos: darwin
format: zip
files: files:
- LICENSE.txt - LICENSE.txt
- README.md - README.md
@ -62,7 +76,7 @@ snapcrafts:
of common code. of common code.
grade: stable grade: stable
confinement: strict confinement: strict
publish: true publish: false
license: GPL-3.0 license: GPL-3.0
apps: apps:
adguard-home: adguard-home:

View File

@ -4,7 +4,7 @@ FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.14-alpine as builder
ARG BUILD_DATE ARG BUILD_DATE
ARG VCS_REF ARG VCS_REF
ARG VERSION=dev ARG VERSION=dev
ARG CHANNEL=none ARG CHANNEL=release
ENV CGO_ENABLED 0 ENV CGO_ENABLED 0
ENV GO111MODULE on ENV GO111MODULE on
@ -21,12 +21,17 @@ RUN apk --update --no-cache add \
&& rm -rf /tmp/* /var/cache/apk/* && rm -rf /tmp/* /var/cache/apk/*
WORKDIR /app WORKDIR /app
COPY . ./ COPY . ./
# Prepare the client code
RUN npm --prefix client ci && npm --prefix client run build-prod RUN npm --prefix client ci && npm --prefix client run build-prod
# Download go dependencies
RUN go mod download RUN go mod download
RUN go generate ./... RUN go generate ./...
# It's important to place TARGET* arguments here to avoid running npm and go mod download for every platform
ARG TARGETPLATFORM ARG TARGETPLATFORM
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH

222
Makefile Normal file
View File

@ -0,0 +1,222 @@
#
# Available targets
#
# * build -- builds AdGuardHome for the current platform
# * client -- builds client-side code of AdGuard Home
# * client-watch -- builds client-side code of AdGuard Home and watches for changes there
# * docker -- builds a docker image for the current platform
# * clean -- clean everything created by previous builds
# * lint -- run all linters
# * test -- run all unit-tests
# * dependencies -- installs dependencies (go and npm modules)
# * ci -- installs dependencies, runs linters and tests, intended to be used by CI/CD
#
# Building releases:
#
# * release -- builds release version of AdGuard Home. CHANNEL must be specified (release or beta).
# * snapshot -- builds snapshot version of AdGuard Home. Use with CHANNEL=edge.
# * docker-multi-arch -- builds a multi-arch image. If you want it to be pushed to docker hub,
# you must specify:
# * DOCKER_IMAGE_NAME - adguard/adguard-home
# * DOCKER_OUTPUT - type=image,name=adguard/adguard-home,push=true
GOPATH := $(shell go env GOPATH)
PWD := $(shell pwd)
TARGET=AdGuardHome
BASE_URL="https://static.adguard.com/adguardhome/$(CHANNEL)"
# See release and snapshot targets
DIST_DIR=dist
# Update channel. Can be release, beta or edge. Uses edge by default.
CHANNEL ?= edge
# Validate channel
ifneq ($(CHANNEL),relese)
ifneq ($(CHANNEL),beta)
ifneq ($(CHANNEL),edge)
$(error CHANNEL value is not valid. Valid values are release,beta or edge)
endif
endif
endif
# Version properties
COMMIT=$(shell git rev-parse --short HEAD)
TAG_NAME=$(shell git describe --abbrev=0)
# Remove leading "v" from the tag name
RELEASE_VERSION=$(TAG_NAME:v%=%)
SNAPSHOT_VERSION=$(RELEASE_VERSION)-SNAPSHOT-$(COMMIT)
# Set proper version
VERSION=
ifeq ($(TAG_NAME),$(shell git describe --abbrev=4))
VERSION=$(RELEASE_VERSION)
else
VERSION=$(SNAPSHOT_VERSION)
endif
# Docker target parameters
DOCKER_IMAGE_NAME ?= adguardhome-dev
DOCKER_PLATFORMS=linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/386,linux/ppc64le
DOCKER_OUTPUT ?= type=image,name=$(DOCKER_IMAGE_NAME),push=false
BUILD_DATE=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')
# Docker tags (can be redefined)
DOCKER_TAGS ?=
ifndef DOCKER_TAGS
ifeq ($(CHANNEL),release)
DOCKER_TAGS := $(DOCKER_TAGS) --tag $(DOCKER_IMAGE_NAME):latest
endif
ifeq ($(CHANNEL),beta)
DOCKER_TAGS := $(DOCKER_TAGS) --tag $(DOCKER_IMAGE_NAME):beta
endif
ifeq ($(CHANNEL),edge)
DOCKER_TAGS := $(DOCKER_TAGS) --tag $(DOCKER_IMAGE_NAME):edge
endif
endif
# Validate docker build arguments
ifndef DOCKER_IMAGE_NAME
$(error DOCKER_IMAGE_NAME value is not set)
endif
.PHONY: all build client client-watch docker lint test dependencies clean release snapshot docker-multi-arch
all: build
build: dependencies client
go generate ./...
CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=$(VERSION) -X main.channel=$(CHANNEL) -X main.goarm=$(GOARM)"
PATH=$(GOPATH)/bin:$(PATH) packr clean
client:
npm --prefix client run build-prod
client-watch:
npm --prefix client run watch
docker:
DOCKER_CLI_EXPERIMENTAL=enabled \
docker buildx build \
--build-arg VERSION=$(VERSION) \
--build-arg CHANNEL=$(CHANNEL) \
--build-arg VCS_REF=$(COMMIT) \
--build-arg BUILD_DATE=$(BUILD_DATE) \
$(DOCKER_TAGS) \
--load \
-t "$(DOCKER_IMAGE_NAME)" -f ./Dockerfile .
@echo Now you can run the docker image:
@echo docker run --name "adguard-home" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_NAME)
lint:
@echo Running linters
golangci-lint run ./...
npm --prefix client run lint
test:
@echo Running unit-tests
go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./...
ci: dependencies client test
dependencies:
npm --prefix client ci
go mod download
clean:
# make build output
rm -f AdGuardHome
rm -f AdGuardHome.exe
# static build output
rm -rf build
# dist folder
rm -rf $(DIST_DIR)
# client deps
rm -rf client/node_modules
# packr-generated files
PATH=$(GOPATH)/bin:$(PATH) packr clean
docker-multi-arch:
DOCKER_CLI_EXPERIMENTAL=enabled \
docker buildx build \
--platform $(DOCKER_PLATFORMS) \
--build-arg VERSION=$(VERSION) \
--build-arg CHANNEL=$(CHANNEL) \
--build-arg VCS_REF=$(COMMIT) \
--build-arg BUILD_DATE=$(BUILD_DATE) \
$(DOCKER_TAGS) \
--output "$(DOCKER_OUTPUT)" \
-t "$(DOCKER_IMAGE_NAME):$(VERSION)" -f ./Dockerfile .
@echo If the image was pushed to the registry, you can now run it:
@echo docker run --name "adguard-home" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_NAME)
snapshot: dependencies client
@echo Starting snapshot build: version $(VERSION), channel $(CHANNEL)
CHANNEL=$(CHANNEL) goreleaser release --rm-dist --skip-publish --snapshot
$(call write_version_file,$(VERSION))
PATH=$(GOPATH)/bin:$(PATH) packr clean
release: dependencies client
@echo Starting release build: version $(VERSION), channel $(CHANNEL)
CHANNEL=$(CHANNEL) goreleaser release --rm-dist --skip-publish
$(call write_version_file,$(VERSION))
PATH=$(GOPATH)/bin:$(PATH) packr clean
define write_version_file
$(eval version := $(1))
@echo Writing version file: $(version)
# Variables for CI
rm -f $(DIST_DIR)/version.txt
echo "version=v$(version)" > $(DIST_DIR)/version.txt
# Prepare the version.json file
rm -f $(DIST_DIR)/version.json
echo "{" >> $(DIST_DIR)/version.json
echo " \"version\": \"v$(version)\"," >> $(DIST_DIR)/version.json
echo " \"announcement\": \"AdGuard Home $(version) is now available!\"," >> $(DIST_DIR)/version.json
echo " \"announcement_url\": \"https://github.com/AdguardTeam/AdGuardHome/releases\"," >> $(DIST_DIR)/version.json
echo " \"selfupdate_min_version\": \"v0.0\"," >> $(DIST_DIR)/version.json
# Windows builds
echo " \"download_windows_amd64\": \"$(BASE_URL)/AdGuardHome_windows_amd64.zip\"," >> $(DIST_DIR)/version.json
echo " \"download_windows_386\": \"$(BASE_URL)/AdGuardHome_windows_386.zip\"," >> $(DIST_DIR)/version.json
# MacOS builds
echo " \"download_darwin_386\": \"$(BASE_URL)/AdGuardHome_darwin_386.zip\"," >> $(DIST_DIR)/version.json
echo " \"download_darwin_amd64\": \"$(BASE_URL)/AdGuardHome_darwin_amd64.zip\"," >> $(DIST_DIR)/version.json
# Linux
echo " \"download_linux_amd64\": \"$(BASE_URL)/AdGuardHome_linux_amd64.tar.gz\"," >> $(DIST_DIR)/version.json
echo " \"download_linux_386\": \"$(BASE_URL)/AdGuardHome_linux_386.tar.gz\"," >> $(DIST_DIR)/version.json
# Linux, all kinds of ARM
echo " \"download_linux_arm\": \"$(BASE_URL)/AdGuardHome_linux_armv6.tar.gz\"," >> $(DIST_DIR)/version.json
echo " \"download_linux_armv5\": \"$(BASE_URL)/AdGuardHome_linux_armv5.tar.gz\"," >> $(DIST_DIR)/version.json
echo " \"download_linux_armv6\": \"$(BASE_URL)/AdGuardHome_linux_armv6.tar.gz\"," >> $(DIST_DIR)/version.json
echo " \"download_linux_armv7\": \"$(BASE_URL)/AdGuardHome_linux_armv7.tar.gz\"," >> $(DIST_DIR)/version.json
echo " \"download_linux_arm64\": \"$(BASE_URL)/AdGuardHome_linux_arm64.tar.gz\"," >> $(DIST_DIR)/version.json
# Linux, MIPS
echo " \"download_linux_mips\": \"$(BASE_URL)/AdGuardHome_linux_mips_softfloat.tar.gz\"," >> $(DIST_DIR)/version.json
echo " \"download_linux_mipsle\": \"$(BASE_URL)/AdGuardHome_linux_mipsle_softfloat.tar.gz\"," >> $(DIST_DIR)/version.json
echo " \"download_linux_mips64\": \"$(BASE_URL)/AdGuardHome_linux_mips64_softfloat.tar.gz\"," >> $(DIST_DIR)/version.json
echo " \"download_linux_mips64le\": \"$(BASE_URL)/AdGuardHome_linux_mips64le_softfloat.tar.gz\"," >> $(DIST_DIR)/version.json
# FreeBSD
echo " \"download_freebsd_386\": \"$(BASE_URL)/AdGuardHome_freebsd_386.tar.gz\"," >> $(DIST_DIR)/version.json
echo " \"download_freebsd_amd64\": \"$(BASE_URL)/AdGuardHome_freebsd_amd64.tar.gz\"," >> $(DIST_DIR)/version.json
# FreeBSD, all kinds of ARM
echo " \"download_freebsd_arm\": \"$(BASE_URL)/AdGuardHome_freebsd_armv6.tar.gz\"," >> $(DIST_DIR)/version.json
echo " \"download_freebsd_armv5\": \"$(BASE_URL)/AdGuardHome_freebsd_armv5.tar.gz\"," >> $(DIST_DIR)/version.json
echo " \"download_freebsd_armv6\": \"$(BASE_URL)/AdGuardHome_freebsd_armv6.tar.gz\"," >> $(DIST_DIR)/version.json
echo " \"download_freebsd_armv7\": \"$(BASE_URL)/AdGuardHome_freebsd_armv7.tar.gz\"," >> $(DIST_DIR)/version.json
echo " \"download_freebsd_arm64\": \"$(BASE_URL)/AdGuardHome_freebsd_arm64.tar.gz\"" >> $(DIST_DIR)/version.json
# Finish
echo "}" >> $(DIST_DIR)/version.json
endef

View File

@ -14,9 +14,6 @@
<a href="https://twitter.com/AdGuard">Twitter</a> | <a href="https://twitter.com/AdGuard">Twitter</a> |
<a href="https://t.me/adguard_en">Telegram</a> <a href="https://t.me/adguard_en">Telegram</a>
<br /><br /> <br /><br />
<a href="https://travis-ci.com/AdguardTeam/AdGuardHome">
<img src="https://travis-ci.com/AdguardTeam/AdGuardHome.svg" alt="Build status" />
</a>
<a href="https://codecov.io/github/AdguardTeam/AdGuardHome?branch=master"> <a href="https://codecov.io/github/AdguardTeam/AdGuardHome?branch=master">
<img src="https://img.shields.io/codecov/c/github/AdguardTeam/AdGuardHome/master.svg" alt="Code Coverage" /> <img src="https://img.shields.io/codecov/c/github/AdguardTeam/AdGuardHome/master.svg" alt="Code Coverage" />
</a> </a>
@ -153,17 +150,11 @@ Is there a chance to handle this in the future? DNS will never be enough to do t
### Prerequisites ### Prerequisites
You will need: You will need this to build AdGuard Home:
* [go](https://golang.org/dl/) v1.14 or later. * [go](https://golang.org/dl/) v1.14 or later.
* [node.js](https://nodejs.org/en/download/) v10 or later. * [node.js](https://nodejs.org/en/download/) v10 or later.
You can either install them via the provided links or use [brew.sh](https://brew.sh/) if you're on Mac:
```bash
brew install go node
```
### Building ### Building
Open Terminal and execute these commands: Open Terminal and execute these commands:
@ -174,15 +165,26 @@ cd AdGuardHome
make make
``` ```
#### (For devs) Upload translations Check the [`Makefile`](https://github.com/AdguardTeam/AdGuardHome/blob/master/Makefile) to learn about other commands.
```
node upload.js
```
#### (For devs) Download translations #### Preparing release
```
node download.js You'll need this to prepare a release build:
```
* [goreleaser](https://goreleaser.com/)
* [snapcraft](https://snapcraft.io/)
Run `make snapshot` or `make release` to build all AdGuard distrs.
#### Docker image
* Run `make docker` to build the Docker image locally.
* Run `make docker-multi-arch` to build the multi-arch Docker image (the one that we publish to Docker Hub).
### Resources that we update periodically
* `scripts/translations`
* `scripts/whotracksme`
<a id="contributing"></a> <a id="contributing"></a>
## Contributing ## Contributing

1
go.sum
View File

@ -146,6 +146,7 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -18,61 +18,20 @@ import (
"time" "time"
"github.com/AdguardTeam/AdGuardHome/util" "github.com/AdguardTeam/AdGuardHome/util"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
) )
// Convert version.json data to our JSON response type updateInfo struct {
func getVersionResp(data []byte) []byte { pkgURL string // URL for the new package
versionJSON := make(map[string]interface{}) pkgName string // Full path to package file
err := json.Unmarshal(data, &versionJSON) newVer string // New version string
if err != nil { updateDir string // Full path to the directory containing unpacked files from the new package
log.Error("version.json: %s", err) backupDir string // Full path to backup directory
return []byte{} configName string // Full path to the current configuration file
} updateConfigName string // Full path to the configuration file to check by the new binary
curBinName string // Full path to the current executable file
ret := make(map[string]interface{}) bkpBinName string // Full path to the current executable file in backup directory
ret["can_autoupdate"] = false newBinName string // Full path to the new executable file
var ok1, ok2, ok3 bool
ret["new_version"], ok1 = versionJSON["version"].(string)
ret["announcement"], ok2 = versionJSON["announcement"].(string)
ret["announcement_url"], ok3 = versionJSON["announcement_url"].(string)
selfUpdateMinVersion, ok4 := versionJSON["selfupdate_min_version"].(string)
if !ok1 || !ok2 || !ok3 || !ok4 {
log.Error("version.json: invalid data")
return []byte{}
}
// the key is download_linux_arm or download_linux_arm64 for regular ARM versions
dloadName := fmt.Sprintf("download_%s_%s", runtime.GOOS, runtime.GOARCH)
if runtime.GOARCH == "arm" && ARMVersion == "5" {
// the key is download_linux_armv5 for ARMv5
dloadName = fmt.Sprintf("download_%s_%sv%s", runtime.GOOS, runtime.GOARCH, ARMVersion)
}
_, ok := versionJSON[dloadName]
if ok && ret["new_version"] != versionString && versionString >= selfUpdateMinVersion {
canUpdate := true
tlsConf := tlsConfigSettings{}
Context.tls.WriteDiskConfig(&tlsConf)
if runtime.GOOS != "windows" &&
((tlsConf.Enabled && (tlsConf.PortHTTPS < 1024 || tlsConf.PortDNSOverTLS < 1024)) ||
config.BindPort < 1024 ||
config.DNS.Port < 1024) {
// On UNIX, if we're running under a regular user,
// but with CAP_NET_BIND_SERVICE set on a binary file,
// and we're listening on ports <1024,
// we won't be able to restart after we replace the binary file,
// because we'll lose CAP_NET_BIND_SERVICE capability.
canUpdate, _ = util.HaveAdminRights()
}
ret["can_autoupdate"] = canUpdate
}
d, _ := json.Marshal(ret)
return d
} }
type getVersionJSONRequest struct { type getVersionJSONRequest struct {
@ -81,7 +40,6 @@ type getVersionJSONRequest struct {
// Get the latest available version from the Internet // Get the latest available version from the Internet
func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) { func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
if Context.disableUpdate { if Context.disableUpdate {
return return
} }
@ -103,7 +61,7 @@ func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
if cached { if cached {
log.Tracef("Returning cached data") log.Tracef("Returning cached data")
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.Write(getVersionResp(data)) _, _ = w.Write(getVersionResp(data))
return return
} }
} }
@ -146,6 +104,80 @@ func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
} }
} }
// Perform an update procedure to the latest available version
func handleUpdate(w http.ResponseWriter, r *http.Request) {
if len(config.versionCheckJSON) == 0 {
httpError(w, http.StatusBadRequest, "/update request isn't allowed now")
return
}
u, err := getUpdateInfo(config.versionCheckJSON)
if err != nil {
httpError(w, http.StatusInternalServerError, "%s", err)
return
}
err = doUpdate(u)
if err != nil {
httpError(w, http.StatusInternalServerError, "%s", err)
return
}
returnOK(w)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
go finishUpdate(u)
}
// Convert version.json data to our JSON response
func getVersionResp(data []byte) []byte {
versionJSON := make(map[string]interface{})
err := json.Unmarshal(data, &versionJSON)
if err != nil {
log.Error("version.json: %s", err)
return []byte{}
}
ret := make(map[string]interface{})
ret["can_autoupdate"] = false
var ok1, ok2, ok3 bool
ret["new_version"], ok1 = versionJSON["version"].(string)
ret["announcement"], ok2 = versionJSON["announcement"].(string)
ret["announcement_url"], ok3 = versionJSON["announcement_url"].(string)
selfUpdateMinVersion, ok4 := versionJSON["selfupdate_min_version"].(string)
if !ok1 || !ok2 || !ok3 || !ok4 {
log.Error("version.json: invalid data")
return []byte{}
}
_, ok := getDownloadURL(versionJSON)
if ok && ret["new_version"] != versionString && versionString >= selfUpdateMinVersion {
canUpdate := true
tlsConf := tlsConfigSettings{}
Context.tls.WriteDiskConfig(&tlsConf)
if runtime.GOOS != "windows" &&
((tlsConf.Enabled && (tlsConf.PortHTTPS < 1024 || tlsConf.PortDNSOverTLS < 1024)) ||
config.BindPort < 1024 ||
config.DNS.Port < 1024) {
// On UNIX, if we're running under a regular user,
// but with CAP_NET_BIND_SERVICE set on a binary file,
// and we're listening on ports <1024,
// we won't be able to restart after we replace the binary file,
// because we'll lose CAP_NET_BIND_SERVICE capability.
canUpdate, _ = util.HaveAdminRights()
}
ret["can_autoupdate"] = canUpdate
}
d, _ := json.Marshal(ret)
return d
}
// Copy file on disk // Copy file on disk
func copyFile(src, dst string) error { func copyFile(src, dst string) error {
d, e := ioutil.ReadFile(src) d, e := ioutil.ReadFile(src)
@ -159,19 +191,6 @@ func copyFile(src, dst string) error {
return nil return nil
} }
type updateInfo struct {
pkgURL string // URL for the new package
pkgName string // Full path to package file
newVer string // New version string
updateDir string // Full path to the directory containing unpacked files from the new package
backupDir string // Full path to backup directory
configName string // Full path to the current configuration file
updateConfigName string // Full path to the configuration file to check by the new binary
curBinName string // Full path to the current executable file
bkpBinName string // Full path to the current executable file in backup directory
newBinName string // Full path to the new executable file
}
// Fill in updateInfo object // Fill in updateInfo object
func getUpdateInfo(jsonData []byte) (*updateInfo, error) { func getUpdateInfo(jsonData []byte) (*updateInfo, error) {
var u updateInfo var u updateInfo
@ -184,7 +203,12 @@ func getUpdateInfo(jsonData []byte) (*updateInfo, error) {
return nil, fmt.Errorf("JSON parse: %s", err) return nil, fmt.Errorf("JSON parse: %s", err)
} }
u.pkgURL = versionJSON[fmt.Sprintf("download_%s_%s", runtime.GOOS, runtime.GOARCH)].(string) pkgURL, ok := getDownloadURL(versionJSON)
if !ok {
return nil, fmt.Errorf("failed to get download URL")
}
u.pkgURL = pkgURL
u.newVer = versionJSON["version"].(string) u.newVer = versionJSON["version"].(string)
if len(u.pkgURL) == 0 || len(u.newVer) == 0 { if len(u.pkgURL) == 0 || len(u.newVer) == 0 {
return nil, fmt.Errorf("invalid JSON") return nil, fmt.Errorf("invalid JSON")
@ -226,6 +250,33 @@ func getUpdateInfo(jsonData []byte) (*updateInfo, error) {
return &u, nil return &u, nil
} }
// getDownloadURL - gets download URL for the current GOOS/GOARCH
// returns
func getDownloadURL(json map[string]interface{}) (string, bool) {
var key string
if runtime.GOARCH == "arm" && ARMVersion != "" {
// the key is:
// download_linux_armv5 for ARMv5
// download_linux_armv6 for ARMv6
// download_linux_armv7 for ARMv7
key = fmt.Sprintf("download_%s_%sv%s", runtime.GOOS, runtime.GOARCH, ARMVersion)
}
u, ok := json[key]
if !ok {
// the key is download_linux_arm or download_linux_arm64 for regular ARM versions
key = fmt.Sprintf("download_%s_%s", runtime.GOOS, runtime.GOARCH)
u, ok = json[key]
}
if !ok {
return "", false
}
return u.(string), true
}
// Unpack all files from .zip file to the specified directory // Unpack all files from .zip file to the specified directory
// Existing files are overwritten // Existing files are overwritten
// Return the list of files (not directories) written // Return the list of files (not directories) written
@ -526,31 +577,3 @@ func finishUpdate(u *updateInfo) {
// Unreachable code // Unreachable code
} }
} }
// Perform an update procedure to the latest available version
func handleUpdate(w http.ResponseWriter, r *http.Request) {
if len(config.versionCheckJSON) == 0 {
httpError(w, http.StatusBadRequest, "/update request isn't allowed now")
return
}
u, err := getUpdateInfo(config.versionCheckJSON)
if err != nil {
httpError(w, http.StatusInternalServerError, "%s", err)
return
}
err = doUpdate(u)
if err != nil {
httpError(w, http.StatusInternalServerError, "%s", err)
return
}
returnOK(w)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
go finishUpdate(u)
}

View File

@ -134,6 +134,15 @@ func Main(version string, channel string, armVer string) {
run(args) run(args)
} }
// version - returns the current version string
func version() string {
msg := "AdGuard Home, version %s, channel %s, arch %s %s"
if ARMVersion != "" {
msg = msg + " v" + ARMVersion
}
return fmt.Sprintf(msg, versionString, updateChannel, runtime.GOOS, runtime.GOARCH)
}
// run initializes configuration and runs the AdGuard Home // run initializes configuration and runs the AdGuard Home
// run is a blocking method! // run is a blocking method!
// nolint // nolint
@ -153,11 +162,7 @@ func run(args options) {
configureLogger(args) configureLogger(args)
// print the first message after logger is configured // print the first message after logger is configured
msg := "AdGuard Home, version %s, channel %s, arch %s %s" log.Println(version())
if ARMVersion != "" {
msg = msg + " v" + ARMVersion
}
log.Printf(msg, versionString, updateChannel, runtime.GOOS, runtime.GOARCH)
log.Debug("Current working directory is %s", Context.workDir) log.Debug("Current working directory is %s", Context.workDir)
if args.runningAsService { if args.runningAsService {
log.Info("AdGuard Home is running as a service") log.Info("AdGuard Home is running as a service")
@ -564,7 +569,7 @@ func loadOptions() options {
{"verbose", "v", "Enable verbose output", nil, func() { o.verbose = true }}, {"verbose", "v", "Enable verbose output", nil, func() { o.verbose = true }},
{"glinet", "", "Run in GL-Inet compatibility mode", nil, func() { o.glinetMode = true }}, {"glinet", "", "Run in GL-Inet compatibility mode", nil, func() { o.glinetMode = true }},
{"version", "", "Show the version and exit", nil, func() { {"version", "", "Show the version and exit", nil, func() {
fmt.Printf("AdGuardHome %s\n", versionString) fmt.Println(version())
os.Exit(0) os.Exit(0)
}}, }},
{"help", "", "Print this help", nil, func() { {"help", "", "Print this help", nil, func() {