Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8ffc81c
Create lint-format.md
NoaheCampbell Oct 15, 2025
b132732
Update lint-format.md
NoaheCampbell Oct 15, 2025
7db62db
Update lint-format.md
NoaheCampbell Oct 16, 2025
14967b8
Tool resolver (#594)
NoaheCampbell Oct 16, 2025
21b18a3
Add lint command to replicated CLI (Helm charts) (#595)
Bishibop Oct 17, 2025
e1c17bb
feat: add image extraction command and library (#593)
bennyyang11 Oct 20, 2025
caa2384
adds auth method via .replicated config files (#596)
NoaheCampbell Oct 20, 2025
81d5e0e
Add preflight linting to `replicated lint` (#597)
Bishibop Oct 20, 2025
5b27868
little cleanup on profile command (#598)
marccampbell Oct 20, 2025
1240311
Presistent profile flags and profile use (#601)
NoaheCampbell Oct 20, 2025
c30fb0c
fix: add support for ephemeralContainers and Troubleshoot resource im…
bennyyang11 Oct 20, 2025
0ab2f05
Merge image to lint command (#604)
bennyyang11 Oct 20, 2025
0705fdc
Add Support Bundle Linting to replicated lint Command (#603)
Bishibop Oct 20, 2025
fed1787
config init command (#608)
NoaheCampbell Oct 21, 2025
09873c3
Add JSON output support for lint command (#611)
bennyyang11 Oct 21, 2025
47baa21
Support glob matching in `replicated lint` (#610)
Bishibop Oct 21, 2025
89d8575
removed hard coded tool resolver defaults (#613)
NoaheCampbell Oct 21, 2025
a10e6c3
test-lint make target (#612)
marccampbell Oct 21, 2025
fd47784
test: add helm integration tests and standardize build tags (#599)
Bishibop Oct 21, 2025
31d40c4
Tool resolver tests (#614)
NoaheCampbell Oct 21, 2025
31dc4a3
calls replicated app to get tool versions not github (#619)
NoaheCampbell Oct 22, 2025
c69471d
can use "latest" as version in tools (#615)
NoaheCampbell Oct 22, 2025
64db9c9
fixing cli flags (#618)
bennyyang11 Oct 22, 2025
f835628
Does not need .replicated file to run lint (#609)
NoaheCampbell Oct 22, 2025
3f7cb9b
Add HelmChart manifest discovery (#616)
Bishibop Oct 22, 2025
3c61265
Implement preflight template rendering (#621)
Bishibop Oct 22, 2025
0512ace
Lint2 feature toggle and profile updates (#622)
NoaheCampbell Oct 22, 2025
556fa21
Fix image extraction by applying builder values from HelmChart manife…
bennyyang11 Oct 22, 2025
47e20e0
Remove cli-image-extraction.md documentation for unimplemented comman…
bennyyang11 Oct 22, 2025
542c42e
Fix test-lint.sh to use release lint command with feature flag (#626)
Bishibop Oct 23, 2025
3ddc74a
runs preflights in auto discovery (#627)
NoaheCampbell Oct 23, 2025
dfcfed9
Revert "runs preflights in auto discovery (#627)" (#628)
Bishibop Oct 23, 2025
01a02e2
Add section headers to improve linter output distinction (#624)
Bishibop Oct 23, 2025
1e8268e
Display discovered config file paths with --verbose flag (#629)
bennyyang11 Oct 23, 2025
6225156
Remove unused --output-file flag from release lint command (#631)
bennyyang11 Oct 23, 2025
485e54b
refactor: simplify linter architecture and eliminate duplication (#630)
Bishibop Oct 23, 2025
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ bin/
vendor/
.idea
.vscode
do-not-commit/
do-not-commit/
pkg/tools/embedded/
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ test-pact:
test-integration: build
go test -v ./pkg/integration/...

.PHONY: test-lint
test-lint: build
./scripts/test-lint.sh

.PHONY: publish-pact
publish-pact:
pact-broker publish ./pacts \
Expand Down
16 changes: 16 additions & 0 deletions cli/cmd/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package cmd

import (
"github.com/spf13/cobra"
)

func (r *runners) InitConfigCommand(parent *cobra.Command) *cobra.Command {
cmd := &cobra.Command{
Use: "config",
Short: "Manage .replicated configuration",
Long: `Manage .replicated configuration files for your project.`,
}

parent.AddCommand(cmd)
return cmd
}
342 changes: 342 additions & 0 deletions cli/cmd/image_extraction_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,342 @@
package cmd

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

"github.com/replicatedhq/replicated/pkg/lint2"
"github.com/replicatedhq/replicated/pkg/tools"
)

// getAbsTestDataPath returns the absolute path to a testdata subdirectory
func getAbsTestDataPath(t *testing.T, relPath string) string {
t.Helper()

// Get current working directory
cwd, err := os.Getwd()
if err != nil {
t.Fatalf("failed to get working directory: %v", err)
}

// Build path relative to project root (two levels up from cli/cmd)
projectRoot := filepath.Join(cwd, "..", "..")
absPath := filepath.Join(projectRoot, relPath)

// Verify path exists
if _, err := os.Stat(absPath); err != nil {
t.Fatalf("test data path does not exist: %s (error: %v)", absPath, err)
}

return absPath
}

func TestExtractImagesFromConfig_ChartWithRequiredValues_WithMatchingHelmChartManifest(t *testing.T) {
// Test that builder values from HelmChart manifest enable rendering of charts with required values
chartPath := getAbsTestDataPath(t, filepath.Join("testdata", "image-extraction", "chart-with-required-values-test", "chart"))
manifestGlob := getAbsTestDataPath(t, filepath.Join("testdata", "image-extraction", "chart-with-required-values-test", "manifests")) + "/*.yaml"

config := &tools.Config{
Charts: []tools.ChartConfig{
{Path: chartPath},
},
Manifests: []string{manifestGlob},
}

r := &runners{}
ctx := context.Background()

// Extract charts with metadata
charts, err := lint2.GetChartsWithMetadataFromConfig(config)
if err != nil {
t.Fatalf("GetChartsWithMetadataFromConfig failed: %v", err)
}

// Extract HelmChart manifests
helmChartManifests, err := lint2.DiscoverHelmChartManifests(config.Manifests)
if err != nil {
t.Fatalf("GetHelmChartManifestsFromConfig failed: %v", err)
}

result, err := r.extractImagesFromCharts(ctx, charts, helmChartManifests)
if err != nil {
t.Fatalf("extractImagesFromCharts failed: %v", err)
}

// Should successfully extract both postgres and redis images
if len(result.Images) < 2 {
t.Fatalf("Expected at least 2 images to be extracted with builder values, got %d", len(result.Images))
}

// Check that we got the expected images
foundPostgres := false
foundRedis := false
for _, img := range result.Images {
if (img.Repository == "library/postgres" || img.Repository == "postgres") && img.Tag == "15-alpine" {
foundPostgres = true
}
if (img.Repository == "library/redis" || img.Repository == "redis") && img.Tag == "7-alpine" {
foundRedis = true
}
}

if !foundPostgres {
t.Errorf("Expected to find postgres:15-alpine image. Got images: %+v", result.Images)
}
if !foundRedis {
t.Errorf("Expected to find redis:7-alpine image. Got images: %+v", result.Images)
}
}

func TestExtractImagesFromConfig_ChartWithRequiredValues_NoHelmChartManifest(t *testing.T) {
// Test that extraction fails when manifests are not configured
chartPath := getAbsTestDataPath(t, filepath.Join("testdata", "image-extraction", "chart-with-required-values-test", "chart"))

config := &tools.Config{
Charts: []tools.ChartConfig{
{Path: chartPath},
},
Manifests: []string{}, // No manifests configured
}

// Try to extract HelmChart manifests - should fail because manifests are required
_, err := lint2.DiscoverHelmChartManifests(config.Manifests)

// Should fail because manifests are required
if err == nil {
t.Fatal("Expected error when manifests not configured, got nil")
}

// Error should mention manifests configuration
if !strings.Contains(err.Error(), "no manifests configured") {
t.Errorf("Expected error about manifests configuration, got: %v", err)
}
}


func TestExtractImagesFromConfig_NonMatchingHelmChart_FailsToRender(t *testing.T) {
// Test that HelmChart manifest must match chart name:version exactly
chartPath := getAbsTestDataPath(t, filepath.Join("testdata", "image-extraction", "non-matching-helmchart-test", "chart"))
manifestGlob := getAbsTestDataPath(t, filepath.Join("testdata", "image-extraction", "non-matching-helmchart-test", "manifests")) + "/*.yaml"

config := &tools.Config{
Charts: []tools.ChartConfig{
{Path: chartPath},
},
Manifests: []string{manifestGlob},
}

r := &runners{}
ctx := context.Background()

// Extract charts with metadata
charts, err := lint2.GetChartsWithMetadataFromConfig(config)
if err != nil {
t.Fatalf("GetChartsWithMetadataFromConfig failed: %v", err)
}

// Extract HelmChart manifests
helmChartManifests, err := lint2.DiscoverHelmChartManifests(config.Manifests)
if err != nil {
t.Fatalf("GetHelmChartManifestsFromConfig failed: %v", err)
}

result, err := r.extractImagesFromCharts(ctx, charts, helmChartManifests)
if err != nil {
t.Fatalf("extractImagesFromCharts failed: %v", err)
}

// Should get 0 images because HelmChart doesn't match (different name)
if len(result.Images) != 0 {
t.Errorf("Expected 0 images (HelmChart name doesn't match chart name), got %d: %+v", len(result.Images), result.Images)
}

// Should have a warning about the failure
if len(result.Warnings) == 0 {
t.Error("Expected at least one warning about failed extraction")
}
}

func TestExtractImagesFromConfig_MultipleCharts_MixedScenario(t *testing.T) {
// Test extracting from multiple charts - one with builder values, one without
chart1Path := getAbsTestDataPath(t, filepath.Join("testdata", "image-extraction", "chart-with-required-values-test", "chart"))
chart2Path := getAbsTestDataPath(t, filepath.Join("testdata", "image-extraction", "simple-chart-test", "chart"))
manifestGlob := getAbsTestDataPath(t, filepath.Join("testdata", "image-extraction", "chart-with-required-values-test", "manifests")) + "/*.yaml"

config := &tools.Config{
Charts: []tools.ChartConfig{
{Path: chart1Path},
{Path: chart2Path},
},
Manifests: []string{manifestGlob},
}

r := &runners{}
ctx := context.Background()

// Extract charts with metadata
charts, err := lint2.GetChartsWithMetadataFromConfig(config)
if err != nil {
t.Fatalf("GetChartsWithMetadataFromConfig failed: %v", err)
}

// Extract HelmChart manifests
helmChartManifests, err := lint2.DiscoverHelmChartManifests(config.Manifests)
if err != nil {
t.Fatalf("GetHelmChartManifestsFromConfig failed: %v", err)
}

result, err := r.extractImagesFromCharts(ctx, charts, helmChartManifests)
if err != nil {
t.Fatalf("extractImagesFromCharts failed: %v", err)
}

// Should extract images from both charts
// Chart 1: postgres:15-alpine, redis:7-alpine (using builder values)
// Chart 2: nginx:1.21 (hardcoded)
if len(result.Images) < 3 {
t.Errorf("Expected at least 3 images (2 from chart1, 1 from chart2), got %d", len(result.Images))
}

foundPostgres := false
foundRedis := false
foundNginx := false

for _, img := range result.Images {
if (img.Repository == "library/postgres" || img.Repository == "postgres") && img.Tag == "15-alpine" {
foundPostgres = true
}
if (img.Repository == "library/redis" || img.Repository == "redis") && img.Tag == "7-alpine" {
foundRedis = true
}
if (img.Repository == "library/nginx" || img.Repository == "nginx") && img.Tag == "1.21" {
foundNginx = true
}
}

if !foundPostgres {
t.Errorf("Expected to find postgres:15-alpine from chart with builder values. Got: %+v", result.Images)
}
if !foundRedis {
t.Errorf("Expected to find redis:7-alpine from chart with builder values. Got: %+v", result.Images)
}
if !foundNginx {
t.Errorf("Expected to find nginx:1.21 from simple chart. Got: %+v", result.Images)
}
}

func TestExtractImagesFromConfig_NoCharts_ReturnsError(t *testing.T) {
// Test that empty chart configuration returns error
config := &tools.Config{
Charts: []tools.ChartConfig{},
Manifests: []string{},
}

// Extract charts with metadata - should error when no charts configured
_, err := lint2.GetChartsWithMetadataFromConfig(config)

// Should get error about no charts
if err == nil {
t.Fatal("expected error when no charts in config")
}
if !strings.Contains(err.Error(), "no charts found") {
t.Errorf("expected 'no charts found' error, got: %v", err)
}
}

func TestExtractImagesFromConfig_NoManifests_ReturnsError(t *testing.T) {
// Test that manifests are required for image extraction
chartPath := getAbsTestDataPath(t, filepath.Join("testdata", "image-extraction", "simple-chart-test", "chart"))

config := &tools.Config{
Charts: []tools.ChartConfig{
{Path: chartPath},
},
Manifests: []string{}, // No manifests configured
}

// Try to extract HelmChart manifests - should fail because manifests are required
_, err := lint2.DiscoverHelmChartManifests(config.Manifests)

// Should fail because manifests are required
if err == nil {
t.Fatal("Expected error when manifests not configured, got nil")
}

// Error should mention manifests configuration
if !strings.Contains(err.Error(), "no manifests configured") {
t.Errorf("Expected error about manifests configuration, got: %v", err)
}
}


func TestExtractImagesFromConfig_EmptyBuilder_FailsToRender(t *testing.T) {
// Test that HelmChart manifest with empty builder section doesn't provide values
chartPath := getAbsTestDataPath(t, filepath.Join("testdata", "image-extraction", "empty-builder-test", "chart"))
manifestGlob := getAbsTestDataPath(t, filepath.Join("testdata", "image-extraction", "empty-builder-test", "manifests")) + "/*.yaml"

config := &tools.Config{
Charts: []tools.ChartConfig{
{Path: chartPath},
},
Manifests: []string{manifestGlob},
}

r := &runners{}
ctx := context.Background()

// Extract charts with metadata
charts, err := lint2.GetChartsWithMetadataFromConfig(config)
if err != nil {
t.Fatalf("GetChartsWithMetadataFromConfig failed: %v", err)
}

// Extract HelmChart manifests
helmChartManifests, err := lint2.DiscoverHelmChartManifests(config.Manifests)
if err != nil {
t.Fatalf("GetHelmChartManifestsFromConfig failed: %v", err)
}

result, err := r.extractImagesFromCharts(ctx, charts, helmChartManifests)
if err != nil {
t.Fatalf("extractImagesFromCharts failed: %v", err)
}

// Should get 0 images because empty builder provides no values
if len(result.Images) != 0 {
t.Errorf("Expected 0 images (empty builder provides no values), got %d: %+v", len(result.Images), result.Images)
}

// Should have a warning about the failure
if len(result.Warnings) == 0 {
t.Error("Expected at least one warning about failed extraction")
}
}

func TestExtractImagesFromConfig_NoHelmChartInManifests_FailsDiscovery(t *testing.T) {
// Test that manifests with other K8s resources but no HelmChart kind fail discovery
chartPath := getAbsTestDataPath(t, filepath.Join("testdata", "image-extraction", "no-helmchart-test", "chart"))
manifestGlob := getAbsTestDataPath(t, filepath.Join("testdata", "image-extraction", "no-helmchart-test", "manifests")) + "/*.yaml"

config := &tools.Config{
Charts: []tools.ChartConfig{
{Path: chartPath},
},
Manifests: []string{manifestGlob},
}

// Try to extract HelmChart manifests - should fail because manifests don't contain HelmCharts
_, err := lint2.DiscoverHelmChartManifests(config.Manifests)

// Should fail because manifests are configured but contain no HelmCharts
if err == nil {
t.Fatal("Expected error when manifests configured but no HelmCharts found, got nil")
}

// Error should mention no HelmChart resources found
if !strings.Contains(err.Error(), "no HelmChart resources found") {
t.Errorf("Expected error about no HelmCharts, got: %v", err)
}
}
Loading