diff --git a/bake/hclparser/stdlib.go b/bake/hclparser/stdlib.go index d8ef0004e239..cd0f132394f6 100644 --- a/bake/hclparser/stdlib.go +++ b/bake/hclparser/stdlib.go @@ -132,6 +132,7 @@ var stdlibFunctions = []funcDef{ // value in a list. func indexOfFunc() function.Function { return function.New(&function.Spec{ + Description: `Finds the element index for a given value in a list.`, Params: []function.Parameter{ { Name: "list", @@ -177,6 +178,7 @@ func indexOfFunc() function.Function { // basenameFunc constructs a function that returns the last element of a path. func basenameFunc() function.Function { return function.New(&function.Spec{ + Description: `Returns the last element of a path.`, Params: []function.Parameter{ { Name: "path", @@ -194,6 +196,7 @@ func basenameFunc() function.Function { // dirnameFunc constructs a function that returns the directory of a path. func dirnameFunc() function.Function { return function.New(&function.Spec{ + Description: `Returns the directory of a path.`, Params: []function.Parameter{ { Name: "path", @@ -212,6 +215,7 @@ func dirnameFunc() function.Function { // leaving only characters that are valid for a Bake target name. func sanitizeFunc() function.Function { return function.New(&function.Spec{ + Description: `Replaces all non-alphanumeric characters with a underscore, leaving only characters that are valid for a Bake target name.`, Params: []function.Parameter{ { Name: "name", @@ -240,8 +244,9 @@ func sanitizeFunc() function.Function { // This function was imported from terraform's datetime utilities. func timestampFunc() function.Function { return function.New(&function.Spec{ - Params: []function.Parameter{}, - Type: function.StaticReturnType(cty.String), + Description: `Returns a string representation of the current date and time.`, + Params: []function.Parameter{}, + Type: function.StaticReturnType(cty.String), Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { return cty.StringVal(time.Now().UTC().Format(time.RFC3339)), nil }, diff --git a/docs/bake-reference.md b/docs/bake-reference.md index 22592ec373ba..af8cddfc74a7 100644 --- a/docs/bake-reference.md +++ b/docs/bake-reference.md @@ -1399,8 +1399,7 @@ $ docker buildx bake ## Function -A [set of general-purpose functions][bake_stdlib] -provided by [go-cty][go-cty] +A [set of general-purpose functions][bake_stdlib] provided by [go-cty][go-cty] are available for use in HCL files: ```hcl @@ -1440,7 +1439,7 @@ target "webapp-dev" { [add-host]: https://docs.docker.com/reference/cli/docker/buildx/build/#add-host [attestations]: https://docs.docker.com/build/attestations/ -[bake_stdlib]: https://github.com/docker/buildx/blob/master/bake/hclparser/stdlib.go +[bake_stdlib]: https://github.com/docker/buildx/blob/master/docs/bake-stdlib.md [build-arg]: https://docs.docker.com/reference/cli/docker/image/build/#build-arg [build-context]: https://docs.docker.com/reference/cli/docker/buildx/build/#build-context [cache-backends]: https://docs.docker.com/build/cache/backends/ diff --git a/docs/bake-stdlib.md b/docs/bake-stdlib.md new file mode 100644 index 000000000000..0906e5357dfd --- /dev/null +++ b/docs/bake-stdlib.md @@ -0,0 +1,128 @@ +--- +title: Bake standard library functions +--- + + + +| Name | Description | +|:-------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `absolute` | If the given number is negative then returns its positive equivalent, or otherwise returns the given number unchanged. | +| [`add`](#add) | Returns the sum of the two given numbers. | +| `and` | Applies the logical AND operation to the given boolean values. | +| `base64decode` | | +| `base64encode` | | +| `basename` | Returns the last element of a path. | +| `bcrypt` | | +| `byteslen` | Returns the total number of bytes in the given buffer. | +| `bytesslice` | Extracts a subslice from the given buffer. | +| `can` | | +| `ceil` | Returns the smallest whole number that is greater than or equal to the given value. | +| `chomp` | Removes one or more newline characters from the end of the given string. | +| `chunklist` | Splits a single list into multiple lists where each has at most the given number of elements. | +| `cidrhost` | | +| `cidrnetmask` | | +| `cidrsubnet` | | +| `cidrsubnets` | | +| `coalesce` | Returns the first of the given arguments that isn't null, or raises an error if there are no non-null arguments. | +| `coalescelist` | Returns the first of the given sequences that has a length greater than zero. | +| `compact` | Removes all empty string elements from the given list of strings. | +| `concat` | Concatenates together all of the given lists or tuples into a single sequence, preserving the input order. | +| `contains` | Returns true if the given value is a value in the given list, tuple, or set, or false otherwise. | +| `convert` | | +| `csvdecode` | Parses the given string as Comma Separated Values (as defined by RFC 4180) and returns a map of objects representing the table of data, using the first row as a header row to define the object attributes. | +| `dirname` | Returns the directory of a path. | +| `distinct` | Removes any duplicate values from the given list, preserving the order of remaining elements. | +| `divide` | Divides the first given number by the second. | +| `element` | Returns the element with the given index from the given list or tuple, applying the modulo operation to the given index if it's greater than the number of elements. | +| `equal` | Returns true if the two given values are equal, or false otherwise. | +| `flatten` | Transforms a list, set, or tuple value into a tuple by replacing any given elements that are themselves sequences with a flattened tuple of all of the nested elements concatenated together. | +| `floor` | Returns the greatest whole number that is less than or equal to the given value. | +| `format` | Constructs a string by applying formatting verbs to a series of arguments, using a similar syntax to the C function \"printf\". | +| `formatdate` | Formats a timestamp given in RFC 3339 syntax into another timestamp in some other machine-oriented time syntax, as described in the format string. | +| `formatlist` | Constructs a list of strings by applying formatting verbs to a series of arguments, using a similar syntax to the C function \"printf\". | +| `greaterthan` | Returns true if and only if the second number is greater than the first. | +| `greaterthanorequalto` | Returns true if and only if the second number is greater than or equal to the first. | +| `hasindex` | Returns true if if the given collection can be indexed with the given key without producing an error, or false otherwise. | +| `indent` | Adds a given number of spaces after each newline character in the given string. | +| `index` | Returns the element with the given key from the given collection, or raises an error if there is no such element. | +| `indexof` | Finds the element index for a given value in a list. | +| `int` | Discards any fractional portion of the given number. | +| `join` | Concatenates together the elements of all given lists with a delimiter, producing a single string. | +| `jsondecode` | Parses the given string as JSON and returns a value corresponding to what the JSON document describes. | +| `jsonencode` | Returns a string containing a JSON representation of the given value. | +| `keys` | Returns a list of the keys of the given map in lexicographical order. | +| `length` | Returns the number of elements in the given collection. | +| `lessthan` | Returns true if and only if the second number is less than the first. | +| `lessthanorequalto` | Returns true if and only if the second number is less than or equal to the first. | +| `log` | Returns the logarithm of the given number in the given base. | +| `lookup` | Returns the value of the element with the given key from the given map, or returns the default value if there is no such element. | +| `lower` | Returns the given string with all Unicode letters translated to their lowercase equivalents. | +| `max` | Returns the numerically greatest of all of the given numbers. | +| `md5` | | +| `merge` | Merges all of the elements from the given maps into a single map, or the attributes from given objects into a single object. | +| `min` | Returns the numerically smallest of all of the given numbers. | +| `modulo` | Divides the first given number by the second and then returns the remainder. | +| `multiply` | Returns the product of the two given numbers. | +| `negate` | Multiplies the given number by -1. | +| `not` | Applies the logical NOT operation to the given boolean value. | +| `notequal` | Returns false if the two given values are equal, or true otherwise. | +| `or` | Applies the logical OR operation to the given boolean values. | +| `parseint` | Parses the given string as a number of the given base, or raises an error if the string contains invalid characters. | +| `pow` | Returns the given number raised to the given power (exponentiation). | +| `range` | Returns a list of numbers spread evenly over a particular range. | +| `regex` | Applies the given regular expression pattern to the given string and returns information about a single match, or raises an error if there is no match. | +| `regex_replace` | Applies the given regular expression pattern to the given string and replaces all matches with the given replacement string. | +| `regexall` | Applies the given regular expression pattern to the given string and returns a list of information about all non-overlapping matches, or an empty list if there are no matches. | +| `replace` | Replaces all instances of the given substring in the given string with the given replacement string. | +| `reverse` | Returns the given string with all of its Unicode characters in reverse order. | +| `reverselist` | Returns the given list with its elements in reverse order. | +| `rsadecrypt` | | +| `sanitize` | Replaces all non-alphanumeric characters with a underscore, leaving only characters that are valid for a Bake target name. | +| `sethaselement` | Returns true if the given set contains the given element, or false otherwise. | +| `setintersection` | Returns the intersection of all given sets. | +| `setproduct` | Calculates the cartesian product of two or more sets. | +| `setsubtract` | Returns the relative complement of the two given sets. | +| `setsymmetricdifference` | Returns the symmetric difference of the two given sets. | +| `setunion` | Returns the union of all given sets. | +| `sha1` | | +| `sha256` | | +| `sha512` | | +| `signum` | Returns 0 if the given number is zero, 1 if the given number is positive, or -1 if the given number is negative. | +| `slice` | Extracts a subslice of the given list or tuple value. | +| `sort` | Applies a lexicographic sort to the elements of the given list. | +| `split` | Produces a list of one or more strings by splitting the given string at all instances of a given separator substring. | +| `strlen` | Returns the number of Unicode characters (technically: grapheme clusters) in the given string. | +| `substr` | Extracts a substring from the given string. | +| `subtract` | Returns the difference between the two given numbers. | +| `timeadd` | Adds the duration represented by the given duration string to the given RFC 3339 timestamp string, returning another RFC 3339 timestamp. | +| `timestamp` | Returns a string representation of the current date and time. | +| `title` | Replaces one letter after each non-letter and non-digit character with its uppercase equivalent. | +| `trim` | Removes consecutive sequences of characters in "cutset" from the start and end of the given string. | +| `trimprefix` | Removes the given prefix from the start of the given string, if present. | +| `trimspace` | Removes any consecutive space characters (as defined by Unicode) from the start and end of the given string. | +| `trimsuffix` | Removes the given suffix from the start of the given string, if present. | +| `try` | | +| `upper` | Returns the given string with all Unicode letters translated to their uppercase equivalents. | +| `urlencode` | | +| `uuidv4` | | +| `uuidv5` | | +| `values` | Returns the values of elements of a given map, or the values of attributes of a given object, in lexicographic order by key or attribute name. | +| `zipmap` | Constructs a map from a list of keys and a corresponding list of values, which must both be of the same length. | + + + + +## Examples + +### `add` + +```hcl +# docker-bake.hcl +target "webapp-dev" { + dockerfile = "Dockerfile.webapp" + tags = ["docker.io/username/webapp:latest"] + args = { + buildno = "${add(123, 1)}" + } +} +``` diff --git a/docs/generate.go b/docs/generate.go index 0cd0033eec64..090b8c07768b 100644 --- a/docs/generate.go +++ b/docs/generate.go @@ -1,10 +1,15 @@ package main import ( + "fmt" "log" "os" + "regexp" + "sort" "strings" + "text/tabwriter" + "github.com/docker/buildx/bake/hclparser" "github.com/docker/buildx/commands" clidocstool "github.com/docker/cli-docs-tool" "github.com/docker/cli/cli/command" @@ -21,10 +26,14 @@ import ( ) const defaultSourcePath = "docs/reference/" +const defaultBakeStdlibSourcePath = "docs/bake-stdlib.md" + +var adjustSep = regexp.MustCompile(`\|:---(\s+)`) type options struct { - source string - formats []string + source string + bakeSource string + formats []string } // fixUpExperimentalCLI trims the " (EXPERIMENTAL)" suffix from the CLI output, @@ -79,6 +88,9 @@ func gen(opts *options) error { if err = c.GenMarkdownTree(cmd); err != nil { return err } + if err = generateBakeStdlibDocs(opts.bakeSource); err != nil { + return errors.Wrap(err, "generating bake stdlib docs") + } case "yaml": // fix up is needed only for yaml (used for generating docs.docker.com contents) fixUpExperimentalCLI(cmd) @@ -97,6 +109,7 @@ func run() error { opts := &options{} flags := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError) flags.StringVar(&opts.source, "source", defaultSourcePath, "Docs source folder") + flags.StringVar(&opts.bakeSource, "bake-stdlib-source", defaultBakeStdlibSourcePath, "Bake stdlib source file") flags.StringSliceVar(&opts.formats, "formats", []string{}, "Format (md, yaml)") if err := flags.Parse(os.Args[1:]); err != nil { return err @@ -113,3 +126,77 @@ func main() { os.Exit(1) } } + +func generateBakeStdlibDocs(filename string) error { + log.Printf("INFO: Generating Markdown for %q", filename) + dt, err := os.ReadFile(filename) + if err != nil { + return err + } + currentContent := string(dt) + + start := strings.Index(currentContent, "") + end := strings.Index(currentContent, "") + if start == -1 { + return errors.Errorf("no start marker in %s", filename) + } + if end == -1 { + return errors.Errorf("no end marker in %s", filename) + } + + table := newMdTable("Name", "Description") + names := make([]string, 0, len(hclparser.Stdlib())) + for name := range hclparser.Stdlib() { + names = append(names, name) + } + sort.Strings(names) + for _, name := range names { + fn := hclparser.Stdlib()[name] + fname := fmt.Sprintf("`%s`", name) + if strings.Contains(currentContent, "") { + fname = fmt.Sprintf("[`%s`](#%s)", name, name) + } + table.AddRow(fname, fn.Description()) + } + + newContent := currentContent[:start] + "\n\n" + table.String() + "\n" + currentContent[end:] + return os.WriteFile(filename, []byte(newContent), 0644) +} + +type mdTable struct { + out *strings.Builder + tabWriter *tabwriter.Writer +} + +func newMdTable(headers ...string) *mdTable { + w := &strings.Builder{} + t := &mdTable{ + out: w, + tabWriter: tabwriter.NewWriter(w, 5, 5, 1, ' ', tabwriter.Debug), + } + t.addHeader(headers...) + return t +} + +func (t *mdTable) addHeader(cols ...string) { + t.AddRow(cols...) + _, _ = t.tabWriter.Write([]byte("|" + strings.Repeat(":---\t", len(cols)) + "\n")) +} + +func (t *mdTable) AddRow(cols ...string) { + for i := range cols { + cols[i] = mdEscapePipe(cols[i]) + } + _, _ = t.tabWriter.Write([]byte("| " + strings.Join(cols, "\t ") + "\t\n")) +} + +func (t *mdTable) String() string { + _ = t.tabWriter.Flush() + return adjustSep.ReplaceAllStringFunc(t.out.String()+"\n", func(in string) string { + return strings.ReplaceAll(in, " ", "-") + }) +} + +func mdEscapePipe(s string) string { + return strings.ReplaceAll(s, `|`, `\|`) +} diff --git a/hack/dockerfiles/docs.Dockerfile b/hack/dockerfiles/docs.Dockerfile index 1306d51ad39e..13cfb028c82a 100644 --- a/hack/dockerfiles/docs.Dockerfile +++ b/hack/dockerfiles/docs.Dockerfile @@ -21,9 +21,9 @@ RUN --mount=target=/context \ --mount=target=.,type=tmpfs <&2 'ERROR: Docs result differs. Please update with "make docs"' - git status --porcelain -- docs/reference + git status --porcelain -- docs/reference docs/bake-stdlib.md exit 1 fi EOT