+8
-6
.air/appview.toml
+8
-6
.air/appview.toml
···-cmd = "tailwindcss -i input.css -o ./appview/pages/static/tw.css && go build -o .bin/app ./cmd/appview/main.go"
+11
.air/knot.toml
+11
.air/knot.toml
···+cmd = 'go build -ldflags "-X tangled.org/core/knotserver.version=$(git describe --tags --long)" -o out/knot.out cmd/knot/main.go'
-7
.air/knotserver.toml
-7
.air/knotserver.toml
+10
.air/spindle.toml
+10
.air/spindle.toml
+13
.editorconfig
+13
.editorconfig
+13
-1
api/tangled/repoblob.go
+13
-1
api/tangled/repoblob.go
·········
-4
api/tangled/repotree.go
-4
api/tangled/repotree.go
···Last_commit *RepoTree_LastCommit `json:"last_commit,omitempty" cborgen:"last_commit,omitempty"`
+6
-45
appview/commitverify/verify.go
+6
-45
appview/commitverify/verify.go
······-func GetVerifiedObjectCommits(e db.Execer, emailToDid map[string]string, commits []*object.Commit) (VerifiedCommits, error) {-func GetVerifiedCommits(e db.Execer, emailToDid map[string]string, ndCommits []types.NiceDiff) (VerifiedCommits, error) {+func GetVerifiedCommits(e db.Execer, emailToDid map[string]string, ndCommits []types.Commit) (VerifiedCommits, error) {······
+3
-2
appview/db/artifact.go
+3
-2
appview/db/artifact.go
·········
+4
-3
appview/db/collaborators.go
+4
-3
appview/db/collaborators.go
·········
+60
-136
appview/db/db.go
+60
-136
appview/db/db.go
······create index if not exists idx_notifications_recipient_created on notifications(recipient_did, created desc);create index if not exists idx_notifications_recipient_read on notifications(recipient_did, read);·········-runMigration(conn, logger, "add-deleted-and-edited-to-issue-comments", func(tx *sql.Tx) error {+orm.RunMigration(conn, logger, "add-deleted-and-edited-to-issue-comments", func(tx *sql.Tx) error {···-runMigration(conn, logger, "add-source-info-to-pulls-and-submissions", func(tx *sql.Tx) error {+orm.RunMigration(conn, logger, "add-source-info-to-pulls-and-submissions", func(tx *sql.Tx) error {······-runMigration(conn, logger, "recreate-pulls-column-for-stacking-support", func(tx *sql.Tx) error {+orm.RunMigration(conn, logger, "recreate-pulls-column-for-stacking-support", func(tx *sql.Tx) error {···············-runMigration(conn, logger, "rename-registrations-read-only-to-needs-upgrade", func(tx *sql.Tx) error {+orm.RunMigration(conn, logger, "rename-registrations-read-only-to-needs-upgrade", func(tx *sql.Tx) error {··················-runMigration(conn, logger, "remove-repo-at-pull-id-from-pull-submissions", func(tx *sql.Tx) error {+orm.RunMigration(conn, logger, "remove-repo-at-pull-id-from-pull-submissions", func(tx *sql.Tx) error {···// knots may report the combined patch for a comparison, we can store that on the appview side// (but not on the pds record), because calculating the combined patch requires a git index···alter table notification_preferences add column user_mentioned integer not null default 1;-func runMigration(c *sql.Conn, logger *slog.Logger, name string, migrationFn migrationFn) error {-err = tx.QueryRow("select exists (select 1 from migrations where name = ?)", name).Scan(&exists)-if (kind == reflect.Slice && rv.Type().Elem().Kind() != reflect.Uint8) || kind == reflect.Array {-if (kind == reflect.Slice && rv.Type().Elem().Kind() != reflect.Uint8) || kind == reflect.Array {
+4
-3
appview/db/follow.go
+4
-3
appview/db/follow.go
·········
+21
-20
appview/db/issues.go
+21
-20
appview/db/issues.go
·········-func GetIssuesPaginated(e Execer, page pagination.Page, filters ...filter) ([]models.Issue, error) {+func GetIssuesPaginated(e Execer, page pagination.Page, filters ...orm.Filter) ([]models.Issue, error) {·······································
+8
-7
appview/db/label.go
+8
-7
appview/db/label.go
························-func NewLabelApplicationCtx(e Execer, filters ...filter) (*models.LabelApplicationCtx, error) {+func NewLabelApplicationCtx(e Execer, filters ...orm.Filter) (*models.LabelApplicationCtx, error) {
+5
-4
appview/db/language.go
+5
-4
appview/db/language.go
·········func UpdateRepoLanguages(tx *sql.Tx, repoAt syntax.ATURI, ref string, langs []models.RepoLanguage) error {
+14
-13
appview/db/notifications.go
+14
-13
appview/db/notifications.go
······-func GetNotificationsPaginated(e Execer, page pagination.Page, filters ...filter) ([]*models.Notification, error) {+func GetNotificationsPaginated(e Execer, page pagination.Page, filters ...orm.Filter) ([]*models.Notification, error) {···-func GetNotificationsWithEntities(e Execer, page pagination.Page, filters ...filter) ([]*models.NotificationWithEntity, error) {+func GetNotificationsWithEntities(e Execer, page pagination.Page, filters ...orm.Filter) ([]*models.NotificationWithEntity, error) {···············func GetNotificationPreference(e Execer, userDid string) (*models.NotificationPreferences, error) {···-func GetNotificationPreferences(e Execer, filters ...filter) (map[syntax.DID]*models.NotificationPreferences, error) {+func GetNotificationPreferences(e Execer, filters ...orm.Filter) (map[syntax.DID]*models.NotificationPreferences, error) {···
+9
-6
appview/db/pipeline.go
+9
-6
appview/db/pipeline.go
······+func GetPipelineStatuses(e Execer, limit int, filters ...orm.Filter) ([]models.Pipeline, error) {······conditions = append(conditions, fmt.Sprintf("(%s and %s)", knotFilter.Condition(), rkeyFilter.Condition()))
+6
-5
appview/db/profile.go
+6
-5
appview/db/profile.go
···············
+21
-20
appview/db/pulls.go
+21
-20
appview/db/pulls.go
························-pulls, err := GetPullsWithLimit(e, 1, FilterEq("repo_at", repoAt), FilterEq("pull_id", pullId))+pulls, err := GetPullsWithLimit(e, 1, orm.FilterEq("repo_at", repoAt), orm.FilterEq("pull_id", pullId))···-func GetPullSubmissions(e Execer, filters ...filter) (map[syntax.ATURI][]*models.PullSubmission, error) {+func GetPullSubmissions(e Execer, filters ...orm.Filter) (map[syntax.ATURI][]*models.PullSubmission, error) {···············// Only used when stacking to update contents in the event of a rebase (the interdiff should be empty).······
+2
-1
appview/db/punchcard.go
+2
-1
appview/db/punchcard.go
······
+4
-3
appview/db/reference.go
+4
-3
appview/db/reference.go
······+func GetReferencesAll(e Execer, filters ...orm.Filter) (map[syntax.ATURI][]syntax.ATURI, error) {······
+4
-3
appview/db/registration.go
+4
-3
appview/db/registration.go
·········
+20
-36
appview/db/repos.go
+20
-36
appview/db/repos.go
·····················
+6
-5
appview/db/spindle.go
+6
-5
appview/db/spindle.go
···············
+43
-102
appview/db/star.go
+43
-102
appview/db/star.go
·········-_, err := e.Exec(`delete from stars where starred_by_did = ? and repo_at = ?`, starredByDid, repoAt)-_, err := e.Exec(`delete from stars where starred_by_did = ? and rkey = ?`, starredByDid, rkey)······-func GetStarStatuses(e Execer, userDid string, repoAts []syntax.ATURI) (map[string]bool, error) {+func GetStarStatuses(e Execer, userDid string, subjectAts []syntax.ATURI) (map[string]bool, error) {···············// GetTopStarredReposLastWeek returns the top 8 most starred repositories from the last week···
+4
-3
appview/db/strings.go
+4
-3
appview/db/strings.go
············
+11
-20
appview/db/timeline.go
+11
-20
appview/db/timeline.go
······func getTimelineRepos(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) {······func getTimelineStars(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) {······func getTimelineFollows(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) {···
+7
-12
appview/email/email.go
+7
-12
appview/email/email.go
······-pattern := `^[a-zA-Z0-9.!#$%&'*+/=?^_\x60{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$`
+53
appview/email/email_test.go
+53
appview/email/email_test.go
···
+3
-1
appview/indexer/issues/indexer.go
+3
-1
appview/indexer/issues/indexer.go
···
+3
-1
appview/indexer/pulls/indexer.go
+3
-1
appview/indexer/pulls/indexer.go
···
+28
-27
appview/ingester.go
+28
-27
appview/ingester.go
················································
+110
-121
appview/issues/issues.go
+110
-121
appview/issues/issues.go
························+newIssue.Mentions, newIssue.References = rp.mentionsResolver.Resolve(r.Context(), newIssue.Body)·········+roles := repoinfo.RolesInRepo{Roles: rp.enforcer.GetPermissionsInRepo(user.Did, f.Knot, f.DidSlashRepo())}······+roles := repoinfo.RolesInRepo{Roles: rp.enforcer.GetPermissionsInRepo(user.Did, f.Knot, f.DidSlashRepo())}·········-rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", f.OwnerSlashRepo(), issue.IssueId, commentId))+rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", ownerSlashRepo, issue.IssueId, commentId))··················+newComment.Mentions, newComment.References = rp.mentionsResolver.Resolve(r.Context(), newBody)···················································
+3
-3
appview/issues/opengraph.go
+3
-3
appview/issues/opengraph.go
···
+37
-19
appview/knots/knots.go
+37
-19
appview/knots/knots.go
··········································
+5
-4
appview/labels/labels.go
+5
-4
appview/labels/labels.go
·········
+67
appview/mentions/resolver.go
+67
appview/mentions/resolver.go
···+func (r *Resolver) Resolve(ctx context.Context, source string) ([]syntax.DID, []syntax.ATURI) {
+7
-4
appview/middleware/middleware.go
+7
-4
appview/middleware/middleware.go
······-log.Printf("%s does not have perms of a %s in repo %s", actor.Did, requiredPerm, f.OwnerSlashRepo())+log.Printf("%s does not have perms of a %s in repo %s", actor.Did, requiredPerm, f.DidSlashRepo())·········
+3
-1
appview/models/profile.go
+3
-1
appview/models/profile.go
······
+47
appview/models/repo.go
+47
appview/models/repo.go
···
+14
-5
appview/models/star.go
+14
-5
appview/models/star.go
···
+1
-1
appview/models/string.go
+1
-1
appview/models/string.go
+1
-1
appview/models/timeline.go
+1
-1
appview/models/timeline.go
+5
-4
appview/notifications/notifications.go
+5
-4
appview/notifications/notifications.go
············
+83
-67
appview/notify/db/db.go
+83
-67
appview/notify/db/db.go
·········func (n *databaseNotifier) NewIssue(ctx context.Context, issue *models.Issue, mentions []syntax.DID) {······func (n *databaseNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) {························func (n *databaseNotifier) NewIssueState(ctx context.Context, actor syntax.DID, issue *models.Issue) {···func (n *databaseNotifier) NewPullState(ctx context.Context, actor syntax.DID, pull *models.Pull) {······+// if the user is attempting to mention >maxMentions users, this is probably spam, do not mention anybody···
+2
-2
appview/notify/posthog/notifier.go
+2
-2
appview/notify/posthog/notifier.go
······
+3
-2
appview/oauth/handler.go
+3
-2
appview/oauth/handler.go
······
+15
-2
appview/oauth/oauth.go
+15
-2
appview/oauth/oauth.go
·········func (o *OAuth) ServiceClient(r *http.Request, os ...ServiceClientOpt) (*xrpc.Client, error) {···
+81
-10
appview/pages/funcmap.go
+81
-10
appview/pages/funcmap.go
························
+1
-1
appview/pages/markup/extension/atlink.go
+1
-1
appview/pages/markup/extension/atlink.go
+4
-4
appview/pages/markup/markdown.go
+4
-4
appview/pages/markup/markdown.go
············
+37
-116
appview/pages/pages.go
+37
-116
appview/pages/pages.go
··········································-return p.executePlain("repo/fragments/diff", w, []any{params.RepoInfo.FullName, ¶ms.Diff})+func (p *Pages) RepoCompareDiffFragment(w io.Writer, params RepoCompareDiffFragmentParams) error {···
+25
-22
appview/pages/repoinfo/repoinfo.go
+25
-22
appview/pages/repoinfo/repoinfo.go
············
+28
appview/pages/templates/fragments/starBtn.html
+28
appview/pages/templates/fragments/starBtn.html
···
+8
appview/pages/templates/fragments/tabSelector.html
+8
appview/pages/templates/fragments/tabSelector.html
···<div class="flex justify-between divide-x divide-gray-200 dark:divide-gray-700 rounded border border-gray-200 dark:border-gray-700 overflow-hidden">class="p-2 whitespace-nowrap flex justify-center items-center gap-2 text-sm w-full block hover:no-underline text-center {{ if $isActive }} {{$activeTab }} {{ else }} {{ $inactiveTab }} {{ end }}">
+22
appview/pages/templates/fragments/tinyAvatarList.html
+22
appview/pages/templates/fragments/tinyAvatarList.html
···+class="rounded-full size-8 mr-1 border-2 border-gray-100 dark:border-gray-900 z-{{sub 5 $i}}0 {{ $classes }}"
+23
-7
appview/pages/templates/knots/dashboard.html
+23
-7
appview/pages/templates/knots/dashboard.html
···+<div class="bg-white dark:bg-gray-800 p-6 rounded relative w-full mx-auto drop-shadow-sm dark:text-white">+<h2 class="text-sm pb-2 uppercase font-bold">{{ .Tab }} · {{ .Registration.Domain }}</h2>···-<section class="bg-white dark:bg-gray-800 p-6 rounded relative w-full mx-auto drop-shadow-sm dark:text-white">+<section class="bg-white dark:bg-gray-800 rounded relative w-full mx-auto drop-shadow-sm dark:text-white">···class="btn text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 gap-2 group"······class="btn text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 gap-2 group"
+18
-13
appview/pages/templates/knots/fragments/addMemberModal.html
+18
-13
appview/pages/templates/knots/fragments/addMemberModal.html
···-class="bg-white w-full md:w-96 dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 drop-shadow dark:text-white backdrop:bg-gray-400/50 dark:backdrop:bg-gray-800/50">+bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 dark:text-white backdrop:bg-gray-400/50 dark:backdrop:bg-gray-800/50···<p class="text-sm text-gray-500 dark:text-gray-400">Members can create repositories and run workflows on this knot.</p>···
+3
-3
appview/pages/templates/knots/fragments/knotListing.html
+3
-3
appview/pages/templates/knots/fragments/knotListing.html
···-<a href="/knots/{{ .Domain }}" class="hover:no-underline flex items-center gap-2 min-w-0 max-w-[60%]">+<a href="/settings/knots/{{ .Domain }}" class="hover:no-underline flex items-center gap-2 min-w-0 max-w-[60%]">···class="btn text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 gap-2 group"···
+42
-11
appview/pages/templates/knots/index.html
+42
-11
appview/pages/templates/knots/index.html
···+<div class="bg-white dark:bg-gray-800 p-6 rounded relative w-full mx-auto drop-shadow-sm dark:text-white">-<section class="bg-white dark:bg-gray-800 p-6 rounded relative w-full mx-auto drop-shadow-sm dark:text-white">······+class="bg-white w-full md:w-96 dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 drop-shadow dark:text-white backdrop:bg-gray-400/50 dark:backdrop:bg-gray-800/50">
+1
appview/pages/templates/layouts/base.html
+1
appview/pages/templates/layouts/base.html
···
-2
appview/pages/templates/layouts/fragments/topbar.html
-2
appview/pages/templates/layouts/fragments/topbar.html
···
+8
-7
appview/pages/templates/layouts/profilebase.html
+8
-7
appview/pages/templates/layouts/profilebase.html
···-<meta property="og:url" content="https://tangled.org/{{ or .Card.UserHandle .Card.UserDid }}?tab={{ .Active }}" />-<meta property="og:description" content="{{ or .Card.Profile.Description .Card.UserHandle .Card.UserDid }}" />-<meta name="twitter:description" content="{{ or .Card.Profile.Description .Card.UserHandle .Card.UserDid }}" />
+4
-1
appview/pages/templates/layouts/repobase.html
+4
-1
appview/pages/templates/layouts/repobase.html
···
+64
-39
appview/pages/templates/repo/blob.html
+64
-39
appview/pages/templates/repo/blob.html
···-{{ $code_number_style := "text-gray-400 dark:text-gray-500 left-0 bg-white dark:bg-gray-800 text-right mr-6 select-none inline-block w-12" }}···<div id="file-info" class="text-gray-500 dark:text-gray-400 text-xs md:text-sm flex flex-wrap items-center gap-1 md:gap-0">+<a href="/{{ .RepoInfo.FullName }}/blob/{{ .Ref }}/{{ .Path }}?code={{ .BlobView.ShowingRendered }}" hx-boost="true">-<video controls class="max-w-full h-auto mx-auto border border-gray-200 dark:border-gray-700 rounded">+This directory is a git submodule of <a href="{{ .BlobView.ContentSrc }}">{{ .BlobView.ContentSrc }}</a>.+<video controls class="max-w-full h-auto mx-auto border border-gray-200 dark:border-gray-700 rounded">+<div id="blob-contents" class="whitespace-pre peer-target:bg-yellow-200 dark:peer-target:bg-yellow-900">{{ code .BlobView.Contents .Path | escapeHtml }}</div>+<div id="blob-contents" class="prose dark:prose-invert">{{ .BlobView.Contents | readme }}</div>-<div id="blob-contents" class="whitespace-pre peer-target:bg-yellow-200 dark:peer-target:bg-yellow-900">{{ $.Contents | escapeHtml }}</div>+<div id="blob-contents" class="whitespace-pre peer-target:bg-yellow-200 dark:peer-target:bg-yellow-900">{{ code .BlobView.Contents .Path | escapeHtml }}</div>+<div id="blob-contents" class="whitespace-pre peer-target:bg-yellow-200 dark:peer-target:bg-yellow-900">{{ code .BlobView.Contents .Path | escapeHtml }}</div>
+35
-10
appview/pages/templates/repo/commit.html
+35
-10
appview/pages/templates/repo/commit.html
···-<a href="mailto:{{ $commit.Author.Email }}" class="no-underline hover:underline text-gray-500 dark:text-gray-300">{{ $commit.Author.Name }}</a><a href="/{{ $repo }}/commit/{{ $commit.This }}" class="no-underline hover:underline text-gray-500 dark:text-gray-300">{{ slice $commit.This 0 8 }}</a>···+authored by {{ template "attributedUser" (list $commit.Author.Email $commit.Author.Name $.EmailToDid) }}+and committed by {{ template "attributedUser" (list $commit.Committer.Email $commit.Committer.Name $.EmailToDid) }}+<a href="mailto:{{ $email }}" class="no-underline hover:underline text-gray-500 dark:text-gray-300">{{ $name }}</a>···
+2
-2
appview/pages/templates/repo/compare/compare.html
+2
-2
appview/pages/templates/repo/compare/compare.html
······
+1
-1
appview/pages/templates/repo/empty.html
+1
-1
appview/pages/templates/repo/empty.html
···<p><span class="{{$bullet}}">1</span>First, generate a new <a href="https://git-scm.com/book/en/v2/Git-on-the-Server-Generating-Your-SSH-Public-Key" class="underline">SSH key pair</a>.</p><p><span class="{{$bullet}}">2</span>Then add the public key to your account from the <a href="/settings" class="underline">settings</a> page.</p>-<p><span class="{{$bullet}}">3</span>Configure your remote to <code>git@{{ $knot | stripPort }}:{{ .RepoInfo.OwnerHandle }}/{{ .RepoInfo.Name }}</code></p>+<p><span class="{{$bullet}}">3</span>Configure your remote to <code>git@{{ $knot | stripPort }}:{{ resolve .RepoInfo.OwnerDid }}/{{ .RepoInfo.Name }}</code></p>
+2
-1
appview/pages/templates/repo/fork.html
+2
-1
appview/pages/templates/repo/fork.html
······-<p class="text-sm text-gray-500 dark:text-gray-400">A knot hosts repository data. <a href="/knots" class="underline">Learn how to register your own knot.</a></p>+<p class="text-sm text-gray-500 dark:text-gray-400">A knot hosts repository data. <a href="/settings/knots" class="underline">Learn how to register your own knot.</a></p>
+3
-2
appview/pages/templates/repo/fragments/cloneDropdown.html
+3
-2
appview/pages/templates/repo/fragments/cloneDropdown.html
···class="flex-1 px-3 py-2 text-sm bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-l select-all cursor-pointer whitespace-nowrap overflow-x-auto"
+2
-3
appview/pages/templates/repo/fragments/diff.html
+2
-3
appview/pages/templates/repo/fragments/diff.html
+15
-1
appview/pages/templates/repo/fragments/editLabelPanel.html
+15
-1
appview/pages/templates/repo/fragments/editLabelPanel.html
···
+1
-16
appview/pages/templates/repo/fragments/participants.html
+1
-16
appview/pages/templates/repo/fragments/participants.html
···-class="rounded-full h-8 w-8 mr-1 border-2 border-gray-100 dark:border-gray-900 z-{{sub 5 $i}}0"
-26
appview/pages/templates/repo/fragments/repoStar.html
-26
appview/pages/templates/repo/fragments/repoStar.html
···
+39
-10
appview/pages/templates/repo/index.html
+39
-10
appview/pages/templates/repo/index.html
···<a href="/{{ .RepoInfo.FullName }}/commits/{{ .Ref | urlquery }}" class="inline-flex items-center text-sm gap-1 font-bold">···<summary class="flex gap-[1px] h-4 scale-y-50 hover:scale-y-100 origin-top group-open:scale-y-100 transition-all hover:cursor-pointer overflow-hidden rounded-t">···<div class="px-4 py-2 border-b border-gray-200 dark:border-gray-600 flex items-center gap-4 flex-wrap">···onchange="window.location.href = '/{{ .RepoInfo.FullName }}/tree/' + encodeURIComponent(this.value)"class="p-1 border max-w-32 border-gray-200 bg-white dark:bg-gray-800 dark:text-white dark:border-gray-700"······->{{ if $did }}{{ template "user/fragments/picHandleLink" $did }}{{ else }}{{ .Author.Name }}{{ end }}</a>···
+125
-49
appview/pages/templates/repo/issues/issues.html
+125
-49
appview/pages/templates/repo/issues/issues.html
···-<input type="hidden" name="state" value="{{ if .FilteringByOpen }}open{{ else }}closed{{ end }}">-<input class="flex-1 p-1 pl-10 pr-10 peer" type="text" name="q" value="{{ .FilterQuery }}" placeholder=" ">+<input type="hidden" name="state" value="{{ if .FilteringByOpen }}open{{ else }}closed{{ end }}">+class="flex-1 py-1 pl-2 pr-10 mr-[-1px] rounded-r-none focus:border-0 focus:outline-none focus:ring focus:ring-blue-400 ring-inset peer"class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hidden peer-[:not(:placeholder-shown)]:block"-class="btn-create text-sm flex items-center justify-center gap-2 no-underline hover:no-underline hover:text-white"+{{ template "fragments/tabSelector" (dict "Name" "state" "Values" $values "Active" $active "Include" "#search-q") }}+class="col-start-3 btn-create text-sm flex items-center justify-center gap-2 no-underline hover:no-underline hover:text-white"···{{ template "repo/issues/fragments/issueListing" (dict "Issues" .Issues "RepoPrefix" .RepoInfo.FullName "LabelDefs" .LabelDefs) }}-class="btn flex items-center gap-2 no-underline hover:no-underline dark:text-white dark:hover:bg-gray-700"-href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&q={{ .FilterQuery }}&offset={{ $prev.Offset }}&limit={{ $prev.Limit }}"+href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&q={{ .FilterQuery }}&offset={{ $prev }}&limit={{ .Page.Limit }}"+href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&q={{ .FilterQuery }}&offset=0&limit={{ .Page.Limit }}"+href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&q={{ .FilterQuery }}&offset={{ $prev }}&limit={{ .Page.Limit }}"+href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&q={{ .FilterQuery }}&offset={{ $next }}&limit={{ .Page.Limit }}"+href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&q={{ .FilterQuery }}&offset={{ $lastPage }}&limit={{ .Page.Limit }}"-class="btn flex items-center gap-2 no-underline hover:no-underline dark:text-white dark:hover:bg-gray-700"-href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&q={{ .FilterQuery }}&offset={{ $next.Offset }}&limit={{ $next.Limit }}"+href="/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&q={{ .FilterQuery }}&offset={{ $next }}&limit={{ .Page.Limit }}"
+40
-23
appview/pages/templates/repo/log.html
+40
-23
appview/pages/templates/repo/log.html
···-<div class="py-2 text-sm text-left text-gray-700 dark:text-gray-300 uppercase font-bold col-span-2">Author</div>+<div class="py-2 text-sm text-left text-gray-700 dark:text-gray-300 uppercase font-bold col-span-3">Author</div><div class="py-2 text-sm text-left text-gray-700 dark:text-gray-300 uppercase font-bold col-span-3">Commit</div><div class="py-2 text-sm text-left text-gray-700 dark:text-gray-300 uppercase font-bold col-span-6">Message</div>-<div class="py-2 text-sm text-left text-gray-700 dark:text-gray-300 uppercase font-bold col-span-1"></div><div class="py-2 text-sm text-left text-gray-700 dark:text-gray-300 uppercase font-bold col-span-2 justify-self-end">Date</div>-<a href="mailto:{{ $commit.Author.Email }}" class="text-gray-700 dark:text-gray-300 no-underline hover:underline">{{ $commit.Author.Name }}</a>···<a href="/{{ $.RepoInfo.FullName }}/commit/{{ $commit.Hash.String }}" class="dark:text-white no-underline hover:underline">{{ index $messageParts 0 }}</a><button class="py-1/2 px-1 bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 rounded" hx-on:click="this.parentElement.nextElementSibling.classList.toggle('hidden')">{{ i "ellipsis" "w-3 h-3" }}</button>···+{{ template "repo/pipelines/fragments/pipelineSymbolLong" (dict "Pipeline" $pipeline "RepoInfo" $.RepoInfo) }}<p class="hidden mt-1 text-sm text-gray-600 dark:text-gray-400">{{ nl2br (index $messageParts 1) }}</p>-{{ template "repo/pipelines/fragments/pipelineSymbolLong" (dict "Pipeline" $pipeline "RepoInfo" $.RepoInfo) }}<div class="align-top justify-self-end text-gray-500 dark:text-gray-400 col-span-2">{{ template "repo/fragments/shortTimeAgo" $commit.Committer.When }}</div>···-{{ if $did }}{{ template "user/fragments/picHandleLink" $did }}{{ else }}{{ $commit.Author.Name }}{{ end }}···
+2
-1
appview/pages/templates/repo/new.html
+2
-1
appview/pages/templates/repo/new.html
······
+3
-3
appview/pages/templates/repo/pipelines/fragments/logBlock.html
+3
-3
appview/pages/templates/repo/pipelines/fragments/logBlock.html
···<details id="step-{{ .Id }}" {{if not .Collapsed}}open{{end}} class="group pb-2 rounded-sm border border-gray-200 dark:border-gray-700"><summary class="sticky top-0 pt-2 px-2 group-open:pb-2 group-open:mb-2 list-none cursor-pointer group-open:border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 hover:text-gray-500 hover:dark:text-gray-400">+<div class="group-open:hidden flex items-center gap-1">{{ i "chevron-right" "w-4 h-4" }} {{ template "stepHeader" . }}</div>+<div class="hidden group-open:flex items-center gap-1">{{ i "chevron-down" "w-4 h-4" }} {{ template "stepHeader" . }}</div><div class="font-mono whitespace-pre overflow-x-auto px-2"><div class="text-blue-600 dark:text-blue-300">{{ .Command }}</div><div id="step-body-{{ .Id }}"></div></div>···<span class="ml-auto text-sm text-gray-500 tabular-nums" data-timer="{{ .Id }}" data-start="{{ .StartTime.Unix }}"></span>
+81
-83
appview/pages/templates/repo/pulls/fragments/pullActions.html
+81
-83
appview/pages/templates/repo/pulls/fragments/pullActions.html
···-class="btn p-2 flex items-center gap-2 no-underline hover:no-underline group text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300">-hx-confirm="Are you sure you want to merge pull #{{ .Pull.PullId }} into the `{{ .Pull.TargetBranch }}` branch?"+class="btn p-2 flex items-center gap-2 no-underline hover:no-underline group text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300">+hx-confirm="Are you sure you want to merge pull #{{ .Pull.PullId }} into the `{{ .Pull.TargetBranch }}` branch?"-class="btn p-2 flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed group" {{ $disabled }}+class="btn p-2 flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed group" {{ $disabled }}
+1
-1
appview/pages/templates/repo/pulls/patch.html
+1
-1
appview/pages/templates/repo/pulls/patch.html
+30
-22
appview/pages/templates/repo/pulls/pulls.html
+30
-22
appview/pages/templates/repo/pulls/pulls.html
···-<input class="flex-1 p-1 pl-10 pr-10 peer" type="text" name="q" value="{{ .FilterQuery }}" placeholder=" ">+class="flex-1 py-1 pl-2 pr-10 mr-[-1px] rounded-r-none focus:border-0 focus:outline-none focus:ring focus:ring-blue-400 ring-inset peer"class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hidden peer-[:not(:placeholder-shown)]:block"-class="btn-create text-sm flex items-center gap-2 no-underline hover:no-underline hover:text-white"+{{ template "fragments/tabSelector" (dict "Name" "state" "Values" $values "Active" $active "Include" "#search-q") }}+class="col-start-3 btn-create text-sm flex items-center gap-2 no-underline hover:no-underline hover:text-white"
+22
-14
appview/pages/templates/repo/settings/access.html
+22
-14
appview/pages/templates/repo/settings/access.html
······-class="bg-white w-full md:w-96 dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 drop-shadow dark:text-white backdrop:bg-gray-400/50 dark:backdrop:bg-gray-800/50">···<p class="text-sm text-gray-500 dark:text-gray-400">Collaborators can push to this repository.</p>
+1
-1
appview/pages/templates/repo/settings/general.html
+1
-1
appview/pages/templates/repo/settings/general.html
+8
appview/pages/templates/repo/tree.html
+8
appview/pages/templates/repo/tree.html
···+{{ $link = printf "/%s/%s/%s/%s/%s" $.RepoInfo.FullName "blob" (urlquery $.Ref) $.TreePath .Name }}+{{ $link = printf "/%s/%s/%s/%s/%s" $.RepoInfo.FullName "blob" (urlquery $.Ref) $.TreePath .Name }}
+22
-6
appview/pages/templates/spindles/dashboard.html
+22
-6
appview/pages/templates/spindles/dashboard.html
···+<div class="bg-white dark:bg-gray-800 p-6 rounded relative w-full mx-auto drop-shadow-sm dark:text-white">···class="btn text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 gap-2 group"······class="btn text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 gap-2 group"
+17
-12
appview/pages/templates/spindles/fragments/addMemberModal.html
+17
-12
appview/pages/templates/spindles/fragments/addMemberModal.html
···-class="bg-white w-full md:w-96 dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 drop-shadow dark:text-white backdrop:bg-gray-400/50 dark:backdrop:bg-gray-800/50">+bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 dark:text-white backdrop:bg-gray-400/50 dark:backdrop:bg-gray-800/50···<p class="text-sm text-gray-500 dark:text-gray-400">Members can register repositories and run workflows on this spindle.</p>
+3
-3
appview/pages/templates/spindles/fragments/spindleListing.html
+3
-3
appview/pages/templates/spindles/fragments/spindleListing.html
···-<a href="/spindles/{{ .Instance }}" class="hover:no-underline flex items-center gap-2 min-w-0 max-w-[60%]">+<a href="/settings/spindles/{{ .Instance }}" class="hover:no-underline flex items-center gap-2 min-w-0 max-w-[60%]">···class="btn text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 gap-2 group"···
+90
-59
appview/pages/templates/spindles/index.html
+90
-59
appview/pages/templates/spindles/index.html
···+<div class="bg-white dark:bg-gray-800 p-6 rounded relative w-full mx-auto drop-shadow-sm dark:text-white">-<section class="bg-white dark:bg-gray-800 p-6 rounded relative w-full mx-auto drop-shadow-sm dark:text-white">···-<div class="flex items-center justify-center p-2 border-b border-gray-200 dark:border-gray-700 text-gray-500">+<div class="flex items-center justify-center p-2 border-b border-gray-200 dark:border-gray-700 text-gray-500">-class="flex-1 w-full dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:placeholder-gray-400 px-3 py-2 border rounded"-class="btn rounded flex items-center py-2 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600 group"+class="flex-1 w-full dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:placeholder-gray-400 px-3 py-2 border rounded"+class="btn rounded flex items-center py-2 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600 group"+class="bg-white w-full md:w-96 dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 drop-shadow dark:text-white backdrop:bg-gray-400/50 dark:backdrop:bg-gray-800/50">
+6
-5
appview/pages/templates/strings/dashboard.html
+6
-5
appview/pages/templates/strings/dashboard.html
···-<meta property="og:url" content="https://tangled.org/{{ or .Card.UserHandle .Card.UserDid }}" />-<meta property="og:description" content="{{ or .Card.Profile.Description .Card.UserHandle .Card.UserDid }}" />···-<a href="/strings/{{ or $root.Card.UserHandle $root.Card.UserDid }}/{{ $s.Rkey }}">{{ $s.Filename }}</a>
+13
-9
appview/pages/templates/strings/string.html
+13
-9
appview/pages/templates/strings/string.html
···-{{ define "title" }}{{ .String.Filename }} · by {{ didOrHandle .Owner.DID.String .Owner.Handle.String }}{{ end }}<meta property="og:url" content="https://tangled.org/strings/{{ $ownerId }}/{{ .String.Rkey }}" />······<a href="/strings/{{ $ownerId }}/{{ .String.Rkey }}" class="font-bold">{{ .String.Filename }}</a>······-<div id="blob-contents" class="whitespace-pre peer-target:bg-yellow-200 dark:peer-target:bg-yellow-900">{{ .String.Contents | escapeHtml }}</div>+<div id="blob-contents" class="whitespace-pre peer-target:bg-yellow-200 dark:peer-target:bg-yellow-900">{{ code .String.Contents .String.Filename | escapeHtml }}</div>
+1
-2
appview/pages/templates/timeline/fragments/goodfirstissues.html
+1
-2
appview/pages/templates/timeline/fragments/goodfirstissues.html
···<div class="flex items-center justify-between gap-2 bg-purple-200 dark:bg-purple-900 border border-purple-400 dark:border-purple-500 rounded mb-4 py-4 px-6 ">
+5
-5
appview/pages/templates/timeline/fragments/timeline.html
+5
-5
appview/pages/templates/timeline/fragments/timeline.html
···<div class="flex flex-col divide-y divide-gray-200 dark:divide-gray-700 border border-gray-200 dark:border-gray-700 rounded-sm">···<span class="text-gray-700 dark:text-gray-400 text-xs">{{ template "repo/fragments/time" $repo.Created }}</span>-{{ template "user/fragments/repoCard" (list $root . true true (dict "IsStarred" $event.IsStarred "RepoAt" .RepoAt "Stats" (dict "StarCount" $event.StarCount))) }}+{{ template "user/fragments/repoCard" (list $root . true true (dict "IsStarred" $event.IsStarred "SubjectAt" .RepoAt "StarCount" $event.StarCount)) }}<div class="pl-6 py-2 bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-300 flex flex-wrap items-center gap-2 text-sm">···<span class="text-gray-700 dark:text-gray-400 text-xs">{{ template "repo/fragments/time" .Created }}</span>-{{ template "user/fragments/repoCard" (list $root . true true (dict "IsStarred" $event.IsStarred "RepoAt" .RepoAt "Stats" (dict "StarCount" $event.StarCount))) }}+{{ template "user/fragments/repoCard" (list $root . true true (dict "IsStarred" $event.IsStarred "SubjectAt" .RepoAt "StarCount" $event.StarCount)) }}
+4
-2
appview/pages/templates/user/followers.html
+4
-2
appview/pages/templates/user/followers.html
······+<div class="text-base text-gray-500 flex items-center justify-center italic p-12 border border-gray-200 dark:border-gray-700 rounded">
+4
-2
appview/pages/templates/user/following.html
+4
-2
appview/pages/templates/user/following.html
······+<div class="text-base text-gray-500 flex items-center justify-center italic p-12 border border-gray-200 dark:border-gray-700 rounded">
+7
-1
appview/pages/templates/user/fragments/editBio.html
+7
-1
appview/pages/templates/user/fragments/editBio.html
···
+1
-1
appview/pages/templates/user/fragments/profileCard.html
+1
-1
appview/pages/templates/user/fragments/profileCard.html
···
+2
-1
appview/pages/templates/user/fragments/repoCard.html
+2
-1
appview/pages/templates/user/fragments/repoCard.html
······
+22
-4
appview/pages/templates/user/overview.html
+22
-4
appview/pages/templates/user/overview.html
······+<div class="text-base text-gray-500 flex items-center justify-center italic p-12 border border-gray-200 dark:border-gray-700 rounded">·········class="flex text-black dark:text-white items-center gap-2 no-underline hover:no-underline group">···+<div class="text-base text-gray-500 flex items-center justify-center italic p-12 border border-gray-200 dark:border-gray-700 rounded">
+4
-2
appview/pages/templates/user/repos.html
+4
-2
appview/pages/templates/user/repos.html
······+<div class="text-base text-gray-500 flex items-center justify-center italic p-12 border border-gray-200 dark:border-gray-700 rounded">
+1
-1
appview/pages/templates/user/settings/notifications.html
+1
-1
appview/pages/templates/user/settings/notifications.html
···
+9
-6
appview/pages/templates/user/signup.html
+9
-6
appview/pages/templates/user/signup.html
···<button class="btn text-base w-full my-2 mt-6" type="submit" id="signup-button" tabindex="7" >+By signing up, you agree to our <a href="/terms" class="underline">Terms of Service</a> and <a href="/privacy" class="underline">Privacy Policy</a>.
+4
-2
appview/pages/templates/user/starred.html
+4
-2
appview/pages/templates/user/starred.html
······+<div class="text-base text-gray-500 flex items-center justify-center italic p-12 border border-gray-200 dark:border-gray-700 rounded">
+5
-3
appview/pages/templates/user/strings.html
+5
-3
appview/pages/templates/user/strings.html
······+<div class="text-base text-gray-500 flex items-center justify-center italic p-12 border border-gray-200 dark:border-gray-700 rounded">···-<a href="/strings/{{ or $root.Card.UserHandle $root.Card.UserDid }}/{{ $s.Rkey }}">{{ $s.Filename }}</a>
+19
-22
appview/pipelines/pipelines.go
+19
-22
appview/pipelines/pipelines.go
······························
+3
-2
appview/pulls/opengraph.go
+3
-2
appview/pulls/opengraph.go
·········
+122
-139
appview/pulls/pulls.go
+122
-139
appview/pulls/pulls.go
······························-func (s *Pulls) mergeCheck(r *http.Request, f *reporesolver.ResolvedRepo, pull *models.Pull, stack models.Stack) types.MergeCheckResponse {+func (s *Pulls) mergeCheck(r *http.Request, f *models.Repo, pull *models.Pull, stack models.Stack) types.MergeCheckResponse {······-func (s *Pulls) branchDeleteStatus(r *http.Request, f *reporesolver.ResolvedRepo, pull *models.Pull) *models.BranchDeleteStatus {+func (s *Pulls) branchDeleteStatus(r *http.Request, repo *models.Repo, pull *models.Pull) *models.BranchDeleteStatus {···// NOTE: appview could cache branches/tags etc. for every repo by listening for gitRefUpdates···-func (s *Pulls) resubmitCheck(r *http.Request, f *reporesolver.ResolvedRepo, pull *models.Pull, stack models.Stack) pages.ResubmitResult {+func (s *Pulls) resubmitCheck(r *http.Request, repo *models.Repo, pull *models.Pull, stack models.Stack) pages.ResubmitResult {if pull.State == models.PullMerged || pull.State == models.PullDeleted || pull.PullSource == nil {······+branchResp, err := tangled.RepoBranch(r.Context(), xrpcc, pull.PullSource.Branch, didSlashName)·································-s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d#comment-%d", f.OwnerSlashRepo(), pull.PullId, commentId))+s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d#comment-%d", ownerSlashRepo, pull.PullId, commentId))·········+roles := repoinfo.RolesInRepo{Roles: s.enforcer.GetPermissionsInRepo(user.Did, f.Knot, f.DidSlashRepo())}······+xrpcBytes, err := tangled.RepoCompare(r.Context(), xrpcc, didSlashRepo, targetBranch, sourceBranch)···-s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, combined, sourceRev, pullSource, recordPullSource, isStacked)+s.createPullRequest(w, r, repo, user, title, body, targetBranch, patch, combined, sourceRev, pullSource, recordPullSource, isStacked)-func (s *Pulls) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *reporesolver.ResolvedRepo, user *oauth.User, title, body, targetBranch, patch string, isStacked bool) {+func (s *Pulls) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, repo *models.Repo, user *oauth.User, title, body, targetBranch, patch string, isStacked bool) {-s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, "", "", nil, nil, isStacked)+s.createPullRequest(w, r, repo, user, title, body, targetBranch, patch, "", "", nil, nil, isStacked)-func (s *Pulls) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *reporesolver.ResolvedRepo, user *oauth.User, forkRepo string, title, body, targetBranch, sourceBranch string, isStacked bool) {+func (s *Pulls) handleForkBasedPull(w http.ResponseWriter, r *http.Request, repo *models.Repo, user *oauth.User, forkRepo string, title, body, targetBranch, sourceBranch string, isStacked bool) {···-s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, combined, sourceRev, pullSource, recordPullSource, isStacked)+s.createPullRequest(w, r, repo, user, title, body, targetBranch, patch, combined, sourceRev, pullSource, recordPullSource, isStacked)·····················-stack, err := s.newStack(r.Context(), f, user, targetBranch, patch, pullSource, stackId.String())+stack, err := s.newStack(r.Context(), repo, user, targetBranch, patch, pullSource, stackId.String())·································+roles := repoinfo.RolesInRepo{Roles: s.enforcer.GetPermissionsInRepo(user.Did, f.Knot, f.DidSlashRepo())}···xrpcBytes, err := tangled.RepoCompare(r.Context(), xrpcc, repo, pull.TargetBranch, pull.PullSource.Branch)···············-newStack, err := s.newStack(r.Context(), f, user, targetBranch, patch, pull.PullSource, stackId)+newStack, err := s.newStack(r.Context(), repo, user, targetBranch, patch, pull.PullSource, stackId)···············+roles := repoinfo.RolesInRepo{Roles: s.enforcer.GetPermissionsInRepo(user.Did, f.Knot, f.DidSlashRepo())}······+roles := repoinfo.RolesInRepo{Roles: s.enforcer.GetPermissionsInRepo(user.Did, f.Knot, f.DidSlashRepo())}···-func (s *Pulls) newStack(ctx context.Context, f *reporesolver.ResolvedRepo, user *oauth.User, targetBranch, patch string, pullSource *models.PullSource, stackId string) (models.Stack, error) {+func (s *Pulls) newStack(ctx context.Context, repo *models.Repo, user *oauth.User, targetBranch, patch string, pullSource *models.PullSource, stackId string) (models.Stack, error) {······
-65
appview/refresolver/resolver.go
-65
appview/refresolver/resolver.go
···-func (r *Resolver) Resolve(ctx context.Context, source string) ([]syntax.DID, []syntax.ATURI) {
+2
-2
appview/repo/archive.go
+2
-2
appview/repo/archive.go
···
+21
-14
appview/repo/artifact.go
+21
-14
appview/repo/artifact.go
·····················-func (rp *Repo) resolveTag(ctx context.Context, f *reporesolver.ResolvedRepo, tagParam string) (*types.TagReference, error) {+func (rp *Repo) resolveTag(ctx context.Context, f *models.Repo, tagParam string) (*types.TagReference, error) {···
+142
-68
appview/repo/blob.go
+142
-68
appview/repo/blob.go
·········-breadcrumbs = append(breadcrumbs, []string{f.Name, fmt.Sprintf("/%s/tree/%s", f.OwnerSlashRepo(), url.PathEscape(ref))})+breadcrumbs = append(breadcrumbs, []string{f.Name, fmt.Sprintf("/%s/tree/%s", ownerSlashRepo, url.PathEscape(ref))})breadcrumbs = append(breadcrumbs, []string{elem, fmt.Sprintf("%s/%s", breadcrumbs[idx][1], url.PathEscape(elem))})···l.Error("knotserver returned non-OK status for raw blob", "url", blobURL, "statuscode", resp.StatusCode)······+func NewBlobView(resp *tangled.RepoBlob_Output, config *config.Config, repo *models.Repo, ref, filePath string, queryParams url.Values) models.BlobView {
+2
-2
appview/repo/branches.go
+2
-2
appview/repo/branches.go
······
+18
-18
appview/repo/compare.go
+18
-18
appview/repo/compare.go
···············
+24
-17
appview/repo/feed.go
+24
-17
appview/repo/feed.go
···-func (rp *Repo) getRepoFeed(ctx context.Context, f *reporesolver.ResolvedRepo) (*feeds.Feed, error) {+func (rp *Repo) getRepoFeed(ctx context.Context, repo *models.Repo, ownerSlashRepo string) (*feeds.Feed, error) {-pulls, err := db.GetPullsWithLimit(rp.db, feedLimitPerType, db.FilterEq("repo_at", f.RepoAt()))+pulls, err := db.GetPullsWithLimit(rp.db, feedLimitPerType, orm.FilterEq("repo_at", repo.RepoAt()))···-Link: &feeds.Link{Href: fmt.Sprintf("%s/%s", rp.config.Core.AppviewHost, f.OwnerSlashRepo()), Type: "text/html", Rel: "alternate"},+Link: &feeds.Link{Href: fmt.Sprintf("%s/%s", rp.config.Core.AppviewHost, ownerSlashRepo), Type: "text/html", Rel: "alternate"},······-func (rp *Repo) createPullItems(ctx context.Context, pull *models.Pull, f *reporesolver.ResolvedRepo) ([]*feeds.Item, error) {+func (rp *Repo) createPullItems(ctx context.Context, pull *models.Pull, repo *models.Repo, ownerSlashRepo string) ([]*feeds.Item, error) {···-Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d", rp.config.Core.AppviewHost, f.OwnerSlashRepo(), pull.PullId)},+Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d", rp.config.Core.AppviewHost, ownerSlashRepo, pull.PullId)},···-Description: fmt.Sprintf("@%s submitted changes (at round #%d) on PR #%d in %s", owner.Handle, round.RoundNumber, pull.PullId, f.OwnerSlashRepo()),-Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d/round/%d/", rp.config.Core.AppviewHost, f.OwnerSlashRepo(), pull.PullId, round.RoundNumber)},+Description: fmt.Sprintf("@%s submitted changes (at round #%d) on PR #%d in @%s", owner.Handle, round.RoundNumber, pull.PullId, ownerSlashRepo),+Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d/round/%d/", rp.config.Core.AppviewHost, ownerSlashRepo, pull.PullId, round.RoundNumber)},···-func (rp *Repo) createIssueItem(ctx context.Context, issue models.Issue, f *reporesolver.ResolvedRepo) (*feeds.Item, error) {+func (rp *Repo) createIssueItem(ctx context.Context, issue models.Issue, repo *models.Repo, ownerSlashRepo string) (*feeds.Item, error) {···-Description: fmt.Sprintf("@%s %s issue #%d in %s", owner.Handle, state, issue.IssueId, f.OwnerSlashRepo()),-Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/issues/%d", rp.config.Core.AppviewHost, f.OwnerSlashRepo(), issue.IssueId)},+Description: fmt.Sprintf("@%s %s issue #%d in @%s", owner.Handle, state, issue.IssueId, ownerSlashRepo),+Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/issues/%d", rp.config.Core.AppviewHost, ownerSlashRepo, issue.IssueId)},···
+22
-24
appview/repo/index.go
+22
-24
appview/repo/index.go
·································// buildIndexResponse creates a RepoIndexResponse by combining multiple xrpc calls in parallel-func (rp *Repo) buildIndexResponse(ctx context.Context, xrpcc *indigoxrpc.Client, f *reporesolver.ResolvedRepo, ref string) (*types.RepoIndexResponse, error) {+func (rp *Repo) buildIndexResponse(ctx context.Context, xrpcc *indigoxrpc.Client, repo *models.Repo, ref string) (*types.RepoIndexResponse, error) {············
+8
-11
appview/repo/log.go
+8
-11
appview/repo/log.go
···············-vc, err := commitverify.GetVerifiedCommits(rp.db, emailToDidMap, []types.NiceDiff{*result.Diff})+vc, err := commitverify.GetVerifiedCommits(rp.db, emailToDidMap, []types.Commit{result.Diff.Commit})···
+4
-3
appview/repo/opengraph.go
+4
-3
appview/repo/opengraph.go
·········
+37
-37
appview/repo/repo.go
+37
-37
appview/repo/repo.go
···························-ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoNSID, f.Repo.Did, f.Repo.Rkey)······-ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoNSID, f.Repo.Did, f.Repo.Rkey)···················································
+20
-35
appview/repo/repo_util.go
+20
-35
appview/repo/repo_util.go
······func balanceIndexItems(commitCount, branchCount, tagCount, fileCount int) (commitsTrunc int, branchesTrunc int, tagsTrunc int) {······
-1
appview/repo/router.go
-1
appview/repo/router.go
+41
-12
appview/repo/settings.go
+41
-12
appview/repo/settings.go
·········-defaultLabels, err := db.GetLabelDefinitions(rp.db, db.FilterIn("at_uri", rp.config.Label.DefaultLabelDefs))+defaultLabels, err := db.GetLabelDefinitions(rp.db, orm.FilterIn("at_uri", rp.config.Label.DefaultLabelDefs))·········+repoCollaborators, err := rp.enforcer.E.GetImplicitUsersForResourceByDomain(repo.DidSlashRepo(), repo.Knot)············
+10
-9
appview/repo/tree.go
+10
-9
appview/repo/tree.go
············// redirects tree paths trying to access a blob; in this case the result.Files is unpopulated,-redirectTo := fmt.Sprintf("/%s/blob/%s/%s", f.OwnerSlashRepo(), url.PathEscape(ref), result.Parent)+redirectTo := fmt.Sprintf("/%s/blob/%s/%s", ownerSlashRepo, url.PathEscape(ref), result.Parent)-breadcrumbs = append(breadcrumbs, []string{f.Name, fmt.Sprintf("/%s/tree/%s", f.OwnerSlashRepo(), url.PathEscape(ref))})+breadcrumbs = append(breadcrumbs, []string{f.Name, fmt.Sprintf("/%s/tree/%s", ownerSlashRepo, url.PathEscape(ref))})breadcrumbs = append(breadcrumbs, []string{elem, fmt.Sprintf("%s/%s", breadcrumbs[idx][1], url.PathEscape(elem))})
+76
-164
appview/reporesolver/resolver.go
+76
-164
appview/reporesolver/resolver.go
······-func New(config *config.Config, enforcer *rbac.Enforcer, resolver *idresolver.Resolver, execer db.Execer) *RepoResolver {+// NOTE: this... should not even be here. the entire package will be removed in future refactor-repoCollaborators, err := f.rr.enforcer.E.GetImplicitUsersForResourceByDomain(f.DidSlashRepo(), f.Knot)
+5
-4
appview/serververify/verify.go
+5
-4
appview/serververify/verify.go
·········
+2
appview/settings/settings.go
+2
appview/settings/settings.go
+44
-26
appview/spindles/spindles.go
+44
-26
appview/spindles/spindles.go
······················································
+6
-5
appview/state/gfi.go
+6
-5
appview/state/gfi.go
············
+6
-5
appview/state/knotstream.go
+6
-5
appview/state/knotstream.go
·········return fmt.Errorf("failed to look for repo in DB (%s/%s): %w", record.RepoDid, record.RepoName, err)···
+28
-21
appview/state/profile.go
+28
-21
appview/state/profile.go
·······································
+19
-13
appview/state/router.go
+19
-13
appview/state/router.go
··················
+2
-1
appview/state/spindlestream.go
+2
-1
appview/state/spindlestream.go
······
+9
-13
appview/state/star.go
+9
-13
appview/state/star.go
·········
+29
-28
appview/state/state.go
+29
-28
appview/state/state.go
··················-gfiLabel, err := db.GetLabelDefinition(s.db, db.FilterEq("at_uri", s.config.Label.GoodFirstIssue))+gfiLabel, err := db.GetLabelDefinition(s.db, orm.FilterEq("at_uri", s.config.Label.GoodFirstIssue))············
+21
-8
appview/strings/strings.go
+21
-8
appview/strings/strings.go
···············
+2
-1
appview/validator/issue.go
+2
-1
appview/validator/issue.go
···
+1
-34
crypto/verify.go
+1
-34
crypto/verify.go
······
+3
-3
docs/hacking.md
+3
-3
docs/hacking.md
······
+1
-1
docs/knot-hosting.md
+1
-1
docs/knot-hosting.md
···
+3
-3
docs/migrations.md
+3
-3
docs/migrations.md
······
+20
-3
flake.lock
+20
-3
flake.lock
·········
+12
-12
flake.nix
+12
-12
flake.nix
···url = "https://github.com/IBM/plex/releases/download/%40ibm%2Fplex-mono%401.1.0/ibm-plex-mono.zip";···············+services.tangled.appview.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.appview;···+services.tangled.knot.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.knot;···+services.tangled.spindle.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.spindle;
+5
-15
go.mod
+5
-15
go.mod
························
-21
go.sum
-21
go.sum
···github.com/bluesky-social/jetstream v0.0.0-20241210005130-ea96859b93d1 h1:CFvRtYNSnWRAi/98M3O466t9dYuwtesNbu6FVPymRrA=github.com/bluesky-social/jetstream v0.0.0-20241210005130-ea96859b93d1/go.mod h1:WiYEeyJSdUwqoaZ71KJSpTblemUCpwJfh5oVXplK6T4=github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=···github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=-github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=-github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU=github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=···-github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=···github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=···github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=github.com/whyrusleeping/cbor-gen v0.3.1/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so=-github.com/wyatt915/goldmark-treeblood v0.0.1/go.mod h1:SmcJp5EBaV17rroNlgNQFydYwy0+fv85CUr/ZaCz208=github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
+34
input.css
+34
input.css
······
+15
-4
jetstream/jetstream.go
+15
-4
jetstream/jetstream.go
······
+81
knotserver/db/db.go
+81
knotserver/db/db.go
···
-64
knotserver/db/init.go
-64
knotserver/db/init.go
···
+1
-17
knotserver/git/diff.go
+1
-17
knotserver/git/diff.go
···
+38
-2
knotserver/git/fork.go
+38
-2
knotserver/git/fork.go
······
+60
-2
knotserver/git/git.go
+60
-2
knotserver/git/git.go
·········
+4
-13
knotserver/git/tree.go
+4
-13
knotserver/git/tree.go
············
+1
-1
knotserver/ingester.go
+1
-1
knotserver/ingester.go
+1
-1
knotserver/internal.go
+1
-1
knotserver/internal.go
+1
-1
knotserver/server.go
+1
-1
knotserver/server.go
+1
-1
knotserver/xrpc/create_repo.go
+1
-1
knotserver/xrpc/create_repo.go
···
+21
-2
knotserver/xrpc/repo_blob.go
+21
-2
knotserver/xrpc/repo_blob.go
·········
+6
-1
knotserver/xrpc/repo_log.go
+6
-1
knotserver/xrpc/repo_log.go
···
+3
-5
knotserver/xrpc/repo_tree.go
+3
-5
knotserver/xrpc/repo_tree.go
···
+49
-5
lexicons/repo/blob.json
+49
-5
lexicons/repo/blob.json
·····················
+1
-9
lexicons/repo/tree.json
+1
-9
lexicons/repo/tree.json
······
-30
nix/gomod2nix.toml
-30
nix/gomod2nix.toml
············
+278
-12
nix/modules/appview.nix
+278
-12
nix/modules/appview.nix
···+default = "at://did:plc:wshs7t2adsemcrrd4snkeqli/sh.tangled.label.definition/good-first-issue";···
+60
-2
nix/modules/knot.nix
+60
-2
nix/modules/knot.nix
·····················
+2
nix/pkgs/appview-static-files.nix
+2
nix/pkgs/appview-static-files.nix
······
+1
-1
nix/pkgs/knot-unwrapped.nix
+1
-1
nix/pkgs/knot-unwrapped.nix
+7
-5
nix/pkgs/sqlite-lib.nix
+7
-5
nix/pkgs/sqlite-lib.nix
···
+5
-5
nix/vm.nix
+5
-5
nix/vm.nix
······
+122
orm/orm.go
+122
orm/orm.go
···+func RunMigration(c *sql.Conn, logger *slog.Logger, name string, migrationFn migrationFn) error {+err = tx.QueryRow("select exists (select 1 from migrations where name = ?)", name).Scan(&exists)+if (kind == reflect.Slice && rv.Type().Elem().Kind() != reflect.Uint8) || kind == reflect.Array {+if (kind == reflect.Slice && rv.Type().Elem().Kind() != reflect.Uint8) || kind == reflect.Array {
-1
patchutil/patchutil.go
-1
patchutil/patchutil.go
+8
rbac/rbac.go
+8
rbac/rbac.go
···
+31
sets/gen.go
+31
sets/gen.go
···
+35
sets/readme.txt
+35
sets/readme.txt
···
+174
sets/set.go
+174
sets/set.go
···
+411
sets/set_test.go
+411
sets/set_test.go
···
+1
-1
spindle/config/config.go
+1
-1
spindle/config/config.go
···JetstreamEndpoint string `env:"JETSTREAM_ENDPOINT, default=wss://jetstream1.us-west.bsky.network/subscribe"`
+10
-9
spindle/engines/nixery/engine.go
+10
-9
spindle/engines/nixery/engine.go
···func (e *Engine) InitWorkflow(twf tangled.Pipeline_Workflow, tpl tangled.Pipeline) (*models.Workflow, error) {······func (e *Engine) RunStep(ctx context.Context, wid models.WorkflowId, w *models.Workflow, idx int, secrets []secrets.UnlockedSecret, wfLogger *models.WorkflowLogger) error {······
-73
spindle/engines/nixery/setup_steps.go
-73
spindle/engines/nixery/setup_steps.go
······-func cloneStep(twf tangled.Pipeline_Workflow, tr tangled.Pipeline_TriggerMetadata, dev bool) Step {
+150
spindle/models/clone.go
+150
spindle/models/clone.go
···+func BuildCloneStep(twf tangled.Pipeline_Workflow, tr tangled.Pipeline_TriggerMetadata, dev bool) CloneStep {
+371
spindle/models/clone_test.go
+371
spindle/models/clone_test.go
···
+4
-3
spindle/models/pipeline.go
+4
-3
spindle/models/pipeline.go
+77
spindle/models/pipeline_env.go
+77
spindle/models/pipeline_env.go
···+func PipelineEnvVars(tr *tangled.Pipeline_TriggerMetadata, pipelineId PipelineId, devMode bool) map[string]string {
+260
spindle/models/pipeline_env_test.go
+260
spindle/models/pipeline_env_test.go
···+t.Errorf("Expected TANGLED_REPO_DEFAULT_BRANCH='main', got '%s'", env["TANGLED_REPO_DEFAULT_BRANCH"])+t.Errorf("Expected TANGLED_REPO_URL='https://example.com/did:plc:user123/my-repo', got '%s'", env["TANGLED_REPO_URL"])+t.Errorf("Expected TANGLED_PR_SOURCE_BRANCH='feature-branch', got '%s'", env["TANGLED_PR_SOURCE_BRANCH"])+t.Errorf("Expected TANGLED_PR_TARGET_BRANCH='main', got '%s'", env["TANGLED_PR_TARGET_BRANCH"])+t.Errorf("Expected TANGLED_PR_SOURCE_SHA='pr-sha-789', got '%s'", env["TANGLED_PR_SOURCE_SHA"])+t.Errorf("Expected TANGLED_INPUT_ENVIRONMENT='production', got '%s'", env["TANGLED_INPUT_ENVIRONMENT"])
+15
-7
spindle/secrets/openbao.go
+15
-7
spindle/secrets/openbao.go
············
+5
-2
spindle/secrets/openbao_test.go
+5
-2
spindle/secrets/openbao_test.go
······
+96
-40
spindle/server.go
+96
-40
spindle/server.go
······+func New(ctx context.Context, cfg *config.Config, engines map[string]models.Engine) (*Spindle, error) {···+return nil, fmt.Errorf("openbao proxy address is required when using openbao secrets provider")···logger.Info("using openbao secrets provider", "proxy_address", cfg.Server.Secrets.OpenBao.ProxyAddr, "mount", cfg.Server.Secrets.OpenBao.Mount)···jc, err := jetstream.NewJetstreamClient(cfg.Server.JetstreamEndpoint, "spindle", collections, nil, log.SubLogger(logger, "jetstream"), d, true, true)············-logger.Error("server error", "error", http.ListenAndServe(cfg.Server.ListenAddr, spindle.Router()))······
+5
spindle/stream.go
+5
spindle/stream.go
···+if err := conn.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(time.Second)); err != nil {
+199
types/commit.go
+199
types/commit.go
···
+2
-12
types/diff.go
+2
-12
types/diff.go
······
+39
-18
types/repo.go
+39
-18
types/repo.go
······
+88
-5
types/tree.go
+88
-5
types/tree.go
···