Add source environment variables
This commit is contained in:
parent
60afdd2a32
commit
a238d1f239
@ -113,7 +113,8 @@ To execute an action, Intake executes the command specified by that action's `ar
|
||||
The process's environment is as follows:
|
||||
|
||||
* `intake`'s environment is inherited.
|
||||
* `STATE_PATH` is set to the absolute path of a file containing the source's persistent state.
|
||||
* Each environment variable defined in the source is set.
|
||||
* `STATE_PATH` is set to the absolute path of a file that the source can use for persistent state. This file can be used for any data in any format. Changes to the state file are only saved if the action succeeds.
|
||||
|
||||
When an action receives an item as input, that item's JSON representation is written to that action's `stdin`.
|
||||
When an action outputs an item, it should write the item's JSON representation to `stdout` on one line.
|
||||
|
@ -1,21 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var sourceEditCmd = &cobra.Command{
|
||||
Use: "edit",
|
||||
Short: "Edit a source",
|
||||
Long: `
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Fatal("not implemented")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
sourceCmd.AddCommand(sourceEditCmd)
|
||||
}
|
53
cmd/sourceEnv.go
Normal file
53
cmd/sourceEnv.go
Normal file
@ -0,0 +1,53 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/Jaculabilis/intake/core"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var sourceEnvCmd = &cobra.Command{
|
||||
Use: "env",
|
||||
Short: "Manage source environment variables",
|
||||
Long: `Add, edit, list, or delete environment variables.
|
||||
|
||||
When --set is not specified, list the environment for the source.
|
||||
|
||||
--set KEY=VALUE will add or edit an environment variable to be set in all
|
||||
action executions.
|
||||
|
||||
--set KEY= will delete the environment variable from the source.
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
sourceEnv(stringArg(cmd, "source"), stringArrayArg(cmd, "set"))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
sourceCmd.AddCommand(sourceEnvCmd)
|
||||
|
||||
sourceEnvCmd.Flags().StringP("source", "s", "", "Source to edit")
|
||||
sourceEnvCmd.MarkFlagRequired("source")
|
||||
|
||||
sourceEnvCmd.Flags().StringArray("set", nil, "Set or modify environment variable")
|
||||
}
|
||||
|
||||
func sourceEnv(source string, env []string) {
|
||||
db := openAndMigrateDb()
|
||||
|
||||
if len(env) == 0 {
|
||||
envs, err := core.GetEnvs(db, source)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to get envs: %v", err)
|
||||
}
|
||||
for _, env := range envs {
|
||||
fmt.Println(env)
|
||||
}
|
||||
}
|
||||
|
||||
if err := core.SetEnvs(db, source, env); err != nil {
|
||||
log.Fatalf("failed to set envs: %v", err)
|
||||
}
|
||||
}
|
57
core/env.go
Normal file
57
core/env.go
Normal file
@ -0,0 +1,57 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func GetEnvs(db *DB, source string) ([]string, error) {
|
||||
rows, err := db.Query(`
|
||||
select name, value
|
||||
from envs
|
||||
where source = ?
|
||||
`, source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var envs []string
|
||||
for rows.Next() {
|
||||
var name string
|
||||
var value string
|
||||
if err := rows.Scan(&name, &value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
envs = append(envs, fmt.Sprintf("%s=%s", name, value))
|
||||
}
|
||||
return envs, nil
|
||||
}
|
||||
|
||||
func SetEnvs(db *DB, source string, envs []string) error {
|
||||
return db.Transact(func(tx *sql.Tx) error {
|
||||
for _, env := range envs {
|
||||
parts := strings.SplitN(env, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("invalid env format: %s", env)
|
||||
}
|
||||
if parts[1] == "" {
|
||||
_, err := tx.Exec(`
|
||||
delete from envs
|
||||
where source = ? and name = ?
|
||||
`, source, parts[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to clear source %s env %s: %v", source, parts[0], err)
|
||||
}
|
||||
} else {
|
||||
_, err := tx.Exec(`
|
||||
insert into envs (source, name, value)
|
||||
values (?, ?, ?)
|
||||
`, source, parts[0], parts[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set source %s env %s = %s: %v", source, parts[0], parts[1], err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
91
core/env_test.go
Normal file
91
core/env_test.go
Normal file
@ -0,0 +1,91 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEnvs(t *testing.T) {
|
||||
db := EphemeralDb(t)
|
||||
if err := AddSource(db, "_"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Insert env
|
||||
if err := SetEnvs(db, "_", []string{"ONE=hello"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
envs, err := GetEnvs(db, "_")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(envs) != 1 {
|
||||
t.Fatal("expected 1 env")
|
||||
}
|
||||
if envs[0] != "ONE=hello" {
|
||||
t.Fatalf("Expected ONE=hello, got %s", envs[0])
|
||||
}
|
||||
|
||||
// Insert env with = in value
|
||||
if err := SetEnvs(db, "_", []string{"TWO=world=true"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
envs, err = GetEnvs(db, "_")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(envs) != 2 {
|
||||
t.Fatal("expected 2 envs")
|
||||
}
|
||||
slices.Sort(envs) // ONE > TWO
|
||||
if envs[1] != "TWO=world=true" {
|
||||
t.Fatalf("Expected TWO=world=true, got %s", envs[1])
|
||||
}
|
||||
|
||||
// Replace env
|
||||
if err := SetEnvs(db, "_", []string{"TWO=goodbye"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
envs, err = GetEnvs(db, "_")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(envs) != 2 {
|
||||
t.Fatal("expected 2 envs")
|
||||
}
|
||||
slices.Sort(envs) // ONE > TWO
|
||||
if envs[1] != "TWO=goodbye" {
|
||||
t.Fatalf("Expected TWO=goodbye, got %s", envs[1])
|
||||
}
|
||||
|
||||
// Insert is transactional on error
|
||||
if err := SetEnvs(db, "_", []string{"THREE=crowd", "FOUR"}); err == nil {
|
||||
t.Fatal("expected bad env insert to fail")
|
||||
}
|
||||
envs, err = GetEnvs(db, "_")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(envs) != 2 {
|
||||
t.Fatal("expected 2 envs after failed insert")
|
||||
}
|
||||
slices.Sort(envs) // ONE > TWO
|
||||
if envs[0] != "ONE=hello" || envs[1] != "TWO=goodbye" {
|
||||
t.Fatalf("Expected ONE=hello and TWO=goodbye, got %v", envs)
|
||||
}
|
||||
|
||||
// Delete env
|
||||
if err := SetEnvs(db, "_", []string{"ONE="}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
envs, err = GetEnvs(db, "_")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(envs) != 1 {
|
||||
t.Fatal("expected 1 env after deletion")
|
||||
}
|
||||
if envs[0] != "TWO=goodbye" {
|
||||
t.Fatalf("Expected TWO=goodbye, got %s", envs[0])
|
||||
}
|
||||
}
|
@ -10,6 +10,13 @@ create table actions(
|
||||
primary key (source, name),
|
||||
foreign key (source) references sources (name) on delete cascade
|
||||
) strict;
|
||||
create table envs(
|
||||
source text not null,
|
||||
name text not null,
|
||||
value text not null,
|
||||
unique (source, name) on conflict replace,
|
||||
foreign key (source) references sources (name) on delete cascade
|
||||
) strict;
|
||||
create table items(
|
||||
source text not null,
|
||||
id text not null,
|
||||
|
Loading…
Reference in New Issue
Block a user