Skip to content
Draft
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
81 changes: 81 additions & 0 deletions internal/sync/link/link.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Package link validates and writes repository source descriptors.
package link

import (
"errors"
"os"
"path/filepath"

syncdomain "github.com/launchdarkly/ldcli/internal/sync"
synclocal "github.com/launchdarkly/ldcli/internal/sync/local"
syncreference "github.com/launchdarkly/ldcli/internal/sync/reference"
)

// Options are the explicit inputs for one repository source link.
type Options struct {
Store synclocal.Store
RepositoryRoot string
WorkingDirectory string
ID syncdomain.ResourceID
File string
Format string
Select string
Map map[string]string
}

// Prepared is one validated descriptor and its compiled local resource.
type Prepared struct {
ID syncdomain.ResourceID
Reference syncdomain.Reference
Definition synclocal.SourceDefinition
Path string
Content []byte
Previous []byte
}

// Prepare validates a repository source without writing its descriptor.
func Prepare(options Options) (Prepared, error) {
if err := options.Store.ValidateDescriptorTarget(options.ID); err != nil {
return Prepared{}, err
}
reference, err := synclocal.NewSourceReference(
options.RepositoryRoot,
options.WorkingDirectory,
options.File,
options.Format,
options.Select,
options.Map,
)
if err != nil {
return Prepared{}, err
}
reference, err = syncreference.NormalizeReference(options.ID, reference)
if err != nil {
return Prepared{}, err
}
definition, err := synclocal.CompileSource(options.RepositoryRoot, options.ID, reference)
if err != nil {
return Prepared{}, err
}
path, err := synclocal.ReferencePath(options.ID)
if err != nil {
return Prepared{}, err
}
content, err := synclocal.RenderDescriptor(options.ID, reference)
if err != nil {
return Prepared{}, err
}
previous, readErr := os.ReadFile(filepath.Join(options.RepositoryRoot, filepath.FromSlash(path)))
if readErr != nil && !errors.Is(readErr, os.ErrNotExist) {
return Prepared{}, readErr
}
return Prepared{
ID: options.ID, Reference: reference, Definition: definition, Path: path, Content: content,
Previous: previous,
}, nil
}

// Write atomically publishes the prepared descriptor.
func (prepared Prepared) Write(store synclocal.Store) (string, error) {
return store.WriteDescriptor(prepared.ID, prepared.Reference)
}
45 changes: 45 additions & 0 deletions internal/sync/link/link_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package link

import (
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/require"

syncdomain "github.com/launchdarkly/ldcli/internal/sync"
synclocal "github.com/launchdarkly/ldcli/internal/sync/local"
)

func TestPrepareDoesNotWriteUntilExplicitWrite(t *testing.T) {
root := t.TempDir()
source := []byte(`from agents import Agent
support = Agent(instructions="Help with orders.")
`)
require.NoError(t, os.WriteFile(filepath.Join(root, "agents.py"), source, 0o644))
id := syncdomain.VariationID("production", "support", "default")

prepared, err := Prepare(Options{
Store: synclocal.NewStore(root),
RepositoryRoot: root,
WorkingDirectory: root,
ID: id,
File: "agents.py",
Format: "python",
Select: `agents[id="support"]`,
Map: map[string]string{"prompt": "instructions"},
})

require.NoError(t, err)
require.Equal(t, ".launchdarkly/production/configs/support/default.ref.yaml", prepared.Path)
require.NoFileExists(t, filepath.Join(root, filepath.FromSlash(prepared.Path)))
path, err := prepared.Write(synclocal.NewStore(root))
require.NoError(t, err)
descriptor, err := os.ReadFile(filepath.Join(root, filepath.FromSlash(path)))
require.NoError(t, err)
require.Contains(t, string(descriptor), "formatVersion: 1")
require.Contains(t, string(descriptor), `select: agents[id="support"]`)
actual, err := os.ReadFile(filepath.Join(root, "agents.py"))
require.NoError(t, err)
require.Equal(t, source, actual)
}
Loading