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
2 changes: 1 addition & 1 deletion scaffold/.go/.github/workflows/test-and-lint.yaml.tmplt
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ jobs:
- name: Lint with golangci-lint
uses: golangci/golangci-lint-action@v8
with:
version: "{% .GolangcilintTag %}"
version: v{% .GolangcilintVersion %}
4 changes: 2 additions & 2 deletions scaffold/.go/.pre-commit-config.yaml.tmplt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
repos:
- repo: https://github.com/golangci/golangci-lint
rev: "{% .GolangcilintTag %}"
rev: v{% .GolangcilintVersion %}
hooks:
- id: golangci-lint-config-verify
- id: golangci-lint-fmt
- id: golangci-lint-full

- repo: https://github.com/godaddy/tartufo
rev: "{% .TartufoTag %}"
rev: v{% .TartufoVersion %}
hooks:
- id: tartufo
7 changes: 0 additions & 7 deletions scaffold/.python/.flake8.tmplt

This file was deleted.

14 changes: 5 additions & 9 deletions scaffold/.python/.pre-commit-config.yaml.tmplt
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
repos:
- repo: https://github.com/psf/black
rev: "{% .BlackVersion %}"
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v{% .RuffVersion %}
hooks:
- id: black

- repo: https://github.com/PyCQA/flake8
rev: "{% .Flake8Version %}"
hooks:
- id: flake8
- id: ruff-check
- id: ruff-format

- repo: https://github.com/kxue43/shell-cmd-on-change
rev: "2.0.0"
Expand All @@ -23,6 +19,6 @@ repos:
verbose: true

