Skip to content

Improved the cancellation propagation and added more documentation #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 28, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
51 changes: 23 additions & 28 deletions .golangci.yaml
Original file line number Diff line number Diff line change
@@ -2,10 +2,6 @@ run:
# Timeout for analysis, e.g. 30s, 5m.
timeout: 3m

# Don't lint the vendor directory
skip-dirs:
- ^vendor$

# Fail if the error was met.
issues-exit-code: 1

@@ -19,11 +15,6 @@ run:
build-tags:
- integration

# When enabled linter will skip directories: vendor$, third_party$,
# testdata$, examples$, Godeps$, builtin$ Skipping `examples` sounds scary
# to me but skipping `testdata` sounds ok.
skip-dirs-use-default: false

# With the read-only mode linter will fail if go.mod file is outdated.
modules-download-mode: readonly

@@ -45,12 +36,11 @@ linters:
- asciicheck # checks that your code does not contain non-ASCII identifiers
- bidichk # checks for dangerous unicode character sequences
- bodyclose # checks whether HTTP response body is closed successfully
- cyclop # checks function and package cyclomatic complexity
# - cyclop # checks function and package cyclomatic complexity
- dupl # tool for code clone detection
- durationcheck # checks for two durations multiplied together
- errname # checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error
- errorlint # finds code that will cause problems with the error wrapping scheme introduced in Go 1.13
- execinquery # checks query string in Query function which reads your Go src files and warning it finds
- exhaustive # checks exhaustiveness of enum switch statements
- exportloopref # checks for pointers to enclosing loop variables
- forbidigo # forbids identifiers
@@ -69,7 +59,6 @@ linters:
- gomodguard # allow and block lists linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations
- goprintffuncname # checks that printf-like functions are named with f at the end
- gosec # inspects source code for security problems
- lll # reports long lines
- loggercheck # checks key value pairs for common logger libraries (kitlog,klog,logr,zap)
- makezero # finds slice declarations with non-zero initial length
- mirror # reports wrong mirror patterns of bytes/strings usage
@@ -101,15 +90,13 @@ linters:
- usestdlibvars # detects the possibility to use variables/constants from the Go standard library
- wastedassign # finds wasted assignment statements
- whitespace # detects leading and trailing whitespace
- contextcheck # Check whether the function uses a non-inherited context.
- misspell # finds commonly misspelled English words in comments
- prealloc # can be used to find slice declarations that could potentially be preallocated
- thelper # detects golang test helpers without t.Helper() call and checks the consistency of test helpers
- containedctx # detects struct contained context.Context field
- decorder # checks declaration order and count of types, constants, variables and functions
- dogsled # checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
# - exhaustruct # [highly recommend to enable] checks if all structure fields are initialized
- nlreturn # checks for a new line before return and branch statements to increase code clarity
- paralleltest # detects missing usage of t.Parallel() method in your Go test

## you may want to enable
#- gci # controls golang package import order and makes it always deterministic
@@ -123,8 +110,11 @@ linters:
#- varnamelen # [great idea, but too many false positives] checks that the length of a variable's name matches its scope
#- wrapcheck # checks that errors returned from external packages are wrapped
#- zerologlint # detects the wrong usage of zerolog that a user forgets to dispatch zerolog.Event
# - nlreturn # checks for a new line before return and branch statements to increase code clarity

## disabled
# - lll # reports long lines
# - exhaustruct # [highly recommend to enable] checks if all structure fields are initialized
#- depguard # [replaced by gomodguard] checks if package imports are in a list of acceptable packages
#- dupword # [useless without config] checks for duplicate words in the source code
#- errchkjson # [don't see profit + I'm against of omitting errors like in the first example https://github.com/breml/errchkjson] checks types passed to the json encoding functions. Reports unsupported types and optionally reports occasions, where the check for the returned error can be omitted
@@ -136,13 +126,15 @@ linters:
#- grouper # analyzes expression groups
#- importas # enforces consistent import aliases
#- maintidx # measures the maintainability index of each function
#- paralleltest # [too many false positives] detects missing usage of t.Parallel() method in your Go test
#- wsl # [too strict and mostly code is not more readable] whitespace linter forces you to use empty lines
#- tagliatelle # checks the struct tags
#- contextcheck # Check whether the function uses a non-inherited context.

# This file contains only configs which differ from defaults.
# All possible options can be found here https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml
linters-settings:
nestif:
min-complexity: 8
cyclop:
# The maximal code complexity to report.
# Default: 10
@@ -168,7 +160,7 @@ linters-settings:
- map

nlreturn:
block-size: 120
block-size: 8

exhaustruct:
# Default: []
@@ -202,18 +194,19 @@ linters-settings:
# Checks the number of lines in a function.
# If lower than 0, disable the check.
# Default: 60
lines: 100
lines: 150
# Checks the number of statements in a function.
# If lower than 0, disable the check.
# Default: 40
statements: 50
statements: 60
# Ignore comments when counting lines.
# Default false
ignore-comments: true

gocognit:
# Minimal code complexity to report.
min-complexity: 30
# Default: 30 (but we recommend 10-20)
min-complexity: 70

gocritic:
# Settings passed to gocritic.
@@ -256,12 +249,6 @@ linters-settings:
# Default: []
disable:
- fieldalignment # too strict
# Settings per analyzer.
settings:
shadow:
# Whether to be strict about shadowing; can be noisy.
# Default: false
strict: true

nakedret:
# Make an issue if func has more lines of code than this setting, and it has naked returns.
@@ -297,6 +284,15 @@ issues:
# Default: 3
max-same-issues: 50

# Don't lint the vendor or example directories.
exclude-dirs:
- ^vendor$
- examples

