Skip to content

Commit

Permalink
Support --stdin flag in rcc holotree hash to read the package fil…
Browse files Browse the repository at this point in the history
…e content from stdin.
  • Loading branch information
fabioz committed Oct 25, 2024
1 parent a710c83 commit db661af
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 42 deletions.
35 changes: 32 additions & 3 deletions cmd/holotreeHash.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ package cmd

import (
"fmt"
"io"
"os"
"strings"

"github.com/robocorp/rcc/common"
"github.com/robocorp/rcc/conda"
"github.com/robocorp/rcc/htfs"
"github.com/robocorp/rcc/operations"
"github.com/robocorp/rcc/pretty"
Expand All @@ -12,7 +16,8 @@ import (
)

var (
holotreeShowBlueprint bool
holotreeShowBlueprint bool
holotreeReadInputFromStdin bool
)

var holotreeHashCmd = &cobra.Command{
Expand All @@ -24,8 +29,31 @@ var holotreeHashCmd = &cobra.Command{
if common.DebugFlag() {
defer common.Stopwatch("Conda YAML hash calculation lasted").Report()
}
_, holotreeBlueprint, err := htfs.ComposeFinalBlueprint(args, "", common.DevDependencies)
pretty.Guard(err == nil, 1, "Blueprint calculation failed: %v", err)

var err error
if holotreeReadInputFromStdin {
// Now, read from stdin until EOF and use that as the input to calculate the blueprint hash.
data, err := io.ReadAll(os.Stdin)
pretty.Guard(err == nil, 1, "Failed to read from stdin: %v", err)

if len(args) != 1 {
common.Fatal("When reading from stdin, the target file path must be provided as the first (and only) argument.", nil)
}

filename := args[0]
if !strings.HasSuffix(filename, ".yaml") {
common.Fatal("When reading from stdin, the target file path must be provided as the first (and only) argument and it must be a `.yaml` file.", nil)
}

environment, err := conda.ReadPackageCondaYamlFromContents(data, filename, common.DevDependencies)
pretty.Guard(err == nil, 1, "Failed to read from stdin: %v (filename: %q)", err, filename)

holotreeBlueprint, err = htfs.BlueprintFromEnvironment(environment)
pretty.Guard(err == nil, 1, "Failed to calculate blueprint from environment: %v", err)
} else {
_, holotreeBlueprint, err = htfs.ComposeFinalBlueprint(args, "", common.DevDependencies)
pretty.Guard(err == nil, 1, "Blueprint calculation failed: %v", err)
}
hash := common.BlueprintHash(holotreeBlueprint)

common.Log("Blueprint hash for %v is %v.", args, hash)
Expand Down Expand Up @@ -59,4 +87,5 @@ func init() {
holotreeHashCmd.Flags().BoolVarP(&common.DevDependencies, "devdeps", "", false, "Include dev-dependencies from the `package.yaml` when calculating the hash (only valid when dealing with a `package.yaml` file).")
holotreeHashCmd.Flags().BoolVarP(&holotreeJson, "json", "j", false, "Show environment as JSON.")
holotreeHashCmd.Flags().BoolVarP(&holotreeShowBlueprint, "show-blueprint", "", false, "Show full blueprint, not just hash.")
holotreeHashCmd.Flags().BoolVarP(&holotreeReadInputFromStdin, "stdin", "", false, "Read the conda.yaml/package.yaml contents from stdin.")
}
2 changes: 1 addition & 1 deletion common/version.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package common

const (
Version = `v18.4.0`
Version = `v18.5.0`
)
17 changes: 0 additions & 17 deletions conda/condayaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ package conda

import (
"fmt"
"os"
"regexp"
"strings"

"github.com/robocorp/rcc/cloud"
"github.com/robocorp/rcc/common"
"github.com/robocorp/rcc/pathlib"

Expand Down Expand Up @@ -658,21 +656,6 @@ func CondaYamlFrom(content []byte) (*Environment, error) {
return result.AsEnvironment(), nil
}

func readCondaYaml(filename string) (*Environment, error) {
var content []byte
var err error

if pathlib.IsFile(filename) {
content, err = os.ReadFile(filename)
} else {
content, err = cloud.ReadFile(filename)
}
if err != nil {
return nil, fmt.Errorf("%q: %w", filename, err)
}
return CondaYamlFrom(content)
}

func pipContent(result []*Dependency, value interface{}) []*Dependency {
values, ok := value.([]interface{})
if !ok {
Expand Down
18 changes: 6 additions & 12 deletions conda/packageyaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"strings"

"github.com/robocorp/rcc/cloud"
"github.com/robocorp/rcc/common"
"github.com/robocorp/rcc/pathlib"
"gopkg.in/yaml.v2"
)
Expand Down Expand Up @@ -90,25 +89,19 @@ func packageYamlFrom(content []byte, devDependencies bool) (*Environment, error)
return result.AsEnvironment(devDependencies), nil
}

func ReadPackageCondaYaml(filename string, devDependencies bool) (*Environment, error) {
func ReadPackageCondaYamlFromContents(content []byte, filename string, devDependencies bool) (*Environment, error) {
basename := strings.ToLower(filepath.Base(filename))
if basename == "package.yaml" {
environment, err := readPackageYaml(filename, devDependencies)
if err == nil {
return environment, nil
}
return packageYamlFrom(content, devDependencies)
}
if devDependencies {
// error: only valid when dealing with a `package.yaml` file
return nil, fmt.Errorf("'--devdeps' flag is only valid when dealing with a `package.yaml` file. Current file: %q", filename)
}
return readCondaYaml(filename)
return CondaYamlFrom(content)
}

func readPackageYaml(filename string, devDependencies bool) (*Environment, error) {
if devDependencies {
common.Debug("Reading file %q with dev dependencies", filename)
}
func ReadPackageCondaYaml(filename string, devDependencies bool) (*Environment, error) {
var content []byte
var err error

Expand All @@ -120,5 +113,6 @@ func readPackageYaml(filename string, devDependencies bool) (*Environment, error
if err != nil {
return nil, fmt.Errorf("%q: %w", filename, err)
}
return packageYamlFrom(content, devDependencies)

return ReadPackageCondaYamlFromContents(content, filename, devDependencies)
}
6 changes: 6 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# rcc change log

## v18.5.0 (date: 25.10.2024)

- support `--stdin` flag in `rcc holotree hash` to read the package file content from stdin
(note that the actual package file path still is expected to be given as an
argument, but its contents will be read from stdin instead).

## v18.4.0 (date: 25.10.2024)

- support `--show-blueprint` flag in `rcc holotree hash` to show blueprint.
Expand Down
11 changes: 8 additions & 3 deletions htfs/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,12 +270,17 @@ func ComposeFinalBlueprint(userFiles []string, packfile string, devDependencies
fail.On(err != nil, "Failure: %v", err)
}
fail.On(right == nil, "Missing environment specification(s).")
content, err := right.AsYaml()
fail.On(err != nil, "YAML error: %v", err)
blueprint = []byte(strings.TrimSpace(content))
blueprint, err = BlueprintFromEnvironment(right)
fail.On(err != nil, "Blueprint from environment error: %v", err)
if !right.IsCacheable() {
fingerprint := common.BlueprintHash(blueprint)
pretty.Warning("Holotree blueprint %q is not publicly cacheable. Use `rcc robot diagnostics` to find out more.", fingerprint)
}
return config, blueprint, nil
}

func BlueprintFromEnvironment(environment *conda.Environment) ([]byte, error) {
content, err := environment.AsYaml()
fail.On(err != nil, "YAML error: %v", err)
return []byte(strings.TrimSpace(content)), nil
}
4 changes: 2 additions & 2 deletions robot_tests/__init__.robot
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
*** Settings ***
Resource resources.robot
Suite Setup Prepare Local
Suite Teardown Clean Local

Suite Setup Prepare Local
Suite Teardown Clean Local
1 change: 1 addition & 0 deletions robot_tests/expected/ht_hash/ht_hash_txt_conda.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Blueprint hash for [robot_tests/conda.yaml] is 79ba58276b645f09.
42 changes: 42 additions & 0 deletions robot_tests/ht_hash.robot
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,45 @@ Goal: Check that hash command works correctly with devdeps
...
... robot_tests/expected/ht_hash/ht_hash_devdeps_txt.txt
... use_stream=stderr

Goal: Check that hash command works reading from stdin
Run And Check Expected Output
... build/rcc holotree hash --json --show-blueprint --devdeps --controller citests --stdin package.yaml
...
... robot_tests/expected/ht_hash/ht_hash_devdeps_json_show.txt
... pass_file_as_stdin=robot_tests/bare_action/package.yaml

Run And Check Expected Output
... build/rcc holotree hash --json --devdeps --controller citests --stdin package.yaml
...
... robot_tests/expected/ht_hash/ht_hash_devdeps_json.txt
... pass_file_as_stdin=robot_tests/bare_action/package.yaml

Run And Check Expected Output
... build/rcc holotree hash --show-blueprint --devdeps --controller citests --stdin robot_tests/bare_action/package.yaml
...
... robot_tests/expected/ht_hash/ht_hash_devdeps_txt_show.txt
... use_stream=stderr
... pass_file_as_stdin=robot_tests/bare_action/package.yaml

Run And Check Expected Output
... build/rcc holotree hash --devdeps --controller citests --stdin robot_tests/bare_action/package.yaml
...
... robot_tests/expected/ht_hash/ht_hash_devdeps_txt.txt
... use_stream=stderr
... pass_file_as_stdin=robot_tests/bare_action/package.yaml

# Check conda with stdin in the same
Run And Check Expected Output
... build/rcc holotree hash --controller citests --stdin robot_tests/conda.yaml
...
... robot_tests/expected/ht_hash/ht_hash_txt_conda.txt
... use_stream=stderr
... pass_file_as_stdin=robot_tests/conda.yaml

# Check conda without stdin in the same
Run And Check Expected Output
... build/rcc holotree hash --controller citests robot_tests/conda.yaml
...
... robot_tests/expected/ht_hash/ht_hash_txt_conda.txt
... use_stream=stderr
19 changes: 15 additions & 4 deletions robot_tests/supporting.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import logging
import subprocess
import sys
from pathlib import Path

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -61,6 +62,7 @@ def run_and_return_code_output_error(
env: dict[str, str] | None = None,
cwd: str | None = None,
check: bool = False,
stdin_contents: str | None = None,
) -> tuple[int, str, str]:
command = fix_command(command)
cwd = get_cwd() if cwd is None else cwd
Expand All @@ -71,10 +73,13 @@ def run_and_return_code_output_error(
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE if stdin_contents else None,
cwd=cwd,
env=env,
)
out, err = task.communicate()
out, err = task.communicate(
input=stdin_contents.encode("utf-8") if stdin_contents else None
)
if check:
assert (
task.returncode == 0
Expand Down Expand Up @@ -114,11 +119,17 @@ def normalize_output(output: str) -> str:


def run_and_check_expected_output(
command: str, expected_output: str, expected_file: str, use_stream: str = "stdout"
command: str,
expected_output: str,
expected_file: str,
use_stream: str = "stdout",
pass_file_as_stdin: Path | None = None,
):
from pathlib import Path
stdin_contents = pass_file_as_stdin.read_text() if pass_file_as_stdin else None

ret, out, err = run_and_return_code_output_error(command)
ret, out, err = run_and_return_code_output_error(
command, stdin_contents=stdin_contents
)
assert (
ret == 0
), f"Unexpected exit code {ret!r} from {command!r} with output: {out!r} and error: {err!r}"
Expand Down

0 comments on commit db661af

Please sign in to comment.