ResolveSHA resolves the SHA for a given action@version using GitHub CLI Returns the SHA and an error if resolution fails
(ctx context.Context, repo, version string)
| 91 | // ResolveSHA resolves the SHA for a given action@version using GitHub CLI |
| 92 | // Returns the SHA and an error if resolution fails |
| 93 | func (r *ActionResolver) ResolveSHA(ctx context.Context, repo, version string) (string, error) { |
| 94 | resolverLog.Printf("Resolving SHA for action: %s@%s", repo, version) |
| 95 | |
| 96 | // Create a cache key for tracking failed resolutions and cache lookups. |
| 97 | // Computed once here and reused below to avoid duplicate allocation. |
| 98 | cacheKey := formatActionCacheKey(repo, version) |
| 99 | r.usedCacheKeys[cacheKey] = true |
| 100 | |
| 101 | // Check if we've already failed to resolve this action in this run |
| 102 | if r.failedResolutions[cacheKey] { |
| 103 | resolverLog.Printf("Skipping resolution for %s@%s: already failed in this run", repo, version) |
| 104 | return "", fmt.Errorf("previously failed to resolve %s@%s in this compilation run", repo, version) |
| 105 | } |
| 106 | |
| 107 | // Check cache first using the pre-computed key to avoid a second key allocation. |
| 108 | if sha, found := r.cache.GetByCacheKey(cacheKey); found { |
| 109 | resolverLog.Printf("Cache hit for %s@%s: %s", repo, version, sha) |
| 110 | return sha, nil |
| 111 | } |
| 112 | |
| 113 | resolverLog.Printf("Cache miss for %s@%s, checking embedded action pins", repo, version) |
| 114 | |
| 115 | // Check embedded action pins for a semver-compatible version before making |
| 116 | // a network call. The embedded pins are the source-of-truth for known versions |
| 117 | // and are always available without network access. This avoids a ~1s gh-api |
| 118 | // subprocess for any action that is already covered by the embedded pin set. |
| 119 | requested := semverutil.EnsureVPrefix(version) |
| 120 | requestedVer := semverutil.ParseVersion(requested) |
| 121 | requestedIsPrecise := requestedVer != nil && requestedVer.IsPreciseVersion() |
| 122 | |
| 123 | for _, pin := range actionpins.GetActionPinsByRepo(repo) { |
| 124 | pinVersion := semverutil.EnsureVPrefix(pin.Version) |
| 125 | if requestedIsPrecise { |
| 126 | if pinVersion != requested { |
| 127 | continue |
| 128 | } |
| 129 | } else if !semverutil.IsCompatible(pinVersion, requested) { |
| 130 | continue |
| 131 | } |
| 132 | |
| 133 | resolverLog.Printf("Embedded pin hit for %s@%s → %s (%s)", repo, version, pin.SHA, pin.Version) |
| 134 | // Note: we intentionally do NOT call r.cache.Set() here. The embedded pins |
| 135 | // are always available in memory so there is nothing to persist, and writing |
| 136 | // to the on-disk cache would create root-owned files when compiling inside |
| 137 | // Docker containers (e.g. the Alpine CI test), preventing cleanup by the host. |
| 138 | return pin.SHA, nil |
| 139 | } |
| 140 | |
| 141 | resolverLog.Printf("No embedded pin for %s@%s, querying GitHub API", repo, version) |
| 142 | resolverLog.Printf("This may take a moment as we query GitHub API at /repos/%s/git/ref/tags/%s", gitutil.ExtractBaseRepo(repo), version) |
| 143 | |
| 144 | // Resolve using GitHub CLI |
| 145 | sha, err := r.resolveFromGitHub(ctx, repo, version) |
| 146 | if err != nil { |
| 147 | resolverLog.Printf("Failed to resolve %s@%s: %v", repo, version, err) |
| 148 | // Mark this resolution as failed for this compilation run |
| 149 | r.failedResolutions[cacheKey] = true |
| 150 | resolverLog.Printf("Marked %s as failed, will not retry in this run", cacheKey) |