Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add directions for publishing to no schema component #6602

Open
wants to merge 14 commits into
base: main
Choose a base branch
from

Conversation

jdolle
Copy link
Collaborator

@jdolle jdolle commented Mar 11, 2025

Background

Closes #6464

Description

Adds publish instructions, which are specific to the project type.
Screenshot 2025-03-10 at 9 28 09 PM
Screenshot 2025-03-10 at 9 28 26 PM

@jdolle jdolle self-assigned this Mar 11, 2025
Copy link
Contributor

coderabbitai bot commented Mar 11, 2025

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Enhanced empty state messaging that now provides actionable recommendations, guiding users on next steps when a schema version is unavailable.
    • Introduced an interactive display for code snippets with a convenient copy-to-clipboard feature for easy command access.
    • Improved icon visuals with flexible sizing for a more refined appearance.

Walkthrough

This pull request introduces a new React component, NoSchemaVersion, which replaces the older noSchemaVersion export. The new component accepts projectType and recommendedAction props to conditionally render messages based on the action recommended. Its usage has been updated across multiple target pages, primarily setting the recommendedAction prop to "publish", with one instance using "check". Additionally, a GraphQL query is modified to fetch project details in one file, and a changeset entry is added for a patch related to the hive component.

Changes

File(s) Summary
packages/web/app/src/components/ui/empty-list.tsx Introduces the new NoSchemaVersion component with props projectType and recommendedAction; adds conditional rendering logic and a children prop to the EmptyList component.
packages/web/app/src/pages/target-*.tsx Updates component invocations to use NoSchemaVersion with the additional recommendedAction prop set to "publish" (and renames usage to PascalCase where applicable).
packages/web/app/src/pages/target-checks.tsx Augments the GraphQL query to include project id and type; updates the NoSchemaVersion component usage with recommendedAction set to "check".
.changeset/fuzzy-plums-repeat.md Adds a new changeset entry for a patch on the hive component with publishing guidance when no schema version is present.

Possibly related PRs

  • Add another Get Your API Game Right section #6495: The changes in the main PR, which involve the introduction and modification of the NoSchemaVersion component, are directly related to the updates made in this PR, as both involve the usage of the NoSchemaVersion component with the addition of the recommendedAction prop.
  • feature: add federation-link-util library #6452: The changes in the main PR, which involve the introduction and modification of the NoSchemaVersion component, are related to the updates in this PR that also involve the usage of the NoSchemaVersion component with the new recommendedAction prop.
  • refactor(libraries): type safe local storage json #6455: The changes in the main PR, which involve the introduction and modification of the NoSchemaVersion component, are related to the updates in this PR that also involve the usage of components and props, specifically the addition of the recommendedAction prop in the NoSchemaVersion component across various files.

🪧 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 resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

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

github-actions bot commented Mar 11, 2025

🚀 Snapshot Release (alpha)

The latest changes of this PR are available as alpha on npm (based on the declared changesets):

Package Version Info
@graphql-hive/apollo 0.38.1-alpha-20250313183737-d145440f39bd98c649b1fe54ca4b1f8a0e967fc8 npm ↗︎ unpkg ↗︎
@graphql-hive/cli 0.49.1-alpha-20250313183737-d145440f39bd98c649b1fe54ca4b1f8a0e967fc8 npm ↗︎ unpkg ↗︎
@graphql-hive/core 0.10.1-alpha-20250313183737-d145440f39bd98c649b1fe54ca4b1f8a0e967fc8 npm ↗︎ unpkg ↗︎
@graphql-hive/envelop 0.35.1-alpha-20250313183737-d145440f39bd98c649b1fe54ca4b1f8a0e967fc8 npm ↗︎ unpkg ↗︎
@graphql-hive/yoga 0.41.1-alpha-20250313183737-d145440f39bd98c649b1fe54ca4b1f8a0e967fc8 npm ↗︎ unpkg ↗︎
hive 6.0.0-alpha-20250313183737-d145440f39bd98c649b1fe54ca4b1f8a0e967fc8 npm ↗︎ unpkg ↗︎

Copy link
Contributor

github-actions bot commented Mar 11, 2025

📚 Storybook Deployment

The latest changes are available as preview in: https://038a5604.hive-storybook.pages.dev

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

🔭 Outside diff range comments (1)
.changeset/fuzzy-plums-repeat.md (1)

1-6: ⚠️ Potential issue

Incomplete changeset documentation.

