Skip to content

Conversation

@jsbroks
Copy link
Member

@jsbroks jsbroks commented Apr 8, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a new VariableReleaseManager class for managing variable releases.
    • Added a VersionReleaseManager class to handle version releases and related policies.
    • Implemented a VersionRuleEngine for structured version filtering and selection based on rules.
    • Added new exports for version and variable management functionalities.
    • Introduced a sensitive field in the deploymentVariableValue and variableSetValue tables to indicate sensitive information.
  • Refactor

    • Streamlined release management by renaming and restructuring database tables and related queries.
    • Unified naming conventions and export interfaces across components for improved consistency.
    • Updated method signatures and types to reflect changes in context handling.
  • Bug Fixes

    • Removed outdated logic for updating release versions and variables, improving the overall deployment process.
  • Chore

    • Updated the underlying database schema and query patterns to support the new release management flow.
    • Removed unnecessary files and exports to simplify the codebase.
    • Added tailwind.config.js to .gitignore to prevent it from being tracked in version control.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Apr 8, 2025

Walkthrough

This pull request removes the legacy deployment evaluation logic by deleting the old evaluation file and refactoring many components. The public API now exports a new versioned rule engine. Updates span type renaming (switching from DeploymentResourceContext to RuleEngineContext) across evaluation, repository, and test files. In addition, the database schema has been revised to use versioned releases, and several new manager modules have been introduced to handle version and variable release upsert operations and rule evaluation. These changes collectively modernize the rule engine’s structure and querying mechanisms.

Changes

