check / check (push) Waiting to run
mfer gen now writes index.mf instead of .index.mf, the name fetch requests and the README calls the standard filename. Given a directory, check, freshen, list and export look only for index.mf; .index.mf is no longer recognized. Because index.mf is not hidden, gen and freshen now leave the manifest they write out of its own listing by file identity (os.SameFile), so it is recognized however its path is spelled, through a symlink included, while an ordinary file of the same name elsewhere in the tree is still listed. Model: opus-5-5
87 lines
2.2 KiB
Go
87 lines
2.2 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/urfave/cli/v2"
|
|
)
|
|
|
|
// 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(pathOrURL string) (io.ReadCloser, error) {
|
|
if isHTTPURL(pathOrURL) {
|
|
client := &http.Client{Timeout: manifestFetchTimeout}
|
|
|
|
req, err := http.NewRequestWithContext(
|
|
context.Background(), 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(ctx *cli.Context) (string, error) {
|
|
if ctx.Args().Len() > 0 {
|
|
arg := ctx.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, ".")
|
|
}
|