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
4 changes: 4 additions & 0 deletions .github/actions/prepare-ee-test-env/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ runs:
key: ${{ matrix.sdk-version }}

- name: Download Tarantool SDK
# Only download on a cache miss: the SDK is already restored from cache
# otherwise, and an unconditional re-download fails the whole job when
# download.tarantool.io is unavailable even though a good cache exists.
if: steps.cache-sdk.outputs.cache-hit != 'true'
run: |
ARCHIVE_NAME=tarantool-enterprise-sdk-${{ inputs.sdk-gc }}-${{ inputs.sdk-version }}.tar.gz
ARCHIVE_PATH=$(echo ${{ inputs.sdk-version }} | sed -rn \
Expand Down
4 changes: 3 additions & 1 deletion .github/actions/static-code-check/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ runs:
- name: pre-commit checks (diff)
uses: pre-commit/action@v3.0.1
env:
SKIP: golangci-lint-full
# golangci-lint-strict scans whole packages (not diff-based), so run it
# once in the "full" step below instead of here.
SKIP: golangci-lint-full,golangci-lint-strict
with:
extra_args: --all-files --from-ref=${{ env.BASE_BRANCH }} --to-ref=HEAD --hook-stage=manual

Expand Down
100 changes: 100 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
version: "2"

# NOTE: this config is intentionally scoped to the freshly written code only.
# The `cli/manifest` exclusion below limits all linters to that package so the
# strict ruleset can be adopted incrementally instead of flooding the whole tt
# tree with findings. Drop the `path-except` rule to lint everything.
#
# The project-wide lint still runs from `golangci-lint.yml` (see magefile.go);
# this `.golangci.yml` is what a bare `golangci-lint run` picks up.

run:
timeout: 3m
build-tags:
- go_tarantool_ssl_disable
- tt_ssl_disable

formatters:
enable:
- goimports

issues:
# Disable limits on the number of printed issues
max-issues-per-linter: 0 # 0 = no limit
max-same-issues: 0 # 0 = no limit

linters:
default: all

disable:
- dupl # Dupl is disabled, since we're generating a lot of boilerplate code.
- cyclop # Cyclop is disabled, since cyclomatic complexities is very abstract metric,
# that sometimes lead to strange linter behaviour.
- wsl # WSL is disabled, since it's obsolete. Using WSL_v5.
- nlreturn # nlreturn is disabled, since it's duplicated by wsl_v5.return check.
- ireturn # ireturn is disabled, since it's not needed.
- godox # godox is disabled to allow TODO comments for unimplemented functionality.
- gocognit # gocognit is disabled, cognitive complexity is too restrictive.
- funlen # funlen is disabled, function length limits are too restrictive.
- maintidx # maintidx is disabled, purpose of this metric is unknown.

exclusions:
generated: lax
rules:
# Scope linting to the freshly written code only. An issue is excluded
# when its path is NOT under cli/manifest/.
- path-except: (^|/)cli/manifest/
text: ".*"
- path: _test.go
linters:
- wrapcheck
- err113
- funlen

settings:
varnamelen:
ignore-names:
- tt
- ok
- tb
ignore-decls:
- t T
revive:
rules:
- name: var-naming
arguments:
- []
- []
- - skip-package-name-checks: true

exhaustive:
# A switch with a default clause is treated as exhaustive.
default-signifies-exhaustive: true
godot:
scope: all
lll:
line-length: 100
tab-width: 4
wsl_v5:
allow-first-in-block: true
allow-whole-block: false
branch-max-lines: 2
case-max-lines: 0
default: all
depguard:
rules:
main:
files:
- "$all"
- "!$test"
allow:
- $gostd
- "github.com/pelletier/go-toml/v2"
test:
files:
- "$test"
allow:
- $gostd
- "github.com/pelletier/go-toml/v2"
- "github.com/stretchr/testify"
- "github.com/tarantool/tt/cli/manifest"
15 changes: 15 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,21 @@ repos:
additional_dependencies:
- github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6

- id: golangci-lint-strict
language: golang
types: [go]
name: "Go: strict lint (migrated packages)"
description: |
Strict `default: all` ruleset, scoped by `.golangci.yml` to packages
that have been fully cleaned up (currently `cli/manifest`). Adopt it
for more code by widening the `path-except` rule in `.golangci.yml`.
pass_filenames: false
always_run: true
stages: [manual]
entry: golangci-lint run --config=.golangci.yml --allow-parallel-runners
additional_dependencies:
- github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.4

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.2
hooks:
Expand Down
80 changes: 80 additions & 0 deletions cli/manifest/component.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package manifest

// Build backends.
const (
backendMake = "make"
backendShell = "shell"
backendC = "c"
backendLuaC = "lua-c"
)

// Component is a group of files plus an optional build ([components.<name>]).
type Component struct {
Path string `toml:"path"` // Required.
Include []string `toml:"include,omitempty"` // Default *.lua, *.so.
Exclude []string `toml:"exclude,omitempty"`
Namespace *string `toml:"namespace,omitempty"` // Nil: pkg name; "": flat.
Dependencies map[string]Dependency `toml:"dependencies,omitempty"`
Build *Build `toml:"build,omitempty"`
}

// Build describes the native build of a component ([components.<name>.build]).
// The Hook type is an alias of Build; hooks reuse the same shape but allow only
// the make/shell backends.
type Build struct {
Backend string `toml:"backend"` // make|shell|c|lua-c - closed enum.
Cwd string `toml:"cwd,omitempty"`
Env map[string]string `toml:"env,omitempty"`
Output []string `toml:"output,omitempty"`

// make backend.
MakeTarget string `toml:"make_target,omitempty"`
Entrypoint string `toml:"entrypoint,omitempty"`
Flags []string `toml:"flags,omitempty"`

// shell backend.
Command string `toml:"command,omitempty"`
Args []string `toml:"args,omitempty"`

// c / lua-c backends.
Module string `toml:"module,omitempty"`
Sources []string `toml:"sources,omitempty"`
IncludeDirs []string `toml:"include_dirs,omitempty"`
Libraries []string `toml:"libraries,omitempty"`
LibraryDirs []string `toml:"library_dirs,omitempty"`
Defines []string `toml:"defines,omitempty"`
Platforms map[string]BuildOverlay `toml:"platforms,omitempty"`
}

// BuildOverlay is the per-OS addition to a c/lua-c build
// ([components.<name>.build.platforms.<os>]). It only adds to the base lists
// for the current OS.
type BuildOverlay struct {
IncludeDirs []string `toml:"include_dirs,omitempty"`
Libraries []string `toml:"libraries,omitempty"`
LibraryDirs []string `toml:"library_dirs,omitempty"`
Defines []string `toml:"defines,omitempty"`
}

// Product is a named set of components built and packed as a unit
// ([products.<name>]).
type Product struct {
Components []string `toml:"components"` // Required; names must exist in [components].
Default bool `toml:"default,omitempty"`
}

// Hook is a lifecycle hook ([hooks.pre_build]/[hooks.post_build]). It shares
// the Build shape, but only the make/shell backends are valid and there is no
// module/sources.
type Hook = Build

// EffectiveNamespace returns the install namespace of the component given the
// package name. An unset namespace falls back to the package name; an explicit
// empty string means a flat layout.
func (c Component) EffectiveNamespace(packageName string) string {
if c.Namespace == nil {
return packageName
}

return *c.Namespace
}
98 changes: 98 additions & 0 deletions cli/manifest/constraint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package manifest

import "strings"

// Constraint is a platform version requirement parsed into its semver part and
// an optional flavor. It is stored as "<semver-constraint>[<flavor>]" in TOML,
// where <flavor> is [ce] or [ee].
//
// The semver constraint itself is kept verbatim - validating the range is the
// resolver's job, not this layer's. Only the flavor suffix is parsed here.
//
//nolint:recvcheck // TextMarshaler needs a value receiver, UnmarshalText a pointer.
type Constraint struct {
// Version is the semver constraint part, e.g. ">=3.0.0,<4.0.0". Empty for
// an unset (omitted) constraint.
Version string
// Flavor is "ce", "ee" or "" (unspecified). For tarantool/tt an empty
// flavor means [ce]; see EffectiveFlavor.
Flavor string
}

// IsZero reports whether the constraint is unset.
func (c Constraint) IsZero() bool {
return c.Version == "" && c.Flavor == ""
}

// EffectiveFlavor returns the flavor with the [ce] default applied to an
// unspecified flavor.
func (c Constraint) EffectiveFlavor() string {
if c.Flavor == "" {
return "ce"
}

return c.Flavor
}

// String renders the constraint back to its "<constraint>[<flavor>]" form.
func (c Constraint) String() string {
if c.Flavor == "" {
return c.Version
}

return c.Version + "[" + c.Flavor + "]"
}

// MarshalText implements encoding.TextMarshaler so the constraint serializes as
// a TOML string.
func (c Constraint) MarshalText() ([]byte, error) {
return []byte(c.String()), nil
}

// UnmarshalText implements encoding.TextUnmarshaler. It splits off a trailing
// [flavor] suffix and validates the flavor token; an unknown flavor or a
// bracket suffix with no version is a parse error. The semver part is stored
// verbatim.
func (c *Constraint) UnmarshalText(text []byte) error {
version, flavor, err := splitFlavor(string(text))
if err != nil {
return err
}

c.Version = version
c.Flavor = flavor

return nil
}

// splitFlavor separates a "<version>[<flavor>]" string into its parts. A string
// with no trailing [flavor] suffix is returned verbatim as the version with an
// empty flavor. An unbalanced bracket, an unknown flavor token, or a suffix
// with no version before it is a parse error.
func splitFlavor(raw string) (string, string, error) {
if !strings.HasSuffix(raw, "]") {
return raw, "", nil
}

open := strings.LastIndex(raw, "[")
if open < 0 {
return "", "", invalid("", "invalid version constraint %q: unbalanced %q", raw, "[")
}

version := raw[:open]
flavor := raw[open+1 : len(raw)-1]

switch flavor {
case "ce", "ee":
default:
return "", "", invalid("",
"invalid version constraint %q: unknown flavor %q (want [ce] or [ee])", raw, flavor)
}

if version == "" {
return "", "", invalid("",
"invalid version constraint %q: missing version before flavor", raw)
}

return version, flavor, nil
}
Loading
Loading