Skip to content
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

Improve option parsing #28

Merged
merged 3 commits into from
Mar 6, 2025
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
10 changes: 5 additions & 5 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,23 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Ensures all tags are fetched

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "stable"

- name: Build
run: go build -v ./cmd/...
run: make

- name: vet
run: go vet ./...

- uses: dominikh/staticcheck-action@v1.2.0
- uses: dominikh/staticcheck-action@v1
with:
version: "2022.1.1"
version: "latest"

- name: gofmt
uses: Jerome1337/[email protected]
Expand All @@ -45,5 +47,3 @@ jobs:
- name: Revive Action
uses: morphy2k/[email protected]

- name: Tests
run: go test -v ./...
45 changes: 45 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# This file is Free Software under the Apache-2.0 License
# without warranty, see README.md and LICENSES/Apache-2.0.txt for details.
#
# SPDX-License-Identifier: Apache-2.0
#
# SPDX-FileCopyrightText: 2025 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de>
# Software-Engineering: 2025 Intevation GmbH <https://intevation.de>

.PHONY: all fakedoc createtemplate test

all: fakedoc createtemplate test

# See comment here (2024-11-15)
# https://github.com/gocsaf/csaf/blob/3093f717817b9369d390e56d1012eaedcfa19e32/Makefile#L40-L49
GITDESC := $(shell git describe --tags --always)
GITDESCPATCH := $(shell echo '$(GITDESC)' | sed -E 's/v?[0-9]+\.[0-9]+\.([0-9]+)[-+]?.*/\1/')
SEMVERPATCH := $(shell echo $$(( $(GITDESCPATCH) + 1 )))
# Hint: The second regexp in the next line only matches
# if there is a hyphen (`-`) followed by a number,
# by which we assume that git describe has added a string after the tag
SEMVER := $(shell echo '$(GITDESC)' | sed -E -e 's/^v//' -e 's/([0-9]+\.[0-9]+\.)([0-9]+)(-[1-9].*)/\1$(SEMVERPATCH)\3/' )
testsemver:
@echo from \'$(GITDESC)\' transformed to \'$(SEMVER)\'

LDFLAGS=-ldflags "-X github.com/gocsaf/fakedoc/pkg/fakedoc.SemVersion=$(SEMVER)"
GO_FLAGS=$(LDFLAGS)

# Build for coverage profile generation
ifeq ($(BUILD_COVER), true)
GO_FLAGS += "-cover"
endif


fakedoc: build_pkg
cd cmd/fakedoc && go build $(GO_FLAGS)

createtemplate: build_pkg
cd cmd/createtemplate && go build $(GO_FLAGS)

build_pkg:
cd pkg && go build $(GO_FLAGS) ./...

test:
go test ./...

110 changes: 39 additions & 71 deletions cmd/fakedoc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
"bytes"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"log"
Expand All @@ -24,47 +23,9 @@
"strings"
"text/template"

"github.com/gocsaf/fakedoc/pkg/fakedoc"
)

const (
seedDocumentation = `
random number seed, format 'pcg:<1-16 hex digits>:<1-16 hex digits>'.
If omitted, the generator uses a random seed.
`

outputDocumentation = `
output filename. Setting this will also set the tracking ID in the
generated file so that it matches the filename. The filename must end
with '.json'
`

numOutputDocumentation = `
How many documents to generate . If greate than 1, the output filename
must be given. It is treated as a template for filenames in which {{$}}
will be replaced with the number of the file, starting with 0.
`

formattedDocumentation = `
Output JSON should be formatted.
`
"github.com/jessevdk/go-flags"

limitsDocumentation = `
Guidance on the Size of CSAF Documents.
`

sizeFactorDocumentation = `
Factor by which to multiply the maxima given in the limits file.
`

forceMaxSizeDocumentation = `
Try to force size of arrays to their maxiumum as defined in the limits
file and modified by the size factor.
`

requireDocumentation = `
Specifies with a regular expression what fields to force as required.
`
"github.com/gocsaf/fakedoc/pkg/fakedoc"
)

