Skip to content

feat(daemon): support user-scope systemd service installation on Linux#118

Merged
AnnatarHe merged 1 commit intomainfrom
claude/issue-116-20251003-0835
Oct 3, 2025
Merged

feat(daemon): support user-scope systemd service installation on Linux#118
AnnatarHe merged 1 commit intomainfrom
claude/issue-116-20251003-0835

Conversation

@AnnatarHe
Copy link
Copy Markdown
Contributor

Changed Linux daemon installation from system-wide (root) to user-scope to match the Darwin implementation pattern and eliminate root permission requirements.

Closes #116

🤖 Generated with Claude Code

Changed Linux daemon installation from system-wide (root) to user-scope
to match the Darwin implementation pattern and eliminate root permission
requirements.

Changes:
- Updated all systemctl commands to use --user flag
- Changed service path from /etc/systemd/system/ to ~/.config/systemd/user/
- Removed User=root and Group=root from service template
- Changed WantedBy target from multi-user.target to default.target
- Updated service management methods to use user-scope paths

Closes #116

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Le He <AnnatarHe@users.noreply.github.com>
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @AnnatarHe, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant enhancement to the Linux daemon installation process by transitioning from system-wide to user-scope systemd services. This modification removes the necessity for root privileges during installation and service management, streamlining the user experience and ensuring consistency with the daemon's behavior on Darwin systems.

Highlights

  • User-Scope Systemd Services: The Linux daemon installation now utilizes user-scope systemd services, moving away from system-wide installations.
  • Permission Reduction: Installation and management of the daemon no longer require root privileges, simplifying the process for users.
  • Cross-Platform Consistency: This change aligns the Linux daemon installation pattern with the existing Darwin implementation, promoting a unified approach.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@codecov
Copy link
Copy Markdown

codecov Bot commented Oct 3, 2025

Codecov Report

❌ Patch coverage is 0% with 36 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
model/daemon-installer.linux.go 0.00% 36 Missing ⚠️
Flag Coverage Δ
unittests 20.36% <0.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
model/daemon-installer.linux.go 0.00% <0.00%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@AnnatarHe AnnatarHe merged commit 3b1b2a5 into main Oct 3, 2025
2 of 3 checks passed
@AnnatarHe AnnatarHe deleted the claude/issue-116-20251003-0835 branch October 3, 2025 08:53
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request successfully migrates the Linux daemon installation from a system-wide service to a user-scoped one, which is a great improvement as it removes the need for root permissions. The changes to the systemd service file and the systemctl commands are correct. I've identified a couple of areas for improvement regarding code duplication and error handling consistency that would make the implementation more robust and maintainable.

// Also disable to clean up
_ = exec.Command("systemctl", "--user", "disable", "shelltime").Run()
// Remove old symlink if exists
_ = os.Remove(servicePath)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The error from os.Remove is ignored here. This is inconsistent with UnregisterService (line 150), which correctly handles the error by checking for os.IsNotExist. For better robustness and consistency, you should handle the error here as well. This will prevent silent failures if the file removal fails for unexpected reasons, such as permission issues.

		if err := os.Remove(servicePath); err != nil && !os.IsNotExist(err) {
			return fmt.Errorf("failed to remove old service symlink: %w", err)
		}

Comment on lines +92 to +98
currentUser, err := user.Current()
if err != nil {
return fmt.Errorf("failed to get current user: %w", err)
}

// Create systemd user directory if it doesn't exist
systemdUserDir := filepath.Join(currentUser.HomeDir, ".config/systemd/user")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

There is code duplication in RegisterService, CheckAndStopExistingService, and UnregisterService. Each of these functions retrieves the current user and constructs paths for the systemd user directory. To improve maintainability and adhere to the DRY (Don't Repeat Yourself) principle, consider refactoring this logic into a shared helper method. This method could be responsible for providing the systemdUserDir and servicePath, centralizing the path construction logic.

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.

feat: support daemon service install in user permission instead of root permission

1 participant