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
26 changes: 13 additions & 13 deletions MAINTAINERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

This is a monorepo with two Go modules:

| Directory | Go module | Description |
|-----------|-----------|-------------|
| `function/` | `github.com/crossplane-contrib/function-hcl/function` | Crossplane composition function |
| `language-server/` | `github.com/crossplane-contrib/function-hcl/language-server` | LSP language server |
| Directory | Go module | Description |
|--------------------|--------------------------------------------------------------|---------------------------------|
| `function/` | `github.com/crossplane-contrib/function-hcl/function` | Crossplane composition function |
| `language-server/` | `github.com/crossplane-contrib/function-hcl/language-server` | LSP language server |

Other top-level directories (`vscode/`, `jetbrains/`, `docs-site/`, `Formula/`) are not Go modules and are released as part of the same workflow.

Expand Down Expand Up @@ -102,15 +102,15 @@ git push origin main v0.3.0-rc1 function/v0.3.0-rc1 language-server/v0.3.0-rc1 -

## Required repository secrets

| Secret | Used by |
|--------|---------|
| `XPKG_ACCESS_ID` | Push Crossplane package to xpkg.upbound.io |
| `XPKG_TOKEN` | Push Crossplane package to xpkg.upbound.io |
| `VSCE_PAT` | Publish VS Code extension |
| `JETBRAINS_PUBLISH_TOKEN` | Publish JetBrains plugin |
| `JETBRAINS_CERTIFICATE_CHAIN` | Sign JetBrains plugin |
| `JETBRAINS_PRIVATE_KEY` | Sign JetBrains plugin |
| `JETBRAINS_PRIVATE_KEY_PASSWORD` | Sign JetBrains plugin |
| Secret | Used by |
|----------------------------------|--------------------------------------------|
| `XPKG_ACCESS_ID` | Push Crossplane package to xpkg.upbound.io |
| `XPKG_TOKEN` | Push Crossplane package to xpkg.upbound.io |
| `VSCE_PAT` | Publish VS Code extension |
| `JETBRAINS_PUBLISH_TOKEN` | Publish JetBrains plugin |
| `JETBRAINS_CERTIFICATE_CHAIN` | Sign JetBrains plugin |
| `JETBRAINS_PRIVATE_KEY` | Sign JetBrains plugin |
| `JETBRAINS_PRIVATE_KEY_PASSWORD` | Sign JetBrains plugin |

