getWithGoGetter fetches the package at the given address into the given target directory. The given address must already be in normalized form (using NormalizePackageAddress) or else the behavior is undefined. This function deals only in entire packages, so it's always the caller's responsibility t
(ctx context.Context, instPath, packageAddr string)
| 157 | // reasonable way to improve these error messages at this layer because |
| 158 | // the underlying errors are not separately recognizable. |
| 159 | func (g *reusingGetter) getWithGoGetter(ctx context.Context, instPath, packageAddr string) error { |
| 160 | var err error |
| 161 | |
| 162 | // For now we hold the "previousInstalls" mutex throughout our entire work here |
| 163 | // since we don't currently try to use a single getter concurrently anyway. |
| 164 | // If we _do_ want to enable more concurrency in future then we'll need a |
| 165 | // more interesting strategy to make sure that only concurrent attempts to |
| 166 | // install the _same_ package get serialized, but we'll wait until we have |
| 167 | // that need before we introduce that complexity. |
| 168 | // |
| 169 | // NOTE WELL: [getter.Client.Get] modifies internal state inside each of the |
| 170 | // getters passed in [getter.Client.Getters] before calling into them, so |
| 171 | // it is _not_ safe to reuse the same getter instances across multiple |
| 172 | // concurrent calls. If we want to make this work concurrently in future |
| 173 | // then we'll need to instead instantiate the getters on demand for each |
| 174 | // request. |
| 175 | g.previousInstallsMu.Lock() |
| 176 | defer g.previousInstallsMu.Unlock() |
| 177 | if g.previousInstalls == nil { |
| 178 | g.previousInstalls = make(map[string]string) |
| 179 | } |
| 180 | |
| 181 | if prevDir, exists := g.previousInstalls[packageAddr]; exists { |
| 182 | log.Printf("[TRACE] getmodules: copying previous install of %q from %s to %s", packageAddr, prevDir, instPath) |
| 183 | err := os.Mkdir(instPath, os.ModePerm) |
| 184 | if err != nil { |
| 185 | return fmt.Errorf("failed to create directory %s: %w", instPath, err) |
| 186 | } |
| 187 | err = copy.CopyDir(instPath, prevDir) |
| 188 | if err != nil { |
| 189 | return fmt.Errorf("failed to copy from %s to %s: %w", prevDir, instPath, err) |
| 190 | } |
| 191 | } else { |
| 192 | log.Printf("[TRACE] getmodules: fetching %q to %q", packageAddr, instPath) |
| 193 | client := getter.Client{ |
| 194 | Src: packageAddr, |
| 195 | Dst: instPath, |
| 196 | Pwd: instPath, |
| 197 | |
| 198 | Mode: getter.ClientModeDir, |
| 199 | |
| 200 | Detectors: goGetterNoDetectors, // our caller should've already done detection |
| 201 | Decompressors: goGetterDecompressors, |
| 202 | Getters: g.getters, |
| 203 | Ctx: ctx, |
| 204 | } |
| 205 | err = client.Get() |
| 206 | if err != nil { |
| 207 | return err |
| 208 | } |
| 209 | // Remember where we installed this so we might reuse this directory |
| 210 | // on subsequent calls to avoid re-downloading. |
| 211 | g.previousInstalls[packageAddr] = instPath |
| 212 | } |
| 213 | |
| 214 | // If we get down here then we've either downloaded the package or |
| 215 | // copied a previous tree we downloaded, and so either way we should |
| 216 | // have got the full module package structure written into instPath. |