Commit ba120a7365

ba120a7365b3c61f2785debcaedfb78c095fd144

parent: ddb7ee92e6

Verified · cmc ci/build: success ci/test: error

cmc <hello@cleberg.net> · 2026-09-20 00:45 UTC

profile: the tests and the CLI table follow the about out of the flags

The profile form no longer previews, the about round-trip is a commit
into .gitbay, and the passthrough help no longer advertises --about,
--about-format or stdin.

Ref #236
cmd/gitbay/main.go +4 −4
@@ -102,8 +102,8 @@ func newRoot() *cobra.Command {
102102 orgCmd(),
103103 group("profile", "user and org profiles",
104104 pass("show", "show a profile: [name]", passOpts{server: []string{"profile", "show"}}),
105 pass("set", "set your profile: [--description d] [--website url] [--about t|--file -] [--about-format md|org] [--link label|url]...",
106 passOpts{server: []string{"profile", "set"}, stdinOK: true}),
105 pass("set", "set your profile: [--description d] [--website url] [--link label|url]...",
106 passOpts{server: []string{"profile", "set"}}),
107107 ),
108108 webhookCmd(),
109109 remoteCmd(),
@@ -671,8 +671,8 @@ func orgCmd() *cobra.Command {
671671 pass("show", "show an organization and its members", passOpts{server: []string{"org", "show"}}),
672672 pass("rename", "rename an organization: <old> <new>", passOpts{server: []string{"org", "rename"}}),
673673 pass("delete", "delete an empty organization (--yes)", passOpts{server: []string{"org", "delete"}}),
674 pass("profile", "show or set an org profile: <org> [--description d] [--website url] [--about t|--file -] [--about-format md|org] [--link label|url]...",
675 passOpts{server: []string{"org", "profile"}, stdinOK: true}),
674 pass("profile", "show or set an org profile: <org> [--description d] [--website url] [--link label|url]...",
675 passOpts{server: []string{"org", "profile"}}),
676676 group("members", "manage members",
677677 pass("add", "add or update a member: <org> <user> [--role member|admin]", passOpts{server: []string{"org", "members", "add"}}),
678678 pass("remove", "remove a member: <org> <user>", passOpts{server: []string{"org", "members", "remove"}}),
e2e/previewweb_test.go +4 −8
@@ -137,14 +137,10 @@ func TestMarkupPreviewWeb(t *testing.T) {
137137 })
138138 previewed("release edit", b, "<h2", "## release notes")
139139
140 // The profile's about text.
141 b = post(inst.base()+"/settings", url.Values{
142 "field": {"profile"}, "about": {"# about me"}, "format": {"md"},
143 "description": {"kept"}, "preview": {"1"},
144 })
145 previewed("profile about", b, "<h1", `value="kept"`)
146 if out, _, _ := inst.ssh(t, aliceKey, "", "profile", "show", "--json"); strings.Contains(out, "about me") {
147 t.Fatalf("preview saved the profile: %s", out)
140 // The profile form takes no markup: the about text is a file, and the
141 // file editor below is what previews it.
142 if _, body := browserGet(t, alice, inst.base()+"/settings"); strings.Contains(body, `name="preview"`) {
143 t.Fatalf("the profile form offers a preview:\n%s", body)
148144 }
149145
150146 // The file editor previews a rendered path and offers nothing on one
e2e/profile_test.go +25 −28
@@ -11,7 +11,9 @@ func TestOwnerProfiles(t *testing.T) {
1111 inst := startInstance(t)
1212 aliceKey := inst.newKey(t, "alice")
1313 bobKey := inst.newKey(t, "bob")
14 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
14 // A verified address, because the about text is a commit now.
15 inst.admin(t, "admin", "user", "create", "alice",
16 "--key", aliceKey+".pub", "--email", "alice@example.test", "--verified")
1517 inst.admin(t, "admin", "user", "create", "bob", "--key", bobKey+".pub")
1618
1719 // Self-service user profile; website validated.
@@ -136,30 +138,22 @@ func TestOwnerProfiles(t *testing.T) {
136138 t.Fatalf("org profile show: %s", out)
137139 }
138140
139 // About: long-form markdown, set inline or piped, rendered on the page.
140 if _, errOut, code := inst.ssh(t, aliceKey, "# Hello\n\nI maintain *small tools*.\n",
141 "profile", "set", "--file", "-"); code != 0 {
142 t.Fatalf("about from stdin: %s", errOut)
143 }
144 out, _, _ = inst.ssh(t, aliceKey, "", "profile", "show", "alice", "--json")
145 if !strings.Contains(out, "I maintain *small tools*.") {
146 t.Fatalf("about not stored: %s", out)
141 // About: a file in <owner>/.gitbay, rendered on the page. The
142 // extension picks the renderer; there is no stored format.
143 if _, errOut, code := inst.ssh(t, aliceKey, "", "repo", "create", "alice/.gitbay"); code != 0 {
144 t.Fatalf("creating alice/.gitbay: %s", errOut)
147145 }
148 if !strings.Contains(out, "tinkerer") {
149 t.Fatalf("about clobbered the description: %s", out)
150 }
151
152 // Org-mode about: the stored format picks the renderer.
153146 if _, errOut, code := inst.ssh(t, aliceKey, "* Tools\n\nI maintain /small tools/.\n",
154 "profile", "set", "--file", "-", "--about-format", "org"); code != 0 {
147 "repo", "commit-file", "alice/.gitbay", "profile/README.org",
148 "--ref", "main", "--file", "-"); code != 0 {
155149 t.Fatalf("org about: %s", errOut)
156150 }
157151 out, _, _ = inst.ssh(t, aliceKey, "", "profile", "show", "alice", "--json")
158152 if !strings.Contains(out, `"about_format":"org"`) {
159 t.Fatalf("about format not stored: %s", out)
153 t.Fatalf("about format not read from the extension: %s", out)
160154 }
161 if _, _, code := inst.ssh(t, aliceKey, "", "profile", "set", "--about-format", "rst"); code != 2 {
162 t.Fatal("unknown about format accepted")
155 if !strings.Contains(out, "tinkerer") {
156 t.Fatalf("about clobbered the description: %s", out)
163157 }
164158 // Org emphasis parsed, not left as literal slashes the way the
165159 // markdown renderer would.
@@ -168,10 +162,15 @@ func TestOwnerProfiles(t *testing.T) {
168162 t.Fatalf("org about not rendered as org: %s", body)
169163 }
170164
171 // Back to markdown for the rest of the checks.
172 if _, _, code := inst.ssh(t, aliceKey, "# Hello\n\nI maintain *small tools*.\n",
173 "profile", "set", "--file", "-", "--about-format", "md"); code != 0 {
174 t.Fatal("markdown about")
165 // Markdown for the rest of the checks: .md wins the resolution order.
166 if _, errOut, code := inst.ssh(t, aliceKey, "# Hello\n\nI maintain *small tools*.\n",
167 "repo", "commit-file", "alice/.gitbay", "profile/README.md",
168 "--ref", "main", "--file", "-"); code != 0 {
169 t.Fatalf("markdown about: %s", errOut)
170 }
171 out, _, _ = inst.ssh(t, aliceKey, "", "profile", "show", "alice", "--json")
172 if !strings.Contains(out, "I maintain *small tools*.") {
173 t.Fatalf("about not read from the repository: %s", out)
175174 }
176175
177176 // Links: free-form, labelled or bare, capped, cleared by an empty one.
@@ -218,16 +217,14 @@ func TestOwnerProfiles(t *testing.T) {
218217 t.Error("repositories render above the activity graph")
219218 }
220219
221 // Clearing works the same way as the other fields.
220 // Clearing works the same way as the other fields. The about is not
221 // among them: it is a file, and it goes the way a file goes.
222222 if _, _, code := inst.ssh(t, aliceKey, "", "profile", "set", "--link", "''"); code != 0 {
223223 t.Fatal("clear links failed")
224224 }
225 if _, _, code := inst.ssh(t, aliceKey, "", "profile", "set", "--about", "''"); code != 0 {
226 t.Fatal("clear about failed")
227 }
228225 out, _, _ = inst.ssh(t, aliceKey, "", "profile", "show", "alice", "--json")
229 if strings.Contains(out, "fosstodon") || strings.Contains(out, "small tools") {
230 t.Fatalf("about or links not cleared: %s", out)
226 if strings.Contains(out, "fosstodon") {
227 t.Fatalf("links not cleared: %s", out)
231228 }
232229 status, body = inst.get(t, "/workshop")
233230 if status != 200 || !strings.Contains(body, "where things get made") ||