| 241 | } |
| 242 | |
| 243 | func InstallArchive(archiveData []byte, plugin Plugin, options InstallOptions) (InstallResult, error) { |
| 244 | options = normalizeInstallOptions(options) |
| 245 | id := strings.TrimSpace(plugin.ID) |
| 246 | if !validPluginID(id) { |
| 247 | return InstallResult{}, fmt.Errorf("invalid plugin id %q", plugin.ID) |
| 248 | } |
| 249 | version := normalizeVersion(plugin.Version) |
| 250 | if !validPluginVersion(version) { |
| 251 | return InstallResult{}, fmt.Errorf("invalid plugin version %q", plugin.Version) |
| 252 | } |
| 253 | plugin.Version = version |
| 254 | reader, errZip := zip.NewReader(bytes.NewReader(archiveData), int64(len(archiveData))) |
| 255 | if errZip != nil { |
| 256 | return InstallResult{}, fmt.Errorf("open zip: %w", errZip) |
| 257 | } |
| 258 | |
| 259 | libraryData, mode, errLibrary := readTargetLibrary(reader, id, version, options.GOOS) |
| 260 | if errLibrary != nil { |
| 261 | return InstallResult{}, errLibrary |
| 262 | } |
| 263 | |
| 264 | targetPath, errTarget := installTargetPath(options, id, version) |
| 265 | if errTarget != nil { |
| 266 | return InstallResult{}, errTarget |
| 267 | } |
| 268 | overwritten := false |
| 269 | if _, errStat := os.Stat(targetPath); errStat == nil { |
| 270 | overwritten = true |
| 271 | } else if !errors.Is(errStat, os.ErrNotExist) { |
| 272 | return InstallResult{}, fmt.Errorf("stat target plugin: %w", errStat) |
| 273 | } |
| 274 | if overwritten { |
| 275 | existingData, errReadExisting := os.ReadFile(targetPath) |
| 276 | if errReadExisting != nil { |
| 277 | return InstallResult{}, fmt.Errorf("read target plugin: %w", errReadExisting) |
| 278 | } |
| 279 | if bytes.Equal(existingData, libraryData) { |
| 280 | return InstallResult{ |
| 281 | ID: id, |
| 282 | Version: strings.TrimSpace(plugin.Version), |
| 283 | Path: targetPath, |
| 284 | Overwritten: true, |
| 285 | Skipped: true, |
| 286 | }, nil |
| 287 | } |
| 288 | } |
| 289 | // Re-check immediately before replacing an existing file: the same version |
| 290 | // may have been loaded while the archive was being downloaded and verified. |
| 291 | if overwritten && options.BeforeWrite != nil { |
| 292 | if errBeforeWrite := options.BeforeWrite(); errBeforeWrite != nil { |
| 293 | return InstallResult{}, fmt.Errorf("prepare plugin write: %w", errBeforeWrite) |
| 294 | } |
| 295 | } |
| 296 | if overwritten && loadedPluginInstallBlocked(options) { |
| 297 | return InstallResult{}, ErrLoadedPluginLocked |
| 298 | } |
| 299 | if errWrite := writeFileAtomic(targetPath, libraryData, mode); errWrite != nil { |
| 300 | return InstallResult{}, errWrite |