# When enabled, the following directories: are going to be skipped: vendor$,
# third_party$, testdata$, examples$, Godeps$, builtin$.
exclude-dirs-use-default: false

exclude-rules:
- source: "(noinspection|TODO)"
linters: [godot]
@@ -312,6 +308,5 @@ issues:
- noctx
- wrapcheck
- exhaustruct
- path: "client"
linters:
- unparam
- errcheck
53 changes: 53 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
before:
hooks:
- go mod tidy
builds:
- main: ./cmd/server
binary: server
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
goarch:
- arm64
- amd64
- 386
output:
dir: dist/{{ .Os }}/{{ .Arch }}

- main: ./cmd/client
binary: client
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
goarch:
- amd64
- 386
output:
dir: dist/{{ .Os }}/{{ .Arch }}

archives:
- format: tar.gz
name_template: >-
{{ .ProjectName }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
{{- if .Arm }}v{{ .Arm }}{{ end }}
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ incpatch .Version }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'

# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
12 changes: 4 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# Include variables from the .envrc file
include .envrc

.DEFAULT_GOAL := build

# ==================================================================================== #
@@ -36,19 +33,18 @@ test: test
@echo 'Removing test cache...'
go clean -testcache
@echo 'Running tests...'
go test -race -vet=off -timeout 30s ./...
go test -race -vet=off -timeout 10s ./...


## audit: tidy and vendor dependencies and format, vet and test all code
.PHONY: audit
audit: vendor
@echo 'Formatting code...'
go fmt ./...
@echo 'Vetting code...'
go vet ./...
staticcheck ./...
@echo 'Linting code...'
golangci-lint run
@echo 'Running tests...'
go test -race -vet=off ./...
.PHONY:audit

## vendor: tidy and vendor dependencies
vendor:
116 changes: 96 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -13,32 +13,108 @@ on my [website][1]
![Screenshot of website][3]

# How it works

After spending some time debugging different language servers in Neovim, I felt
inspired to write my own server that would simply parse metadata and aggregate
statistics about my coding sessions so that I could display it on my website.
statistics about my coding sessions.

I launch the server from this repository as a daemon every time my laptop
boots. It then receives remote procedure calls from the neovim plugin
pertaining to events such as the opening of buffers, windows gaining focus, the
initiation of new `nvim` processes, etc.
I run the server from this repository as a daemon, and it receives remote
procedure calls from the neovim plugin pertaining to events such as the opening
of buffers, windows gaining focus, the initiation of new `nvim` processes, etc.

These calls contains the path to the buffer, which the server parses and writes
to an append-only log-structured key-value store. Every segment is roughly 10KB
on disk. The server requests all of the buffers from this KV store every 15
minutes, and proceeds to aggregate them to a remote MongoDB database.

I chose this approach primarily because I wanted to build a log-structured
storage engine in order to better understand the inner workings of some popular
databases. It is a work in progress, but it now includes some core features
such as hash indexes, segmentation, and compaction. As a bonus, it helps me
avoid surpassing the limits set by the free tier for the MongoDB database!

This project has evolved into a playground where I can experiment with
different ideas and technologies. The most challenging part so far has been
designing the website, as I wanted it to have a unique look and feel and to
build all of the components from scratch. I'm in the process of making it open
source, but I have a few things that I'd like to clean up first.
to a log-structured key-value store. The store is a work in progress, but it
now includes some core features such as hash indexes, segmentation, and
compaction.

The server runs a background job which requests all of the buffers from the KV
store, and proceeds to aggregate them to a remote database. I chose this
approach primarily because I wanted to avoid surpassing the limits set by the
free tier for the MongoDB database.

The only things that aren't included in this repository is the API which
retrieves the data and the website that displays it. The website has been the
most challenging part so far because I wanted it to have a unique look and feel
and to build all of the components from scratch. I'm in the process of making
it open source, but there are still a few things that I'd like to clean up
first.

# Running this project

## 1. Download the binaries
Download and unpack the server and client binaries from the [releases](https://github.com/creativecreature/sturdyc/releases).
Next, you'll want to make sure that they are reachable from your `$PATH`.

## 2. Create a configuration file
Create a configuration file. It should be located at `$HOME/.pulse/config.yaml`

```yml
server:
name: "pulse-server"
hostname: "localhost"
port: "1122"
aggregationInterval: "10m"
segmentationInterval: "5m"
database:
uri: "mongodb+srv://<USERNAME>:xxxxxxx@serverless.xxxx.mongodb.net/?retryWrites=true"
name: "pulse"
collection: "sessions"
```

## 3. Launch the server as a daemon
On linux, you can setup a systemd service to run the server, and on macOS you
can create a launch daemon.

I'm using a Mac, and my launch daemon configuration looks like this:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>

<key>Label</key>
<string>dev.conner.pulse.plist</string>

<key>RunAtLoad</key>
<true/>

<key>StandardErrorPath</key>
<string>/Users/conner/.pulse/logs/stderr.log</string>

<key>StandardOutPath</key>
<string>/Users/conner/.pulse/logs/stdout.log</string>

<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string><![CDATA[/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin]]></string>
</dict>

<key>WorkingDirectory</key>
<string>/Users/conner</string>

<key>ProgramArguments</key>
<array>
<string>/Users/conner/bin/pulse-server</string>
</array>

<key>KeepAlive</key>
<true/>

</dict>
</plist>
```

## 4. Install the neovim plugin
Here is an example using lazy.nvim:

```lua
return {
-- Does not require any configuration.
{ "creativecreature/pulse" },
}
```

[1]: https://conner.dev
[2]: ./screenshots/website1.png
Loading