Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions components/dashboard/src/data/featureflag-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const featureFlags = {
enable_experimental_jbtb: false,
enabled_configuration_prebuild_full_clone: false,
enterprise_onboarding_enabled: false,
commit_annotation_setting_enabled: false,
};

type FeatureFlags = typeof featureFlags;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type UpdateOrganizationSettingsArgs = Partial<
| "roleRestrictions"
| "maxParallelRunningWorkspaces"
| "onboardingSettings"
| "annotateGitCommits"
>
>;

Expand All @@ -47,6 +48,7 @@ export const useUpdateOrgSettingsMutation = () => {
roleRestrictions,
maxParallelRunningWorkspaces,
onboardingSettings,
annotateGitCommits,
}) => {
const settings = await organizationClient.updateOrganizationSettings({
organizationId: teamId,
Expand All @@ -63,6 +65,7 @@ export const useUpdateOrgSettingsMutation = () => {
updateRoleRestrictions: !!roleRestrictions,
maxParallelRunningWorkspaces,
onboardingSettings,
annotateGitCommits,
});
return settings.settings!;
},
Expand Down
44 changes: 43 additions & 1 deletion components/dashboard/src/teams/TeamSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { InputWithCopy } from "../components/InputWithCopy";
import Modal, { ModalBody, ModalFooter, ModalHeader } from "../components/Modal";
import { InputField } from "../components/forms/InputField";
import { TextInputField } from "../components/forms/TextInputField";
import { Heading2, Heading3, Subheading } from "../components/typography/headings";
import { useIsOwner } from "../data/organizations/members-query";
import { useOrgSettingsQuery } from "../data/organizations/org-settings-query";
import { useCurrentOrg, useOrganizationsInvalidator } from "../data/organizations/orgs-query";
Expand All @@ -32,6 +31,9 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
import { useDocumentTitle } from "../hooks/use-document-title";
import { PlainMessage } from "@bufbuild/protobuf";
import { useToast } from "../components/toasts/Toasts";
import { SwitchInputField } from "@podkit/switch/Switch";
import { Heading2, Heading3, Subheading } from "@podkit/typography/Headings";
import { useFeatureFlag } from "../data/featureflag-query";

export default function TeamSettingsPage() {
useDocumentTitle("Organization Settings - General");
Expand All @@ -47,6 +49,7 @@ export default function TeamSettingsPage() {
const [updated, setUpdated] = useState(false);

const updateOrg = useUpdateOrgMutation();
const isCommitAnnotationEnabled = useFeatureFlag("commit_annotation_setting_enabled");

const close = () => setModal(false);

Expand Down Expand Up @@ -122,6 +125,17 @@ export default function TeamSettingsPage() {
[updateTeamSettings, org?.id, isOwner, settings, toast],
);

const handleUpdateAnnotatedCommits = useCallback(
async (value: boolean) => {
try {
await handleUpdateTeamSettings({ annotateGitCommits: value });
} catch (error) {
console.error(error);
}
},
[handleUpdateTeamSettings],
);

return (
<>
<OrgSettingsPage>
Expand Down Expand Up @@ -207,6 +221,34 @@ export default function TeamSettingsPage() {
/>
</ConfigurationSettingsField>

{isCommitAnnotationEnabled && (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious, how we indicate what kind of settings should be on General and Policies? (I was trying to find this setting on Policies tab before look through the code)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mustard-mh I generally go with my gut here. This time around, I didn't consider this feature a policy, since it's not restricting anything per se, like our other policies we have on that page.

It could definitely be argued that "it's asserting that all of an organization's commits have our Tool: trailer", though, so maybe? What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong opinion there, just curious 😋 Any place is good for me (Once the docs to the user is clear, and for sure you will do it)

<ConfigurationSettingsField>
<Heading3>Insights</Heading3>
<Subheading className="mb-4">
Configure insights into usage of Gitpod in your organization.
</Subheading>

<InputField
label="Annotate git commits"
hint={
<>
Add a <code>Tool:</code> field to all git commit messages created from
workspaces in your organization to associate them with this Gitpod instance.
</>
}
id="annotate-git-commits"
>
<SwitchInputField
id="annotate-git-commits"
checked={settings?.annotateGitCommits || false}
disabled={!isOwner || isLoading}
onCheckedChange={handleUpdateAnnotatedCommits}
label=""
/>
</InputField>
</ConfigurationSettingsField>
)}

{showImageEditModal && (
<OrgDefaultWorkspaceImageModal
settings={settings}
Expand Down
67 changes: 67 additions & 0 deletions components/gitpod-cli/cmd/git-commit-message-helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) 2025 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License.AGPL.txt in the project root for license information.

package cmd

import (
"context"
"fmt"
"os"
"strings"
"time"

"github.com/gitpod-io/gitpod/gitpod-cli/pkg/gitpod"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

var gitCommitMessageHelperOpts struct {
CommitMessageFile string
}

var gitCommitMessageHelper = &cobra.Command{
Use: "git-commit-message-helper",
Short: "Gitpod's Git commit message helper",
Long: "Automatically adds Tool information to Git commit messages",
Args: cobra.ExactArgs(0),
Hidden: true,
RunE: func(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(cmd.Context(), 5*time.Second)
defer cancel()

wsInfo, err := gitpod.GetWSInfo(ctx)
if err != nil {
return err
}

content, err := os.ReadFile(gitCommitMessageHelperOpts.CommitMessageFile)
if err != nil {
log.WithError(err).Fatal("error reading commit message file")
return err
}

toolAttribution := fmt.Sprintf("Tool: gitpod/%s", wsInfo.GitpodApi.Host)

msg := string(content)
if strings.Contains(msg, toolAttribution) {
return nil
}

newMsg := fmt.Sprintf("%s\n\n%s", msg, toolAttribution)

err = os.WriteFile(gitCommitMessageHelperOpts.CommitMessageFile, []byte(newMsg), 0644)
if err != nil {
log.WithError(err).Fatal("error writing commit message file")
return err
}

return nil
},
}

func init() {
rootCmd.AddCommand(gitCommitMessageHelper)
gitCommitMessageHelper.Flags().StringVarP(&gitCommitMessageHelperOpts.CommitMessageFile, "file", "f", "", "Path to the commit message file")
_ = gitCommitMessageHelper.MarkFlagRequired("file")
}
3 changes: 3 additions & 0 deletions components/gitpod-db/src/typeorm/entity/db-team-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ export class DBOrgSettings implements OrganizationSettings {
@Column("json", { nullable: true })
onboardingSettings?: OnboardingSettings | undefined;

@Column({ type: "boolean", default: false })
annotateGitCommits?: boolean | undefined;

@Column()
deleted: boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Copyright (c) 2025 Gitpod GmbH. All rights reserved.
* Licensed under the GNU Affero General Public License (AGPL).
* See License.AGPL.txt in the project root for license information.
*/

import { MigrationInterface, QueryRunner } from "typeorm";
import { columnExists } from "./helper/helper";

const table = "d_b_org_settings";
const newColumn = "annotateGitCommits";

export class AddOrgSettingsCommitAnnotation1736951418625 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
if (!(await columnExists(queryRunner, table, newColumn))) {
await queryRunner.query(`ALTER TABLE ${table} ADD COLUMN ${newColumn} BOOLEAN DEFAULT FALSE`);
}
}

public async down(queryRunner: QueryRunner): Promise<void> {
if (await columnExists(queryRunner, table, newColumn)) {
await queryRunner.query(`ALTER TABLE ${table} DROP COLUMN ${newColumn}`);
}
}
}
1 change: 1 addition & 0 deletions components/gitpod-db/src/typeorm/team-db-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ export class TeamDBImpl extends TransactionalDBImpl<TeamDB> implements TeamDB {
"roleRestrictions",
"maxParallelRunningWorkspaces",
"onboardingSettings",
"annotateGitCommits",
],
});
}
Expand Down
3 changes: 3 additions & 0 deletions components/gitpod-protocol/src/teams-projects-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,9 @@ export interface OrganizationSettings {

// onboarding settings for the organization
onboardingSettings?: OnboardingSettings;

// whether to add a special annotation to commits that are created through Gitpod
annotateGitCommits?: boolean;
}

export type TimeoutSettings = {
Expand Down
4 changes: 4 additions & 0 deletions components/public-api/gitpod/v1/organization.proto
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ message OrganizationSettings {
// max_parallel_running_workspaces is the maximum number of workspaces that a single user can run in parallel. 0 resets to the default, which depends on the org plan
int32 max_parallel_running_workspaces = 9;
OnboardingSettings onboarding_settings = 10;
bool annotate_git_commits = 11;
}

service OrganizationService {
Expand Down Expand Up @@ -193,6 +194,9 @@ message UpdateOrganizationSettingsRequest {

// onboarding_settings are the settings for the organization's onboarding
optional OnboardingSettings onboarding_settings = 16;

// annotate_git_commits specifies whether to annotate git commits created in Gitpod workspaces with the gitpod host
optional bool annotate_git_commits = 17;
}

message UpdateOrganizationSettingsResponse {
Expand Down
Loading
Loading