mirror of
https://github.com/dnote/dnote
synced 2026-03-14 22:45:50 +01:00
Automate release
This commit is contained in:
parent
23589ffdb8
commit
4734d82f8f
9 changed files with 190 additions and 49 deletions
19
.github/workflows/ci.yml
vendored
19
.github/workflows/ci.yml
vendored
|
|
@ -11,25 +11,6 @@ jobs:
|
|||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: dnote_test
|
||||
POSTGRES_PORT: 5432
|
||||
# Wait until postgres has started
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
||||
# Expose port to the host
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-go@v6
|
||||
|
|
|
|||
91
.github/workflows/release-server.yml
vendored
Normal file
91
.github/workflows/release-server.yml
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
name: Release Server
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'server-v*'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '>=1.25.0'
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Extract version from tag
|
||||
id: version
|
||||
run: |
|
||||
TAG=${GITHUB_REF#refs/tags/server-v}
|
||||
echo "version=$TAG" >> $GITHUB_OUTPUT
|
||||
echo "Releasing version: $TAG"
|
||||
|
||||
- name: Install dependencies
|
||||
run: make install
|
||||
|
||||
- name: Run tests
|
||||
run: make test
|
||||
|
||||
- name: Build server
|
||||
run: make version=${{ steps.version.outputs.version }} build-server
|
||||
|
||||
- name: Prepare Docker build context
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
cp build/server/dnote_server_${VERSION}_linux_amd64.tar.gz host/docker/
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ./host/docker
|
||||
push: true
|
||||
tags: |
|
||||
dnote/dnote:${{ steps.version.outputs.version }}
|
||||
dnote/dnote:latest
|
||||
build-args: |
|
||||
tarballName=dnote_server_${{ steps.version.outputs.version }}_linux_amd64.tar.gz
|
||||
|
||||
- name: Create GitHub release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
TAG="server-v${VERSION}"
|
||||
|
||||
# Determine if prerelease (version not matching major.minor.patch)
|
||||
FLAGS=""
|
||||
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
FLAGS="--prerelease"
|
||||
fi
|
||||
|
||||
gh release create "$TAG" \
|
||||
build/server/*.tar.gz \
|
||||
build/server/*_checksums.txt \
|
||||
$FLAGS \
|
||||
--title="$TAG" \
|
||||
--notes="Please see the [CHANGELOG](https://github.com/dnote/dnote/blob/master/CHANGELOG.md)" \
|
||||
--draft
|
||||
|
||||
- name: Push to Docker Hub
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
|
||||
echo "$DOCKER_TOKEN" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
||||
docker push dnote/dnote:${VERSION}
|
||||
docker push dnote/dnote:latest
|
||||
|
|
@ -19,17 +19,18 @@ mv ./dnote-server /usr/local/bin
|
|||
3. Run Dnote
|
||||
|
||||
```bash
|
||||
APP_ENV=PRODUCTION \
|
||||
WebURL=$webURL \
|
||||
DisableRegistration=false \
|
||||
dnote-server start
|
||||
dnote-server start --webUrl=$webURL
|
||||
```
|
||||
|
||||
Replace `$webURL` with the full URL to your server, without a trailing slash (e.g. `https://your.server`).
|
||||
|
||||
Set `DisableRegistration=true` if you would like to disable user registrations.
|
||||
Additional flags:
|
||||
- `--port`: Server port (default: `3000`)
|
||||
- `--disableRegistration`: Disable user registration (default: `false`)
|
||||
- `--logLevel`: Log level: `debug`, `info`, `warn`, or `error` (default: `info`)
|
||||
- `--appEnv`: environment (default: `PRODUCTION`)
|
||||
|
||||
By default, dnote server will run on the port 3000.
|
||||
You can also use environment variables: `PORT`, `WebURL`, `DisableRegistration`, `LOG_LEVEL`, `APP_ENV`.
|
||||
|
||||
## Configuration
|
||||
|
||||
|
|
@ -111,9 +112,7 @@ User=$user
|
|||
Restart=always
|
||||
RestartSec=3
|
||||
WorkingDirectory=/home/$user
|
||||
ExecStart=/usr/local/bin/dnote-server start
|
||||
Environment=APP_ENV=PRODUCTION
|
||||
Environment=WebURL=$WebURL
|
||||
ExecStart=/usr/local/bin/dnote-server start --webUrl=$WebURL
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
@ -121,7 +120,7 @@ WantedBy=multi-user.target
|
|||
|
||||
Replace `$user` and `$WebURL` with the actual values.
|
||||
|
||||
By default, the database will be stored at `$XDG_DATA_HOME/dnote/server.db` (typically `~/.local/share/dnote/server.db`). To use a custom location, add `Environment=DBPath=/path/to/database.db` to the service file.
|
||||
By default, the database will be stored at `$XDG_DATA_HOME/dnote/server.db` (typically `~/.local/share/dnote/server.db`). To use a custom location, add `--dbPath=/path/to/database.db` to the `ExecStart` command.
|
||||
|
||||
2. Reload the change by running `sudo systemctl daemon-reload`.
|
||||
3. Enable the Daemon by running `sudo systemctl enable dnote`.`
|
||||
|
|
@ -150,7 +149,7 @@ The following is an example configuration:
|
|||
|
||||
```yaml
|
||||
editor: nvim
|
||||
apiEndpoint: https://api.getdnote.com
|
||||
apiEndpoint: https://localhost:3000/api
|
||||
```
|
||||
|
||||
Simply change the value for `apiEndpoint` to a full URL to the self-hosted instance, followed by '/api', and save the configuration file.
|
||||
|
|
|
|||
|
|
@ -35,8 +35,9 @@ import (
|
|||
var testServerBinary string
|
||||
|
||||
func init() {
|
||||
// Use absolute path
|
||||
testServerBinary = "/home/device10/development/dnote/pkg/e2e/tmp/.dnote/test-server"
|
||||
// Build server binary in temp directory
|
||||
tmpDir := os.TempDir()
|
||||
testServerBinary = fmt.Sprintf("%s/dnote-test-server", tmpDir)
|
||||
buildCmd := exec.Command("go", "build", "-tags", "fts5", "-o", testServerBinary, "../server")
|
||||
if out, err := buildCmd.CombinedOutput(); err != nil {
|
||||
panic(fmt.Sprintf("failed to build server: %v\n%s", err, out))
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ type Config struct {
|
|||
DBPath string
|
||||
AssetBaseURL string
|
||||
HTTP500Page []byte
|
||||
LogLevel string
|
||||
}
|
||||
|
||||
// Params are the configuration parameters for creating a new Config
|
||||
|
|
@ -84,6 +85,7 @@ type Params struct {
|
|||
WebURL string
|
||||
DBPath string
|
||||
DisableRegistration bool
|
||||
LogLevel string
|
||||
}
|
||||
|
||||
// New constructs and returns a new validated config.
|
||||
|
|
@ -95,6 +97,7 @@ func New(p Params) (Config, error) {
|
|||
WebURL: getOrEnv(p.WebURL, "WebURL", ""),
|
||||
DBPath: getOrEnv(p.DBPath, "DBPath", DefaultDBPath),
|
||||
DisableRegistration: p.DisableRegistration || readBoolEnv("DisableRegistration"),
|
||||
LogLevel: getOrEnv(p.LogLevel, "LOG_LEVEL", "info"),
|
||||
AssetBaseURL: "/static",
|
||||
HTTP500Page: assets.MustGetHTTP500ErrorPage(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,9 +143,12 @@ func migrate(db *gorm.DB, fsys fs.FS) error {
|
|||
|
||||
log.WithFields(log.Fields{
|
||||
"version": version,
|
||||
"files": filenames,
|
||||
}).Info("Database schema version.")
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"files": filenames,
|
||||
}).Debug("Database migration files.")
|
||||
|
||||
// Apply pending migrations
|
||||
for _, m := range migrations {
|
||||
if m.version <= version {
|
||||
|
|
@ -178,6 +181,5 @@ func migrate(db *gorm.DB, fsys fs.FS) error {
|
|||
}).Info("Migrate success.")
|
||||
}
|
||||
|
||||
log.Info("Migration complete.")
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,9 +32,19 @@ const (
|
|||
fieldKeyTimestamp = "ts"
|
||||
fieldKeyUnixTimestamp = "ts_unix"
|
||||
|
||||
levelInfo = "info"
|
||||
levelWarn = "warn"
|
||||
levelError = "error"
|
||||
// LevelDebug represents debug log level
|
||||
LevelDebug = "debug"
|
||||
// LevelInfo represents info log level
|
||||
LevelInfo = "info"
|
||||
// LevelWarn represents warn log level
|
||||
LevelWarn = "warn"
|
||||
// LevelError represents error log level
|
||||
LevelError = "error"
|
||||
)
|
||||
|
||||
var (
|
||||
// currentLevel is the currently configured log level
|
||||
currentLevel = LevelInfo
|
||||
)
|
||||
|
||||
// Fields represents a set of information to be included in the log
|
||||
|
|
@ -58,19 +68,50 @@ func WithFields(fields Fields) Entry {
|
|||
return newEntry(fields)
|
||||
}
|
||||
|
||||
// SetLevel sets the global log level
|
||||
func SetLevel(level string) {
|
||||
currentLevel = level
|
||||
}
|
||||
|
||||
// levelPriority returns a numeric priority for comparison
|
||||
func levelPriority(level string) int {
|
||||
switch level {
|
||||
case LevelDebug:
|
||||
return 0
|
||||
case LevelInfo:
|
||||
return 1
|
||||
case LevelWarn:
|
||||
return 2
|
||||
case LevelError:
|
||||
return 3
|
||||
default:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// shouldLog returns true if the given level should be logged based on currentLevel
|
||||
func shouldLog(level string) bool {
|
||||
return levelPriority(level) >= levelPriority(currentLevel)
|
||||
}
|
||||
|
||||
// Debug logs the given entry at a debug level
|
||||
func (e Entry) Debug(msg string) {
|
||||
e.write(LevelDebug, msg)
|
||||
}
|
||||
|
||||
// Info logs the given entry at an info level
|
||||
func (e Entry) Info(msg string) {
|
||||
e.write(levelInfo, msg)
|
||||
e.write(LevelInfo, msg)
|
||||
}
|
||||
|
||||
// Warn logs the given entry at a warning level
|
||||
func (e Entry) Warn(msg string) {
|
||||
e.write(levelWarn, msg)
|
||||
e.write(LevelWarn, msg)
|
||||
}
|
||||
|
||||
// Error logs the given entry at an error level
|
||||
func (e Entry) Error(msg string) {
|
||||
e.write(levelError, msg)
|
||||
e.write(LevelError, msg)
|
||||
}
|
||||
|
||||
// ErrorWrap logs the given entry with the error message annotated by the given message
|
||||
|
|
@ -106,6 +147,10 @@ func (e Entry) formatJSON(level, msg string) []byte {
|
|||
}
|
||||
|
||||
func (e Entry) write(level, msg string) {
|
||||
if !shouldLog(level) {
|
||||
return
|
||||
}
|
||||
|
||||
serialized := e.formatJSON(level, msg)
|
||||
|
||||
_, err := fmt.Fprintln(os.Stderr, string(serialized))
|
||||
|
|
@ -114,6 +159,11 @@ func (e Entry) write(level, msg string) {
|
|||
}
|
||||
}
|
||||
|
||||
// Debug logs a debug message without additional fields
|
||||
func Debug(msg string) {
|
||||
newEntry(Fields{}).Debug(msg)
|
||||
}
|
||||
|
||||
// Info logs an info message without additional fields
|
||||
func Info(msg string) {
|
||||
newEntry(Fields{}).Info(msg)
|
||||
|
|
|
|||
|
|
@ -19,11 +19,10 @@
|
|||
package mailer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/dnote/dnote/pkg/server/log"
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/gomail.v2"
|
||||
)
|
||||
|
|
@ -104,9 +103,12 @@ func NewDefaultBackend(enabled bool) (*DefaultBackend, error) {
|
|||
func (b *DefaultBackend) Queue(subject, from string, to []string, contentType, body string) error {
|
||||
// If not enabled, just log the email
|
||||
if !b.Enabled {
|
||||
log.Println("Not sending email because backend is disabled.")
|
||||
log.Printf("Subject: %s, to: %s, from: %s", subject, to, from)
|
||||
fmt.Println(body)
|
||||
log.WithFields(log.Fields{
|
||||
"subject": subject,
|
||||
"to": to,
|
||||
"from": from,
|
||||
"body": body,
|
||||
}).Info("Not sending email because email backend is not configured.")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ package main
|
|||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
|
|
@ -31,6 +30,7 @@ import (
|
|||
"github.com/dnote/dnote/pkg/server/config"
|
||||
"github.com/dnote/dnote/pkg/server/controllers"
|
||||
"github.com/dnote/dnote/pkg/server/database"
|
||||
"github.com/dnote/dnote/pkg/server/log"
|
||||
"github.com/dnote/dnote/pkg/server/mailer"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
|
|
@ -51,7 +51,7 @@ func initApp(cfg config.Config) app.App {
|
|||
if err != nil {
|
||||
emailBackend = &mailer.DefaultBackend{Enabled: false}
|
||||
} else {
|
||||
log.Printf("Email backend configured")
|
||||
log.Info("Email backend configured")
|
||||
}
|
||||
|
||||
return app.App{
|
||||
|
|
@ -85,6 +85,7 @@ Flags:
|
|||
webURL := startFlags.String("webUrl", "", "Full URL to server without trailing slash (env: WebURL, example: https://example.com)")
|
||||
dbPath := startFlags.String("dbPath", "", "Path to SQLite database file (env: DBPath, default: $XDG_DATA_HOME/dnote/server.db)")
|
||||
disableRegistration := startFlags.Bool("disableRegistration", false, "Disable user registration (env: DisableRegistration, default: false)")
|
||||
logLevel := startFlags.String("logLevel", "", "Log level: debug, info, warn, or error (env: LOG_LEVEL, default: info)")
|
||||
|
||||
startFlags.Parse(args)
|
||||
|
||||
|
|
@ -94,6 +95,7 @@ Flags:
|
|||
WebURL: *webURL,
|
||||
DBPath: *dbPath,
|
||||
DisableRegistration: *disableRegistration,
|
||||
LogLevel: *logLevel,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %s\n\n", err)
|
||||
|
|
@ -101,6 +103,9 @@ Flags:
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Set log level
|
||||
log.SetLevel(cfg.LogLevel)
|
||||
|
||||
app := initApp(cfg)
|
||||
defer func() {
|
||||
sqlDB, err := app.DB.DB()
|
||||
|
|
@ -121,8 +126,15 @@ Flags:
|
|||
panic(errors.Wrap(err, "initializing router"))
|
||||
}
|
||||
|
||||
log.Printf("Dnote version %s is running on port %s", buildinfo.Version, cfg.Port)
|
||||
log.Fatalln(http.ListenAndServe(fmt.Sprintf(":%s", cfg.Port), r))
|
||||
log.WithFields(log.Fields{
|
||||
"version": buildinfo.Version,
|
||||
"port": cfg.Port,
|
||||
}).Info("Dnote server starting")
|
||||
|
||||
if err := http.ListenAndServe(fmt.Sprintf(":%s", cfg.Port), r); err != nil {
|
||||
log.ErrorWrap(err, "server failed")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func versionCmd() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue