Skip to content

[gen readme] Implement gen readme command #1728

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

Merged
merged 2 commits into from
Jan 18, 2024
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: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ See the [CLI Reference](https://www.jetpack.io/devbox/docs/cli_reference/devbox/

Devbox is an opensource project so contributions are always welcome. Please read [our contributing guide](CONTRIBUTING.md) before submitting pull requests.

[Devbox development readme](devbox.md)

## Related Work

Thanks to [Nix](https://nixos.org/) for providing isolated shells.
Expand Down
10 changes: 8 additions & 2 deletions devbox.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"name": "devbox",
"description": "Instant, easy, and predictable development environments",
"packages": {
"go": "latest",
"runx:golangci/golangci-lint": "latest",
Expand All @@ -13,7 +15,9 @@
"test -z $FISH_VERSION && unset CGO_ENABLED GO111MODULE GOARCH GOFLAGS GOMOD GOOS GOROOT GOTOOLCHAIN GOWORK",
],
"scripts": {
"build": "go build -o dist/devbox ./cmd/devbox",
"build":
// Build devbox for the current platform
"go build -o dist/devbox ./cmd/devbox",
"build-darwin-amd64": "GOOS=darwin GOARCH=amd64 go build -o dist/devbox-darwin-amd64 ./cmd/devbox",
"build-darwin-arm64": "GOOS=darwin GOARCH=arm64 go build -o dist/devbox-darwin-arm64 ./cmd/devbox",
"build-linux-amd64": "GOOS=linux GOARCH=amd64 go build -o dist/devbox-linux-amd64 ./cmd/devbox",
Expand All @@ -24,7 +28,9 @@
"devbox run build-linux-amd64",
"devbox run build-linux-arm64",
],
"code": "code .",
"code":
// Open VSCode
"code .",
"lint": "golangci-lint run --timeout 5m && scripts/gofumpt.sh",
"fmt": "scripts/gofumpt.sh",
"test": "go test -race -cover ./...",
Expand Down
103 changes: 103 additions & 0 deletions devbox.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<!-- gen-readme start - generated by https://github.com/jetpack-io/devbox/ -->
# devbox

Instant, easy, and predictable development environments

## Scripts

* [build](#build)
* [build-all](#build-all)
* [build-darwin-amd64](#build-darwin-amd64)
* [build-darwin-arm64](#build-darwin-arm64)
* [build-linux-amd64](#build-linux-amd64)
* [build-linux-arm64](#build-linux-arm64)
* [code](#code)
* [fmt](#fmt)
* [lint](#lint)
* [test](#test)
* [tidy](#tidy)
* [update-examples](#update-examples)

## Init Hook

```sh
test -z $FISH_VERSION && unset CGO_ENABLED GO111MODULE GOARCH GOFLAGS GOMOD GOOS GOROOT GOTOOLCHAIN GOWORK
```

## Packages

* [go@latest](https://www.nixhub.io/packages/go)
* [runx:golangci/golangci-lint@latest](https://www.github.com/golangci/golangci-lint)
* [runx:mvdan/gofumpt@latest](https://www.github.com/mvdan/gofumpt)
* nixpkgs/63143ac2c9186be6d9da6035fa22620018c85932#hello

## Script Details

### build
Build devbox for the current platform
```sh
go build -o dist/devbox ./cmd/devbox
```

### build-all
```sh
devbox run build-darwin-amd64
devbox run build-darwin-arm64
devbox run build-linux-amd64
devbox run build-linux-arm64
```

### build-darwin-amd64
```sh
GOOS=darwin GOARCH=amd64 go build -o dist/devbox-darwin-amd64 ./cmd/devbox
```

### build-darwin-arm64
```sh
GOOS=darwin GOARCH=arm64 go build -o dist/devbox-darwin-arm64 ./cmd/devbox
```

### build-linux-amd64
```sh
GOOS=linux GOARCH=amd64 go build -o dist/devbox-linux-amd64 ./cmd/devbox
```

### build-linux-arm64
```sh
GOOS=linux GOARCH=arm64 go build -o dist/devbox-linux-arm64 ./cmd/devbox
```

### code
Open VSCode
```sh
code .
```

### fmt
```sh
scripts/gofumpt.sh
```

### lint
```sh
golangci-lint run --timeout 5m && scripts/gofumpt.sh
```

### test
```sh
go test -race -cover ./...
```

### tidy
```sh
go mod tidy
```

### update-examples
```sh
devbox run build && go run testscripts/testrunner/updater/main.go
```



<!-- gen-readme end -->
25 changes: 25 additions & 0 deletions internal/boxcli/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"go.jetpack.io/devbox/internal/cloud"
"go.jetpack.io/devbox/internal/devbox"
"go.jetpack.io/devbox/internal/devbox/devopt"
"go.jetpack.io/devbox/internal/devbox/docgen"
)

type generateCmdFlags struct {
Expand All @@ -26,6 +27,7 @@ func generateCmd() *cobra.Command {

command := &cobra.Command{
Use: "generate",
Aliases: []string{"gen"},
Short: "Generate supporting files for your project",
Args: cobra.MaximumNArgs(0),
PersistentPreRunE: ensureNixInstalled,
Expand All @@ -34,6 +36,7 @@ func generateCmd() *cobra.Command {
command.AddCommand(dockerfileCmd())
command.AddCommand(debugCmd())
command.AddCommand(direnvCmd())
command.AddCommand(genReadmeCmd())
command.AddCommand(sshConfigCmd())
flags.config.register(command)

Expand Down Expand Up @@ -136,6 +139,28 @@ func sshConfigCmd() *cobra.Command {
return command
}

func genReadmeCmd() *cobra.Command {
flags := &generateCmdFlags{}
command := &cobra.Command{
Use: "readme [filename]",
Short: "Generate markdown readme file for this project",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
box, err := devbox.Open(&devopt.Opts{
Dir: flags.config.path,
Environment: flags.config.environment,
Stderr: cmd.ErrOrStderr(),
})
if err != nil {
return errors.WithStack(err)
}
return docgen.GenerateReadme(box, args[0])
},
}
flags.config.register(command)
return command
}

func runGenerateCmd(cmd *cobra.Command, flags *generateCmdFlags) error {
// Check the directory exists.
box, err := devbox.Open(&devopt.Opts{
Expand Down
12 changes: 6 additions & 6 deletions internal/devbox/devbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func (d *Devbox) ConfigHash() (string, error) {

buf := bytes.Buffer{}
buf.WriteString(h)
for _, pkg := range d.configPackages() {
for _, pkg := range d.ConfigPackages() {
buf.WriteString(pkg.Hash())
}
for _, inc := range d.Includes() {
Expand Down Expand Up @@ -1002,15 +1002,15 @@ func (d *Devbox) PackageNames() []string {
return d.cfg.Packages.VersionedNames()
}

// configPackages returns the packages that are defined in devbox.json
// ConfigPackages returns the packages that are defined in devbox.json
// NOTE: the return type is different from devconfig.Packages
func (d *Devbox) configPackages() []*devpkg.Package {
func (d *Devbox) ConfigPackages() []*devpkg.Package {
return devpkg.PackagesFromConfig(d.cfg, d.lockfile)
}

// InstallablePackages returns the packages that are to be installed
func (d *Devbox) InstallablePackages() []*devpkg.Package {
return lo.Filter(d.configPackages(), func(pkg *devpkg.Package, _ int) bool {
return lo.Filter(d.ConfigPackages(), func(pkg *devpkg.Package, _ int) bool {
return pkg.IsInstallable()
})
}
Expand All @@ -1033,7 +1033,7 @@ func (d *Devbox) Includes() []plugin.Includable {
}

func (d *Devbox) HasDeprecatedPackages() bool {
for _, pkg := range d.configPackages() {
for _, pkg := range d.ConfigPackages() {
if pkg.IsLegacy() {
return true
}
Expand All @@ -1046,7 +1046,7 @@ func (d *Devbox) findPackageByName(name string) (*devpkg.Package, error) {
return nil, errors.New("package name cannot be empty")
}
results := map[*devpkg.Package]bool{}
for _, pkg := range d.configPackages() {
for _, pkg := range d.ConfigPackages() {
if pkg.Raw == name || pkg.CanonicalName() == name {
results[pkg] = true
}
Expand Down
33 changes: 33 additions & 0 deletions internal/devbox/docgen/docgen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package docgen

import (
_ "embed"
"os"
"text/template"

"go.jetpack.io/devbox/internal/devbox"
)

//go:embed readme.tmpl
var readmeTemplate string

func GenerateReadme(devbox *devbox.Devbox, path string) error {
t, err := template.New("readme").Parse(readmeTemplate)
if err != nil {
return err
}

f, err := os.Create(path)
if err != nil {
return err
}

return t.Execute(f, map[string]any{
"Name": devbox.Config().Name,
"Description": devbox.Config().Description,
"Scripts": devbox.Config().Scripts(),
"EnvVars": devbox.Config().Env,
"InitHook": devbox.Config().InitHook(),
"Packages": devbox.ConfigPackages(),
})
}
56 changes: 56 additions & 0 deletions internal/devbox/docgen/readme.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!-- gen-readme start - generated by https://github.com/jetpack-io/devbox/ -->

{{- if .Name }}
# {{ .Name }}
{{ end }}

{{- if .Description }}
{{ .Description }}
{{ end }}

{{- if .Scripts }}
## Scripts
{{ range $name, $_ := .Scripts }}
* [{{ $name }}](#{{ $name }})
{{- end }}
{{ end }}

{{- if .Env }}
## Environment

```sh
{{- range $key, $value := .Env }}
{{ $key }}="{{ $value }}"
{{- end }}
```
{{ end }}

{{- if .InitHook }}
## Init Hook

```sh
{{ .InitHook }}
```
{{ end }}

{{- if .Packages }}
## Packages
{{ range .Packages }}
* {{ if .DocsURL }}[{{ .Raw }}]({{ .DocsURL }}){{ else }}{{ .Raw }}{{ end }}
{{- end }}
{{ end }}

{{- if .Scripts }}
## Script Details
{{ range $name, $commands := .Scripts }}
### {{ $name }}
{{- if .Comments }}
{{ .Comments }}
{{- end }}
```sh
{{ $commands }}
```
{{ end }}
{{ end }}

<!-- gen-readme end -->
2 changes: 1 addition & 1 deletion internal/devbox/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ func (d *Devbox) ensureStateIsUpToDate(ctx context.Context, mode installMode) er
d.lockfile.Tidy()

// Update lockfile with new packages that are not to be installed
for _, pkg := range d.configPackages() {
for _, pkg := range d.ConfigPackages() {
if err := pkg.EnsureUninstallableIsInLockfile(); err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion internal/devbox/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (d *Devbox) inputsToUpdate(
opts devopt.UpdateOpts,
) ([]*devpkg.Package, error) {
if len(opts.Pkgs) == 0 {
return d.configPackages(), nil
return d.ConfigPackages(), nil
}

var pkgsToUpdate []*devpkg.Package
Expand Down
33 changes: 33 additions & 0 deletions internal/devconfig/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package devconfig

import (
"bytes"
"regexp"
"slices"

"github.com/tailscale/hujson"
Expand Down Expand Up @@ -319,3 +320,35 @@ func (c *configAST) appendStringSliceField(name, fieldName string, fieldValues [
}
c.root.Format()
}

func (c *configAST) beforeComment(path ...any) []byte {
elem := c.root
for _, pathItem := range path {
obj := elem.Value.(*hujson.Object)
i, ok := pathItem.(int)
if !ok {
i = c.memberIndex(obj, pathItem.(string))
}
if i == -1 {
return nil
}
elem = obj.Members[i].Value
}

// Match all single are multi line comments.
re := regexp.MustCompile(`(?:\/\/(.*?)\n)|(?s:\/\*(.*?)\*\/)`)

return bytes.TrimSpace(
re.ReplaceAllFunc(elem.BeforeExtra, func(s []byte) []byte {
singleLineRe := regexp.MustCompile(`\/\/(.*?)\n`)
multiLineRe := regexp.MustCompile(`(?s:\/\*(.*?)\*\/)`)

if singleLineRe.Match(s) {
return singleLineRe.ReplaceAll(s, []byte("$1\n"))
} else if multiLineRe.Match(s) {
return multiLineRe.ReplaceAll(s, []byte("$1"))
}
return s
}),
)
}
Loading