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
env:
GO_VERSION: 1.14
NODE_VERSION: 13
on:
push:
branches:
- master
- '*'
tags:
- v*
pull_request:
@ -26,16 +30,19 @@ jobs:
-
name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
-
name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.14
go-version: ${{ env.GO_VERSION }}
-
name: Set up Node
uses: actions/setup-node@v1
with:
node-version: 13
node-version: ${{ env.NODE_VERSION }}
-
name: Set up Go modules cache
uses: actions/cache@v2
@ -58,33 +65,14 @@ jobs:
restore-keys: |
${{ runner.os }}-node-
-
name: Run node build-prod
name: Run make ci
shell: bash
run: |
npm --prefix client 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 ./...
make ci
-
name: Upload coverage
uses: codecov/codecov-action@v1
if: success()
if: success() && matrix.os == 'ubuntu-latest'
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.txt
@ -96,26 +84,18 @@ jobs:
-
name: Checkout
uses: actions/checkout@v2
-
name: Prepare
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
with:
fetch-depth: 0
-
name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.14
go-version: ${{ env.GO_VERSION }}
-
name: Set up Node
uses: actions/setup-node@v1
with:
node-version: 13
node-version: ${{ env.NODE_VERSION }}
-
name: Set up Go modules cache
uses: actions/cache@v2
@ -142,98 +122,30 @@ jobs:
run: |
sudo apt-get -yq --no-install-suggests --no-install-recommends install snapcraft
-
name: Run node build-prod
name: Set up GoReleaser
run: |
npm --prefix client ci
npm --prefix client run build-prod
curl -sfL https://install.goreleaser.com/github.com/goreleaser/goreleaser.sh | BINDIR="$(go env GOPATH)/bin" sh
-
name: Snapcraft Login
if: success() && startsWith(github.ref, 'refs/tags/v')
env:
SNAPCRAFT_LOGIN: ${{ secrets.SNAPCRAFT_LOGIN }}
name: Run snapshot build
run: |
snapcraft login --with <(echo "$SNAPCRAFT_LOGIN")
-
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 }}
make snapshot
docker:
runs-on: ubuntu-latest
needs: test
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
uses: crazy-max/ghaction-docker-buildx@v1
-
name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
-
name: Docker Buildx (build)
run: |
docker buildx build --output "type=image,push=false" ${{ steps.prepare.outputs.buildx_args }}
-
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 }}
make docker-multi-arch
-
name: Clear
if: always() && startsWith(github.ref, 'refs/tags/v')
@ -242,7 +154,8 @@ jobs:
notify:
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
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
- arm
- arm64
- mips
- mipsle
- mips64
- mips64le
goarm:
- 5
- 6
- 7
gomips:
- softfloat
ignore:
- goos: freebsd
goarch: arm
goarch: mips
- goos: freebsd
goarch: arm64
goarch: mipsle
archives:
-
replacements:
386: i386
amd64: x86_64
# Archive name template.
# Defaults:
# - 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:
- goos: windows
format: zip
- goos: darwin
format: zip
files:
- LICENSE.txt
- README.md
@ -62,7 +76,7 @@ snapcrafts:
of common code.
grade: stable
confinement: strict
publish: true
publish: false
license: GPL-3.0
apps:
adguard-home:

View File

@ -4,7 +4,7 @@ FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.14-alpine as builder
ARG BUILD_DATE
ARG VCS_REF
ARG VERSION=dev
ARG CHANNEL=none
ARG CHANNEL=release
ENV CGO_ENABLED 0
ENV GO111MODULE on
@ -21,12 +21,17 @@ RUN apk --update --no-cache add \
&& rm -rf /tmp/* /var/cache/apk/*
WORKDIR /app
COPY . ./
# Prepare the client code
RUN npm --prefix client ci && npm --prefix client run build-prod
# Download go dependencies
RUN go mod download
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 TARGETOS
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://t.me/adguard_en">Telegram</a>
<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">
<img src="https://img.shields.io/codecov/c/github/AdguardTeam/AdGuardHome/master.svg" alt="Code Coverage" />
</a>
@ -153,17 +150,11 @@ Is there a chance to handle this in the future? DNS will never be enough to do t
### Prerequisites
You will need:
You will need this to build AdGuard Home:
* [go](https://golang.org/dl/) v1.14 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
Open Terminal and execute these commands:
@ -174,15 +165,26 @@ cd AdGuardHome
make
```
#### (For devs) Upload translations
```
node upload.js
```
Check the [`Makefile`](https://github.com/AdguardTeam/AdGuardHome/blob/master/Makefile) to learn about other commands.
#### (For devs) Download translations
```
node download.js
```
#### Preparing release
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>
## 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/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-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
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=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -18,61 +18,20 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/util"
"github.com/AdguardTeam/golibs/log"
)
// 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{}
}
// 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 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
}
type getVersionJSONRequest struct {
@ -81,7 +40,6 @@ type getVersionJSONRequest struct {
// Get the latest available version from the Internet
func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
if Context.disableUpdate {
return
}
@ -103,7 +61,7 @@ func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
if cached {
log.Tracef("Returning cached data")
w.Header().Set("Content-Type", "application/json")
w.Write(getVersionResp(data))
_, _ = w.Write(getVersionResp(data))
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
func copyFile(src, dst string) error {
d, e := ioutil.ReadFile(src)
@ -159,19 +191,6 @@ func copyFile(src, dst string) error {
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
func getUpdateInfo(jsonData []byte) (*updateInfo, error) {
var u updateInfo
@ -184,7 +203,12 @@ func getUpdateInfo(jsonData []byte) (*updateInfo, error) {
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)
if len(u.pkgURL) == 0 || len(u.newVer) == 0 {
return nil, fmt.Errorf("invalid JSON")
@ -226,6 +250,33 @@ func getUpdateInfo(jsonData []byte) (*updateInfo, error) {
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
// Existing files are overwritten
// Return the list of files (not directories) written
@ -526,31 +577,3 @@ func finishUpdate(u *updateInfo) {
// 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)
}
// 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 is a blocking method!
// nolint
@ -153,11 +162,7 @@ func run(args options) {
configureLogger(args)
// print the first message after logger is configured
msg := "AdGuard Home, version %s, channel %s, arch %s %s"
if ARMVersion != "" {
msg = msg + " v" + ARMVersion
}
log.Printf(msg, versionString, updateChannel, runtime.GOOS, runtime.GOARCH)
log.Println(version())
log.Debug("Current working directory is %s", Context.workDir)
if args.runningAsService {
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 }},
{"glinet", "", "Run in GL-Inet compatibility mode", nil, func() { o.glinetMode = true }},
{"version", "", "Show the version and exit", nil, func() {
fmt.Printf("AdGuardHome %s\n", versionString)
fmt.Println(version())
os.Exit(0)
}},
{"help", "", "Print this help", nil, func() {