Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
APP_ENV = development
SONG_SPOTIFY_CLIENT_ID = your_client_id
SONG_SPOTIFY_CLIENT_SECRET = your_client_secret
APP_ENV=
SONG_SPOTIFY_CLIENT_ID=
SONG_SPOTIFY_CLIENT_SECRET=
4 changes: 2 additions & 2 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
echo "Linting"
golangci-lint run
if [ $? -ne 0 ]; then
echo "golangci-lint failed. Please fix the errors before committing."
exit 1
echo "golangci-lint failed."
exit 1
fi
8 changes: 4 additions & 4 deletions .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ permissions:
pull-requests: read

jobs:
golangci:
lint:
name: lint
runs-on: ubuntu-latest

Expand All @@ -22,9 +22,9 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.23.1
go-version: 1.25.0

- name: golangci-lint
uses: golangci/golangci-lint-action@v6
uses: golangci/golangci-lint-action@v7
with:
version: v1.60
version: v2.4.0
10 changes: 5 additions & 5 deletions .github/workflows/sqlc-diff.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ jobs:
diff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: sqlc-dev/setup-sqlc@v3
with:
sqlc-version: '1.27.0'
- run: sqlc diff
- uses: actions/checkout@v4
- uses: sqlc-dev/setup-sqlc@v3
with:
sqlc-version: "1.29.0"
- run: sqlc diff
86 changes: 58 additions & 28 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,35 +1,65 @@
version: "2"

issues:
max-issues-per-linter: 0
max-same-issues: 0
fix: true

run:
timeout: 5m
modules-download-mode: readonly

linters:
enable:
- bodyclose # checks whether HTTP response body is closed successfully
- copyloopvar # detects copy loop variable
- errcheck # checks for unchecked errors in go programs
- errname # checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`
- gochecknoinits # checks that no init functions are present in Go code
- goimports # check import statements are formatted correctly
- gosimple # checks for code simplifications in Go code
- govet # runs the go vet tool
- importas # enforces consistent import aliases
- ineffassign # detects when assignments to existing variables are not used
- noctx # finds sending http request without context.Context
- paralleltest # detects missing usage of t.Parallel() method in go tests
- prealloc # finds slice declarations that could potentially be preallocated
- revive # checks for golang coding style
- rowserrcheck # checks whether Err of rows is checked successfully
- sqlclosecheck # checks that sql.Rows and sql.Stmt are closed
- staticcheck # Applies static code analysis
- tenv # detects using os.Setenv instead of t.Setenv
- testpackage # makes you use a separate _test package
- thelper # detects golang test helpers without t.Helper() call and checks consistency of test helpers
- unconvert # removes unnecessary type conversions
- unparam # removes unused function parameters
- unused # finds unused variables
fast: true
- copyloopvar # https://github.com/karamaru-alpha/copyloopvar?tab=readme-ov-file
- errchkjson # https://github.com/breml/errchkjson
- errname # https://github.com/Antonboom/errname
- errorlint # https://github.com/polyfloyd/go-errorlint
- exhaustive # https://github.com/nishanths/exhaustive
- exptostd # https://github.com/ldez/exptostd
- gocritic # https://github.com/go-critic/go-critic?tab=readme-ov-file
- loggercheck # https://github.com/timonwong/loggercheck
- perfsprint # https://github.com/catenacyber/perfsprint
- prealloc # https://github.com/alexkohler/prealloc
- revive # https://github.com/mgechev/revive?tab=readme-ov-file#available-rules
- unconvert # https://github.com/mdempsky/unconvert
- unparam # https://github.com/mvdan/unparam

issues:
exclude-use-default: false
max-issues-per-linter: 0
max-same-issues: 0
settings:
copyloopvar:
check-alias: true
errchkjson:
report-no-exported: true
exhaustive:
default-signifies-exhaustive: true
loggercheck:
kitlog: false
klog: false
logr: false
slog: false
revive:
rules:
- name: blank-imports
- name: context-as-argument
- name: dot-imports
- name: empty-block
- name: error-naming
- name: error-return
- name: error-strings
- name: errorf
- name: increment-decrement
- name: indent-error-flow
- name: range
- name: receiver-naming
- name: redefines-builtin-id
- name: superfluous-else
- name: time-naming
- name: unexported-return
- name: unreachable-code
- name: unused-parameter
- name: var-declaration
- name: var-naming
arguments:
- []
- []
- - skip-package-name-checks: true
3 changes: 2 additions & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
golang 1.23.1
golang 1.25.0
golangci-lint 2.4.0
97 changes: 54 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,70 +1,81 @@
# Screen Cammie Chat

