Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,23 @@ jobs:
- name: Run tests with race detector
run: go test -race -v ./...

gofmt:
name: gofmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version-file: .go-version
- name: Check gofmt
run: |
unformatted=$(gofmt -l .)
if [ -n "$unformatted" ]; then
echo "The following files are not gofmt'd:"
echo "$unformatted"
exit 1
fi

test-against-bash:
name: Test against Bash (Docker)
runs-on: ubuntu-latest
Expand Down
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ The shell is supported on Linux, Windows and macOS.

- `README.md` and `SHELL_FEATURES.md` must be kept up to date with the implementation.

## Code Style

- **All Go files must be formatted with `gofmt` before committing.** Run `gofmt -w .` from the repo root and verify with `gofmt -l .` (no output means clean). CI enforces this and will fail if any file is not properly formatted.

## Pull Requests

- **Always open pull requests in draft mode.** Use `gh pr create --draft` (or the GitHub UI's "Draft pull request" option). Only mark a PR ready for review once all CI checks pass and the work is complete.

## CRITICAL: Bug Fixes and Bash Compatibility

- **ALWAYS prioritise fixing the shell implementation to match bash behaviour over changing tests to match the current (incorrect) shell output.** Never "fix" a failing test by updating its expected output to match broken shell behaviour — fix the shell instead.
Expand Down
1 change: 0 additions & 1 deletion interp/builtins/printf/printf.go
Original file line number Diff line number Diff line change
Expand Up @@ -1049,7 +1049,6 @@ func parseFloatArg(s string) (floatArg, error) {
return floatArg{f: val}, nil
}


// processBEscapes handles backslash escapes for %b (like echo -e).
// Returns the processed string, whether \c was seen (stop all output),
// and any warning messages to emit to stderr.
Expand Down
2 changes: 1 addition & 1 deletion interp/builtins/strings_cmd/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ const (

// radixFlagVal implements pflag.Value for the -t / --radix flag.
// Validation happens in Set so pflag reports errors during parsing, which also
// correctly rejects empty values (e.g. --radix= or -t '').
// correctly rejects empty values (e.g. --radix= or -t ).
type radixFlagVal struct{ target *radixFormat }

func (r *radixFlagVal) String() string {
Expand Down
10 changes: 5 additions & 5 deletions interp/builtins/tail/tail.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,15 @@ type countMode struct {
// returns a bound handler whose flag variables are captured by closure. The
// framework calls Parse and passes positional arguments to the handler.
func registerFlags(fs *builtins.FlagSet) builtins.HandlerFunc {
help := fs.BoolP("help", "h", false, "print usage and exit")
help := fs.BoolP("help", "h", false, "print usage and exit")
zeroTerminated := fs.BoolP("zero-terminated", "z", false, "use NUL as line delimiter")

// quietFlag, silentFlag, and verboseFlag share a sequence counter so that
// after parsing we can tell which appeared last on the command line and
// apply last-flag-wins semantics (e.g. "-q -v" should show headers).
var headerSeq int
quietFlag := newHeaderFlag(&headerSeq)
silentFlag := newHeaderFlag(&headerSeq)
quietFlag := newHeaderFlag(&headerSeq)
silentFlag := newHeaderFlag(&headerSeq)
verboseFlag := newHeaderFlag(&headerSeq)
fs.VarP(quietFlag, "quiet", "q", "never print file name headers")
fs.Var(silentFlag, "silent", "alias for --quiet")
Expand Down Expand Up @@ -162,10 +162,10 @@ func registerFlags(fs *builtins.FlagSet) builtins.HandlerFunc {
// Bytes mode wins if -c/--bytes was parsed after -n/--lines.
useBytesMode := bytesFlag.pos > linesFlag.pos

countStr := linesFlag.val
countStr := linesFlag.val
modeLabel := "lines"
if useBytesMode {
countStr = bytesFlag.val
countStr = bytesFlag.val
modeLabel = "bytes"
}

Expand Down
38 changes: 19 additions & 19 deletions interp/builtins/wc/wc.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,8 @@ func countReader(ctx context.Context, r io.Reader) (counts, error) {
}
c.chars += int64(utf8.RuneCount(chunk))
// carryN bytes are subtracted here and will be re-added via
// n += carryN at the top of the next iteration.
c.bytes -= int64(carryN)
// n += carryN at the top of the next iteration.
c.bytes -= int64(carryN)

for i := 0; i < len(chunk); {
r, size := utf8.DecodeRune(chunk[i:])
Expand Down Expand Up @@ -353,25 +353,25 @@ func runeWidth(r rune) int {
// codepoints per UAX #11, matching the ranges used by wcwidth(3).
var eastAsianWide = &unicode.RangeTable{
R16: []unicode.Range16{
{0x1100, 0x115F, 1}, // Hangul Jamo initials
{0x2329, 0x232A, 1}, // CJK angle brackets
{0x2E80, 0x303E, 1}, // CJK Radicals Supplement .. CJK Symbols
{0x3040, 0x33BF, 1}, // Hiragana .. CJK Compatibility
{0x33C0, 0x33FF, 1}, // CJK Compatibility (cont.)
{0x3400, 0x4DBF, 1}, // CJK Unified Ideographs Extension A
{0x4E00, 0xA4CF, 1}, // CJK Unified Ideographs .. Yi
{0xAC00, 0xD7A3, 1}, // Hangul Syllables
{0xF900, 0xFAFF, 1}, // CJK Compatibility Ideographs
{0xFE10, 0xFE19, 1}, // Vertical Forms
{0xFE30, 0xFE6F, 1}, // CJK Compatibility Forms + Small Form Variants
{0xFF01, 0xFF60, 1}, // Fullwidth Forms
{0xFFE0, 0xFFE6, 1}, // Fullwidth Signs
{0x1100, 0x115F, 1}, // Hangul Jamo initials
{0x2329, 0x232A, 1}, // CJK angle brackets
{0x2E80, 0x303E, 1}, // CJK Radicals Supplement .. CJK Symbols
{0x3040, 0x33BF, 1}, // Hiragana .. CJK Compatibility
{0x33C0, 0x33FF, 1}, // CJK Compatibility (cont.)
{0x3400, 0x4DBF, 1}, // CJK Unified Ideographs Extension A
{0x4E00, 0xA4CF, 1}, // CJK Unified Ideographs .. Yi
{0xAC00, 0xD7A3, 1}, // Hangul Syllables
{0xF900, 0xFAFF, 1}, // CJK Compatibility Ideographs
{0xFE10, 0xFE19, 1}, // Vertical Forms
{0xFE30, 0xFE6F, 1}, // CJK Compatibility Forms + Small Form Variants
{0xFF01, 0xFF60, 1}, // Fullwidth Forms
{0xFFE0, 0xFFE6, 1}, // Fullwidth Signs
},
R32: []unicode.Range32{
{0x1F300, 0x1F64F, 1}, // Misc Symbols/Pictographs + Emoticons
{0x1F900, 0x1F9FF, 1}, // Supplemental Symbols and Pictographs
{0x20000, 0x2FFFD, 1}, // CJK Extension B..F
{0x30000, 0x3FFFD, 1}, // CJK Extension G+
{0x1F300, 0x1F64F, 1}, // Misc Symbols/Pictographs + Emoticons
{0x1F900, 0x1F9FF, 1}, // Supplemental Symbols and Pictographs
{0x20000, 0x2FFFD, 1}, // CJK Extension B..F
{0x30000, 0x3FFFD, 1}, // CJK Extension G+
},
}

Expand Down
1 change: 0 additions & 1 deletion interp/builtins/wc/wc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,4 +451,3 @@ func TestWcChunkBoundaryMultibyte(t *testing.T) {
// max line length: 32766 + 2 (emoji display width) = 32768
assert.Equal(t, "32768 32768 file.txt\n", stdout)
}

2 changes: 1 addition & 1 deletion interp/register_builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
"github.com/DataDog/rshell/interp/builtins"
breakcmd "github.com/DataDog/rshell/interp/builtins/break"
"github.com/DataDog/rshell/interp/builtins/cat"
"github.com/DataDog/rshell/interp/builtins/cut"
continuecmd "github.com/DataDog/rshell/interp/builtins/continue"
"github.com/DataDog/rshell/interp/builtins/cut"
"github.com/DataDog/rshell/interp/builtins/echo"
"github.com/DataDog/rshell/interp/builtins/exit"
falsecmd "github.com/DataDog/rshell/interp/builtins/false"
Expand Down
10 changes: 5 additions & 5 deletions tests/compliance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,11 @@ func TestComplianceLicense3rdPartyFormat(t *testing.T) {
// Known SPDX identifiers (extend as needed).
knownSPDX := map[string]bool{
"MIT": true,
"Apache-2.0": true,
"BSD-2-Clause": true,
"BSD-3-Clause": true,
"ISC": true,
"MPL-2.0": true,
"Apache-2.0": true,
"BSD-2-Clause": true,
"BSD-3-Clause": true,
"ISC": true,
"MPL-2.0": true,
"MIT AND Apache-2.0": true,
}

Expand Down
2 changes: 1 addition & 1 deletion tests/scenarios_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const dockerBashImage = "debian:bookworm-slim"
// scenario represents a single test scenario.
type scenario struct {
Description string `yaml:"description"`
SkipAssertAgainstBash bool `yaml:"skip_assert_against_bash"` // true = skip bash comparison
SkipAssertAgainstBash bool `yaml:"skip_assert_against_bash"` // true = skip bash comparison
Setup setup `yaml:"setup"`
Input input `yaml:"input"`
Expect expected `yaml:"expect"`
Expand Down
Loading