Skip to content

Commit

Workspace settings promise only what exists: the Soon rows Deployments, Project defaults, Spaces and sharing and Applications are gone from Workspace mode's sidebar, with the Artifacts group they left empty, and the sidebar no longer draws Soon rows at all. None had a plan behind it. Permissions loses its five locked Soon settings (forking private repositories, who can create spaces, default sharing, who can deploy to production, who can publish packages), so every setting on it saves. The permissions and workspaces guides say the same. Checked: the site's typecheck, its tests and a production build, the deploy tool's tests and the docs build.

syntaqxcommitted Parent05ac671Browse files
6 files+26−2020/6 viewed
+2−25
11 ---
22 title: Permissions
3−description: Who may do what in a workspace, in one place, part by part — repositories, teams, chat, artifacts, deployments and packages — what each setting does, and which are still to come.
3+description: Who may do what in a workspace, in one place, part by part — repositories, teams and chat — and what each setting does.
44 ---
5−
6−import Soon from '../../../components/Soon.astro';
75
86 **Permissions** is where a workspace's owners decide who may do what, part
97 by part: `g1t.sh/<workspace>/-/permissions`, under **Workspace → Access**
119 setting says what it decides, and each card saves on its own with its
1210 **Save** button; the change holds on everyone's next request and is
1311 recorded in the [audit log](/guides/audit-log/).
14−
15−A setting marked **Soon** is not configurable yet. It is shown the way it
16−will be, locked, with the rule that holds today under it, so nobody has to
17−look elsewhere to learn what members can do.
1812
1913 ## Code
2014
2620 | **Repository admins can change visibility** | Whether a member with Admin on a repository can make it public or private. | On |
2721 | **Repository admins can delete and transfer repositories** | Whether a member with Admin on a repository can delete it, or transfer it to a workspace where they can create one. | Off |
2822 | **Repository admins can add outside collaborators** | Whether a member with Admin on a repository can give a role on it to someone outside the workspace. | On |
29−| **Members can fork private repositories** <Soon /> | Whether a member may fork a private repository under their own name. | g1t has no personal forks: work happens on branches of the repository itself. |
3023
3124 The five switches are the workspace's
3225 [member privileges](/guides/workspaces/#member-privileges), the same ones
5043 [Chat settings](/guides/chat/#workspace-chat-settings), under **Workspace →
5144 Chat**: they are about what a channel's own members may do, not who may do
5245 what in the workspace.
53−
54−## Artifacts
5546
56−| Setting | What it decides | Today |
57−| --- | --- | --- |
58−| **Who can create spaces** <Soon /> | Who may add a space beside the ones the workspace has. | Any member, from **+** beside **Spaces** in the Artifacts sidebar. |
59−| **Default sharing** <Soon /> | Who a new artifact is shared with before its author chooses. | A new artifact is its author's, shared with the people and teams they add, or opened to the workspace from its **Share** menu. See [sharing](/guides/artifacts/#sharing). |
60−
61−## Deployments and packages
62−
63−| Setting | What it decides | Today |
64−| --- | --- | --- |
65−| **Who can deploy to production** <Soon /> | Who may deploy a project's production environment, and redeploy or take it down. | Anyone with Write on the project's repository redeploys or takes an app down; turning deployments on for a project takes Admin. See [deployments](/guides/deployments/). |
66−| **Who can publish packages** <Soon /> | Who may publish a new version of one of the workspace's packages. | Anyone with Write on the package's repository, or a token given write on packages. See [packages](/guides/packages/). |
67−
6847 ## Through the API
6948
70−Every setting that exists is set the way it was before it had this page:
49+Every setting is set the way it was before it had this page:
7150
7251 | Setting | Route | MCP |
7352 | --- | --- | --- |
7453 | Base permission | [`PUT /workspaces/{workspace}/base_permission`](/reference/api/access/set-base-permission/) | `access` `set_base_permission` |
7554 | Member privileges, who can create teams | [`PATCH /workspaces/{workspace}`](/reference/api/workspaces/update-workspace/) with `members_can_…` or `team_creation` | `workspace` `update` |
7655 | Public and private channels | The chat service's workspace settings, as [Chat settings](/guides/chat/#workspace-chat-settings) saves them | |
77−
78−A setting marked Soon has no route yet.
7956
8057 ## Next
8158
+2−6
722722 for it. There is no second Settings inside it: every page is in the
723723 sidebar, under the group it belongs to, and the group you are in stays
724724 open while the others fold when you select their name. A page only owners
725−open is left out for members; a page that is coming is a greyed **Soon**
726−row, with a hint saying what it will hold.
725+open is left out for members.
727726
728727 | Group | Page | Who | |
729728 | --- | --- | --- | --- |
731730 | **General** | **General** | Owners | The icon, the display name, a one-line description, the address (the slug), [requiring two-factor authentication](#require-two-factor-authentication), [data residency](#data-residency), and deleting the workspace. `-/settings` |
732731 | **Access** | **Members** | Members | Who is in the workspace and their roles, with search and filters; owners [invite people](#add-people), change roles, and see **Outside collaborators**. `-/members` |
733732 | | **Invitations** | Owners | Every invitation to join, filtered by where it stands; send one again, revoke it, copy its link. `-/members/invitations` |
734−| | [**Permissions**](/guides/permissions/) | Owners | Who may do what, part by part: the base permission, repositories, teams, chat, artifacts, deployments and packages. `-/permissions` |
733+| | [**Permissions**](/guides/permissions/) | Owners | Who may do what, part by part: the base permission, repositories, teams and chat. `-/permissions` |
735734 | | [**Access tokens**](#workspace-access-tokens) | Members | The workspace's own tokens. Owners create and delete them. `-/tokens` |
736735 | | **Personal access tokens** | Owners | The workspace's rules for members' tokens, and approving them. `-/personal-access-tokens` |
737736 | **Money** | [**Usage**](/guides/usage-and-billing/#the-usage-page) | Members | What the workspace used, by product, project and day. `-/usage` |
741740 | | **Actions** | Members | How workflows run in the workspace: what jobs' tokens may do, and for owners the [workflows the workspace manages](#workflows-the-workspace-manages) in its repositories. `-/actions` |
742741 | | [**Secrets and variables**](/guides/secrets-and-variables/) | Members | What runs and deployments are given. Owners change them. `-/secrets` |
743742 | | [**Webhooks**](/guides/webhooks/) | Members | Where the workspace's events are sent. Owners add and change them. `-/webhooks` |
744−| | **Deployments**, **Project defaults** | | Soon: defaults for every project's deployments, and what a new project starts with. |
745743 | **Chat** | [**Chat settings**](/guides/chat/#workspace-chat-settings) | Members | Renaming and archiving channels, emoji, default channels. Owners change it. `-/settings/chat` |
746744 | | **Emoji** | Members | The workspace's own emoji. `-/emoji` |
747−| **Artifacts** | **Spaces and sharing** | | Soon: which spaces a workspace starts with, and how a new artifact is shared by default. |
748745 | **Security** | **Security settings** | Members | The workspace's security policies. `-/security/settings` |
749746 | | [**Guardrails**](/guides/guardrails/) | Members | How long checks, the merge queue and merge checks may run. `-/guardrails` |
750747 | | [**Rules**](/guides/rules/) | Members | Rulesets across projects. `-/rules` |
751748 | | [**Audit log**](/guides/audit-log/) | Members | Every change, by whom, to what. `-/audit` |
752749 | **Integrations** | [**Integrations**](/guides/integrations/) | Members | The tools connected for everyone. `-/integrations` |
753−| | **Applications** | | Soon: OAuth applications the workspace registers. |
754750
755751 Each page's address is under `g1t.sh/<workspace>/`. The page's header
756752 shows where you are: **General**, **Members / Invitations**, **Permissions**.
+13−45
202202 }
203203
204204 /**
205− * Something a project will have, shown where it will live so people can
206− * see where g1t is going. Not a link: nothing is behind it yet.
207− */
208−function SidebarSoon({ icon, children, about }: { icon: ReactNode; children: ReactNode; about: string }) {
209− return (
210− <SidebarMenuItem>
211− <Hint label={about} side="right">
212− <SidebarMenuButton asChild variant="faint">
213− <div aria-disabled="true" className="cursor-default">
214− {icon}
215− <span className="min-w-0 grow truncate">{children}</span>
216− <span className="sr-only">{about}</span>
217− <SoonPill />
218− </div>
219− </SidebarMenuButton>
220− </Hint>
221− </SidebarMenuItem>
222− );
223−}
224−
225−/**
226205 * A page that is coming, as a link to its roadmap page: faint, with Soon,
227206 * and marked current on any of its section's Soon pages.
228207 */
13251304 actions: <PlayCircle size={15} />,
13261305 secrets: <Lock size={15} />,
13271306 webhooks: <Webhook size={15} />,
1328− deployments: <Rocket size={15} />,
1329− projects: <Layers size={15} />,
13301307 "chat-settings": <MessagesSquare size={15} />,
13311308 emoji: <Smile size={15} />,
1332− "artifact-settings": <Shapes size={15} />,
13331309 "security-settings": <Shield size={15} />,
13341310 guardrails: <Gauge size={15} />,
13351311 rules: <Scale size={15} />,
13361312 audit: <History size={15} />,
13371313 integrations: <Plug size={15} />,
1338− applications: <Blocks size={15} />,
13391314 };
13401315
13411316 /**
13701345 /**
13711346 * The Workspace mode's sidebar: the workspace's settings area, every page
13721347 * one click away. Its overview, then the groups in lib/workspace-nav.ts
1373− * (General, Access, Money, Compute, Code, Chat, Artifacts,
1374− * Security, Integrations), each folding, the one you are in held open.
1375− * Pages only owners open are left out for members, and a group with none
1376− * left goes with them; a page that is coming is a Soon row, not a link.
1348+ * (General, Access, Money, Compute, Code, Chat, Security, Integrations),
1349+ * each folding, the one you are in held open. Pages only owners open are
1350+ * left out for members, and a group with none left goes with them.
13771351 */
13781352 export function WorkspaceSidebar({ slug, owner }: { slug: string; owner: boolean }) {
13791353 const { pathname } = useLocation();
13951369 <Fragment key={group.key}>
13961370 <Rule />
13971371 <FoldingGroup title={group.title} current={here?.group === group.key}>
1398− {group.pages.filter(shown).map((page) =>
1399− page.soon ? (
1400− <SidebarSoon key={page.key} icon={WORKSPACE_PAGE_ICONS[page.key] ?? <Sparkles size={15} />} about={page.soon}>
1401− {page.label}
1402− </SidebarSoon>
1403− ) : (
1404− <SidebarLink
1405− key={page.key}
1406− to={`/${slug}/-/${page.path}`}
1407− icon={WORKSPACE_PAGE_ICONS[page.key] ?? <Settings size={15} />}
1408− current={here?.page === page.key}
1409− >
1410− {page.label}
1411− </SidebarLink>
1412− ),
1413− )}
1372+ {group.pages.filter(shown).map((page) => (
1373+ <SidebarLink
1374+ key={page.key}
1375+ to={`/${slug}/-/${page.path}`}
1376+ icon={WORKSPACE_PAGE_ICONS[page.key] ?? <Settings size={15} />}
1377+ current={here?.page === page.key}
1378+ >
1379+ {page.label}
1380+ </SidebarLink>
1381+ ))}
14141382 </FoldingGroup>
14151383 </Fragment>
14161384 ))}
+1−1
142142 for (const page of group.pages) {
143143 assert.ok(!keys.has(page.key), `${page.key} twice`);
144144 keys.add(page.key);
145− if (!page.soon) assert.equal(modeOf(`/acme/-/${page.path}`, "acme"), "workspace", page.path);
145+ assert.equal(modeOf(`/acme/-/${page.path}`, "acme"), "workspace", page.path);
146146 }
147147 }
148148 });
+3−37
2828 /**
2929 * Workspace mode's pages, in the groups its sidebar lists them under: the
3030 * workspace's settings area, every page one click away. `owner` marks the
31− * pages only owners open; the sidebar shows members the rest. A page that
32− * is not here yet is a `soon` row: what the group will hold, not a link.
31+ * pages only owners open; the sidebar shows members the rest.
3332 * `path` is under `-/`.
3433 */
35−export type WorkspaceSettingsPage = { key: string; label: string; path: string; owner?: boolean; soon?: string };
34+export type WorkspaceSettingsPage = { key: string; label: string; path: string; owner?: boolean };
3635
3736 export type WorkspaceSettingsGroup = { key: string; title: string; pages: WorkspaceSettingsPage[] };
3837
7473 { key: "actions", label: "Actions", path: "actions" },
7574 { key: "secrets", label: "Secrets and variables", path: "secrets" },
7675 { key: "webhooks", label: "Webhooks", path: "webhooks" },
77− {
78− key: "deployments",
79− label: "Deployments",
80− path: "soon/deployments",
81− soon: "Defaults for every project's deployments: environments, who approves production, and the domains the workspace owns.",
82− },
83− {
84− key: "projects",
85− label: "Project defaults",
86− path: "soon/projects",
87− soon: "What a new project starts with: its visibility, branch rules and merge method.",
88− },
8976 ],
9077 },
9178 {
9481 pages: [
9582 { key: "chat-settings", label: "Chat settings", path: "settings/chat" },
9683 { key: "emoji", label: "Emoji", path: "emoji" },
97− ],
98− },
99− {
100− key: "artifacts",
101− title: "Artifacts",
102− pages: [
103− {
104− key: "artifact-settings",
105− label: "Spaces and sharing",
106− path: "soon/artifact-settings",
107− soon: "Which spaces a workspace starts with, and how a new artifact is shared by default.",
108− },
10984 ],
11085 },
11186 {
12196 {
12297 key: "integrations",
12398 title: "Integrations",
124− pages: [
125− { key: "integrations", label: "Integrations", path: "integrations" },
126− {
127− key: "applications",
128− label: "Applications",
129− path: "soon/applications",
130− soon: "OAuth applications the workspace registers, and the ones it lets its members authorize.",
131− },
132− ],
99+ pages: [{ key: "integrations", label: "Integrations", path: "integrations" }],
133100 },
134101 ];
135102
147114 let found: { group: string; page: string; depth: number } | null = null;
148115 for (const group of WORKSPACE_SETTINGS_GROUPS) {
149116 for (const page of group.pages) {
150− if (page.soon) continue;
151117 if (rest === page.path || rest.startsWith(`${page.path}/`)) {
152118 const depth = page.path.split("/").length;
153119 if (!found || depth > found.depth) found = { group: group.key, page: page.key, depth };
+5−88
1−import { Code2, MessagesSquare, Rocket, Shapes, UsersRound } from "lucide-react";
1+import { Code2, MessagesSquare, UsersRound } from "lucide-react";
22 import { type ReactNode, useState } from "react";
33 import { Form, Link, data, useNavigation } from "react-router";
44
1717 import type { Route } from "./+types/permissions";
1818 import { page } from "../../lib/meta";
1919 import { ErrorText, SubmitButton } from "../../components/ui";
20−import { Badge } from "../../components/ui/badge";
2120 import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../../components/ui/card";
2221 import { RadioGroup, RadioOption } from "../../components/ui/radio-group";
2322 import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../components/ui/select";
3231 /**
3332 * Permissions: who may do what in the workspace, in one place, a section
3433 * for each part of g1t. What exists today saves through the service that
35− * owns it (identity for repositories and teams, chat for channels); what
36− * does not yet is shown as it will be, marked
37− * Soon, with the rule that holds today. Owners only.
34+ * owns it (identity for repositories and teams, chat for channels).
35+ * Owners only.
3836 */
3937 export async function loader({ params, context }: Route.LoaderArgs) {
4038 const viewer = getViewer(context);
160158 }
161159
162160 /** A setting's heading inside a part, and a line on what it decides. */
163−function Setting({ title, about, soon, children }: { title: string; about: ReactNode; soon?: boolean; children: ReactNode }) {
161+function Setting({ title, about, children }: { title: string; about: ReactNode; children: ReactNode }) {
164162 return (
165163 <div>
166− <h3 className="flex items-center gap-2 text-sm font-medium">
167− {title}
168− {soon && <Badge>Soon</Badge>}
169− </h3>
164+ <h3 className="text-sm font-medium">{title}</h3>
170165 <p className="mt-1 text-xs leading-relaxed text-faint">{about}</p>
171166 <div className="mt-3">{children}</div>
172167 </div>
192187 );
193188 }
194189
195−/**
196− * A setting g1t will have but does not yet: the control as it will be,
197− * off, with the rule that holds today, so nobody looks for it elsewhere.
198− */
199−function Stub({ title, about, today, control }: { title: string; about: string; today: string; control: ReactNode }) {
200− return (
201− <Setting title={title} about={about} soon>
202− <div aria-disabled="true" className="opacity-70">
203− {control}
204− </div>
205− <p className="mt-2 text-xs text-muted">
206− <span className="font-medium text-fg-soft">Not configurable yet; today:</span> {today}
207− </p>
208− </Setting>
209− );
210−}
211−
212−/** A who-can choice as a stub shows it: the rule that holds, chosen and locked. */
213−function LockedChoice({ name, options, value }: { name: string; options: { value: string; label: string; description?: string }[]; value: string }) {
214− return (
215− <RadioGroup name={name} value={value} disabled aria-label={name.replace(/_/g, " ")} className="grid gap-2 sm:grid-cols-2">
216− {options.map((option) => (
217− <RadioOption key={option.value} value={option.value} label={option.label} description={option.description} disabled />
218− ))}
219− </RadioGroup>
220− );
221−}
222−
223−const MEMBERS_OR_OWNERS = [
224− { value: "members", label: "Any member", description: "The default." },
225− { value: "owners", label: "Owners only" },
226−];
227−
228190 export default function WorkspacePermissions({ loaderData, actionData, params }: Route.ComponentProps) {
229191 const { slug, base, privileges, teamCreation, channels } = loaderData;
230192 return (
232194 <Part icon={<Code2 />} title="Code" about="What members may do with the workspace's repositories, beyond the role each repository gives them. Owners always can do all of it.">
233195 <BasePermissionSetting key={base} base={base} slug={params.owner} answer={actionData} />
234196 <PrivilegesSetting key={JSON.stringify(privileges)} privileges={privileges} answer={actionData} />
235− <Stub
236− title="Members can fork private repositories"
237− about="Whether a member may make a fork of a private repository under their own name."
238− today="g1t has no personal forks, so nothing is forked; work happens on branches of the repository itself."
239− control={<SwitchCard title="Members can fork private repositories" checked={false} disabled />}
240− />
241197 </Part>
242198
243199 <Part icon={<UsersRound />} title="People and teams" about="Who may shape the workspace's teams.">
252208 <p className="text-sm text-muted">Chat didn't answer. Reload to try again.</p>
253209 </Setting>
254210 )}
255− </Part>
256−
257− <Part icon={<Shapes />} title="Artifacts" about="Who may make the places documents live, and how a new one is shared.">
258− <Stub
259− title="Who can create spaces"
260− about="Who may add a space beside the ones the workspace has."
261− today="any member can create a space, from + beside Spaces in the Artifacts sidebar."
262− control={<LockedChoice name="create_spaces" options={MEMBERS_OR_OWNERS} value="members" />}
263− />
264− <Stub
265− title="Default sharing"
266− about="Who a new artifact is shared with before its author chooses."
267− today="a new artifact is its author's, shared with the people and teams they add, or opened to the workspace from its Share menu."
268− control={
269− <LockedChoice
270− name="default_sharing"
271− options={[
272− { value: "author", label: "Its author", description: "Shared only with whom they add." },
273− { value: "workspace", label: "Everyone in the workspace" },
274− ]}
275− value="author"
276− />
277− }
278− />
279− </Part>
280−
281− <Part icon={<Rocket />} title="Deployments and packages" about="Who may put code in front of people, and publish what others install.">
282− <Stub
283− title="Who can deploy to production"
284− about="Who may deploy a project's production environment, and redeploy or take it down."
285− today="anyone with Write on the project's repository redeploys or takes an app down; turning deployments on for a project takes Admin."
286− control={<LockedChoice name="deploy_production" options={[{ value: "write", label: "Write on the repository", description: "The rule today." }, { value: "admin", label: "Admin on the repository" }, { value: "owners", label: "Owners only" }]} value="write" />}
287− />
288− <Stub
289− title="Who can publish packages"
290− about="Who may publish a new version of one of the workspace's packages."
291− today="anyone with Write on the package's repository, or a token given write on packages, publishes new versions and tags."
292− control={<LockedChoice name="publish_packages" options={[{ value: "write", label: "Write on the repository", description: "The rule today." }, { value: "admin", label: "Admin on the repository" }, { value: "owners", label: "Owners only" }]} value="write" />}
293− />
294211 </Part>
295212
296213 <p className="text-xs text-faint">