-
Notifications
You must be signed in to change notification settings - Fork 1
feat: Build gonsole, the command line brick #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
0c69414
chore(gonsole): scaffold the module
SirLouen f4cf1a2
feat(gonsole): read a command line and run the command it names
SirLouen 8b7a795
feat(gonsole): print the command list and a help page per command
SirLouen 3701b96
feat(gonsole): keep writes a dry run until -yes and answer -json
SirLouen 702f88b
feat(gonsole): check and record the account a write acts as
SirLouen 34c7199
feat(gonsole): read settings under one prefix with bounds and plain e…
SirLouen 959d2d2
feat(gonsole): add the version, serve, migrate and seed words
SirLouen 18379d5
refactor(gonsole): say command instead of word in names and docblocks
SirLouen 170552f
feat(gonsole): check command names and recover a crashing command
SirLouen 1fe9db3
feat(gonsole): serve HTTP until a signal ends the run
SirLouen 76b770a
feat(gonsole): record an applied write even after a signal
SirLouen 3f5b307
feat(gonsole): collect the commands each plugin provides
SirLouen 75fbe75
feat(gonsole): register the plugins once per run and release them
SirLouen ea77cb6
feat(gonsole): run the commands of registered plugins
SirLouen 7a4bb28
feat(gonsole): list the commands of registered plugins
SirLouen 39d1e91
feat(gonsole): migrate, seed and check the registered plugins
SirLouen 5438bc7
test(gonsole): use one constant for the dry-run notice
SirLouen cf7ce8a
feat(gonsole): hand each call the flags its line set
SirLouen 410fcf7
feat(gonsole): add a testkit for programs built on gonsole
SirLouen f3c8662
ci: test, lint and scan the gonsole module
SirLouen 6f8b6a1
docs(gonsole): list the module and its unreleased changes
SirLouen b14e9b6
test(gottext): use a generic product name in the error fixture
SirLouen 8ed7778
test(gonsole): wait for a ready line before the first signal
SirLouen a1ff707
test(gonsole): count one imported report per line of input
SirLouen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| version: "2" | ||
|
|
||
| linters: | ||
| default: standard | ||
| enable: | ||
| - cyclop | ||
| - depguard | ||
| - gocognit | ||
| - lll | ||
| - misspell | ||
| - revive | ||
| - unconvert | ||
| - unparam | ||
| settings: | ||
| cyclop: | ||
| max-complexity: 10 | ||
| gocognit: | ||
| min-complexity: 15 | ||
| lll: | ||
| line-length: 120 | ||
| revive: | ||
| rules: | ||
| - name: blank-imports | ||
| disabled: true | ||
| - name: exported | ||
| depguard: | ||
| rules: | ||
| engine-purity: | ||
| list-mode: strict | ||
| files: | ||
| - "**/*.go" | ||
| allow: | ||
| - $gostd | ||
| - github.com/gopherium/framework/gonsole | ||
| exclusions: | ||
| rules: | ||
| - path: _test\.go | ||
| linters: | ||
| - cyclop | ||
| - gocognit | ||
|
|
||
| formatters: | ||
| enable: | ||
| - gofmt | ||
| - goimports | ||
| settings: | ||
| goimports: | ||
| local-prefixes: | ||
| - github.com/gopherium/framework |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # Changelog | ||
|
|
||
| All notable changes to the `gonsole` module are documented in this | ||
| file. The format follows | ||
| [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the | ||
| module follows [Semantic Versioning](https://semver.org/). While at | ||
| v0.x, minor releases may contain breaking changes. | ||
|
|
||
| Releases of this module are tagged `gonsole/vX.Y.Z`. | ||
|
|
||
| ## [Unreleased] | ||
|
|
||
| ### Added | ||
|
|
||
| - `Program`, `Main` and `Run`, running a command line and answering exit code 0, 1 or 2. | ||
| - `Command`, `Call` and `Step`, a command, what one run of it receives, and a named schema step. | ||
| - `Misuse` and `ErrMisused`, marking an error the program answers with exit 2. | ||
| - Command names alone or as `namespace:command`, a help page for each, and a listing of them all. | ||
| - `-yes` dry runs for commands that write and `-json` for commands that answer one document. | ||
| - `-as` with `Authorize` and `Record`, checking and recording the account that acts. | ||
| - `Call.Flags`, the command's own flags the line set, for the audit record. | ||
| - The base commands `help`, `list`, `version`, `check`, `serve`, `migrate` and `seed`. | ||
| - `Program.Check`, refusing every naming offence in the program and its plugins. | ||
| - `Renamed`, keeping an old two word spelling working with a warning. | ||
| - `Env` with `Required`, `Duration`, `Count`, `Flag`, `Within`, `Parse` and `Timeouts`. | ||
| - `NewServer` and `Serve`, serving HTTP until a signal ends the run. | ||
| - `Program.Plugins`, `Call.Plugins`, `Loaded`, `Provider` and `Walk`, for compiled plugins' commands. | ||
| - `testkit`, running programs from tests in process and as built binaries. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| package gonsole | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "runtime/debug" | ||
| ) | ||
|
|
||
| // perform authorizes the acting account of call, migrates when cmd asks, runs cmd and records the run when it applied. | ||
| func (r *runner) perform(ctx context.Context, cmd Command, call Call) error { | ||
| if err := r.authorize(ctx, cmd, call); err != nil { | ||
| return err | ||
| } | ||
| if cmd.Migrates && call.Apply { | ||
| if err := r.migrate(ctx, call, call.Stderr); err != nil { | ||
| return err | ||
| } | ||
| } | ||
| if err := cmd.Run(ctx, call); err != nil { | ||
| return err | ||
| } | ||
| if !call.Apply { | ||
| r.warn("dry run, nothing changed, pass -yes to apply") | ||
| return nil | ||
| } | ||
| return r.record(ctx, cmd, call) | ||
| } | ||
|
|
||
| // authorize refuses the acting account of call when it lacks the capability cmd names. | ||
| func (r *runner) authorize(ctx context.Context, cmd Command, call Call) error { | ||
| if cmd.Capability == "" { | ||
| return nil | ||
| } | ||
| return r.program.Authorize(ctx, call, cmd.Capability) | ||
| } | ||
|
|
||
| // record stores the applied run of cmd when cmd names a capability, under a context the end of the run cannot cancel. | ||
| func (r *runner) record(ctx context.Context, cmd Command, call Call) error { | ||
| if cmd.Capability == "" { | ||
| return nil | ||
| } | ||
| return r.program.Record(context.WithoutCancel(ctx), call, cmd.Name) | ||
| } | ||
|
|
||
| // panicked is a panic recovered from the run of one command. | ||
| type panicked struct { | ||
| command string | ||
| value any | ||
| stack []byte | ||
| } | ||
|
|
||
| // Error returns the line naming the command and the panic value. | ||
| func (p panicked) Error() string { | ||
| return fmt.Sprintf("%s: panic: %v", p.command, p.value) | ||
| } | ||
|
|
||
| // crashes returns every panic err holds, in the order its message names them. | ||
| func crashes(err error) []panicked { | ||
| switch e := err.(type) { | ||
| case panicked: | ||
| return []panicked{e} | ||
| case interface{ Unwrap() []error }: | ||
| var all []panicked | ||
| for _, inner := range e.Unwrap() { | ||
| all = append(all, crashes(inner)...) | ||
| } | ||
| return all | ||
| case interface{ Unwrap() error }: | ||
| return crashes(e.Unwrap()) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // recoverRun turns a panic in the run of the command called name into the error err points at. | ||
| func recoverRun(name string, err *error) { | ||
| if value := recover(); value != nil { | ||
| *err = panicked{command: name, value: value, stack: debug.Stack()} | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the audit store waits for its context to finish, an applied command can remain blocked after the request deadline expires.
context.WithoutCancelremoves that deadline without replacing it. This non-blocking concern can leave an operator waiting indefinitely when audit storage does not respond.Artifacts
Go source for the bounded applied-command audit test
Command used to run the audit test against both revisions
Audit test output before the context change
Audit test output with the PR candidate
Prompt To Fix With AI