hey make some pipelines
+1
-5
.tangled/workflows/build.yml
+1
-5
.tangled/workflows/build.yml
+1
-1
.tangled/workflows/fmt.yml
+1
-1
.tangled/workflows/fmt.yml
+2
-2
.tangled/workflows/test.yml
+2
-2
.tangled/workflows/test.yml
+17
-12
appview/db/issues.go
+17
-12
appview/db/issues.go
···
9
9
)
10
10
11
11
type Issue struct {
12
+
ID int64
12
13
RepoAt syntax.ATURI
13
14
OwnerDid string
14
15
IssueId int
···
65
66
66
67
issue.IssueId = nextId
67
68
68
-
_, err = tx.Exec(`
69
+
res, err := tx.Exec(`
69
70
insert into issues (repo_at, owner_did, issue_id, title, body)
70
71
values (?, ?, ?, ?, ?)
71
72
`, issue.RepoAt, issue.OwnerDid, issue.IssueId, issue.Title, issue.Body)
72
73
if err != nil {
73
74
return err
74
75
}
76
+
77
+
lastID, err := res.LastInsertId()
78
+
if err != nil {
79
+
return err
80
+
}
81
+
issue.ID = lastID
75
82
76
83
if err := tx.Commit(); err != nil {
77
84
return err
···
89
96
var issueAt string
90
97
err := e.QueryRow(`select issue_at from issues where repo_at = ? and issue_id = ?`, repoAt, issueId).Scan(&issueAt)
91
98
return issueAt, err
92
-
}
93
-
94
-
func GetIssueId(e Execer, repoAt syntax.ATURI) (int, error) {
95
-
var issueId int
96
-
err := e.QueryRow(`select next_issue_id from repo_issue_seqs where repo_at = ?`, repoAt).Scan(&issueId)
97
-
return issueId - 1, err
98
99
}
99
100
100
101
func GetIssueOwnerDid(e Execer, repoAt syntax.ATURI, issueId int) (string, error) {
···
114
115
`
115
116
with numbered_issue as (
116
117
select
118
+
i.id,
117
119
i.owner_did,
118
120
i.issue_id,
119
121
i.created,
···
132
134
i.id, i.owner_did, i.issue_id, i.created, i.title, i.body, i.open
133
135
)
134
136
select
137
+
id,
135
138
owner_did,
136
139
issue_id,
137
140
created,
···
153
156
var issue Issue
154
157
var createdAt string
155
158
var metadata IssueMetadata
156
-
err := rows.Scan(&issue.OwnerDid, &issue.IssueId, &createdAt, &issue.Title, &issue.Body, &issue.Open, &metadata.CommentCount)
159
+
err := rows.Scan(&issue.ID, &issue.OwnerDid, &issue.IssueId, &createdAt, &issue.Title, &issue.Body, &issue.Open, &metadata.CommentCount)
157
160
if err != nil {
158
161
return nil, err
159
162
}
···
182
185
183
186
rows, err := e.Query(
184
187
`select
188
+
i.id,
185
189
i.owner_did,
186
190
i.repo_at,
187
191
i.issue_id,
···
213
217
var issueCreatedAt, repoCreatedAt string
214
218
var repo Repo
215
219
err := rows.Scan(
220
+
&issue.ID,
216
221
&issue.OwnerDid,
217
222
&issue.RepoAt,
218
223
&issue.IssueId,
···
257
262
}
258
263
259
264
func GetIssue(e Execer, repoAt syntax.ATURI, issueId int) (*Issue, error) {
260
-
query := `select owner_did, created, title, body, open from issues where repo_at = ? and issue_id = ?`
265
+
query := `select id, owner_did, created, title, body, open from issues where repo_at = ? and issue_id = ?`
261
266
row := e.QueryRow(query, repoAt, issueId)
262
267
263
268
var issue Issue
264
269
var createdAt string
265
-
err := row.Scan(&issue.OwnerDid, &createdAt, &issue.Title, &issue.Body, &issue.Open)
270
+
err := row.Scan(&issue.ID, &issue.OwnerDid, &createdAt, &issue.Title, &issue.Body, &issue.Open)
266
271
if err != nil {
267
272
return nil, err
268
273
}
···
277
282
}
278
283
279
284
func GetIssueWithComments(e Execer, repoAt syntax.ATURI, issueId int) (*Issue, []Comment, error) {
280
-
query := `select owner_did, issue_id, created, title, body, open from issues where repo_at = ? and issue_id = ?`
285
+
query := `select id, owner_did, issue_id, created, title, body, open from issues where repo_at = ? and issue_id = ?`
281
286
row := e.QueryRow(query, repoAt, issueId)
282
287
283
288
var issue Issue
284
289
var createdAt string
285
-
err := row.Scan(&issue.OwnerDid, &issue.IssueId, &createdAt, &issue.Title, &issue.Body, &issue.Open)
290
+
err := row.Scan(&issue.ID, &issue.OwnerDid, &issue.IssueId, &createdAt, &issue.Title, &issue.Body, &issue.Open)
286
291
if err != nil {
287
292
return nil, nil, err
288
293
}
+7
-13
appview/issues/issues.go
+7
-13
appview/issues/issues.go
···
687
687
return
688
688
}
689
689
690
-
err = db.NewIssue(tx, &db.Issue{
690
+
issue := &db.Issue{
691
691
RepoAt: f.RepoAt,
692
692
Title: title,
693
693
Body: body,
694
694
OwnerDid: user.Did,
695
-
})
696
-
if err != nil {
697
-
log.Println("failed to create issue", err)
698
-
rp.pages.Notice(w, "issues", "Failed to create issue.")
699
-
return
700
695
}
701
-
702
-
issueId, err := db.GetIssueId(rp.db, f.RepoAt)
696
+
err = db.NewIssue(tx, issue)
703
697
if err != nil {
704
-
log.Println("failed to get issue id", err)
698
+
log.Println("failed to create issue", err)
705
699
rp.pages.Notice(w, "issues", "Failed to create issue.")
706
700
return
707
701
}
···
723
717
Title: title,
724
718
Body: &body,
725
719
Owner: user.Did,
726
-
IssueId: int64(issueId),
720
+
IssueId: int64(issue.IssueId),
727
721
},
728
722
},
729
723
})
···
733
727
return
734
728
}
735
729
736
-
err = db.SetIssueAt(rp.db, f.RepoAt, issueId, resp.Uri)
730
+
err = db.SetIssueAt(rp.db, f.RepoAt, issue.IssueId, resp.Uri)
737
731
if err != nil {
738
732
log.Println("failed to set issue at", err)
739
733
rp.pages.Notice(w, "issues", "Failed to create issue.")
···
744
738
err = rp.posthog.Enqueue(posthog.Capture{
745
739
DistinctId: user.Did,
746
740
Event: "new_issue",
747
-
Properties: posthog.Properties{"repo_at": f.RepoAt.String(), "issue_id": issueId},
741
+
Properties: posthog.Properties{"repo_at": f.RepoAt.String(), "issue_id": issue.IssueId},
748
742
})
749
743
if err != nil {
750
744
log.Println("failed to enqueue posthog event:", err)
751
745
}
752
746
}
753
747
754
-
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueId))
748
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issue.IssueId))
755
749
return
756
750
}
757
751
}
+37
appview/notify/merged_notifier.go
+37
appview/notify/merged_notifier.go
···
1
+
package notify
2
+
3
+
import (
4
+
"context"
5
+
6
+
"tangled.sh/tangled.sh/core/appview/db"
7
+
)
8
+
9
+
type mergedNotifier struct {
10
+
notifiers []Notifier
11
+
}
12
+
13
+
func NewMergedNotifier(notifiers ...Notifier) Notifier {
14
+
return &mergedNotifier{
15
+
notifiers,
16
+
}
17
+
}
18
+
19
+
var _ Notifier = &mergedNotifier{}
20
+
21
+
func (m *mergedNotifier) NewIssue(ctx context.Context, issue db.Issue) {
22
+
for _, notifier := range m.notifiers {
23
+
notifier.NewIssue(ctx, issue)
24
+
}
25
+
}
26
+
27
+
func (m *mergedNotifier) NewIssueComment(ctx context.Context, comment db.Comment) {
28
+
for _, notifier := range m.notifiers {
29
+
notifier.NewIssueComment(ctx, comment)
30
+
}
31
+
}
32
+
33
+
func (m *mergedNotifier) NewPullComment(ctx context.Context, comment db.PullComment) {
34
+
for _, notifier := range m.notifiers {
35
+
notifier.NewPullComment(ctx, comment)
36
+
}
37
+
}
+14
appview/notify/notifier.go
+14
appview/notify/notifier.go
···
1
+
package notify
2
+
3
+
import (
4
+
"context"
5
+
6
+
"tangled.sh/tangled.sh/core/appview/db"
7
+
)
8
+
9
+
type Notifier interface {
10
+
NewIssue(ctx context.Context, issue db.Issue)
11
+
NewIssueComment(ctx context.Context, comment db.Comment)
12
+
13
+
NewPullComment(ctx context.Context, comment db.PullComment)
14
+
}
+1
-1
appview/pages/templates/timeline.html
+1
-1
appview/pages/templates/timeline.html
···
108
108
{{ $starrerHandle := index $.DidHandleMap .Star.StarredByDid }}
109
109
{{ $repoOwnerHandle := index $.DidHandleMap .Star.Repo.Did }}
110
110
<div class="flex items-center">
111
-
<p class="text-gray-600 dark:text-gray-300 flex flex-wrap items-center gap-2">
111
+
<p class="text-gray-600 dark:text-gray-300 flex items-center gap-2">
112
112
{{ template "user/fragments/picHandle" $starrerHandle }}
113
113
starred
114
114
<a
+1
-1
appview/state/router.go
+1
-1
appview/state/router.go
···
191
191
}
192
192
193
193
func (s *State) IssuesRouter(mw *middleware.Middleware) http.Handler {
194
-
issues := issues.New(s.oauth, s.repoResolver, s.pages, s.idResolver, s.db, s.config, s.posthog)
194
+
issues := issues.New(s.oauth, s.repoResolver, s.pages, s.idResolver, s.db, s.config, s.posthog, s.notifier)
195
195
return issues.Router(mw)
196
196
197
197
}
+6
appview/state/state.go
+6
appview/state/state.go
···
25
25
"tangled.sh/tangled.sh/core/appview/config"
26
26
"tangled.sh/tangled.sh/core/appview/db"
27
27
"tangled.sh/tangled.sh/core/appview/idresolver"
28
+
"tangled.sh/tangled.sh/core/appview/notify"
28
29
"tangled.sh/tangled.sh/core/appview/oauth"
29
30
"tangled.sh/tangled.sh/core/appview/pages"
30
31
"tangled.sh/tangled.sh/core/appview/reporesolver"
···
37
38
38
39
type State struct {
39
40
db *db.DB
41
+
notifier notify.Notifier
40
42
oauth *oauth.OAuth
41
43
enforcer *rbac.Enforcer
42
44
tidClock syntax.TIDClock
···
134
136
}
135
137
spindlestream.Start(ctx)
136
138
139
+
notifier := notify.NewMergedNotifier(
140
+
)
141
+
137
142
state := &State{
138
143
d,
144
+
notifier,
139
145
oauth,
140
146
enforcer,
141
147
clock,
+2
-4
docs/spindle/hosting.md
+2
-4
docs/spindle/hosting.md
···
31
31
2. **Build the Spindle binary.**
32
32
33
33
```shell
34
-
cd core
35
-
go mod download
36
-
go build -o cmd/spindle/spindle cmd/spindle/main.go
34
+
go build -o spindle core/spindle/server.go
37
35
```
38
36
39
37
3. **Run the Spindle binary.**
40
38
41
39
```shell
42
-
./cmd/spindle/spindle
40
+
./spindle
43
41
```
44
42
45
43
Spindle will now start, connect to the Jetstream server, and begin processing pipelines.
+50
-41
flake.nix
+50
-41
flake.nix
···
53
53
}: let
54
54
supportedSystems = ["x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin"];
55
55
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
56
-
nixpkgsFor = forAllSystems (system: nixpkgs.legacyPackages.${system});
56
+
nixpkgsFor = forAllSystems (system:
57
+
import nixpkgs {
58
+
inherit system;
59
+
overlays = [self.overlays.default];
60
+
});
57
61
inherit (gitignore.lib) gitignoreSource;
58
-
mkPackageSet = pkgs: let
62
+
in {
63
+
overlays.default = final: prev: let
59
64
goModHash = "sha256-SLi+nALwCd/Lzn3aljwPqCo2UaM9hl/4OAjcHQLt2Bk=";
60
-
sqlite-lib = pkgs.callPackage ./nix/pkgs/sqlite-lib.nix {
61
-
inherit (pkgs) gcc;
62
-
inherit sqlite-lib-src;
65
+
appviewDeps = {
66
+
inherit htmx-src htmx-ws-src lucide-src inter-fonts-src ibm-plex-mono-src goModHash gitignoreSource;
63
67
};
64
-
genjwks = pkgs.callPackage ./nix/pkgs/genjwks.nix {inherit goModHash gitignoreSource;};
65
-
lexgen = pkgs.callPackage ./nix/pkgs/lexgen.nix {inherit indigo;};
66
-
appview = pkgs.callPackage ./nix/pkgs/appview.nix {
67
-
inherit sqlite-lib htmx-src htmx-ws-src lucide-src inter-fonts-src ibm-plex-mono-src goModHash gitignoreSource;
68
+
knotDeps = {
69
+
inherit goModHash gitignoreSource;
68
70
};
69
-
spindle = pkgs.callPackage ./nix/pkgs/spindle.nix {inherit sqlite-lib goModHash gitignoreSource;};
70
-
knot-unwrapped = pkgs.callPackage ./nix/pkgs/knot-unwrapped.nix {inherit sqlite-lib goModHash gitignoreSource;};
71
-
knot = pkgs.callPackage ./nix/pkgs/knot.nix {inherit knot-unwrapped;};
72
-
in {
73
-
inherit lexgen appview spindle knot-unwrapped knot sqlite-lib genjwks;
74
-
};
75
-
in {
76
-
overlays.default = final: prev: mkPackageSet final;
71
+
spindleDeps = {
72
+
inherit goModHash gitignoreSource;
73
+
};
74
+
mkPackageSet = pkgs: {
75
+
lexgen = pkgs.callPackage ./nix/pkgs/lexgen.nix {inherit indigo;};
76
+
appview = pkgs.callPackage ./nix/pkgs/appview.nix appviewDeps;
77
+
knot = pkgs.callPackage ./nix/pkgs/knot.nix {};
78
+
spindle = pkgs.callPackage ./nix/pkgs/spindle.nix spindleDeps;
79
+
knot-unwrapped = pkgs.callPackage ./nix/pkgs/knot-unwrapped.nix knotDeps;
80
+
sqlite-lib = pkgs.callPackage ./nix/pkgs/sqlite-lib.nix {
81
+
inherit (pkgs) gcc;
82
+
inherit sqlite-lib-src;
83
+
};
84
+
genjwks = pkgs.callPackage ./nix/pkgs/genjwks.nix {inherit goModHash gitignoreSource;};
85
+
};
86
+
in
87
+
mkPackageSet final;
77
88
78
89
packages = forAllSystems (system: let
79
90
pkgs = nixpkgsFor.${system};
80
-
packages = mkPackageSet pkgs;
81
-
staticPackages = mkPackageSet pkgs.pkgsStatic;
82
-
crossPackages = mkPackageSet pkgs.pkgsCross.gnu64.pkgsStatic;
91
+
staticPkgs = pkgs.pkgsStatic;
92
+
crossPkgs = pkgs.pkgsCross.gnu64.pkgsStatic;
83
93
in {
84
-
appview = packages.appview;
85
-
lexgen = packages.lexgen;
86
-
knot = packages.knot;
87
-
knot-unwrapped = packages.knot-unwrapped;
88
-
spindle = packages.spindle;
89
-
genjwks = packages.genjwks;
90
-
sqlite-lib = packages.sqlite-lib;
94
+
appview = pkgs.appview;
95
+
lexgen = pkgs.lexgen;
96
+
knot = pkgs.knot;
97
+
knot-unwrapped = pkgs.knot-unwrapped;
98
+
spindle = pkgs.spindle;
99
+
genjwks = pkgs.genjwks;
100
+
sqlite-lib = pkgs.sqlite-lib;
91
101
92
-
pkgsStatic-appview = staticPackages.appview;
93
-
pkgsStatic-knot = staticPackages.knot;
94
-
pkgsStatic-knot-unwrapped = staticPackages.knot-unwrapped;
95
-
pkgsStatic-spindle = staticPackages.spindle;
96
-
pkgsStatic-sqlite-lib = staticPackages.sqlite-lib;
102
+
pkgsStatic-appview = staticPkgs.appview;
103
+
pkgsStatic-knot = staticPkgs.knot;
104
+
pkgsStatic-knot-unwrapped = staticPkgs.knot-unwrapped;
105
+
pkgsStatic-spindle = staticPkgs.spindle;
106
+
pkgsStatic-sqlite-lib = staticPkgs.sqlite-lib;
97
107
98
-
pkgsCross-gnu64-pkgsStatic-appview = crossPackages.appview;
99
-
pkgsCross-gnu64-pkgsStatic-knot = crossPackages.knot;
100
-
pkgsCross-gnu64-pkgsStatic-knot-unwrapped = crossPackages.knot-unwrapped;
101
-
pkgsCross-gnu64-pkgsStatic-spindle = crossPackages.spindle;
108
+
pkgsCross-gnu64-pkgsStatic-appview = crossPkgs.appview;
109
+
pkgsCross-gnu64-pkgsStatic-knot = crossPkgs.knot;
110
+
pkgsCross-gnu64-pkgsStatic-knot-unwrapped = crossPkgs.knot-unwrapped;
111
+
pkgsCross-gnu64-pkgsStatic-spindle = crossPkgs.spindle;
102
112
});
103
-
defaultPackage = forAllSystems (system: self.packages.${system}.appview);
104
-
formatter = forAllSystems (system: nixpkgsFor.${system}.alejandra);
113
+
defaultPackage = forAllSystems (system: nixpkgsFor.${system}.appview);
114
+
formatter = forAllSystems (system: nixpkgsFor."${system}".alejandra);
105
115
devShells = forAllSystems (system: let
106
116
pkgs = nixpkgsFor.${system};
107
-
packages' = self.packages.${system};
108
117
staticShell = pkgs.mkShell.override {
109
118
stdenv = pkgs.pkgsStatic.stdenv;
110
119
};
···
115
124
pkgs.air
116
125
pkgs.gopls
117
126
pkgs.httpie
127
+
pkgs.lexgen
118
128
pkgs.litecli
119
129
pkgs.websocat
120
130
pkgs.tailwindcss
121
131
pkgs.nixos-shell
122
132
pkgs.redis
123
-
packages'.lexgen
124
133
];
125
134
shellHook = ''
126
135
mkdir -p appview/pages/static/{fonts,icons}
···
130
139
cp -f ${inter-fonts-src}/web/InterVariable*.woff2 appview/pages/static/fonts/
131
140
cp -f ${inter-fonts-src}/web/InterDisplay*.woff2 appview/pages/static/fonts/
132
141
cp -f ${ibm-plex-mono-src}/fonts/complete/woff2/IBMPlexMono-Regular.woff2 appview/pages/static/fonts/
133
-
export TANGLED_OAUTH_JWKS="$(${packages'.genjwks}/bin/genjwks)"
142
+
export TANGLED_OAUTH_JWKS="$(${pkgs.genjwks}/bin/genjwks)"
134
143
'';
135
144
env.CGO_ENABLED = 1;
136
145
};