MCPcopy Index your code
hub / github.com/cli/cli / updateSkillInPlace

Function updateSkillInPlace

pkg/cmd/skills/update/update.go:418–462  ·  view source on GitHub ↗

updateSkillInPlace installs the resolved update into a staging directory alongside the existing skill directory and, on success, atomically swaps the staged contents into place via same-filesystem renames. This guarantees: - The skill directory's own inode is preserved, so symlinks, mounts, and ext

(opts *UpdateOptions, u pendingUpdate, apiClient *api.Client, gitRoot, homeDir string)

Source from the content-addressed store, hash-verified

416// skill completely untouched: existing files are first moved aside into
417// a backup directory and restored if any subsequent step fails.
418func updateSkillInPlace(opts *UpdateOptions, u pendingUpdate, apiClient *api.Client, gitRoot, homeDir string) error {
419 if u.local.dir == "" {
420 return fmt.Errorf("cannot update %s: no install location recorded", u.local.name)
421 }
422
423 parent := filepath.Dir(u.local.dir)
424 if err := os.MkdirAll(parent, 0o755); err != nil {
425 return fmt.Errorf("could not ensure parent directory %s: %w", parent, err)
426 }
427
428 // Stage as a sibling of the existing skill directory so the swap stays
429 // on the same filesystem and every rename is atomic.
430 staging, err := os.MkdirTemp(parent, "."+u.skill.Name+".gh-skill-update-")
431 if err != nil {
432 return fmt.Errorf("could not create staging directory: %w", err)
433 }
434 defer os.RemoveAll(staging)
435
436 installOpts := &installer.Options{
437 Host: u.local.repoHost,
438 Owner: u.local.owner,
439 Repo: u.local.repo,
440 Ref: u.resolved.Ref,
441 SHA: u.resolved.SHA,
442 Skills: []discovery.Skill{u.skill},
443 Dir: staging,
444 GitRoot: gitRoot,
445 HomeDir: homeDir,
446 Client: apiClient,
447 }
448 if _, err := installer.Install(installOpts); err != nil {
449 return err
450 }
451
452 stagedSkillDir := filepath.Join(staging, u.skill.Name)
453 if _, err := os.Stat(stagedSkillDir); err != nil {
454 return fmt.Errorf("installer did not produce %s: %w", stagedSkillDir, err)
455 }
456
457 if err := os.MkdirAll(u.local.dir, 0o755); err != nil {
458 return fmt.Errorf("could not ensure skill directory %s: %w", u.local.dir, err)
459 }
460
461 return swapDirectoryContents(u.local.dir, stagedSkillDir)
462}
463
464// swapDirectoryContents replaces the entries inside dest with the entries
465// inside src, preserving dest's inode. It first moves every existing entry

Callers 1

updateRunFunction · 0.85

Calls 4

InstallFunction · 0.92
swapDirectoryContentsFunction · 0.85
JoinMethod · 0.80
ErrorfMethod · 0.65

Tested by

no test coverage detected