Parent directory

db.go

2211 bytes
 1package main
 2
 3import (
 4	"context"
 5	"database/sql"
 6	"fmt"
 7	"os"
 8	"path/filepath"
 9	"time"
10
11	_ "modernc.org/sqlite"
12)
13
14type database struct {
15	connection *sql.DB
16}
17
18func openDatabase(path string) (*database, error) {
19	if err := os.MkdirAll(filepath.Dir(path), 0o750); err != nil {
20		return nil, fmt.Errorf("create database directory: %w", err)
21	}
22
23	connection, err := sql.Open("sqlite", path)
24	if err != nil {
25		return nil, fmt.Errorf("open database: %w", err)
26	}
27	connection.SetMaxOpenConns(1)
28
29	database := &database{connection: connection}
30	if err := database.bootstrap(context.Background()); err != nil {
31		_ = connection.Close()
32		return nil, err
33	}
34	return database, nil
35}
36
37func (database *database) bootstrap(ctx context.Context) error {
38	_, err := database.connection.ExecContext(ctx, `
39		CREATE TABLE IF NOT EXISTS builds (
40			build_id TEXT PRIMARY KEY,
41			repository TEXT NOT NULL,
42			sha TEXT NOT NULL,
43			state TEXT NOT NULL,
44			queued_at INTEGER NOT NULL,
45			started_at INTEGER,
46			finished_at INTEGER
47		);
48		CREATE INDEX IF NOT EXISTS builds_repository_queued_at ON builds (repository, queued_at DESC);
49	`)
50	if err != nil {
51		return fmt.Errorf("bootstrap database: %w", err)
52	}
53	return nil
54}
55
56func (database *database) close() error {
57	return database.connection.Close()
58}
59
60func (database *database) queue(job buildJob, queuedAt time.Time) error {
61	_, err := database.connection.Exec(
62		`INSERT INTO builds (build_id, repository, sha, state, queued_at) VALUES (?, ?, ?, ?, ?)`,
63		job.ID,
64		job.Request.Repository,
65		job.Request.SHA,
66		"queued",
67		queuedAt.Unix(),
68	)
69	if err != nil {
70		return fmt.Errorf("save queued build: %w", err)
71	}
72	return nil
73}
74
75func (database *database) update(job buildJob, state string, updatedAt time.Time) error {
76	var query string
77	switch state {
78	case "running":
79		query = `UPDATE builds SET state = ?, started_at = ? WHERE build_id = ?`
80	case "passed", "failed":
81		query = `UPDATE builds SET state = ?, finished_at = ? WHERE build_id = ?`
82	default:
83		return fmt.Errorf("unsupported build state %q", state)
84	}
85
86	if _, err := database.connection.Exec(query, state, updatedAt.Unix(), job.ID); err != nil {
87		return fmt.Errorf("save build state: %w", err)
88	}
89	return nil
90}