Skip to content

fix: Gradual rollout starts at approval date if approval gate on environment#292

Merged
adityachoudhari26 merged 6 commits intomainfrom
grad-rollouts-stuff
Jan 27, 2025
Merged

fix: Gradual rollout starts at approval date if approval gate on environment#292
adityachoudhari26 merged 6 commits intomainfrom
grad-rollouts-stuff

Conversation

@adityachoudhari26
Copy link
Copy Markdown
Member

@adityachoudhari26 adityachoudhari26 commented Jan 24, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a timestamp field to track when environment policy approvals are granted
  • Database Changes

    • Added approved_at column to environment_policy_approval table
  • Improvements

    • Enhanced rollout policy logic to handle approval statuses more precisely
    • Updated UI components to display more detailed approval state information
  • Code Cleanup

    • Removed deprecated gradual rollout module and related exports

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 24, 2025

Walkthrough

This pull request introduces enhancements to the environment policy approval process by adding an approvedAt timestamp field to track when approvals are granted. The changes span multiple files across different packages, including database schema updates, job dispatch policy modifications, and frontend components. The primary focus is on improving the tracking and management of approval statuses for release job triggers, with updates to how rollout windows are calculated and displayed.

Changes

File Change Summary
packages/db/src/schema/environment.ts Added approvedAt field to environment_policy_approval table
packages/job-dispatch/src/gradual-rollout.ts File deleted, removing existing rollout-related functions
packages/job-dispatch/src/policies/gradual-rollout.ts Reimplemented rollout functions with new hashing and time window calculations
packages/job-dispatch/src/index.ts Removed export for deleted gradual-rollout.js module
apps/webservice/src/app/.../FlowPolicyNode.tsx Updated GradualRolloutCheck component to handle approval status
packages/api/src/router/environment-approval.ts Added approvedAt timestamp when approving a policy
packages/db/drizzle/0064_misty_stryfe.sql Database migration to add approved_at column
packages/db/drizzle/meta/_journal.json Updated journal entry for the migration

Sequence Diagram

sequenceDiagram
    participant User
    participant ApprovalSystem
    participant JobDispatch
    participant Database

    User->>ApprovalSystem: Request Release
    ApprovalSystem->>Database: Check Approval Policy
    Database-->>ApprovalSystem: Return Policy Details
    ApprovalSystem->>ApprovalSystem: Calculate Rollout Window
    ApprovalSystem->>Database: Record Approval
    Database->>Database: Set approvedAt Timestamp
    JobDispatch->>Database: Check Approval Status
    JobDispatch->>JobDispatch: Determine Job Trigger Eligibility
Loading

Possibly related PRs

Suggested reviewers

  • jsbroks

Poem

🐰 A Rollout's Tale of Timing Fine

Timestamps dance, approvals align,
With murmurhash and windows bright,
Our rabbit code takes careful flight!
Tracking moments, release's design,
Gradual rollout now feels divine! 🚀

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/job-dispatch/src/policies/gradual-rollout.ts (1)

21-25: Validate distribution approach in isReleaseJobTriggerInRolloutWindow.
The condition (murmurhash.v3(session, 11) % 100) < timeWindowPercent(...) effectively gates rollouts. Confirm that the 0–99 range is broad enough to achieve the desired distribution granularity.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 086b29a and 2d7bc87.

📒 Files selected for processing (3)
  • packages/db/src/schema/environment.ts (1 hunks)
  • packages/job-dispatch/src/gradual-rollout.ts (0 hunks)
  • packages/job-dispatch/src/policies/gradual-rollout.ts (3 hunks)
💤 Files with no reviewable changes (1)
  • packages/job-dispatch/src/gradual-rollout.ts
🧰 Additional context used
📓 Path-based instructions (2)
packages/db/src/schema/environment.ts (1)

Pattern **/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.

packages/job-dispatch/src/policies/gradual-rollout.ts (1)

Pattern **/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.

⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: Typecheck
  • GitHub Check: build (linux/amd64)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: Lint