File(s) Change Summary
packages/rule-engine/src/evaluate.ts Removed file: Eliminated legacy evaluation logic including deny windows and approval rules.
packages/rule-engine/src/index.ts API update: Changed export from ./rule-engine.js to ./manager/version-rule-engine.js.
packages/rule-engine/src/releases.ts, repositories/db-release-repository.ts, repositories/types.ts, rules/__tests__/deployment-deny-rule.test.ts Type refactoring: Updated context types from DeploymentResourceContext to RuleEngineContext and adjusted method signatures accordingly.
packages/rule-engine/src/rules/deployment-deny-rule.ts, rules/version-approval-rule.ts Rule refactoring: Modified rule implementations to be generic and use new RuleEngine types (e.g. RuleEngineFilter, RuleEngineRuleResult), updating filter method signatures and logic.
packages/rule-engine/src/types.ts Type updates: Renamed types (e.g., DeploymentResourceContext to RuleEngineContext) and converted rule result/selection types into generic versions with updated property names.
apps/event-worker/src/workers/evaluate-release-target.ts, job-dispatch/github.ts Query adjustments: Updated queries and variable names to reference versionRelease over release and changed destructured variable names (e.g., chosenCandidate).
packages/db/src/schema/release.ts Schema changes: Renamed the release table to versionRelease, updated related tables (e.g. releaseVariablevariableRelease), added variableReleaseValue, and revised relations to align with the new naming conventions.
packages/rule-engine/src/repositories/get-releases.ts Query refactoring: Modified joins and order-by clauses to use schema.versionRelease instead of the old release structures.
packages/rule-engine/src/manager/* (including types.ts, variable-manager.ts, version-manager.ts, version-manager-rules.ts, version-rule-engine.ts) New manager modules: Introduced new interfaces and classes to manage version releases, including upsert operations, policy retrieval, rule generation, and high-level evaluation via the VersionRuleEngine.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant VRM as VersionReleaseManager
    participant DB as Database
    participant VRE as VersionRuleEngine

    Client->>VRM: evaluate()
    VRM->>DB: get context & latest release
    DB-->>VRM: return context, candidate list
    VRM->>VRM: getPolicy(forceRefresh)
    VRM->>VRE: evaluate(context, candidates)
    VRE->>VRE: apply filtering rules sequentially
    VRE-->>VRM: return evaluation result (chosenCandidate, rejection reasons)
    VRM-->>Client: evaluation result
Loading
sequenceDiagram
    participant Client
    participant VRM as VersionReleaseManager
    participant DB as Database

    Client->>VRM: upsertRelease(versionId)
    VRM->>DB: findLatestRelease()
    alt Version ID matches existing record
        DB-->>VRM: existing release
        VRM-->>Client: { created: false, release: existing release }
    else New version detected
        VRM->>DB: insert new release record
        DB-->>VRM: new release record
        VRM-->>Client: { created: true, release: new release }
    end
Loading

Possibly related PRs

  • add new upser tfunctions #457: The changes in the main PR, which involve the complete removal of the DatabaseReleaseRepository class and its associated functionality, are related to the modifications in the retrieved PR that update the DatabaseReleaseRepository class, specifically in terms of method signatures and functionality related to release management. Both PRs focus on the DatabaseReleaseRepository, albeit in different ways.
  • init deny rule #421: The changes in the main PR, which involve the removal of the evaluate.ts file and its associated functions, are related to the DeploymentDenyRule class introduced in the retrieved PR, as both pertain to the evaluation of deployment rules and policies, specifically regarding deny rules. However, the main PR removes the logic for evaluating these rules, while the retrieved PR introduces a new implementation for handling deny rules.
  • fix: More deployment version refactor #371: The changes in the main PR, which involve the removal of the evaluate.ts file and its associated functions, are related to the modifications in the retrieved PR that refactor database queries to focus on deploymentVersion instead of release, as both PRs reflect a shift in the handling of deployment-related logic.

Suggested reviewers

  • zacharyblasczyk

Poem

I'm a bouncy rabbit, coding in the field,
Hop-scotching through changes, with no need to yield.
Out go the old, in come the new,
Version rules and schemas - a fresh debut!
With every line of refactored art,
I celebrate the changes from my heart! 🐰💻


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 76b68c4 and 2115443.

📒 Files selected for processing (4)
  • packages/db/drizzle/0086_marvelous_katie_power.sql (1 hunks)
  • packages/db/drizzle/meta/_journal.json (1 hunks)
  • packages/db/src/schema/deployment-version.ts (1 hunks)
  • packages/rule-engine/src/manager/variable-manager.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/db/drizzle/0086_marvelous_katie_power.sql
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/rule-engine/src/manager/variable-manager.ts
  • packages/db/drizzle/meta/_journal.json
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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/db/src/schema/deployment-version.ts
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Typecheck
  • GitHub Check: Lint
  • GitHub Check: build (linux/amd64)
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (1)
packages/db/src/schema/deployment-version.ts (1)

128-130: Improved timestamp handling with timezone support and precision

This change enhances the createdAt field by adding timezone support and millisecond precision (3 decimal places). This is a good improvement that ensures consistent timestamp handling across different environments and time zones, which is particularly important in distributed systems.

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @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
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 (4)
packages/rule-engine/src/version-rule-engine.ts (4)

1-2: Consider partial lodash imports.
If you are only using a few lodash functions (such as minBy and maxBy), you could import them individually to reduce bundle size, e.g., import minBy from "lodash/minBy";.


10-16: Consider narrowing the types for config and metadata.
Using Record<string, any> and Record<string, string> works but can over-generalize. You might use more specific types or generics to better capture the data shape, which improves maintainability and type safety.


38-39: Consider simplifying the rule definition union.
Having both direct RuleEngineFilter<Version> objects and functions returning filters can be flexible but increases complexity. You might unify this approach if the codebase consistently relies on one pattern.


161-165: Consider storing requiresSequentialUpgrade as a boolean.
Filtering on v.metadata.requiresSequentialUpgrade === "true" suggests it’s stored as a string. A boolean field could be more direct and less error-prone.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between fb82448 and 863dde4.

📒 Files selected for processing (11)
  • packages/rule-engine/src/evaluate.ts (3 hunks)
  • packages/rule-engine/src/index.ts (1 hunks)
  • packages/rule-engine/src/releases.ts (3 hunks)
  • packages/rule-engine/src/repositories/db-release-repository.ts (5 hunks)
  • packages/rule-engine/src/repositories/types.ts (2 hunks)
  • packages/rule-engine/src/rules/__tests__/deployment-deny-rule.test.ts (1 hunks)
  • packages/rule-engine/src/rules/__tests__/version-approval-rule.test.ts (1 hunks)
  • packages/rule-engine/src/rules/deployment-deny-rule.ts (4 hunks)
  • packages/rule-engine/src/rules/version-approval-rule.ts (6 hunks)
  • packages/rule-engine/src/types.ts (2 hunks)
  • packages/rule-engine/src/version-rule-engine.ts (7 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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/rule-engine/src/index.ts
  • packages/rule-engine/src/rules/__tests__/version-approval-rule.test.ts
  • packages/rule-engine/src/evaluate.ts
  • packages/rule-engine/src/repositories/types.ts
  • packages/rule-engine/src/releases.ts
  • packages/rule-engine/src/repositories/db-release-repository.ts
  • packages/rule-engine/src/rules/__tests__/deployment-deny-rule.test.ts
  • packages/rule-engine/src/rules/deployment-deny-rule.ts
  • packages/rule-engine/src/types.ts
  • packages/rule-engine/src/version-rule-engine.ts
  • packages/rule-engine/src/rules/version-approval-rule.ts
🧬 Code Definitions (7)
packages/rule-engine/src/rules/__tests__/version-approval-rule.test.ts (2)
packages/rule-engine/src/releases.ts (1)
  • Releases (18-304)
packages/rule-engine/src/types.ts (1)
  • RuleEngineContext (35-40)
packages/rule-engine/src/repositories/types.ts (1)
packages/rule-engine/src/types.ts (1)
  • RuleEngineContext (35-40)
packages/rule-engine/src/releases.ts (1)
packages/rule-engine/src/types.ts (2)
  • RuleEngineContext (35-40)
  • ResolvedRelease (5-15)
packages/rule-engine/src/repositories/db-release-repository.ts (2)
packages/db/src/schema/release.ts (1)
  • releaseTarget (18-40)
packages/rule-engine/src/types.ts (1)
  • RuleEngineContext (35-40)
packages/rule-engine/src/rules/deployment-deny-rule.ts (1)
packages/rule-engine/src/types.ts (3)
  • RuleEngineFilter (55-61)
  • RuleEngineContext (35-40)
  • RuleEngineRuleResult (42-45)
packages/rule-engine/src/types.ts (2)
packages/db/src/schema/resource.ts (1)
  • Resource (105-105)
packages/db/src/schema/policy.ts (2)
  • policy (23-39)
  • Policy (104-104)
packages/rule-engine/src/rules/version-approval-rule.ts (2)
packages/rule-engine/src/types.ts (3)
  • RuleEngineContext (35-40)
  • RuleEngineFilter (55-61)
  • RuleEngineRuleResult (42-45)
packages/rule-engine/src/version-rule-engine.ts (1)
  • Version (10-16)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: Lint
  • GitHub Check: Typecheck
🔇 Additional comments (48)
packages/rule-engine/src/index.ts (1)

3-3: Correctly updated export to use versioned rule engine

The export statement has been updated to use the new versioned rule engine implementation, which aligns with the refactoring effort to adopt a more generic, version-specific approach.

packages/rule-engine/src/rules/__tests__/version-approval-rule.test.ts (1)

3-3: Proper type transition to RuleEngineContext

The import and type declaration have been correctly updated to use RuleEngineContext instead of DeploymentResourceContext, maintaining consistency with the broader rule engine refactoring.

Also applies to: 10-10

packages/rule-engine/src/repositories/types.ts (2)

3-3: Updated import statement to use RuleEngineContext

The import statement has been correctly updated to import RuleEngineContext instead of DeploymentResourceContext, maintaining consistency with the rule engine refactoring.


73-73: Updated getCtx method return type

The getCtx method's return type has been properly updated to use RuleEngineContext instead of DeploymentResourceContext, which aligns with the changes in other files and maintains type consistency throughout the codebase.

packages/rule-engine/src/releases.ts (2)

1-1: Updated import statement to use RuleEngineContext

The import statement has been correctly updated to import RuleEngineContext instead of DeploymentResourceContext, maintaining consistency with the broader refactoring effort.


103-103: Updated method parameter types to RuleEngineContext

The parameter types for getDesired and getEffectiveTarget methods have been properly updated to use RuleEngineContext instead of DeploymentResourceContext. This ensures type consistency across the codebase while maintaining the same functionality.

Also applies to: 120-120

packages/rule-engine/src/rules/__tests__/deployment-deny-rule.test.ts (2)

5-5: Adoption of new RuleEngineContext import looks good
This change aligns with the broader shift from DeploymentResourceContext to RuleEngineContext.


11-11: Ensure full migration to RuleEngineContext
Double-check for any lingering references to the old DeploymentResourceContext type across test utilities or fixtures to avoid inconsistent context usage.

packages/rule-engine/src/evaluate.ts (6)

4-6: Updated imports align with new engine types
The addition of RuleEngineFilter, RuleEngineSelectionResult, and Version ensures clarity and consistency with the revised architecture.


14-14: Switch to VersionRuleEngine
Replacing the straightforward RuleEngine with the version-specific VersionRuleEngine reflects the new domain-specific approach. No concerns here.


21-25: Parameterizing DeploymentDenyRule with <Version>
Using the generic form for DeploymentDenyRule and providing getCandidateId clarifies type handling while ensuring each version can be correctly identified.


129-130: Consistent policy retrieval
Retrieving the policy via repository.getPolicy() maintains a straightforward approach; no issues found.


136-136: Instantiation of VersionRuleEngine
Initializing new VersionRuleEngine(rules) is correct in context of typed filters.


138-141: Evaluating context and versions
Passing ctx and mapped versions to engine.evaluate is consistent with the updated filtering methodology. Looks solid.

packages/rule-engine/src/repositories/db-release-repository.ts (5)

8-8: Renamed import to RuleEngineContext
This matches the broader refactor away from DeploymentResourceContext.


27-33: DatabaseReleaseTarget interface
This new interface name clarifies that the release target is database-centric, aligning with the naming consistency.


46-53: Factory method create for DatabaseReleaseRepository
The async creation pattern matches the overall approach. No immediate issues.


60-62: Constructor now leverages DatabaseReleaseTarget
Integrates the renamed interface consistently. No concerns from a correctness standpoint.


237-246: getCtx method returning RuleEngineContext | undefined
Ensure the returned object shape fully matches the RuleEngineContext definition (deployment, environment, resource, etc.). The usage of .findFirst() is straightforward, but verify each nested relation is present in the final result.

packages/rule-engine/src/rules/deployment-deny-rule.ts (7)

11-13: Replaced old rule engine imports
Upgrading to RuleEngineContext, RuleEngineFilter, and RuleEngineRuleResult is consistent with the new architecture.


45-55: Generic DeploymentDenyRuleOptions
The added getCandidateId function and generic <T> parameter support more flexible candidate types. Well-structured approach.


57-64: DeploymentDenyRule now implements RuleEngineFilter<T>
Great alignment with the typed filter approach. The class signature is correct, specifying name and a compliant filter method.


67-67: Constructor enhancements
Storing getCandidateId ensures each candidate can be mapped to a reason. The usage of time-based logic remains consistent.

Also applies to: 71-71, 73-73, 74-74, 77-77, 78-78


100-102: filter method signature
Accepting generic candidates and returning a RuleEngineRuleResult<T> aligns with the new filter pattern.


109-114: Building rejection reasons for each candidate
Mapping candidates to the getCandidateId ensures detailed, candidate-specific denial reasons.


118-118: Returning all candidates if not denied
Clear logic ensures a straightforward pass-through when no restriction is active.

packages/rule-engine/src/version-rule-engine.ts (7)

4-7: Updated type imports look consistent.
No concerns with the new imports from ./types.js; this aligns well with the new architecture.


28-28: Class rename is aligned with the new approach.
Implementing RuleEngine<Version> channels the new design correctly.


71-73: Method signature aligns with the new generics.
Accepting RuleEngineContext and Version[] fits well. No concerns here.


81-81: Use of allowedCandidates is properly integrated.
This ensures the new filter outputs are uniformly handled. Merging with existing rejection reasons in subsequent lines also looks good.

Also applies to: 83-86


102-103: Selecting the final release is cleanly refactored.
Switching to chosenCandidate and returning null if none are found is consistent with the new result type.

Also applies to: 106-106, 110-110


137-140: Signature updated for selectFinalRelease.
Passing RuleEngineContext and Version[] reflects the new architecture.


147-149: Use of _.minBy/_.maxBy is appropriate.
This straightforward selection logic is easy to follow.

packages/rule-engine/src/types.ts (5)

35-40: Context type change is consistent with the refactor.
RuleEngineContext better communicates the new, generic rule engine scope.


42-45: Renamed fields align with candidate-based filtering.
Switching from allowedReleases to allowedCandidates is clear and cohesive with the rest of the types.


47-50: RuleEngineSelectionResult properly references chosenCandidate.
This naming works consistently with the new design.


55-60: RuleEngineFilter interface is clear.
Accepting a context and a candidate array, returning a RuleEngineRuleResult<T> matches the updated architecture.


82-87: RuleEngine interface is straightforward.
Your evaluate method signature is consistent across the codebase, ensuring easy extensibility.

packages/rule-engine/src/rules/version-approval-rule.ts (10)

8-12: Import changes align with the new rule engine types.
Shifting to RuleEngineContext and RuleEngineRuleResult is consistent with the reorganized architecture.


22-22: GetApprovalRecordsFunc now takes RuleEngineContext.
Properly updated to match the rest of the approach.


32-32: Implements RuleEngineFilter<Version> suitably.
Renaming to VersionApprovalRule clarifies its intent and usage.


38-40: New signature for filter method.
Accommodates RuleEngineContext and Version[]. Looks good.


42-43: Use of lodash chain for IDs is efficient.
Extracting version IDs into a unique list ensures minimal duplication.


51-52: Filtering logic is straightforward.
Rejecting based on any rejections present and requiring a minimum number of approvals is clear.


68-68: Properly returning allowedCandidates with rejections.
This matches the new RuleEngineRuleResult<Version> structure.


73-73: Context parameter naming underscores we are not using it here.
Using _ is a valid approach for unused parameters.


89-89: Same pattern for role approval records retrieval.
The new context parameter is consistent.


105-105: Likewise for user approval records.
No concerns found here.

Copy link
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/rule-engine/src/version-rule-engine.ts (3)

10-16: Optional: Add documentation for each field of the Version type.

To improve clarity and maintainability, consider adding short doc comments describing each property (e.g., id, tag, config, metadata, createdAt) so future maintainers understand their purpose at a glance.


27-41: Consider handling rule execution errors more gracefully.

If a rule (or its promise) throws an error, the engine will fail entirely. Depending on your requirements, you might want to catch exceptions and annotate rejection reasons instead of abruptly crashing the evaluation process.


152-157: Consider validating metadata.requiresSequentialUpgrade as a boolean.

Currently, this check strictly compares against the string "true". If metadata ever stores this flag in a different form (e.g., uppercase or real boolean), the filter would fail. Structuring it as a typed boolean can make the logic more robust.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 863dde4 and e85ac9e.

📒 Files selected for processing (1)
  • packages/rule-engine/src/version-rule-engine.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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/rule-engine/src/version-rule-engine.ts
🧬 Code Definitions (1)
packages/rule-engine/src/version-rule-engine.ts (2)
packages/rule-engine/src/types.ts (4)
  • RuleEngine (82-87)
  • RuleEngineFilter (55-61)
  • RuleEngineContext (35-40)
  • RuleEngineSelectionResult (47-50)
packages/job-dispatch/src/release-job-trigger.ts (1)
  • versions (73-76)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Lint
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (3)
packages/rule-engine/src/version-rule-engine.ts (3)

1-2: No issues with Lodash import usage.

The usage of Lodash for minBy and maxBy is justified and clear.


67-110: Evaluation logic looks correct and straightforward.

Sequentially applying rules, accumulating rejection reasons, and short-circuiting when all candidates are disqualified aligns well with typical rule-based filtering workflows.


128-141: Robust version selection strategy.

Choosing the oldest version for sequential upgrades and falling back to the newest version by creation date is consistent with the documented intent.

Copy link
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 (6)
packages/db/src/schema/release.ts (6)

42-55: Consider indexing for performance.

The new version_release table looks good. However, if you often query by both releaseTargetId and versionId, consider adding an index on these columns (together or separately) for improved query performance.

 export const versionRelease = pgTable("version_release", {
   id: uuid("id").primaryKey().defaultRandom(),
   releaseTargetId: uuid("release_target_id")
     .notNull()
     .references(() => releaseTarget.id, { onDelete: "cascade" }),
   versionId: uuid("version_id")
     .notNull()
     .references(() => deploymentVersion.id, { onDelete: "cascade" }),
   createdAt: timestamp("created_at", { withTimezone: true })
     .notNull()
     .defaultNow(),
 })
+(t) => ({
+  idxVersionRelease: uniqueIndex("version_release_target_version_idx")
+    .on(t.releaseTargetId, t.versionId),
+})

57-65: Potential need for uniqueness constraints.

variable_release references a single releaseTargetId. If the functional requirements demand only one variable_release per target, add a unique index on releaseTargetId to prevent duplicates. Otherwise, this is fine as is.


67-78: Secure sensitive values.

The variable_release_value table flags sensitive data but still stores it as plain JSON. Consider storing sensitive values in an encrypted form or in an external secrets manager to enhance security.


81-95: Optional indexing for frequent lookups.

Referencing versionReleaseId and variableReleaseId is correct. If you anticipate frequent lookups or joins on these columns, adding indexes can improve performance. Otherwise, this looks good.


123-136: Rename relation for clarity.

The release: many(release) relationship can confuse readers since it suggests a single release rather than multiple. Consider naming it releases to better reflect the nature of the relationship.


138-149: Likewise rename for plural consistency.

Currently, release: many(release) might cause confusion. Consider renaming it to releases for consistency with its one-to-many cardinality.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f4bc202 and b346a65.

📒 Files selected for processing (6)
  • apps/event-worker/src/workers/evaluate-release-target.ts (2 hunks)
  • apps/event-worker/src/workers/job-dispatch/github.ts (1 hunks)
  • packages/db/src/schema/release.ts (4 hunks)
  • packages/rule-engine/src/repositories/db-release-repository.ts (6 hunks)
  • packages/rule-engine/src/repositories/get-releases.ts (3 hunks)
  • packages/rule-engine/src/repositories/types.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/rule-engine/src/repositories/db-release-repository.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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/event-worker/src/workers/job-dispatch/github.ts
  • packages/rule-engine/src/repositories/get-releases.ts
  • packages/rule-engine/src/repositories/types.ts
  • apps/event-worker/src/workers/evaluate-release-target.ts
  • packages/db/src/schema/release.ts
🧬 Code Definitions (4)
packages/rule-engine/src/repositories/get-releases.ts (2)
packages/db/src/schema/job.ts (1)
  • JobStatus (136-136)
packages/db/src/schema/release.ts (1)
  • releaseTarget (18-40)
packages/rule-engine/src/repositories/types.ts (1)
packages/rule-engine/src/types.ts (1)
  • RuleEngineContext (35-40)
apps/event-worker/src/workers/evaluate-release-target.ts (2)
packages/db/src/schema/release.ts (1)
  • release (81-95)
packages/rule-engine/src/evaluate.ts (1)
  • evaluateRepository (103-142)
packages/db/src/schema/release.ts (2)
packages/db/src/schema/deployment-version.ts (1)
  • deploymentVersion (109-134)
packages/db/src/schema/job.ts (1)
  • job (74-106)
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: Typecheck
  • GitHub Check: Lint
  • GitHub Check: build (linux/amd64)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (18)
packages/rule-engine/src/repositories/types.ts (3)

3-3: Import updated to use RuleEngineContext

The import statement has been correctly updated to use RuleEngineContext instead of DeploymentResourceContext, which aligns with the broader refactoring to make the rule engine more generic and version-specific.


9-9: Base Release type updated to use versionRelease

The base Release type has been appropriately updated to use schema.versionRelease.$inferSelect instead of the previous schema.release.$inferSelect, maintaining type safety while reflecting the schema changes.


73-73: Method signature updated for getCtx()

The return type of getCtx() has been properly updated from Promise<DeploymentResourceContext | undefined> to Promise<RuleEngineContext | undefined>, which is consistent with the import changes and aligns with the more generic rule engine approach.

apps/event-worker/src/workers/job-dispatch/github.ts (2)

78-80: Join statements updated for versionRelease

The inner join has been correctly updated to use SCHEMA.versionRelease instead of SCHEMA.release, maintaining the relationship with SCHEMA.releaseJob through the appropriate ID field.


83-83: Join condition updated for versionRelease

The join condition has been properly updated to reference SCHEMA.versionRelease.releaseTargetId instead of SCHEMA.release.releaseTargetId, maintaining the relationship with SCHEMA.releaseTarget.id.

apps/event-worker/src/workers/evaluate-release-target.ts (3)

22-23: createJobForRelease updated to use versionRelease

The query in createJobForRelease has been correctly updated to use versionRelease table instead of release, reflecting the schema changes.


94-95: Variable renamed from chosenRelease to chosenCandidate

The variable destructured from evaluateRepository has been renamed from chosenRelease to chosenCandidate, reflecting changes in the rule engine which now returns a more generic chosenCandidate instead of a specific chosenRelease.


101-101: Function argument updated to use chosenCandidate

The argument passed to createJobForRelease has been updated to use chosenCandidate.id instead of chosenRelease.id, consistent with the variable renaming.

packages/rule-engine/src/repositories/get-releases.ts (7)

55-56: Inner join updated for schema.release reference

The database query has been updated to maintain the correct relationship between tables, with the inner join now properly referencing schema.release and using the appropriate join condition with schema.release.releaseId.

Also applies to: 59-59


65-65: Order by clause updated to use versionRelease

The order by clause has been updated to sort by schema.versionRelease.createdAt instead of schema.release.createdAt, consistent with the schema changes.


87-90: findMany query updated to use versionRelease

The query method has been updated from db.query.release.findMany to db.query.versionRelease.findMany with appropriate where conditions targeting versionRelease.releaseTargetId and versionRelease.createdAt, reflecting the schema changes.

Also applies to: 95-96


98-102: Date comparison updated for desiredRelease

The date comparison logic for desiredRelease has been updated to correctly use schema.versionRelease.createdAt, maintaining the same functionality while working with the new schema.


108-108: Result ordering updated to use versionRelease

The order by clause in the result has been updated to use schema.versionRelease.createdAt, ensuring consistent ordering with the updated schema.


128-131: findFirst query updated to use versionRelease

The query for finding the latest policy-matching release has been updated to use versionRelease with appropriate conditions and ordering, maintaining functionality with the new schema.


136-139: findFirst query with policy filter updated

The second query that filters by policy has been updated to use versionRelease with appropriate conditions and ordering, correctly implementing the filtering logic with the new schema.

Also applies to: 145-145

packages/db/src/schema/release.ts (3)

150-158: Looks clean and consistent.

No issues spotted with the variableReleaseValue relations. The referencing logic is straightforward, and the naming is clear.


160-173: Relationships are properly defined.

Your releaseRelations consistently reference versionRelease, variableRelease, and job. This appears well-organized and clear. Nice work.


97-97:

✅ Verification successful

Verify cyclical references.

releaseTarget references desiredRelease, while the newly added versionReleases and variableReleases point back to releaseTarget. Ensure this circular dependency does not cause migration or relationship resolution issues.

Also applies to: 118-122


🏁 Script executed:

#!/bin/bash
# Check for potential circular references in table definitions.
ast-grep --pattern $'pgTable($_, { $$$ }, (t) => { $$$ })' --json

Length of output: 69


Circular Dependency Verification Completed

We ran an AST search for table definitions using the provided pattern and found no evidence of problematic circular dependencies in the schema. Based on the verification, the relationships between releaseTarget, desiredRelease, versionReleases, and variableReleases appear to be properly defined and should not introduce migration or relationship resolution issues.

Copy link
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: 3

🧹 Nitpick comments (1)
packages/rule-engine/src/version-manager/version-manager.ts (1)

39-41: Implement the findLatestRelease method.

The findLatestRelease method is currently a placeholder. Consider implementing it to find the latest version release for the given release target.

Would you like me to provide an implementation suggestion for this method based on the database schema?

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b346a65 and cf1fb1a.

📒 Files selected for processing (1)
  • packages/rule-engine/src/version-manager/version-manager.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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/rule-engine/src/version-manager/version-manager.ts
🧬 Code Definitions (1)
packages/rule-engine/src/version-manager/version-manager.ts (2)
packages/db/src/common.ts (1)
  • Tx (22-22)
packages/db/src/schema/release.ts (2)
  • releaseTarget (18-40)
  • release (81-95)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: Lint
  • GitHub Check: Typecheck
🔇 Additional comments (1)
packages/rule-engine/src/version-manager/version-manager.ts (1)

8-14: Verify the workspaceId property in ReleaseTarget interface.

The ReleaseTarget interface includes a workspaceId property that doesn't appear in the release_target table schema from the provided code snippets. Verify if this property is necessary or if it's retrieved from another related table.

Comment on lines 16 to 20
export class VersionReleaseManager {
private constructor(
private readonly db: Tx = dbClient,
private readonly releaseTarget: ReleaseTarget,
) {}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Class needs a static factory method for instantiation.

The VersionReleaseManager class has a private constructor, making it impossible to create instances directly. However, there's no public static factory method provided to instantiate the class. You should add a static factory method to allow consumers to create instances.

export class VersionReleaseManager {
  private constructor(
    private readonly db: Tx = dbClient,
    private readonly releaseTarget: ReleaseTarget,
  ) {}

+  static forReleaseTarget(releaseTarget: ReleaseTarget, db: Tx = dbClient) {
+    return new VersionReleaseManager(db, releaseTarget);
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export class VersionReleaseManager {
private constructor(
private readonly db: Tx = dbClient,
private readonly releaseTarget: ReleaseTarget,
) {}
export class VersionReleaseManager {
private constructor(
private readonly db: Tx = dbClient,
private readonly releaseTarget: ReleaseTarget,
) {}
static forReleaseTarget(releaseTarget: ReleaseTarget, db: Tx = dbClient) {
return new VersionReleaseManager(db, releaseTarget);
}
}

Comment on lines 43 to 45
async canadiates() {
// ...
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in method name and implement the method.

There's a typo in the method name - "canadiates" should be "candidates". Also, this method is currently a placeholder and needs implementation.

-  async canadiates() {
+  async candidates() {
    // ...
  }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 22 to 37
async upsertRelease(versionId: string) {
const release = await this.db.query.versionRelease.findFirst({
where: and(
eq(schema.versionRelease.releaseTargetId, this.releaseTarget.id),
eq(schema.versionRelease.versionId, versionId),
),
orderBy: desc(schema.versionRelease.createdAt),
});

if (release?.versionId === versionId) return release;

return this.db
.insert(schema.versionRelease)
.values({ releaseTargetId: this.releaseTarget.id, versionId })
.returning();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider error handling for database operations.

The upsertRelease method returns the database insert promise directly without awaiting it. This could cause issues if the caller expects a resolved value rather than a promise. Also, there's no error handling for database operations.

  async upsertRelease(versionId: string) {
    const release = await this.db.query.versionRelease.findFirst({
      where: and(
        eq(schema.versionRelease.releaseTargetId, this.releaseTarget.id),
        eq(schema.versionRelease.versionId, versionId),
      ),
      orderBy: desc(schema.versionRelease.createdAt),
    });

    if (release?.versionId === versionId) return release;

-    return this.db
+    const [newRelease] = await this.db
      .insert(schema.versionRelease)
      .values({ releaseTargetId: this.releaseTarget.id, versionId })
      .returning();
+    return newRelease;
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async upsertRelease(versionId: string) {
const release = await this.db.query.versionRelease.findFirst({
where: and(
eq(schema.versionRelease.releaseTargetId, this.releaseTarget.id),
eq(schema.versionRelease.versionId, versionId),
),
orderBy: desc(schema.versionRelease.createdAt),
});
if (release?.versionId === versionId) return release;
return this.db
.insert(schema.versionRelease)
.values({ releaseTargetId: this.releaseTarget.id, versionId })
.returning();
}
async upsertRelease(versionId: string) {
const release = await this.db.query.versionRelease.findFirst({
where: and(
eq(schema.versionRelease.releaseTargetId, this.releaseTarget.id),
eq(schema.versionRelease.versionId, versionId),
),
orderBy: desc(schema.versionRelease.createdAt),
});
if (release?.versionId === versionId) return release;
const [newRelease] = await this.db
.insert(schema.versionRelease)
.values({ releaseTargetId: this.releaseTarget.id, versionId })
.returning();
return newRelease;
}

Copy link
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: 2

🧹 Nitpick comments (2)
packages/rule-engine/src/manager/version-manager.ts (1)

16-24: Consider adding input validation for versionId

The upsertRelease method doesn't validate the versionId parameter. Consider adding validation to ensure it's not empty or invalid before proceeding with database operations.

   async upsertRelease(versionId: string) {
+    if (!versionId) {
+      throw new Error("Version ID cannot be empty");
+    }
+
     const release = await this.findLatestRelease();
     if (release?.versionId === versionId) return release;
     return this.db
       .insert(schema.versionRelease)
       .values({ releaseTargetId: this.releaseTarget.id, versionId })
       .returning()
       .then(takeFirst);
   }
packages/rule-engine/src/manager/variable-manager.ts (1)

20-32: Add comments to explain variable transformations

The lodash transformations for oldVars and newVars could benefit from a comment explaining the purpose of converting the variable arrays to key-value objects for comparison.

+    // Convert variable arrays to key-value objects for efficient comparison
     const oldVars = _(latestRelease?.values ?? [])
       .map((v) => [v.key, v.value])
       .fromPairs()
       .value();

     const newVars = _(variables)
       .compact()
       .map((v) => [v.key, v.value])
       .fromPairs()
       .value();

     const isSame = _.isEqual(oldVars, newVars);
     if (isSame) return { created: false, latestRelease };
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between cf1fb1a and be1cf56.

📒 Files selected for processing (3)
  • packages/rule-engine/src/manager/types.ts (1 hunks)
  • packages/rule-engine/src/manager/variable-manager.ts (1 hunks)
  • packages/rule-engine/src/manager/version-manager.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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/rule-engine/src/manager/types.ts
  • packages/rule-engine/src/manager/variable-manager.ts
  • packages/rule-engine/src/manager/version-manager.ts
🧬 Code Definitions (2)
packages/rule-engine/src/manager/variable-manager.ts (4)
packages/db/src/common.ts (2)
  • Tx (22-22)
  • takeFirst (9-13)
packages/db/src/schema/release.ts (2)
  • releaseTarget (18-40)
  • release (81-95)
packages/rule-engine/src/manager/types.ts (1)
  • ReleaseTarget (1-7)
packages/rule-engine/src/repositories/variables/types.ts (1)
  • MaybeVariable (9-9)
packages/rule-engine/src/manager/version-manager.ts (3)
packages/db/src/common.ts (2)
  • Tx (22-22)
  • takeFirst (9-13)
packages/db/src/schema/release.ts (2)
  • releaseTarget (18-40)
  • release (81-95)
packages/rule-engine/src/manager/types.ts (1)
  • ReleaseTarget (1-7)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: Typecheck
  • GitHub Check: build (linux/amd64)
  • GitHub Check: Lint
🔇 Additional comments (1)
packages/rule-engine/src/manager/types.ts (1)

1-7: Well-defined interface for release targets

The ReleaseTarget interface is clearly defined with appropriate properties for identifying a release target within the system.

Copy link
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 (7)
packages/rule-engine/src/manager/types.ts (2)

1-7: Consider adding doc comments to clarify ReleaseTarget.
Providing short JSDoc descriptions for each property would improve readability and help new contributors understand its purpose.


9-11: Use more explicit parameter types for upsertRelease.
Currently, (...args: any[]) weakens type safety. Consider adopting a more specific parameter signature to ensure correctness and maintainability of implementations.

packages/rule-engine/src/manager/variable-manager.ts (3)

11-15: Revisit private constructor usage.
A private constructor can make external instantiation impossible. If that's intended, consider a factory method or comment explaining how instances should be created.


35-53: Consider partial upsert strategies.
Creating a new release whenever variables differ ensures version tracking, but you might also consider partial updates if your use case allows it. This can avoid storing large duplicate patterns.


55-61: Return type consistency.
findLatestRelease may return null if no release is found. Consider clarifying the expected return type or explicitly handling the absence of a release.

packages/rule-engine/src/manager/version-manager.ts (2)

10-14: Clarify private constructor intent.
Similar to VariableReleaseManager, if this class must be directly instantiated elsewhere, consider making the constructor public or offering a factory method.


30-35: Handle null case in findLatestRelease.
Add a consistent approach to manage the scenario when no recent release exists, e.g., a default object or explicit null checks.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between be1cf56 and 03c2baa.

📒 Files selected for processing (3)
  • packages/rule-engine/src/manager/types.ts (1 hunks)
  • packages/rule-engine/src/manager/variable-manager.ts (1 hunks)
  • packages/rule-engine/src/manager/version-manager.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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/rule-engine/src/manager/version-manager.ts
  • packages/rule-engine/src/manager/types.ts
  • packages/rule-engine/src/manager/variable-manager.ts
🧬 Code Definitions (1)
packages/rule-engine/src/manager/version-manager.ts (3)
packages/rule-engine/src/manager/types.ts (2)
  • ReleaseManager (9-11)
  • ReleaseTarget (1-7)
packages/db/src/common.ts (2)
  • Tx (22-22)
  • takeFirst (9-13)
packages/db/src/schema/release.ts (2)
  • releaseTarget (18-40)
  • release (81-95)
🔇 Additional comments (2)
packages/rule-engine/src/manager/variable-manager.ts (1)

17-33:

❓ Verification inconclusive

Verify concurrency handling for upsertRelease.
If multiple calls occur simultaneously, overwriting changes is possible. Check if concurrency is mitigated through higher-level locking or single-thread usage.


🏁 Script executed:

#!/bin/bash
# Verify all callers of the upsertRelease method to see if concurrency is addressed
rg -A 5 'upsertRelease'

Length of output: 4843


Action Required: Review Concurrency Control in upsertRelease

The current scan of caller usages did not reveal any explicit concurrency safeguards (such as locking or atomic operations) within the upsertRelease implementations (in both variable-manager and related modules). Given that multiple concurrent invocations could potentially lead to an overwrite of changes, please verify that this risk is mitigated at a higher level—whether that’s through a locking mechanism in the repository/version manager or by relying on the single-threaded guarantees of the runtime.

packages/rule-engine/src/manager/version-manager.ts (1)

16-28:

❓ Verification inconclusive

Confirm atomic behavior for upsertRelease.
Two concurrent calls with different versionIds could generate multiple nearly identical releases. Validate if this is acceptable or if additional constraints are needed.


🏁 Script executed:

#!/bin/bash
# Search for concurrency handling or usage patterns for upsertRelease
rg -A 5 'upsertRelease'

Length of output: 4843


Concurrency Consideration: Verify Atomic Behavior in upsertRelease

The current implementation of upsertRelease uses a check-then-act pattern without explicit concurrency controls. This means that if two concurrent calls with different versionIds occur, both might pass the check (findLatestRelease) and proceed to insert new releases, potentially creating multiple nearly identical releases. Please confirm whether this behavior is acceptable or if additional safeguards (e.g., database unique constraints, full transactional handling, or distributed locking) should be added to ensure atomicity.

  • Location: packages/rule-engine/src/manager/version-manager.ts (Lines 16-28)
  • Action: Confirm intended concurrent behavior. If atomicity is required, consider refactoring this logic to enforce a single release update per target.

Copy link
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

♻️ Duplicate comments (1)
packages/rule-engine/src/manager/version-manager.ts (1)

17-20: Constructor privacy noted.

A previous commit comment already addressed the need for a factory method or public constructor. Since this is intentionally private, no further changes appear necessary here.

🧹 Nitpick comments (1)
packages/rule-engine/src/manager/version-rule-engine.ts (1)

148-149: Consider using a boolean rather than a string in metadata.requiresSequentialUpgrade.

Relying on v.metadata.requiresSequentialUpgrade === "true" can be error-prone if there is a mismatch in the string. Having a typed property or boolean field would be more robust.

- (v) => v.metadata.requiresSequentialUpgrade === "true"
+ (v) => v.metadata.requiresSequentialUpgrade === true
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 03c2baa and 0ec389d.

📒 Files selected for processing (7)
  • packages/rule-engine/src/evaluate.ts (0 hunks)
  • packages/rule-engine/src/index.ts (1 hunks)
  • packages/rule-engine/src/manager/variable-manager.ts (1 hunks)
  • packages/rule-engine/src/manager/version-manager-rules.ts (1 hunks)
  • packages/rule-engine/src/manager/version-manager.ts (1 hunks)
  • packages/rule-engine/src/manager/version-rule-engine.ts (1 hunks)
  • packages/rule-engine/src/rules/version-approval-rule.ts (6 hunks)
💤 Files with no reviewable changes (1)
  • packages/rule-engine/src/evaluate.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/rule-engine/src/index.ts
  • packages/rule-engine/src/manager/variable-manager.ts
  • packages/rule-engine/src/rules/version-approval-rule.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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/rule-engine/src/manager/version-manager-rules.ts
  • packages/rule-engine/src/manager/version-rule-engine.ts
  • packages/rule-engine/src/manager/version-manager.ts
🧬 Code Definitions (2)
packages/rule-engine/src/manager/version-manager-rules.ts (4)
packages/rule-engine/src/rules/deployment-deny-rule.ts (1)
  • DeploymentDenyRule (57-176)
packages/rule-engine/src/manager/version-rule-engine.ts (1)
  • Version (10-16)
packages/rule-engine/src/rules/version-approval-rule.ts (4)
  • VersionApprovalRule (32-70)
  • getAnyApprovalRecords (72-86)
  • getRoleApprovalRecords (88-102)
  • getUserApprovalRecords (104-118)
packages/rule-engine/src/types.ts (1)
  • RuleEngineFilter (55-61)
packages/rule-engine/src/manager/version-manager.ts (7)
packages/rule-engine/src/manager/types.ts (2)
  • ReleaseManager (9-11)
  • ReleaseTarget (1-7)
packages/db/src/common.ts (1)
  • Tx (22-22)
packages/rule-engine/src/db/get-applicable-policies.ts (1)
  • getApplicablePolicies (108-139)
packages/rule-engine/src/utils/merge-policies.ts (1)
  • mergePolicies (29-47)
packages/rule-engine/src/types.ts (1)
  • RuleEngineContext (35-40)
packages/rule-engine/src/manager/version-manager-rules.ts (1)
  • getRules (63-72)
packages/rule-engine/src/manager/version-rule-engine.ts (1)
  • VersionRuleEngine (27-151)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: Lint
  • GitHub Check: Typecheck
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (4)
packages/rule-engine/src/manager/version-manager-rules.ts (2)

50-61: Check usage of approval data in versionUserApprovalRule.

Currently, the code ignores the mapped item within the loop (lines 55-56) and always sets minApprovals: 1 regardless of the approvalRules content. This may be intentional, but please confirm this logic or consider allowing each user approval rule to configure minApprovals more dynamically.


63-72: Overall approach looks good.

The flexible composition of deny windows and approval rules is clean and matches the policy-based design. The code is easy to read, modular, and each function serves a single purpose.

packages/rule-engine/src/manager/version-rule-engine.ts (1)

27-103: Kudos on a solid rule evaluation flow.

Sequential application of rules, accumulating rejection reasons, and early exit when no candidates remain is a clear, reliable pattern. The approach cleanly handles both sync and async filter steps.

packages/rule-engine/src/manager/version-manager.ts (1)

77-78: Verify passing an empty array into the rule engine.

engine.evaluate(ctx, []) will produce a null candidate because there are no versions to filter or select. If this is not intentional, consider fetching actual version candidates and passing them in.

Copy link
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 (3)
packages/rule-engine/src/manager/variable-manager.ts (2)

13-14: Unused cachedPolicy property.
The private cachedPolicy field is declared but never used in this class. Consider removing it or leveraging it if you plan to cache or merge policies here.


12-12: Explicitly specify the generic parameter for ReleaseManager.
By default, ReleaseManager expects a release type of { id: string; createdAt: Date }. If schema.variableRelease does not strictly conform to this shape, you may want to specify a more precise type parameter to ensure type safety.

packages/rule-engine/src/manager/version-manager.ts (1)

56-77: Validate environment references in evaluate.
You correctly handle a missing release target by throwing an error. Consider adding additional checks (e.g., verifying related environment/resource references are not stale) to ensure consistent context for rule evaluation.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0ec389d and 8e8697c.

📒 Files selected for processing (2)
  • packages/rule-engine/src/manager/variable-manager.ts (1 hunks)
  • packages/rule-engine/src/manager/version-manager.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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/rule-engine/src/manager/variable-manager.ts
  • packages/rule-engine/src/manager/version-manager.ts
🧬 Code Definitions (1)
packages/rule-engine/src/manager/variable-manager.ts (4)
packages/rule-engine/src/manager/types.ts (2)
  • ReleaseManager (9-11)
  • ReleaseTarget (1-7)
packages/db/src/common.ts (2)
  • Tx (22-22)
  • takeFirst (9-13)
packages/db/src/schema/release.ts (2)
  • releaseTarget (18-40)
  • release (81-95)
packages/rule-engine/src/repositories/variables/types.ts (1)
  • MaybeVariable (9-9)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: Typecheck
  • GitHub Check: Lint
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (3)
packages/rule-engine/src/manager/variable-manager.ts (2)

28-35: Potential concurrency issue on repeated writes.
If multiple calls to upsertRelease occur in parallel with the same new variable set, a short timing gap might allow more than one new record to be created. Consider adding a more robust concurrency check, or unique constraints on the underlying table, if duplicates would cause problems.

Would you like to verify concurrency handling or add a uniqueness constraint to ensure only one release is created for the same variable set?


38-57: Transaction usage is appropriate, but watch for partial inserts.
Wrapping inserts in a transaction is a good approach. If an error occurs when inserting variable values, the partial release record is rolled back correctly. Ensure error handling is tested thoroughly (e.g., if variableReleaseValue insertion fails).

packages/rule-engine/src/manager/version-manager.ts (1)

43-54: Handle null policy scenario.
The getPolicy method can return null if no policy is found. Ensure the call sites (such as evaluate) are prepared to handle a null policy object and avoid runtime errors down the line.

Would you like a script to check if getRules(policy) or VersionRuleEngine can handle a null policy?

Comment on lines +22 to +34
async upsertRelease(versionId: string) {
const latestRelease = await this.findLatestRelease();
if (latestRelease?.versionId === versionId)
return { created: false, release: latestRelease };

const release = await this.db
.insert(schema.versionRelease)
.values({ releaseTargetId: this.releaseTarget.id, versionId })
.returning()
.then(takeFirst);

return { created: true, release };
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider concurrency guard for upsertRelease.
No transaction is used here. If multiple requests try to create a release with the same versionId concurrently, two releases may be created. If uniqueness is required, consider wrapping these inserts in a transaction or defining a unique constraint to avoid duplicates.

Copy link
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

🔭 Outside diff range comments (1)
packages/db/src/schema/release.ts (1)

33-35: 🛠️ Refactor suggestion

Type safety concern with reference casting

The desiredReleaseId field references the release.id with a type cast to any. This bypasses TypeScript's type checking and could lead to runtime errors. Consider using a proper type reference to ensure type safety.

   desiredReleaseId: uuid("desired_release_id")
-     .references((): any => release.id, { onDelete: "set null" })
+     .references(() => release.id, { onDelete: "set null" })
     .default(sql`NULL`),
♻️ Duplicate comments (1)
packages/rule-engine/src/manager/version-manager.ts (1)

22-34: 🛠️ Refactor suggestion

Consider adding concurrency protection for upsertRelease

No transaction is used here. If multiple requests try to create a release with the same versionId concurrently, two releases may be created. Consider wrapping these operations in a transaction or adding a unique constraint on (releaseTargetId, versionId) to prevent duplicates.

  async upsertRelease(versionId: string) {
-    const latestRelease = await this.findLatestRelease();
-    if (latestRelease?.versionId === versionId)
-      return { created: false, release: latestRelease };
-
-    const release = await this.db
-      .insert(schema.versionRelease)
-      .values({ releaseTargetId: this.releaseTarget.id, versionId })
-      .returning()
-      .then(takeFirst);
-
-    return { created: true, release };
+    return this.db.transaction(async (tx) => {
+      const latestRelease = await tx.query.versionRelease.findFirst({
+        where: eq(schema.versionRelease.releaseTargetId, this.releaseTarget.id),
+        orderBy: desc(schema.versionRelease.createdAt),
+      });
+      
+      if (latestRelease?.versionId === versionId)
+        return { created: false, release: latestRelease };
+      
+      const release = await tx
+        .insert(schema.versionRelease)
+        .values({ releaseTargetId: this.releaseTarget.id, versionId })
+        .returning()
+        .then(takeFirst);
+      
+      return { created: true, release };
+    });
  }
🧹 Nitpick comments (4)
apps/event-worker/src/workers/evaluate-release-target.ts (2)

31-31: Improve error message specificity

The error message could be more descriptive to help with debugging. Consider including the ID in the error message.

-  if (!versionRelease) throw new Error("Failed to get release");
+  if (!versionRelease) throw new Error(`Failed to get version release with ID: ${versionReleaseId}`);

45-45: Improve error message specificity

Similar to the previous error message, this one could also be more descriptive.

-  if (!variableRelease) throw new Error("Failed to get variable release");
+  if (!variableRelease) throw new Error(`Failed to get variable release with ID: ${variableReleaseId}`);
packages/rule-engine/src/manager/version-manager.ts (1)

36-41: Add explicit return type for findLatestRelease method

The findLatestRelease method doesn't explicitly specify its return type, which could lead to confusion. Consider adding a specific return type to improve code readability and type safety.

-  async findLatestRelease() {
+  async findLatestRelease(): Promise<typeof schema.versionRelease.$inferSelect | undefined> {
     return this.db.query.versionRelease.findFirst({
       where: eq(schema.versionRelease.releaseTargetId, this.releaseTarget.id),
       orderBy: desc(schema.versionRelease.createdAt),
     });
  }
packages/db/src/schema/release.ts (1)

57-65: Consider adding an index on releaseTargetId

The variableRelease table defines a foreign key to releaseTarget.id but doesn't have an index on this column. Consider adding an index to improve query performance, especially since this column will likely be used in WHERE clauses frequently.

export const variableRelease = pgTable("variable_release", {
  id: uuid("id").primaryKey().defaultRandom(),
  releaseTargetId: uuid("release_target_id")
    .notNull()
    .references(() => releaseTarget.id, { onDelete: "cascade" }),
  createdAt: timestamp("created_at", { withTimezone: true })
    .notNull()
    .defaultNow(),
+}, (t) => ({
+  releaseTargetIdx: index("variable_release_target_id_idx").on(t.releaseTargetId),
}));
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between c0215fb and 70c93c7.

📒 Files selected for processing (4)
  • apps/event-worker/src/workers/evaluate-release-target.ts (3 hunks)
  • packages/db/src/schema/release.ts (4 hunks)
  • packages/rule-engine/src/manager/variable-manager.ts (1 hunks)
  • packages/rule-engine/src/manager/version-manager.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/rule-engine/src/manager/variable-manager.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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/event-worker/src/workers/evaluate-release-target.ts
  • packages/rule-engine/src/manager/version-manager.ts
  • packages/db/src/schema/release.ts
🧬 Code Definitions (3)
apps/event-worker/src/workers/evaluate-release-target.ts (5)
packages/db/src/common.ts (2)
  • Tx (22-22)
  • takeFirst (9-13)
packages/db/src/schema/release.ts (3)
  • versionRelease (42-55)
  • variableRelease (57-65)
  • releaseTarget (18-40)
packages/db/src/schema/job.ts (1)
  • job (74-106)
packages/rule-engine/src/manager/version-manager.ts (1)
  • VersionReleaseManager (15-78)
packages/rule-engine/src/manager/variable-manager.ts (1)
  • VariableReleaseManager (12-66)
packages/rule-engine/src/manager/version-manager.ts (7)
packages/rule-engine/src/manager/types.ts (2)
  • ReleaseManager (9-11)
  • ReleaseTarget (1-7)
packages/db/src/common.ts (2)
  • Tx (22-22)
  • takeFirst (9-13)
packages/rule-engine/src/db/get-applicable-policies.ts (1)
  • getApplicablePolicies (108-139)
packages/rule-engine/src/utils/merge-policies.ts (1)
  • mergePolicies (29-47)
packages/rule-engine/src/types.ts (1)
  • RuleEngineContext (35-40)
packages/rule-engine/src/manager/version-manager-rules.ts (1)
  • getRules (63-72)
packages/rule-engine/src/manager/version-rule-engine.ts (1)
  • VersionRuleEngine (27-151)
packages/db/src/schema/release.ts (2)
packages/db/src/schema/deployment-version.ts (1)
  • deploymentVersion (109-134)
packages/db/src/schema/job.ts (1)
  • job (74-106)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: Typecheck
  • GitHub Check: build (linux/amd64)
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (8)
apps/event-worker/src/workers/evaluate-release-target.ts (3)

19-79: Function refactored for versioned release architecture

This function has been renamed from createJobForRelease to createRelease and updated to work with the new versioned release architecture. The implementation now handles both version releases and variable releases separately, which aligns with the modernized rule engine structure.


102-126: Clean implementation of the new rule engine architecture

The code now uses the VersionReleaseManager and VariableReleaseManager classes to manage releases, which is a cleaner architecture compared to the legacy deployment evaluation logic. This makes the code more modular and easier to understand.


128-134:

❓ Verification inconclusive

Verify job creation behavior for production environments

The code only creates and dispatches a job in development environments. If this is intentional, it's fine, but if jobs should also be created in production, you should add the necessary code for production environments.


🏁 Script executed:

#!/bin/bash
# Check if there's a separate job creation mechanism for production environments
rg -A 5 'createRelease\(' --type ts

Length of output: 1760


Production Job Creation Verification

The current implementation in apps/event-worker/src/workers/evaluate-release-target.ts creates and dispatches a job only when env.NODE_ENV is set to "development". The search did not reveal any separate job creation mechanism for production environments. Please verify that this behavior is intentional. If job creation should also occur in production, consider implementing the corresponding logic or adding an inline comment that clarifies where production jobs are handled.

packages/rule-engine/src/manager/version-manager.ts (2)

15-20: Well-structured manager class implementation

The VersionReleaseManager class provides a clean interface for managing version releases, with good separation of concerns and appropriate use of dependency injection for the database client.


56-77: Well-implemented evaluation method

The evaluate method correctly retrieves the context, fetches policies and rules, and evaluates them using the rule engine. It includes appropriate error handling and returns the evaluation result.

packages/db/src/schema/release.ts (3)

42-55: Good schema restructuring for versioned releases

The versionRelease table correctly represents the new architecture where releases are versioned. The structure includes all necessary fields and appropriate references to related tables.


81-95: Well-designed release join table

The release table effectively joins version releases, variable releases, and jobs. This design allows for flexible management of releases while maintaining data integrity through appropriate foreign key constraints.


97-174: Comprehensive relation definitions

The relations section properly defines the relationships between all the tables, which will facilitate querying the data with proper joins. Good job ensuring all the necessary relationships are defined.

Copy link
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

♻️ Duplicate comments (1)
apps/event-worker/src/workers/evaluate-release-target.ts (1)

97-101: 🛠️ Refactor suggestion

Improve type safety by adding a proper interface instead of using any

Similar to the previous comment, this function also uses any for the releaseTarget parameter, which reduces type safety.

-const handleVariableRelease = async (releaseTarget: any) => {
+const handleVariableRelease = async (releaseTarget: schema.ReleaseTarget & {
+  resource: schema.Resource;
+  environment: schema.Environment;
+  deployment: schema.Deployment;
+}) => {
  const varrm = new VariableReleaseManager(db, {
    ...releaseTarget,
    workspaceId: releaseTarget.resource.workspaceId,
  });
🧹 Nitpick comments (5)
apps/event-worker/src/workers/evaluate-release-target.ts (2)

131-132: Consider wrapping both release operations in a single transaction

The version and variable release operations are performed separately. If one succeeds and the other fails, you may end up with an inconsistent state. Consider wrapping both operations in a single transaction.

-      const versionRelease = await handleVersionRelease(releaseTarget);
-      const variableRelease = await handleVariableRelease(releaseTarget);
+      const { versionRelease, variableRelease } = await db.transaction(async (tx) => {
+        const vrmTx = new VersionReleaseManager(tx, {
+          ...releaseTarget,
+          workspaceId: releaseTarget.resource.workspaceId,
+        });
+        const varrmTx = new VariableReleaseManager(tx, {
+          ...releaseTarget,
+          workspaceId: releaseTarget.resource.workspaceId,
+        });
+        
+        const { chosenCandidate } = await vrmTx.evaluate();
+        if (!chosenCandidate) throw new Error("Failed to get chosen release");
+        const { release: versionRelease } = await vrmTx.upsertRelease(chosenCandidate.id);
+        
+        const { chosenCandidate: variableValues } = await varrmTx.evaluate();
+        const { release: variableRelease } = await varrmTx.upsertRelease(variableValues);
+        
+        return { versionRelease, variableRelease };
+      });

17-17: Update worker name in logger to match file purpose

The worker name in the logger is "policy-evaluate" but the file is about evaluating release targets. Consider updating the worker name for consistency.

-const log = logger.child({ worker: "policy-evaluate" });
+const log = logger.child({ worker: "evaluate-release-target" });
packages/rule-engine/src/manager/variable-manager.ts (3)

35-37: Potential performance optimization for large variable sets

The deep comparison with _.isEqual can be expensive for large variable sets. For very large sets, consider using a more optimized approach like comparing object keys count first or using hashing.

-    const isSame = _.isEqual(oldVars, newVars);
+    // Quick length check before deep comparison
+    const isSame = Object.keys(oldVars).length === Object.keys(newVars).length && 
+                  _.isEqual(oldVars, newVars);

13-14: Cached policy field is unused

The cachedPolicy field is initialized but never used in this class. Consider removing it if it's not needed, or implement the caching mechanism if it's intended for future use.

export class VariableReleaseManager implements ReleaseManager {
-  private cachedPolicy: Policy | null = null;

1-73: Missing method documentation comments

Consider adding JSDoc comments for the public methods to improve code documentation. This helps other developers understand how to use these methods properly.

+  /**
+   * Creates or retrieves a variable release.
+   * If the variables are the same as the latest release, returns the existing release.
+   * Otherwise, creates a new release with the provided variables.
+   * @param variables - Array of variables (possibly including nulls) to be associated with the release
+   * @returns Object containing created flag and the release
+   */
   async upsertRelease(variables: MaybeVariable[]) {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 70c93c7 and 9a1f193.

📒 Files selected for processing (11)
  • apps/event-worker/src/workers/evaluate-release-target.ts (2 hunks)
  • apps/event-worker/src/workers/job-dispatch/github.ts (2 hunks)
  • apps/event-worker/src/workers/new-deployment-version.ts (0 hunks)
  • apps/event-worker/src/workers/update-deployment-variable.ts (0 hunks)
  • apps/event-worker/src/workers/update-resource-variable.ts (0 hunks)
  • packages/rule-engine/src/index.ts (1 hunks)
  • packages/rule-engine/src/manager/variable-manager.ts (1 hunks)
  • packages/rule-engine/src/repositories/db-release-repository.ts (0 hunks)
  • packages/rule-engine/src/repositories/get-releases.ts (0 hunks)
  • packages/rule-engine/src/repositories/index.ts (0 hunks)
  • packages/rule-engine/src/repositories/types.ts (0 hunks)
💤 Files with no reviewable changes (7)
  • apps/event-worker/src/workers/new-deployment-version.ts
  • apps/event-worker/src/workers/update-deployment-variable.ts
  • packages/rule-engine/src/repositories/db-release-repository.ts
  • apps/event-worker/src/workers/update-resource-variable.ts
  • packages/rule-engine/src/repositories/index.ts
  • packages/rule-engine/src/repositories/types.ts
  • packages/rule-engine/src/repositories/get-releases.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/event-worker/src/workers/job-dispatch/github.ts
  • packages/rule-engine/src/index.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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/event-worker/src/workers/evaluate-release-target.ts
  • packages/rule-engine/src/manager/variable-manager.ts
🧬 Code Definitions (2)
apps/event-worker/src/workers/evaluate-release-target.ts (5)
packages/db/src/common.ts (2)
  • Tx (22-22)
  • takeFirst (9-13)
packages/db/src/schema/release.ts (3)
  • versionRelease (42-55)
  • variableRelease (57-65)
  • releaseTarget (18-40)
packages/db/src/schema/job.ts (1)
  • job (74-106)
packages/rule-engine/src/manager/version-manager.ts (1)
  • VersionReleaseManager (15-78)
packages/rule-engine/src/manager/variable-manager.ts (1)
  • VariableReleaseManager (13-73)
packages/rule-engine/src/manager/variable-manager.ts (5)
packages/rule-engine/src/manager/types.ts (2)
  • ReleaseManager (9-11)
  • ReleaseTarget (1-7)
packages/db/src/common.ts (2)
  • Tx (22-22)
  • takeFirst (9-13)
packages/db/src/schema/release.ts (2)
  • releaseTarget (18-40)
  • release (81-95)
packages/rule-engine/src/manager/variables/types.ts (1)
  • MaybeVariable (9-9)
packages/rule-engine/src/manager/variables/variables.ts (1)
  • VariableManager (31-68)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Typecheck
  • GitHub Check: Lint
  • GitHub Check: build (linux/amd64)
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (4)
apps/event-worker/src/workers/evaluate-release-target.ts (2)

19-79: Code looks well structured with appropriate error handling

The createRelease function is well structured with appropriate error handling for missing data. It correctly:

  1. Retrieves necessary data
  2. Checks for required entities
  3. Creates a job with the right configuration
  4. Associates variables with the job
  5. Creates a release record

110-149: Error handling looks appropriate with proper mutex management

The worker implementation correctly handles locking, error catching, and unlocking in a finally block to ensure the mutex is always released, even if an error occurs.

packages/rule-engine/src/manager/variable-manager.ts (2)

21-58: Well-implemented upsert with optimizations to prevent unnecessary database writes

The upsertRelease method efficiently:

  1. Checks if variables have changed before creating a new release
  2. Uses transactions for data consistency
  3. Handles empty variable arrays
  4. Returns appropriate metadata about the operation

68-72:

❓ Verification inconclusive

Consider adding error handling for variable retrieval

The evaluate method doesn't include explicit error handling. If variableManager.getVariables() fails, the error will propagate up. Consider adding try/catch if there are specific recovery actions needed.


🏁 Script executed:

#!/bin/bash
# Check for error handling patterns in similar evaluate methods
rg -A 2 -B 2 "async evaluate\(\)" --type ts

Length of output: 864


Action: Review Error Handling Strategy in evaluate Method

After verifying similar implementations in the codebase (e.g., in packages/rule-engine/src/manager/version-manager.ts), it appears that the absence of local try/catch blocks in the evaluate methods is a deliberate design choice to let errors propagate for centralized handling. If there’s no specific recovery action required immediately at this layer, the current behavior is consistent with other parts of the system. However, if you foresee a need for more granular error management or improved logging/debugging at this level, consider wrapping the variable retrieval in a try/catch block. For example:

  • Option 1 (Keep current approach):
    Rely on the higher-level error handler if the design intent is to allow error propagation.

  • Option 2 (Add error handling):

    async evaluate() {
      try {
        const variableManager = await VariableManager.database(this.releaseTarget);
        const variables = await variableManager.getVariables();
        return { chosenCandidate: variables };
      } catch (error) {
        // Optionally log the error or perform recovery actions
        throw error;
      }
    }

Please verify if the current design’s error propagation aligns with the broader error management strategy of the project, or if adding localized error handling would provide additional benefits.

Copy link
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 (2)
packages/rule-engine/src/manager/__tests__/version-rule-engine.test.ts (2)

16-31: Consider using a descriptive parameter name instead of _

While using _ for unused parameters is common, consider using a more descriptive name like context even if it's unused. This makes the code more self-documenting, especially if future implementations might need to use the context parameter.

-  filter(
-    _: RuleEngineContext,
-    candidates: Version[],
-  ): RuleEngineRuleResult<Version> {
+  filter(
+    context: RuleEngineContext,
+    candidates: Version[],
+  ): RuleEngineRuleResult<Version> {

88-158: Consider adding test for async rule behavior

The RuleEngineFilter interface supports returning either a direct result or a Promise, but the tests only cover the synchronous case. Consider adding a test with a mock rule that returns a Promise to verify that the engine properly handles asynchronous rules.

class AsyncMockRule implements RuleEngineFilter<Version> {
  public readonly name = "AsyncMockRule";
  constructor(private readonly allowedIds: string[]) {}

  async filter(
    context: RuleEngineContext,
    candidates: Version[],
  ): Promise<RuleEngineRuleResult<Version>> {
    // Simulate async operation
    await new Promise(resolve => setTimeout(resolve, 10));
    
    const rejectionReasons = new Map<string, string>();
    const allowedCandidates = candidates.filter((candidate) => {
      if (this.allowedIds.includes(candidate.id)) {
        return true;
      }
      rejectionReasons.set(candidate.id, `Rejected by ${this.name}`);
      return false;
    });

    return { allowedCandidates, rejectionReasons };
  }
}

it("should handle async rules", async () => {
  const asyncRule = new AsyncMockRule(["ver-2", "ver-3"]);
  const engine = new VersionRuleEngine([asyncRule]);
  const result = await engine.evaluate(context, candidates);

  expect(result.chosenCandidate).not.toBeNull();
  expect(result.chosenCandidate?.id).toBe("ver-3"); // Should pick ver-3 as it's the oldest sequential upgrade
  expect(result.rejectionReasons.get("ver-1")).toBe("Rejected by AsyncMockRule");
  expect(result.rejectionReasons.get("ver-4")).toBe("Rejected by AsyncMockRule");
});
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9a1f193 and 1e5a43c.

📒 Files selected for processing (5)
  • packages/rule-engine/src/manager/__tests__/version-rule-engine.test.ts (1 hunks)
  • packages/rule-engine/src/releases.ts (0 hunks)
  • packages/rule-engine/src/rules/__tests__/deployment-deny-rule.test.ts (10 hunks)
  • packages/rule-engine/src/rules/__tests__/version-approval-rule.test.ts (4 hunks)
  • packages/rule-engine/src/types.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • packages/rule-engine/src/releases.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/rule-engine/src/rules/tests/version-approval-rule.test.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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/rule-engine/src/manager/__tests__/version-rule-engine.test.ts
  • packages/rule-engine/src/rules/__tests__/deployment-deny-rule.test.ts
  • packages/rule-engine/src/types.ts
🧬 Code Definitions (3)
packages/rule-engine/src/manager/__tests__/version-rule-engine.test.ts (2)
packages/rule-engine/src/types.ts (3)
  • RuleEngineFilter (43-49)
  • RuleEngineContext (23-28)
  • RuleEngineRuleResult (30-33)
packages/rule-engine/src/manager/version-rule-engine.ts (2)
  • Version (10-16)
  • VersionRuleEngine (27-151)
packages/rule-engine/src/rules/__tests__/deployment-deny-rule.test.ts (2)
packages/rule-engine/src/types.ts (1)
  • RuleEngineContext (23-28)
packages/rule-engine/src/rules/deployment-deny-rule.ts (1)
  • DeploymentDenyRule (57-176)
packages/rule-engine/src/types.ts (1)
packages/db/src/schema/resource.ts (1)
  • Resource (105-105)
🪛 Biome (1.9.4)
packages/rule-engine/src/rules/__tests__/deployment-deny-rule.test.ts

[error] 6-17: Do not export from a test file.

(lint/suspicious/noExportsInTest)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Lint
  • GitHub Check: Typecheck
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (33)
packages/rule-engine/src/rules/__tests__/deployment-deny-rule.test.ts (24)

4-4: LGTM! Updated import correctly.

The import has been updated from DeploymentResourceContext to RuleEngineContext which aligns with the broader refactoring effort in the rule engine.


20-21: LGTM! Variable types successfully updated.

The variables releases and context have been properly updated to reflect the new type definitions (ResolvedRelease[] and RuleEngineContext).


72-77: LGTM! Generic type parameter correctly added.

The DeploymentDenyRule constructor now uses the generic type parameter <ResolvedRelease> and includes the required getCandidateId function, which aligns with the updated interface.


86-89: LGTM! Test assertion updated to use new property name.

The test assertion has been correctly updated to check allowedCandidates.length instead of allowedReleases.length, reflecting the property name change in the result type.


93-99: LGTM! Constructor call correctly includes generic type and callback.

The DeploymentDenyRule constructor now properly uses the generic type parameter and includes the getCandidateId callback function.


108-117: LGTM! Tests updated to verify rejection behavior.

The assertion has been correctly updated to check allowedCandidates.length and the rejection reasons are properly verified.


121-127: LGTM! Custom reason test updated correctly.

The constructor call now includes the generic type parameter and the getCandidateId function, maintaining the test's functionality while adapting to the new API.


144-151: LGTM! Time interval test updated correctly.

The constructor call has been properly updated with the generic type parameter and getCandidateId function while maintaining the time-based rule configuration.


157-160: LGTM! Test assertions properly updated.

The test assertion has been correctly updated to check allowedCandidates.length for the within-denied-period test case.


165-168: LGTM! Test assertions updated for outside-denied-period case.

The assertion has been correctly updated to check allowedCandidates.length for the outside-denied-period test case.


173-176: LGTM! Test assertions updated for evening time case.

The assertion has been correctly updated to check allowedCandidates.length for the evening outside-denied-period test case.


180-187: LGTM! Timezone test updated correctly.

The constructor call now includes the generic type parameter and getCandidateId function while maintaining the timezone-specific configuration.


193-196: LGTM! Timezone within-denied-period test assertion updated.

The assertion has been correctly updated to check allowedCandidates.length for the timezone-specific within-denied-period test case.


201-204: LGTM! Timezone outside-denied-period test assertion updated.

The assertion has been correctly updated to check allowedCandidates.length for the timezone-specific outside-denied-period test case.


207-213: LGTM! DST change test updated correctly.

The constructor call now includes the generic type parameter and getCandidateId function while maintaining the daylight saving time transition test configuration.


225-228: LGTM! DST change before-transition test updated correctly.

The assertion has been correctly updated to check allowedCandidates.length for the DST before-transition test case.


232-235: LGTM! DST change after-transition test updated correctly.

The assertion has been correctly updated to check allowedCandidates.length for the DST after-transition test case.


246-249: LGTM! Morning DST test assertion updated correctly.

The assertion has been correctly updated to check allowedCandidates.length for the morning before-DST-transition test case.


253-256: LGTM! Morning after-DST test assertion updated correctly.

The assertion has been correctly updated to check allowedCandidates.length for the morning after-DST-transition test case.


259-265: LGTM! Fall DST test updated correctly.

The constructor call now includes the generic type parameter and getCandidateId function while maintaining the fall DST transition test configuration.


277-280: LGTM! Fall DST before-transition test assertion updated.

The assertion has been correctly updated to check allowedCandidates.length for the fall DST before-transition test case.


284-287: LGTM! Fall DST after-transition test assertion updated.

The assertion has been correctly updated to check allowedCandidates.length for the fall DST after-transition test case.


298-301: LGTM! Evening fall DST before-transition test assertion updated.

The assertion has been correctly updated to check allowedCandidates.length for the evening fall DST before-transition test case.


305-308: LGTM! Evening fall DST after-transition test assertion updated.

The assertion has been correctly updated to check allowedCandidates.length for the evening fall DST after-transition test case.

packages/rule-engine/src/types.ts (5)

23-28: LGTM! Successfully renamed context type.

The type has been renamed from DeploymentResourceContext to RuleEngineContext while maintaining the same properties, which aligns with the broader refactoring of the rule engine.


30-33: LGTM! Rule result type successfully updated.

The type has been renamed from DeploymentResourceRuleResult to RuleEngineRuleResult<T> and the property has been changed from allowedReleases to allowedCandidates. This makes the type more generic and reusable for different types of candidates.


35-38: LGTM! Selection result type successfully updated.

The type has been renamed from DeploymentResourceSelectionResult to RuleEngineSelectionResult<T> and the property has been changed from chosenRelease to chosenCandidate, making it more generic and consistent with the other type changes.


43-49: LGTM! Filter interface successfully updated.

The interface has been renamed from DeploymentResourceRule to RuleEngineFilter<T> and the method signature has been updated to use the new types. The generic type parameter makes the interface more flexible for different types of candidates.


65-70: LGTM! New RuleEngine type added.

A new type RuleEngine<T> has been added with an evaluate method that processes a context and candidates to determine the best candidate. This addition aligns with the refactoring effort to make the rule engine more generic and flexible.

packages/rule-engine/src/manager/__tests__/version-rule-engine.test.ts (4)

11-31: Good implementation of the mock rule filter

The MockRule implementation correctly follows the RuleEngineFilter interface. It provides a simple way to filter versions by ID and accumulate rejection reasons, which is perfect for testing the VersionRuleEngine.


33-86: Well-structured test setup with comprehensive test data

The test setup is well-organized with appropriate sample data for the context and candidates. The use of beforeEach ensures consistent test data across all test cases.


88-158: Comprehensive test suite covering critical scenarios

The test cases cover a good range of scenarios including sequential rule application, empty candidates, sequential upgrades, and rejection reason accumulation. This provides good coverage for the VersionRuleEngine implementation.


75-76:

❓ Verification inconclusive

Note on metadata boolean representation

The metadata uses string values ("true") for boolean flags. While this works, consider if actual boolean values would be more appropriate for the requiresSequentialUpgrade flag, depending on how the implementation handles these values.

Also applies to: 82-83


🏁 Script executed:

#!/bin/bash
# Check how Version metadata is used in the codebase
grep -r "requiresSequentialUpgrade" --include="*.ts" .

Length of output: 434


Action Required: Revisit the Metadata Boolean Representation

After verifying the usage in the codebase, it appears that the requiresSequentialUpgrade flag is intentionally represented as a string in both tests and implementation:

  • The tests in packages/rule-engine/src/manager/__tests__/version-rule-engine.test.ts (lines 75–76 and 82–83) set the flag as "true".
  • The implementation in packages/rule-engine/src/manager/version-rule-engine.ts explicitly checks v.metadata.requiresSequentialUpgrade === "true".

While this consistent usage works as-is, if you decide that using an actual boolean would be clearer or more type-safe, you'll need to update both the test fixtures and the comparison logic accordingly.

Comment on lines +7 to +17
export type ResolvedRelease = {
id: string;
createdAt: Date;
version: {
id: string;
tag: string;
config: Record<string, any>;
metadata: Record<string, string>;
};
variables: Record<string, unknown>;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Consider moving this type definition out of the test file.

Exporting type definitions from test files is generally not recommended as it could lead to circular dependencies or make the test file part of your public API unintentionally.

#!/bin/bash
# Check if this type is being used elsewhere in the codebase
rg "import.*ResolvedRelease.*from.*deployment-deny-rule\.test" --type=ts
🧰 Tools
🪛 Biome (1.9.4)

[error] 6-17: Do not export from a test file.

(lint/suspicious/noExportsInTest)

Copy link
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

♻️ Duplicate comments (1)
packages/rule-engine/src/manager/version-manager.ts (1)

31-43: Consider concurrency guard for upsertRelease.

No transaction is used here. If multiple requests try to create a release with the same versionId concurrently, two releases may be created. If uniqueness is required, consider wrapping these inserts in a transaction or defining a unique constraint to avoid duplicates.

🧹 Nitpick comments (2)
packages/rule-engine/src/manager/version-manager.ts (2)

64-64: Typo in method name.

Please rename findLastestDeployedVersion to findLatestDeployedVersion for clarity.

-  async findLastestDeployedVersion() {
+  async findLatestDeployedVersion() {

142-153: Caching policy implementation is reasonable.

A basic per-instance in-memory cache may be sufficient unless your system experiences multiple concurrent requests in long-lived processes. If you need a more robust caching approach, you might consider an external store or time-based invalidation strategy.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1e5a43c and 42e6415.

📒 Files selected for processing (1)
  • packages/rule-engine/src/manager/version-manager.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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/rule-engine/src/manager/version-manager.ts
🧬 Code Definitions (1)
packages/rule-engine/src/manager/version-manager.ts (8)
packages/rule-engine/src/manager/types.ts (2)
  • ReleaseManager (9-11)
  • ReleaseTarget (1-7)
packages/db/src/schema/release.ts (2)
  • releaseTarget (18-40)
  • release (81-95)
packages/db/src/schema/deployment-version.ts (1)
  • deploymentVersion (109-134)
packages/db/src/schema/job.ts (1)
  • JobStatus (136-136)
packages/rule-engine/src/db/get-applicable-policies.ts (1)
  • getApplicablePolicies (108-139)
packages/rule-engine/src/utils/merge-policies.ts (1)
  • mergePolicies (29-47)
packages/rule-engine/src/types.ts (1)
  • RuleEngineContext (23-28)
packages/rule-engine/src/manager/version-rule-engine.ts (1)
  • VersionRuleEngine (27-151)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: Typecheck
  • GitHub Check: Lint
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (5)
packages/rule-engine/src/manager/version-manager.ts (5)

1-3: Imports look good.

Clean usage of external libraries. No issues to report.


24-29: Class definition appears correct.

The constructor is public, and dependency injection is clearly handled.


89-133: Logic for version filtering is well-structured.

The use of condition-based filtering and in-place metadata transformation is clear and efficient.


135-140: Sufficient approach to retrieving the latest release.

The ordering by descending creation date is standard and appropriate.


155-176: Confirm robustness of the evaluation logic.

If no versions are found, the engine might return null or an empty result. Verify that the caller of evaluate() properly handles that scenario.

Copy link
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/db/src/schema/release.ts (1)

35-36: Consider addressing the circular reference

There's a circular reference between releaseTarget and release where:

  • releaseTarget references release.id on line 35
  • The new release table (line 100) indirectly references releaseTarget through versionRelease and variableSetRelease

This circular nature is handled by : any type cast, but it would be better to use a forward reference pattern consistently.

-      .references((): any => release.id, { onDelete: "set null" })
+      // Use consistent forward reference pattern
+      .references(() => release.id as any, { onDelete: "set null" })
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 45bc7ba and 567aa7b.

📒 Files selected for processing (2)
  • packages/db/src/schema/release.ts (5 hunks)
  • packages/rule-engine/src/manager/variable-manager.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/rule-engine/src/manager/variable-manager.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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/db/src/schema/release.ts
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Lint
  • GitHub Check: Typecheck
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (8)
packages/db/src/schema/release.ts (8)

17-17: Good addition of workspace import

This import is properly added to support the reference to the workspace table in the new variableValueSnapshot table.


43-56: LGTM: Proper renaming of release table to versionRelease

The renamed table properly captures the specific version-related aspects of a release with appropriate references to deploymentVersion and releaseTarget.


58-66: LGTM: Good addition of variableSetRelease table

This table correctly represents variable set releases with proper references to the releaseTarget table.


68-82: LGTM: Well-structured variableSetReleaseValue table

The table properly links variable set releases with their corresponding variable value snapshots, and includes a unique index constraint to prevent duplicates.


84-98: LGTM: Good design of variableValueSnapshot table

This table effectively implements immutable snapshots of variable values with proper workspace association and uniqueness constraints.


100-111: LGTM: Well-designed release table

The new release table properly ties together both version releases and variable releases, creating a cohesive release object that can be referenced by other parts of the system.


123-144: LGTM: Updated releaseTarget relations

The relations are properly updated to reference the new table names and add connections to both versionReleases and variableReleases.


146-208: LGTM: Well-defined relation objects

All new tables have properly defined relations that accurately reflect the foreign key constraints in the schema. The bidirectional relationships between entities are maintained consistently.

Copy link
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 (2)
packages/db/src/schema/deployment-variables.ts (1)

82-93: Consider updating the validation schema to include the new sensitive field.

The createDeploymentVariableValue schema doesn't explicitly account for the new sensitive field. While this will work due to the default value, consider extending the schema to allow clients to specify this field during creation.

export const createDeploymentVariableValue = createInsertSchema(
  deploymentVariableValue,
  { resourceSelector: resourceCondition },
)
  .omit({
    id: true,
  })
  .extend({
    default: z.boolean().optional(),
+   sensitive: z.boolean().optional().default(false),
  });
packages/db/src/schema/variable-sets.ts (1)

50-80: Consider adding validation schema for variableSetValue.

Unlike deploymentVariableValue, there's no explicit validation schema for creating or updating variableSetValue entries. For consistency, consider adding one that includes the new sensitive field.

export const createVariableSetValue = createInsertSchema(variableSetValue)
  .omit({ id: true })
  .extend({
    sensitive: z.boolean().optional().default(false),
  });

export const updateVariableSetValue = createVariableSetValue.partial();
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4c6b84f and 6f07468.

📒 Files selected for processing (2)
  • packages/db/src/schema/deployment-variables.ts (2 hunks)
  • packages/db/src/schema/variable-sets.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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/db/src/schema/deployment-variables.ts
  • packages/db/src/schema/variable-sets.ts
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Lint
  • GitHub Check: Typecheck
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (3)
packages/db/src/schema/deployment-variables.ts (1)

64-64: LGTM - Addition of sensitive field for handling secure data.

Adding the sensitive field with a default value of false is a good approach for marking variable values that contain sensitive information. This will allow filtering or special handling of sensitive values in the application layer.

packages/db/src/schema/variable-sets.ts (2)

3-10: LGTM - Improved import organization.

Grouping related imports from the same module is a good practice for better code organization and readability.


65-65: LGTM - Consistent implementation of sensitive field.

Adding the sensitive field to the variableSetValue table maintains consistency with the similar field in deploymentVariableValue. This parallel structure will make working with both tables more intuitive.

Copy link
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

♻️ Duplicate comments (1)
apps/event-worker/src/workers/evaluate-release-target.ts (1)

79-83: Improve type safety by adding a proper interface instead of using any

The releaseTarget parameter is typed as any, which reduces type safety. Consider creating or using an existing interface that describes the structure of the release target.

-const handleVersionRelease = async (releaseTarget: any) => {
+const handleVersionRelease = async (releaseTarget: schema.ReleaseTarget & {
+  resource: schema.Resource;
+  environment: schema.Environment;
+  deployment: schema.Deployment;
+}) => {
  const vrm = new VersionReleaseManager(db, {
    ...releaseTarget,
    workspaceId: releaseTarget.resource.workspaceId,
  });
🧹 Nitpick comments (1)
apps/event-worker/src/workers/evaluate-release-target.ts (1)

132-140: Consider handling conflicts explicitly

The onConflictDoNothing() method will silently ignore conflicts when inserting records. This could make debugging issues difficult if you expect a record to be created but it isn't due to a conflict. Consider handling conflicts explicitly or logging when they occur.

  const release = await db
    .insert(schema.release)
    .values({
      versionReleaseId: versionRelease.id,
      variableReleaseId: variableRelease.id,
    })
-   .onConflictDoNothing()
+   .onConflictDoUpdate({
+     target: [schema.release.versionReleaseId, schema.release.variableReleaseId],
+     set: { updatedAt: new Date() }
+   })
    .returning()
    .then(takeFirst);
+   
+  if (!release) {
+    log.warn("Release already exists", {
+      versionReleaseId: versionRelease.id,
+      variableReleaseId: variableRelease.id,
+    });
+  }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6f07468 and 6cf814f.

📒 Files selected for processing (1)
  • apps/event-worker/src/workers/evaluate-release-target.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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/event-worker/src/workers/evaluate-release-target.ts
🧬 Code Graph Analysis (1)
apps/event-worker/src/workers/evaluate-release-target.ts (4)
packages/db/src/common.ts (2)
  • Tx (22-22)
  • takeFirst (9-13)
packages/db/src/schema/release.ts (3)
  • release (110-121)
  • versionRelease (43-57)
  • releaseTarget (19-41)
packages/rule-engine/src/manager/version-manager.ts (1)
  • VersionReleaseManager (24-177)
packages/rule-engine/src/manager/variable-manager.ts (1)
  • VariableReleaseManager (13-76)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Typecheck
  • GitHub Check: Lint
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (5)
apps/event-worker/src/workers/evaluate-release-target.ts (5)

33-35: Consider checking for nested property existence

Before destructuring versionRelease.version.deployment, it would be safer to verify that versionRelease.version exists to prevent potential runtime errors if the data structure is incomplete.

- const { jobAgent, jobAgentConfig: deploymentJobAgentConfig } =
-   versionRelease.version.deployment;
- if (!jobAgent) throw new Error("Deployment has no Job Agent");
+ if (!versionRelease.version) throw new Error("Version data not found");
+ const { jobAgent, jobAgentConfig: deploymentJobAgentConfig } =
+   versionRelease.version.deployment;
+ if (!jobAgent) throw new Error("Deployment has no Job Agent");

19-77: LGTM: createRelease function is well-structured

The renamed and refactored createRelease function is well-structured with clear separation of concerns, proper error handling, and logical flow. The function extracts the necessary information from version and variable releases, creates a job with the correct configuration, and associates variables and release records properly.


85-93: LGTM: Version release handling logic is clear

The version release evaluation and upserting logic is clear and follows a good pattern of evaluating first, then upserting the result. The error handling for missing chosen candidates is appropriately included.


101-106: LGTM: Variable release handling logic is clear

Similar to the version release handling, the variable release logic follows a clean evaluate-then-upsert pattern, maintaining consistency in the codebase.


142-145:

❓ Verification inconclusive

Verify the conditional job creation logic

Job creation only happens in development mode. Is this intentional? If job creation is required in production as well, this condition should be removed. If there's another mechanism for production, consider adding a comment explaining why this is dev-only.


🏁 Script executed:

#!/bin/bash
# Check if there's any other code handling job creation in production

# Search for imports or uses of createRelease function
rg -A 5 "createRelease" --type ts

# Search for dispatchJob channel usage to see if jobs are created elsewhere
rg -A 5 "Channel.DispatchJob" --type ts

Length of output: 23544


Action Required: Clarify Conditional Job Creation in evaluate-release-target

Our search confirms that the code in apps/event-worker/src/workers/evaluate-release-target.ts only creates a job when NODE_ENV equals "development". We found no alternative production logic in this worker; production job creation appears to be handled elsewhere (e.g., via createReleaseJobTriggers in various API routes). Please verify whether this design is intentional. If so, a clarifying comment should be added to explain why this worker is limited to development mode. Otherwise, consider updating the conditional to also create jobs in production.

Comment on lines +95 to +99
const handleVariableRelease = async (releaseTarget: any) => {
const varrm = new VariableReleaseManager(db, {
...releaseTarget,
workspaceId: releaseTarget.resource.workspaceId,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add proper typing to function parameter

Similar to handleVersionRelease, this function uses any for the releaseTarget parameter, which reduces type safety. Use the same interface to ensure consistent typing across functions.

-const handleVariableRelease = async (releaseTarget: any) => {
+const handleVariableRelease = async (releaseTarget: schema.ReleaseTarget & {
+  resource: schema.Resource;
+  environment: schema.Environment;
+  deployment: schema.Deployment;
+}) => {
  const varrm = new VariableReleaseManager(db, {
    ...releaseTarget,
    workspaceId: releaseTarget.resource.workspaceId,
  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleVariableRelease = async (releaseTarget: any) => {
const varrm = new VariableReleaseManager(db, {
...releaseTarget,
workspaceId: releaseTarget.resource.workspaceId,
});
const handleVariableRelease = async (releaseTarget: schema.ReleaseTarget & {
resource: schema.Resource;
environment: schema.Environment;
deployment: schema.Deployment;
}) => {
const varrm = new VariableReleaseManager(db, {
...releaseTarget,
workspaceId: releaseTarget.resource.workspaceId,
});

Copy link
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

♻️ Duplicate comments (3)
apps/event-worker/src/workers/evaluate-release-target.ts (2)

79-79: Use stronger typing for releaseTarget.
Using any reduces type safety and maintainability. A typed interface ensures clarity.


95-95: Use stronger typing for releaseTarget in handleVariableRelease.
This repeats the earlier suggestion for typed parameters.

packages/rule-engine/src/manager/version-manager.ts (1)

31-43: upsertRelease(versionId: string) concurrency concern.
If multiple processes call upsertRelease concurrently with the same versionId, it might create duplicate records unless there's a DB-level constraint. Consider a transaction or a unique index on (releaseTargetId, versionId) to ensure uniqueness.

🧹 Nitpick comments (2)
apps/event-worker/src/workers/evaluate-release-target.ts (2)

30-30: Validate versionRelease.version existence.
Though unlikely, consider checking versionRelease.version or versionRelease.version.deployment for null references to avoid runtime errors, especially if data is malformed.

 if (!versionRelease || !versionRelease.version?.deployment) {
   throw new Error("Failed to get version/deployment");
 }

Also applies to: 32-35


128-136: Insert new release record with onConflictDoNothing().
This prevents duplicate records but might silently skip needed updates if changes occur.

 .onConflictDoNothing()
+.onConflictUseUpdate({
+  versionReleaseId: versionRelease.id,
+  variableReleaseId: variableRelease.id,
+})
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6cf814f and 3999320.

📒 Files selected for processing (3)
  • apps/event-worker/src/workers/evaluate-release-target.ts (1 hunks)
  • packages/rule-engine/src/manager/variable-manager.ts (1 hunks)
  • packages/rule-engine/src/manager/version-manager.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/rule-engine/src/manager/variable-manager.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{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...

**/*.{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/event-worker/src/workers/evaluate-release-target.ts
  • packages/rule-engine/src/manager/version-manager.ts
🧬 Code Graph Analysis (2)
apps/event-worker/src/workers/evaluate-release-target.ts (5)
packages/db/src/common.ts (2)
  • Tx (22-22)
  • takeFirst (9-13)
packages/db/src/schema/release.ts (3)
  • release (110-121)
  • versionRelease (43-57)
  • releaseTarget (19-41)
packages/db/src/schema/job.ts (1)
  • job (74-106)
packages/rule-engine/src/manager/version-manager.ts (1)
  • VersionReleaseManager (24-181)
packages/rule-engine/src/manager/variable-manager.ts (1)
  • VariableReleaseManager (13-88)
packages/rule-engine/src/manager/version-manager.ts (7)
packages/rule-engine/src/manager/types.ts (2)
  • ReleaseManager (9-11)
  • ReleaseTarget (1-7)
packages/db/src/schema/deployment-version.ts (1)
  • deploymentVersion (109-134)
packages/db/src/schema/job.ts (1)
  • JobStatus (136-136)
packages/rule-engine/src/db/get-applicable-policies.ts (1)
  • getApplicablePolicies (108-139)
packages/rule-engine/src/utils/merge-policies.ts (1)
  • mergePolicies (29-47)
packages/rule-engine/src/types.ts (1)
  • RuleEngineContext (23-28)
packages/rule-engine/src/manager/version-rule-engine.ts (1)
  • VersionRuleEngine (27-151)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Typecheck
  • GitHub Check: Lint
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (30)
apps/event-worker/src/workers/evaluate-release-target.ts (20)

17-17: Looks good!
Using a child logger instance improves log contextualization.


19-23: Good use of typed parameters in createRelease.
The function signature properly delineates the release object structure.


25-25: Query correctness check.
Ensures the correct versionReleaseId is fetched. No issues found.


39-46: Variable release check.
Fetching variableRelease with strong constraints ensures fast failure if data is missing. This is appropriate for mission-critical flows.


58-59: Clear comment on adding job variables.
This clarifies the intent and reduces confusion for other developers.


70-70: ReleaseJob relation creation.
Associating the newly-created release record with a job is consistent with the schema. This helps track and retrieve related job metadata.

Also applies to: 72-72


80-83: Instantiation of VersionReleaseManager.
Looks correct and passes the necessary workspace context.


85-87: Evaluation result check.
This early throw is good for preventing usage of undefined data.


88-91: Upsert release logic is straightforward.
No concurrency concerns here since the manager handles creation or reuse.


92-92: Return statement and function closure in handleVersionRelease.
Implementation is concise and clear.

Also applies to: 93-93


96-99: VariableReleaseManager instantiation.
Mirrors how VersionReleaseManager is used, maintaining parity in approach.


101-103: Evaluation and upsert for variables.
Logic is consistent and ensures the correct variable release is created.


105-105: Return statement and function closure in handleVariableRelease.
The function is succinct and clear.

Also applies to: 106-106


112-112: Mutex usage.
Locking the release target ensures concurrency control, well done.


114-114: Comment for retrieving the release target.
Keeps the code self-documenting.


121-121: Eager loading of related data.
Fetching resource, environment, deployment in one query is effective.


123-123: Guard clause for missing release target.
Immediate throw helps troubleshoot missing data quickly.


125-126: Sequential handling of version and variable release.
Both operations are well-isolated in dedicated helper functions.


139-139: Transactional approach for createRelease.
Wrapping in a transaction ensures atomic operations.


144-146: Verbose error logging.
Capturing error and jobId is helpful for troubleshooting.

packages/rule-engine/src/manager/version-manager.ts (10)

1-2: Imports for external libraries.
Everything looks standard.


4-16: Re-exported DB utilities.
All relevant DB methods (and, desc, etc.) are properly imported.


17-23: Import of local rule-engine modules.
Correct references to the internal modules and types.


24-29: Class initialization and property definitions.
The cachedPolicy property ensures we don’t re-fetch policies unnecessarily.


45-62: findLatestVersionMatchingPolicy()
Pulls the relevant policy, then retrieves the highest-createdAt version fulfilling that policy. Logic is clear.


64-91: findLastestDeployedVersion()
Joins multiple tables to find a successful job, ensuring only valid, successfully deployed versions are considered.


93-137: findVersionsForEvaluate()
Combines policy-based conditions, plus a time-window approach using the deployed and matching versions. This is elegantly structured.


139-144: findLatestRelease()
Returns the most recent release for the target. Straightforward usage of descending order.


146-157: getPolicy(forceRefresh)
Caches the merged policy to reduce redundant lookups. This is efficient.


159-181: evaluate()

  1. Retrieves context.
  2. Throws an error if missing.
  3. Fetches & merges policies, then uses the rule engine to evaluate.
    All steps are cohesive, simplifying debugging.

Comment on lines +61 to +65
variableRelease.values.map((v) => ({
jobId: job.id,
key: v.key,
sensitive: v.sensitive,
value: v.value,
key: v.variableValueSnapshot.key,
sensitive: v.variableValueSnapshot.sensitive,
value: v.variableValueSnapshot.value,
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential security concern for sensitive values.
Storing sensitive variables in plain text might pose risks. Consider encrypting or masking sensitive values in the database or restricting access to these fields.

 // Example approach for encryption (pseudo-code illustration)
- value: v.variableValueSnapshot.value,
+ value: encrypt(v.variableValueSnapshot.value),

Also applies to: 68-68

Copy link
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/db/drizzle/0085_sloppy_mister_fear.sql (3)

14-21: [SQL] Creating table "variable_value_snapshot".
The table is established correctly, with a JSONB column for value and a boolean sensitive field defaulting to false. As a minor note, please verify that applying a unique index later on a JSONB column (if needed) meets your requirements for equality.


35-38: [SQL] Adding new columns to tables.
The addition of the sensitive column to both "deployment_variable_value" and "variable_set_value", as well as the new "version_release_id" and "variable_release_id" columns on the "release" table, are clearly necessary for the updated schema.

Note: When adding NOT NULL columns to an existing table, ensure that data migration is handled (e.g., setting default values or backfilling data) to avoid issues with existing rows.


75-76: [SQL] Creating unique indexes.
The unique index on "variable_set_release_value" and the composite unique index on "variable_value_snapshot" are well-defined.

Suggestion: Double-check that a unique index on a JSONB column (i.e., "value") functions as intended for your equality semantics.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6061585 and 76b68c4.

📒 Files selected for processing (2)
  • packages/db/drizzle/0085_sloppy_mister_fear.sql (1 hunks)
  • packages/db/drizzle/meta/_journal.json (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: Typecheck
  • GitHub Check: Lint
  • GitHub Check: build (linux/amd64)
🔇 Additional comments (15)
packages/db/drizzle/0085_sloppy_mister_fear.sql (14)

1-5: [SQL] Creating table "variable_set_release".
The table is defined with a UUID primary key generated by gen_random_uuid() and a default timestamp for created_at. This design appears sound.


7-12: [SQL] Creating table "variable_set_release_value".
The table uses UUIDs with proper defaults and includes a created_at field. The structure is clear and aligns with the new schema requirements.


23-28: [SQL] Creating table "version_release".
This new table effectively captures versioning with appropriate foreign key references. The design is straightforward and consistent with the overall schema modernization.


30-30: [SQL] Dropping legacy table "release_variable".
Removing the legacy table is appropriate for cleaning up the outdated deployment evaluation logic. Verify that downstream code no longer depends on this table.


31-34: [SQL] Dropping foreign key constraints from "release".
The removal of constraints "release_release_target_id_release_target_id_fk" and "release_version_id_deployment_version_id_fk" is in line with the revised schema. Please ensure that all related application logic has been updated to reference the new relationships.


39-43: [SQL] Adding foreign key constraint on "variable_set_release".
The DO block applies a foreign key on "release_target_id" referencing "public"."release_target". The exception handling for a duplicate constraint is a pragmatic approach in migration scripts.


45-49: [SQL] Adding foreign key constraint on "variable_set_release_value" for "variable_set_release_id".
This constraint ensures referential integrity between "variable_set_release_value" and "variable_set_release". The pattern is consistent with other parts of the migration.


51-55: [SQL] Adding foreign key constraint on "variable_set_release_value" for "variable_value_snapshot_id".
The addition of the foreign key is straightforward and follows the established practice in this script.


57-61: [SQL] Adding foreign key constraint on "variable_value_snapshot" for "workspace_id".
This block properly enforces the relationship with the "workspace" table while handling duplicate constraint exceptions.


63-67: [SQL] Adding foreign key constraint on "version_release" for "release_target_id".
The constraint on "version_release" linking to "release_target" is correctly implemented with cascading deletes.


69-73: [SQL] Adding foreign key constraint on "version_release" for "version_id".
This DO block sets up the foreign key to "deployment_version", ensuring integrity between version releases and deployment versions.


77-81: [SQL] Adding foreign key constraint on "release" for "version_release_id".
This constraint adds a necessary relationship between the "release" and "version_release" tables. The use of exception handling helps maintain idempotency in migrations.


83-87: [SQL] Adding foreign key constraint on "release" for "variable_release_id".
The foreign key is set up to link the "release" table with "variable_set_release", aligning with the new relational design.


89-91: [SQL] Dropping obsolete columns from "release" and "release_job".
Removing "release_target_id" and "version_id" from the "release" table, and "created_at" from "release_job", cleans up legacy elements of the schema. Ensure that any dependencies on these columns are properly migrated.

packages/db/drizzle/meta/_journal.json (1)

599-606: [JSON] Adding new journal entry for migration "0085_sloppy_mister_fear".
The new entry with idx: 85 is correctly formatted and integrated into the journal. Verify that the timestamp (when) and tag values accurately reflect the changes in this migration.

@adityachoudhari26 adityachoudhari26 merged commit e2e4039 into main Apr 9, 2025
10 checks passed
@adityachoudhari26 adityachoudhari26 deleted the refactor-rules branch April 9, 2025 00:23
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.

3 participants