Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23dc6dd49d |
-12
@@ -11,10 +11,6 @@ COPY . .
|
||||
# Touch .pb.go so make does not try to regenerate via protoc (file is committed)
|
||||
RUN touch mfer/mf.pb.go
|
||||
|
||||
# Changing value from script/cibuild; forces the check steps below to
|
||||
# re-run instead of being served from a stale layer cache.
|
||||
ARG CHECK_EPOCH
|
||||
|
||||
# Go half of fmt-check only: this image has no node, so no prettier. The
|
||||
# markdown half runs in the mdfmt stage below.
|
||||
RUN make fmt-check-go
|
||||
@@ -31,10 +27,6 @@ RUN yarn install --frozen-lockfile
|
||||
|
||||
COPY . .
|
||||
|
||||
# Changing value from script/cibuild; forces the check step below to
|
||||
# re-run instead of being served from a stale layer cache.
|
||||
ARG CHECK_EPOCH
|
||||
|
||||
# No make in this image; call the script entrypoint directly.
|
||||
RUN script/prettier --check
|
||||
|
||||
@@ -55,10 +47,6 @@ COPY . .
|
||||
# Touch .pb.go so make does not try to regenerate via protoc (file is committed)
|
||||
RUN touch mfer/mf.pb.go
|
||||
|
||||
# Changing value from script/cibuild; forces the check steps below to
|
||||
# re-run instead of being served from a stale layer cache.
|
||||
ARG CHECK_EPOCH
|
||||
|
||||
RUN make test
|
||||
RUN cd cmd/mfer && go build -tags urfave_cli_no_docs -o /mfer .
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ only thing left of the `chore/align-repo-policies` branch is the list below.
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-09-21: made `script/cibuild` re-run the checks on an unchanged tree via a
|
||||
changing `CHECK_EPOCH` build arg in each Dockerfile check stage (#89)
|
||||
- 2026-09-21: fixed the `-v` collision between `--verbose` and `--version`;
|
||||
verbose owns `-v`, and version answers to `--version` and `-V` (#64)
|
||||
- 2026-08-09: added `.prettierrc`/`.prettierignore`, gave `script/fmt` and
|
||||
`script/fmt-check` one shared prettier file set via `script/prettier`, dropped
|
||||
the `|| true` that hid prettier failures, and added a node-based Dockerfile
|
||||
|
||||
@@ -27,6 +27,7 @@ const (
|
||||
testManifest = "/manifest.mf"
|
||||
testFlagBase = "--base"
|
||||
testFlagNoExtra = "--no-extra-files"
|
||||
testFlagVersion = "--version"
|
||||
)
|
||||
|
||||
var errSimulatedWrite = errors.New("simulated write failure")
|
||||
@@ -102,7 +103,7 @@ func TestVersionCommand(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fs := afero.NewMemMapFs()
|
||||
opts := testOpts([]string{testApp, "version"}, fs)
|
||||
opts := testOpts([]string{testApp, cmdVersion}, fs)
|
||||
|
||||
exitCode := runCLI(opts)
|
||||
|
||||
@@ -113,6 +114,69 @@ func TestVersionCommand(t *testing.T) {
|
||||
assert.Contains(t, stdout, "abc123")
|
||||
}
|
||||
|
||||
// TestVFlagCollision covers the -v/--verbose vs --version flag interaction
|
||||
// (issue #64). Verbose owns -v; version answers to --version and -V. None of
|
||||
// these invocations may produce a parser error, and the two ways of asking
|
||||
// for the version must print the same thing.
|
||||
func TestVFlagCollision(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Invocations that must print the version and exit 0.
|
||||
versionCases := map[string][]string{
|
||||
"long version flag": {testApp, testFlagVersion},
|
||||
"short version flag": {testApp, "-V"},
|
||||
"verbose then version": {testApp, "-v", testFlagVersion},
|
||||
"long verbose and version": {testApp, "--verbose", testFlagVersion},
|
||||
}
|
||||
|
||||
for name, args := range versionCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
opts := testOpts(args, afero.NewMemMapFs())
|
||||
exitCode := runCLI(opts)
|
||||
|
||||
assert.Equal(t, 0, exitCode, "stderr: %s", testStderr(t, opts))
|
||||
assert.Contains(t, testStdout(t, opts), mfer.Version)
|
||||
assert.NotContains(t, testStderr(t, opts), "two forms of the same flag")
|
||||
})
|
||||
}
|
||||
|
||||
// Invocations that must enable verbose and exit 0 without a parser error.
|
||||
verboseCases := map[string][]string{
|
||||
"short verbose flag": {testApp, "-v"},
|
||||
"long verbose flag": {testApp, "--verbose"},
|
||||
}
|
||||
|
||||
for name, args := range verboseCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
opts := testOpts(args, afero.NewMemMapFs())
|
||||
exitCode := runCLI(opts)
|
||||
|
||||
assert.Equal(t, 0, exitCode, "stderr: %s", testStderr(t, opts))
|
||||
assert.Contains(t, testStdout(t, opts), cmdGenerate,
|
||||
"root should show help listing subcommands")
|
||||
assert.Empty(t, testStderr(t, opts))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestVersionFlagAndCommandMatch asserts that "mfer --version" and
|
||||
// "mfer version" produce identical output (issue #64).
|
||||
func TestVersionFlagAndCommandMatch(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
flagOpts := testOpts([]string{testApp, testFlagVersion}, afero.NewMemMapFs())
|
||||
require.Equal(t, 0, runCLI(flagOpts))
|
||||
|
||||
cmdOpts := testOpts([]string{testApp, cmdVersion}, afero.NewMemMapFs())
|
||||
require.Equal(t, 0, runCLI(cmdOpts))
|
||||
|
||||
assert.Equal(t, testStdout(t, flagOpts), testStdout(t, cmdOpts))
|
||||
}
|
||||
|
||||
func TestHelpCommand(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
+25
-2
@@ -18,6 +18,7 @@ const (
|
||||
cmdGenerate = "generate"
|
||||
cmdCheck = "check"
|
||||
cmdExport = "export"
|
||||
cmdVersion = "version"
|
||||
|
||||
flagProgress = "progress"
|
||||
|
||||
@@ -67,6 +68,12 @@ func (mfa *CLIApp) VersionString() string {
|
||||
return mfer.Version
|
||||
}
|
||||
|
||||
// printVersion writes the version line shared by the --version flag and the
|
||||
// version subcommand, so both produce identical output.
|
||||
func (mfa *CLIApp) printVersion() {
|
||||
_, _ = fmt.Fprintf(mfa.Stdout, "%s version %s\n", mfa.appname, mfa.VersionString())
|
||||
}
|
||||
|
||||
func (mfa *CLIApp) printBanner() {
|
||||
if log.GetLevel() <= log.InfoLevel {
|
||||
_, _ = fmt.Fprintln(mfa.Stdout, banner)
|
||||
@@ -265,10 +272,10 @@ func (mfa *CLIApp) exportCommand() *cli.Command {
|
||||
|
||||
func (mfa *CLIApp) versionCommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "version",
|
||||
Name: cmdVersion,
|
||||
Usage: "Show version",
|
||||
Action: func(_ *cli.Context) error {
|
||||
_, _ = fmt.Fprintln(mfa.Stdout, mfa.VersionString())
|
||||
mfa.printVersion()
|
||||
|
||||
return nil
|
||||
},
|
||||
@@ -324,6 +331,20 @@ func (mfa *CLIApp) run(args []string) {
|
||||
log.SetOutput(mfa.Stdout, mfa.Stderr)
|
||||
log.Init()
|
||||
|
||||
// -v means verbose, not version. urfave/cli's built-in version flag
|
||||
// claims -v by default, colliding with the -v verbose alias used here and
|
||||
// on every subcommand; that collision makes "mfer -v --version" fail to
|
||||
// parse. Verbose is the more common meaning of -v in tools that offer
|
||||
// both, so verbose keeps -v and the version flag takes the capital -V.
|
||||
// VersionFlag and VersionPrinter are urfave/cli package globals; run() is
|
||||
// serialized in tests, so assigning them here is safe.
|
||||
cli.VersionFlag = &cli.BoolFlag{
|
||||
Name: cmdVersion,
|
||||
Aliases: []string{"V"},
|
||||
Usage: "print the version",
|
||||
}
|
||||
cli.VersionPrinter = func(_ *cli.Context) { mfa.printVersion() }
|
||||
|
||||
mfa.app = &cli.App{
|
||||
Name: mfa.appname,
|
||||
Usage: "Manifest generator",
|
||||
@@ -331,11 +352,13 @@ func (mfa *CLIApp) run(args []string) {
|
||||
EnableBashCompletion: true,
|
||||
Writer: mfa.Stdout,
|
||||
ErrWriter: mfa.Stderr,
|
||||
Flags: commonFlags(),
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.Args().Len() > 0 {
|
||||
return fmt.Errorf("%w %q", errUnknownCommand, c.Args().First())
|
||||
}
|
||||
|
||||
mfa.setVerbosity(c)
|
||||
mfa.printBanner()
|
||||
|
||||
return cli.ShowAppHelp(c)
|
||||
|
||||
+4
-9
@@ -1,19 +1,14 @@
|
||||
#!/bin/sh
|
||||
# script/cibuild: run the CI build. The Dockerfile runs make fmt-check-go,
|
||||
# make lint, the prettier check, and make test as build steps. A bare
|
||||
# `docker build .` would serve those steps from Docker's layer cache on an
|
||||
# unchanged tree, exiting 0 without running them. CHECK_EPOCH is a build
|
||||
# arg placed above the check steps in every check stage; passing a fresh
|
||||
# value each run busts the cache for those steps (and nothing above them),
|
||||
# so a green build means the checks actually ran. Dependency layers above
|
||||
# the arg stay cached. The Gitea workflow runs this on push.
|
||||
# script/cibuild: run the CI build. The Dockerfile runs script/check
|
||||
# (via make check), so a successful build implies all checks pass.
|
||||
# Generic: needs no adaptation. The Gitea workflow runs this on push.
|
||||
set -eu
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||
|
||||
main() {
|
||||
cd "$ROOT"
|
||||
docker build --build-arg CHECK_EPOCH="$(date +%s)" .
|
||||
docker build .
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user