Files
mfer/internal/cli/manifest_loader.go
T
sneak ccd018aef0
check / check (push) Failing after 3s
Move the CLI to urfave/cli v3 (closes #110)
Ported per the library's v2-to-v3 migration guide: the app is a root
cli.Command, actions take a context and the command, and flag environment
variables become value sources. -v, -q and the version flag are local so,
as before, only the commands defining them accept them. ErrWriter is
stdout so usage errors print with their help, as before. The action's
context reaches the manifest download in check, export and list. testify
rises to v1.12.1, which v3 requires; the urfave_cli_no_docs build tag,
which v3 lacks, is dropped. v3 no longer refuses a flag given under two
names, so -v --verbose gives debug output; the test pinning the refusal
becomes a TestVerboseCount case.

Model: opus-5-5
2026-10-04 18:36:04 +00:00

87 lines
2.2 KiB
Go

package cli
import (
"context"
"errors"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/urfave/cli/v3"
)
// manifestFetchTimeout bounds HTTP requests made to fetch a manifest.
const manifestFetchTimeout = 30 * time.Second
// errHTTPStatus indicates an HTTP response with a non-OK status code.
//
// Its text is the literal "HTTP" prefix of the rendered "HTTP <code>"
// message that mfer has always printed, so that wrapping it does not
// change any user-visible output. Match it with errors.Is; do not read
// its message.
var errHTTPStatus = errors.New("HTTP")
// isHTTPURL returns true if the string starts with http:// or https://.
func isHTTPURL(s string) bool {
return strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://")
}
// openManifestReader opens a manifest from a path or URL and returns a ReadCloser.
// The caller must close the returned reader.
func (mfa *CLIApp) openManifestReader(
ctx context.Context, pathOrURL string,
) (io.ReadCloser, error) {
if isHTTPURL(pathOrURL) {
client := &http.Client{Timeout: manifestFetchTimeout}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, pathOrURL, nil)
if err != nil {
return nil, fmt.Errorf("failed to fetch %s: %w", pathOrURL, err)
}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to fetch %s: %w", pathOrURL, err)
}
if resp.StatusCode != http.StatusOK {
_ = resp.Body.Close()
return nil, fmt.Errorf("failed to fetch %s: %w %d",
pathOrURL, errHTTPStatus, resp.StatusCode)
}
return resp.Body, nil
}
f, err := mfa.Fs.Open(pathOrURL)
if err != nil {
return nil, err
}
return f, nil
}
// resolveManifestArg resolves the manifest path from CLI arguments.
// HTTP(S) URLs are returned as-is. Directories are searched for index.mf.
// If no argument is given, the current directory is searched.
func (mfa *CLIApp) resolveManifestArg(cmd *cli.Command) (string, error) {
if cmd.Args().Len() > 0 {
arg := cmd.Args().Get(0)
if isHTTPURL(arg) {
return arg, nil
}
info, statErr := mfa.Fs.Stat(arg)
if statErr == nil && info.IsDir() {
return findManifest(mfa.Fs, arg)
}
return arg, nil
}
return findManifest(mfa.Fs, ".")
}