Skip to content

Commit 2e11a7f

Browse files
authored
Merge pull request #44 from packethost/github-actions
Enable GitHub Actions for CI
2 parents 27d2111 + 98343c9 commit 2e11a7f

12 files changed

Lines changed: 256 additions & 3 deletions

File tree

.envrc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
which nix &>/dev/null && use nix && unset GOPATH
1+
has nix && use nix
2+
dotenv_if_exists
3+
PATH_add bin
4+
path_add GOBIN bin

.github/workflows/ci.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: For each commit and PR
2+
on:
3+
push:
4+
pull_request:
5+
6+
jobs:
7+
validation:
8+
runs-on: ubuntu-latest
9+
env:
10+
CGO_ENABLED: 0
11+
steps:
12+
- name: Setup Dynamic Env
13+
run: |
14+
echo "MAKEFLAGS=-j$(nproc)" | tee $GITHUB_ENV
15+
- name: Checkout code
16+
uses: actions/checkout@v2
17+
- name: Install nix
18+
uses: cachix/install-nix-action@018abf956a0a15673dae4932ae26f0f071ac0944
19+
with:
20+
nix_path: nixpkgs=channel:nixpkgs-unstable
21+
- name: lint
22+
run: nix-shell --run "make --keep-going verify"
23+
- name: test
24+
run: nix-shell --run "make test"
25+
- name: go test coverage
26+
run: nix-shell --run "make coverage"
27+
- name: upload codecov
28+
run: bash <(curl -s https://codecov.io/bash)
29+
- name: run example
30+
if: ${{ startsWith(github.ref, 'refs/heads/master') }}
31+
env:
32+
PACKET_ENV: "ci"
33+
PACKET_VERSION: "${GITHUB_SHA}"
34+
ROLLBAR_TOKEN: ${{secrets.ROLLBAR_TOKEN}}
35+
run: nix-shell --run 'make run'

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,11 @@
1212
*.out
1313

1414
coverage.txt
15-
/examplelog
15+
cmd/examplelog/examplelog
1616
.idea/
17+
18+
bin/
19+
20+
21+
# added by lint-install
22+
out/

.golangci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
linters:
2+
disable:
3+
- errcheck

.yamllint

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
extends: default
3+
4+
rules:
5+
braces:
6+
max-spaces-inside: 1
7+
brackets:
8+
max-spaces-inside: 1
9+
comments: disable
10+
comments-indentation: disable
11+
document-start: disable
12+
line-length:
13+
level: warning
14+
max: 160
15+
allow-non-breakable-inline-mappings: true
16+
truthy: disable

Makefile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
help: ## Print this help
2+
@grep --no-filename -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sed 's/:.*##/·/' | sort | column -ts '·' -c 120
3+
4+
all: example ## Build example binary
5+
6+
-include lint.mk
7+
-include rules.mk
8+
9+
test: ## Run tests
10+
go test ./...
11+
12+
coverage: ## Generate coverage report
13+
CGO_ENABLED=1 go test -race -coverprofile=coverage.txt -covermode=atomic ./...
14+
15+
# Enable this later
16+
#verify: gofumpt prettier lint # # Verify code style, is lint free, freshness ...
17+
verify: goimports lint ## Verify code style, is lint free, freshness ...
18+
git diff | (! grep .)
19+
20+
# Enable this later
21+
#fix: gofumpt-fix prettier-fix ## Fix code formatting errors
22+
23+
tools: ${toolsBins} ## Build Go based build tools
24+
25+
run: run-example ## Run examplelog
26+
27+
.PHONY: all coverage help run test tools verify

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ require (
1414
github.com/rollbar/rollbar-go v1.4.2
1515
github.com/rollbar/rollbar-go/errors v0.0.0-20210929193720-32947096267e
1616
github.com/stretchr/testify v1.7.0
17+
github.com/tinkerbell/lint-install v0.0.0-20211012174934-5ee5ab01db76
1718
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0
1819
go.uber.org/atomic v1.9.0 // indirect
1920
go.uber.org/multierr v1.7.0 // indirect
@@ -24,4 +25,5 @@ require (
2425
google.golang.org/genproto v0.0.0-20211018162055-cf77aa76bad2 // indirect
2526
google.golang.org/grpc v1.41.0
2627
google.golang.org/grpc/examples v0.0.0-20210728214646-ad0a2a847cdf
28+
mvdan.cc/gofumpt v0.1.1
2729
)

go.sum

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
8181
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
8282
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
8383
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
84+
github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
85+
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
8486
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
8587
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
8688
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@@ -147,6 +149,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
147149
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
148150
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
149151
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
152+
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
153+
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
150154
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
151155
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
152156
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@@ -156,6 +160,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
156160
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
157161
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
158162
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
163+
github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw=
164+
github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
159165
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
160166
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
161167
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -204,6 +210,7 @@ github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0
204210
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
205211
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
206212
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
213+
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
207214
github.com/rollbar/rollbar-go v1.4.2 h1:UzxjFgg9CFE0Vb3grGPpZHCnbKzNd8RYFtFHEKovauU=
208215
github.com/rollbar/rollbar-go v1.4.2/go.mod h1:kLQ9gP3WCRGrvJmF0ueO3wK9xWocej8GRX98D8sa39w=
209216
github.com/rollbar/rollbar-go/errors v0.0.0-20210929193720-32947096267e h1:iGFeKprD5OJQ7rBsZq2Uks834Ikz2ZOWo2SC39TgKQU=
@@ -222,6 +229,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
222229
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
223230
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
224231
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
232+
github.com/tinkerbell/lint-install v0.0.0-20211012174934-5ee5ab01db76 h1:XFtCcjMWRMVO1rzZYPHR3OZ4aUOOtqu0H3v0KtNkURE=
233+
github.com/tinkerbell/lint-install v0.0.0-20211012174934-5ee5ab01db76/go.mod h1:0h2KsALaQLNkoVeV+G+HjBWWCnp0COFYhJdRd5WCQPM=
225234
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
226235
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
227236
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -290,6 +299,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
290299
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
291300
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
292301
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
302+
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
293303
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
294304
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
295305
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -437,6 +447,7 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
437447
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
438448
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
439449
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
450+
golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
440451
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
441452
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
442453
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
@@ -560,6 +571,10 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
560571
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
561572
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
562573
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
574+
k8s.io/klog/v2 v2.10.0 h1:R2HDMDJsHVTHA2n4RjwbeYXdOcBymXdX/JRb1v0VGhE=
575+
k8s.io/klog/v2 v2.10.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
576+
mvdan.cc/gofumpt v0.1.1 h1:bi/1aS/5W00E2ny5q65w9SnKpWEF/UIOqDYBILpo9rA=
577+
mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48=
563578
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
564579
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
565580
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

lint.mk

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
2+
# BEGIN: lint-install -makefile lint.mk .
3+
# http://github.com/tinkerbell/lint-install
4+
5+
GOLINT_VERSION ?= v1.42.1
6+
7+
8+
YAMLLINT_VERSION ?= 1.26.3
9+
LINT_OS := $(shell uname)
10+
LINT_ARCH := $(shell uname -m)
11+
LINT_ROOT := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
12+
13+
# shellcheck and hadolint lack arm64 native binaries: rely on x86-64 emulation
14+
ifeq ($(LINT_OS),Darwin)
15+
ifeq ($(LINT_ARCH),arm64)
16+
LINT_ARCH=x86_64
17+
endif
18+
endif
19+
20+
21+
GOLINT_CONFIG = $(LINT_ROOT)/.golangci.yml
22+
YAMLLINT_ROOT = out/linters/yamllint-$(YAMLLINT_VERSION)
23+
24+
.PHONY: lint
25+
lint: out/linters/golangci-lint-$(GOLINT_VERSION)-$(LINT_ARCH) $(YAMLLINT_ROOT)/dist/bin/yamllint
26+
find . -name go.mod -execdir "$(LINT_ROOT)/out/linters/golangci-lint-$(GOLINT_VERSION)-$(LINT_ARCH)" run -c "$(GOLINT_CONFIG)" \;
27+
PYTHONPATH=$(YAMLLINT_ROOT)/dist $(YAMLLINT_ROOT)/dist/bin/yamllint .
28+
29+
.PHONY: fix
30+
fix: out/linters/golangci-lint-$(GOLINT_VERSION)-$(LINT_ARCH)
31+
find . -name go.mod -execdir "$(LINT_ROOT)/out/linters/golangci-lint-$(GOLINT_VERSION)-$(LINT_ARCH)" run -c "$(GOLINT_CONFIG)" --fix \;
32+
33+
out/linters/golangci-lint-$(GOLINT_VERSION)-$(LINT_ARCH):
34+
mkdir -p out/linters
35+
rm -rf out/linters/golangci-lint-*
36+
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b out/linters $(GOLINT_VERSION)
37+
mv out/linters/golangci-lint out/linters/golangci-lint-$(GOLINT_VERSION)-$(LINT_ARCH)
38+
39+
$(YAMLLINT_ROOT)/dist/bin/yamllint:
40+
mkdir -p out/linters
41+
rm -rf out/linters/yamllint-*
42+
curl -sSfL https://github.com/adrienverge/yamllint/archive/refs/tags/v$(YAMLLINT_VERSION).tar.gz | tar -C out/linters -zxf -
43+
cd $(YAMLLINT_ROOT) && pip3 install . -t dist
44+
# END: lint-install -makefile lint.mk .

rules.mk

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Only use the recipes defined in these makefiles
2+
MAKEFLAGS += --no-builtin-rules
3+
.SUFFIXES:
4+
# Delete target files if there's an error
5+
# This avoids a failure to then skip building on next run if the output is created by shell redirection for example
6+
# Not really necessary for now, but just good to have already if it becomes necessary later.
7+
.DELETE_ON_ERROR:
8+
# Treat the whole recipe as a one shell script/invocation instead of one-per-line
9+
.ONESHELL:
10+
# Use bash instead of plain sh
11+
SHELL := bash
12+
.SHELLFLAGS := -o pipefail -euc
13+
14+
binaries := cmd/examplelog/examplelog
15+
version := $(shell git rev-parse --short HEAD)
16+
tag := $(shell git tag --points-at HEAD)
17+
ifneq (,$(tag))
18+
version := $(tag)-$(version)
19+
endif
20+
LDFLAGS := -ldflags "-X main.version=$(version)"
21+
export CGO_ENABLED := 0
22+
23+
.PHONY: example run-example $(binaries)
24+
example: cmd/examplelog/examplelog
25+
run-example: ./cmd/examplelog/examplelog
26+
$^
27+
28+
crossbinaries := $(addsuffix -linux-,$(binaries))
29+
crossbinaries := $(crossbinaries:=386) $(crossbinaries:=amd64) $(crossbinaries:=arm64) $(crossbinaries:=armv6) $(crossbinaries:=armv7)
30+
31+
.PHONY: crosscompile $(crossbinaries)
32+
%-386: FLAGS=GOOS=linux GOARCH=386
33+
%-amd64: FLAGS=GOOS=linux GOARCH=amd64
34+
%-arm64: FLAGS=GOOS=linux GOARCH=arm64
35+
%-armv6: FLAGS=GOOS=linux GOARCH=arm GOARM=6
36+
%-armv7: FLAGS=GOOS=linux GOARCH=arm GOARM=7
37+
$(binaries) $(crossbinaries):
38+
$(FLAGS) go build $(LDFLAGS) -o $@ ./$(@D)
39+
strip $@
40+
41+
IMAGE_TAG ?= mediator:latest
42+
.PHONY: server-image
43+
server-image: mediator-linux-amd64
44+
docker build -t $(IMAGE_TAG) .
45+
46+
ifeq ($(origin GOBIN), undefined)
47+
GOBIN := ${PWD}/bin
48+
export GOBIN
49+
PATH := ${GOBIN}:${PATH}
50+
export PATH
51+
endif
52+
53+
toolsBins := $(addprefix bin/,$(notdir $(shell awk -F'"' '/^\s*_/ {print $$2}' tools.go)))
54+
55+
# installs cli tools defined in tools.go
56+
$(toolsBins): go.mod go.sum tools.go
57+
$(toolsBins): CMD=$(shell awk -F'"' '/$(@F)"/ {print $$2}' tools.go)
58+
$(toolsBins):
59+
go install $(CMD)
60+
61+
.PHONY: gofumpt
62+
gofumpt: bin/gofumpt
63+
gofumpt -s -d .
64+
65+
gofumpt-fix: bin/gofumpt
66+
gofumpt -s -w .
67+
68+
.PHONY: prettier prettier-fix
69+
prettier:
70+
prettier --list-different --ignore-path .gitignore .
71+
72+
prettier-fix:
73+
prettier --write --ignore-path .gitignore .
74+
75+
goimports: bin/goimports
76+
goimports -d . | (! grep .)

0 commit comments

Comments
 (0)