## Go module tagging

Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
A crossplane function that uses an opinionated DSL built on [HCL](https://github.com/hashicorp/hcl)
to model desired resources. It has more than a passing familiarity with Terraform syntax.

This is a monorepo that contains
the [function implementation](function/),
[language server](language-server/),
[VS code extension](vscode/),
[Jetbrains extension](jetbrains/),
[documentation content](docs-site/),
and [homebrew formula](Formula/).

Detailed documentation [can be found here](https://crossplane-contrib.github.io/function-hcl/).

![CI](https://github.com/crossplane-contrib/function-hcl/actions/workflows/ci.yaml/badge.svg?branch=main)
[![Go Report Card](https://goreportcard.com/badge/github.com/crossplane-contrib/function-hcl)](https://goreportcard.com/report/github.com/crossplane-contrib/function-hcl)
[![function-hcl coverage](https://github.com/crossplane-contrib/function-hcl/wiki/coverage.svg)](https://raw.githack.com/wiki/crossplane-contrib/function-hcl/coverage.html)
Expand Down Expand Up @@ -90,7 +100,7 @@ In addition, it emits an event for every such discarded resource telling you exa
and maintains a status condition explicitly for this purpose.
This allows you to fix any typos that prevent resources from being rendered as opposed to unknown dependency state.

Start with the [examples](example/README.md), then read the [spec](spec.md).
Start with the [examples](example/README.md), then [read the docs](https://crossplane-contrib.github.io/function-hcl/).

There is also tooling in [fn-hcl-tools](cmd/fn-hcl-tools) that can package multiple HCL files into txtar format
after static analysis. It also provides a formatter for canonical formatting of HCL files.
Expand Down
170 changes: 170 additions & 0 deletions docs-site/content/en/docs/getting-started/crd-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
---
title: "Set up CRDs"
linkTitle: "CRD setup"
weight: 4
description: >
Create local CRD definitions for language server use.
---

The language server provides completion for language constructs by default.
To enable completions for XRD and CRD fields, you need to supply two pieces of information:

1. **The composite type** — tells the language server which XRD this composition targets, enabling
completions for the `req.composite` variable.
2. **CRD definitions** — tells the language server what resource types exist, enabling completions
for `apiVersion`, `kind`, and resource-specific fields.

## Declaring the composite type

Create a `composition.yaml` file in the same directory as your `.hcl` files.
This file tells the language server which XRD your composition targets:

```yaml
xrd:
apiVersion: example.com/v1
kind: XExample
```

The language server reads this file when you open any `.hcl` file in the directory.
It uses the `apiVersion` and `kind` to look up the matching XRD schema from the CRD definitions you have
configured (see below), and uses that schema to provide completions for the built-in `composite` variable.

If `composition.yaml` is absent, or if either field is empty, the language server still works but cannot
provide composite-specific field completions.

## Setting up CRD definitions

You use the `fn-hcl-tools extract-crds` command to extract CRD definitions from your YAML files and OCI images,
then place those definitions where the language server can find them.

## How the language server discovers CRDs

When you open an HCL file, the language server walks up the directory tree from the file's location
looking for one of two things (in this order):

1. A file named `.crd-sources.yaml` — an explicit configuration listing which YAML files to load.
2. A directory named `.crds/` — the default drop location for extracted CRD files.

Once found, it loads matching YAML files in the background and starts providing completions for the
resource types it finds. It watches for file changes and reloads automatically.

## Extracting CRDs

`fn-hcl-tools extract-crds` reads YAML files and pulls out CRD and XRD definitions. It can also
follow `Provider` and `Configuration` object references and pull CRDs from the referenced OCI images.

**Install `fn-hcl-tools`** if you have not already — see [Installation](../installation#install-fn-hcl-tools).

### From local YAML files

Point the command at any YAML files that contain CRDs, XRDs, Providers, or Configurations:

```bash
fn-hcl-tools extract-crds --output-dir .crds providers.yaml crossplane.yaml
```

The tool writes one YAML file per processed image into `.crds/`. Objects from local files
are grouped under a file named `local-objects.yaml`.

### From stdin (filter mode)

When called with no arguments, the command acts as a filter: reads YAML from stdin and writes to stdout.
Use `-` to explicitly request stdin while also passing file arguments:

```bash
cat providers.yaml | fn-hcl-tools extract-crds --output-dir .crds -
fn-hcl-tools extract-crds --output-dir .crds - extra.yaml < providers.yaml
```

Note: the tool processes individual YAML documents, not Kubernetes `List` objects.
If your source produces a `List`, extract the individual items first before piping.

### From a Helm chart

`helm template` renders chart manifests as individual YAML documents, which the extractor handles
directly. This is useful for pulling CRDs out of provider charts:

```bash
helm template --include-crds my-release oci://registry-1.docker.io/bitnamicharts/crossplane \
| fn-hcl-tools extract-crds --output-dir .crds -
```

```bash
helm template my-release ./my-provider-chart --include-crds \
| fn-hcl-tools extract-crds --output-dir .crds -
```

## Simple setup: the `.crds/` directory

The simplest configuration is to create a `.crds/` directory in (or above) your compositions directory
and run `extract-crds` into it. No further configuration is needed.

```
my-compositions/
.crds/
local-objects.yaml # extracted from local YAML files
xpkg-upbound-io-...yaml # extracted from a Provider image
compositions/
composition1/
composition.yaml
main.hcl
```

The language server loads all `*.yaml` files from `.crds/` and makes all resource types
(both cluster-scoped and namespace-scoped) available for completion.

## Advanced setup: `.crd-sources.yaml`

Create a `.crd-sources.yaml` file when you need more control — for example, to load files from
a different location or to limit completions to a specific resource scope.

```yaml
scope: both # "namespaced", "cluster", or "both" (default: "both")
paths:
- .crds/*.yaml # paths are relative to this file's directory
- /absolute/path/to/more/crds/**/*.yaml
```

| Field | Values | Default | Description |
|---------|-------------------------------------|----------|-------------------------------------------------------|
| `scope` | `namespaced`, `cluster`, `both` | `both` | Which resource scopes to include in completions. |
| `paths` | list of file paths or glob patterns | required | Files to load; relative paths resolve from this file. |

The `paths` field supports `**` double-star globs for recursive matching.

### Example: separate CRD directories per scope

```yaml
scope: cluster
paths:
- cluster-crds/*.yaml
- /shared/xrds/*.yaml
```

## Placement

Put `.crds/` or `.crd-sources.yaml` in the root of your compositions repository so that all
composition files in subdirectories share a single CRD configuration.

```
repo-root/
.crds/ # or .crd-sources.yaml here
providers.yaml
xrds.yaml
compositions/
postgres/
composition.hcl # language server finds .crds/ by walking up
redis/
composition.hcl # same
```

## Keeping CRDs up to date

Re-run `extract-crds` whenever you add a new Provider, Configuration, or XRD to your setup.
The language server detects file changes and reloads without requiring a restart.

```bash
fn-hcl-tools extract-crds --output-dir .crds crossplane.yaml
```

Use `--progress=false` and `--warnings=false` to suppress diagnostic output in scripts.
18 changes: 16 additions & 2 deletions docs-site/content/en/docs/getting-started/ide-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ The language server provides:
- Document symbols
- Semantic token highlighting

## Installation instructions
## Installation

Coming soon. Watch this space...
### Visual Studio Code

Install the [Crossplane Function HCL](https://marketplace.visualstudio.com/items?itemName=function-hcl-authors.function-hcl) extension shown below.

{{< figure src="../vscode-extension.png" >}}

### Jetbrains products

Install the [function-hcl plugin](https://plugins.jetbrains.com/plugin/30965-function-hcl) as shown below.

{{< figure src="../jetbrains-plugin.png" >}}

### Post-install

Follow the instructions [on this page](../crd-setup) to register types with the language server.
27 changes: 26 additions & 1 deletion docs-site/content/en/docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,37 @@ You should see `HEALTHY: True` and `INSTALLED: True` in the output.
## Install fn-hcl-tools

`fn-hcl-tools` is the companion CLI for packaging, formatting, and analyzing your HCL files.
Install it with `go install`:

### Prebuilt release

Install it by downloading the [appropriate file for your OS for the latest release](https://github.com/crossplane-contrib/function-hcl/releases)

### Homebrew

For MacOS, you can also use homebrew to install it.

```bash
# since the formula comes from the same monorepo, you need to run the `tap` subcommand as follows
brew tap crossplane-contrib/function-hcl https://github.com/crossplane-contrib/function-hcl
brew trust crossplane-contrib/function-hcl
brew install fn-hcl-tools
```

To upgrade the version:

```bash
brew update
brew upgrade fn-hcl-tools
```

### Install from source

```bash
go install github.com/crossplane-contrib/function-hcl/function/cmd/fn-hcl-tools@{{< version >}}
```

Note that the version printed by `fn-hcl-tools version` will be incorrect using this method.

Verify it works:

```bash
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 5 additions & 13 deletions docs-site/content/en/docs/language-guide/hcl-basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,6 @@ resource my-bucket {
body = { /* ... */ }
}

# Block with two labels
resource my-bucket {
# 'composite' is the block type, 'status' is a label
composite status {
body = { /* ... */ }
}
}
```

Unlike attributes, some block types **can** appear multiple times in the same scope. For example,
Expand Down Expand Up @@ -85,6 +78,7 @@ body = {
{{% alert title="Note" color="info" %}}
HCL also allows `:` as a delimiter inside object literals (e.g. `kind: "Bucket"`). Both `=` and
`:` are valid, but this documentation uses `=` consistently for clarity.
The formatter also normalizes colons and replaces them with equal signs.
{{% /alert %}}

An attribute can only be set **once** in a given scope. Setting the same attribute name twice
Expand Down Expand Up @@ -201,12 +195,10 @@ Some commonly used ones:

```hcl
locals {
merged = merge(defaults, overrides)
safe = try(obj.field, "fallback")
ok = can(obj.field)
items = join(",", list)
encoded = base64encode(secret)
count = length(names)
merged = merge(defaults, overrides) # merge overrides
safe = try(obj.field, "fallback") # handle optional fields
ok = can(obj.field) # check if an optional field is present
encoded = base64encode(secret) # encode a secret value for Crossplane use
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ All `locals` blocks in a given scope are processed together and variable orderin
Dependencies are resolved automatically. This is the same behavior as Terraform.

```hcl
# These two blocks are treated identically:
# These two blocks are treated exactly the same as the single block in the previous section
locals {
computedName = "${baseName}-bucket"
}
Expand Down
2 changes: 1 addition & 1 deletion docs-site/content/en/docs/language-guide/source-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ You should always use the `fn-hcl-tools package` command to produce the txtar sc
HCL source files -- even for a single file. Do not hand-craft txtar bundles.

```bash
fn-hcl-tools package ./my-composition/*.hcl
fn-hcl-tools package ./my-composition/
```

Before producing the txtar output, `fn-hcl-tools package` **analyzes** every HCL file and will
Expand Down
22 changes: 11 additions & 11 deletions docs-site/content/en/docs/reference/dsl-specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: >
---

{{% alert title="Source of truth" color="info" %}}
This page is derived from [`spec.md`](https://github.com/crossplane-contrib/function-hcl/blob/main/spec.md)
This page is derived from [`spec.md`](https://github.com/crossplane-contrib/function-hcl/blob/main/function/spec.md)
in the repository. If you find a discrepancy, the repository version is authoritative.
{{% /alert %}}

Expand All @@ -20,16 +20,16 @@ via the `input` field of the Composition pipeline step. All files are treated as

Created automatically from the `RunFunctionRequest`. Accessed as `req.<field>`.

| Variable | Type | Description |
|----------|------|-------------|
| `req.composite` | object | Observed composite resource (XR) |
| `req.composite_connection` | map(string, bytes) | Observed connection details of the composite |
| `req.resource` | map(string, object) | Observed resource bodies, keyed by crossplane name |
| `req.connection` | map(string, map(string, bytes)) | Observed connection details, keyed by resource name |
| `req.resources` | map(string, list(object)) | Observed resource collections, keyed by base name |
| `req.connections` | map(string, list(map(string, bytes))) | Connection details of collections |
| `req.context` | map(string, any) | Pipeline context |
| `req.extra_resources` | map(string, list(object)) | Extra resources from `requirement` blocks |
| Variable | Type | Description |
|----------------------------|---------------------------------------|-----------------------------------------------------|
| `req.composite` | object | Observed composite resource (XR) |
| `req.composite_connection` | map(string, bytes) | Observed connection details of the composite |
| `req.resource` | map(string, object) | Observed resource bodies, keyed by crossplane name |
| `req.connection` | map(string, map(string, bytes)) | Observed connection details, keyed by resource name |
| `req.resources` | map(string, list(object)) | Observed resource collections, keyed by base name |
| `req.connections` | map(string, list(map(string, bytes))) | Connection details of collections |
| `req.context` | map(string, any) | Pipeline context |
| `req.extra_resources` | map(string, list(object)) | Extra resources from `requirement` blocks |

## Top-Level Blocks

Expand Down
Loading
Loading