Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Blame · forge/instancerepo/seed.go

Line attribution on main · 217 line(s)

3e7c5e67
Keenan Nemetz
1d ago
1 package instancerepo
2
3 // Creating the repository Vole reads for its presentation. `vole admin init`
4 // calls Ensure so a new instance starts with a repository an admin can open,
5 // edit, and push to like any other. The Admin page calls the same function, so
6 // an instance that predates this behaviour has the same repair path.
7
8 import (
9 "errors"
a407c4af
Keenan Nemetz
1d ago
10 "net/url"
3e7c5e67
Keenan Nemetz
1d ago
11 "os"
12 "strings"
13
d37d301f
Keenan Nemetz
17h ago
14 "rodent.software/vole/core/git"
15 "rodent.software/vole/forge/repo"
3e7c5e67
Keenan Nemetz
1d ago
16 )
17
18 // DefaultDescription is what the repository list shows before an admin edits
19 // it.
20 const DefaultDescription = "Instance customization: profile README, custom CSS, favicon, and assets"
21
22 // Ensure creates name when it is missing and seeds the starter tree when it has
23 // no commits. An existing repository with history is left alone. owner may be
24 // empty, which creates a private repository that instance admins can still
25 // administer; author names the commit when the starter files are written.
26 //
27 // It reports whether it wrote anything, so a caller can audit a creation
28 // without inventing its own answer to "was this new?".
a407c4af
Keenan Nemetz
1d ago
29 func Ensure(repos *repo.Store, name, owner, author string) (*repo.Meta, bool, error) {
3e7c5e67
Keenan Nemetz
1d ago
30 name = strings.TrimSpace(name)
31 if name == "" || repos == nil {
32 return nil, false, nil
33 }
34 created := false
35 m, err := repos.Meta(name)
36 if err != nil {
37 if !errors.Is(err, os.ErrNotExist) {
38 return nil, false, err
39 }
40 m, err = repos.Create(name, owner, DefaultDescription)
41 if err != nil {
42 return nil, false, err
43 }
44 created = true
45 }
46 g := m.Git()
47 tip, err := g.RevParseRaw("refs/heads/" + m.DefaultBranch)
48 if err != nil {
49 return nil, created, err
50 }
51 if tip != "" {
52 return m, created, nil
53 }
a407c4af
Keenan Nemetz
1d ago
54 tree, err := git.WriteFlatTree(g, Template(name))
3e7c5e67
Keenan Nemetz
1d ago
55 if err != nil {
56 return nil, created, err
57 }
58 commit, err := g.CommitTreeAs(tree, "", "Create the instance repository", author)
59 if err != nil {
60 return nil, created, err
61 }
62 if err := g.UpdateRef("refs/heads/"+m.DefaultBranch, commit); err != nil {
63 return nil, created, err
64 }
65 return m, true, nil
66 }
67
68 // Template is the starter tree. The root README explains the fixed paths; the
69 // profile files give an admin something to edit rather than a blank page, and
a407c4af
Keenan Nemetz
1d ago
70 // the CSS names the variables worth overriding. repoName is the repository the
71 // starter tree is written to, so the introduction can link back to its overview.
72 func Template(repoName string) map[string][]byte {
73 repoName = strings.TrimSpace(repoName)
74 // The page supplies the heading (instance_name), so the starter file carries
75 // no header of its own: it explains how to replace itself and links to the
76 // repository where the other files live.
77 intro := "This is the landing page introduction. Tell people what this forge is for."
78 if repoName != "" {
79 intro = "This is the landing page introduction. To customize it, edit `profile/README.md` in the [" +
ed03b3d1
Keenan Nemetz
5h ago
80 repoName + "](/" + url.PathEscape(repoName) + ") repository; its README lists every file Vole reads."
3e7c5e67
Keenan Nemetz
1d ago
81 }
82 return map[string][]byte{
83 "README.md": []byte(`# Instance customization
84
85 This repository customizes the Vole instance. Vole creates it with ` + "`vole admin init`" + ` and
86 reloads it on a short timer, so a push is live within a couple of seconds.
87
88 Only these paths are read; other files are ignored, so this README and any
89 notes can live here too.
90
91 profile/README.md the landing page introduction (markdown)
92 profile/private/README.md replaces it for signed-in viewers
f7a34d87
Keenan Nemetz
1d ago
93 profile/custom.css instance-wide styles, loaded after the theme
d222b0cf
Keenan Nemetz
1d ago
94 profile/themes/<id>.css one stylesheet per theme in Account -> Appearance
3e7c5e67
Keenan Nemetz
1d ago
95 profile/favicon.svg replaces the built-in favicon
ed03b3d1
Keenan Nemetz
5h ago
96 assets/ served at /-/static/custom/assets/
3e7c5e67
Keenan Nemetz
1d ago
97
98 Edit a file here and push, or open it in the web interface and use the editor.
99 This repository has the usual pages, history, issues, and change requests. Its
100 visibility is a repository setting, so an instance admin can publish it or keep
101 it private.
102
103 The files above are served to visitors (the private README only to signed-in
104 viewers), even when the repository itself is private.
f7a34d87
Keenan Nemetz
1d ago
105
106 ## Themes
107
108 The Account page's Appearance picker lists the built-in Everforest variants and
d222b0cf
Keenan Nemetz
1d ago
109 every CSS file in profile/themes/. A theme is self-describing: its file name is
110 the id and the label, and its rules are scoped to that id. Add one by creating
111 the stylesheet:
f7a34d87
Keenan Nemetz
1d ago
112
113 profile/themes/acme-ocean.css
114 :root[data-theme="acme-ocean"] {
115 color-scheme: dark;
116 --bg: #0b1720;
117 --bg-panel: #102433;
118 --bg-raised: #163041;
119 --fg: #d7e8f0;
120 --fg-muted: #8fb0c0;
121 --fg-faint: #6f8f9f;
122 --accent: #38bdf8;
123 --accent-fg: #04121a;
124 --accent-soft: color-mix(in srgb, var(--accent) 14%, transparent);
125 }
126
127 profile/themes/acme-paper.css
128 :root[data-theme="acme-paper"] {
129 color-scheme: light;
130 --bg: #fffdf7;
131 --bg-panel: #f8f3e8;
132 --bg-raised: #f1eadc;
133 --fg: #2b2a26;
134 --fg-muted: #6b675d;
135 --fg-faint: #8d887c;
136 --accent: #9a5b13;
137 --accent-fg: #fffdf7;
138 --accent-soft: color-mix(in srgb, var(--accent) 14%, transparent);
139 }
140
d222b0cf
Keenan Nemetz
1d ago
141 The file name becomes the id and the label: acme-ocean.css is the "Acme Ocean"
0584bb70
Keenan Nemetz
1d ago
142 theme. It must use lowercase letters, digits, and dashes, and cannot collide with
97b3b022
Keenan Nemetz
1d ago
143 a built-in value: system, nord, dracula, tokyo-night, or a name starting with
144 everforest-, catppuccin-, solarized-, rose-pine-, kanagawa-, or ayu-. Each stylesheet is
f7a34d87
Keenan Nemetz
1d ago
145 loaded after the built-in sheets and before profile/custom.css, and its rules
146 apply only while that theme is selected. Set color-scheme and the tokens the
147 theme changes: a light theme needs color-scheme: light and light surface and
148 text tokens.
149
150 profile/custom.css applies to every theme, so keep instance-wide overrides
151 there and put a selectable palette in profile/themes/<id>.css. Keep each theme
d222b0cf
Keenan Nemetz
1d ago
152 file small: every theme is linked on every page.
f7a34d87
Keenan Nemetz
1d ago
153
d222b0cf
Keenan Nemetz
1d ago
154 The picker stores the choice in the browser, not on the account. A theme whose
155 file is deleted falls back to System the next time the page loads.
3e7c5e67
Keenan Nemetz
1d ago
156 `),
a407c4af
Keenan Nemetz
1d ago
157 "profile/README.md": []byte(intro + "\n"),
f7a34d87
Keenan Nemetz
1d ago
158 "profile/custom.css": []byte(`/* Custom styles. This file is loaded last: after vole.css, everforest.css, and
159 * any profile/themes/<id>.css, so it can override every theme.
160 *
161 * For a palette that appears in Account -> Appearance, add
d222b0cf
Keenan Nemetz
1d ago
162 * profile/themes/<id>.css; its file name is the id and label. This file is for
163 * overrides that apply no matter which theme is selected.
3e7c5e67
Keenan Nemetz
1d ago
164 *
bc95767f
Keenan Nemetz
1d ago
165 * Prefer tokens over rules: a token keeps the layout, contrast, and responsive
166 * behaviour that the built-in sheets already got right.
3e7c5e67
Keenan Nemetz
1d ago
167 *
bc95767f
Keenan Nemetz
1d ago
168 * Use :root[data-theme], not plain :root. Everforest sets its tokens on
169 * :root[data-theme^="everforest-"], which is more specific than :root, so a
170 * plain :root override can lose even though this file loads later.
171 */
172
173 :root[data-theme] {
174 /* Uncomment one line at a time and reload the page.
175 *
176 * --accent: #c678dd;
177 * --accent-soft: color-mix(in srgb, var(--accent) 14%, transparent);
178 * --radius: 4px;
179 * --radius-lg: 6px;
180 * --page-max: 1200px;
181 * --font: ui-sans-serif, system-ui, sans-serif;
182 */
183 }
184
185 /* A value that has to differ between dark and light modes belongs in one of
186 * these blocks. They match every Everforest variant.
187 *
188 * :root[data-theme^="everforest-dark"] { --bg: #101418; }
189 * :root[data-theme^="everforest-light"] { --bg: #fffdf7; }
190 */
191
192 /* Tokens, grouped by what they change:
193 *
194 * surfaces --bg --bg-panel --bg-raised --bg-hover --bg-active
195 * --border --border-strong
196 * text --fg --fg-muted --fg-faint
197 * identity --accent --accent-fg --accent-soft
198 * status --good --good-soft --bad --bad-soft --warn --warn-soft
199 * --running --merged --merged-soft --status-fg
200 * shape --radius --radius-lg --page-max --page-gutter --search-w
201 * type --font --mono --fs --lh
202 * (--font-scale is the reader's text-size preset: leave it alone)
203 * syntax --syn-comment --syn-punctuation --syn-operator --syn-number
204 * --syn-string --syn-keyword --syn-function --syn-type
205 * --syn-variable --syn-added --syn-deleted
206 */
207
208 /* If a token cannot express the change, these page-level classes cover most
209 * re-skins. They are not a public API; an upgrade may move markup, so prefer
210 * tokens when one exists.
211 *
212 * .panel .readme .md .repo-card .repo-grid .home-grid .home-rail
213 * .repohead .tabs .chip .label .status .config-table .diff .blob
3e7c5e67
Keenan Nemetz
1d ago
214 */
215 `),
216 }
217 }