diff --git a/internal/cmd/local/install.go b/internal/cmd/local/install.go index 3dd157dd..6a51950a 100644 --- a/internal/cmd/local/install.go +++ b/internal/cmd/local/install.go @@ -19,6 +19,7 @@ import ( type InstallCmd struct { Chart string `help:"Path to chart." xor:"chartver"` ChartVersion string `help:"Version to install." xor:"chartver"` + Devel bool `help:"Include development versions when searching for charts. Use with caution."` DisableAuth bool `help:"Disable auth."` DockerEmail string `group:"docker" help:"Docker email." env:"ABCTL_LOCAL_INSTALL_DOCKER_EMAIL"` DockerPassword string `group:"docker" help:"Docker password." env:"ABCTL_LOCAL_INSTALL_DOCKER_PASSWORD"` @@ -195,6 +196,7 @@ func (i *InstallCmd) installOpts(ctx context.Context, user string) (*service.Ins Hosts: i.Host, LocalStorage: !supportMinio, EnablePsql17: enablePsql17, + Devel: i.Devel, DockerServer: i.DockerServer, DockerUser: i.DockerUsername, DockerPass: i.DockerPassword, @@ -232,7 +234,15 @@ func (i *InstallCmd) installOpts(ctx context.Context, user string) (*service.Ins } func (i *InstallCmd) setDefaultChartFlags(helmClient goHelm.Client) error { - resolver := helm.NewChartResolver(helmClient) + // Add warning if using development charts + if i.Devel { + pterm.Warning.Println("Using development charts. These may be unstable.") + if i.ChartVersion == "" { + pterm.Info.Println("Consider specifying an explicit version with --chart-version for reproducibility") + } + } + + resolver := helm.NewChartResolverWithDevel(helmClient, i.Devel) resolvedChart, resolvedVersion, err := resolver.ResolveChartReference(i.Chart, i.ChartVersion) if err != nil { return fmt.Errorf("failed to resolve chart flags: %w", err) diff --git a/internal/helm/chart.go b/internal/helm/chart.go index 75e91c59..5472d8fe 100644 --- a/internal/helm/chart.go +++ b/internal/helm/chart.go @@ -17,23 +17,37 @@ type ChartResolver struct { v2RepoURL string // client provides Chart.yaml metadata extraction for local paths and URLs client goHelm.Client + // includeDevel allows prerelease versions to be considered + includeDevel bool } // NewChartResolver creates a resolver with default Airbyte v1/v2 repository URLs func NewChartResolver(client goHelm.Client) *ChartResolver { return &ChartResolver{ - v1RepoURL: common.AirbyteRepoURLv1, - v2RepoURL: common.AirbyteRepoURLv2, - client: client, + v1RepoURL: common.AirbyteRepoURLv1, + v2RepoURL: common.AirbyteRepoURLv2, + client: client, + includeDevel: false, + } +} + +// NewChartResolverWithDevel creates a resolver with default Airbyte v1/v2 repository URLs and devel flag +func NewChartResolverWithDevel(client goHelm.Client, includeDevel bool) *ChartResolver { + return &ChartResolver{ + v1RepoURL: common.AirbyteRepoURLv1, + v2RepoURL: common.AirbyteRepoURLv2, + client: client, + includeDevel: includeDevel, } } // NewChartResolverWithURLs creates a resolver with custom v1/v2 repository URLs func NewChartResolverWithURLs(client goHelm.Client, v1URL, v2URL string) *ChartResolver { return &ChartResolver{ - v1RepoURL: v1URL, - v2RepoURL: v2URL, - client: client, + v1RepoURL: v1URL, + v2RepoURL: v2URL, + client: client, + includeDevel: false, } } @@ -55,13 +69,14 @@ func ChartIsV2Plus(v string) bool { func (r *ChartResolver) ResolveChartReference(chart, version string) (string, string, error) { if chart == "" { if version == "" { - chartURL, chartVersion, err := GetLatestAirbyteChartUrlFromRepoIndex("", r.v2RepoURL) + chartURL, chartVersion, err := GetLatestAirbyteChartUrlFromRepoIndex("", r.v2RepoURL, r.includeDevel) if err != nil { return "", "", err } return chartURL, chartVersion, nil } else { - if ChartIsV2Plus(version) { + // Always use v2 repo for development versions + if r.includeDevel || ChartIsV2Plus(version) { // Construct the v2 chart URL. return fmt.Sprintf("%s/airbyte-%s.tgz", r.v2RepoURL, version), version, nil } else { diff --git a/internal/helm/locate.go b/internal/helm/locate.go index 79590125..93632c0b 100644 --- a/internal/helm/locate.go +++ b/internal/helm/locate.go @@ -41,8 +41,8 @@ var defaultLoadIndexFile loadIndexFile = repo.LoadIndexFile // GetLatestAirbyteChartUrlFromRepoIndex fetches the latest stable Airbyte Helm chart URL and version // from the given Helm repository index. Returns the chart download URL, the chart version, and an error if any. -// Only stable (non-prerelease) versions are considered. -func GetLatestAirbyteChartUrlFromRepoIndex(repoName, repoUrl string) (string, string, error) { +// Only stable (non-prerelease) versions are considered unless includeDevel is true. +func GetLatestAirbyteChartUrlFromRepoIndex(repoName, repoUrl string, includeDevel bool) (string, string, error) { chartRepository, err := defaultNewChartRepo(&repo.Entry{ Name: repoName, URL: repoUrl, @@ -78,7 +78,8 @@ func GetLatestAirbyteChartUrlFromRepoIndex(repoName, repoUrl string) (string, st version = "v" + version } - if semver.Prerelease(version) == "" { + // Include prerelease versions if includeDevel is true + if includeDevel || semver.Prerelease(version) == "" { latest = entry break } diff --git a/internal/service/install.go b/internal/service/install.go index 8b6cf231..aea29bc2 100644 --- a/internal/service/install.go +++ b/internal/service/install.go @@ -49,6 +49,7 @@ type InstallOpts struct { Hosts []string LocalStorage bool EnablePsql17 bool + Devel bool DockerServer string DockerUser string @@ -252,16 +253,23 @@ func (m *Manager) Install(ctx context.Context, opts *InstallOpts) error { pterm.Success.Println(fmt.Sprintf("Secret from '%s' created or updated", secretFile)) } + // Use v2 repo for development versions or v2+ versions + repoURL := common.AirbyteRepoURLv1 + if opts.Devel || helm.ChartIsV2Plus(opts.HelmChartVersion) { + repoURL = common.AirbyteRepoURLv2 + } + if err := m.handleChart(ctx, chartRequest{ name: "airbyte", repoName: common.AirbyteRepoName, - repoURL: common.AirbyteRepoURLv1, + repoURL: repoURL, chartName: common.AirbyteChartName, chartRelease: common.AirbyteChartRelease, chartVersion: opts.HelmChartVersion, chartLoc: opts.AirbyteChartLoc, namespace: common.AirbyteNamespace, valuesYAML: opts.HelmValuesYaml, + devel: opts.Devel, }); err != nil { // if trace.SpanError isn't called here, the logs attached // in the diagnoseAirbyteChartFailure method are lost @@ -598,6 +606,7 @@ type chartRequest struct { namespace string valuesYAML string uninstallFirst bool + devel bool } // errHelmStuck is the error returned (only from a msg perspective, not this actual error) from the underlying helm