🔇 Additional comments (8)
packages/job-dispatch/src/policies/gradual-rollout.ts (7)

1-3: Ensure hashing collisions are acceptable.
Using murmurhash is a practical choice. However, confirm that potential collisions—and thus unanticipated rollout overlaps—are acceptable for your use case.


4-8: Imports look fine.
No concerns here; the imports from @ctrlplane/db and schema references are consistent with the rest of the file.


9-19: Double-check edge cases in timeWindowPercent.
The logic comprehensively covers zero or negative durations, future start dates, and durations already passed. Ensure test coverage for each condition, including very large durations that might yield floating-point edge cases.


27-34: Confirm partial rollout date calculations.
getRolloutDateForReleaseJobTrigger offsets startDate by a fraction of duration based on the hash. Consider clamping or handling extremely large durations, or negative durations if that’s possible upstream.


66-75: Validate join performance.
Using .leftJoin on environmentPolicyApproval is appropriate for optional approvals. Confirm that indexes exist on (policyId, releaseId) to maintain efficient queries at scale.


86-96: Automatic approval logic is consistent.
When approvalRequirement is "automatic", using p.release.createdAt for rollout timing aligns with the new policy. Ensure the code handles short-living releases (where rolloutDuration is small).


97-106: Manual approval logic is synchronized with approvedAt.
This conditional rightly checks status === "approved" && approvedAt != null before applying the rollout window. Ensure that concurrency or unexpected delays in updating approvedAt do not mismatch real-time rollout triggers.

packages/db/src/schema/environment.ts (1)

244-247: New approvedAt timestamp field is appropriate.
Storing the approval timestamp is beneficial for auditing. The default NULL is consistent for unapproved rows. Confirm that time zone handling aligns with the rest of the schema.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (10)
apps/webservice/src/app/[workspaceSlug]/(app)/_components/environment-policy-drawer/ApprovalAndGovernance.tsx (1)

53-68: LGTM with a suggestion for error handling.

The checkbox implementation is clean, accessible, and follows React best practices. The state management and event handling are well-structured.

Consider adding error handling to the Promise chain:

 onCheckedChange={(value) => {
   updatePolicy
     .mutateAsync({
       id,
       data: { approvalRequirement: value ? "manual" : "automatic" },
     })
-    .then(invalidatePolicy);
+    .then(invalidatePolicy)
+    .catch((error) => {
+      // Handle or report error
+      console.error('Failed to update approval requirement:', error);
+    });
 }}
apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/ApprovalCheck.tsx (4)

61-83: Consider adding error handling for the policy query.

The loading state is handled well, but there's no handling for potential query errors. Consider adding error feedback to improve user experience.

          {policyQ.isLoading && (
            <AlertDialogDescription className="flex items-center justify-center">
              <IconLoader2 className="animate-spin" />
            </AlertDialogDescription>
          )}