func check(err error) {
Expand All @@ -73,46 +34,53 @@
}
}

func main() {
var (
templatefile string
limitsfile string
sizeFactor float64
forceMaxSize bool
seed string
outputfile string
numOutputs int
formatted bool
requireRegex string
)
type Options struct {

Check warning on line 37 in cmd/fakedoc/main.go

View workflow job for this annotation

GitHub Actions / build

exported type Options should have comment or be unexported

Check warning on line 37 in cmd/fakedoc/main.go

View workflow job for this annotation

GitHub Actions / build

exported type Options should have comment or be unexported
Version bool `long:"version" description:"Display version of the binary"`
TemplateFile string `long:"template" short:"t" description:"Template file"`
LimitsFile string `long:"limit-file" short:"l" description:"Guidance on the Size of CSAF Documents."`
SizeFactor float64 `long:"size" default:"0.00001" description:"Factor by which to multiply the maxima given in the limits file."`
ForceMaxSize bool `long:"force-max-size" description:"Try to force size of arrays to their maxiumum as defined in the limits file and modified by the size factor."`
Seed string `long:"seed" description:"random number seed, format 'pcg:<1-16 hex digits>:<1-16 hex digits>'. If omitted, the generator uses a random seed."`
OutputFile string `long:"output-file" short:"o" description:"Output filename. Setting this will also set the tracking ID in the generated file so that it matches the filename. The filename must end with '.json'"`
NumOutputs int `long:"num-outputs" short:"n" default:"1" description:"How many documents to generate . If greater than 1, the output filename must be given. It is treated as a template for filenames in which {{$}} will be replaced with the number of the file, starting with 0."`
Formatted bool `long:"format" short:"f" description:"Output JSON should be formatted."`
RequireRegex string `long:"require" description:"Specifies with a regular expression what fields to force as required."`
}

flag.StringVar(&templatefile, "template", "", "template file")
flag.StringVar(&limitsfile, "l", "", limitsDocumentation)
flag.Float64Var(&sizeFactor, "size", 0.00001, sizeFactorDocumentation)
flag.BoolVar(&forceMaxSize, "force-max-size", false, forceMaxSizeDocumentation)
flag.StringVar(&seed, "seed", "", seedDocumentation)
flag.StringVar(&outputfile, "o", "", outputDocumentation)
flag.IntVar(&numOutputs, "n", 1, numOutputDocumentation)
flag.BoolVar(&formatted, "f", false, formattedDocumentation)
flag.StringVar(&requireRegex, "require", "", requireDocumentation)
func main() {
var opts Options
parser := flags.NewParser(&opts, flags.Default)
// Only used when compiled with 'profile' tag.
pf := addProfileFlags()
flag.Parse()
pf, err := addProfileFlags(parser)
check(err)

_, err = parser.Parse()
if err != nil {
if flags.WroteHelp(err) {
os.Exit(0)
}
log.Fatal(err)
}

if opts.Version {
fmt.Println(fakedoc.SemVersion)
return
}

if numOutputs > 1 && outputfile == "" {
if opts.NumOutputs > 1 && opts.OutputFile == "" {
log.Fatal("Multiple outputs require an explicit output file template")
}

rng, err := fakedoc.ParseSeed(seed)
rng, err := fakedoc.ParseSeed(opts.Seed)
check(err)

check(pf.profile(func() error {
return generate(
templatefile, rng,
outputfile, limitsfile,
sizeFactor, forceMaxSize,
numOutputs, formatted,
requireRegex)
opts.TemplateFile, rng,
opts.OutputFile, opts.LimitsFile,
opts.SizeFactor, opts.ForceMaxSize,
opts.NumOutputs, opts.Formatted,
opts.RequireRegex)
}))
}

Expand Down
6 changes: 4 additions & 2 deletions cmd/fakedoc/no_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@

package main

import "github.com/jessevdk/go-flags"

// profileFlags is empty.
type profileFlags struct{}

// addProfileFlags does nothing and returns nil.
func addProfileFlags() *profileFlags {
return nil
func addProfileFlags(_ *flags.Parser) (*profileFlags, error) {
return nil, nil
}

// profile only calls fn and returns its return value.
Expand Down
36 changes: 15 additions & 21 deletions cmd/fakedoc/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,40 +12,34 @@ package main

import (
"errors"
"flag"
"os"
"runtime"
"runtime/pprof"
)

const (
cpuProfileDocumentation = `
Name of the profile file. If empty (default) no profile file is written.
`
memProfileDocumentation = `
Name of the memory profile file. If empty (default) no memory profile file is written.
`
"github.com/jessevdk/go-flags"
)

type profileFlags struct {
// cpuProfile is the file name of the cpu profile.
cpuProfile string
// memProfile is the file name of the memory profile.
memProfile string
// CpuProfile is the file name of the cpu profile.
CpuProfile string `long:"cpuprofile" description:"Name of the profile file. If empty (default) no profile file is written."`
// MemProfile is the file name of the memory profile.
MemProfile string `long:"memprofile" description:"Name of the memory profile file. If empty (default) no memory profile file is written."`
}

// addProfileFlags adds flags for the profiler to the command line parser.
func addProfileFlags() *profileFlags {
func addProfileFlags(parser *flags.Parser) (*profileFlags, error) {
pf := profileFlags{}
flag.StringVar(&pf.cpuProfile, "cpuprofile", "", cpuProfileDocumentation)
flag.StringVar(&pf.memProfile, "memprofile", "", memProfileDocumentation)
return &pf
_, err := parser.AddGroup("Profile flags", "Configuration for profile collection", &pf)
if err != nil {
return nil, err
}
return &pf, nil
}

// profile create cpu and/or mery profile files for the given function.
func (pf *profileFlags) profile(fn func() error) error {
if pf.cpuProfile != "" {
f, err := os.Create(pf.cpuProfile)
if pf.CpuProfile != "" {
f, err := os.Create(pf.CpuProfile)
if err != nil {
return err
}
Expand All @@ -56,8 +50,8 @@ func (pf *profileFlags) profile(fn func() error) error {
defer pprof.StopCPUProfile()
}
ret := fn()
if pf.memProfile != "" {
f, err := os.Create(pf.memProfile)
if pf.MemProfile != "" {
f, err := os.Create(pf.MemProfile)
if err != nil {
return errors.Join(ret, err)
}
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ go 1.23

require (
github.com/BurntSushi/toml v1.4.0
github.com/go-loremipsum/loremipsum v1.1.3
github.com/jessevdk/go-flags v1.6.1
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
)

require github.com/go-loremipsum/loremipsum v1.1.3
require golang.org/x/sys v0.21.0 // indirect
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-loremipsum/loremipsum v1.1.3 h1:ZRhA0ZmJ49lGe5HhWeMONr+iGftWDsHfrYBl5ktDXso=
github.com/go-loremipsum/loremipsum v1.1.3/go.mod h1:OJQjXdvwlG9hsyhmMQoT4HOm4DG4l62CYywebw0XBoo=
github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
13 changes: 13 additions & 0 deletions pkg/fakedoc/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// This file is Free Software under the Apache-2.0 License
// without warranty, see README.md and LICENSES/Apache-2.0.txt for details.
//
// SPDX-License-Identifier: Apache-2.0
//
// SPDX-FileCopyrightText: 2024 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de>
// Software-Engineering: 2024 Intevation GmbH <https://intevation.de>

package fakedoc

// SemVersion the version in semver.org format, MUST be overwritten during
// the linking stage of the build process
var SemVersion = "0.0.0"
Loading