chore: modernize CI and update Go toolchain

- Bump Go from 1.19 to 1.26 and update all dependencies
- Rewrite CI workflow with matrix strategy (Linux, macOS, Windows)
- Update GitHub Actions to current versions (checkout@v4, setup-go@v5)
- Update CodeQL actions from v1 to v3
- Fix cross-platform bug in mock/path.go (path.Join -> filepath.Join)
- Clean up dependabot config (weekly schedule, remove stale ignore)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Christopher Allen Lane
2026-02-14 20:58:51 -05:00
parent cc85a4bdb1
commit 2a19755804
657 changed files with 49050 additions and 32001 deletions

View File

@@ -21,9 +21,9 @@ type sshParser struct {
type sshParserStateFn func() sshParserStateFn
// Formats and panics an error message based on a token
func (p *sshParser) raiseErrorf(tok *token, msg string, args ...interface{}) {
func (p *sshParser) raiseErrorf(tok *token, msg string) {
// TODO this format is ugly
panic(tok.Position.String() + ": " + fmt.Sprintf(msg, args...))
panic(tok.Position.String() + ": " + msg)
}
func (p *sshParser) raiseError(tok *token, err error) {
@@ -105,9 +105,7 @@ func (p *sshParser) parseKV() sshParserStateFn {
comment = tok.val
}
if strings.ToLower(key.val) == "match" {
// https://github.com/kevinburke/ssh_config/issues/6
p.raiseErrorf(val, "ssh_config: Match directive parsing is unsupported")
return nil
return p.parseMatch(val, hasEquals, comment)
}
if strings.ToLower(key.val) == "host" {
strPatterns := strings.Split(val.val, " ")
@@ -118,7 +116,7 @@ func (p *sshParser) parseKV() sshParserStateFn {
}
pat, err := NewPattern(strPatterns[i])
if err != nil {
p.raiseErrorf(val, "Invalid host pattern: %v", err)
p.raiseErrorf(val, fmt.Sprintf("Invalid host pattern: %v", err))
return nil
}
patterns = append(patterns, pat)
@@ -144,7 +142,7 @@ func (p *sshParser) parseKV() sshParserStateFn {
return nil
}
if err != nil {
p.raiseErrorf(val, "Error parsing Include directive: %v", err)
p.raiseErrorf(val, fmt.Sprintf("Error parsing Include directive: %v", err))
return nil
}
lastHost.Nodes = append(lastHost.Nodes, inc)
@@ -165,6 +163,73 @@ func (p *sshParser) parseKV() sshParserStateFn {
return p.parseStart
}
func (p *sshParser) parseMatch(val *token, hasEquals bool, comment string) sshParserStateFn {
// val.val contains everything after "Match ", e.g. "Host *.example.com"
// or "all".
trimmed := strings.TrimRightFunc(val.val, unicode.IsSpace)
spaceBeforeComment := val.val[len(trimmed):]
fields := strings.Fields(trimmed)
if len(fields) == 0 {
p.raiseErrorf(val, "ssh_config: Match directive requires at least one criterion")
return nil
}
criterion := strings.ToLower(fields[0])
switch criterion {
case "all":
// "Match all" is equivalent to "Host *" — matches everything.
p.config.Hosts = append(p.config.Hosts, &Host{
Patterns: []*Pattern{matchAll},
Nodes: make([]Node, 0),
EOLComment: comment,
spaceBeforeComment: spaceBeforeComment,
hasEquals: hasEquals,
isMatch: true,
matchKeyword: fields[0], // preserve original case
})
return p.parseStart
case "host":
patterns := make([]*Pattern, 0)
for _, s := range fields[1:] {
if s == "" {
continue
}
pat, err := NewPattern(s)
if err != nil {
p.raiseErrorf(val, fmt.Sprintf("Invalid host pattern: %v", err))
return nil
}
patterns = append(patterns, pat)
}
if len(patterns) == 0 {
p.raiseErrorf(val, "ssh_config: Match Host requires at least one pattern")
return nil
}
p.config.Hosts = append(p.config.Hosts, &Host{
Patterns: patterns,
Nodes: make([]Node, 0),
EOLComment: comment,
spaceBeforeComment: spaceBeforeComment,
hasEquals: hasEquals,
isMatch: true,
matchKeyword: fields[0], // preserve original case
})
return p.parseStart
case "exec":
// Match Exec runs arbitrary commands. Supporting it would allow
// untrusted SSH config files to execute code on the parsing
// machine. Reject it explicitly.
p.raiseErrorf(val, "ssh_config: Match Exec is not supported")
return nil
default:
p.raiseErrorf(val, fmt.Sprintf("ssh_config: unsupported Match criterion %q", criterion))
return nil
}
}
func (p *sshParser) parseComment() sshParserStateFn {
comment := p.getToken()
lastHost := p.config.Hosts[len(p.config.Hosts)-1]