+         {policyQ.error && (
+           <AlertDialogDescription className="text-red-500">
+             Failed to load environments. Please try again.
+           </AlertDialogDescription>
+         )}
          {!policyQ.isLoading && (

32-37: Consider moving invalidation logic to a custom hook.

The invalidation logic could be reused across components. Consider extracting it to a custom hook:

+// Create a new hook: useApprovalInvalidation.ts
+export const useApprovalInvalidation = (policyId: string, releaseId: string) => {
+  const utils = api.useUtils();
+  return () => utils.environment.policy.approval.statusByReleasePolicyId.invalidate({
+    policyId,
+    releaseId,
+  });
+};

// In ApprovalCheck.tsx
-  const utils = api.useUtils();
-  const invalidateApproval = () =>
-    utils.environment.policy.approval.statusByReleasePolicyId.invalidate({
-      policyId,
-      releaseId: release.id,
-    });
+  const invalidateApproval = useApprovalInvalidation(policyId, release.id);

61-65: Consider using a shared loading spinner component.

The loading spinner could be reused across the application. Consider extracting it to a shared component:

+// Create a new component: LoadingSpinner.tsx
+export const LoadingSpinner: React.FC = () => (
+  <IconLoader2 className="animate-spin" />
+);

// In ApprovalCheck.tsx
-            <AlertDialogDescription className="flex items-center justify-center">
-              <IconLoader2 className="animate-spin" />
-            </AlertDialogDescription>
+            <AlertDialogDescription className="flex items-center justify-center">
+              <LoadingSpinner />
+            </AlertDialogDescription>

71-79: Consider extracting environment badge to a reusable component.

The environment badge could be reused across the application. Consider extracting it to a shared component:

+// Create a new component: EnvironmentBadge.tsx
+export const EnvironmentBadge: React.FC<{ name: string }> = ({ name }) => (
+  <Badge variant="secondary" className="max-w-40">
+    <span className="truncate">{name}</span>
+  </Badge>
+);

// In ApprovalCheck.tsx
-                  {policyQ.data?.environments.map((env) => (
-                    <Badge
-                      key={env.id}
-                      variant="secondary"
-                      className="max-w-40"
-                    >
-                      <span className="truncate">{env.name}</span>
-                    </Badge>
-                  ))}
+                  {policyQ.data?.environments.map((env) => (
+                    <EnvironmentBadge key={env.id} name={env.name} />
+                  ))}
apps/webservice/src/app/api/v1/releases/[releaseId]/openapi.ts (1)

34-37: Consider defining a more specific schema for jobAgentConfig.

The current schema allows any object structure. Consider defining specific properties and their types to ensure data consistency and provide better documentation.

   jobAgentConfig: {
     type: "object",
-    additionalProperties: true,
+    properties: {
+      // Define specific properties here
+      // Example:
+      // timeout: { type: "number" },
+      // retries: { type: "number" },
+    },
+    additionalProperties: false,
   },
openapi.v1.json (2)

2581-2584: Add property description for better API documentation.

The jobAgentConfig property lacks a description explaining its purpose and expected content.

Add a description to improve API documentation:

   "jobAgentConfig": {
     "type": "object",
+    "description": "Configuration settings for the job agent that will process this release",
     "additionalProperties": true
   },

2581-2584: Consider adding schema validation for jobAgentConfig properties.

The jobAgentConfig is defined with additionalProperties: true without any property constraints. This could lead to inconsistent data and make it harder to maintain backward compatibility.

Consider defining a more specific schema:

   "jobAgentConfig": {
     "type": "object",
+    "properties": {
+      "type": {
+        "type": "string",
+        "description": "Type of job agent to use"
+      },
+      "version": {
+        "type": "string",
+        "description": "Version of job agent configuration schema"
+      },
+      "settings": {
+        "type": "object",
+        "description": "Job agent specific settings",
+        "additionalProperties": true
+      }
+    },
+    "required": ["type", "version"],
     "additionalProperties": true
   },

Also applies to: 1218-1221, 1286-1289

apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/ReleaseEnvironmentCell.tsx (2)

95-97: Consider combining loading states and conditions for better maintainability.

The loading states and conditions are spread across multiple lines. Consider combining them:

-  const isLoading =
-    isWorkspaceLoading ||
-    isStatusesLoading ||
-    isResourcesLoading ||
-    isBlockedEnvsLoading ||
-    isApprovalLoading;
-
-  if (isLoading)
-    return <p className="text-xs text-muted-foreground">Loading...</p>;
-
-  const hasResources = total > 0;
-  const isAlreadyDeployed = statuses != null && statuses.length > 0;
-
-  const hasJobAgent = deployment.jobAgentId != null;
-  const isBlockedByReleaseChannel = blockedEnv != null;
-
-  const isPendingApproval = approval?.status === "pending";
-
-  const showBlockedByReleaseChannel =
-    isBlockedByReleaseChannel &&
-    !statuses?.some((s) => s.job.status === JobStatus.InProgress);
-
-  const showRelease =
-    isAlreadyDeployed && !showBlockedByReleaseChannel && !isPendingApproval;
+  const states = {
+    isLoading: isWorkspaceLoading || isStatusesLoading || isResourcesLoading || 
+               isBlockedEnvsLoading || isApprovalLoading,
+    hasResources: total > 0,
+    isAlreadyDeployed: statuses != null && statuses.length > 0,
+    hasJobAgent: deployment.jobAgentId != null,
+    isBlockedByReleaseChannel: blockedEnv != null,
+    isPendingApproval: approval?.status === "pending",
+  };
+
+  if (states.isLoading)
+    return <p className="text-xs text-muted-foreground">Loading...</p>;
+
+  const showBlockedByReleaseChannel =
+    states.isBlockedByReleaseChannel &&
+    !statuses?.some((s) => s.job.status === JobStatus.InProgress);
+
+  const showRelease =
+    states.isAlreadyDeployed && !showBlockedByReleaseChannel && !states.isPendingApproval;

Also applies to: 107-108, 113-114


174-185: Consider extracting button styles to a shared component.

The button styling could be reused across the application. Consider extracting it to a shared component:

+// Create a new component: PendingApprovalButton.tsx
+export const PendingApprovalButton: React.FC<ButtonProps> = (props) => (
+  <Button
+    className="w-full border-dashed border-neutral-800/50 bg-transparent text-center 
+              text-neutral-800 hover:border-blue-400 hover:bg-transparent hover:text-blue-400"
+    variant="outline"
+    size="sm"
+    {...props}
+  />
+);

// In ReleaseEnvironmentCell.tsx
-        <Button
-          className="w-full border-dashed border-neutral-800/50 bg-transparent text-center 
-                    text-neutral-800 hover:border-blue-400 hover:bg-transparent hover:text-blue-400"
-          variant="outline"
-          size="sm"
-        >
-          Pending approval
-        </Button>
+        <PendingApprovalButton>
+          Pending approval
+        </PendingApprovalButton>
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d7bc87 and d2ae812.

📒 Files selected for processing (12)
  • apps/webservice/src/app/[workspaceSlug]/(app)/_components/environment-policy-drawer/ApprovalAndGovernance.tsx (2 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/ReleaseEnvironmentCell.tsx (5 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/ApprovalCheck.tsx (5 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/EnvironmentApprovalRow.tsx (2 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/FlowDiagram.tsx (0 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/FlowPolicyNode.tsx (1 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/ResourceReleaseTable.tsx (0 hunks)
  • apps/webservice/src/app/api/v1/openapi.ts (2 hunks)
  • apps/webservice/src/app/api/v1/releases/[releaseId]/openapi.ts (2 hunks)
  • apps/webservice/src/app/api/v1/releases/openapi.ts (2 hunks)
  • apps/webservice/src/app/api/v1/releases/route.ts (2 hunks)
  • openapi.v1.json (6 hunks)
💤 Files with no reviewable changes (2)
  • apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/FlowDiagram.tsx
  • apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/ResourceReleaseTable.tsx
🧰 Additional context used
📓 Path-based instructions (9)
apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/EnvironmentApprovalRow.tsx (1)

Pattern **/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.

apps/webservice/src/app/api/v1/openapi.ts (1)

Pattern **/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.

apps/webservice/src/app/[workspaceSlug]/(app)/_components/environment-policy-drawer/ApprovalAndGovernance.tsx (1)

Pattern **/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.

apps/webservice/src/app/api/v1/releases/openapi.ts (1)

Pattern **/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.

apps/webservice/src/app/api/v1/releases/[releaseId]/openapi.ts (1)

Pattern **/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.

apps/webservice/src/app/api/v1/releases/route.ts (1)

Pattern **/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.

apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/FlowPolicyNode.tsx (1)

Pattern **/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.

apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/ReleaseEnvironmentCell.tsx (1)

Pattern **/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.

apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/ApprovalCheck.tsx (1)

Pattern **/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Typecheck
  • GitHub Check: build (linux/amd64)
  • GitHub Check: Lint
🔇 Additional comments (15)
apps/webservice/src/app/[workspaceSlug]/(app)/_components/environment-policy-drawer/ApprovalAndGovernance.tsx (1)

5-5: LGTM!

The Checkbox import is correctly placed and follows the project's import structure.

apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/EnvironmentApprovalRow.tsx (1)

3-3: LGTM! Clean removal of unused environment dependencies.

The removal of the linkedEnvironments prop and its related type import simplifies the component's interface while maintaining its core functionality.

Also applies to: 20-20

apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/FlowPolicyNode.tsx (2)

132-132: LGTM! Clean removal of unused environment dependencies.

The removal of the linkedEnvironments prop from ApprovalCheck usage simplifies the component's interface while maintaining its core functionality.


132-132: LGTM! Prop removal is consistent with the refactoring.

The removal of the linkedEnvironments prop from ApprovalCheck aligns with the broader changes to simplify the approval workflow.

apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/ReleaseEnvironmentCell.tsx (2)

21-21: LGTM! Well-structured approval status integration.

The changes effectively integrate approval status handling with:

  • Proper loading state management
  • Clear user feedback for pending approvals
  • Consistent UI state transitions

Also applies to: 52-56, 95-97, 107-108, 113-114, 174-186, 187-187


52-56: LGTM! Well-structured query with proper type safety.

The approval status query is correctly configured with:

  • Proper type safety through the query parameters
  • Appropriate enabling condition based on policy existence
apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/ApprovalCheck.tsx (1)

3-3: LGTM! Well-structured approval dialog enhancements.

The changes effectively:

  • Add proper loading states with visual feedback
  • Ensure cache consistency through invalidation
  • Improve the user interface with conditional rendering

Also applies to: 26-28, 32-37, 43-43, 49-49, 61-83, 85-90, 99-99, 142-142

apps/webservice/src/app/api/v1/releases/[releaseId]/openapi.ts (1)

57-57: LGTM! Good use of schema reference.

The change promotes schema reuse and maintains consistency across the API specification.

apps/webservice/src/app/api/v1/releases/openapi.ts (2)

28-31: Schema specificity concern.

Same concern as in [releaseId]/openapi.ts about defining a more specific schema for jobAgentConfig.


52-52: LGTM! Consistent schema reference.

Same approval as in [releaseId]/openapi.ts for using schema reference.

apps/webservice/src/app/api/v1/releases/route.ts (2)

75-75: LGTM! Consistent conflict update handling.

The jobAgentConfig field is correctly added to the list of updatable columns.


120-125: LGTM! Improved code formatting.

The reformatting improves readability while maintaining the same functionality.

apps/webservice/src/app/api/v1/openapi.ts (2)

64-64: Schema specificity concern.

Same concern as in other files about defining a more specific schema for jobAgentConfig.


76-76: Verify impact of making jobAgentConfig required.

Adding jobAgentConfig to required fields is a breaking change. Please verify that:

  1. All existing releases have this field.
  2. All creation/update flows are updated to include this field.
openapi.v1.json (1)

2581-2584: Verify the impact of making jobAgentConfig required in the Release schema.

The jobAgentConfig property has been added to the Release schema and marked as required. This is a breaking change that could affect existing clients.

Run the following script to check for existing releases that might be affected:

Also applies to: 2604-2605

Comment on lines +1218 to +1221
"jobAgentConfig": {
"type": "object",
"additionalProperties": true
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ensure consistent schema usage across PATCH and POST endpoints.

The jobAgentConfig property has been added to both PATCH and POST request bodies for releases. However, there's an inconsistency:

  • POST /v1/releases requires jobAgentConfig (via Release schema)
  • PATCH /v1/releases/{releaseId} makes it optional

This inconsistency could lead to confusion. Consider making it consistently required or optional across both endpoints.

Also applies to: 1286-1289

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/job-dispatch/src/policies/gradual-rollout.ts (2)

22-26: Consider extracting the magic number and improving documentation.

The hash seed (11) appears to be a magic number. Consider extracting it to a named constant and documenting why this specific value was chosen.

+const HASH_SEED = 11; // Document why this specific seed was chosen
+
 export const isReleaseJobTriggerInRolloutWindow = (
   session: string,
   startDate: Date,
   duration: number,
-) => murmurhash.v3(session, 11) % 100 < timeWindowPercent(startDate, duration);
+) => murmurhash.v3(session, HASH_SEED) % 100 < timeWindowPercent(startDate, duration);

52-59: Consider improving the readability of the date calculation.

The date calculation logic is complex and could benefit from being broken down into more readable steps.

 export const getRolloutDateForReleaseJobTrigger = (
   session: string,
   startDate: Date,
   duration: number,
-) =>
-  new Date(
-    startDate.getTime() + ((duration * murmurhash.v3(session, 11)) % 100) / 100,
-  );
+) => {
+  const hashValue = murmurhash.v3(session, HASH_SEED);
+  const percentageOffset = (hashValue % 100) / 100;
+  const timeOffset = duration * percentageOffset;
+  return new Date(startDate.getTime() + timeOffset);
+};
apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/FlowPolicyNode.tsx (1)

77-81: Consider adding error handling for the query.

While the current implementation handles loading state well, it would be beneficial to handle query errors explicitly.

Consider adding error handling:

-  const { data: approvalStatus, isLoading } =
+  const { data: approvalStatus, isLoading, error } =
     api.environment.policy.approval.statusByReleasePolicyId.useQuery(
       { policyId: data.id, releaseId: data.release.id },
       { enabled: data.approvalRequirement === "manual" },
     );
+
+  if (error) {
+    return (
+      <div className="flex items-center gap-2 text-destructive">
+        <Cancelled /> Error loading approval status
+      </div>
+    );
+  }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d2ae812 and df2ac3c.

📒 Files selected for processing (6)
  • apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/FlowPolicyNode.tsx (3 hunks)
  • packages/api/src/router/environment-approval.ts (1 hunks)
  • packages/db/drizzle/0064_misty_stryfe.sql (1 hunks)
  • packages/db/drizzle/meta/_journal.json (1 hunks)
  • packages/job-dispatch/src/index.ts (0 hunks)
  • packages/job-dispatch/src/policies/gradual-rollout.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • packages/job-dispatch/src/index.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/db/drizzle/0064_misty_stryfe.sql
🧰 Additional context used
📓 Path-based instructions (3)
packages/api/src/router/environment-approval.ts (1)

Pattern **/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.

apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/FlowPolicyNode.tsx (1)

Pattern **/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.

packages/job-dispatch/src/policies/gradual-rollout.ts (1)

Pattern **/*.{ts,tsx}: Note on Error Handling:
Avoid strict enforcement of try/catch blocks. Code may use early returns, Promise chains (.then().catch()), or other patterns for error handling. These are acceptable as long as they maintain clarity and predictability.

🔇 Additional comments (10)
packages/db/drizzle/meta/_journal.json (1)

452-458: LGTM! Verify the associated migration file.

The new migration entry follows the established format and maintains sequential ordering.

Let's verify the existence and content of the associated migration file:

✅ Verification successful

Migration file verified successfully

The migration file 0064_misty_stryfe.sql exists and contains the expected approved_at column addition to the environment_policy_approval table with appropriate timestamp type.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence and content of the migration file

# Check if the migration file exists
ls -l packages/db/drizzle/0064_misty_stryfe.sql

# If it exists, display its content to verify the approved_at column addition
cat packages/db/drizzle/0064_misty_stryfe.sql

Length of output: 297

packages/job-dispatch/src/policies/gradual-rollout.ts (3)

1-9: LGTM! Good choice on the dependency changes.

The replacement of lodash with murmurhash is a good optimization, as it provides a more focused and lightweight solution for hash generation.


28-50: LGTM! Well-structured approval status handling.

The function properly handles both automatic and manual approval scenarios, with appropriate null handling for invalid states.


98-125: 🛠️ Refactor suggestion

Consider adding error handling for database operations.

While the Promise.all usage is correct, consider adding error handling for potential database operation failures.

 return Promise.all(
   policies.map(async (p) => {
+    try {
       const { release_job_trigger, environment_policy, release } = p;
       if (
         environment_policy == null ||
         environment_policy.rolloutDuration === 0
       )
         return release_job_trigger;

       const rolloutStart = await getRolloutStart(
         db,
         release,
         environment_policy,
       );
       if (rolloutStart == null) return null;

       if (
         isReleaseJobTriggerInRolloutWindow(
           release_job_trigger.resourceId,
           rolloutStart,
           environment_policy.rolloutDuration,
         )
       )
         return release_job_trigger;

       return null;
+    } catch (error) {
+      console.error('Failed to process policy:', error);
+      return null;
+    }
   }),
 ).then((results) => results.filter(isPresent));

Likely invalid or redundant comment.

packages/api/src/router/environment-approval.ts (2)

74-74: LGTM! Good variable extraction.

Extracting userId improves code readability and reusability.


77-77: LGTM! Good timestamp tracking implementation.

Adding approvedAt timestamp enhances audit capabilities by tracking when approvals occur.

Let's verify the database schema supports this new field:

✅ Verification successful

Schema verification successful: approvedAt timestamp is properly supported

The database schema includes the approved_at timestamp column in the environment_policy_approval table with the correct type (timestamp with time zone), making it fully compatible with the implementation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify environment_policy_approval table schema includes approvedAt field
rg -A 5 "environment_policy_approval.*approvedAt"

Length of output: 51


Script:

#!/bin/bash
# Find SQL files and search for environment-related tables
echo "=== Looking for SQL files with environment tables ==="
fd -e sql -x cat {} | grep -i "environment" -A 10

echo -e "\n=== Looking for migration files ==="
fd -e ts migration | xargs cat | grep -i "environment" -A 10

Length of output: 25019

apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/FlowPolicyNode.tsx (4)

18-18: LGTM! Good icon organization.

Importing additional status icons enhances UI feedback capabilities.


104-104: LGTM! Proper dependency update.

Correctly updated useEffect dependency to use startDate instead of data.release.createdAt.


108-133: LGTM! Comprehensive UI state handling.

Good implementation of loading and approval states with appropriate visual feedback.

The implementation:

  1. Shows loading state while fetching approval status
  2. Handles pending approval state
  3. Shows rejection feedback
  4. Uses appropriate icons for each state

77-86: LGTM! Well-structured approval status handling.

Good implementation of conditional query and startDate calculation based on approval requirement.

Let's verify the API endpoint exists and returns the expected data:

✅ Verification successful

API endpoint verification successful

The statusByReleasePolicyId endpoint is properly implemented with correct input parameters, authorization checks, and returns the expected approval status data including the approvedAt field used in the frontend.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the API endpoint implementation
ast-grep --pattern 'statusByReleasePolicyId = protectedProcedure
  .input($$$)
  .query($$$)'

Length of output: 95


Script:

#!/bin/bash
# Find potential router files
fd -e ts -e tsx router
fd -e ts -e tsx approval

Length of output: 542


Script:

#!/bin/bash
# Check the router implementation
cat packages/api/src/router/environment-approval.ts

# Backup search for the endpoint name
rg "statusByReleasePolicyId" -A 10

Length of output: 13955

@adityachoudhari26 adityachoudhari26 changed the title init fix: Gradual rollout starts at approval date if approval gate on environment Jan 27, 2025
@adityachoudhari26 adityachoudhari26 merged commit fbf86c4 into main Jan 27, 2025
@adityachoudhari26 adityachoudhari26 deleted the grad-rollouts-stuff branch January 27, 2025 18:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants