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

Multiple owners/maintainers #258

Merged
merged 14 commits into from
Dec 10, 2024
Merged

Multiple owners/maintainers #258

merged 14 commits into from
Dec 10, 2024

Conversation

ZenVoich
Copy link
Owner

@ZenVoich ZenVoich commented Nov 18, 2024

fixes #251
fixes #257

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced commands for managing package owners and maintainers (mops owner and mops maintainer).
    • Added functionality to retrieve and manage multiple package owners and maintainers.
    • Enhanced package summary retrieval with improved user validation.
  • Bug Fixes

    • Fixed issues related to the mops watch command when dfx.json is absent.
  • Documentation

    • Added documentation for new commands and the roles of package owners and maintainers.
  • Style

    • Updated styles for displaying owners and maintainers in the UI.
  • Chores

    • Updated the DFX version in the configuration file.

Copy link
Contributor

coderabbitai bot commented Nov 18, 2024

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 eslint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

frontend/components/package/PackageDocs.svelte

Oops! Something went wrong! :(

ESLint: 8.57.0

Error: Failed to load plugin 'svelte' declared in 'frontend/.eslintrc.json': Cannot find module 'svelte/compiler'
Require stack:

  • /node_modules/svelte-eslint-parser/lib/parser/template.js
  • /node_modules/svelte-eslint-parser/lib/parser/index.js
  • /node_modules/svelte-eslint-parser/lib/index.js
  • /node_modules/eslint-plugin-svelte/lib/rules/infinite-reactive-loop.js
  • /node_modules/eslint-plugin-svelte/lib/utils/rules.js
  • /node_modules/eslint-plugin-svelte/lib/index.js
  • /node_modules/@eslint/eslintrc/dist/eslintrc.cjs
    Referenced from: /frontend/.eslintrc.json
    at Module._resolveFilename (node:internal/modules/cjs/loader:1248:15)
    at Module._load (node:internal/modules/cjs/loader:1074:27)
    at TracingChannel.traceSync (node:diagnostics_channel:315:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:217:24)
    at Module.require (node:internal/modules/cjs/loader:1339:12)
    at require (node:internal/modules/helpers:135:16)
    at Object. (/node_modules/svelte-eslint-parser/lib/parser/template.js:4:20)
    at Module._compile (node:internal/modules/cjs/loader:1546:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1691:10)
    at Module.load (node:internal/modules/cjs/loader:1317:32)

Walkthrough

The changes in this pull request enhance the package management system by allowing multiple principals to be assigned as owners and maintainers of a package. The PackagePublisher, Main, and Registry classes have been updated to support these modifications, including the introduction of new methods for managing ownership and maintainership. The CLI commands have been adjusted to reflect these changes, and documentation has been added to guide users on the new functionalities. Additionally, various components in the frontend have been modified to display owners and maintainers appropriately.

Changes

File Path Change Summary
backend/main/PackagePublisher.mo Enhanced package publishing logic, added isNewPackage variable, refined permission checks, streamlined validation for package names, and updated dependency checks.
backend/main/main-canister.mo Introduced ownersByPackage and maintainersByPackage structures, added new public query functions for managing owners and maintainers, and updated backup/restore logic.
backend/main/registry/Registry.mo Replaced packageOwners with new mappings, introduced functions for ownership checks, and updated ownership management logic.
backend/main/registry/getPackageSummary.mo Enhanced user validation in getPackageSummary, updated handling of owner variables.
backend/main/registry/searchInRegistry.mo Modified to support multiple package owners during search operations.
backend/main/types.mo Updated PackageSummary type to include arrays for owners and maintainers, added publisher field.
cli/CHANGELOG.md Updated changelog to reflect removal of mops transfer-ownership command and addition of mops owner and mops maintainers commands.
cli/cli.ts Added commands for managing package owners and maintainers, removed transfer-ownership command.
cli/commands/maintainer.ts Implemented functions for managing maintainers: printMaintainers, addMaintainer, removeMaintainer.
cli/commands/owner.ts Implemented functions for managing owners: printOwners, addOwner, removeOwner.
cli/commands/transfer-ownership.ts Removed file for the now obsolete transfer-ownership command.
cli/commands/watch/parseDfxJson.ts Added safeguards against undefined canisters in getMotokoCanisters functions.
cli/declarations/main/main.did Updated type definitions, added new methods for managing owners and maintainers.
cli/declarations/main/main.did.d.ts Updated interface for Main, added methods for managing owners and maintainers.
cli/declarations/main/main.did.js Updated IDL definitions to include new methods and types for ownership management.
dfx.json Updated DFX version from 0.24.0 to 0.24.2.
docs/docs/articles/03-package-owners-and-maintainers.md New documentation file outlining roles of package owners and maintainers.
docs/docs/cli/2-publish/05-mops-owner.md New documentation for mops owner command detailing its usage.
docs/docs/cli/2-publish/05-mops-transfer-ownership.md Removed documentation for the obsolete mops transfer-ownership command.
docs/docs/cli/2-publish/06-mops-maintainer.md New documentation for mops maintainer command detailing its usage.
frontend/components/package/PackageDocs.svelte Updated scrolling behavior and added console logs for error handling in documentation display.
frontend/components/package/PackageRightPanel.svelte Replaced owner display logic with new sections for owners and maintainers using UserCard.
frontend/components/package/PackageVersionSummary.svelte Added section to display publisher information using UserCard.
frontend/components/package/UserCard.svelte New component for displaying user information with links to profiles.
frontend/declarations/main/main.did Updated type declarations to support new ownership and maintainership structures.
frontend/declarations/main/main.did.d.ts Updated interface definitions to include new ownership management methods.
frontend/declarations/main/main.did.js Updated IDL definitions to reflect new ownership management methods.

Assessment against linked issues

Objective Addressed Explanation
Support multiple principals as owners of a package (251)

Possibly related PRs

  • mops watch #254: The changes in the mops watch PR are related to the overall package management and deployment process, which aligns with the enhancements made in the main PR regarding package publishing and validation logic.

Poem

In the land of code where packages dwell,
Owners and maintainers now work quite well.
With multiple friends, they share the load,
Together they publish, on a bright, new road.
Hopping through changes, with joy they sing,
In this world of packages, teamwork is king! 🐇✨


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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

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

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Experiment)
  • @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.

@ZenVoich ZenVoich marked this pull request as ready for review December 10, 2024 05:59
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: 13

🧹 Outside diff range and nitpick comments (21)
cli/commands/owner.ts (1)

31-49: Extract confirmation prompt to a reusable function

The confirmation prompt logic in addOwner is duplicated in removeOwner. Consider extracting this code into a reusable function to adhere to the DRY (Don't Repeat Yourself) principle and enhance maintainability.

Here's how you might refactor the confirmation prompt:

+async function confirmAction(message: string): Promise<boolean> {
+  const promptsConfig = {
+    onCancel() {
+      console.log('aborted');
+      process.exit(0);
+    },
+  };
+  const { confirm } = await prompts({
+    type: 'confirm',
+    name: 'confirm',
+    message,
+    initial: true,
+  }, promptsConfig);
+  return confirm;
+}

...

 if (!yes) {
-   let promptsConfig = {
-     onCancel() {
-       console.log('aborted');
-       process.exit(0);
-     },
-   };
-
-   let {confirm} = await prompts({
-     type: 'confirm',
-     name: 'confirm',
-     message: `Are you sure you want to add owner ${chalk.yellow(owner)} to ${chalk.yellow(config.package?.name)} package?`,
-     initial: true,
-   }, promptsConfig);
-
-   if (!confirm) {
+   const message = `Are you sure you want to add owner ${chalk.yellow(owner)} to ${chalk.yellow(config.package?.name)} package?`;
+   const confirmed = await confirmAction(message);
+   if (!confirmed) {
      return;
    }
 }

Apply similar changes in removeOwner to use the confirmAction function.

cli/commands/maintainer.ts (2)

31-49: Extract confirmation prompt to a reusable function

The confirmation prompt logic in addMaintainer is duplicated in removeMaintainer. Consider extracting this code into a reusable function to adhere to the DRY (Don't Repeat Yourself) principle and enhance maintainability.

Here's how you might refactor the confirmation prompt:

+async function confirmAction(message: string): Promise<boolean> {
+  const promptsConfig = {
+    onCancel() {
+      console.log('aborted');
+      process.exit(0);
+    },
+  };
+  const { confirm } = await prompts({
+    type: 'confirm',
+    name: 'confirm',
+    message,
+    initial: true,
+  }, promptsConfig);
+  return confirm;
+}

...

 if (!yes) {
-   let promptsConfig = {
-     onCancel() {
-       console.log('aborted');
-       process.exit(0);
-     },
-   };
-
-   let {confirm} = await prompts({
-     type: 'confirm',
-     name: 'confirm',
-     message: `Are you sure you want to add maintainer ${chalk.yellow(maintainer)} to ${chalk.yellow(config.package?.name)} package?`,
-     initial: true,
-   }, promptsConfig);
-
-   if (!confirm) {
+   const message = `Are you sure you want to add maintainer ${chalk.yellow(maintainer)} to ${chalk.yellow(config.package?.name)} package?`;
+   const confirmed = await confirmAction(message);
+   if (!confirmed) {
      return;
    }
 }

Apply similar changes in removeMaintainer to use the confirmAction function.


8-105: Consider consolidating owner and maintainer command logic

The functions in this file are very similar to those in owner.ts. To reduce code duplication and improve maintainability, consider consolidating shared logic into reusable functions or a common module.

backend/main/registry/Registry.mo (2)

211-213: Define constants for maximum owners and maintainers

The maximum number of owners and maintainers is hard-coded as 5. Consider defining constants or configuration parameters for these limits to improve maintainability and flexibility.

Here's how you might implement it:

+ public let MAX_OWNERS = 5;
+ public let MAX_MAINTAINERS = 5;
...
   // In addOwner function
-  if (owners.size() >= 5) {
+  if (owners.size() >= MAX_OWNERS) {
     return #err("Maximum number of owners reached");
   };
...
   // In addMaintainer function
-  if (maintainers.size() >= 5) {
+  if (maintainers.size() >= MAX_MAINTAINERS) {
     return #err("Maximum number of maintainers reached");
   };

Also applies to: 228-230


184-201: Optimize owner and maintainer look-up

The isOwner and isMaintainer functions iterate over arrays to check for membership, which can be inefficient for larger lists. Consider using HashSet or TrieSet for ownersByPackage and maintainersByPackage to improve lookup performance.

Here's how you might refactor the data structures:

- ownersByPackage : TrieMap.TrieMap<PackageName, [Principal]>,
- maintainersByPackage : TrieMap.TrieMap<PackageName, [Principal]>,
+ ownersByPackage : TrieMap.TrieMap<PackageName, TrieSet.TrieSet<Principal>>,
+ maintainersByPackage : TrieMap.TrieMap<PackageName, TrieSet.TrieSet<Principal>>,

Update the related functions accordingly to work with sets.

frontend/declarations/main/main.did (3)

160-163: Review Redundancy Between owner and owners Fields

The PackageSummary record now includes both owner: principal and owners: vec User. Having both fields may introduce redundancy and potential confusion. Consider whether both are necessary or if owners can replace owner to represent multiple owners consistently.


210-215: Review Redundancy in PackageSummary Fields

Similar to previous comments, the PackageSummary record includes both owner, ownerInfo, and owners. To improve maintainability and clarity, consider unifying these fields. For example, if owners provides all necessary owner information, owner and ownerInfo might be redundant.


Line range hint 281-301: Inconsistent Use of PackageName and PackageName__1 Types

Within PackageConfigV3 and PackageConfigV3_Publishing, the name field uses PackageName, while other parts of the code use PackageName__1. Since both types are defined as text, consider standardizing on a single PackageName type to avoid confusion.

frontend/declarations/main/main.did.d.ts (1)

236-241: Assess Redundancy Between owner and owners in PackageSummary

Having both owner and owners fields in PackageSummary may lead to confusion. Evaluate if it's necessary to keep both or if one can be removed in favor of a unified approach.

cli/declarations/main/main.did.d.ts (1)

236-241: Evaluate Necessity of Both owner and owners Fields

Including both owner and owners in PackageSummary may be redundant. Consider unifying these fields to simplify the data model.

backend/main/PackagePublisher.mo (1)

72-74: Simplify Permission Check Logic

The updated permission check allows both owners and maintainers to publish packages, which is correct. However, the condition can be simplified for better readability.

Consider refactoring the condition:

-if (not isNewPackage and not registry.isOwner(config.name, caller) and not registry.isMaintainer(config.name, caller)) {
+if (not isNewPackage and not (registry.isOwner(config.name, caller) or registry.isMaintainer(config.name, caller))) {
     return #err("Only owners and maintainers can publish packages");
 };
docs/docs/cli/2-publish/05-mops-owner.md (3)

24-24: Specify Language Identifier in Code Block

The code block on line 24 lacks a language specifier, which helps with syntax highlighting.

Apply the following change:

-```
+mops owner add <principal>
+```bash
🧰 Tools
🪛 Markdownlint (0.35.0)

24-24: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


31-31: Specify Language Identifier in Code Block

The code block on line 31 should specify the language for clarity.

Apply the following change:

-```
+mops owner remove <principal>
+```bash
🧰 Tools
🪛 Markdownlint (0.35.0)

31-31: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


48-48: Specify Language Identifier in Code Block

Including the language specifier improves readability and complies with markdown standards.

Apply the following change:

-```
+mops owner add 2d2zu-vaaaa-aaaak-qb6pq-cai
+```bash
🧰 Tools
🪛 Markdownlint (0.35.0)

48-48: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

docs/docs/cli/2-publish/06-mops-maintainer.md (3)

24-24: Specify Language Identifier in Code Block

The code block on line 24 is missing a language specifier.

Apply the following change:

-```
+mops maintainer add <principal>
+```bash
🧰 Tools
🪛 Markdownlint (0.35.0)

24-24: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


31-31: Specify Language Identifier in Code Block

Add a language specifier to the code block on line 31 for better syntax highlighting.

Apply the following change:

-```
+mops maintainer remove <principal>
+```bash
🧰 Tools
🪛 Markdownlint (0.35.0)

31-31: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


48-48: Specify Language Identifier in Code Block

Including the language specifier enhances readability and helps with code formatting.

Apply the following change:

-```
+mops maintainer add 2d2zu-vaaaa-aaaak-qb6pq-cai
+```bash
🧰 Tools
🪛 Markdownlint (0.35.0)

48-48: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

frontend/components/package/UserCard.svelte (1)

22-23: Enhance accessibility for social media links

The current implementation could benefit from improved accessibility attributes.

-<img class="github-icon" src="{githubImg}" alt="GitHub logo" loading="lazy" />
+<img class="github-icon" src="{githubImg}" alt="GitHub profile" loading="lazy" aria-hidden="true" />
+<span class="sr-only">GitHub profile for {user.github}</span>

-<img class="twitter-icon" src="{twitterImg}" alt="Twitter logo" loading="lazy" />
+<img class="twitter-icon" src="{twitterImg}" alt="Twitter profile" loading="lazy" aria-hidden="true" />
+<span class="sr-only">Twitter profile for {user.twitter}</span>

Also add this to your styles:

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

Also applies to: 28-29

backend/main/registry/searchInRegistry.mo (1)

44-61: Consider weighted scoring for primary vs secondary owners

The current implementation gives equal weight to all owners in search results, which might not reflect the intended hierarchy.

Consider implementing a weighted scoring system where the primary owner gets higher points than secondary owners. This would help maintain more relevant search results while still supporting multiple owners.

Example approach:

  1. Modify Registry.mo to track primary vs secondary owners
  2. Adjust scoring in search:
    • Primary owner match: 5 points
    • Secondary owner match: 3 points
cli/cli.ts (2)

293-322: Add principal validation for owner management commands.

While the command structure is well-organized, consider adding validation for the principal parameter to ensure it's in the correct format before making the API call.

 ownerCommand
   .command('add <principal>')
   .description('Add package owner')
   .addOption(new Option('--yes', 'Do not ask for confirmation'))
+  .addOption(new Option('--validate', 'Validate principal format').default(true))
   .action(async (data, options) => {
+    if (options.validate && !isPrincipalValid(data)) {
+      console.error('Invalid principal format');
+      process.exit(1);
+    }
     await addOwner(data, options.yes);
   });

324-353: Consider refactoring common command patterns.

The maintainer commands mirror the owner commands, suggesting an opportunity for code reuse. Consider extracting the common command pattern into a reusable function.

function createEntityCommand(entityType: 'owner' | 'maintainer') {
  const command = new Command(entityType)
    .description(`Package ${entityType} management`);

  command
    .command('list')
    .description(`List package ${entityType}s`)
    .action(async () => {
      await (entityType === 'owner' ? printOwners : printMaintainers)();
    });

  command
    .command('add <principal>')
    .description(`Add package ${entityType}`)
    .addOption(new Option('--yes', 'Do not ask for confirmation'))
    .addOption(new Option('--validate', 'Validate principal format').default(true))
    .action(async (data, options) => {
      if (options.validate && !isPrincipalValid(data)) {
        console.error('Invalid principal format');
        process.exit(1);
      }
      await (entityType === 'owner' ? addOwner : addMaintainer)(data, options.yes);
    });

  // ... similar for remove command

  return command;
}

// Usage
program.addCommand(createEntityCommand('owner'));
program.addCommand(createEntityCommand('maintainer'));
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 8e3804f and 6dfdc71.

📒 Files selected for processing (27)
  • backend/main/PackagePublisher.mo (2 hunks)
  • backend/main/main-canister.mo (12 hunks)
  • backend/main/registry/Registry.mo (4 hunks)
  • backend/main/registry/getPackageSummary.mo (2 hunks)
  • backend/main/registry/searchInRegistry.mo (1 hunks)
  • backend/main/types.mo (1 hunks)
  • cli/CHANGELOG.md (1 hunks)
  • cli/cli.ts (2 hunks)
  • cli/commands/maintainer.ts (1 hunks)
  • cli/commands/owner.ts (1 hunks)
  • cli/commands/transfer-ownership.ts (0 hunks)
  • cli/commands/watch/parseDfxJson.ts (1 hunks)
  • cli/declarations/main/main.did (13 hunks)
  • cli/declarations/main/main.did.d.ts (11 hunks)
  • cli/declarations/main/main.did.js (16 hunks)
  • dfx.json (1 hunks)
  • docs/docs/articles/03-package-owners-and-maintainers.md (1 hunks)
  • docs/docs/cli/2-publish/05-mops-owner.md (1 hunks)
  • docs/docs/cli/2-publish/05-mops-transfer-ownership.md (0 hunks)
  • docs/docs/cli/2-publish/06-mops-maintainer.md (1 hunks)
  • frontend/components/package/PackageDocs.svelte (1 hunks)
  • frontend/components/package/PackageRightPanel.svelte (3 hunks)
  • frontend/components/package/PackageVersionSummary.svelte (3 hunks)
  • frontend/components/package/UserCard.svelte (1 hunks)
  • frontend/declarations/main/main.did (13 hunks)
  • frontend/declarations/main/main.did.d.ts (11 hunks)
  • frontend/declarations/main/main.did.js (16 hunks)
💤 Files with no reviewable changes (2)
  • docs/docs/cli/2-publish/05-mops-transfer-ownership.md
  • cli/commands/transfer-ownership.ts
✅ Files skipped from review due to trivial changes (2)
  • dfx.json
  • docs/docs/articles/03-package-owners-and-maintainers.md
🧰 Additional context used
🪛 Markdownlint (0.35.0)
docs/docs/cli/2-publish/06-mops-maintainer.md

24-24: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


31-31: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


48-48: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

docs/docs/cli/2-publish/05-mops-owner.md

24-24: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


31-31: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


48-48: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (40)
frontend/components/package/PackageRightPanel.svelte (2)

75-83: Implementation of owners display looks good

The new "Owners" section correctly iterates over packageDetails.owners and uses the UserCard component to display each owner.


84-93: Conditional display of maintainers is correctly implemented

The "Maintainers" section is only displayed when there are maintainers, and correctly iterates over packageDetails.maintainers using the UserCard component.

frontend/declarations/main/main.did (2)

319-320: Addition of Methods for Managing Owners and Maintainers

The new methods addMaintainer and addOwner enhance the package management capabilities by allowing multiple maintainers and owners. This is a valuable addition aligning with the PR objectives.


358-359: Methods to Retrieve Owners and Maintainers Added Successfully

The inclusion of getPackageOwners and getPackageMaintainers provides necessary functionality for retrieving owner and maintainer information.

frontend/declarations/main/main.did.d.ts (2)

28-28: Consistent Typing for Package Names

As previously mentioned, consider consolidating PackageName and PackageName__1 into a single PackageName type to maintain consistency throughout the codebase.


59-60: New Methods for Ownership Management Added

The addition of addMaintainer and addOwner methods to the Main interface effectively extends ownership management functionalities.

cli/declarations/main/main.did.d.ts (3)

28-28: Consistent Use of PackageName Type

As mentioned earlier, standardize the usage of PackageName and PackageName__1 to a single PackageName type for clarity.


59-60: Added Methods Enhance Ownership Management

The methods addMaintainer and addOwner in the Main interface improve the package management system as per the PR objectives.


317-322: Consistency in Error Types for Result_3

Ensure that Result_3 uses the Err type for the err variant, similar to other Result types, for consistent error handling.

frontend/declarations/main/main.did.js (5)

61-63: Standardize Dependency Name Type

In the DependencyV2 record, the name field uses PackageName. Ensure that this is consistent with other parts of the code that use PackageName__1.


275-282: Inconsistent Use of PackageName in Config Records

PackageConfigV3_Publishing uses name: PackageName, whereas other places use PackageName__1. For clarity and consistency, consider using a single PackageName type.


312-313: New Owner and Maintainer Management Functions Added

The functions addMaintainer and addOwner enhance the Main service with necessary ownership management capabilities.


380-389: Addition of Functions to Retrieve Owners and Maintainers

The new functions getPackageOwners and getPackageMaintainers are appropriately added to facilitate access to ownership information.


419-424: Ensure Consistent Error Handling in removeMaintainer and removeOwner

The removeMaintainer and removeOwner functions return Result_3, which should consistently use the Err type for the err variant as previously discussed.

backend/main/PackagePublisher.mo (2)

76-81: Consider Consistent Validation for Existing Packages

Currently, the validation for invalid characters '.' and '_' in package names is only applied to new packages. This means existing packages with these characters are allowed without any warnings.

Ensure that this is the intended behavior. If you want to discourage the use of these characters in all packages, consider issuing a warning for existing packages or handling it accordingly.


105-105: Prevent Naming Conflicts with GitHub Dependencies

The check correctly identifies conflicts between GitHub dependencies and existing packages in the Mops registry, preventing potential naming collisions.

backend/main/main-canister.mo (7)

65-67: Initialize Owners and Maintainers Data Structures

The introduction of ownersByPackage and maintainersByPackage enhances the management of package ownership.


495-501: Add Public Queries for Owners and Maintainers

Providing public query functions getPackageOwners and getPackageMaintainers allows users to retrieve the lists of owners and maintainers for a package.


503-516: Implement Functions to Manage Owners and Maintainers

The addition of addOwner, addMaintainer, removeOwner, and removeMaintainer functions enables modification of package ownership and maintainership as intended.


552-556: Include New Data Structures in Backup

The backup now includes ownersByPackage and maintainersByPackage, ensuring these new data structures are preserved.

Confirm that the backup process correctly handles these additions and maintains backward compatibility.


582-598: Update Backup Process to Handle New Variables

The backup function uploads chunks for the new data structures. Ensure all necessary data is included, and the versioning (v8) is correctly managed.


Line range hint 608-666: Handle New Data Structures in Restore Process

The restore function now processes the new backup format, including owners and maintainers. Verify that all data is accurately restored without corruption.


734-745: ⚠️ Potential issue

Ensure Accurate Migration in Post-Upgrade

The migration from packageOwners to ownersByPackage converts single owners into lists. It's crucial to handle this migration carefully to avoid data loss.

Consider adding error handling or logging to detect any issues during the migration process.

Ensure that all existing owners are correctly migrated to the new structure.

cli/commands/watch/parseDfxJson.ts (1)

54-56: Verify if defensive programming is necessary given the type definition

While the defensive programming using dfxJson.canisters || {} is good practice, the DfxConfig type suggests that canisters is a required field. Let's verify if this protection is necessary.

Also applies to: 61-63

✅ Verification successful

Based on the dfx.schema.json, I can now generate the final response since it clearly shows that the canisters field in dfx.json can be null according to the schema:

"canisters": {
  "description": "Mapping between canisters and their settings.",
  "type": [
    "object",
    "null"
  ],
  ...
}

Defensive programming is necessary and correct

The defensive programming using dfxJson.canisters || {} is necessary and correct since the schema explicitly allows the canisters field to be null. This null-safety ensures the code handles all valid dfx.json configurations properly.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for dfx.json files in the codebase to verify if canisters field is optional
fd -e json -x sh -c 'echo "=== {} ==="; cat {} | jq "has(\"canisters\")"' \;

Length of output: 1621


Script:

#!/bin/bash
# Let's check the DfxConfig type definition and any related interfaces
rg -A 10 "interface DfxConfig" 

# Also check for any dfx.json schema definitions
cat dfx.schema.json

Length of output: 37841

frontend/components/package/PackageVersionSummary.svelte (3)

8-8: LGTM: Clean import addition

The UserCard import is properly placed with other component imports.


70-72: LGTM: Well-structured publisher information display

The publisher section is properly implemented using the UserCard component with appropriate compact mode.


95-100: LGTM: Consistent styling

The publisher section styling follows the existing patterns in the file and provides appropriate spacing and font size.

cli/CHANGELOG.md (1)

3-8: LGTM: Well-documented changes

The changelog entries clearly document:

  • The removal of the transfer-ownership command
  • The addition of new owner and maintainers commands with documentation links
  • An unrelated bug fix

The format is consistent with the rest of the changelog.

backend/main/registry/getPackageSummary.mo (3)

3-4: LGTM: Required imports added

The Array and Principal imports are necessary for the new functionality.


29-39: LGTM: Comprehensive user validation

Good implementation of user validation for publisher, owners, and maintainers.


46-49: LGTM: Clean implementation of backward compatibility

The changes maintain backward compatibility by:

  • Keeping the legacy owner field
  • Adding new arrays for owners and maintainers
backend/main/types.mo (2)

92-93: LGTM: Proper handling of legacy fields.

The fields are correctly marked as legacy while being retained for backward compatibility.


94-95: LGTM: New fields align with multiple ownership model.

The addition of owners, maintainers, and publisher fields properly implements the multiple ownership model requested in issue #251.

Also applies to: 98-98

cli/declarations/main/main.did (2)

160-166: LGTM: Type definitions properly updated for multiple ownership.

The PackageSummary type correctly includes the new fields while maintaining backward compatibility with legacy fields.


319-320: LGTM: Comprehensive owner/maintainer management methods.

The service interface provides a complete set of methods for managing owners and maintainers:

  • addOwner/removeOwner for ownership management
  • addMaintainer/removeMaintainer for maintainer management

Also applies to: 378-379

frontend/components/package/PackageDocs.svelte (2)

258-259: LGTM: Improved error handling with logging.

Added appropriate logging when a definition is not found, helping with debugging.


261-264: LGTM: Better cross-browser compatibility for scrolling.

Changed to document.scrollingElement.scroll which provides better cross-browser compatibility compared to document.body.scroll.

cli/cli.ts (1)

29-30: LGTM!

The new imports for owner and maintainer management functions are properly organized.

cli/declarations/main/main.did.js (2)

2-3: LGTM!

The new type definitions for PackageName__1 and Result_3 are properly structured.


312-313: LGTM!

The new service methods for owner and maintainer management are well-defined and follow the existing pattern.

Also applies to: 419-424

Comment on lines +29 to +30
let principal = Principal.fromText(owner);

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle exceptions when parsing Principal from owner string

Principal.fromText(owner) could throw an error if the input owner is not a valid principal string. Consider wrapping this call in a try-catch block to handle invalid input gracefully.

Apply this diff to handle the exception:

 let principal;
- principal = Principal.fromText(owner);
+ try {
+   principal = Principal.fromText(owner);
+ } catch (e) {
+   console.error(chalk.red('Invalid Principal: ') + owner);
+   process.exit(1);
+ }
📝 Committable suggestion

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

Suggested change
let principal = Principal.fromText(owner);
let principal;
try {
principal = Principal.fromText(owner);
} catch (e) {
console.error(chalk.red('Invalid Principal: ') + owner);
process.exit(1);
}

Comment on lines +71 to +72
let principal = Principal.fromText(owner);

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle exceptions when parsing Principal from owner string

Principal.fromText(owner) could throw an error if the input owner is not a valid principal string. Consider wrapping this call in a try-catch block to handle invalid input gracefully.

Apply this diff to handle the exception:

 let principal;
- principal = Principal.fromText(owner);
+ try {
+   principal = Principal.fromText(owner);
+ } catch (e) {
+   console.error(chalk.red('Invalid Principal: ') + owner);
+   process.exit(1);
+ }
📝 Committable suggestion

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

Suggested change
let principal = Principal.fromText(owner);
let principal;
try {
principal = Principal.fromText(owner);
} catch (e) {
console.error(chalk.red('Invalid Principal: ') + owner);
process.exit(1);
}

Comment on lines +29 to +30
let principal = Principal.fromText(maintainer);

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle exceptions when parsing Principal from maintainer string

Principal.fromText(maintainer) could throw an error if the input maintainer is not a valid principal string. Consider wrapping this call in a try-catch block to handle invalid input gracefully.

Apply this diff to handle the exception:

 let principal;
- principal = Principal.fromText(maintainer);
+ try {
+   principal = Principal.fromText(maintainer);
+ } catch (e) {
+   console.error(chalk.red('Invalid Principal: ') + maintainer);
+   process.exit(1);
+ }
📝 Committable suggestion

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

Suggested change
let principal = Principal.fromText(maintainer);
let principal;
try {
principal = Principal.fromText(maintainer);
} catch (e) {
console.error(chalk.red('Invalid Principal: ') + maintainer);
process.exit(1);
}

Comment on lines +71 to +72
let principal = Principal.fromText(maintainer);

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle exceptions when parsing Principal from maintainer string

Principal.fromText(maintainer) could throw an error if the input maintainer is not a valid principal string. Consider wrapping this call in a try-catch block to handle invalid input gracefully.

Apply this diff to handle the exception:

 let principal;
- principal = Principal.fromText(maintainer);
+ try {
+   principal = Principal.fromText(maintainer);
+ } catch (e) {
+   console.error(chalk.red('Invalid Principal: ') + maintainer);
+   process.exit(1);
+ }
📝 Committable suggestion

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

Suggested change
let principal = Principal.fromText(maintainer);
let principal;
try {
principal = Principal.fromText(maintainer);
} catch (e) {
console.error(chalk.red('Invalid Principal: ') + maintainer);
process.exit(1);
}

Comment on lines +109 to +110
err: text;
ok;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ensure Consistent Usage of Error Types in Result_3

In Result_3, the err variant is of type text, whereas in other Result types, err is of type Err. To maintain consistency and clarity, consider defining Result_3 as:

-type Result_3 = 
-  variant {
-    err: text;
-    ok;
-  };
+type Result_3 = 
+  variant {
+    err: Err;
+    ok: null;
+  };

This change aligns Result_3 with the other Result types by using the Err type and explicitly specifying ok as null.

📝 Committable suggestion

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

Suggested change
err: text;
ok;
type Result_3 =
variant {
err: Err;
ok: null;
};

Comment on lines +11 to +33
<div class="user-card" class:compact={compact}>
<div class="value user">
{#if user.name}
<a class="value" href="/search/owner:{user.name}" use:link>{user.name}</a> <small class="principal-text">{user.id}</small>
{:else}
<a class="value" href="/search/owner:{user.id}" use:link>{user.id}</a>
{/if}
</div>
{#if !compact}
{#if user.github}
<a class="value with-icon" href="https://github.com/{user.github}" target="_blank">
<img class="github-icon" src="{githubImg}" alt="GitHub logo" loading="lazy" />
{user.github}
</a>
{/if}
{#if user.twitter}
<a class="value with-icon" href="https://twitter.com/{user.twitter}" target="_blank">
<img class="twitter-icon" src="{twitterImg}" alt="Twitter logo" loading="lazy" />
{user.twitter}
</a>
{/if}
{/if}
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for social media profiles

The component should handle cases where social media profiles might be invalid or inaccessible.

Consider adding error states and validation:

<script lang="ts">
  export let user: User;
  export let compact = false;
+ let githubError = false;
+ let twitterError = false;
+
+ function handleImageError(type: 'github' | 'twitter') {
+   if (type === 'github') githubError = true;
+   if (type === 'twitter') twitterError = true;
+ }
</script>

{#if !compact}
  {#if user.github && !githubError}
    <a class="value with-icon" href="https://github.com/{user.github}" target="_blank" rel="noopener noreferrer">
-     <img class="github-icon" src="{githubImg}" alt="GitHub logo" loading="lazy" />
+     <img 
+       class="github-icon" 
+       src="{githubImg}" 
+       alt="GitHub profile" 
+       loading="lazy"
+       on:error={() => handleImageError('github')}
+     />
      {user.github}
    </a>
  {/if}

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

Comment on lines +48 to 52
for (ownerId in owners.vals()) {
if (searchOwnerNameOrId == Principal.toText(ownerId)) {
sortingPoints += 3;
};
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize owner search performance

The current implementation iterates over owners multiple times and might impact performance with a large number of owners.

Consider consolidating the owner search into a single pass:

-for (ownerId in owners.vals()) {
-  if (searchOwnerNameOrId == Principal.toText(ownerId)) {
-    sortingPoints += 3;
-  };
-};
-// search by owner name
-else {
-  for (ownerId in owners.vals()) {
-    let ownerInfo = users.getUserOpt(ownerId)!;
-    if (searchOwnerNameOrId == ownerInfo.name) {
-      sortingPoints += 3;
-    };
-  };
+let found = false;
+label search for (ownerId in owners.vals()) {
+  if (Option.isSome(PrincipalExt.fromText(searchOwnerNameOrId))) {
+    if (searchOwnerNameOrId == Principal.toText(ownerId)) {
+      found := true;
+      break search;
+    };
+  } else {
+    let ownerInfo = users.getUserOpt(ownerId)!;
+    if (searchOwnerNameOrId == ownerInfo.name) {
+      found := true;
+      break search;
+    };
+  };
+};
+if (found) sortingPoints += 3;

Also applies to: 56-61

Comment on lines +57 to +58
let ownerInfo = users.getUserOpt(ownerId)!;
if (searchOwnerNameOrId == ownerInfo.name) {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for getUserOpt

The force unwrap operator ! on getUserOpt could cause runtime errors if user info is not found.

-let ownerInfo = users.getUserOpt(ownerId)!;
-if (searchOwnerNameOrId == ownerInfo.name) {
+switch (users.getUserOpt(ownerId)) {
+  case (?ownerInfo) {
+    if (searchOwnerNameOrId == ownerInfo.name) {
+      sortingPoints += 3;
+    };
+  };
+  case null {
+    // Log or handle missing user info
+  };
+};

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


let owner = if (owners.size() > 0) owners[0] else Principal.fromText("aaaaa-aa");
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Principal aaaaa-aa usage needs to be reviewed

The principal appears in two different contexts:

  • In dfx.schema.json as part of Bitcoin canister configuration
  • In getPackageSummary.mo as a fallback owner when no owners are present

The usage in the code appears to be problematic as:

  1. It's using the IC management canister as a fallback owner for packages
  2. This could lead to packages appearing to be owned by the management canister when they have no real owners
  3. The comment // legacy suggests this might be technical debt
  • Consider using a dedicated null principal or handling the no-owner case differently
  • Review if fallback to management canister is the intended behavior
🔗 Analysis chain

Verify the hardcoded principal value

The fallback principal aaaaa-aa seems to be the IC management canister, but this should be verified.


Let me check the content of these files to understand the context better.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other occurrences of this principal to verify its usage
rg -l "aaaaa-aa"

Length of output: 75


Script:

#!/bin/bash
# Check the content around the principal usage
rg -A 5 -B 5 "aaaaa-aa" 

Length of output: 1953

@@ -57,9 +58,9 @@ export const idlFactory = ({ IDL }) => {
});
const PackageVersion = IDL.Text;
const Script = IDL.Record({ 'value' : IDL.Text, 'name' : IDL.Text });
const PackageName__1 = IDL.Text;
const PackageName = IDL.Text;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider consistent type naming.

The file uses both PackageName and PackageName__1. For consistency, consider using only PackageName__1 throughout the file.

-const PackageName = IDL.Text;
 const DependencyV2 = IDL.Record({
-  'name' : PackageName,
+  'name' : PackageName__1,
   'repo' : IDL.Text,
   'version' : IDL.Text,
 });
 const PackageConfigV3 = IDL.Record({
   // ...
-  'name' : PackageName,
+  'name' : PackageName__1,
   // ...
 });

Also applies to: 63-63, 74-74

@ZenVoich ZenVoich merged commit b7df34a into main Dec 10, 2024
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

mops watch fails when dfx.json doesn't exist Multiple principals as owners of a package
1 participant