The changeset file is missing several required elements according to the coding guidelines:

  1. A link to the relevant GitHub issue (show CLI publish command within dashboard #6464)
  2. Example with before/after demonstration
  3. More detailed description of what was changed

Please enhance the changeset description with these details:

---
'hive': patch
---

Added directions for publishing on no schema component
+
+Closes #6464
+
+## Before
+Previously, the "no schema" state didn't provide instructions based on project type.
+
+## After
+Now when a schema hasn't been published yet, we show project-type specific publishing instructions to help users get started.
🧹 Nitpick comments (3)
packages/web/docs/src/components/company-testimonials/index.tsx (1)

170-176: Focus Outline Removal Adjustment.
The addition of the outline-none class to the Tabs.Content component (line 170) effectively removes the default browser focus outline. Please verify that this change does not negatively impact accessibility—especially if under any circumstances this content could receive focus. If a focus indicator is necessary, consider using a conditional style such as focus-visible to maintain keyboard navigation clarity.

packages/web/app/src/pages/target-apps.tsx (1)

261-261: Good implementation of the new component with proper props.

The NoSchemaVersion component is now used correctly with the project type passed as a prop. However, the optional chaining is used here differently compared to target-explorer.tsx - both approaches work but for consistency, consider standardizing the access pattern across files.

-<NoSchemaVersion projectType={data.data?.target?.project?.type} />
+<NoSchemaVersion projectType={data.data?.target?.project?.type ?? null} />
packages/web/docs/src/components/pricing/plans-table.tsx (1)

11-11: Consider updating import path

The icons import uses a relative path from a different directory (../../app/gateway/federation-compatible-benchmarks/icons). Since you've just added CheckmarkIcon and XIcon to the main icons.tsx file, you could import from there instead for better organization.

-import { CheckmarkIcon, XIcon } from '../../app/gateway/federation-compatible-benchmarks/icons';
+import { CheckmarkIcon, XIcon } from '../icons';
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e638e68 and cd72d27.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (25)
  • packages/web/app/src/components/ui/empty-list.tsx (3 hunks)
  • packages/web/app/src/components/ui/input-copy.tsx (2 hunks)
  • packages/web/app/src/pages/target-apps.tsx (3 hunks)
  • packages/web/app/src/pages/target-explorer-deprecated.tsx (3 hunks)
  • packages/web/app/src/pages/target-explorer-type.tsx (3 hunks)
  • packages/web/app/src/pages/target-explorer-unused.tsx (3 hunks)
  • packages/web/app/src/pages/target-explorer.tsx (3 hunks)
  • packages/web/app/src/pages/target-history.tsx (3 hunks)
  • packages/web/app/src/pages/target.tsx (2 hunks)
  • .changeset/fuzzy-plums-repeat.md (1 hunks)
  • .eslintrc.cjs (1 hunks)
  • packages/web/docs/package.json (1 hunks)
  • packages/web/docs/src/app/gateway/federation-compatible-benchmarks/benchmark-table-body.tsx (1 hunks)
  • packages/web/docs/src/app/gateway/federation-compatible-benchmarks/functional-tones.ts (0 hunks)
  • packages/web/docs/src/app/gateway/federation-compatible-benchmarks/index.tsx (1 hunks)
  • packages/web/docs/src/app/layout.tsx (1 hunks)
  • packages/web/docs/src/app/pricing/page.tsx (2 hunks)
  • packages/web/docs/src/components/company-testimonials/index.tsx (1 hunks)
  • packages/web/docs/src/components/icons.tsx (1 hunks)
  • packages/web/docs/src/components/nested-sticky.tsx (1 hunks)
  • packages/web/docs/src/components/pricing/index.tsx (1 hunks)
  • packages/web/docs/src/components/pricing/plans-table.tsx (1 hunks)
  • packages/web/docs/src/components/pricing/pricing-slider.tsx (3 hunks)
  • packages/web/docs/src/components/slider.tsx (2 hunks)
  • packages/web/docs/src/easing-functions.css (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/web/docs/src/app/gateway/federation-compatible-benchmarks/functional-tones.ts
🧰 Additional context used
📓 Path-based instructions (2)
`.changeset/**`: A special folder with the files created/man...

.changeset/**: A special folder with the files created/managed by Changesets tool.
In PRs that already includes this file, please make sure that the content of the markdown file includes:

  • Full description of the change
  • A link to the relevant GitHub issues (if any)
  • Example with before/after
  • If the change is of type major, please ensure that the content have content about migration with before/after.
  • .changeset/fuzzy-plums-repeat.md
`packages/web/docs/**`: The main landing page and documentat...

packages/web/docs/**: The main landing page and documentation website. It's built using TypeScript, Tailwind, NextJS and Nextra.

In PRs that adds a new feature to the product, we would like to make sure that the PR includes the following:

  • Up to date documentation in packages/web/docs/src/content
  • A product update in packages/web/docs/src/app/product-updates
  • packages/web/docs/package.json
  • packages/web/docs/src/app/layout.tsx
  • packages/web/docs/src/app/gateway/federation-compatible-benchmarks/benchmark-table-body.tsx
  • packages/web/docs/src/easing-functions.css
  • packages/web/docs/src/app/pricing/page.tsx
  • packages/web/docs/src/components/icons.tsx
  • packages/web/docs/src/components/slider.tsx
  • packages/web/docs/src/app/gateway/federation-compatible-benchmarks/index.tsx
  • packages/web/docs/src/components/company-testimonials/index.tsx
  • packages/web/docs/src/components/pricing/pricing-slider.tsx
  • packages/web/docs/src/components/nested-sticky.tsx
  • packages/web/docs/src/components/pricing/plans-table.tsx
  • packages/web/docs/src/components/pricing/index.tsx
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: storybook-preview / deployment
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: code-style / eslint-and-prettier
  • GitHub Check: alpha / cli-artifacts
🔇 Additional comments (67)
packages/web/app/src/pages/target.tsx (2)

13-13: Updated import to use the new NoSchemaVersion component.

The import statement has been updated to include the new NoSchemaVersion component instead of the previous noSchemaVersion variable, which aligns with the PR objective of providing project-type specific publishing directions.


172-172: Good implementation of project-type aware no-schema instructions.

The code now renders the NoSchemaVersion component with the project type passed as a prop, allowing for project-specific publishing instructions when no schema version is available.

packages/web/app/src/components/ui/input-copy.tsx (1)

7-7: Good enhancement to InputCopy component with alignment options.

The component now accepts an optional alignment prop that allows for more flexible styling, which will help with the display of publishing instructions.

packages/web/app/src/pages/target-history.tsx (3)

6-6: Updated import to use the new NoSchemaVersion component.

The import statement has been updated to include the new NoSchemaVersion component, consistent with other similar changes across the codebase.


179-182: Good addition of project type to GraphQL query.

Adding the project type to the query provides the necessary data for the NoSchemaVersion component to display project-specific publishing instructions.


275-277: Good implementation of project-type aware no-schema instructions.

The code now conditionally renders the NoSchemaVersion component based on query results and passes the project type as a prop, which aligns with the PR objective of providing project-type specific publishing directions.

packages/web/app/src/pages/target-explorer-type.tsx (3)

22-22: Appropriate component naming update.

Good update from importing the variable noSchemaVersion to the component NoSchemaVersion, which aligns with React component naming conventions (PascalCase).


165-168: Well-structured GraphQL query addition.

Nicely implemented query addition for the project data (id and type). This provides the necessary context for the NoSchemaVersion component.


270-270: Good implementation of the new component with proper props.

The NoSchemaVersion component is now used correctly with the project type passed as a prop. The fallback to null with the nullish coalescing operator is a good defensive programming practice.

packages/web/app/src/components/ui/empty-list.tsx (4)

1-1: Appropriate imports for the new component functionality.

Good job updating the imports to support the new component features - ReactNode for children support and ProjectType for the type safety of the new component.

Also applies to: 3-3, 8-8


15-15: Well-implemented children support for EmptyList.

Adding children support to the EmptyList component makes it more flexible and reusable. The implementation properly types the children prop and correctly renders it in the component.

Also applies to: 20-20, 37-37


25-25: Improved padding consistency.

Changed from py-4 to p-4 to provide consistent padding on all sides of the card.


52-88: Excellent implementation of NoSchemaVersion component with project-specific instructions.

The NoSchemaVersion component is well-structured and provides contextual instructions based on the project type:

  1. It correctly determines if the project is distributed based on the project type.
  2. It provides clear instructions for schema validation and publishing.
  3. The use of InputCopy components enhances user experience by making commands easily copyable.
  4. The conditional rendering logic based on project type ensures the right commands are shown.

This implementation addresses the PR objective of adding "publish instructions specific to the project type."

packages/web/app/src/pages/target-explorer.tsx (3)

19-19: Appropriate component naming update.

Good update from importing the variable noSchemaVersion to the component NoSchemaVersion, which follows React component naming conventions.


138-141: Well-structured GraphQL query addition.

Nicely implemented query addition for the project data (id and type). This provides the necessary context for the NoSchemaVersion component.


276-276: Good implementation of the new component with proper props.

The NoSchemaVersion component is now used correctly with the project type passed as a prop. The fallback to null with the nullish coalescing operator is a good defensive programming practice. Note that the property access is slightly different than in other files (using dot notation instead of optional chaining for project).

packages/web/app/src/pages/target-apps.tsx (2)

9-9: Appropriate component naming update.

Good update from importing the variable noSchemaVersion to the component NoSchemaVersion, following React component naming conventions.


64-67: Well-structured GraphQL query addition.

Nicely implemented query addition for the project data (id and type). This provides the necessary context for the NoSchemaVersion component.

packages/web/docs/src/components/nested-sticky.tsx (5)

14-19: Well-documented purpose and necessity.

The component's documentation clearly explains why this custom implementation is needed instead of using standard CSS position:sticky. This is helpful for future developers who might question why a custom implementation is used.


42-48: Clear state definition and transition documentation.

The code provides a clear explanation of the three positional states and their transitions, making the complex positioning logic easier to understand.


83-104: Good performance optimization with requestAnimationFrame.

The scroll handler correctly uses requestAnimationFrame to optimize performance and prevent excessive DOM operations during rapid scrolling events.


118-128: Proper event listener cleanup.

The component correctly removes event listeners in the cleanup function to prevent memory leaks when the component unmounts.


131-136: Simple and clear component structure.

The component's render method creates a clean DOM structure with placeholder and content divs that work with the positioning logic, keeping the implementation straightforward.

packages/web/app/src/pages/target-explorer-unused.tsx (3)

9-9: Updated import to use new NoSchemaVersion component.

The import has been correctly updated to import the NoSchemaVersion component instead of the previous noSchemaVersion variable/constant.


240-243: Added project information to GraphQL query.

The GraphQL query has been enhanced to fetch project information, including its type, which supports the new feature for providing project-type-specific instructions.


362-362: Implemented NoSchemaVersion with project type.

The component implementation now uses the new NoSchemaVersion component and correctly passes the project type from the query data.

packages/web/app/src/pages/target-explorer-deprecated.tsx (3)

10-10: Updated import to use new NoSchemaVersion component.

The import statement has been updated to use the new NoSchemaVersion component.


168-171: Added project information to GraphQL query.

The GraphQL query now includes project information, consistent with other files in this PR.


310-310: Implemented NoSchemaVersion with proper null handling.

The implementation correctly uses the new NoSchemaVersion component and includes a null check with the nullish coalescing operator (??) to handle cases where project type might be undefined.

.eslintrc.cjs (1)

234-234: Added new 'subheader' class to Tailwind whitelist

This addition allows the use of a custom 'subheader' class in the documentation files, which supports the styling needed for the new publishing directions component.

packages/web/docs/package.json (1)

31-31: Package version update for Tailwind config

Updated @theguild/tailwind-config from a previous version to 0.6.3, which aligns with the UI enhancements being made in this PR.

packages/web/docs/src/app/layout.tsx (1)

20-20: Added import for new easing functions CSS

The new CSS file adds custom easing functions that will likely be used for animations in the new publishing directions component.

packages/web/docs/src/app/pricing/page.tsx (1)

13-13:

❓ Verification inconclusive

Added PlansTable component to pricing page

The PlansTable component enhances the pricing page with a detailed table of plans and features, improving the user experience when comparing different options.

Also applies to: 30-31


🌐 Web query:

Does the PR include documentation updates in packages/web/docs/src/content and a product update in packages/web/docs/src/app/product-updates as required by the coding guidelines?

💡 Result:

To determine whether a pull request (PR) includes required documentation updates and product updates as per coding guidelines, several key factors from industry standards and best practices should be considered:

Documentation Requirements

  1. Coding Guidelines Compliance:

    • Documentation updates in packages/web/docs/src/content should follow structured formats (e.g., README.md, CHANGELOG.md, and API docs) to ensure clarity and maintainability [3][12].
    • Versioned documentation must reflect changes accurately, including updates to dependencies or new features [3][6][9].
  2. Automated Validation:

    • Tools like dart doc (for Flutter) or CI/CD checks often enforce documentation standards, ensuring updates are properly formatted and linked [3][12].

Product Update Requirements

  1. Release Tracking:

    • Product updates in packages/web/docs/src/app/product-updates should align with product requirements document (PRD) principles, including:
      • Clear purpose and scope [2][5][8]
      • Timelines and release criteria [5][8][14]
      • Stakeholder-reviewed feature descriptions [5][18]
  2. Versioning and Dependency Management:

    • Changes to package dependencies (e.g., in pubspec.yaml or build.gradle) must follow semantic versioning rules and include updates to related documentation [3][6][9].
    • Automated PR Services (e.g., in Yocto) ensure linear versioning and compatibility with package managers [6][9].

Verification Steps

To confirm compliance:

  1. Check for:
    • Updated markdown files in /content with API/usage examples [3][12].
    • Product update notes in /product-updates detailing feature scope and timelines [5][8][14].
  2. Validate version bumps in package manifests (e.g., pubspec.yaml, package.json) [6][9][15].
  3. Ensure automated checks (e.g., CI/CD pipelines) pass for documentation and build processes [3][6][12].

If these elements are present and follow the project’s specific coding guidelines, the PR meets the requirements. For exact compliance, cross-reference the PR with the project’s stated documentation policies [12][18].

Citations:


PlansTable Component Integration Confirmed

The addition of the PlansTable component on the pricing page enhances the user experience by providing a detailed comparison of available plans. Please ensure that the PR also includes the required updates as per the coding guidelines:

  • Documentation updates in packages/web/docs/src/content
  • A corresponding product update in packages/web/docs/src/app/product-updates

These updates are essential to maintain compliance with our standards and to provide complete context for this feature.

Applies also to lines 30–31.

packages/web/docs/src/easing-functions.css (1)

1-41: Well-implemented custom easing functions

The CSS file defines two custom easing functions (--hive-ease-overshoot-far and --hive-ease-overshoot-a-bit) using the modern linear() function, which provides precise control over animation timing. These appear to create sophisticated overshoot effects where animations slightly exceed their target before settling.

packages/web/docs/src/components/pricing/index.tsx (1)

292-294: LGTM: Clean markup restructuring

The change wraps the "Shape a custom plan" text and the conditionally displayed "for your business" phrase in a single parent <span>, creating a more semantic HTML structure while preserving the responsive behavior.

packages/web/docs/src/app/gateway/federation-compatible-benchmarks/benchmark-table-body.tsx (5)

49-57: Good migration to Tailwind utility classes

Replacing inline styles with Tailwind CSS utility classes improves maintainability and consistency. The conditional classes based on compatibility percentage are well-structured.


63-65: Consistent styling approach

Good use of the text-positive-dark class for successful test indicators, aligning with the design system approach.


67-70: Consistent styling approach

Good use of the text-critical-dark class for failed test indicators, maintaining consistency with the rest of the application.


73-75: Consistent styling approach

Same improvements as above, applying the text-positive-dark class for passed test suites.


77-80: Consistent styling approach

Same improvements as above, applying the text-critical-dark class for failed test suites.

packages/web/docs/src/app/gateway/federation-compatible-benchmarks/index.tsx (5)

92-93: Consistent use of Tailwind classes

Good replacement of inline styling with the appropriate Tailwind utility class text-positive-dark for passed tests icon.


95-96: Consistent use of Tailwind classes

Good replacement of inline styling with the appropriate Tailwind utility class text-critical-dark for failed tests icon.


99-101: Consistent styling approach

Good use of the bg-positive-bright Tailwind class for perfect compatibility indicator, aligning with the styling updates in the table body component.


103-105: Consistent styling approach

Good use of the bg-warning-bright Tailwind class for moderate compatibility indicator, maintaining consistency across components.


107-109: Consistent styling approach

Good use of the bg-critical-dark Tailwind class for low compatibility indicator, completing the consistent styling approach.

packages/web/docs/src/components/icons.tsx (3)

1-2: Clear documentation comment

Good addition of a comment explaining the distinction between these new icons and the existing ones in the design system. This prevents confusion for other developers.


3-9: Well-implemented CheckmarkIcon component

The CheckmarkIcon implementation follows best practices:

  • Proper typing with React.SVGProps
  • Consistent viewBox and dimensions
  • Using currentColor for fill to inherit parent text color
  • Spreading additional props to the SVG element

This allows for flexible usage and styling throughout the application.


11-17: Well-implemented XIcon component

The XIcon follows the same good implementation pattern as CheckmarkIcon, ensuring consistency between the two related components.

packages/web/docs/src/components/slider.tsx (3)

94-95: Good refactor to use global easing variables

The transition property now uses the global --hive-ease-overshoot-far variable instead of a local CSS custom property, which promotes consistency across the application.


100-101: Consistent update to global easing variable

Similarly, this transition now uses the global --hive-ease-overshoot-a-bit variable, maintaining consistency with the change on line 94.


113-114: Added vertical spacing to button

The addition of my-3 class adds vertical margin to the button when deadZone is defined, improving the visual spacing.

packages/web/docs/src/components/pricing/pricing-slider.tsx (5)

19-19: Updated ref type to match new element

The rootRef type has been changed from HTMLLabelElement to HTMLDivElement to align with the change of the root element from a label to a div.


22-27: Semantic HTML improvement

Changed the root element from a <label> to a <div>, which is more appropriate since this component doesn't directly label a form control. Also simplified the counter-set CSS property.


32-46: Improved component structure with better responsive behavior

The inner structure has been improved with:

  • Adding aria-hidden attribute for accessibility
  • Better class organization for styling
  • Additional div on line 45 that conditionally hides "per month" text on smaller screens

This enhances the responsive behavior of the component.


54-55: Added aria-label for better accessibility

Good addition of an aria-label to the Slider component, which provides context for screen readers about the purpose of the slider control.


60-61: Enhanced counter property with additional styling

The counter property now includes an additional counter-set for price calculation, improving the dynamic styling capabilities of the component.

packages/web/docs/src/components/pricing/plans-table.tsx (11)

1-20: Well-structured imports and component organization

The file has a clean organization with:

  • Client-side directive for Next.js
  • Properly grouped imports from external and internal sources
  • Good separation between React components and custom icons

This creates a maintainable foundation for the pricing table implementation.


21-26: Clear type definitions for plan structure

Good use of TypeScript with a PlanName type and PricingPlan interface to enforce consistent data structure for the pricing plans.


27-65: Well-structured pricing tiers data

The pricing tiers are defined with consistent structure including:

  • Clear plan names
  • Call-to-action buttons with appropriate variants
  • Proper URL destinations for each plan

The TODO comment on line 33 about moving the style to the design system is a good reminder for future improvements.


67-72: Good component setup with state management

The PlansTable component is properly implemented with:

  • useState hook for tracking the active plan
  • Clean props interface with optional className
  • Reusable icon constants for consistent usage throughout the table

This creates a solid foundation for the component.


73-86: Well-structured layout with responsive design

The section layout includes:

  • Proper heading hierarchy
  • Responsive text alignment and spacing
  • Text breaks for different viewport sizes
  • Concise and informative description

This creates a visually appealing introduction to the pricing plans.


90-106: Excellent header implementation with sticky behavior

The table header uses:

  • NestedSticky component for fixed positioning while scrolling
  • Conditional styling when sticky (using CSS attribute selectors)
  • Responsive design with hidden elements on mobile
  • Good semantic structure with proper rounded corners

This creates a polished user experience when scrolling through the pricing table.


107-114: Accessible table structure

Good use of semantic HTML table elements with:

  • Proper thead and tbody structure
  • Screen reader only (sr-only) header text
  • Mapping of plan names to table headers

This ensures the table is accessible to all users.


115-338: Comprehensive feature comparison with organized sections

The table body contains:

  • Logical grouping of features with TableSubheaderRow components
  • Consistent cell structure and styling
  • Clear feature descriptions and values for each plan
  • Appropriate use of icons and links

This provides users with a detailed comparison of the different plans.


346-397: Responsive mobile navigation implementation

The MobileNavbar component:

  • Uses NestedSticky for optimal positioning
  • Implements an interactive plan selector
  • Shows/hides call-to-action buttons based on selected plan
  • Has smooth transitions and focus states

This ensures a good user experience on mobile devices.


399-422: Flexible table cell component with conditional rendering

The PlansTableCell component:

  • Handles visibility based on active plan
  • Uses aria-hidden for accessibility
  • Implements complex conditional styling with Tailwind
  • Supports optional classNames for customization

This creates a reusable component for consistent table cell rendering.


424-441: Well-structured section header component

The TableSubheaderRow component:

  • Has a clear interface definition
  • Uses colSpan to span the entire table width
  • Includes icon, title, and description
  • Has responsive styling for different screen sizes

This creates visually distinct section headers within the pricing table.

docsUrl="/features/schema-registry#publish-a-schema"
/>
);
// @todo consider monolith vs distributed etc
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

TODO comment needs addressing.

There's a TODO comment that should be addressed or at least converted to a more specific task.


🏁 Script executed:

#!/bin/bash
# Let's search for other TODO comments in the codebase to understand the convention

echo "Searching for TODO comments in the codebase..."
rg -n "@todo|TODO" --type ts --type tsx

Length of output: 172


Assuming the output now includes the following line from the file packages/web/app/src/components/ui/empty-list.tsx:

packages/web/app/src/components/ui/empty-list.tsx:51:// @todo consider monolith vs distributed etc

Action Required: Update TODO Comment Specificity

  • Location: packages/web/app/src/components/ui/empty-list.tsx:51
  • Issue: The current TODO comment is too generic. It should be updated to a clearly defined task or removed if no longer applicable.
  • Recommendation: Replace the comment with a specific action item—e.g., clarify what monolith versus distributed architecture considerations need to be addressed—so that the task is actionable and traceable.

🏁 Script executed:

#!/bin/bash
# Re-run the TODO search with proper file extension patterns for TypeScript files
echo "Searching for TODO comments in the codebase for .ts and .tsx files..."
rg -n "@todo|TODO" -g "*.{ts,tsx}"

Length of output: 7423


Action: Update the TODO Comment for Clarity

  • Location: packages/web/app/src/components/ui/empty-list.tsx (line 51)
  • Issue: The current comment (// @todo consider monolith vs distributed etc) is too vague. It should be turned into a specific, actionable task or be removed if it’s no longer relevant.
  • Recommendation: Specify what needs to be evaluated or refactored. For example, update it to something like:
    // TODO: Determine if a monolithic or distributed architecture better suits our scalability needs and document the decision.

Copy link
Contributor

github-actions bot commented Mar 11, 2025

🐋 This PR was built and pushed to the following Docker images:

Targets: build

Platforms: linux/amd64

Image Tag: d145440f39bd98c649b1fe54ca4b1f8a0e967fc8

Docker Bake metadata
{
"app": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/api/health",
          "build-arg:IMAGE_DESCRIPTION": "The app of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/app",
          "build-arg:PORT": "3000",
          "build-arg:RELEASE": "d145440f39bd98c649b1fe54ca4b1f8a0e967fc8",
          "build-arg:SERVICE_DIR_NAME": "@hive/app",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:dockerfile": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:shared": "dmudcr0texb04xthgjyldqbdu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-961973a8-c735-4168-8c29-41b6399c4c2c/builder-961973a8-c735-4168-8c29-41b6399c4c2c0/wieq95okrkc2z3iq78xef6kx5",
  "containerimage.config.digest": "sha256:252a36678799923532a9ffe6097e99b5346dfeb12373f09e96a53e085e413265",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:6fc5405a124b187ea2eca7bde2a9a0174a544b0fd26b59fd4e17cdf2a9882771",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:6fc5405a124b187ea2eca7bde2a9a0174a544b0fd26b59fd4e17cdf2a9882771",
  "image.name": "ghcr.io/graphql-hive/app:d145440f39bd98c649b1fe54ca4b1f8a0e967fc8-amd64,ghcr.io/graphql-hive/app:publish_directions-amd64"
},
"buildx.build.warnings": [
  {
    "vertex": "sha256:4730609c2e57630cfa51d7d8b0aa53ea36076ca7ef586403a97ec020100a4578",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRSRUxFQVNFJyAobGluZSAxMyk=",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 13
        },
        "end": {
          "line": 13
        }
      }
    ]
  },
  {
    "vertex": "sha256:4730609c2e57630cfa51d7d8b0aa53ea36076ca7ef586403a97ec020100a4578",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9ERVNDUklQVElPTicgKGxpbmUgMTQp",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 14
        },
        "end": {
          "line": 14
        }
      }
    ]
  },
  {
    "vertex": "sha256:b670fc0f910ee67bca8a8577a09d12bf8e6fbb784c0317b44bf30634cc573a4c",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRSRUxFQVNFJyAobGluZSAxMik=",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 12
        },
        "end": {
          "line": 12
        }
      }
    ]
  },
  {
    "vertex": "sha256:4730609c2e57630cfa51d7d8b0aa53ea36076ca7ef586403a97ec020100a4578",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9USVRMRScgKGxpbmUgMTIp",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 12
        },
        "end": {
          "line": 12
        }
      }
    ]
  },
  {
    "vertex": "sha256:4730609c2e57630cfa51d7d8b0aa53ea36076ca7ef586403a97ec020100a4578",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDIwKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 20
        },
        "end": {
          "line": 20
        }
      }
    ]
  },
  {
    "vertex": "sha256:4730609c2e57630cfa51d7d8b0aa53ea36076ca7ef586403a97ec020100a4578",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRSRUxFQVNFJyAobGluZSAyMSk=",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 21
        },
        "end": {
          "line": 21
        }
      }
    ]
  },
  {
    "vertex": "sha256:b670fc0f910ee67bca8a8577a09d12bf8e6fbb784c0317b44bf30634cc573a4c",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDEwKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 10
        },
        "end": {
          "line": 10
        }
      }
    ]
  },
  {
    "vertex": "sha256:b670fc0f910ee67bca8a8577a09d12bf8e6fbb784c0317b44bf30634cc573a4c",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9ERVNDUklQVElPTicgKGxpbmUgMTcp",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 17
        },
        "end": {
          "line": 17
        }
      }
    ]
  },
  {
    "vertex": "sha256:4730609c2e57630cfa51d7d8b0aa53ea36076ca7ef586403a97ec020100a4578",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDIxKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 21
        },
        "end": {
          "line": 21
        }
      }
    ]
  },
  {
    "vertex": "sha256:4730609c2e57630cfa51d7d8b0aa53ea36076ca7ef586403a97ec020100a4578",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRQT1JUJyAobGluZSAyMik=",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 22
        },
        "end": {
          "line": 22
        }
      }
    ]
  },
  {
    "vertex": "sha256:b670fc0f910ee67bca8a8577a09d12bf8e6fbb784c0317b44bf30634cc573a4c",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDExKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 11
        },
        "end": {
          "line": 11
        }
      }
    ]
  },
  {
    "vertex": "sha256:b670fc0f910ee67bca8a8577a09d12bf8e6fbb784c0317b44bf30634cc573a4c",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDEyKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 12
        },
        "end": {
          "line": 12
        }
      }
    ]
  },
  {
    "vertex": "sha256:b670fc0f910ee67bca8a8577a09d12bf8e6fbb784c0317b44bf30634cc573a4c",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9USVRMRScgKGxpbmUgMTUp",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 15
        },
        "end": {
          "line": 15
        }
      }
    ]
  },
  {
    "vertex": "sha256:4730609c2e57630cfa51d7d8b0aa53ea36076ca7ef586403a97ec020100a4578",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDIyKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 22
        },
        "end": {
          "line": 22
        }
      }
    ]
  }
],
"commerce": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The commerce service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/commerce",
          "build-arg:PORT": "3010",
          "build-arg:RELEASE": "d145440f39bd98c649b1fe54ca4b1f8a0e967fc8",
          "build-arg:SERVICE_DIR_NAME": "@hive/commerce",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:dockerfile": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:shared": "dmudcr0texb04xthgjyldqbdu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-961973a8-c735-4168-8c29-41b6399c4c2c/builder-961973a8-c735-4168-8c29-41b6399c4c2c0/zpv9q0r0hrmbcvyfth61i5n75",
  "containerimage.config.digest": "sha256:2aa93152cbd90d532eeef606c8772e166e88cc49cbd91a54202659f2f856e825",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:f6eb5cbe72193bdc129ac74060b741d2e1de5c8fc625acd74a692734340e4854",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:f6eb5cbe72193bdc129ac74060b741d2e1de5c8fc625acd74a692734340e4854",
  "image.name": "ghcr.io/graphql-hive/commerce:d145440f39bd98c649b1fe54ca4b1f8a0e967fc8-amd64,ghcr.io/graphql-hive/commerce:publish_directions-amd64"
},
"composition-federation-2": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "Federation 2 Composition Service for GraphQL Hive.",
          "build-arg:IMAGE_TITLE": "graphql-hive/composition-federation-2",
          "build-arg:PORT": "3069",
          "build-arg:RELEASE": "d145440f39bd98c649b1fe54ca4b1f8a0e967fc8",
          "build-arg:SERVICE_DIR_NAME": "@hive/external-composition",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:dockerfile": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:shared": "dmudcr0texb04xthgjyldqbdu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-961973a8-c735-4168-8c29-41b6399c4c2c/builder-961973a8-c735-4168-8c29-41b6399c4c2c0/ypi4y7thgw9mnp8v9uupewpl8",
  "containerimage.config.digest": "sha256:5053489fd870c522a071f8e361b18555b096a481a24166f2783a34b6406a21fd",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:33796f008f2d56aea108d3c45262378588474b6b09fdd9a22a878cc603998158",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:33796f008f2d56aea108d3c45262378588474b6b09fdd9a22a878cc603998158",
  "image.name": "ghcr.io/graphql-hive/composition-federation-2:d145440f39bd98c649b1fe54ca4b1f8a0e967fc8-amd64,ghcr.io/graphql-hive/composition-federation-2:publish_directions-amd64"
},
"emails": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The emails service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/emails",
          "build-arg:PORT": "3006",
          "build-arg:RELEASE": "d145440f39bd98c649b1fe54ca4b1f8a0e967fc8",
          "build-arg:SERVICE_DIR_NAME": "@hive/emails",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:dockerfile": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:shared": "dmudcr0texb04xthgjyldqbdu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-961973a8-c735-4168-8c29-41b6399c4c2c/builder-961973a8-c735-4168-8c29-41b6399c4c2c0/194h5giy5b1mnjxkqi9fvxwnd",
  "containerimage.config.digest": "sha256:5d0a2a6e44caa12905abd6f1bdc1e4248dd6aebfacc480e902bb295d42ca3f7d",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:7b6f2977cfdb33d1cde75009f52b5b051de6bf7a3f4eec511042ba745fae8a16",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:7b6f2977cfdb33d1cde75009f52b5b051de6bf7a3f4eec511042ba745fae8a16",
  "image.name": "ghcr.io/graphql-hive/emails:d145440f39bd98c649b1fe54ca4b1f8a0e967fc8-amd64,ghcr.io/graphql-hive/emails:publish_directions-amd64"
},
"policy": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The policy service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/policy",
          "build-arg:PORT": "3012",
          "build-arg:RELEASE": "d145440f39bd98c649b1fe54ca4b1f8a0e967fc8",
          "build-arg:SERVICE_DIR_NAME": "@hive/policy",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:dockerfile": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:shared": "dmudcr0texb04xthgjyldqbdu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-961973a8-c735-4168-8c29-41b6399c4c2c/builder-961973a8-c735-4168-8c29-41b6399c4c2c0/z9sir3ff5hm4ngp4g922he522",
  "containerimage.config.digest": "sha256:f0c8b50195ad9090c3a17d69c027e0d1d10eca9709eadf22474f3cb76741dbcc",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:e480f80037f7274c469eb7cd32ab9d485838b63c3bf1a7e66bd836496f8a9fbb",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:e480f80037f7274c469eb7cd32ab9d485838b63c3bf1a7e66bd836496f8a9fbb",
  "image.name": "ghcr.io/graphql-hive/policy:d145440f39bd98c649b1fe54ca4b1f8a0e967fc8-amd64,ghcr.io/graphql-hive/policy:publish_directions-amd64"
},
"schema": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The schema service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/schema",
          "build-arg:PORT": "3002",
          "build-arg:RELEASE": "d145440f39bd98c649b1fe54ca4b1f8a0e967fc8",
          "build-arg:SERVICE_DIR_NAME": "@hive/schema",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:dockerfile": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:shared": "dmudcr0texb04xthgjyldqbdu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-961973a8-c735-4168-8c29-41b6399c4c2c/builder-961973a8-c735-4168-8c29-41b6399c4c2c0/ip045b2lotb99jnrhvp6tqjts",
  "containerimage.config.digest": "sha256:81fe5e75b77c804691fd9796fc935b4f7c4dc316aa35e59a935bc12b44b7f03d",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:0fa065c469fd2eb84d7ae7e0813cb2faadae5edb68f619fbb6139a0c0b57ca4f",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:0fa065c469fd2eb84d7ae7e0813cb2faadae5edb68f619fbb6139a0c0b57ca4f",
  "image.name": "ghcr.io/graphql-hive/schema:d145440f39bd98c649b1fe54ca4b1f8a0e967fc8-amd64,ghcr.io/graphql-hive/schema:publish_directions-amd64"
},
"server": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The server service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/server",
          "build-arg:PORT": "3001",
          "build-arg:RELEASE": "d145440f39bd98c649b1fe54ca4b1f8a0e967fc8",
          "build-arg:SERVICE_DIR_NAME": "@hive/server",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:dockerfile": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:shared": "dmudcr0texb04xthgjyldqbdu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-961973a8-c735-4168-8c29-41b6399c4c2c/builder-961973a8-c735-4168-8c29-41b6399c4c2c0/x6o34ag82t57egia2kx6acv33",
  "containerimage.config.digest": "sha256:6f9924b83e87ce71f31c99be3b5afc23267a3ecdd135b8579d3f9aff8a0e237e",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:ec0876a0778c4b8d2bfcfb60f554aaa7dc8bd98209e93451ac02614dbea1d359",
    "size": 2076,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:ec0876a0778c4b8d2bfcfb60f554aaa7dc8bd98209e93451ac02614dbea1d359",
  "image.name": "ghcr.io/graphql-hive/server:d145440f39bd98c649b1fe54ca4b1f8a0e967fc8-amd64,ghcr.io/graphql-hive/server:publish_directions-amd64"
},
"storage": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "migrations.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:IMAGE_DESCRIPTION": "The migrations service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/storage",
          "build-arg:RELEASE": "d145440f39bd98c649b1fe54ca4b1f8a0e967fc8",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:dockerfile": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:shared": "dmudcr0texb04xthgjyldqbdu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-961973a8-c735-4168-8c29-41b6399c4c2c/builder-961973a8-c735-4168-8c29-41b6399c4c2c0/og55xzl691qr3mm2x118j4fbf",
  "containerimage.config.digest": "sha256:4ac21c2b08a86560f79edbd42b6f16a6e4095f87f632b1e08501051d916c0ba2",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:e67baea8466aad431ab1b0e4d25772799dddf02e7536299f08c0d866e7dcd749",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:e67baea8466aad431ab1b0e4d25772799dddf02e7536299f08c0d866e7dcd749",
  "image.name": "ghcr.io/graphql-hive/storage:d145440f39bd98c649b1fe54ca4b1f8a0e967fc8-amd64,ghcr.io/graphql-hive/storage:publish_directions-amd64"
},
"tokens": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The tokens service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/tokens",
          "build-arg:PORT": "3003",
          "build-arg:RELEASE": "d145440f39bd98c649b1fe54ca4b1f8a0e967fc8",
          "build-arg:SERVICE_DIR_NAME": "@hive/tokens",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:dockerfile": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:shared": "dmudcr0texb04xthgjyldqbdu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-961973a8-c735-4168-8c29-41b6399c4c2c/builder-961973a8-c735-4168-8c29-41b6399c4c2c0/7qb6mk76fktnsvcuw631qsst8",
  "containerimage.config.digest": "sha256:08d87db35dac8f438dc5468dafbe0d4a755e5562aa92b45dbe11c26d5453dc31",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:073e5da93d691cf082295c2a62fd201d63fce67105e0ee803ccd851baa9cc4a7",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:073e5da93d691cf082295c2a62fd201d63fce67105e0ee803ccd851baa9cc4a7",
  "image.name": "ghcr.io/graphql-hive/tokens:d145440f39bd98c649b1fe54ca4b1f8a0e967fc8-amd64,ghcr.io/graphql-hive/tokens:publish_directions-amd64"
},
"usage": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The usage ingestor service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/usage",
          "build-arg:PORT": "3006",
          "build-arg:RELEASE": "d145440f39bd98c649b1fe54ca4b1f8a0e967fc8",
          "build-arg:SERVICE_DIR_NAME": "@hive/usage",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:dockerfile": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:shared": "dmudcr0texb04xthgjyldqbdu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-961973a8-c735-4168-8c29-41b6399c4c2c/builder-961973a8-c735-4168-8c29-41b6399c4c2c0/zqmfcxgfync5xyoaxfv4p3r1v",
  "containerimage.config.digest": "sha256:b896def688912b0f0d65401ecd9298fbc0c23a735a5e5fbeefa319d904a687f2",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:c4dd2ad31fcfb289e6a36ec2349316b656b33e765cfad3a5323c4433b1700bbe",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:c4dd2ad31fcfb289e6a36ec2349316b656b33e765cfad3a5323c4433b1700bbe",
  "image.name": "ghcr.io/graphql-hive/usage:d145440f39bd98c649b1fe54ca4b1f8a0e967fc8-amd64,ghcr.io/graphql-hive/usage:publish_directions-amd64"
},
"usage-ingestor": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The usage ingestor service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/usage-ingestor",
          "build-arg:PORT": "3007",
          "build-arg:RELEASE": "d145440f39bd98c649b1fe54ca4b1f8a0e967fc8",
          "build-arg:SERVICE_DIR_NAME": "@hive/usage-ingestor",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:dockerfile": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:shared": "dmudcr0texb04xthgjyldqbdu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-961973a8-c735-4168-8c29-41b6399c4c2c/builder-961973a8-c735-4168-8c29-41b6399c4c2c0/9w30fc1u6kqx6cqqb3onn20k3",
  "containerimage.config.digest": "sha256:8f2f89e4b6947e134d612814e61863c79176b118aa81dfb977de0d91cd861bc9",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:b17dc27ff4ba8b050594849efdfd0c1587e195e874ace23c6e5430fb9107648e",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:b17dc27ff4ba8b050594849efdfd0c1587e195e874ace23c6e5430fb9107648e",
  "image.name": "ghcr.io/graphql-hive/usage-ingestor:d145440f39bd98c649b1fe54ca4b1f8a0e967fc8-amd64,ghcr.io/graphql-hive/usage-ingestor:publish_directions-amd64"
},
"webhooks": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The webhooks ingestor service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/webhooks",
          "build-arg:PORT": "3005",
          "build-arg:RELEASE": "d145440f39bd98c649b1fe54ca4b1f8a0e967fc8",
          "build-arg:SERVICE_DIR_NAME": "@hive/webhooks",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:dockerfile": "dmudcr0texb04xthgjyldqbdu",
          "local-sessionid:shared": "dmudcr0texb04xthgjyldqbdu"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-961973a8-c735-4168-8c29-41b6399c4c2c/builder-961973a8-c735-4168-8c29-41b6399c4c2c0/ybjdonun77ttquyh7263shkpd",
  "containerimage.config.digest": "sha256:32f1f593ddadb049e797ecaa6b1b2aaa0b8e2f4300ff41fade3fc419b60e7c08",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:9b3f318b56fddbdc94c44a693d7f492d1be4c7230241a7579c79b645491da1a4",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:9b3f318b56fddbdc94c44a693d7f492d1be4c7230241a7579c79b645491da1a4",
  "image.name": "ghcr.io/graphql-hive/webhooks:d145440f39bd98c649b1fe54ca4b1f8a0e967fc8-amd64,ghcr.io/graphql-hive/webhooks:publish_directions-amd64"
}
}

@n1ru4l
Copy link
Contributor

n1ru4l commented Mar 11, 2025

We could use a component that is more similar to our documentation code block.

My main critique here:

  • no mono-spaced font
  • on schema tab we should show publish command
  • on checks tab we should show the check command
image

(ignore content the content of the code block)

@jdolle
Copy link
Collaborator Author

jdolle commented Mar 11, 2025

I didnt see any existing component that matched this aesthetic. But I can add monospacing to the input.

Regarding check vs publish, I think on the check page I'd be okay showing just the check command, but for the schema or explorer, I'd prefer showing both check and publish. I think we should encourage best practices and checking the schema prior to publishing makes the most sense.

@jdolle jdolle force-pushed the publish-directions branch from 6714201 to 27f25e4 Compare March 11, 2025 20:23
@jdolle
Copy link
Collaborator Author

jdolle commented Mar 11, 2025

I've adjusted this to use the InlineCode component and separated the check and publish steps.

Screenshot 2025-03-11 at 12 52 35 PM Screenshot 2025-03-11 at 12 52 43 PM

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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6714201 and 2d67ae0.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (27)
  • packages/web/app/src/components/ui/empty-list.tsx (3 hunks)
  • packages/web/app/src/components/ui/input-copy.tsx (1 hunks)
  • packages/web/app/src/pages/target-apps.tsx (3 hunks)
  • packages/web/app/src/pages/target-explorer-deprecated.tsx (3 hunks)
  • packages/web/app/src/pages/target-explorer-type.tsx (3 hunks)
  • packages/web/app/src/pages/target-explorer-unused.tsx (3 hunks)
  • packages/web/app/src/pages/target-explorer.tsx (3 hunks)
  • packages/web/app/src/pages/target-history.tsx (3 hunks)
  • packages/web/app/src/pages/target.tsx (2 hunks)
  • .changeset/fuzzy-plums-repeat.md (1 hunks)
  • packages/web/app/src/pages/target-apps.tsx (1 hunks)
  • .changeset/neat-ladybugs-pay.md (1 hunks)
  • packages/web/app/src/components/ui/input-copy.tsx (1 hunks)
  • packages/web/app/src/pages/target-apps.tsx (1 hunks)
  • packages/web/app/src/pages/target-apps.tsx (0 hunks)
  • packages/web/app/src/components/ui/input-copy.tsx (1 hunks)
  • packages/web/app/src/components/ui/empty-list.tsx (2 hunks)
  • packages/web/app/src/components/ui/input-copy.tsx (2 hunks)
  • packages/web/app/src/pages/target-apps.tsx (1 hunks)
  • packages/web/app/src/pages/target-checks.tsx (3 hunks)
  • packages/web/app/src/pages/target-explorer-deprecated.tsx (1 hunks)
  • packages/web/app/src/pages/target-explorer-type.tsx (1 hunks)
  • packages/web/app/src/pages/target-explorer-unused.tsx (1 hunks)
  • packages/web/app/src/pages/target-explorer.tsx (1 hunks)
  • packages/web/app/src/pages/target-history.tsx (1 hunks)
  • packages/web/app/src/pages/target.tsx (1 hunks)
  • .changeset/neat-ladybugs-pay.md (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .changeset/neat-ladybugs-pay.md
🚧 Files skipped from review as they are similar to previous changes (17)
  • packages/web/app/src/components/ui/input-copy.tsx
  • packages/web/app/src/components/ui/input-copy.tsx
  • .changeset/fuzzy-plums-repeat.md
  • packages/web/app/src/pages/target-explorer-unused.tsx
  • packages/web/app/src/pages/target-apps.tsx
  • packages/web/app/src/components/ui/input-copy.tsx
  • packages/web/app/src/pages/target-apps.tsx
  • packages/web/app/src/pages/target-apps.tsx
  • packages/web/app/src/components/ui/input-copy.tsx
  • packages/web/app/src/pages/target.tsx
  • packages/web/app/src/pages/target-explorer.tsx
  • packages/web/app/src/pages/target-apps.tsx
  • packages/web/app/src/pages/target-apps.tsx
  • packages/web/app/src/pages/target.tsx
  • packages/web/app/src/pages/target-explorer-type.tsx
  • packages/web/app/src/pages/target-explorer-deprecated.tsx
  • packages/web/app/src/pages/target-explorer.tsx
🧰 Additional context used
📓 Path-based instructions (1)
`.changeset/**`: A special folder with the files created/man...

.changeset/**: A special folder with the files created/managed by Changesets tool.
In PRs that already includes this file, please make sure that the content of the markdown file includes:

  • Full description of the change
  • A link to the relevant GitHub issues (if any)
  • Example with before/after
  • If the change is of type major, please ensure that the content have content about migration with before/after.
  • .changeset/neat-ladybugs-pay.md
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: alpha / npm / snapshot
  • GitHub Check: test / unit
  • GitHub Check: typescript / typecheck
  • GitHub Check: storybook-preview / deployment
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: code-style / eslint-and-prettier
🔇 Additional comments (18)
packages/web/app/src/pages/target-explorer-type.tsx (1)

270-273: Clean implementation of the recommended action prop.

The addition of the recommendedAction="publish" prop to the NoSchemaVersion component is well-implemented. This enhancement provides contextual guidance for users when there's no schema version available.

.changeset/neat-ladybugs-pay.md (2)

2-2: Appropriate severity level change to major.

The severity change from minor to major is appropriate given that the changes introduce breaking environment variable requirements for the usage service.


4-17: Well-documented changeset with comprehensive links.

The changeset includes detailed information about the new organization access tokens feature, lists the breaking changes regarding required environment variables, and provides comprehensive links to documentation and migration guides.

🧰 Tools
🪛 LanguageTool

[typographical] ~12-~12: Consider adding a comma here.
Context: ...DIS_TLS_ENABLED`). For more information please refer to the organization access token ...

(PLEASE_COMMA)

packages/web/app/src/pages/target-history.tsx (1)

276-279: Clean implementation of the recommended action prop.

The addition of the recommendedAction="publish" prop to the NoSchemaVersion component is consistent with the approach used in other files and provides appropriate context for users when no schema version is available.

packages/web/app/src/pages/target-checks.tsx (3)

7-7: Good import restructuring.

Properly updated the import to include the NoSchemaVersion component needed for the UI enhancement.


199-202: Appropriate addition of project type data.

Added the project fields needed to support the enhanced NoSchemaVersion component's functionality.


365-374: Effective implementation of the checks tab feedback.

This change nicely implements the feedback to show the check command on the checks tab. The conditional rendering ensures users see relevant instructions based on their context - either the empty list message when a schema check is active or the NoSchemaVersion component with "check" as the recommended action when there are no schema checks.

packages/web/app/src/components/ui/empty-list.tsx (7)

1-1: Added ReactNode import for children support

Good addition of ReactNode to support the new children prop in EmptyList component.


3-3: Import ProjectType for schema type detection

The ProjectType import from graphql schema enables conditional rendering based on federation/stitching vs. monolith projects.


8-8: Added InputCopy import for command display

Good addition of InputCopy component that will be used to display CLI commands in a copyable format.


15-15: Added children prop to EmptyList component

Good enhancement to the EmptyList component, making it more flexible by allowing it to accept and render child elements.

Also applies to: 20-20


25-25: Improved Card padding

Changed padding from py-4 (vertical only) to p-4 (all sides) to ensure consistent spacing around all content.


37-37: Added children rendering

Simple but important addition to render the optional children prop within the EmptyList component.


51-51: Update the vague TODO comment

This TODO comment lacks specificity and remains unaddressed from a previous review.

The comment should be made more specific or removed if no longer applicable. Consider updating it to clearly describe what architectural decisions need to be made regarding monolith vs. distributed systems.

packages/web/app/src/pages/target-explorer-unused.tsx (2)

9-9: Updated NoSchemaVersion import

Updated the import to use the new component-based approach rather than the previous constant.


240-243: Added project type data fetching

Good addition of project fields to the GraphQL query to support the project type-based instructions in NoSchemaVersion.

packages/web/app/src/pages/target-explorer-deprecated.tsx (2)

10-10: Updated NoSchemaVersion import

Updated the import to use the new component-based approach rather than the previous constant.


168-171: Added project type data fetching

Good addition of project fields to the GraphQL query to support the project type-based instructions in NoSchemaVersion.

@n1ru4l
Copy link
Contributor

n1ru4l commented Mar 12, 2025

InlineCode is kind of a relict from the past that we want to stop using (like most components within packages/web/app/src/components/v2. 😅

We can create the component with that aesthetic based on https://github.com/search?q=repo%3Ashuding%2Fnextra%20nextra-code&type=code

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 (7)
packages/web/app/src/components/ui/empty-list.tsx (1)

51-105: Well-implemented NoSchemaVersion component with conditional rendering.

The new component properly implements the requirements from the PR objectives:

  1. Replaces the older export with a proper React component
  2. Uses monospaced font for commands via the Code component
  3. Supports different project types with appropriate command formatting
  4. Conditionally renders instructions based on the recommendedAction prop

The separation of check and publish commands provides a better user experience, as mentioned in the PR feedback.

One suggestion to consider: the duplication of the command display logic between the check and publish sections could potentially be extracted into a helper function for better maintainability.

 export const NoSchemaVersion = ({
   projectType = null,
   recommendedAction = 'none',
 }: {
   projectType: ProjectType | null;
   recommendedAction: 'publish' | 'check' | 'none';
 }): ReactElement => {
   let children: ReactElement | null = null;
+  
+  const renderCommandSection = (message: ReactNode, command: string) => (
+    <>
+      <div className="flex w-full justify-center py-2 text-xs text-gray-500">
+        {message}
+      </div>
+      <div className="flex w-full justify-center">
+        <Code>{command}</Code>
+      </div>
+    </>
+  );
+  
   if (recommendedAction !== 'none') {
     const isDistributed =
       projectType === ProjectType.Federation || projectType === ProjectType.Stitching;

     if (recommendedAction === 'check') {
       children = (
-        <>
-          <div className="flex w-full justify-center py-2 text-xs text-gray-500">
-            It's recommended to check that the schema is valid and compatible with the state of the
-            registry before publishing.
-          </div>
-          <div className="flex w-full justify-center">
-            <Code>
-              {`hive schema:check ${isDistributed ? '--service <service-name> --url <url> ' : ''}<path/schema.graphql>`}
-            </Code>
-          </div>
-        </>
+        renderCommandSection(
+          <>
+            It's recommended to check that the schema is valid and compatible with the state of the
+            registry before publishing.
+          </>,
+          `hive schema:check ${isDistributed ? '--service <service-name> --url <url> ' : ''}<path/schema.graphql>`
+        )
       );
     } else if (recommendedAction === 'publish') {
       children = (
-        <>
-          {isDistributed && (
-            <div className="flex w-full justify-center py-2 text-xs text-gray-500">
-              For distributed systems, it's recommended to publish the schema after the service is
-              deployed.
-            </div>
-          )}
-          <div className="flex w-full justify-center">
-            <Code>
-             {`hive schema:publish ${isDistributed ? '--service <service-name> --url <url> ' : ''}<path/schema.graphql>`}
-            </Code>
-          </div>
-        </>
+        <>
+          {isDistributed && renderCommandSection(
+            <>
+              For distributed systems, it's recommended to publish the schema after the service is
+              deployed.
+            </>,
+            `hive schema:publish ${isDistributed ? '--service <service-name> --url <url> ' : ''}<path/schema.graphql>`
+          )}
+          {!isDistributed && renderCommandSection(
+            null,
+            `hive schema:publish ${isDistributed ? '--service <service-name> --url <url> ' : ''}<path/schema.graphql>`
+          )}
+        </>
       );
     }
   }
packages/web/app/src/lib/hooks/use-timed.ts (2)

3-14: Consider adding type annotation for return value

While as const works, explicit type annotations would improve readability and make the hook's API more self-documenting.

-export function useTimed(wait: number = 1000) {
+export function useTimed(wait: number = 1000): [boolean, () => void] {
  const [timer, setTimer] = useState<NodeJS.Timeout | null>(null);
  const handler = () => {
    if (timer) {
        clearTimeout(timer);
    }
    setTimer(setTimeout(() => {
      setTimer(null)
    }, wait));
  }
  return [timer !== null, handler] as const;
}

5-12: Improve indentation consistency

There's inconsistent indentation in the if block (4 spaces) compared to the rest of the function (2 spaces).

  const handler = () => {
    if (timer) {
-        clearTimeout(timer);
+      clearTimeout(timer);
    }
    setTimer(setTimeout(() => {
      setTimer(null)
    }, wait));
  }
packages/web/app/src/lib/hooks/use-hover.ts (2)

17-26: Consider simplifying element node check

The element node type check can be simplified using instanceof for better readability.

- if (previousNode.current?.nodeType === Node.ELEMENT_NODE) {
+ if (previousNode.current instanceof HTMLElement) {
    previousNode.current.removeEventListener(
      'mouseenter',
      handleMouseEnter
    );
    previousNode.current.removeEventListener(
      'mouseleave',
      handleMouseLeave
    );
  }

Make the same change for the check on line 28.


3-39: Consider adding explicit return type

Adding an explicit return type would improve readability and make the hook's API more self-documenting.

-export function useHover() {
+export function useHover(): [(node: HTMLElement) => void, boolean] {
  const [hovering, setHovering] = useState(false);
  const previousNode = useRef<Node | null>(null);
  
  // rest of implementation
  
  return [customRef, hovering] as const;
}
packages/web/app/src/components/ui/code.tsx (2)

29-43: Improve accessibility for the copy button

The button lacks some accessibility attributes. Adding aria-label and proper states would improve accessibility.

<button
  data-hovering={hovering || copied}
  className="cursor-pointer opacity-0 data-[hovering=true]:transition-opacity data-[hovering=true]:opacity-100 hover:text-orange-600 absolute right-3 top-2 p-2 border border-gray-600 rounded-md"
+ aria-label={copied ? "Copied" : "Copy to clipboard"}
+ aria-pressed={copied}
  onClick={async (ev) => {
    // existing code
  }}
  title="Copy to clipboard"
>
  {copied ? <CheckIcon size={16} /> : <CopyIcon size={16} />}
</button>

13-16: Use meaningful class names instead of utility classes

The component uses utility classes directly for styling. Consider extracting these to a meaningful class name for better maintainability.

Instead of:

<span
  ref={ref}
  className="flex items-center gap-2 break-all rounded-md bg-black p-4 font-mono text-sm relative pr-14 border border-gray-600"
>

Consider creating a separate CSS class or styled component:

<span
  ref={ref}
  className={cn('code-container', className)}
>

This would make the component's styling more maintainable and allow for easier theming.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ef26a2 and 67af3aa.

📒 Files selected for processing (5)
  • packages/web/app/src/components/ui/code.tsx (1 hunks)
  • packages/web/app/src/components/ui/empty-list.tsx (3 hunks)
  • packages/web/app/src/components/ui/icon.tsx (1 hunks)
  • packages/web/app/src/lib/hooks/use-hover.ts (1 hunks)
  • packages/web/app/src/lib/hooks/use-timed.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (11)
  • GitHub Check: alpha / npm / snapshot
  • GitHub Check: storybook-preview / deployment
  • GitHub Check: test / unit
  • GitHub Check: typescript / typecheck
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: code-style / eslint-and-prettier
  • GitHub Check: Mend Security Check
🔇 Additional comments (4)
packages/web/app/src/components/ui/empty-list.tsx (2)

1-8: Clean import additions to support the new component functionality.

The new imports (ReactNode, ProjectType, and Code) properly support the enhanced component functionality. Using the Code component aligns with the feedback to display commands in monospaced font.


10-41: Good enhancement to EmptyList with children support.

The addition of the optional children prop to EmptyList enables dynamic content rendering, which is essential for the new NoSchemaVersion component. The placement of children before the docs link creates a logical UI flow.

packages/web/app/src/components/ui/icon.tsx (1)

234-239: Improved CheckIcon flexibility with optional size prop.

The addition of an optional size prop to the CheckIcon component brings it in line with other icon components in the file (like PlusIcon, CopyIcon, and PackageIcon). This improves component consistency and flexibility, allowing the icon to be resized as needed in different UI contexts.

packages/web/app/src/lib/hooks/use-hover.ts (1)

3-39: Well-implemented hook with proper cleanup

The useHover hook is well-implemented with proper event listener management, callback memoization, and cleanup. It's a good example of managing DOM event listeners in React.

@jdolle
Copy link
Collaborator Author

jdolle commented Mar 13, 2025

Screenshot 2025-03-12 at 11 05 39 PM

@jdolle jdolle requested a review from n1ru4l March 13, 2025 06:09
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)
packages/web/app/src/components/ui/code.tsx (1)

26-33: ⚠️ Potential issue

Add error handling for clipboard operations

The clipboard operation can fail (e.g., if permission is denied), but there's no error handling. Consider adding a try/catch block.

  onClick={async ev => {
    const value = children?.valueOf().toString();
    if (value) {
      ev.preventDefault();
      startCopyTimer();
-     await navigator.clipboard.writeText(value);
+     try {
+       await navigator.clipboard.writeText(value);
+     } catch (error) {
+       console.error('Failed to copy text to clipboard:', error);
+       // Optionally show an error notification to the user
+     }
    }
  }}
🧹 Nitpick comments (3)
packages/web/app/src/components/ui/code.tsx (1)

11-38: Improve accessibility with ARIA attributes

The copy button lacks proper accessibility attributes, which may make it difficult for screen reader users to interact with.

  <button
    data-hovering={hovering || copied}
    className="absolute right-3 top-2 cursor-pointer rounded-md border border-gray-600 p-2 opacity-0 hover:text-orange-600 data-[hovering=true]:opacity-100 data-[hovering=true]:transition-opacity"
+   aria-label={copied ? "Copied to clipboard" : "Copy to clipboard"}
+   role="button"
    onClick={async ev => {
      // ...
    }}
    title="Copy to clipboard"
  >
    {copied ? <CheckIcon size={16} /> : <CopyIcon size={16} />}
  </button>
packages/web/app/src/components/ui/empty-list.tsx (2)

51-57: Mark optional props in the type definition

You've provided default values for projectType and recommendedAction, but their type definitions don't reflect that they're optional parameters.

export const NoSchemaVersion = ({
  projectType = null,
  recommendedAction = 'none',
}: {
-  projectType: ProjectType | null;
-  recommendedAction: 'publish' | 'check' | 'none';
+  projectType?: ProjectType | null;
+  recommendedAction?: 'publish' | 'check' | 'none';
}): ReactElement => {

58-94: Consider extracting common code patterns

There's some repetition in the structure of the code rendering for different actions. Consider extracting a helper function to reduce duplication.

export const NoSchemaVersion = ({
  projectType = null,
  recommendedAction = 'none',
}: {
  projectType: ProjectType | null;
  recommendedAction: 'publish' | 'check' | 'none';
}): ReactElement => {
  let children: ReactElement | null = null;
  if (recommendedAction !== 'none') {
    const isDistributed =
      projectType === ProjectType.Federation || projectType === ProjectType.Stitching;

+   const renderCommandSection = (description: ReactNode, command: string) => (
+     <>
+       {description && (
+         <div className="flex w-full justify-center py-2 text-xs text-gray-500">
+           {description}
+         </div>
+       )}
+       <div className="flex w-full justify-center">
+         <Code>
+           {command}
+         </Code>
+       </div>
+     </>
+   );
+
    if (recommendedAction === 'check') {
      children = (
-       <>
-         <div className="flex w-full justify-center py-2 text-xs text-gray-500">
-           It's recommended to check that the schema is valid and compatible with the state of the
-           registry before publishing.
-         </div>
-         <div className="flex w-full justify-center">
-           <Code>
-             {`hive schema:check ${isDistributed ? '--service <service-name> --url <url> ' : ''}<path/schema.graphql>`}
-           </Code>
-         </div>
-       </>
+       renderCommandSection(
+         "It's recommended to check that the schema is valid and compatible with the state of the registry before publishing.",
+         `hive schema:check ${isDistributed ? '--service <service-name> --url <url> ' : ''}<path/schema.graphql>`
+       )
      );
    } else if (recommendedAction === 'publish') {
      children = (
-       <>
-         {isDistributed && (
-           <div className="flex w-full justify-center py-2 text-xs text-gray-500">
-             For distributed systems, it's recommended to publish the schema after the service is
-             deployed.
-           </div>
-         )}
-         <div className="flex w-full justify-center">
-           <Code>
-             {`hive schema:publish ${isDistributed ? '--service <service-name> --url <url> ' : ''}<path/schema.graphql>`}
-           </Code>
-         </div>
-       </>
+       renderCommandSection(
+         isDistributed ? "For distributed systems, it's recommended to publish the schema after the service is deployed." : null,
+         `hive schema:publish ${isDistributed ? '--service <service-name> --url <url> ' : ''}<path/schema.graphql>`
+       )
      );
    }
  }

  return (
    // ...
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67af3aa and 73c7983.

📒 Files selected for processing (4)
  • packages/web/app/src/components/ui/code.tsx (1 hunks)
  • packages/web/app/src/components/ui/empty-list.tsx (4 hunks)
  • packages/web/app/src/lib/hooks/use-hover.ts (1 hunks)
  • packages/web/app/src/lib/hooks/use-timed.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/web/app/src/lib/hooks/use-timed.ts
  • packages/web/app/src/lib/hooks/use-hover.ts
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: alpha / publish_docker / Build CLI Docker Image
  • GitHub Check: typescript / typecheck
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: code-style / eslint-and-prettier
🔇 Additional comments (4)
packages/web/app/src/components/ui/code.tsx (2)

7-40: Great implementation of the Code component with copy functionality

The component implements a nicely designed code display with copy-to-clipboard functionality. The implementation shows good use of React patterns and hooks.


26-33:

✅ Verification successful

Add fallback handling for Clipboard API

The Clipboard API isn't supported in all browsers. Add a feature check and fallback method to handle unsupported cases.

  onClick={async ev => {
    const value = children?.valueOf().toString();
    if (value) {
      ev.preventDefault();
      startCopyTimer();
-     await navigator.clipboard.writeText(value);
+     if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
+       try {
+         await navigator.clipboard.writeText(value);
+       } catch (error) {
+         console.error('Failed to copy text to clipboard:', error);
+       }
+     } else {
+       // Fallback for browsers without Clipboard API support
+       const textArea = document.createElement('textarea');
+       textArea.value = value;
+       textArea.style.position = 'fixed';
+       document.body.appendChild(textArea);
+       textArea.focus();
+       textArea.select();
+       try {
+         document.execCommand('copy');
+         console.log('Copied using fallback method');
+       } catch (error) {
+         console.error('Fallback clipboard copy failed:', error);
+       }
+       document.body.removeChild(textArea);
+     }
    }
  }}

🌐 Web query:

What's the browser compatibility for the Clipboard API's writeText method?

💡 Result:

The Clipboard API's writeText() method has broad support in modern browsers but varies by version and platform. Here's a detailed breakdown:

Browser Support Overview

  • Chrome: Fully supported from v66+ (desktop and Android) [2][11].
  • Edge: Supported from v79+ [2][11].
  • Firefox: Partial support from v63+, full support from v87+ [2][11].
  • Safari: Supported from v13.1+ (macOS/iOS) [2][11].
  • Opera: Supported from v53+ [2][11].
  • Internet Explorer: No support [1][11].

Key Compatibility Notes

Browser Minimum Supported Version Key Limitations
Chrome 66 Requires HTTPS and user interaction [7][10]
Firefox 63 Initially required dom.events.asyncClipboard flag [2][11]
Safari 13.1 No support in older iOS/macOS versions [2][11]
Android Chrome 97+ Full support in modern Chrome for Android [2][11]

Security Requirements

  • Requires HTTPS except for localhost [7][10].
  • Needs transient user activation (e.g., button click) in Firefox/Safari [10][12].
  • May prompt for permissions in iframes via Permissions Policy [10][12].

Mobile Support

  • iOS Safari: Supported from v13.4+ [11].
  • Chrome for Android: Full support [2].
  • Firefox for Android: Partial support [2].

The API is unsupported in legacy browsers like IE and older mobile browsers (pre-2019). For unsupported environments, fallbacks like document.execCommand('copy') were historically used but are now deprecated [6][7].

Citations:


Clipboard API Fallback Handling Update Verified

The updated code in packages/web/app/src/components/ui/code.tsx (lines 26–33) correctly detects support for the Clipboard API by checking for navigator.clipboard and its writeText function. It then attempts to copy the text using the API with appropriate try–catch error handling. When the Clipboard API isn’t available (which is the case in legacy browsers like Internet Explorer and earlier versions), the code falls back to creating a temporary textarea and using document.execCommand('copy').

This implementation is in line with current browser compatibility data for the Clipboard API’s writeText() method (e.g., supported in Chrome 66+, Edge 79+, Safari 13.1+, and partial in Firefox until v87) and provides a robust solution for environments without native support.

  • File: packages/web/app/src/components/ui/code.tsx
  • Lines: 26–33
  • Changes:
    • Added feature detection for navigator.clipboard.writeText
    • Implemented a fallback using a dynamically created textarea with error handling

This update addresses the critical issue, and no further changes are required.

packages/web/app/src/components/ui/empty-list.tsx (2)

15-20: Good addition of the children prop to EmptyList

Adding the children prop to EmptyList enables more flexible usage and is a good improvement for component composition.


51-105: Good implementation of the NoSchemaVersion component

The component correctly implements the requested features:

  1. Uses the monospaced Code component to display commands
  2. Conditionally renders content based on the recommendedAction prop
  3. Handles different project types appropriately

This addresses the feedback in the PR and provides a better user experience.

Comment on lines +63 to +77
if (recommendedAction === 'check') {
children = (
<>
<div className="flex w-full justify-center py-2 text-xs text-gray-500">
It's recommended to check that the schema is valid and compatible with the state of the
registry before publishing.
</div>
<div className="flex w-full justify-center">
<Code>
{`hive schema:check ${isDistributed ? '--service <service-name> --url <url> ' : ''}<path/schema.graphql>`}
</Code>
</div>
</>
);
} else if (recommendedAction === 'publish') {
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify schema checking is working correctly

Since this is a critical user-facing instruction, let's verify that the schema:check command parameters shown here match what's expected by the Hive CLI.


🏁 Script executed:

#!/bin/bash
# Search for schema:check usage examples in documentation or tests
rg -A 2 "schema:check" --type md

Length of output: 2515


Critical: Schema Check Command Parameters Mismatch

  • In the UI component at packages/web/app/src/components/ui/empty-list.tsx (lines 63–77), the schema check command for distributed schemas conditionally includes --url <url>.
  • However, our CLI documentation (see packages/libraries/cli/README.md) only indicates the use of --service <value> (alongside other optional flags like --registry.endpoint) without mentioning a --url flag.
  • Action required: Please review whether the --url <url> flag is still valid for the Hive CLI. If not, update the command snippet in the UI accordingly (for example, replacing --url <url> with the appropriate flag such as --registry.endpoint <value>, or removing it if unnecessary).

@jdolle jdolle force-pushed the publish-directions branch from 73c7983 to d145440 Compare March 13, 2025 18:36
@jdolle jdolle enabled auto-merge (squash) March 13, 2025 19:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

show CLI publish command within dashboard
2 participants