- repo: https://github.com/godaddy/tartufo
rev: "{% .TartufoTag %}"
rev: v{% .TartufoVersion %}
hooks:
- id: tartufo
17 changes: 6 additions & 11 deletions scaffold/.python/pyproject.toml.tmplt
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,23 @@ default-groups = [

[dependency-groups]
linting = [
"black=={% .BlackVersion %}", # This version must be the same as in `./.pre-commit-config.yaml`.
"flake8=={% .Flake8Version %}", # This version must be the same as in `./.pre-commit-config.yaml`.
"mypy~={% .MypyVersion %}",
"ruff~={% .RuffVersion.MajorMinor %}",
"mypy~={% .MypyVersion.MajorMinor %}",
]

test = [
"pytest~={% .PytestVersion %}",
"pytest-mock~={% .PytestMockVersion %}",
"pytest-cov~={% .PytestCovVersion %}",
"pytest~={% .PytestVersion.MajorMinor %}",
"pytest-mock~={% .PytestMockVersion.MajorMinor %}",
"pytest-cov~={% .PytestCovVersion.MajorMinor %}",
]

docs = [
"sphinx~={% .SphinxVersion %}",
"sphinx~={% .SphinxVersion.MajorMinor %}",
"sphinx-rtd-theme",
"sphinx-autodoc-typehints",
"sphinx-copybutton",
]

[tool.black]
line-length = 88
target-version = ["py{% .PythonVersion.NumsOnly %}"]

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "--cov=src --cov-report term-missing"
Expand Down
161 changes: 102 additions & 59 deletions scaffold/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,26 @@ import (

type (
GoProjectCmd struct {
rootDir string
ModulePath string `arg:"" required:"" name:"ModulePath" help:"Module path for the project."`
GoVersion string `name:"go-version" default:"1.24.1" help:"Will appear in go.mod and GitHub Actions workflow."`
GolangcilintTag string `name:"golangci-lint-tag" default:"LATEST" help:"GitHub tag of golangci-lint."`
TartufoTag string `name:"tartufo-tag" default:"LATEST" help:"GitHub tag of tartufo."`
VersionSetters []VersionSetter `kong:"-"`
TimeoutSeconds int `name:"timeout-seconds" default:"1" help:"Timeout scaffolding after this many seconds."`
rootDir string
ModulePath string `arg:"" required:"" name:"ModulePath" help:"Module path for the project."`
GoVersion string `name:"go-version" default:"1.24.1" help:"Will appear in go.mod and GitHub Actions workflow."`
GolangcilintVersion SemVer `name:"golangci-lint-version" default:"LATEST" help:"Version of golangci-lint, a pre-commit hook."`
TartufoVersion SemVer `name:"tartufo-version" default:"LATEST" help:"Version of tartufo, a pre-commit hook."`
VersionSetters []VersionSetter `kong:"-"`
TimeoutSeconds int `name:"timeout-seconds" default:"1" help:"Timeout scaffolding after this many seconds."`
}

PythonProjectCmd struct {
rootDir string
ProjectName string `arg:"" required:"" name:"ProjectName" help:"Python project name."`
Description string `name:"description" default:"PLACEHOLDER" help:"Short description of the project"`
BlackVersion string `name:"black-version" default:"LATEST" help:"Exact major.minor.bugfix version of black."`
Flake8Version string `name:"flake8-version" default:"LATEST" help:"Exact major.minor.bugfix version of flake8."`
TartufoTag string `name:"tartufo-tag" default:"LATEST" help:"GitHub tag of tartufo."`
MypyVersion string `name:"mypy-version" default:"LATEST" help:"Major and minor version of the format X.Y for mypy."`
PytestVersion string `name:"pytest-version" default:"LATEST" help:"Major and minor version of the format X.Y for pytest."`
PytestMockVersion string `name:"pytest-mock-version" default:"LATEST" help:"Major and minor version of the format X.Y for pytest-mock."`
PytestCovVersion string `name:"pytest-cov-version" default:"LATEST" help:"Major and minor version of the format X.Y for pytest-cov."`
SphinxVersion string `name:"sphinx-version" default:"LATEST" help:"Major and minor version of the format X.Y for sphinx."`
TartufoVersion SemVer `name:"tartufo-version" default:"LATEST" help:"Version of tartufo, a pre-commit hook."`
RuffVersion SemVer `name:"ruff-version" default:"LATEST" help:"Version of ruff, a linting dependency."`
MypyVersion SemVer `name:"mypy-version" default:"LATEST" help:"Version of mypy, a linting dependency."`
PytestVersion SemVer `name:"pytest-version" default:"LATEST" help:"Version of pytest, a test dependency."`
PytestMockVersion SemVer `name:"pytest-mock-version" default:"LATEST" help:"Version of pytest-mock, a test dependency."`
PytestCovVersion SemVer `name:"pytest-cov-version" default:"LATEST" help:"Version of pytest-cov, a test dependency."`
SphinxVersion SemVer `name:"sphinx-version" default:"LATEST" help:"Version of sphinx, a doc dependency."`
PythonVersion PythonVersion `name:"python-version" required:"" help:"Python 3 interpreter version. Only accept major and minor version, i.e. the 3.Y format."`
VersionSetters []VersionSetter `kong:"-"`
TimeoutSeconds int `name:"timeout-seconds" default:"1" help:"Timeout scaffolding after this many seconds."`
Expand All @@ -52,20 +51,20 @@ type (
TsCdkProjectCmd struct {
rootDir string
ProjectName string `arg:"" required:"" name:"ProjectName" help:"TypeScript CDK project name."`
EslintVersion string `name:"eslint-version" default:"LATEST" help:"Will appear in package.json."`
EslintJsVersion string `name:"eslint-js-version" default:"LATEST" help:"Will appear in package.json."`
TypeScriptEslintVersion string `name:"typescript-eslint-version" default:"LATEST" help:"Will appear in package.json."`
VitestVersion string `name:"vitest-version" default:"LATEST" help:"Will appear in package.json."`
VitestCoverageV8Version string `name:"vitest-coverage-v8-version" default:"LATEST" help:"Will appear in package.json."`
AwsCdkCliVersion string `name:"aws-cdk-cli-version" default:"LATEST" help:"Will appear in package.json."`
EsbuildVersion string `name:"esbuild-version" default:"LATEST" help:"Will appear in package.json."`
PrettierVersion string `name:"prettier-version" default:"LATEST" help:"Will appear in package.json."`
TsxVersion string `name:"tsx-version" default:"LATEST" help:"Will appear in package.json."`
TypeScriptVersion string `name:"typescript-version" default:"LATEST" help:"Will appear in package.json."`
AwsCdkAssertVersion string `name:"aws-cdk-assert-version" default:"LATEST" help:"Will appear in package.json."`
AwsCdkLibVersion string `name:"aws-cdk-lib-version" default:"LATEST" help:"Will appear in package.json."`
ConstructsVersion string `name:"constructs-version" default:"LATEST" help:"Will appear in package.json."`
YamlVersion string `name:"yaml-version" default:"LATEST" help:"Will appear in package.json."`
EslintVersion SemVer `name:"eslint-version" default:"LATEST" help:"Version of eslint, a dev dependency."`
EslintJsVersion SemVer `name:"eslint-js-version" default:"LATEST" help:"Version of eslint-js, a dev dependency."`
TypeScriptEslintVersion SemVer `name:"typescript-eslint-version" default:"LATEST" help:"Version of typescript-eslint, a dev dependency."`
VitestVersion SemVer `name:"vitest-version" default:"LATEST" help:"Version of vitest, a test dependency."`
VitestCoverageV8Version SemVer `name:"vitest-coverage-v8-version" default:"LATEST" help:"Version of vitest-coverage, a test dependency."`
AwsCdkCliVersion SemVer `name:"aws-cdk-cli-version" default:"LATEST" help:"Version of the AWS CDK CLI."`
AwsCdkLibVersion SemVer `name:"aws-cdk-lib-version" default:"LATEST" help:"Version of the AWS CDK library."`
ConstructsVersion SemVer `name:"constructs-version" default:"LATEST" help:"Version of the constructs library."`
EsbuildVersion SemVer `name:"esbuild-version" default:"LATEST" help:"Version of esbuild, a dev dependency."`
PrettierVersion SemVer `name:"prettier-version" default:"LATEST" help:"Version of prettier, a dev dependency."`
TsxVersion SemVer `name:"tsx-version" default:"LATEST" help:"Version of tsx, a dev dependency."`
TypeScriptVersion SemVer `name:"typescript-version" default:"LATEST" help:"Version of typescript."`
AwsCdkAssertVersion SemVer `name:"aws-cdk-assert-version" default:"LATEST" help:"Version of aws-cdk-assert, a test dependency."`
YamlVersion SemVer `name:"yaml-version" default:"LATEST" help:"Version of yaml, a test dependency."`
NodejsVersion NodejsVersion `name:"nodejs-version" required:"" help:"Only accept major and minor version, i.e. the X.Y format."`
VersionSetters []VersionSetter `kong:"-"`
TimeoutSeconds int `name:"timeout-seconds" default:"1" help:"Timeout scaffolding after this many seconds."`
Expand All @@ -83,14 +82,20 @@ type (
Minor string
}

SemVer struct {
major string
minor string
bugfix string
set bool
}

Registry byte

VersionSetter struct {
Indirect *string
Scope string
Name string
Registry Registry
MajorMinorOnly bool
Indirect *SemVer
Scope string
Name string
Registry Registry
}

setterFunc func(context.Context) error
Expand Down Expand Up @@ -120,15 +125,15 @@ var (

tmpltExt = ".tmplt"

versionRegex = regexp.MustCompile(`^(?:v)?(\d+\.\d+)(?:\.\d+)?$`)
semVerRegex = regexp.MustCompile(`^(\d+)\.(\d+)(\..+)?$`)
)

func (pv *PythonVersion) UnmarshalText(text []byte) error {
regex := regexp.MustCompile(`^3\.(\d+)$`)

m := regex.FindStringSubmatch(string(text))
if len(m) == 0 {
return fmt.Errorf(`%s is not of the "3\.(\d+)" format`, string(text))
return fmt.Errorf(`%s is not of the %s format`, string(text), regex)
}

pv.Major = "3"
Expand All @@ -150,7 +155,7 @@ func (nv *NodejsVersion) UnmarshalText(text []byte) error {

m := regex.FindStringSubmatch(string(text))
if len(m) == 0 {
return fmt.Errorf(`%s is not of the "(\d+)\.(\d+)" format`, string(text))
return fmt.Errorf(`%s is not of the %s format`, string(text), regex)
}

nv.Major = m[1]
Expand All @@ -163,6 +168,47 @@ func (nv *NodejsVersion) String() string {
return nv.Major + "." + nv.Minor
}

func (sv *SemVer) String() string {
return fmt.Sprintf("%s.%s.%s", sv.major, sv.minor, sv.bugfix)
}

func (sv *SemVer) MajorMinor() string {
return fmt.Sprintf("%s.%s", sv.major, sv.minor)
}

func (sv *SemVer) UnmarshalText(text []byte) error {
if strings.EqualFold(string(text), "LATEST") {
return nil
}

m := semVerRegex.FindStringSubmatch(string(text))
if len(m) == 0 {
return fmt.Errorf(`%s is not of the %s format`, string(text), semVerRegex)
}

sv.major = m[1]
sv.minor = m[2]
sv.bugfix = "0"

if m[3] != "" {
sv.bugfix = strings.TrimPrefix(m[3], ".")
}

sv.set = true

return nil
}

func (sv *SemVer) SetFromString(raw string) error {
raw = strings.TrimPrefix(raw, "v")

return sv.UnmarshalText([]byte(raw))
}

func (sv *SemVer) Set() bool {
return sv.set
}

func ToModFile(modulePath, goVersion string) WriteHook {
return func(fd io.Writer) error {
goModFile := new(modfile.File)
Expand Down Expand Up @@ -367,31 +413,29 @@ func writeFiles(dest string, srcFS embed.FS, srcPrefix string, data any) (err er
}

func (vs VersionSetter) Func(ctx context.Context) (err error) {
var rawSpecifier string

switch vs.Registry {
case GitHub:
*vs.Indirect, err = GitHubProjectLatestReleaseTag(ctx, vs.Scope, vs.Name)
rawSpecifier, err = GitHubProjectLatestReleaseTag(ctx, vs.Scope, vs.Name)
if err != nil {
return fmt.Errorf("failed to fetch the latest version of %s/%s from GitHub: %w", vs.Scope, vs.Name, err)
return fmt.Errorf("failed to fetch the latest tag of %s/%s from GitHub: %w", vs.Scope, vs.Name, err)
}
case PyPI:
*vs.Indirect, err = PyPIPackageLatestVersion(ctx, vs.Name)
rawSpecifier, err = PyPIPackageLatestVersion(ctx, vs.Name)
if err != nil {
return fmt.Errorf("failed to fetch the latest version of %s from PyPI: %w", vs.Name, err)
}
case NPM:
*vs.Indirect, err = NPMPackageLatestVersion(ctx, vs.Name)
rawSpecifier, err = NPMPackageLatestVersion(ctx, vs.Name)
if err != nil {
return fmt.Errorf("failed to fetch the latest version of %s from NPM: %w", vs.Name, err)
}
}

if vs.MajorMinorOnly {
m := versionRegex.FindStringSubmatch(*vs.Indirect)
if len(m) == 0 {
return fmt.Errorf("failed to extract major and minor versions from %q for package %s", *vs.Indirect, vs.Name)
}

*vs.Indirect = m[1]
err = vs.Indirect.SetFromString(rawSpecifier)
if err != nil {
return fmt.Errorf("failed to parse %q as a semantic version specifier: %s", rawSpecifier, err.Error())
}

return nil
Expand All @@ -401,7 +445,7 @@ func getSetterFuncs(vss []VersionSetter) []setterFunc {
setterFuncs := make([]setterFunc, 0, len(vss))

for i := range vss {
if *vss[i].Indirect == "LATEST" {
if !vss[i].Indirect.Set() {
setterFuncs = append(setterFuncs, vss[i].Func)
}
}
Expand Down Expand Up @@ -435,8 +479,8 @@ func setVersions(ctx context.Context, fns []setterFunc) error {

func (c *GoProjectCmd) BeforeReset() error {
c.VersionSetters = []VersionSetter{
{Registry: GitHub, Scope: "golangci", Name: "golangci-lint", Indirect: &c.GolangcilintTag},
{Registry: GitHub, Scope: "godaddy", Name: "tartufo", Indirect: &c.TartufoTag},
{Registry: GitHub, Scope: "golangci", Name: "golangci-lint", Indirect: &c.GolangcilintVersion},
{Registry: GitHub, Scope: "godaddy", Name: "tartufo", Indirect: &c.TartufoVersion},
}

return nil
Expand Down Expand Up @@ -471,14 +515,13 @@ func (c *GoProjectCmd) Run() (err error) {

func (c *PythonProjectCmd) BeforeReset() error {
c.VersionSetters = []VersionSetter{
{Registry: GitHub, Scope: "psf", Name: "black", Indirect: &c.BlackVersion},
{Registry: PyPI, Name: "flake8", Indirect: &c.Flake8Version},
{Registry: PyPI, Name: "mypy", Indirect: &c.MypyVersion, MajorMinorOnly: true},
{Registry: PyPI, Name: "pytest", Indirect: &c.PytestVersion, MajorMinorOnly: true},
{Registry: PyPI, Name: "pytest-mock", Indirect: &c.PytestMockVersion, MajorMinorOnly: true},
{Registry: PyPI, Name: "pytest-cov", Indirect: &c.PytestCovVersion, MajorMinorOnly: true},
{Registry: PyPI, Name: "Sphinx", Indirect: &c.SphinxVersion, MajorMinorOnly: true},
{Registry: GitHub, Scope: "godaddy", Name: "tartufo", Indirect: &c.TartufoTag},
{Registry: PyPI, Name: "ruff", Indirect: &c.RuffVersion},
{Registry: PyPI, Name: "mypy", Indirect: &c.MypyVersion},
{Registry: PyPI, Name: "pytest", Indirect: &c.PytestVersion},
{Registry: PyPI, Name: "pytest-mock", Indirect: &c.PytestMockVersion},
{Registry: PyPI, Name: "pytest-cov", Indirect: &c.PytestCovVersion},
{Registry: PyPI, Name: "Sphinx", Indirect: &c.SphinxVersion},
{Registry: GitHub, Scope: "godaddy", Name: "tartufo", Indirect: &c.TartufoVersion},
}

return nil
Expand Down
Loading