Initial commit.
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
---
|
||||
name: "Linting and tests"
|
||||
run-name: "Linting and tests"
|
||||
on:
|
||||
- push
|
||||
|
||||
jobs:
|
||||
Linting:
|
||||
runs-on: "ubuntu-22.04"
|
||||
steps:
|
||||
- name: "checkout source"
|
||||
uses: actions/checkout@v4
|
||||
- name: "lint"
|
||||
uses: docker://code.pztrn.name/containers/go-toolbox:v10
|
||||
with:
|
||||
command: "/usr/local/bin/task lint"
|
||||
|
||||
Tests:
|
||||
runs-on: "ubuntu-22.04"
|
||||
steps:
|
||||
- name: "checkout source"
|
||||
uses: actions/checkout@v4
|
||||
- name: "tests"
|
||||
uses: docker://code.pztrn.name/containers/go-toolbox:v10
|
||||
with:
|
||||
command: "/usr/local/bin/task test"
|
||||
@@ -0,0 +1,3 @@
|
||||
*DS_Store*
|
||||
_build
|
||||
.task
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
version: "2"
|
||||
|
||||
linters:
|
||||
default: all
|
||||
disable:
|
||||
- containedctx
|
||||
- depguard
|
||||
- exhaustruct
|
||||
- gochecknoglobals
|
||||
- interfacebloat
|
||||
- ireturn
|
||||
- mnd
|
||||
- testpackage
|
||||
- tparallel
|
||||
- unused
|
||||
- varnamelen
|
||||
- noinlineerr
|
||||
- wsl
|
||||
settings:
|
||||
cyclop:
|
||||
max-complexity: 30
|
||||
package-average: 30
|
||||
forbidigo:
|
||||
forbid:
|
||||
- pattern: ^(fmt\.Print(|f|ln)|print|println)$
|
||||
- pattern: ^time\.Now\(\)($|\.F|\.A|\.B|\.L|\.UTC\(\)\.I|,|\))(# Calls of time\.Now() without \.UTC() is prohibited\.)?
|
||||
funlen:
|
||||
lines: 200
|
||||
statements: 60
|
||||
ignore-comments: true
|
||||
gocyclo:
|
||||
min-complexity: 20
|
||||
govet:
|
||||
enable-all: true
|
||||
funcorder:
|
||||
constructor: true
|
||||
struct-method: false
|
||||
alphabetical: true
|
||||
lll:
|
||||
line-length: 120
|
||||
nestif:
|
||||
min-complexity: 20
|
||||
tagliatelle:
|
||||
case:
|
||||
rules:
|
||||
json: snake
|
||||
yaml: camel
|
||||
use-field-name: true
|
||||
wsl_v5:
|
||||
allow-first-in-block: true
|
||||
allow-whole-block: false
|
||||
branch-max-lines: 2
|
||||
exclusions:
|
||||
generated: lax
|
||||
rules:
|
||||
- linters:
|
||||
- gosec
|
||||
path: .+_test\.go
|
||||
- linters:
|
||||
- godox
|
||||
text: TODO
|
||||
- linters:
|
||||
- govet
|
||||
text: declaration of "err" shadows
|
||||
- path: (.+)\.go$
|
||||
text: ST1000
|
||||
- path: (.+)\.go$
|
||||
text: package-comments
|
||||
- linters:
|
||||
- cyclop
|
||||
path: (.+)_test\.go
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
issues:
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- gofumpt
|
||||
settings:
|
||||
gofumpt:
|
||||
module-path: "go.dev.pztrn.name/vikunja-notifier"
|
||||
extra-rules: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"line-length": false,
|
||||
"first-line-h1": false
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
repos:
|
||||
- repo: https://code.pztrn.name/misc/pre-commit-hooks-golang
|
||||
rev: cb3db89b1c4bee10eec85a7fa1dbbd35fb347478
|
||||
hooks:
|
||||
- id: golangci-lint
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
// Используйте IntelliSense, чтобы узнать о возможных атрибутах.
|
||||
// Наведите указатель мыши, чтобы просмотреть описания существующих атрибутов.
|
||||
// Для получения дополнительной информации посетите: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "vikunja-notifier docker debug",
|
||||
"type": "go",
|
||||
"debugAdapter": "dlv-dap",
|
||||
"request": "attach",
|
||||
"mode": "remote",
|
||||
"port": 49001,
|
||||
"host": "127.0.0.1",
|
||||
"stopOnEntry": true,
|
||||
"substitutePath": [
|
||||
{
|
||||
"from": "${workspaceFolder}",
|
||||
"to": "/vikunja-notifier"
|
||||
}
|
||||
],
|
||||
"preLaunchTask": "Run vikunja-notifier debug build",
|
||||
"postDebugTask": "Stop vikunja-notifier debug build"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Run vikunja-notifier debug build",
|
||||
"type": "shell",
|
||||
"command": "task deployments:localdev:vikunja-notifier:down; task deployments:localdev:vikunja-notifier:up-debug",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
// Do not open terminal every time debug is launched.
|
||||
"reveal": "silent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Stop vikunja-notifier debug build",
|
||||
"type": "shell",
|
||||
"command": "task deployments:localdev:vikunja-notifier:down-debug",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
// Do not open terminal every time debug is launched.
|
||||
"reveal": "silent"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
extends: default
|
||||
|
||||
ignore: |
|
||||
./vendor
|
||||
|
||||
rules:
|
||||
line-length: disable
|
||||
braces:
|
||||
ignore:
|
||||
- openapi.yaml
|
||||
min-spaces-inside: 1
|
||||
max-spaces-inside: 1
|
||||
@@ -0,0 +1,94 @@
|
||||
# Contributing to vikunja-notifier
|
||||
|
||||
Hey, developer/user! Please, read this file carefully before doing any contributions on vikunja-notifier.
|
||||
|
||||
## Hello from Russia
|
||||
|
||||
First of all, hello from Russia and thanks for stopping by.
|
||||
|
||||
Lead developer of this project living in Yekaterinburg, Russia. If you're uncomfortable with it - please do not use vikunja-notifier and/or do not try to contribute to it.
|
||||
|
||||
## Issues
|
||||
|
||||
## Submitting issues
|
||||
|
||||
Before submitting issues please make sure you:
|
||||
|
||||
1. Searched for similar issues in tracker.
|
||||
|
||||
## Working on issues
|
||||
|
||||
See The Flow below.
|
||||
|
||||
## Developing vikunja-notifier
|
||||
|
||||
### Copyright, licenses, DCOs
|
||||
|
||||
Code is licensed under terms and conditions of GNU General Public License version 3. By submitting any portion of code via pull requests you accept that your code will be licensed under this license.
|
||||
|
||||
By submitting code via Pull Request you should signoff it (via `--signoff` command line parameter to git). This will mean:
|
||||
|
||||
1. You signing [DCO](DCO), meaning that code you submitted was really written by you and not copied from other software (especially with incompatible licensing).
|
||||
2. You agree to transfer all rights on submitted code to [@pztrn](https://code.pztrn.name/pztrn).
|
||||
|
||||
Non-signed-off pull requests won't be accepted.
|
||||
|
||||
### Preparing your development environment
|
||||
|
||||
First of all you should properly configure your development environment.
|
||||
|
||||
vikunja-notifier is developed using VSCode with Go plugin, which utilises gopls and golangci-lint. Ensure linter is running before you commit anything to repository to save your time:
|
||||
|
||||
- If you're using IDEA or IDEA-based products (like OpenIDE): use [pre-commit](https://pre-commit.com/) to run linter on git commit. It's configuration already added to repository.
|
||||
- If you're on VSCode: configure linter to be run on whole workspace to see problems in near-realtime. Fix them before committing code.
|
||||
- If you're on any other editor: you're on your own. As example you can take a look on [Sublime Text 4 as Golang IDE](https://wiki.pztrn.name/software/sublimetext/golang_ide_v4/) (russian) or use pre-commit hooks.
|
||||
|
||||
### The always stable main nonsense
|
||||
|
||||
We (and you) should try hard to keep code in main correct, so anyone should be able to checkout code from main branch, build it and deploy anywhere.
|
||||
|
||||
### Selecting task to work on
|
||||
|
||||
Take a look on issues in Gitea's web interface. If you found desired task - pick it up by expressing your intentions in issue's comments and work on it.
|
||||
|
||||
If issue was created by you still you have to express your intentions so other people will know that this task is being worked on.
|
||||
|
||||
### Git branches types
|
||||
|
||||
There are 3 branch types:
|
||||
|
||||
1. `main` branch. Trying to keep always buildable and deployable. Only @pztrn is able to merge or commit here.
|
||||
2. Feature branches. Named in form of `feature/TASKID_feature_name`, e.g. `feature/2_cicd` or `feature/123_tasks`. These branches created for working on couple of tasks and you should create personal branches from it. Only @pztrn is able to merge or commit here.
|
||||
3. Personal branch. These branches located in your personal fork, from these branches you're making merge requests.
|
||||
|
||||
### The Flow
|
||||
|
||||
vikunja-notifier uses own flow that can be described like that:
|
||||
|
||||
- An issue must be created in issue tracker. All merge requests should be sticked to it.
|
||||
- If this is a bug feature - ask @pztrn to create feature branch for making merge requests.
|
||||
- Create merge requests early, so other developers and involved users can review you code at any point of time. Follow merge requests template as much as possible and describe your work with as much words as possible.
|
||||
- Give commits meaningful descriptions. It can be short, like `implement feature X for Y`, or `linting` (if this commit is only about linting), and can be repeated (if you're kind-of ADHD and forgot to fix linting issues on some files while fixing linting issues in others), but it still should be meaningful.
|
||||
- Rebase on main or merge it in your branch before calling people to review your code.
|
||||
- Your branch should not contain any code that isn't belonging to feature you implement or bug you fix.
|
||||
- CI pipeline for linting and tests should pass. Do not call for review if your pipeline failed.
|
||||
- Of course, there might be exceptions, e.g. you can't understand why it failing. Request help from @pztrn in comments.
|
||||
- Everything should be covered with documentational changes in `docs` directory, if needed.
|
||||
- @pztrn approve is required to merge. Deal with it for now. This can be changed in future.
|
||||
- Merge requests will be automatically squashed in one commit on merging, giving full information about merge request in this commit.
|
||||
|
||||
Git flow can be described like that:
|
||||
|
||||
1. Clone vikunja-notifier to your account.
|
||||
2. Checkout your branch from main branch (or feature branch if specified in task).
|
||||
3. Write some code and push it to your copy. Don't forgen about documentational changes if needed.
|
||||
4. Create merge request into main repository. Follow merge request template as close as possible. Select proper branch (main or feature branch).
|
||||
5. Reiterate 3 and 4 as long as needed.
|
||||
6. Ensure your branch can be merged in destination. Rebase on/merge target in your branch if needed.
|
||||
7. Call developers to review your code.
|
||||
8. If needed, reiterate 3-7 until code is feature complete, CI is green, no objections left in merge request discussions and @pztrn approve is gathered.
|
||||
9. Ping @pztrn every day until he merges.
|
||||
|
||||
---
|
||||
|
||||
*These rules can be changed in any time without further notice.*
|
||||
@@ -0,0 +1,33 @@
|
||||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
FROM code.pztrn.name/containers/go-toolbox:v10 AS build
|
||||
|
||||
WORKDIR /vikunja-notifier
|
||||
COPY . /vikunja-notifier
|
||||
RUN --mount=type=cache,target="/home/container/go" task build --force
|
||||
|
||||
FROM debian:13-slim
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y ca-certificates iputils-ping coreutils && \
|
||||
rm -rf /var/lib/apt/* /var/cache/apt/*
|
||||
|
||||
COPY --from=build /vikunja-notifier/_build/vikunja-notifier /vikunja-notifier
|
||||
COPY --from=build /usr/local/bin/dlv /dlv
|
||||
|
||||
ENTRYPOINT ["/vikunja-notifier"]
|
||||
@@ -0,0 +1,18 @@
|
||||
FROM code.pztrn.name/containers/go-toolbox:v10 AS build
|
||||
|
||||
WORKDIR /vikunja-notifier
|
||||
COPY . /vikunja-notifier
|
||||
RUN --mount=type=cache,target="/home/container/go" GOFLAGS="" task build-debug --force
|
||||
|
||||
FROM debian:13-slim
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y ca-certificates iputils-ping coreutils curl net-tools && \
|
||||
rm -rf /var/lib/apt/* /var/cache/apt/*
|
||||
|
||||
COPY --from=build /vikunja-notifier/_build/vikunja-notifier /vikunja-notifier
|
||||
COPY --from=build /usr/local/bin/dlv /dlv
|
||||
|
||||
HEALTHCHECK --interval=1s --timeout=10s --start-period=1s --retries=3 CMD netstat -an | grep 4000 > /dev/null; if [ 0 != $? ]; then exit 1; fi;
|
||||
|
||||
ENTRYPOINT ["/dlv", "--listen=:4000", "--headless=true", "--log=true", "--accept-multiclient", "--api-version=2", "exec", "/vikunja-notifier"]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Vikunja notifier
|
||||
|
||||
Simple daemon that accepts Vikunja webhooks and sends notifications to various channels.
|
||||
|
||||
Currently supported channels:
|
||||
|
||||
- ntfy
|
||||
|
||||
## Installation
|
||||
|
||||
TBW
|
||||
|
||||
## Configuration
|
||||
|
||||
All configuration is done using environment variables:
|
||||
|
||||
* `VN_HTTP_ADDRESS` - address for listening for HTTP requests.
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
version: "3"
|
||||
|
||||
vars:
|
||||
VERSION:
|
||||
sh: scripts/version_generator.sh
|
||||
BRANCH:
|
||||
sh: git rev-parse --abbrev-ref HEAD
|
||||
COMMIT:
|
||||
sh: git rev-parse --short HEAD
|
||||
BUILD:
|
||||
sh: git rev-list --count HEAD
|
||||
BUILD_DATE:
|
||||
sh: TZ=UTC date +'%Y-%m-%d %H:%M:%S %Z'
|
||||
BASIC_LDFLAGS: "-X 'go.dev.pztrn.name/vikunja-notifier/internal/commons.Version={{.VERSION}}' -X 'go.dev.pztrn.name/vikunja-notifier/internal/commons.Branch={{.BRANCH}}' -X 'go.dev.pztrn.name/vikunja-notifier/internal/commons.Commit={{.COMMIT}}' -X 'go.dev.pztrn.name/vikunja-notifier/internal/commons.Build={{.BUILD}}' -X 'go.dev.pztrn.name/vikunja-notifier/internal/commons.BuildDate={{.BUILD_DATE}}'"
|
||||
|
||||
env:
|
||||
GOFLAGS: "-trimpath"
|
||||
|
||||
includes:
|
||||
deployments: ./deployments
|
||||
|
||||
tasks:
|
||||
build:
|
||||
desc: "Builds vikunja-notifier binary."
|
||||
cmds:
|
||||
- task: ensure-builddir
|
||||
- task: cleanup
|
||||
- go build -ldflags="{{ .BASIC_LDFLAGS }}" -o _build/vikunja-notifier{{exeExt}} main.go
|
||||
sources:
|
||||
- ./Taskfile.yml
|
||||
- ./go.mod
|
||||
- ./main.go
|
||||
- ./internal/*
|
||||
generates:
|
||||
- ./_build/vikunja-notifier{{exeExt}}
|
||||
method: timestamp
|
||||
|
||||
build-debug:
|
||||
desc: "Builds vikunja-notifier binary."
|
||||
cmds:
|
||||
- task: ensure-builddir
|
||||
- task: cleanup
|
||||
- go build -ldflags="{{ .BASIC_LDFLAGS }}" --gcflags "all=-N -l" -o _build/vikunja-notifier{{exeExt}} main.go
|
||||
sources:
|
||||
- ./Taskfile.yml
|
||||
- ./go.mod
|
||||
- ./main.go
|
||||
- ./internal/*
|
||||
generates:
|
||||
- ./_build/vikunja-notifier{{exeExt}}
|
||||
method: timestamp
|
||||
|
||||
cleanup:
|
||||
desc: "Cleanup _build directory."
|
||||
cmds:
|
||||
- rm _build/* || exit 0
|
||||
|
||||
ensure-builddir:
|
||||
internal: true
|
||||
cmds:
|
||||
- mkdir -p _build
|
||||
|
||||
lint:
|
||||
desc: "Lints whole workspace."
|
||||
cmds:
|
||||
- golangci-lint run
|
||||
|
||||
test:
|
||||
desc: "Test whole workspace"
|
||||
cmds:
|
||||
- go test -test.v ./...
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
version: "3"
|
||||
|
||||
includes:
|
||||
localdev: ./localdev
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
version: "3"
|
||||
|
||||
includes:
|
||||
common: ./common
|
||||
vikunja-notifier: ./vikunja-notifier
|
||||
|
||||
tasks:
|
||||
down:
|
||||
desc: "Stops all containers for devzone with data removal."
|
||||
cmds:
|
||||
- task: vikunja-notifier:down
|
||||
- task: common:network-down
|
||||
|
||||
stop:
|
||||
desc: "Stops all containers."
|
||||
cmds:
|
||||
- task: vikunja-notifier:down
|
||||
|
||||
up:
|
||||
desc: "Starts all containers and (up -d)."
|
||||
cmds:
|
||||
- task: common:network-up
|
||||
- task: vikunja-notifier:up
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
version: "3"
|
||||
|
||||
tasks:
|
||||
network-up:
|
||||
desc: "Creates or updates network definition."
|
||||
dir: "./deployments/localdev/common"
|
||||
cmds:
|
||||
- docker compose -p vikunja-notifier-common -f network.yaml up -d
|
||||
run: once
|
||||
|
||||
network-down:
|
||||
desc: "Deletes network definition."
|
||||
dir: "./deployments/localdev/common"
|
||||
cmds:
|
||||
- docker compose -p vikunja-notifier-common -f network.yaml down
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
# dummy service for network creation.
|
||||
services:
|
||||
dummy:
|
||||
image: busybox
|
||||
container_name: vikunja-notifier-dummy
|
||||
hostname: dummy
|
||||
networks:
|
||||
vikunja-notifier:
|
||||
ipv4_address: 10.30.1.254
|
||||
|
||||
networks:
|
||||
vikunja-notifier:
|
||||
driver: bridge
|
||||
name: "vikunja-notifier"
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 10.30.1.0/24
|
||||
gateway: 10.30.1.1
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
version: "3"
|
||||
|
||||
tasks:
|
||||
build:
|
||||
desc: "Builds vikunja-notifier's container."
|
||||
dir: "./deployments/localdev/vikunja-notifier"
|
||||
cmds:
|
||||
- docker compose -p vikunja-notifier -f docker-compose.yaml build
|
||||
|
||||
build-debug:
|
||||
desc: "Builds vikunja-notifier's debug build container."
|
||||
dir: "./deployments/localdev/vikunja-notifier"
|
||||
cmds:
|
||||
- docker compose -p vikunja-notifier-debug -f docker-compose.debug.yaml build
|
||||
|
||||
down:
|
||||
desc: "Deletes all vikunja-notifier's data (down)."
|
||||
dir: "./deployments/localdev/vikunja-notifier"
|
||||
cmds:
|
||||
- docker compose -p vikunja-notifier -f docker-compose.yaml down --volumes
|
||||
|
||||
down-debug:
|
||||
desc: "Deletes all vikunja-notifier's debug build data (down)."
|
||||
dir: "./deployments/localdev/vikunja-notifier"
|
||||
cmds:
|
||||
- docker compose -p vikunja-notifier-debug -f docker-compose.debug.yaml down --volumes
|
||||
|
||||
logs:
|
||||
desc: "Show vikunja-notifier logs."
|
||||
dir: "./deployments/localdev/vikunja-notifier"
|
||||
cmds:
|
||||
- docker compose -p vikunja-notifier -f docker-compose.yaml logs -f
|
||||
|
||||
logs-debug:
|
||||
desc: "Show vikunja-notifier debug build logs."
|
||||
dir: "./deployments/localdev/vikunja-notifier"
|
||||
cmds:
|
||||
- docker compose -p vikunja-notifier-debug -f docker-compose.debug.yaml logs -f
|
||||
|
||||
restart:
|
||||
desc: "Restart vikunja-notifier."
|
||||
dir: "./deployments/localdev/vikunja-notifier"
|
||||
cmds:
|
||||
- docker compose -p vikunja-notifier -f docker-compose.yaml restart
|
||||
|
||||
restart-debug:
|
||||
desc: "Restart vikunja-notifier debug build."
|
||||
dir: "./deployments/localdev/vikunja-notifier"
|
||||
cmds:
|
||||
- docker compose -p vikunja-notifier-debug -f docker-compose.debug.yaml restart
|
||||
|
||||
start:
|
||||
desc: "Start vikunja-notifier."
|
||||
dir: "./deployments/localdev/vikunja-notifier"
|
||||
cmds:
|
||||
- docker compose -p vikunja-notifier -f docker-compose.yaml start
|
||||
|
||||
start-debug:
|
||||
desc: "Start vikunja-notifier debug build."
|
||||
dir: "./deployments/localdev/vikunja-notifier"
|
||||
cmds:
|
||||
- docker compose -p vikunja-notifier -f docker-compose.yaml start
|
||||
|
||||
stop:
|
||||
desc: "Stop vikunja-notifier without deleting it's data."
|
||||
dir: "./deployments/localdev/vikunja-notifier"
|
||||
cmds:
|
||||
- docker compose -p vikunja-notifier -f docker-compose.yaml stop
|
||||
|
||||
stop-debug:
|
||||
desc: "Stop vikunja-notifier debug build without deleting it's data."
|
||||
dir: "./deployments/localdev/vikunja-notifier"
|
||||
cmds:
|
||||
- docker compose -p vikunja-notifier-debug -f docker-compose.debug.yaml stop
|
||||
|
||||
up:
|
||||
desc: "Start vikunja-notifier (up -d)."
|
||||
dir: "./deployments/localdev/vikunja-notifier"
|
||||
cmds:
|
||||
- task: :common:network-up
|
||||
- task: build
|
||||
- docker compose -p vikunja-notifier -f docker-compose.yaml up -d
|
||||
|
||||
up-debug:
|
||||
desc: "Start vikunja-notifier debug build (up -d)."
|
||||
dir: "./deployments/localdev/vikunja-notifier"
|
||||
cmds:
|
||||
- task: :common:network-up
|
||||
- task: build-debug
|
||||
- docker compose -p vikunja-notifier-debug -f docker-compose.debug.yaml up -d --wait
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
services:
|
||||
vikunja-notifier-debug:
|
||||
container_name: "vikunja-notifier-debug"
|
||||
build:
|
||||
context: ../../../
|
||||
dockerfile: Dockerfile.debug
|
||||
ports:
|
||||
- "49000:49000"
|
||||
- "49001:4000"
|
||||
networks:
|
||||
vikunja-notifier:
|
||||
ipv4_address: 10.30.1.2
|
||||
environment:
|
||||
VN_HTTP_ADDRESS: "0.0.0.0:49000"
|
||||
VN_LOGGER_LEVEL: "debug"
|
||||
cap_add:
|
||||
- SYS_PTRACE
|
||||
|
||||
networks:
|
||||
vikunja-notifier:
|
||||
external: true
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
services:
|
||||
vikunja-notifier:
|
||||
container_name: "vikunja-notifier"
|
||||
build:
|
||||
context: ../../../
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "49000:49000"
|
||||
networks:
|
||||
vikunja-notifier:
|
||||
ipv4_address: 10.30.1.2
|
||||
environment:
|
||||
VN_HTTP_ADDRESS: "0.0.0.0:49000"
|
||||
VN_LOGGER_LEVEL: "debug"
|
||||
cap_add:
|
||||
- SYS_PTRACE
|
||||
|
||||
networks:
|
||||
vikunja-notifier:
|
||||
external: true
|
||||
@@ -0,0 +1,203 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var errApplication = errors.New("application")
|
||||
|
||||
// Application is a lifecycle controlling structure for application.
|
||||
type Application struct {
|
||||
shutdownChan chan struct{}
|
||||
ctx context.Context
|
||||
cancelFunc context.CancelFunc
|
||||
baseLogger *slog.Logger
|
||||
appLogger *slog.Logger
|
||||
services []Service
|
||||
}
|
||||
|
||||
// New creates new instance of lifecycle controlling structure.
|
||||
func New() *Application {
|
||||
appl := &Application{}
|
||||
|
||||
appl.initialize()
|
||||
|
||||
return appl
|
||||
}
|
||||
|
||||
func (a *Application) configure() error {
|
||||
// First iteration - core services.
|
||||
for _, service := range a.services {
|
||||
if !strings.Contains(service.Name(), "core/") {
|
||||
continue
|
||||
}
|
||||
|
||||
a.appLogger.Debug("Launching configuration procedure for service", "service", service.Name())
|
||||
|
||||
if err := service.Configure(); err != nil {
|
||||
return fmt.Errorf("configure service '%s': %w", service.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
// Second iteration - rest of the services.
|
||||
for _, service := range a.services {
|
||||
if strings.Contains(service.Name(), "core/") {
|
||||
continue
|
||||
}
|
||||
|
||||
a.appLogger.Debug("Launching configuration procedure for service", "service", service.Name())
|
||||
|
||||
if err := service.Configure(); err != nil {
|
||||
return fmt.Errorf("configure service '%s': %w", service.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Application) connectDependencies() error {
|
||||
// First iteration - core services.
|
||||
for _, service := range a.services {
|
||||
if !strings.Contains(service.Name(), "core/") {
|
||||
continue
|
||||
}
|
||||
|
||||
a.appLogger.Debug("Connecting dependencies for service.", "service", service.Name())
|
||||
|
||||
if err := service.ConnectDependencies(); err != nil {
|
||||
return fmt.Errorf("connect dependencies for service '%s': %w", service.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
// Second iteration - rest of the services.
|
||||
for _, service := range a.services {
|
||||
if strings.Contains(service.Name(), "core/") {
|
||||
continue
|
||||
}
|
||||
|
||||
a.appLogger.Debug("Connecting dependencies for service.", "service", service.Name())
|
||||
|
||||
if err := service.ConnectDependencies(); err != nil {
|
||||
return fmt.Errorf("connect dependencies for service '%s': %w", service.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Context returns application-wide context.
|
||||
func (a *Application) Context() context.Context {
|
||||
return a.ctx
|
||||
}
|
||||
|
||||
// ContextWithTimeout returns context.Context with requested timeout.
|
||||
func (a *Application) ContextWithTimeout(timeout time.Duration) context.Context {
|
||||
ctx, cancelFunc := context.WithTimeout(a.ctx, timeout)
|
||||
|
||||
// As we do not need to call cancelFunc - make linter happy.
|
||||
// This probably will lead to context leak, so it should be investigated.
|
||||
go func(_ context.CancelFunc) {}(cancelFunc)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (a *Application) initialize() {
|
||||
a.ctx, a.cancelFunc = context.WithCancel(context.Background())
|
||||
|
||||
a.initializeLogger()
|
||||
|
||||
a.services = make([]Service, 0)
|
||||
}
|
||||
|
||||
func (a *Application) launchStartupTasks() error {
|
||||
for _, service := range a.services {
|
||||
if strings.Contains(service.Name(), "mainwindow") {
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.Contains(service.Name(), "core/") {
|
||||
continue
|
||||
}
|
||||
|
||||
a.appLogger.Debug("Launching startup tasks for service.", "service", service.Name())
|
||||
|
||||
if err := service.LaunchStartupTasks(); err != nil {
|
||||
return fmt.Errorf("launch startup tasks for core/%s: %w", service.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, service := range a.services {
|
||||
if strings.Contains(service.Name(), "core/") {
|
||||
continue
|
||||
}
|
||||
|
||||
a.appLogger.Debug("Launching startup tasks for service.", "service", service.Name())
|
||||
|
||||
if err := service.LaunchStartupTasks(); err != nil {
|
||||
return fmt.Errorf("launch startup tasks for core/%s: %w", service.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown stops application.
|
||||
func (a *Application) Shutdown() error {
|
||||
a.appLogger.Info("Stopping vikunja-notifier...")
|
||||
|
||||
a.cancelFunc()
|
||||
|
||||
for _, service := range a.services {
|
||||
if !strings.Contains(service.Name(), "features/") {
|
||||
continue
|
||||
}
|
||||
|
||||
a.appLogger.Debug("Shutting down service.", "service", service.Name())
|
||||
|
||||
if err := service.Shutdown(); err != nil {
|
||||
return fmt.Errorf("shutting down service '%s': %w", service.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, service := range a.services {
|
||||
if !strings.Contains(service.Name(), "core/") {
|
||||
continue
|
||||
}
|
||||
|
||||
a.appLogger.Debug("Shutting down service.", "service", service.Name())
|
||||
|
||||
if err := service.Shutdown(); err != nil {
|
||||
return fmt.Errorf("shutting down service '%s': %w", service.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start starts application.
|
||||
// Server application will start a goroutine that monitors SIGTERM and sends empty struct to channel.
|
||||
func (a *Application) Start() error {
|
||||
if err := a.connectDependencies(); err != nil {
|
||||
return fmt.Errorf("%w: %w", errApplication, err)
|
||||
}
|
||||
|
||||
if err := a.configure(); err != nil {
|
||||
return fmt.Errorf("%w: %w", errApplication, err)
|
||||
}
|
||||
|
||||
if err := a.launchStartupTasks(); err != nil {
|
||||
return fmt.Errorf("%w: %w", errApplication, err)
|
||||
}
|
||||
|
||||
a.startServer()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultLogLevel slog.Level = slog.LevelInfo
|
||||
|
||||
logLevelEnvVar = "VN_LOG_LEVEL"
|
||||
)
|
||||
|
||||
func (a *Application) initializeLogger() {
|
||||
logLevel := defaultLogLevel
|
||||
|
||||
logLevelAsString, found := os.LookupEnv(logLevelEnvVar)
|
||||
if found {
|
||||
switch strings.ToLower(logLevelAsString) {
|
||||
case "debug":
|
||||
logLevel = slog.LevelDebug
|
||||
case "info":
|
||||
logLevel = slog.LevelInfo
|
||||
case "warn":
|
||||
logLevel = slog.LevelWarn
|
||||
case "error":
|
||||
logLevel = slog.LevelError
|
||||
}
|
||||
}
|
||||
|
||||
slog.Warn("Setting log level.", "level", logLevel.String())
|
||||
|
||||
a.baseLogger = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
||||
AddSource: true,
|
||||
Level: logLevel,
|
||||
}))
|
||||
|
||||
a.appLogger = a.baseLogger.With("module", "application")
|
||||
}
|
||||
|
||||
// NewLogger creates new sub-instance of base logger and adds some additional data to it for persistent output.
|
||||
func (a *Application) NewLogger(withs ...any) *slog.Logger {
|
||||
return a.baseLogger.With(withs...)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// ShutdownChan returns shutdown channel for main function.
|
||||
func (a *Application) ShutdownChan() chan struct{} {
|
||||
return a.shutdownChan
|
||||
}
|
||||
|
||||
func (a *Application) startServer() {
|
||||
a.shutdownChan = make(chan struct{})
|
||||
|
||||
go func() {
|
||||
listener := make(chan os.Signal, 1)
|
||||
|
||||
signal.Notify(listener, syscall.SIGTERM, os.Interrupt)
|
||||
|
||||
<-listener
|
||||
|
||||
a.shutdownChan <- struct{}{}
|
||||
}()
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrServiceAlreadyRegistered returns if trying to register a service with name already taken by other service.
|
||||
ErrServiceAlreadyRegistered = errors.New("service with such name already registered")
|
||||
// ErrServiceNotFound returns if trying to gather service with unknown name.
|
||||
ErrServiceNotFound = errors.New("service with such name wasn't found")
|
||||
)
|
||||
|
||||
// Service is an interface every service should conform to. Specific services will have own interface for
|
||||
// cross-service interation.
|
||||
type Service interface {
|
||||
// Configure configures service. Called after ConnectDependencies and before LaunchStartupTasks.
|
||||
Configure() error
|
||||
// ConnectDependencies gets necessary dependencies.
|
||||
ConnectDependencies() error
|
||||
// Initialize initializes service's internal state. Called while registering service with Application
|
||||
// lifecycle controller.
|
||||
Initialize() error
|
||||
// Name returns service name.
|
||||
Name() string
|
||||
// LaunchStartupTasks launches tasks on application start. Called after ConnectDependencies and Configure.
|
||||
LaunchStartupTasks() error
|
||||
// Shutdown stops service.
|
||||
Shutdown() error
|
||||
}
|
||||
|
||||
// RegisterService registering service with lifecycle controller for later use in any other service.
|
||||
func (a *Application) RegisterService(srv Service) error {
|
||||
var found bool
|
||||
|
||||
for _, knownService := range a.services {
|
||||
if srv.Name() == knownService.Name() {
|
||||
found = true
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
return fmt.Errorf(
|
||||
"%w: RegisterService: check for service '%s' registration: %w",
|
||||
errApplication,
|
||||
srv.Name(),
|
||||
ErrServiceAlreadyRegistered,
|
||||
)
|
||||
}
|
||||
|
||||
if err := srv.Initialize(); err != nil {
|
||||
return fmt.Errorf("%w: RegisterService: initialize service '%s': %w", errApplication, srv.Name(), err)
|
||||
}
|
||||
|
||||
a.services = append(a.services, srv)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Service returns requested service.
|
||||
func (a *Application) Service(name string) Service {
|
||||
var srv Service
|
||||
|
||||
for _, knownService := range a.services {
|
||||
if knownService.Name() == name {
|
||||
srv = knownService
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if srv == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return srv
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package commons
|
||||
|
||||
var (
|
||||
// Branch это ветка, из которой собрано приложение.
|
||||
Branch string
|
||||
// Build это порядковый номер сборки.
|
||||
Build string
|
||||
// BuildDate это таймштамп сборки приложения.
|
||||
BuildDate string
|
||||
// Commit это коммит, из которого собрано приложение.
|
||||
Commit string
|
||||
// Version это версия, из которой собрано приложение.
|
||||
Version string
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ServiceNameHTTPServer is a name for HTTP server service.
|
||||
const ServiceNameHTTPServer = "core/http_server"
|
||||
|
||||
// ErrHTTPServerIsInvalid appears when HTTP server service implementation is invalid.
|
||||
var ErrHTTPServerIsInvalid = errors.New("HTTP server service implementation is invalid")
|
||||
|
||||
// HTTPServer is an interface for HTTP server service.
|
||||
type HTTPServer interface {
|
||||
// RegisterHandler registers HTTP handler.
|
||||
RegisterHandler(method, path string, handler http.HandlerFunc)
|
||||
// RegisterMiddleware registers HTTP server middlewares.
|
||||
RegisterMiddleware(middleware HTTPMiddlewareFunc)
|
||||
}
|
||||
|
||||
// HTTPMiddlewareFunc is a function that acts as middleware for HTTP requests.
|
||||
type HTTPMiddlewareFunc func(fn http.HandlerFunc) http.HandlerFunc
|
||||
@@ -0,0 +1,8 @@
|
||||
package httpserver
|
||||
|
||||
import "net/http"
|
||||
|
||||
func (h *httpServer) defaultHandler(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
_, _ = w.Write([]byte("Unknown path."))
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"go.dev.pztrn.name/vikunja-notifier/internal/application"
|
||||
"go.dev.pztrn.name/vikunja-notifier/internal/services/core"
|
||||
)
|
||||
|
||||
var (
|
||||
_ = core.HTTPServer(&httpServer{})
|
||||
|
||||
errHTTPServer = errors.New("HTTP server core service")
|
||||
)
|
||||
|
||||
type httpServer struct {
|
||||
app *application.Application
|
||||
logger *slog.Logger
|
||||
httpSrv *http.Server
|
||||
httpMux *http.ServeMux
|
||||
middlewares []core.HTTPMiddlewareFunc
|
||||
}
|
||||
|
||||
// Initialize initializes service.
|
||||
func Initialize(app *application.Application) error {
|
||||
httpSrv := &httpServer{
|
||||
app: app,
|
||||
}
|
||||
|
||||
if err := app.RegisterService(httpSrv); err != nil {
|
||||
return fmt.Errorf("%w: %w", errHTTPServer, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpServer) Configure() error {
|
||||
h.logger.Debug("Configuring service...")
|
||||
|
||||
if err := h.configureHTTPServer(); err != nil {
|
||||
return fmt.Errorf("configure: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpServer) ConnectDependencies() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpServer) Initialize() error {
|
||||
h.logger = h.app.NewLogger("service", core.ServiceNameHTTPServer)
|
||||
|
||||
h.logger.Info("Initializing...")
|
||||
|
||||
h.middlewares = make([]core.HTTPMiddlewareFunc, 0)
|
||||
|
||||
h.RegisterMiddleware(h.requestLoggingMiddleware)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpServer) Name() string {
|
||||
return core.ServiceNameHTTPServer
|
||||
}
|
||||
|
||||
func (h *httpServer) LaunchStartupTasks() error {
|
||||
h.logger.Debug("Launching startup tasks...")
|
||||
|
||||
go h.startHTTPServer()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpServer) Shutdown() error {
|
||||
if err := h.stopHTTPServer(); err != nil {
|
||||
return fmt.Errorf("%w: Shutdown: %w", errHTTPServer, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (h *httpServer) requestLoggingMiddleware(fn http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
startTime := time.Now()
|
||||
|
||||
fn(w, r)
|
||||
|
||||
h.logger.Info(
|
||||
"HTTP request.",
|
||||
"remote_addr", r.RemoteAddr,
|
||||
"user_agent", r.UserAgent(),
|
||||
"host", r.Host,
|
||||
"path", fmt.Sprintf("%s %s", r.Method, r.RequestURI),
|
||||
"duration", time.Since(startTime),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"go.dev.pztrn.name/vikunja-notifier/internal/services/core"
|
||||
)
|
||||
|
||||
const httpServerAddrEnvVar = "VN_HTTP_ADDRESS"
|
||||
|
||||
var (
|
||||
errHTTPServerAddrInvalid = errors.New("VN_HTTP_ADDRESS environment variable contains invalid address to " +
|
||||
"listen, should be 'host:port'")
|
||||
errHTTPServerAddrNotFound = errors.New("VN_HTTP_ADDRESS environment variable empty")
|
||||
)
|
||||
|
||||
func (h *httpServer) configureHTTPServer() error {
|
||||
httpSrvAddr, found := os.LookupEnv(httpServerAddrEnvVar)
|
||||
if !found {
|
||||
return fmt.Errorf("configure HTTP server: get address from environment variable: %w", errHTTPServerAddrNotFound)
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(httpSrvAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("configure HTTP server: validate HTTP server address: %w", err)
|
||||
}
|
||||
|
||||
if httpSrvAddr != host+":"+port {
|
||||
return fmt.Errorf("configure HTTP server: validate HTTP server address: %w", errHTTPServerAddrInvalid)
|
||||
}
|
||||
|
||||
h.httpMux = new(http.ServeMux)
|
||||
|
||||
// Default catch-all handler.
|
||||
h.RegisterHandler("", "/", h.defaultHandler)
|
||||
|
||||
h.httpSrv = &http.Server{
|
||||
Addr: httpSrvAddr,
|
||||
Handler: h.httpMux,
|
||||
ReadHeaderTimeout: time.Second * 3,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpServer) RegisterHandler(method, path string, handler http.HandlerFunc) {
|
||||
h.httpMux.HandleFunc(fmt.Sprintf("%s %s", method, path), func(w http.ResponseWriter, r *http.Request) {
|
||||
for i := len(h.middlewares) - 1; i >= 0; i-- {
|
||||
handler = h.middlewares[i](handler)
|
||||
}
|
||||
|
||||
handler(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func (h *httpServer) RegisterMiddleware(middleware core.HTTPMiddlewareFunc) {
|
||||
h.middlewares = append(h.middlewares, middleware)
|
||||
}
|
||||
|
||||
func (h *httpServer) startHTTPServer() {
|
||||
h.logger.Info("Starting listening for HTTP requests.", "address", h.httpSrv.Addr)
|
||||
|
||||
if err := h.httpSrv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
h.logger.Warn("Error when listening to ", "error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (h *httpServer) stopHTTPServer() error {
|
||||
h.logger.Info("Stopping HTTP server...")
|
||||
|
||||
if err := h.httpSrv.Shutdown(h.app.ContextWithTimeout(time.Second * 3)); err != nil {
|
||||
return fmt.Errorf("stopping HTTP server: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"go.dev.pztrn.name/vikunja-notifier/internal/application"
|
||||
"go.dev.pztrn.name/vikunja-notifier/internal/commons"
|
||||
"go.dev.pztrn.name/vikunja-notifier/internal/services/core/httpserver"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := application.New()
|
||||
|
||||
lgr := app.NewLogger("module", "main")
|
||||
lgr.Info(
|
||||
"Starting vikunja-notifier...",
|
||||
"version", commons.Version,
|
||||
"build_no", commons.Build,
|
||||
"buint_on", commons.BuildDate,
|
||||
"commit", commons.Commit,
|
||||
"branch", commons.Branch,
|
||||
)
|
||||
|
||||
if err := app.Start(); err != nil {
|
||||
lgr.Error("Failed to start vikunja-notifier!", "error", err.Error())
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := httpserver.Initialize(app); err != nil {
|
||||
lgr.Error("Failed to initialize vikunja-notifier", "error", err.Error())
|
||||
}
|
||||
|
||||
if err := app.Start(); err != nil {
|
||||
lgr.Error("Failed to start vikunja-notifier", "error", err.Error())
|
||||
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
lgr.Info("vikunja-notifier started.")
|
||||
|
||||
<-app.ShutdownChan()
|
||||
lgr.Info("Shutting down vikunja-notifier...")
|
||||
|
||||
if err := app.Shutdown(); err != nil {
|
||||
lgr.Error("Failed to shutdown vikunja-notifier!", "error", err.Error())
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Generates version based on git tag.
|
||||
LATEST_TAG=$(git tag | tail -n 1)
|
||||
|
||||
# Check latest tag commit. If it is equal to current - use that tag as version.
|
||||
if [ "${LATEST_TAG}" != "" ]; then
|
||||
LATEST_TAG_COMMIT=$(git rev-list -n 1 $LATEST_TAG)
|
||||
LATEST_COMMIT=$(git rev-list -n 1 HEAD)
|
||||
|
||||
if [ "${LATEST_TAG_COMMIT}" == "${LATEST_COMMIT}" ]; then
|
||||
echo "${LATEST_TAG}"
|
||||
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
# No tags in repo. Assuming latest tag is "v0.0.0".
|
||||
LATEST_TAG="0.0.0"
|
||||
fi
|
||||
|
||||
# If we're here, then latest tag commit is not latest commit on current branch.
|
||||
# We should increase second number and add "-dev" postfix.
|
||||
IFS=. read MAJOR MINOR PATCH <<<"${LATEST_TAG}"
|
||||
MINOR=$(($MINOR + 1))
|
||||
|
||||
case $1 in
|
||||
client)
|
||||
echo "${MAJOR}.${MINOR}.${PATCH}"
|
||||
;;
|
||||
*)
|
||||
echo "v${MAJOR}.${MINOR}.${PATCH}-dev"
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user