Improve github manual sync message and docs

This commit is contained in:
Carlos Monastyrski
2025-09-02 12:38:02 -03:00
parent da0d4a31b1
commit be588c2653
11 changed files with 59 additions and 159 deletions

View File

@@ -137,7 +137,6 @@ export const registerGithubOrgSyncRouter = async (server: FastifyZodProvider) =>
schema: {
response: {
200: z.object({
syncedUsersCount: z.number(),
totalUsers: z.number(),
errors: z.array(z.string()),
createdTeams: z.array(z.string()),
@@ -153,7 +152,6 @@ export const registerGithubOrgSyncRouter = async (server: FastifyZodProvider) =>
});
return {
syncedUsersCount: result.syncedUsersCount,
totalUsers: result.totalUsers,
errors: result.errors,
createdTeams: result.createdTeams,

View File

@@ -553,7 +553,6 @@ export const githubOrgSyncServiceFactory = ({
) as OrgMembershipWithUser[];
const startTime = Date.now();
let syncedUsersCount = 0;
const syncErrors: string[] = [];
const octokit = new OctokitWithPlugin({
@@ -683,8 +682,6 @@ export const githubOrgSyncServiceFactory = ({
const updatedTeams = new Set<string>();
const totalRemovedMemberships = 0;
syncedUsersCount = allGithubUsernamesInTeams.size;
await groupDAL.transaction(async (tx) => {
if (teamsToCreate.length > 0) {
const newGroups = await groupDAL.insertMany(
@@ -810,7 +807,6 @@ export const githubOrgSyncServiceFactory = ({
);
return {
syncedUsersCount,
totalUsers: activeMembers.length,
errors: syncErrors,
createdTeams: Array.from(createdTeams),

View File

@@ -27,7 +27,6 @@ export interface TSyncAllTeamsDTO {
}
export interface TSyncResult {
syncedUsersCount: number;
totalUsers: number;
errors: string[];
createdTeams: string[];

View File

@@ -49,17 +49,59 @@ When a user logs in via the GitHub OAuth flow and selects the configured organiz
You can manually synchronize GitHub teams for all organization members who have previously logged in with GitHub. This bulk sync operation updates team memberships without requiring users to log in again.
To trigger a manual sync, click the **Manually sync GitHub teams for all organization members** button:
<Steps>
<Step title="Generate a GitHub Access Token">
To perform manual syncs, you'll need to create a GitHub Personal Access Token with the appropriate permissions. GitHub offers two types of tokens:
![Manual sync](../../images/platform/external-syncs/github-org-sync-manual-sync.png)
<Tabs>
<Tab title="Classic Token">
1. Go to [GitHub Settings → Personal Access Tokens → Tokens (classic)](https://github.com/settings/tokens)
2. Click **Generate new token** → **Generate new token (classic)**
3. Give your token a descriptive name (e.g., "Infisical GitHub Sync")
4. Set an appropriate expiration date
5. Select the **read:org** scope - Required to read organization team information
6. Click **Generate token**
7. Copy the token immediately (you won't be able to see it again)
If you don't have a GitHub token configured or your token has expired, a modal will appear prompting you to provide one:
![Classic Token Creation](../../images/platform/external-syncs/github-classic-token.png)
</Tab>
<Tab title="Fine-grained Token">
1. Go to [GitHub Settings → Personal Access Tokens → Fine-grained tokens](https://github.com/settings/personal-access-tokens/new)
2. Click **Generate new token**
3. Give your token a descriptive name (e.g., "Infisical GitHub Sync")
4. Set an appropriate expiration date
5. Select your organization under **Resource owner**
6. Under **Organization permissions**, set **Members** to **Read**
7. Click **Generate token**
8. Copy the token immediately (you won't be able to see it again)
![token](../../images/platform/external-syncs/github-org-sync-manual-sync-token.png)
![Fine-grained Token Creation](../../images/platform/external-syncs/github-fine-grained-token.png)
</Tab>
</Tabs>
</Step>
<Step title="Configure the Token in Infisical">
1. Navigate to the **Single Sign-On (SSO)** page and select the **Provisioning** tab.
2. Click the **Configure** button next to your GitHub Organization configuration.
3. In the configuration modal, you'll find an optional **GitHub Access Token** field.
4. Paste the token you generated in the previous step.
5. Click **Update** to save the configuration.
<Info>
The token must be granted **read:org** and **read:user** permissions to successfully sync groups. This configuration is only required once - if you already have a valid token configured, the sync will proceed automatically.
</Info>
![Token Configuration Modal](../../images/platform/external-syncs/github-token-config-modal.png)
</Step>
<Step title="Perform Manual Sync">
Once you have configured the GitHub access token:
1. Navigate to the **Single Sign-On (SSO)** page and select the **Provisioning** tab.
2. You'll see a **Sync Now** section with a button to trigger the manual sync.
3. Click **Sync Now** to synchronize GitHub teams for all organization members.
![Manual Sync Button](../../images/platform/external-syncs/github-manual-sync-button.png)
The sync operation will process all organization members who have previously logged in with GitHub and update their team memberships accordingly.
</Step>
</Steps>
## Troubleshooting

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 KiB

View File

@@ -44,7 +44,6 @@ export const useDeleteGithubSyncOrgConfig = () => {
export const useSyncAllGithubTeams = () => {
return useMutation({
mutationFn: async (): Promise<{
syncedUsersCount: number;
totalUsers: number;
errors: string[];
createdTeams: string[];

View File

@@ -51,7 +51,7 @@ export const GithubOrgSyncConfigModal = ({
formState: { isSubmitting }
} = useForm<FormData>({
resolver: zodResolver(schema),
values: data ? { githubOrgName: data.githubOrgName } : undefined
values: data ? { githubOrgName: data.githubOrgName, githubOrgAccessToken: "" } : undefined
});
const onFormSubmit = async ({ githubOrgName, githubOrgAccessToken }: FormData) => {
@@ -123,21 +123,21 @@ export const GithubOrgSyncConfigModal = ({
</FormControl>
)}
/>
{/* <Controller
<Controller
control={control}
name="githubOrgAccessToken"
render={({ field, fieldState: { error } }) => (
<FormControl
label="GitHub Org Scoped Access Token"
label="GitHub Access Token"
isError={Boolean(error)}
isOptional
errorText={error?.message}
helperText="A GitHub access token is required only for private organizations. It will not be visible after saving."
helperText="Required for manual sync operations. The token must have 'read:org' permissions."
>
<Input {...field} placeholder="example" />
<Input {...field} type="password" placeholder="ghp_xxxxxxxxxxxx" />
</FormControl>
)}
/> */}
/>
<div className="flex gap-8 pt-4">
<Button type="submit" isLoading={isSubmitting} isDisabled={isSubmitting}>
{isUpdate ? "Update" : "Configure"}

View File

@@ -1,21 +1,9 @@
import { useState } from "react";
import { faCircleCheck, faCircleXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useQuery } from "@tanstack/react-query";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions";
import {
Button,
FormControl,
Input,
Modal,
ModalContent,
Skeleton,
Spinner,
Switch
} from "@app/components/v2";
import { Button, Modal, ModalContent, Skeleton, Spinner, Switch } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects, useSubscription } from "@app/context";
import {
githubOrgSyncConfigQueryKeys,
@@ -31,22 +19,9 @@ export const OrgGithubSyncSection = () => {
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
"upgradePlan",
"githubOrgSyncConfig",
"deleteGithubOrgSyncConfig",
"setAccessToken"
"deleteGithubOrgSyncConfig"
] as const);
const [accessToken, setAccessToken] = useState("");
const [tokenValidationResult, setTokenValidationResult] = useState<{
valid: boolean;
organizationInfo?: {
id: number;
login: string;
name: string;
publicRepos?: number;
privateRepos?: number;
};
} | null>(null);
const githubOrgSyncConfig = useQuery({
...githubOrgSyncConfigQueryKeys.get(),
enabled: subscription.githubOrgSync,
@@ -62,7 +37,7 @@ export const OrgGithubSyncSection = () => {
const handleBulkSync = async () => {
try {
const result = await syncAllTeamsMutation.mutateAsync();
let message = `Successfully synced teams for ${result.syncedUsersCount} user${result.syncedUsersCount === 1 ? "" : "s"}`;
let message = "Successfully synced teams";
const details = [];
if (result.createdTeams.length > 0) {
@@ -108,9 +83,8 @@ export const OrgGithubSyncSection = () => {
errorMessage.includes("expired") ||
errorMessage.includes("set a token first"))
) {
handlePopUpOpen("setAccessToken");
createNotification({
text: "Please provide a GitHub access token to continue with the sync",
text: "Please set a GitHub access token in the configuration modal to continue with the sync",
type: "error"
});
} else {
@@ -122,41 +96,6 @@ export const OrgGithubSyncSection = () => {
}
};
const handleSetAccessToken = async () => {
if (!accessToken.trim()) {
createNotification({
text: "Please enter a GitHub access token",
type: "error"
});
return;
}
try {
await updateGithubSyncOrgConfig.mutateAsync({
githubOrgAccessToken: accessToken.trim()
});
createNotification({
text: "GitHub access token set successfully. Starting sync...",
type: "success"
});
setAccessToken("");
handlePopUpToggle("setAccessToken", false);
// Automatically trigger sync after token is set
await handleBulkSync();
} catch (error) {
const errorMessage =
(error as any)?.response?.data?.message || (error as Error)?.message || "Unknown error";
createNotification({
text: `Failed to set GitHub access token: ${errorMessage}`,
type: "error"
});
}
};
return (
<div className="mt-4 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-6">
<p className="text-xl font-semibold text-gray-200">
@@ -265,79 +204,6 @@ export const OrgGithubSyncSection = () => {
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text="You can use GitHub Organization Plan if you switch to Infisical's Enterprise plan."
/>
<Modal
isOpen={popUp?.setAccessToken?.isOpen}
onOpenChange={(isOpen) => {
handlePopUpToggle("setAccessToken", isOpen);
if (!isOpen) {
setAccessToken("");
setTokenValidationResult(null);
}
}}
>
<ModalContent
title="GitHub Access Token Required"
subTitle="Provide a GitHub access token to sync teams from your GitHub organization"
>
<div className="space-y-4">
<FormControl
label="GitHub Access Token"
tooltipText="The provided token must be granted read:org and read:user permissions in order to successfully sync groups"
tooltipClassName="max-w-md"
>
<div className="relative">
<Input
type="password"
placeholder="ghp_xxxxxxxxxxxx"
value={accessToken}
onChange={(e) => {
setAccessToken(e.target.value);
if (tokenValidationResult) {
setTokenValidationResult(null);
}
}}
autoComplete="off"
/>
{tokenValidationResult && (
<div className="absolute right-3 top-1/2 -translate-y-1/2">
{tokenValidationResult.valid ? (
<FontAwesomeIcon icon={faCircleCheck} size="xs" className="text-green-500" />
) : (
<FontAwesomeIcon icon={faCircleXmark} size="xs" className="text-red-500" />
)}
</div>
)}
</div>
</FormControl>
<div className="flex justify-between">
<div className="flex space-x-2">
<Button
colorSchema="secondary"
onClick={() => {
handlePopUpToggle("setAccessToken", false);
setAccessToken("");
setTokenValidationResult(null);
}}
>
Cancel
</Button>
<Button
colorSchema="primary"
onClick={handleSetAccessToken}
isLoading={updateGithubSyncOrgConfig.isPending || syncAllTeamsMutation.isPending}
isDisabled={
!accessToken.trim() ||
updateGithubSyncOrgConfig.isPending ||
syncAllTeamsMutation.isPending
}
>
Set Token & Sync
</Button>
</div>
</div>
</div>
</ModalContent>
</Modal>
</div>
);
};