Displays the cammie chat along with some other statistics.
A terminal based dashboard for viewing the Cammie chat messages and other Zeus related data.

## Development Setup
## Overview

### Prerequisites
The project has 2 main parts:

1. Go: Check the [.tool-versions](.tool-versions) file for the required Go version.
2. Pre-commit hooks: `git config --local core.hooksPath .githooks/`.
3. Goose (DB migrations): `go install github.com/pressly/goose/v3/cmd/goose@latest`.
4. SQLC (Statically typed queries): `go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest`
5. Make (Optional)
### 1. TUI

### Configuration
A terminal interface built with [bubbletea](https://github.com/charmbracelet/bubbletea).
It displays the Cammie chat messages and other Zeus data in a screen-based layout.

1. Create a `.env` file specifying
- `APP_ENV`. Available options are:
- `development`
- `production`
- `BACKEND_SONG_SPOTIFY_CLIENT_ID`
- `BACKEND_SONG_SPOTIFY_CLIENT_SECRET`
2. Configure the appropriate settings in the corresponding configuration file located in the [config directory](./config). you can either set them as environment variables or inside the configuration file.
- **Views**: Reusable components responsible for rendering a single type of data (e.g. the [TAP](github.com/zeusWPI/tap) statistics).
- **Screens**: A combination of multiple views forming a single terminal screen.

## DB
Each view implements a shared interface, exposing methods for initialization, updating, and rendering.
When a screen is loaded:

This project uses a postgres database.
SQLC is used to generate statically typed queries and goose is responsible for the database migrations.
- Each view’s initial data is fetched automatically.
- Each view’s update loop runs in a goroutine, periodically checking for new data.
- The update loop communicates state changes back to the TUI.

### Usefull commands
This design allows each view to be self-contained and independently refresh its data.

- `make db`: Start the database
- `make migrate`: Run database migrations to update your database schema (watch out, migrations might result in minor data loss).
- `make create-migration`: Create a new migration in the [db/migrations](./db/migrations/) directory.
- `make sqlc`: Generate statically typed queries based on the .sql files in the [db/queries](./db/queries/) directory. Add new queries to this directory as needed.
However, not all data can be retrieved directly from persistent external services and that’s where the backend comes in.

## Backend
### 2. Backend

The backend is responsible for fetching and processing external data, which is then stored in the database.
Data can either received by exposing an API or by actively fetching them.
The backend handles data aggregation and persistence.

### Running the backend
- Exposes an API for incoming data.
- Maintaines websockets.
- Makes use of external API's.

To build and run the backend, use the following commands:
Provides persistence data using Postgres for data that the external services do not retain.

- `make build-backend`: Build the backend binary.
- `make run-backend`: Run the backend.
## Development

### Logs
### Prerequisites

Backend logs are saved to `./logs/backend.log` (created on first start) and written to `stdout`.
1. Download the golang version [.tool-versions](.tool-versions).
2. Install make.
3. Install the go tools: `make setup`.
4. (Optional) Install the pre-commit hooks: `git config --local core.hooksPath .githooks/`.

## TUI
### Configuration

The TUI (Text User Interface) displays data retrieved from the database. This flexibility allows for running multiple instances of the TUI, each displaying different data.
1. Copy `.env.example` to `.env`, set `ENV=development` and populate the remaining keys.
2. (Optional) Edit the [config file](./config/development.yml). The defaults work.

### Running the TUI
### Database

To build and run the TUI, use the following commands:
A Postgres database instance is provided via docker compose and started automatically when needed by the [makefile](./makefile).
To use a custom database, update the config and edit the makefile.

- `make build-tui`: Build the TUI binary.
- `make run-tui`: Run the TUI.
-
The TUI requires one argument: the screen name to display. You can create new screens in the [screens directory](./tui/screen/), and you must add them to the startup command list in [tui.go](./internal/cmd/tui.go).
### Run

A screen is responsible for creating and managing the layout, consisting of various [views](./tui/view/).
1. Migrate the database `make migrate`.
2. Start the backend `make backend`.
3. Start a TUI `make tui` and enter the desired screen name (if you're not using the makefile use the `-screen` flag to specify the screen).

### Logs

TUI logs are written to `./logs/tui.log` and _not_ to `stdout`.
- Backend: written to `./logs/backend.log` and to stdout.
- TUI: only written to `./logs/{screen}.log`.

### Useful commands

- `make create-migration`: Create a new migration in the [db/migrations](./db/migrations/) directory.
- `make query`: Generate statically typed queries based on the .sql files in the [db/queries](./db/queries/) directory. Add new queries to this directory as needed.
- `make goose`: Migrate one version up or down.
- `make dead`: Check for unreachable code.

## Production

1. Set `ENV=production` in `.env`.
2. Provide a Postgres database.
3. Populate the [production config file](./config/production.yml).
4. Build the binaries `make build`.
5. Run both binaries with the desired flags.
43 changes: 28 additions & 15 deletions cmd/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package main

import (
"github.com/zeusWPI/scc/internal/cmd"
"github.com/zeusWPI/scc/internal/pkg/db"
"github.com/zeusWPI/scc/internal/database/repository"
"github.com/zeusWPI/scc/internal/server"
"github.com/zeusWPI/scc/internal/server/service"
"github.com/zeusWPI/scc/pkg/config"
"github.com/zeusWPI/scc/pkg/db"
"github.com/zeusWPI/scc/pkg/logger"
"go.uber.org/zap"
)
Expand All @@ -26,29 +29,39 @@ func main() {
zap.S().Info("Initializing backend")

// Database
db, err := db.New()
db, err := db.NewPSQL()
if err != nil {
zap.S().Fatal("DB: Fatal error\n", err)
zap.S().Fatalf("DB: Fatal error %v", err)
}

// Repository
repo := repository.New(db)

var dones []chan bool

// Tap
_, _ = cmd.Tap(db)
_, done := cmd.Tap(*repo)
dones = append(dones, done)

// Zess
_, _, _ = cmd.Zess(db)
_, done = cmd.Zess(*repo)
dones = append(dones, done)

// Gamification
_, _ = cmd.Gamification(db)
// Song
if err := cmd.Song(); err != nil {
zap.S().Fatalf("Initialize song %v", err)
}

// Event
_, _ = cmd.Event(db)
// API
service := service.New(*repo)
api := server.New(*service)

// Spotify
spotify, err := cmd.Song(db)
if err != nil {
zap.S().Error("Spotify: Initiating error, integration will not work.\n", err)
zap.S().Infof("Server is running on %s", api.Addr)
if err := api.Listen(api.Addr); err != nil {
zap.S().Fatalf("Failure while running the server %v", err)
}

// API
cmd.API(db, spotify)
for _, done := range dones {
done <- true
}
}
21 changes: 17 additions & 4 deletions cmd/tui/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
package main

import (
"flag"

"github.com/zeusWPI/scc/internal/cmd"
"github.com/zeusWPI/scc/internal/pkg/db"
"github.com/zeusWPI/scc/internal/database/repository"
"github.com/zeusWPI/scc/pkg/config"
"github.com/zeusWPI/scc/pkg/db"
"github.com/zeusWPI/scc/pkg/logger"
"go.uber.org/zap"
)
Expand All @@ -16,8 +19,16 @@ func main() {
panic(err)
}

screen := flag.String("screen", "", "TUI screen to start")
flag.Parse()

if *screen == "" {
flag.PrintDefaults()
return
}

// Logger
zapLogger, err := logger.New("tui", false)
zapLogger, err := logger.New(*screen, false)
if err != nil {
panic(err)
}
Expand All @@ -26,13 +37,15 @@ func main() {
zap.S().Info("Initializing TUI")

// Database
db, err := db.New()
db, err := db.NewPSQL()
if err != nil {
zap.S().Fatal("DB: Fatal error\n", err)
}

repo := repository.New(db)

// TUI
err = cmd.TUI(db)
err = cmd.TUI(*repo, *screen)
if err != nil {
zap.S().Fatal("TUI: Fatal error\n", err)
}
Expand Down
Loading