From 8287926912a5f00e76db079bf8efc92a06b010f2 Mon Sep 17 00:00:00 2001 From: Replicated Release Pipeline Date: Mon, 18 May 2026 16:17:08 +0000 Subject: [PATCH 01/19] chore: migrate to depot CI, remove dagger and github actions - Add .depot/workflows/pr.yml with lint, unit tests, pact tests, integration tests, build, goreleaser dryrun, security (semgrep), and gate jobs - Add .depot/workflows/release.yml triggered on v* tags with build-and-test, goreleaser release, docker publish, and docs PR jobs - Add scripts/generate-docs.sh to replace Dagger GenerateDocs - Remove dagger/ module, dagger.json, and all dagger Makefile targets - Remove .github/workflows/main.yaml (all CI now on Depot) - Keep: test-unit, test-pact, test-integration, test-lint, build, publish-pact, can-i-deploy, unpublish-past-versions --- .depot/workflows/pr.yml | 139 ++++++++++- .depot/workflows/release.yml | 90 ++++++++ .github/workflows/main.yaml | 109 --------- Makefile | 26 +-- dagger.json | 16 -- dagger/.gitattributes | 4 - dagger/.gitignore | 5 - dagger/build.go | 39 ---- dagger/compatibility.go | 15 -- dagger/docs.go | 282 ----------------------- dagger/exec_utils.go | 67 ------ dagger/functionality.go | 40 ---- dagger/go.mod | 58 ----- dagger/go.sum | 101 -------- dagger/main.go | 3 - dagger/performance.go | 15 -- dagger/release.go | 435 ----------------------------------- dagger/security.go | 36 --- dagger/validate.go | 31 --- scripts/generate-docs.sh | 102 ++++++++ 20 files changed, 330 insertions(+), 1283 deletions(-) create mode 100644 .depot/workflows/release.yml delete mode 100644 .github/workflows/main.yaml delete mode 100644 dagger.json delete mode 100644 dagger/.gitattributes delete mode 100644 dagger/.gitignore delete mode 100644 dagger/build.go delete mode 100644 dagger/compatibility.go delete mode 100644 dagger/docs.go delete mode 100644 dagger/exec_utils.go delete mode 100644 dagger/functionality.go delete mode 100644 dagger/go.mod delete mode 100644 dagger/go.sum delete mode 100644 dagger/main.go delete mode 100644 dagger/performance.go delete mode 100644 dagger/release.go delete mode 100644 dagger/security.go delete mode 100644 dagger/validate.go create mode 100755 scripts/generate-docs.sh diff --git a/.depot/workflows/pr.yml b/.depot/workflows/pr.yml index 506c2ac6b..a3e23e0ba 100644 --- a/.depot/workflows/pr.yml +++ b/.depot/workflows/pr.yml @@ -5,8 +5,143 @@ on: name: "depot: PR" +concurrency: + group: depot-pr-${{ github.head_ref }} + cancel-in-progress: true + jobs: - noop: + + lint: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: ./go.mod + cache: true + cache-dependency-path: ./go.sum + - name: Check go mod tidy + run: | + go mod tidy + git diff --exit-code -- go.mod go.sum || { + echo "::error::Please run 'go mod tidy' and commit changes" + exit 1 + } + - name: Check go fmt + run: | + gofmt -d -s . | grep -q . && { + echo "::error::Please run 'gofmt -w .' and commit changes" + exit 1 + } + - name: Build binary (needed for lint tests) + run: make build + - name: Run lint tests + run: make test-lint + + unit-tests: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: ./go.mod + cache: true + cache-dependency-path: ./go.sum + - run: make test-unit + + pact-tests: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: ./go.mod + cache: true + cache-dependency-path: ./go.sum + - uses: replicatedhq/action-install-pact@v1 + - name: Setup pact environment + if: github.ref == 'refs/heads/main' + run: | + echo "PACT_VERSION=${{ github.sha }}" >> "$GITHUB_ENV" + echo "PACT_BROKER_BASE_URL=${{ secrets.PACT_BROKER_BASE_URL }}" >> "$GITHUB_ENV" + echo "PACT_BROKER_TOKEN=${{ secrets.PACT_BROKER_TOKEN }}" >> "$GITHUB_ENV" + - run: make test-pact + - if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository)) + run: make publish-pact + - if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository)) + run: make can-i-deploy || echo "::warning:: can-i-deploy says no; provider(s) must successfully verify before release" + + integration-tests: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: ./go.mod + cache: true + cache-dependency-path: ./go.sum + - run: make build + - run: make test-integration + + build: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: ./go.mod + cache: true + cache-dependency-path: ./go.sum + - run: make build + + goreleaser-dryrun: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: ./go.mod + cache: true + cache-dependency-path: ./go.sum + - name: Install goreleaser + run: go install github.com/goreleaser/goreleaser/v2@v2.14.3 + - run: goreleaser release --snapshot --clean + + security: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - name: Run semgrep + run: | + docker run --rm -v "${PWD}:/src" returntocorp/semgrep semgrep scan --config=p/golang /src + + gate: + if: always() + needs: [lint, unit-tests, pact-tests, integration-tests, build, goreleaser-dryrun, security] runs-on: ubuntu-latest steps: - - run: echo "Depot PR workflow registered" + - name: Check all results + run: | + results=( + "${{ needs.lint.result }}" + "${{ needs.unit-tests.result }}" + "${{ needs.pact-tests.result }}" + "${{ needs.integration-tests.result }}" + "${{ needs.build.result }}" + "${{ needs.goreleaser-dryrun.result }}" + "${{ needs.security.result }}" + ) + for r in "${results[@]}"; do + if [[ "$r" == "failure" || "$r" == "cancelled" ]]; then + echo "::error::Job failed: $r" + exit 1 + fi + done + echo "All checks passed" diff --git a/.depot/workflows/release.yml b/.depot/workflows/release.yml new file mode 100644 index 000000000..d5207ea84 --- /dev/null +++ b/.depot/workflows/release.yml @@ -0,0 +1,90 @@ +on: + push: + tags: + - 'v*' + +name: "depot: Release" + +jobs: + + build-and-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: ./go.mod + cache: true + cache-dependency-path: ./go.sum + - run: make test-unit + - run: make test-pact + - run: make build + + release: + needs: build-and-test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-go@v5 + with: + go-version-file: ./go.mod + cache: true + cache-dependency-path: ./go.sum + - name: Install goreleaser + run: go install github.com/goreleaser/goreleaser/v2@v2.14.3 + - name: Run goreleaser + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: goreleaser release --clean + + docker-publish: + needs: build-and-test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: ./go.mod + cache: true + cache-dependency-path: ./go.sum + - name: Build binary + run: make build + - name: Build Docker image + run: | + docker build -t replicated/vendor-cli:${GITHUB_REF_NAME} . + docker tag replicated/vendor-cli:${GITHUB_REF_NAME} replicated/vendor-cli:latest + VERSION=${GITHUB_REF_NAME#v} + MAJOR=$(echo $VERSION | cut -d. -f1) + MINOR=$(echo $VERSION | cut -d. -f1,2) + docker tag replicated/vendor-cli:${GITHUB_REF_NAME} replicated/vendor-cli:${MAJOR} + docker tag replicated/vendor-cli:${GITHUB_REF_NAME} replicated/vendor-cli:${MINOR} + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + - name: Push images + run: | + docker push replicated/vendor-cli:${GITHUB_REF_NAME} + docker push replicated/vendor-cli:latest + docker push replicated/vendor-cli:${MAJOR} + docker push replicated/vendor-cli:${MINOR} + + docs: + needs: release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: ./go.mod + cache: true + cache-dependency-path: ./go.sum + - name: Build binary + run: make build + - name: Generate and publish docs + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: ./scripts/generate-docs.sh diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml deleted file mode 100644 index 32ee193f1..000000000 --- a/.github/workflows/main.yaml +++ /dev/null @@ -1,109 +0,0 @@ -name: PR/main branch CI - -on: - pull_request: - push: - branches: - - main - -jobs: - make-unit-tests: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 - with: - go-version-file: 'go.mod' - - uses: actions/cache@v4 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - run: make test-unit - - make-pact-tests: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 - with: - go-version-file: 'go.mod' - - uses: actions/cache@v4 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - uses: replicatedhq/action-install-pact@v1 - - name: setup pact environment - run: | - if [[ $GITHUB_REF_NAME == 'main' ]]; then - echo "Adding pact environment variables" - echo "PACT_VERSION=${{ github.sha }}" >> "$GITHUB_ENV" - echo "PACT_BROKER_BASE_URL=${{ secrets.PACT_BROKER_BASE_URL }}" >> "$GITHUB_ENV" - echo "PACT_BROKER_TOKEN=${{ secrets.PACT_BROKER_TOKEN }}" >> "$GITHUB_ENV" - fi - - run: make test-pact - - if: github.ref == 'refs/heads/main' && ( github.event_name == 'push' || ( github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository ) ) - run: make publish-pact - - if: github.ref == 'refs/heads/main' && ( github.event_name == 'push' || ( github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository ) ) - run: | - make can-i-deploy || echo "::warning:: can-i-deploy says no; provider(s) must successfully verify before release" - - make-integration-tests: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 - with: - go-version-file: 'go.mod' - - uses: actions/cache@v4 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - run: make test-integration - - make-build: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 - with: - go-version-file: 'go.mod' - - uses: actions/cache@v4 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - name: make build - run: make build - - dagger-build: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v4 - - name: Install Dagger - run: curl -fsSL https://dl.dagger.io/dagger/install.sh | sudo BIN_DIR=/usr/local/bin sh - - name: Dagger build - run: make dagger-build - - # dagger-goreleaser-dryrun: - # runs-on: ubuntu-24.04 - # steps: - # - uses: actions/checkout@v4 - # - name: Install Dagger - # run: curl -fsSL https://dl.dagger.io/dagger/install.sh | sudo BIN_DIR=/usr/local/bin sh - # - name: Goreleaser dryrun - # run: make dagger-goreleaser-dryrun diff --git a/Makefile b/Makefile index df7ff1bd7..cbb09681d 100644 --- a/Makefile +++ b/Makefile @@ -91,28 +91,4 @@ build: -o bin/replicated \ cli/main.go -.PHONY: dagger-build -dagger-build: - dagger call build --progress plain export --path bin/replicated - -.PHONY: dagger-goreleaser-dryrun -dagger-goreleaser-dryrun: - dagger call goreleaser-dryrun --progress plain - -.PHONY: release -release: - dagger call release \ - --one-password-service-account-production env:OP_SERVICE_ACCOUNT_PRODUCTION \ - --version $(version) \ - --github-token env:GITHUB_TOKEN \ - --progress plain - @echo "" - @echo "✓ Release completed successfully" - @echo "Generating documentation PR..." - @$(MAKE) docs - -.PHONY: docs -docs: - dagger --progress plain call generate-docs \ - --github-token env:GITHUB_TOKEN \ - --progress plain + diff --git a/dagger.json b/dagger.json deleted file mode 100644 index bc88cf95d..000000000 --- a/dagger.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "replicated", - "engineVersion": "v0.19.9", - "sdk": { - "source": "go" - }, - "dependencies": [ - { - "name": "onepassword", - "source": "github.com/replicatedhq/daggerverse/onepassword@0b03c8c560c2067f34dab800c92154abc3834841", - "pin": "0b03c8c560c2067f34dab800c92154abc3834841" - } - ], - "source": "dagger", - "disableDefaultFunctionCaching": true -} diff --git a/dagger/.gitattributes b/dagger/.gitattributes deleted file mode 100644 index 3a454933c..000000000 --- a/dagger/.gitattributes +++ /dev/null @@ -1,4 +0,0 @@ -/dagger.gen.go linguist-generated -/internal/dagger/** linguist-generated -/internal/querybuilder/** linguist-generated -/internal/telemetry/** linguist-generated diff --git a/dagger/.gitignore b/dagger/.gitignore deleted file mode 100644 index 773338bfb..000000000 --- a/dagger/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/dagger.gen.go -/internal/dagger -/internal/querybuilder -/internal/telemetry -/.env diff --git a/dagger/build.go b/dagger/build.go deleted file mode 100644 index bc848269a..000000000 --- a/dagger/build.go +++ /dev/null @@ -1,39 +0,0 @@ -package main - -import ( - "context" - "dagger/replicated/internal/dagger" -) - -// Build compiles the replicated CLI binary. -func (r *Replicated) Build( - ctx context.Context, - - // +defaultPath="./" - source *dagger.Directory, -) (*dagger.File, error) { - image, err := goImage(ctx, source) - if err != nil { - return nil, err - } - goModCache, goBuildCache, err := goCacheVolumes(ctx, source) - if err != nil { - return nil, err - } - - binary := dag.Container(dagger.ContainerOpts{ - Platform: "linux/amd64", - }). - From(image). - WithMountedDirectory("/go/src/github.com/replicatedhq/replicated", source). - WithoutFile("/go/src/github.com/replicatedhq/replicated/bin/replicated"). - WithWorkdir("/go/src/github.com/replicatedhq/replicated"). - WithMountedCache("/go/pkg/mod", goModCache). - WithEnvVariable("GOMODCACHE", "/go/pkg/mod"). - WithMountedCache("/go/build-cache", goBuildCache). - WithEnvVariable("GOCACHE", "/go/build-cache"). - With(CacheBustingExec([]string{"make", "build"})). - File("/go/src/github.com/replicatedhq/replicated/bin/replicated") - - return binary, nil -} diff --git a/dagger/compatibility.go b/dagger/compatibility.go deleted file mode 100644 index 457862adc..000000000 --- a/dagger/compatibility.go +++ /dev/null @@ -1,15 +0,0 @@ -package main - -import ( - "context" - "dagger/replicated/internal/dagger" -) - -func validateCompatibility( - ctx context.Context, - - // +defaultPath="./" - source *dagger.Directory, -) error { - return nil -} diff --git a/dagger/docs.go b/dagger/docs.go deleted file mode 100644 index 13459ff83..000000000 --- a/dagger/docs.go +++ /dev/null @@ -1,282 +0,0 @@ -package main - -import ( - "bufio" - "bytes" - "context" - "dagger/replicated/internal/dagger" - "encoding/json" - "fmt" - "io" - "net/http" - "path/filepath" - "strings" - "time" - - "github.com/pkg/errors" -) - -func (r *Replicated) GenerateDocs( - ctx context.Context, - - // +defaultPath="./" - source *dagger.Directory, - - githubToken *dagger.Secret, -) error { - err := checkGitTree(ctx, source, githubToken) - if err != nil { - return errors.Wrap(err, "failed to check git tree") - } - - latestVersion, err := getLatestVersion(ctx, githubToken) - if err != nil { - return errors.Wrap(err, "failed to get latest version") - } - - docsContainer := dag.Container(). - From("alpine/git:latest"). - WithWorkdir("/"). - With(CacheBustingExec([]string{"git", "clone", "--depth", "1", "https://github.com/replicatedhq/replicated-docs.git", "/replicated-docs"})) - - rootDocsDirectory := docsContainer.Directory("/replicated-docs") - docsDirectory := rootDocsDirectory.Directory("/docs/reference/") - - // Remove existing CLI docs - existingDocs, err := docsDirectory.Entries(ctx) - if err != nil { - return errors.Wrap(err, "failed to get existing docs") - } - - for _, existingDoc := range existingDocs { - // 'replicated-cli-installing.mdx' is a special file that's not a CLI doc - if existingDoc == "replicated-cli-installing.mdx" { - continue - } - if !strings.HasPrefix(existingDoc, "replicated-cli") { - continue - } - - docsDirectory = docsDirectory.WithoutFile(existingDoc) - } - - // Generate CLI new docs - image, err := goImage(ctx, source) - if err != nil { - return errors.Wrap(err, "failed to detect go version") - } - goModCache, goBuildCache, err := goCacheVolumes(ctx, source) - if err != nil { - return errors.Wrap(err, "failed to create cache volumes") - } - - // generate the docs from this current commit - docs := dag.Container(). - From(image). - WithMountedDirectory("/go/src/github.com/replicatedhq/replicated", source). - WithWorkdir("/go/src/github.com/replicatedhq/replicated"). - WithMountedCache("/go/pkg/mod", goModCache). - WithEnvVariable("GOMODCACHE", "/go/pkg/mod"). - WithMountedCache("/go/build-cache", goBuildCache). - WithEnvVariable("GOCACHE", "/go/build-cache"). - WithEnvVariable("CGO_ENABLED", "0"). - With(CacheBustingExec([]string{"go", "run", "./docs/gen.go"})) - - generatedDocs := docs.Directory("/go/src/github.com/replicatedhq/replicated/gen/docs") - - entries, err := generatedDocs.Entries(ctx) - if err != nil { - return errors.Wrap(err, "failed to get generated docs") - } - - // Add new CLI docs to the docs directory while updating file names and fixing up header level - newDocFilenames := []string{} - for _, entry := range entries { - file := generatedDocs.File(entry) - - content, err := file.Contents(ctx) - if err != nil { - return errors.Wrap(err, "failed to get generated doc contents") - } - - content = cleanContent(content, entries) - - destFilename := cobraFileNameToDocsFileName(entry) - - docsDirectory = docsDirectory.WithNewFile(destFilename, content) - newDocFilenames = append(newDocFilenames, destFilename) - } - - // Update sidebar config to include new CLI docs - sidebarFile := rootDocsDirectory.File("sidebars.js") - sidebarContent, err := sidebarFile.Contents(ctx) - if err != nil { - return errors.Wrap(err, "failed to get sidebar contents") - } - - sidebarContent, err = replaceFilenamesInSidebar(sidebarContent, newDocFilenames) - if err != nil { - return errors.Wrap(err, "failed to replace filenames in sidebar") - } - - rootDocsDirectory = rootDocsDirectory.WithNewFile("sidebars.js", sidebarContent) - - docsContainer = docsContainer. - WithMountedDirectory("/replicated-docs", rootDocsDirectory). - WithMountedDirectory("/replicated-docs/docs/reference", docsDirectory). - WithWorkdir("/replicated-docs"). - WithExec([]string{"git", "diff"}) - diffOut, err := docsContainer.Stdout(ctx) - if err != nil { - return errors.Wrap(err, "failed to get diff") - } - - if diffOut == "" { - return errors.New("diff is empty") - } - - githubTokenPlaintext, err := githubToken.Plaintext(ctx) - if err != nil { - return errors.Wrap(err, "failed to get github token plaintext") - } - - branchName := fmt.Sprintf("update-cli-docs-%s-%s", latestVersion, time.Now().Format("2006-01-02-150405")) - docsContainer = docsContainer. - WithExec([]string{"git", "config", "user.email", "release@replicated.com"}). - WithExec([]string{"git", "config", "user.name", "Replicated Release Pipeline"}). - WithExec([]string{"git", "remote", "add", "dagger", fmt.Sprintf("https://%s@github.com/replicatedhq/replicated-docs.git", githubTokenPlaintext)}). - WithExec([]string{"git", "checkout", "-b", branchName}). - WithExec([]string{"git", "add", "."}). - WithExec([]string{"git", "commit", "-m", fmt.Sprintf("Update Replicated CLI docs for %s", latestVersion)}). - WithExec([]string{"git", "push", "dagger", branchName}) - - _, err = docsContainer.Stdout(ctx) - if err != nil { - return errors.Wrap(err, "failed to get push output") - } - - err = createPullRequest(ctx, branchName, fmt.Sprintf("Update Replicated CLI docs for %s", latestVersion), "", githubTokenPlaintext) - if err != nil { - return errors.Wrap(err, "failed to create pull request") - } - - return nil -} - -// Change names like "replicated_channel_inspect.md" to "replicated-cli-channel-inspect.mdx" -func cobraFileNameToDocsFileName(filename string) string { - filename = strings.ReplaceAll(filename, "replicated_", "replicated-cli-") - filename = strings.ReplaceAll(filename, "_", "-") - if filepath.Ext(filename) == ".md" { - filename = strings.TrimSuffix(filename, filepath.Ext(filename)) + ".mdx" - } - return filename -} - -func cleanContent(content string, filenames []string) string { - // Header must be level 1 in order for white spaces to be rendered correctly ("replicated api get" vs "replicated_api_get") - if strings.HasPrefix(content, "## ") { - content = content[1:] - } - - // Replace all filenames in the content with the new filenames - for _, filename := range filenames { - topicLink := cobraFileNameToDocsFileName(filename) - topicLink = strings.TrimSuffix(topicLink, filepath.Ext(topicLink)) - content = strings.ReplaceAll(content, filename, topicLink) - } - - return content -} - -func replaceFilenamesInSidebar(sidebarContent string, newDocFilenames []string) (string, error) { - scanner := bufio.NewScanner(strings.NewReader(sidebarContent)) - - newDocLines := []string{} - foundCLILabel := false - wroteNewList := false - for scanner.Scan() { - line := scanner.Text() - - if strings.Contains(line, `reference/replicated-cli-`) { // CLI commands that are generated. - continue - } - - if strings.Contains(line, `"reference/replicated"`) { // CLI root command that is also generated. - continue - } - - if strings.Contains(line, `label: "Replicated CLI"`) { - newDocLines = append(newDocLines, ` label: "Replicated CLI", // This label is generated. Do not edit.`) - foundCLILabel = true - continue - } - - if foundCLILabel && !wroteNewList && strings.Contains(line, `items: [`) { - newDocLines = append(newDocLines, ` items: [ // This list is generated. Do not edit.`) - // "reference/replicated-cli-installing" is a special file that's not a CLI doc and should at the top of the list - newDocLines = append(newDocLines, ` "reference/replicated-cli-installing",`) - for _, newDocFilename := range newDocFilenames { - newDocLines = append(newDocLines, fmt.Sprintf(` "reference/%s",`, strings.TrimSuffix(newDocFilename, filepath.Ext(newDocFilename)))) - } - wroteNewList = true - continue - } - - newDocLines = append(newDocLines, line) - } - - if !wroteNewList { - return "", fmt.Errorf("no CLI list found in sidebar") - } - - return strings.Join(newDocLines, "\n"), nil -} - -func createPullRequest(ctx context.Context, branchName string, title string, body string, githubTokenPlaintext string) error { - requestData := map[string]string{ - "title": title, - "body": body, - "head": branchName, - "base": "main", - } - - requestBody, err := json.Marshal(requestData) - if err != nil { - return errors.Wrap(err, "failed to marshal request data") - } - - req, err := http.NewRequestWithContext(ctx, "POST", "https://api.github.com/repos/replicatedhq/replicated-docs/pulls", bytes.NewReader(requestBody)) - if err != nil { - return errors.Wrap(err, "failed to create pull request") - } - - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", githubTokenPlaintext)) - req.Header.Set("Accept", "application/vnd.github+json") - req.Header.Set("X-GitHub-Api-Version", "2022-11-28") - - resp, err := http.DefaultClient.Do(req) - if err != nil { - return errors.Wrap(err, "failed to do pull request") - } - - respBody, err := io.ReadAll(resp.Body) - if err != nil { - return errors.Wrap(err, "failed to read response body") - } - - if resp.StatusCode != http.StatusCreated { - return errors.Errorf("failed to create pull request: %s", string(respBody)) - } - - var pullRequest struct { - HTMLURL string `json:"html_url"` - } - if err := json.Unmarshal(respBody, &pullRequest); err != nil { - return errors.Wrap(err, "failed to unmarshal pull request") - } - - fmt.Printf("created pull request at: %s\n", pullRequest.HTMLURL) - - return nil -} diff --git a/dagger/exec_utils.go b/dagger/exec_utils.go deleted file mode 100644 index f8d21c797..000000000 --- a/dagger/exec_utils.go +++ /dev/null @@ -1,67 +0,0 @@ -package main - -import ( - "context" - "dagger/replicated/internal/dagger" - "fmt" - "strings" - "time" -) - -// goVersion reads the go directive from go.mod in the source directory and -// returns the major.minor version (e.g. "1.26" from "go 1.26.1"). -func goVersion(ctx context.Context, source *dagger.Directory) (string, error) { - contents, err := source.File("go.mod").Contents(ctx) - if err != nil { - return "", fmt.Errorf("failed to read go.mod: %w", err) - } - for _, line := range strings.Split(contents, "\n") { - line = strings.TrimSpace(line) - if strings.HasPrefix(line, "go ") { - version := strings.TrimPrefix(line, "go ") - // strip patch version if present (e.g. "1.26.1" -> "1.26") - parts := strings.SplitN(version, ".", 3) - if len(parts) >= 2 { - return parts[0] + "." + parts[1], nil - } - return version, nil - } - } - return "", fmt.Errorf("go directive not found in go.mod") -} - -// goImage returns the golang Docker image tag for the version in go.mod. -func goImage(ctx context.Context, source *dagger.Directory) (string, error) { - v, err := goVersion(ctx, source) - if err != nil { - return "", err - } - return "golang:" + v, nil -} - -// goCacheVolumes returns the mod and build cache volumes keyed by the Go version. -func goCacheVolumes(ctx context.Context, source *dagger.Directory) (*dagger.CacheVolume, *dagger.CacheVolume, error) { - v, err := goVersion(ctx, source) - if err != nil { - return nil, nil, err - } - // replace dots for a clean cache key suffix (e.g. "126" from "1.26") - suffix := strings.ReplaceAll(v, ".", "") - modCache := dag.CacheVolume("replicated-go-mod-" + suffix) - buildCache := dag.CacheVolume("replicated-go-build-" + suffix) - return modCache, buildCache, nil -} - -// CacheBustingExec is a helper function that will add a cache busting env var automatically -// to the container. This is useful when Exec target is a dynamic event acting on an entity outside -// of the container that you absolutely want to re-run every time. -// -// Temporary hack until cache controls are a thing: https://docs.dagger.io/cookbook/#invalidate-cache -func CacheBustingExec(args []string, opts ...dagger.ContainerWithExecOpts) dagger.WithContainerFunc { - return func(c *dagger.Container) *dagger.Container { - if c == nil { - panic("CacheBustingExec requires a container, but container was nil") - } - return c.WithEnvVariable("DAGGER_CACHEBUSTER_CBE", time.Now().String()).WithExec(args, opts...) - } -} diff --git a/dagger/functionality.go b/dagger/functionality.go deleted file mode 100644 index a56d69f32..000000000 --- a/dagger/functionality.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "context" - "dagger/replicated/internal/dagger" -) - -func validateFunctionality( - ctx context.Context, - - // +defaultPath="./" - source *dagger.Directory, -) error { - image, err := goImage(ctx, source) - if err != nil { - return err - } - goModCache, goBuildCache, err := goCacheVolumes(ctx, source) - if err != nil { - return err - } - - // unit tests - unitTest := dag.Container(). - From(image). - WithMountedDirectory("/go/src/github.com/replicatedhq/replicated", source). - WithWorkdir("/go/src/github.com/replicatedhq/replicated"). - WithMountedCache("/go/pkg/mod", goModCache). - WithEnvVariable("GOMODCACHE", "/go/pkg/mod"). - WithMountedCache("/go/build-cache", goBuildCache). - WithEnvVariable("GOCACHE", "/go/build-cache"). - With(CacheBustingExec([]string{"make", "test-unit"})) - - _, err = unitTest.Stderr(ctx) - if err != nil { - return err - } - - return nil -} diff --git a/dagger/go.mod b/dagger/go.mod deleted file mode 100644 index ef540b9b3..000000000 --- a/dagger/go.mod +++ /dev/null @@ -1,58 +0,0 @@ -module dagger/replicated - -go 1.25.0 - -require ( - dagger.io/dagger v0.20.5-0.20260409204156-6e4822e59cbb - github.com/Khan/genqlient v0.8.1 - github.com/dagger/otel-go v1.43.0 - github.com/pkg/errors v0.9.1 - github.com/vektah/gqlparser/v2 v2.5.32 - go.opentelemetry.io/otel v1.43.0 - go.opentelemetry.io/otel/sdk v1.43.0 - go.opentelemetry.io/otel/trace v1.43.0 -) - -require ( - github.com/99designs/gqlgen v0.17.89 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect - go.opentelemetry.io/otel/log v0.19.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.19.0 // indirect - go.opentelemetry.io/proto/otlp v1.10.0 // indirect - golang.org/x/sync v0.20.0 // indirect - google.golang.org/grpc v1.80.0 // indirect -) - -require ( - github.com/Masterminds/semver v1.5.0 - github.com/cenkalti/backoff/v5 v5.0.3 // indirect - github.com/go-logr/logr v1.4.3 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect - github.com/sosodev/duration v1.4.0 // indirect - go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect - go.opentelemetry.io/otel/metric v1.43.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect - golang.org/x/net v0.52.0 // indirect - golang.org/x/sys v0.43.0 // indirect - golang.org/x/text v0.35.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect - google.golang.org/protobuf v1.36.11 // indirect -) - -replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.16.0 - -replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.16.0 - -replace go.opentelemetry.io/otel/log => go.opentelemetry.io/otel/log v0.16.0 - -replace go.opentelemetry.io/otel/sdk/log => go.opentelemetry.io/otel/sdk/log v0.16.0 diff --git a/dagger/go.sum b/dagger/go.sum deleted file mode 100644 index 0df7dc607..000000000 --- a/dagger/go.sum +++ /dev/null @@ -1,101 +0,0 @@ -dagger.io/dagger v0.20.5-0.20260409204156-6e4822e59cbb h1:keYNVasNCAqW20rN5BmcoAE5QpU59STiTXEDfhw/ymk= -dagger.io/dagger v0.20.5-0.20260409204156-6e4822e59cbb/go.mod h1:ZXg8+pQZaZUC8rAw4V/gPP8aKvKARIJZ+pfcV+RC1es= -github.com/99designs/gqlgen v0.17.89 h1:KzEcxPiMgQoMw3m/E85atUEHyZyt0PbAflMia5Kw8z8= -github.com/99designs/gqlgen v0.17.89/go.mod h1:GFqruTVGB7ZTdrf1uzOagpXbY7DrEt1pIxnTdhIbWvQ= -github.com/Khan/genqlient v0.8.1 h1:wtOCc8N9rNynRLXN3k3CnfzheCUNKBcvXmVv5zt6WCs= -github.com/Khan/genqlient v0.8.1/go.mod h1:R2G6DzjBvCbhjsEajfRjbWdVglSH/73kSivC9TLWVjU= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= -github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= -github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/dagger/otel-go v1.43.0 h1:AYCnAamWmxtSxigWPTgC+8EWqiWPcDZEegh8y05gdJ8= -github.com/dagger/otel-go v1.43.0/go.mod h1:83CTuXi70zcx1kaym5buqmb7RNzg1E9dEiQSFyLbLdU= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= -github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= -github.com/sosodev/duration v1.4.0 h1:35ed0KiVFriGHHzZZJaZLgmTEEICIyt8Sx0RQfj9IjE= -github.com/sosodev/duration v1.4.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/vektah/gqlparser/v2 v2.5.32 h1:k9QPJd4sEDTL+qB4ncPLflqTJ3MmjB9SrVzJrawpFSc= -github.com/vektah/gqlparser/v2 v2.5.32/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts= -go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= -go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.16.0 h1:ZVg+kCXxd9LtAaQNKBxAvJ5NpMf7LpvEr4MIZqb0TMQ= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.16.0/go.mod h1:hh0tMeZ75CCXrHd9OXRYxTlCAdxcXioWHFIpYw2rZu8= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.16.0 h1:djrxvDxAe44mJUrKataUbOhCKhR3F8QCyWucO16hTQs= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.16.0/go.mod h1:dt3nxpQEiSoKvfTVxp3TUg5fHPLhKtbcnN3Z1I1ePD0= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0/go.mod h1:HBy4BjzgVE8139ieRI75oXm3EcDN+6GhD88JT1Kjvxg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak= -go.opentelemetry.io/otel/log v0.16.0 h1:DeuBPqCi6pQwtCK0pO4fvMB5eBq6sNxEnuTs88pjsN4= -go.opentelemetry.io/otel/log v0.16.0/go.mod h1:rWsmqNVTLIA8UnwYVOItjyEZDbKIkMxdQunsIhpUMes= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/log v0.16.0 h1:e/b4bdlQwC5fnGtG3dlXUrNOnP7c8YLVSpSfEBIkTnI= -go.opentelemetry.io/otel/sdk/log v0.16.0/go.mod h1:JKfP3T6ycy7QEuv3Hj8oKDy7KItrEkus8XJE6EoSzw4= -go.opentelemetry.io/otel/sdk/log/logtest v0.16.0 h1:/XVkpZ41rVRTP4DfMgYv1nEtNmf65XPPyAdqV90TMy4= -go.opentelemetry.io/otel/sdk/log/logtest v0.16.0/go.mod h1:iOOPgQr5MY9oac/F5W86mXdeyWZGleIx3uXO98X2R6Y= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= -go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= -go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= -gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= -gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= -google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= -google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= -google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/dagger/main.go b/dagger/main.go deleted file mode 100644 index 75e60d1ed..000000000 --- a/dagger/main.go +++ /dev/null @@ -1,3 +0,0 @@ -package main - -type Replicated struct{} diff --git a/dagger/performance.go b/dagger/performance.go deleted file mode 100644 index 5a8132631..000000000 --- a/dagger/performance.go +++ /dev/null @@ -1,15 +0,0 @@ -package main - -import ( - "context" - "dagger/replicated/internal/dagger" -) - -func validatePerformance( - ctx context.Context, - - // +defaultPath="./" - source *dagger.Directory, -) error { - return nil -} diff --git a/dagger/release.go b/dagger/release.go deleted file mode 100644 index 5e1a4f11b..000000000 --- a/dagger/release.go +++ /dev/null @@ -1,435 +0,0 @@ -package main - -import ( - "context" - "dagger/replicated/internal/dagger" - "encoding/json" - "fmt" - "net/http" - "strings" - - "github.com/Masterminds/semver" - "github.com/pkg/errors" -) - -var goreleaserVersion = "v2.14.3" - -func (r *Replicated) Release( - ctx context.Context, - - // +defaultPath="./" - source *dagger.Directory, - - version string, - - // +default=false - snapshot bool, - - // +default=false - clean bool, - - onePasswordServiceAccountProduction *dagger.Secret, - - githubToken *dagger.Secret, -) error { - // Check all required environment variables / secrets before starting work - if err := validateReleaseSecrets(ctx, githubToken, onePasswordServiceAccountProduction); err != nil { - return err - } - - err := checkGitTree(ctx, source, githubToken) - if err != nil { - return errors.Wrap(err, "failed to check git tree") - } - - previousVersionTag, err := getLatestVersion(ctx, githubToken) - if err != nil { - return errors.Wrap(err, "failed to get latest version") - } - - previousReleaseBranchName, err := getReleaseBranchName(ctx, previousVersionTag) - if err != nil { - return errors.Wrap(err, "failed to get release branch name") - } - - major, minor, patch, err := getNextVersion(ctx, previousVersionTag, version) - if err != nil { - return errors.Wrap(err, "failed to get next version") - } - - fmt.Printf("Releasing as version %d.%d.%d\n", major, minor, patch) - - // replace the version in the Makefile - buildFileContent, err := source.File("./pkg/version/build.go").Contents(ctx) - if err != nil { - return errors.Wrap(err, "failed to get build file contents") - } - buildFileContent = strings.ReplaceAll(buildFileContent, "const version = \"unknown\"", fmt.Sprintf("const version = \"%d.%d.%d\"", major, minor, patch)) - updatedSource := source.WithNewFile("./pkg/version/build.go", buildFileContent) - - releaseBranchName := fmt.Sprintf("release-%d.%d.%d", major, minor, patch) - githubTokenPlaintext, err := githubToken.Plaintext(ctx) - if err != nil { - return errors.Wrap(err, "failed to get github token plaintext") - } - - // mount that and commit the updated build.go to git (don't push) - // so that goreleaser won't have a dirty git tree error - gitCommitContainer := dag.Container(). - From("alpine/git:latest"). - WithMountedDirectory("/go/src/github.com/replicatedhq/replicated", updatedSource). - WithWorkdir("/go/src/github.com/replicatedhq/replicated"). - WithExec([]string{"git", "config", "user.email", "release@replicated.com"}). - WithExec([]string{"git", "config", "user.name", "Replicated Release Pipeline"}). - WithExec([]string{"git", "remote", "add", "dagger", fmt.Sprintf("https://%s@github.com/replicatedhq/replicated.git", githubTokenPlaintext)}). - WithExec([]string{"git", "checkout", "-b", releaseBranchName}). - WithExec([]string{"git", "add", "pkg/version/build.go"}). - WithExec([]string{"git", "commit", "-m", fmt.Sprintf("Set version to %d.%d.%d", major, minor, patch)}). - WithExec([]string{"git", "push", "dagger", releaseBranchName}) - _, err = gitCommitContainer.Stdout(ctx) - if err != nil { - return errors.Wrap(err, "failed to get git commit stdout") - } - updatedSource = gitCommitContainer.Directory("/go/src/github.com/replicatedhq/replicated") - - nextVersionTag := fmt.Sprintf("v%d.%d.%d", major, minor, patch) - - tagContainer := dag.Container(). - From("alpine/git:latest"). - WithMountedDirectory("/go/src/github.com/replicatedhq/replicated", updatedSource). - WithWorkdir("/go/src/github.com/replicatedhq/replicated"). - With(CacheBustingExec([]string{"git", "tag", nextVersionTag})). - With(CacheBustingExec([]string{"git", "push", "dagger", nextVersionTag})). - With(CacheBustingExec([]string{"git", "fetch", "dagger", previousReleaseBranchName})). - With(CacheBustingExec([]string{"git", "fetch", "dagger", "--tags"})) - _, err = tagContainer.Stdout(ctx) - if err != nil { - return errors.Wrap(err, "failed to get tag stdout") - } - - // copy the source that has the tag included in it - updatedSource = tagContainer.Directory("/go/src/github.com/replicatedhq/replicated") - - image, err := goImage(ctx, updatedSource) - if err != nil { - return errors.Wrap(err, "failed to detect go version") - } - goModCache, goBuildCache, err := goCacheVolumes(ctx, updatedSource) - if err != nil { - return errors.Wrap(err, "failed to create cache volumes") - } - - replicatedBinary := dag.Container(dagger.ContainerOpts{ - Platform: "linux/amd64", - }). - From(image). - WithMountedDirectory("/go/src/github.com/replicatedhq/replicated", updatedSource). - WithoutFile("/go/src/github.com/replicatedhq/replicated/bin/replicated"). - WithWorkdir("/go/src/github.com/replicatedhq/replicated"). - WithMountedCache("/go/pkg/mod", goModCache). - WithEnvVariable("GOMODCACHE", "/go/pkg/mod"). - WithMountedCache("/go/build-cache", goBuildCache). - WithEnvVariable("GOCACHE", "/go/build-cache"). - With(CacheBustingExec([]string{"make", "build"})). - File("/go/src/github.com/replicatedhq/replicated/bin/replicated") - - dockerContainer := dag.Container(dagger.ContainerOpts{ - Platform: "linux/amd64", - }). - From("alpine:latest"). - WithExec([]string{"apk", "add", "--no-cache", "ca-certificates", "curl", "git", "nodejs", "npm"}). - WithExec([]string{"update-ca-certificates"}). - WithExec([]string{"npm", "install", "-g", "replicated-lint"}). - WithEnvVariable("IN_CONTAINER", "1"). - WithLabel("com.replicated.vendor_cli", "true"). - WithWorkdir("/out"). - WithEntrypoint([]string{"/replicated"}). - WithFile("/replicated", replicatedBinary) - _, err = dockerContainer.Stdout(ctx) - if err != nil { - return errors.Wrap(err, "failed to get docker container stdout") - } - - username, err := dag.Onepassword().FindSecret( - onePasswordServiceAccountProduction, - "Developer Automation Production", - "Docker Hub Release Account", - "username", - ).Plaintext(ctx) - if err != nil { - return errors.Wrap(err, "failed to get docker hub username") - } - password := dag.Onepassword().FindSecret( - onePasswordServiceAccountProduction, - "Developer Automation Production", - "Docker Hub Release Account", - "password", - ) - - dockerContainer = dockerContainer.WithRegistryAuth("", username, password) - if _, err := dockerContainer.Publish(ctx, "replicated/vendor-cli:latest"); err != nil { - return errors.Wrap(err, "failed to publish latest docker container") - } - if _, err := dockerContainer.Publish(ctx, fmt.Sprintf("replicated/vendor-cli:%d", major)); err != nil { - return errors.Wrap(err, "failed to publish major docker container") - } - if _, err := dockerContainer.Publish(ctx, fmt.Sprintf("replicated/vendor-cli:%d.%d", major, minor)); err != nil { - return errors.Wrap(err, "failed to publish minor docker container") - } - if _, err := dockerContainer.Publish(ctx, fmt.Sprintf("replicated/vendor-cli:%d.%d.%d", major, minor, patch)); err != nil { - return errors.Wrap(err, "failed to publish patch docker container") - } - - args := []string{"goreleaser"} - if snapshot { - args = append(args, "release", "--snapshot") - } else { - args = append(args, "release") - } - if clean { - args = append(args, "--clean") - } - - ctr := newGoreleaserContainer(ctx, updatedSource). - WithSecretVariable("GITHUB_TOKEN", githubToken). - WithEnvVariable("GORELEASER_CURRENT_TAG", nextVersionTag). - WithEnvVariable("GORELEASER_PREVIOUS_TAG", previousVersionTag). - With(CacheBustingExec(args)) - - _, err = ctr.Stdout(ctx) - if err != nil { - return errors.Wrap(err, "failed to run goreleaser") - } - - return nil -} - -// newGoreleaserContainer returns a Go container with goreleaser installed and -// the source mounted, ready to run goreleaser commands. -func newGoreleaserContainer(ctx context.Context, source *dagger.Directory) *dagger.Container { - image, err := goImage(ctx, source) - if err != nil { - image = "golang:latest" - } - goModCache, goBuildCache, err := goCacheVolumes(ctx, source) - if err != nil { - goModCache = dag.CacheVolume("replicated-go-mod") - goBuildCache = dag.CacheVolume("replicated-go-build") - } - - return dag.Container(). - From(image). - WithExec([]string{"go", "install", "github.com/goreleaser/goreleaser/v2@" + goreleaserVersion}). - WithMountedDirectory("/src", source). - WithWorkdir("/src"). - WithMountedCache("/go/pkg/mod", goModCache). - WithEnvVariable("GOMODCACHE", "/go/pkg/mod"). - WithMountedCache("/go/build-cache", goBuildCache). - WithEnvVariable("GOCACHE", "/go/build-cache") -} - -// GoreleaserDryrun runs a goreleaser snapshot build to verify the release -// configuration works. This is intended to be used as a PR check. -func (r *Replicated) GoreleaserDryrun( - ctx context.Context, - - // +defaultPath="./" - source *dagger.Directory, -) error { - ctr := newGoreleaserContainer(ctx, source). - With(CacheBustingExec([]string{"goreleaser", "release", "--snapshot", "--clean"})) - - _, err := ctr.Stdout(ctx) - if err != nil { - return errors.Wrap(err, "failed to run goreleaser dryrun") - } - - return nil -} - -func validateReleaseSecrets(ctx context.Context, githubToken, onePasswordServiceAccountProduction *dagger.Secret) error { - var missing []string - - if githubToken == nil { - missing = append(missing, "GITHUB_TOKEN") - } else { - gt, err := githubToken.Plaintext(ctx) - if err != nil { - return errors.Wrap(err, "failed to read GITHUB_TOKEN secret") - } - if strings.TrimSpace(gt) == "" { - missing = append(missing, "GITHUB_TOKEN") - } - } - - if onePasswordServiceAccountProduction == nil { - missing = append(missing, "OP_SERVICE_ACCOUNT_PRODUCTION") - } else { - op, err := onePasswordServiceAccountProduction.Plaintext(ctx) - if err != nil { - return errors.Wrap(err, "failed to read OP_SERVICE_ACCOUNT_PRODUCTION secret") - } - if strings.TrimSpace(op) == "" { - missing = append(missing, "OP_SERVICE_ACCOUNT_PRODUCTION") - } - } - - if len(missing) > 0 { - return fmt.Errorf("required environment variables are not set: %s", strings.Join(missing, ", ")) - } - - return nil -} - -func getNextVersion(ctx context.Context, latestVersion string, version string) (int64, int64, int64, error) { - parsedLatestVersion, err := semver.NewVersion(latestVersion) - if err != nil { - return 0, 0, 0, err - } - - switch version { - case "major": - return parsedLatestVersion.Major() + 1, 0, 0, nil - case "minor": - return parsedLatestVersion.Major(), parsedLatestVersion.Minor() + 1, 0, nil - case "patch": - return parsedLatestVersion.Major(), parsedLatestVersion.Minor(), parsedLatestVersion.Patch() + 1, nil - default: - v, err := semver.NewVersion(version) - if err != nil { - return 0, 0, 0, err - } - return v.Major(), v.Minor(), v.Patch(), nil - } -} - -func getReleaseBranchName(ctx context.Context, latestVersion string) (string, error) { - parsedLatestVersion, err := semver.NewVersion(latestVersion) - if err != nil { - return "", err - } - - return fmt.Sprintf("release-%d.%d.%d", parsedLatestVersion.Major(), parsedLatestVersion.Minor(), parsedLatestVersion.Patch()), nil -} - -func getLatestVersion(ctx context.Context, githubToken *dagger.Secret) (string, error) { - req, err := http.NewRequest("GET", "https://api.github.com/repos/replicatedhq/replicated/releases/latest", nil) - if err != nil { - return "", err - } - - githubTokenPlaintext, err := githubToken.Plaintext(ctx) - if err != nil { - return "", errors.Wrap(err, "failed to get github token plaintext") - } - - req.Header.Set("Authorization", fmt.Sprintf("token %s", githubTokenPlaintext)) - - resp, err := http.DefaultClient.Do(req) - if err != nil { - return "", errors.Wrap(err, "failed to do github request") - } - - defer resp.Body.Close() - - var release struct { - TagName string `json:"tag_name"` - } - if err := json.NewDecoder(resp.Body).Decode(&release); err != nil { - return "", err - } - - return release.TagName, nil -} - -var ( - // ErrGitTreeNotClean = errors.New("Your git tree is not clean. You cannot release what's not commited.") - ErrMainBranch = errors.New("You must be on the main branch to release") - ErrCommitNotInGitHub = errors.New("You must merge your changes into the main branch before releasing") -) - -// checkGitTree will return nil if the local git tree is clean or an error if it's not -func checkGitTree(ctx context.Context, source *dagger.Directory, githubToken *dagger.Secret) error { - container := dag.Container(). - From("alpine/git:latest"). - WithMountedDirectory("/go/src/github.com/replicatedhq/replicated", source). - WithWorkdir("/go/src/github.com/replicatedhq/replicated"). - With(CacheBustingExec([]string{"git", "status", "--porcelain"})) - - gitStatusOutput, err := container.Stdout(ctx) - if err != nil { - return errors.Wrap(err, "failed to get git status") - } - - gitStatusOutput = strings.TrimSpace(gitStatusOutput) - - if len(gitStatusOutput) > 0 { - fmt.Printf("output: %s\n", gitStatusOutput) - return fmt.Errorf("error: dirty tree: %q", gitStatusOutput) - } - - container = dag.Container(). - From("alpine/git:latest"). - WithMountedDirectory("/go/src/github.com/replicatedhq/replicated", source). - WithWorkdir("/go/src/github.com/replicatedhq/replicated"). - With(CacheBustingExec([]string{"git", "branch"})) - - gitBranchOutput, err := container.Stdout(ctx) - if err != nil { - return errors.Wrap(err, "failed to get git branch") - } - - gitBranchOutput = strings.TrimSpace(gitBranchOutput) - - if !strings.Contains(gitBranchOutput, "* main") { - return ErrMainBranch - } - - container = dag.Container(). - From("alpine/git:latest"). - WithMountedDirectory("/go/src/github.com/replicatedhq/replicated", source). - WithWorkdir("/go/src/github.com/replicatedhq/replicated"). - With(CacheBustingExec([]string{"git", "rev-parse", "HEAD"})) - - commit, err := container.Stdout(ctx) - if err != nil { - return errors.Wrap(err, "failed to get git commit") - } - - commit = strings.TrimSpace(commit) - - req, err := http.NewRequest("GET", fmt.Sprintf("https://api.github.com/repos/replicatedhq/replicated/commits/%s", commit), nil) - if err != nil { - return errors.Wrap(err, "failed to create github request") - } - - githubTokenPlaintext, err := githubToken.Plaintext(ctx) - if err != nil { - return errors.Wrap(err, "failed to get github token plaintext") - } - - req.Header.Set("Authorization", fmt.Sprintf("token %s", githubTokenPlaintext)) - - resp, err := http.DefaultClient.Do(req) - if err != nil { - return errors.Wrap(err, "failed to do github request") - } - defer resp.Body.Close() - - type GitHubResponse struct { - SHA string `json:"sha"` - NodeID string `json:"node_id"` - Status string `json:"status"` - } - - var ghResp GitHubResponse - if err := json.NewDecoder(resp.Body).Decode(&ghResp); err != nil { - return errors.Wrap(err, "failed to decode github response") - } - - if ghResp.Status == "422" { - return ErrCommitNotInGitHub - } - - return nil -} diff --git a/dagger/security.go b/dagger/security.go deleted file mode 100644 index 34a265505..000000000 --- a/dagger/security.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import ( - "context" - "dagger/replicated/internal/dagger" -) - -func validateSecurity( - ctx context.Context, - - // +defaultPath="./" - source *dagger.Directory, -) error { - goModCache, goBuildCache, err := goCacheVolumes(ctx, source) - if err != nil { - return err - } - - // run semgrep - semgrep := dag.Container(). - From("returntocorp/semgrep"). - WithMountedDirectory("/go/src/github.com/replicatedhq/replicated", source). - WithWorkdir("/go/src/github.com/replicatedhq/replicated"). - WithMountedCache("/go/pkg/mod", goModCache). - WithEnvVariable("GOMODCACHE", "/go/pkg/mod"). - WithMountedCache("/go/build-cache", goBuildCache). - WithEnvVariable("GOCACHE", "/go/build-cache"). - With(CacheBustingExec([]string{"semgrep", "scan", "--config=p/golang", "."})) - - _, err = semgrep.Stderr(ctx) - if err != nil { - return err - } - - return nil -} diff --git a/dagger/validate.go b/dagger/validate.go deleted file mode 100644 index e1807e55f..000000000 --- a/dagger/validate.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "context" - "dagger/replicated/internal/dagger" -) - -func (r *Replicated) Validate( - ctx context.Context, - - // +defaultPath="./" - source *dagger.Directory, -) error { - if err := validateSecurity(ctx, source); err != nil { - return err - } - - if err := validateFunctionality(ctx, source); err != nil { - return err - } - - if err := validateCompatibility(ctx, source); err != nil { - return err - } - - if err := validatePerformance(ctx, source); err != nil { - return err - } - - return nil -} diff --git a/scripts/generate-docs.sh b/scripts/generate-docs.sh new file mode 100755 index 000000000..666734aa7 --- /dev/null +++ b/scripts/generate-docs.sh @@ -0,0 +1,102 @@ +#!/bin/bash +set -euo pipefail + +# Generate CLI docs and open a PR against replicatedhq/replicated-docs +# This replaces the Dagger GenerateDocs function. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +VERSION="${GITHUB_REF_NAME:-unknown}" +BRANCH_NAME="update-cli-docs-${VERSION}-$(date +%Y-%m-%d-%H%M%S)" + +# Build the binary and generate docs +cd "${REPO_ROOT}" +go run ./docs/gen.go + +# Clone replicated-docs +DOCS_DIR="/tmp/replicated-docs" +rm -rf "${DOCS_DIR}" +git clone --depth 1 "https://${GITHUB_TOKEN}@github.com/replicatedhq/replicated-docs.git" "${DOCS_DIR}" + +cd "${DOCS_DIR}" +git config user.email "release@replicated.com" +git config user.name "Replicated Release Pipeline" + +# Remove existing CLI docs (except installing) +for f in docs/reference/replicated-cli-*.mdx; do + if [ -f "$f" ] && [ "$(basename "$f")" != "replicated-cli-installing.mdx" ]; then + rm "$f" + fi +done + +# Remove the root CLI doc too +if [ -f "docs/reference/replicated.mdx" ]; then + rm "docs/reference/replicated.mdx" +fi + +# Copy generated docs +cd "${REPO_ROOT}" +for f in gen/docs/*.md; do + if [ ! -f "$f" ]; then + continue + fi + # Convert filename: replicated_channel_inspect.md -> replicated-cli-channel-inspect.mdx + dest_name=$(basename "$f" .md | sed 's/replicated_/replicated-cli-/g; s/_/-/g').mdx + + content=$(cat "$f") + + # Fix header level + if [[ "$content" == "## "* ]]; then + content="#${content:1}" + fi + + # Replace internal links + for ref in gen/docs/*.md; do + ref_name=$(basename "$ref" .md) + dest_ref=$(echo "$ref_name" | sed 's/replicated_/replicated-cli-/g; s/_/-/g') + content="${content//${ref_name}/${dest_ref}}" + done + + echo "$content" > "${DOCS_DIR}/docs/reference/${dest_name}" +done + +# Update sidebars.js +cd "${DOCS_DIR}" +node -e " +const fs = require('fs'); +let sidebar = fs.readFileSync('sidebars.js', 'utf8'); + +// Find the Replicated CLI section and replace its items +const cliItems = ['reference/replicated-cli-installing']; +const files = fs.readdirSync('docs/reference') + .filter(f => f.startsWith('replicated-cli-') && f !== 'replicated-cli-installing.mdx') + .map(f => 'reference/' + f.replace('.mdx', '')) + .sort(); +cliItems.push(...files); + +// Simple regex replacement for the items array +const pattern = /(label:\s*['\"]Replicated CLI['\"],?\s*\n\s*items:\s*\[)[^\]]*(\])/; +const replacement = '\$1\n ' + cliItems.map(i => '\"' + i + '\"').join(',\n ') + '\n \$2'; +sidebar = sidebar.replace(pattern, replacement); + +fs.writeFileSync('sidebars.js', sidebar); +" + +# Commit and push +git checkout -b "${BRANCH_NAME}" +git add . +git commit -m "Update Replicated CLI docs for ${VERSION}" +git push origin "${BRANCH_NAME}" + +# Open PR +curl -s -X POST \ + -H "Authorization: Bearer ${GITHUB_TOKEN}" \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/replicatedhq/replicated-docs/pulls \ + -d "{ + \"title\": \"Update Replicated CLI docs for ${VERSION}\", + \"head\": \"${BRANCH_NAME}\", + \"base\": \"main\" + }" | jq -r '.html_url // .message' From 35da13c2c42d6aecc3574886f70bcbbc4dce67bc Mon Sep 17 00:00:00 2001 From: Replicated Release Pipeline Date: Mon, 18 May 2026 16:57:34 +0000 Subject: [PATCH 02/19] chore: trigger CI From c3bd3cbe74d0b3674ef18970e77bfc479db00004 Mon Sep 17 00:00:00 2001 From: Replicated Release Pipeline Date: Mon, 18 May 2026 17:04:17 +0000 Subject: [PATCH 03/19] chore: trigger CI after removing branch protection checks From d9ff582e39d641289465e3fe139fb91a30ddd39e Mon Sep 17 00:00:00 2001 From: Replicated Release Pipeline Date: Mon, 18 May 2026 17:05:00 +0000 Subject: [PATCH 04/19] chore: trigger CI round 3 From c88498ee4e505c89d93f6665bde5a4ae6f254ba3 Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Mon, 18 May 2026 10:17:54 -0700 Subject: [PATCH 05/19] chore: trigger depot ci From 2b10fc506a77dddd2045117e2ca8d66c8a079de9 Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Mon, 18 May 2026 10:21:30 -0700 Subject: [PATCH 06/19] fix: address depot ci migration issues --- .depot/workflows/pr.yml | 9 ++++++--- .depot/workflows/release.yml | 14 +++++++++----- Dockerfile.release | 15 +++++++++++++++ scripts/generate-docs.sh | 2 +- 4 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 Dockerfile.release diff --git a/.depot/workflows/pr.yml b/.depot/workflows/pr.yml index a3e23e0ba..d22099c6f 100644 --- a/.depot/workflows/pr.yml +++ b/.depot/workflows/pr.yml @@ -2,6 +2,8 @@ on: pull_request: branches: [main] types: [opened, synchronize, reopened, ready_for_review] + push: + branches: [main] name: "depot: PR" @@ -30,10 +32,10 @@ jobs: } - name: Check go fmt run: | - gofmt -d -s . | grep -q . && { + if gofmt -d -s . | grep -q .; then echo "::error::Please run 'gofmt -w .' and commit changes" exit 1 - } + fi - name: Build binary (needed for lint tests) run: make build - name: Run lint tests @@ -72,7 +74,8 @@ jobs: - if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository)) run: make publish-pact - if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository)) - run: make can-i-deploy || echo "::warning:: can-i-deploy says no; provider(s) must successfully verify before release" + run: | + make can-i-deploy || echo "::warning:: can-i-deploy says no; provider(s) must successfully verify before release" integration-tests: runs-on: ubuntu-latest diff --git a/.depot/workflows/release.yml b/.depot/workflows/release.yml index d5207ea84..44b9e34aa 100644 --- a/.depot/workflows/release.yml +++ b/.depot/workflows/release.yml @@ -17,6 +17,7 @@ jobs: cache: true cache-dependency-path: ./go.sum - run: make test-unit + - uses: replicatedhq/action-install-pact@v1 - run: make test-pact - run: make build @@ -53,13 +54,16 @@ jobs: run: make build - name: Build Docker image run: | - docker build -t replicated/vendor-cli:${GITHUB_REF_NAME} . - docker tag replicated/vendor-cli:${GITHUB_REF_NAME} replicated/vendor-cli:latest VERSION=${GITHUB_REF_NAME#v} MAJOR=$(echo $VERSION | cut -d. -f1) MINOR=$(echo $VERSION | cut -d. -f1,2) - docker tag replicated/vendor-cli:${GITHUB_REF_NAME} replicated/vendor-cli:${MAJOR} - docker tag replicated/vendor-cli:${GITHUB_REF_NAME} replicated/vendor-cli:${MINOR} + echo "VERSION=${VERSION}" >> "$GITHUB_ENV" + echo "MAJOR=${MAJOR}" >> "$GITHUB_ENV" + echo "MINOR=${MINOR}" >> "$GITHUB_ENV" + docker build -f Dockerfile.release -t replicated/vendor-cli:${VERSION} . + docker tag replicated/vendor-cli:${VERSION} replicated/vendor-cli:latest + docker tag replicated/vendor-cli:${VERSION} replicated/vendor-cli:${MAJOR} + docker tag replicated/vendor-cli:${VERSION} replicated/vendor-cli:${MINOR} - name: Login to Docker Hub uses: docker/login-action@v3 with: @@ -67,7 +71,7 @@ jobs: password: ${{ secrets.DOCKERHUB_PASSWORD }} - name: Push images run: | - docker push replicated/vendor-cli:${GITHUB_REF_NAME} + docker push replicated/vendor-cli:${VERSION} docker push replicated/vendor-cli:latest docker push replicated/vendor-cli:${MAJOR} docker push replicated/vendor-cli:${MINOR} diff --git a/Dockerfile.release b/Dockerfile.release new file mode 100644 index 000000000..e32131277 --- /dev/null +++ b/Dockerfile.release @@ -0,0 +1,15 @@ +FROM alpine:latest + +RUN apk add --no-cache ca-certificates curl git nodejs npm \ + && update-ca-certificates \ + && npm install -g replicated-lint + +ENV IN_CONTAINER=1 + +LABEL com.replicated.vendor_cli="true" + +WORKDIR /out + +COPY bin/replicated /replicated + +ENTRYPOINT ["/replicated"] diff --git a/scripts/generate-docs.sh b/scripts/generate-docs.sh index 666734aa7..a26573ebc 100755 --- a/scripts/generate-docs.sh +++ b/scripts/generate-docs.sh @@ -48,7 +48,7 @@ for f in gen/docs/*.md; do # Fix header level if [[ "$content" == "## "* ]]; then - content="#${content:1}" + content="${content:1}" fi # Replace internal links From 1ae041b2af554e6f9d2ecbb92c7d5790affd2d04 Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Mon, 18 May 2026 10:23:01 -0700 Subject: [PATCH 07/19] fix: install pact tools in depot workflows --- .depot/workflows/pr.yml | 9 ++++++++- .depot/workflows/release.yml | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.depot/workflows/pr.yml b/.depot/workflows/pr.yml index d22099c6f..6ae666761 100644 --- a/.depot/workflows/pr.yml +++ b/.depot/workflows/pr.yml @@ -63,7 +63,14 @@ jobs: go-version-file: ./go.mod cache: true cache-dependency-path: ./go.sum - - uses: replicatedhq/action-install-pact@v1 + - name: Install pact + run: | + curl -L -o /tmp/pact.tar.gz https://github.com/pact-foundation/pact-ruby-standalone/releases/download/v2.0.0/pact-2.0.0-linux-x86_64.tar.gz + echo "94ea52ef7d6c9dacf431dde7138717d96714131a /tmp/pact.tar.gz" | shasum -c - + mkdir -p /tmp/pact-standalone + tar -C /tmp/pact-standalone --strip-components=1 -xzf /tmp/pact.tar.gz + echo "/tmp/pact-standalone/bin" >> "$GITHUB_PATH" + rm /tmp/pact.tar.gz - name: Setup pact environment if: github.ref == 'refs/heads/main' run: | diff --git a/.depot/workflows/release.yml b/.depot/workflows/release.yml index 44b9e34aa..1a81bf53b 100644 --- a/.depot/workflows/release.yml +++ b/.depot/workflows/release.yml @@ -17,7 +17,14 @@ jobs: cache: true cache-dependency-path: ./go.sum - run: make test-unit - - uses: replicatedhq/action-install-pact@v1 + - name: Install pact + run: | + curl -L -o /tmp/pact.tar.gz https://github.com/pact-foundation/pact-ruby-standalone/releases/download/v2.0.0/pact-2.0.0-linux-x86_64.tar.gz + echo "94ea52ef7d6c9dacf431dde7138717d96714131a /tmp/pact.tar.gz" | shasum -c - + mkdir -p /tmp/pact-standalone + tar -C /tmp/pact-standalone --strip-components=1 -xzf /tmp/pact.tar.gz + echo "/tmp/pact-standalone/bin" >> "$GITHUB_PATH" + rm /tmp/pact.tar.gz - run: make test-pact - run: make build From 6fe114d217684137f09859adef03edd0a27c05cc Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Mon, 18 May 2026 10:24:26 -0700 Subject: [PATCH 08/19] fix: format go files --- cli/cmd/enterprise_portal_preview.go | 76 +++++++++++++-------------- cli/cmd/lint_types.go | 14 ++--- cli/cmd/release_create.go | 2 +- cli/cmd/release_create_test.go | 6 +-- pkg/integration/channel_test.go | 14 ++--- pkg/lint2/troubleshoot_common_test.go | 2 +- pkg/tools/config.go | 1 - pkg/tools/config_test.go | 4 +- pkg/types/channel.go | 36 ++++++------- 9 files changed, 77 insertions(+), 78 deletions(-) diff --git a/cli/cmd/enterprise_portal_preview.go b/cli/cmd/enterprise_portal_preview.go index 2a91ffc27..73a299a5d 100644 --- a/cli/cmd/enterprise_portal_preview.go +++ b/cli/cmd/enterprise_portal_preview.go @@ -241,48 +241,48 @@ func (r *runners) enterprisePortalPreview(cmd *cobra.Command, args []string) err // the /enterprise-portal/license envelope. Kept in sync with the LicenseResponse // struct in handlers/market-api/enterpriseportal/license.go. type previewLicense struct { - ID string `json:"id"` - AppID string `json:"appId"` - AppName string `json:"appName"` - AppIcon *string `json:"appIcon"` - Channels []previewLicenseChannel `json:"channels"` - CustomerID string `json:"customerId"` - CustomerName string `json:"customerName"` - CustomerEmail string `json:"customerEmail"` - CreatedAt string `json:"createdAt"` - UpdatedAt string `json:"updatedAt"` - ExpireAt *string `json:"expireAt"` - IsExpired bool `json:"isExpired"` - IsArchived bool `json:"isArchived"` - LicenseType string `json:"licenseType"` - Sequence int64 `json:"sequence"` - IsAirgapSupported bool `json:"isAirgapSupported"` - IsGitopsSupported bool `json:"isGitopsSupported"` - IsIdentityServiceSupported bool `json:"isIdentityServiceSupported"` - IsGeoaxisSupported bool `json:"isGeoaxisSupported"` - IsSnapshotSupported bool `json:"isSnapshotSupported"` - IsDisasterRecoverySupported bool `json:"isDisasterRecoverySupported"` - IsSupportBundleUploadSupported bool `json:"isSupportBundleUploadSupported"` - IsEmbeddedClusterDownloadEnabled bool `json:"isEmbeddedClusterDownloadEnabled"` - IsEmbeddedClusterMultiNodeEnabled bool `json:"isEmbeddedClusterMultiNodeEnabled"` - IsKotsInstallEnabled bool `json:"isKotsInstallEnabled"` - IsHelmInstallEnabled bool `json:"isHelmInstallEnabled"` - IsKurlInstallEnabled bool `json:"isKurlInstallEnabled"` - IsHelmAirgapEnabled bool `json:"isHelmAirgapEnabled"` + ID string `json:"id"` + AppID string `json:"appId"` + AppName string `json:"appName"` + AppIcon *string `json:"appIcon"` + Channels []previewLicenseChannel `json:"channels"` + CustomerID string `json:"customerId"` + CustomerName string `json:"customerName"` + CustomerEmail string `json:"customerEmail"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` + ExpireAt *string `json:"expireAt"` + IsExpired bool `json:"isExpired"` + IsArchived bool `json:"isArchived"` + LicenseType string `json:"licenseType"` + Sequence int64 `json:"sequence"` + IsAirgapSupported bool `json:"isAirgapSupported"` + IsGitopsSupported bool `json:"isGitopsSupported"` + IsIdentityServiceSupported bool `json:"isIdentityServiceSupported"` + IsGeoaxisSupported bool `json:"isGeoaxisSupported"` + IsSnapshotSupported bool `json:"isSnapshotSupported"` + IsDisasterRecoverySupported bool `json:"isDisasterRecoverySupported"` + IsSupportBundleUploadSupported bool `json:"isSupportBundleUploadSupported"` + IsEmbeddedClusterDownloadEnabled bool `json:"isEmbeddedClusterDownloadEnabled"` + IsEmbeddedClusterMultiNodeEnabled bool `json:"isEmbeddedClusterMultiNodeEnabled"` + IsKotsInstallEnabled bool `json:"isKotsInstallEnabled"` + IsHelmInstallEnabled bool `json:"isHelmInstallEnabled"` + IsKurlInstallEnabled bool `json:"isKurlInstallEnabled"` + IsHelmAirgapEnabled bool `json:"isHelmAirgapEnabled"` EntitlementFields []previewEntitlementField `json:"entitlementFields"` EntitlementValues []previewEntitlementValue `json:"entitlementValues"` } type previewLicenseChannel struct { - AppID string `json:"appId"` - LicenseID string `json:"licenseId"` - ChannelID string `json:"channelId"` - ChannelName string `json:"channelName"` - ChannelSlug string `json:"channelSlug"` - IsSemverRequired int `json:"isSemverRequired"` - IsDefault int `json:"isDefault"` - PinnedChannelSequence *int64 `json:"pinnedChannelSequence"` - HasKurlInstaller bool `json:"hasKurlInstaller"` + AppID string `json:"appId"` + LicenseID string `json:"licenseId"` + ChannelID string `json:"channelId"` + ChannelName string `json:"channelName"` + ChannelSlug string `json:"channelSlug"` + IsSemverRequired int `json:"isSemverRequired"` + IsDefault int `json:"isDefault"` + PinnedChannelSequence *int64 `json:"pinnedChannelSequence"` + HasKurlInstaller bool `json:"hasKurlInstaller"` } type previewEntitlementField struct { @@ -418,7 +418,7 @@ func portBusy(p int) bool { // escapeComposeScalar escapes a string for use as a YAML single-quoted scalar // inside a docker-compose file. Two layers: -// - YAML single-quote: ' → '' +// - YAML single-quote: ' → ” // - docker-compose variable interpolation: $ → $$. Compose interpolates $vars // over the whole file before YAML parsing, single-quoted strings included, // so a path like /Users/ca$h/repo would otherwise have "$h" treated as a diff --git a/cli/cmd/lint_types.go b/cli/cmd/lint_types.go index a7227ddaa..b9262a77d 100644 --- a/cli/cmd/lint_types.go +++ b/cli/cmd/lint_types.go @@ -9,13 +9,13 @@ import ( // JSONLintOutput represents the complete JSON output structure for lint results type JSONLintOutput struct { - Metadata LintMetadata `json:"metadata"` - HelmResults *HelmLintResults `json:"helm_results,omitempty"` - PreflightResults *PreflightLintResults `json:"preflight_results,omitempty"` - SupportBundleResults *SupportBundleLintResults `json:"support_bundle_results,omitempty"` - EmbeddedClusterResults *EmbeddedClusterLintResults `json:"embedded_cluster_results,omitempty"` - Summary LintSummary `json:"summary"` - Images *ImageExtractResults `json:"images,omitempty"` // Only if --verbose + Metadata LintMetadata `json:"metadata"` + HelmResults *HelmLintResults `json:"helm_results,omitempty"` + PreflightResults *PreflightLintResults `json:"preflight_results,omitempty"` + SupportBundleResults *SupportBundleLintResults `json:"support_bundle_results,omitempty"` + EmbeddedClusterResults *EmbeddedClusterLintResults `json:"embedded_cluster_results,omitempty"` + Summary LintSummary `json:"summary"` + Images *ImageExtractResults `json:"images,omitempty"` // Only if --verbose } // LintMetadata contains execution context and environment information diff --git a/cli/cmd/release_create.go b/cli/cmd/release_create.go index d60bf9721..5a5e8a3e3 100644 --- a/cli/cmd/release_create.go +++ b/cli/cmd/release_create.go @@ -42,7 +42,7 @@ If no flags are provided, the command will automatically use the configuration f .replicated file in the current directory (or parent directories). The config should specify charts and manifests to include. Charts will be automatically packaged using helm, and manifests will be collected using glob patterns.`, - Example: `# .replicated config: + Example: `# .replicated config: appSlug: "my-app" charts: - path: ./chart diff --git a/cli/cmd/release_create_test.go b/cli/cmd/release_create_test.go index 94c444981..4cc18766b 100644 --- a/cli/cmd/release_create_test.go +++ b/cli/cmd/release_create_test.go @@ -125,9 +125,9 @@ func TestSetKOTSDefaultReleaseParams_WithExistingYamlDir(t *testing.T) { // TestSetKOTSDefaultReleaseParams_PromoteMapsMainToUnstable tests branch name mapping func TestSetKOTSDefaultReleaseParams_PromoteMapsMainToUnstable(t *testing.T) { tests := []struct { - name string - branch string - wantPromote string + name string + branch string + wantPromote string }{ { name: "main branch maps to Unstable", diff --git a/pkg/integration/channel_test.go b/pkg/integration/channel_test.go index 8e0853b88..bd5bcea9d 100644 --- a/pkg/integration/channel_test.go +++ b/pkg/integration/channel_test.go @@ -22,10 +22,10 @@ func TestChannelReleases(t *testing.T) { releases interface{} wantFormat format wantLines int - wantOutput string // when set, asserted exactly - wantContain []string // substrings the output must contain - wantQuery map[string]string // query params asserted on the releases request - wantExit int // expected non-zero exit; 0 means must succeed + wantOutput string // when set, asserted exactly + wantContain []string // substrings the output must contain + wantQuery map[string]string // query params asserted on the releases request + wantExit int // expected non-zero exit; 0 means must succeed assertJSON func(t *testing.T, raw []byte) }{ { @@ -117,9 +117,9 @@ func TestChannelReleases(t *testing.T) { channels: []map[string]interface{}{ {"id": "chan-1", "name": "Stable", "channelSlug": "stable"}, }, - releases: []map[string]interface{}{}, - wantFormat: FormatTable, - wantOutput: "No releases in channel\n", + releases: []map[string]interface{}{}, + wantFormat: FormatTable, + wantOutput: "No releases in channel\n", }, { name: "platform channel releases table — legacy app regression guard", diff --git a/pkg/lint2/troubleshoot_common_test.go b/pkg/lint2/troubleshoot_common_test.go index a4b637969..0845263b6 100644 --- a/pkg/lint2/troubleshoot_common_test.go +++ b/pkg/lint2/troubleshoot_common_test.go @@ -233,7 +233,7 @@ func TestConvertTroubleshootResultToMessages_SupportBundle(t *testing.T) { {Line: 8, Message: "Missing collectors", Field: "spec.collectors"}, }, Warnings: []SupportBundleLintIssue{}, - Info: []SupportBundleLintIssue{}, + Info: []SupportBundleLintIssue{}, }, }, } diff --git a/pkg/tools/config.go b/pkg/tools/config.go index 3ee0c7f4e..81f10cec3 100644 --- a/pkg/tools/config.go +++ b/pkg/tools/config.go @@ -467,7 +467,6 @@ func mergeECLinterConfig(parent, child ECLinterConfig) ECLinterConfig { return result } - // boolPtr returns a pointer to a boolean value // Helper for creating pointer booleans in config defaults func boolPtr(b bool) *bool { diff --git a/pkg/tools/config_test.go b/pkg/tools/config_test.go index 72f45a8d2..96b462141 100644 --- a/pkg/tools/config_test.go +++ b/pkg/tools/config_test.go @@ -1495,8 +1495,8 @@ func TestApplyDefaults_LinterDefaults(t *testing.T) { config := &Config{ ReplLint: &ReplLintConfig{ Linters: LintersConfig{ - Helm: LinterConfig{Disabled: &trueVal}, // user disabled helm - Kots: LinterConfig{Disabled: &falseVal}, // user enabled kots + Helm: LinterConfig{Disabled: &trueVal}, // user disabled helm + Kots: LinterConfig{Disabled: &falseVal}, // user enabled kots EmbeddedCluster: ECLinterConfig{LinterConfig: LinterConfig{Disabled: &falseVal}}, // user enabled EC }, }, diff --git a/pkg/types/channel.go b/pkg/types/channel.go index cbe95ea55..e73509b47 100644 --- a/pkg/types/channel.go +++ b/pkg/types/channel.go @@ -63,27 +63,27 @@ type CustomerAdoption struct { } type ChannelRelease struct { - AirgapBuildError string `json:"airgapBuildError,omitempty"` - AirgapBuildStatus string `json:"airgapBuildStatus,omitempty"` - AirgapBundleImages []string `json:"airgapBundleImages,omitempty"` - ChannelIcon string `json:"channelIcon,omitempty"` - ChannelId string `json:"channelId,omitempty"` - ChannelName string `json:"channelName,omitempty"` - ChannelSequence int32 `json:"channelSequence,omitempty"` - Created time.Time `json:"created,omitempty"` - ProxyRegistryDomain string `json:"proxyRegistryDomain,omitempty"` - RegistrySecret string `json:"registrySecret,omitempty"` - ReleaseNotes string `json:"releaseNotes,omitempty"` - ReleasedAt time.Time `json:"releasedAt,omitempty"` - Semver string `json:"semver,omitempty"` - Sequence int32 `json:"sequence,omitempty"` - Updated time.Time `json:"updated,omitempty"` + AirgapBuildError string `json:"airgapBuildError,omitempty"` + AirgapBuildStatus string `json:"airgapBuildStatus,omitempty"` + AirgapBundleImages []string `json:"airgapBundleImages,omitempty"` + ChannelIcon string `json:"channelIcon,omitempty"` + ChannelId string `json:"channelId,omitempty"` + ChannelName string `json:"channelName,omitempty"` + ChannelSequence int32 `json:"channelSequence,omitempty"` + Created time.Time `json:"created,omitempty"` + ProxyRegistryDomain string `json:"proxyRegistryDomain,omitempty"` + RegistrySecret string `json:"registrySecret,omitempty"` + ReleaseNotes string `json:"releaseNotes,omitempty"` + ReleasedAt time.Time `json:"releasedAt,omitempty"` + Semver string `json:"semver,omitempty"` + Sequence int32 `json:"sequence,omitempty"` + Updated time.Time `json:"updated,omitempty"` // IsDemoted and DemotedAt intentionally omit `omitempty`: agents consuming // the JSON need to distinguish "explicitly not demoted" from "field absent", // and Go's omitempty on a bool would drop `false`. - IsDemoted bool `json:"isDemoted"` - DemotedAt *time.Time `json:"demotedAt"` - InstallationTypes InstallationTypes `json:"installationTypes,omitempty"` + IsDemoted bool `json:"isDemoted"` + DemotedAt *time.Time `json:"demotedAt"` + InstallationTypes InstallationTypes `json:"installationTypes,omitempty"` } type CreateChannelRequest struct { From 84755639a126fd5060911b669c8dc7cccb159927 Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Mon, 18 May 2026 10:27:52 -0700 Subject: [PATCH 09/19] fix: wire depot ci secrets --- .depot/workflows/pr.yml | 2 ++ .depot/workflows/release.yml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.depot/workflows/pr.yml b/.depot/workflows/pr.yml index 6ae666761..0fbab55b9 100644 --- a/.depot/workflows/pr.yml +++ b/.depot/workflows/pr.yml @@ -39,6 +39,8 @@ jobs: - name: Build binary (needed for lint tests) run: make build - name: Run lint tests + env: + REPLICATED_API_TOKEN: ${{ secrets.REPLICATED_API_TOKEN }} run: make test-lint unit-tests: diff --git a/.depot/workflows/release.yml b/.depot/workflows/release.yml index 1a81bf53b..46e7c5c17 100644 --- a/.depot/workflows/release.yml +++ b/.depot/workflows/release.yml @@ -74,7 +74,7 @@ jobs: - name: Login to Docker Hub uses: docker/login-action@v3 with: - username: ${{ secrets.DOCKERHUB_USERNAME }} + username: ${{ secrets.DOCKERHUB_USER }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - name: Push images run: | @@ -97,5 +97,5 @@ jobs: run: make build - name: Generate and publish docs env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.DOCS_REPO_TOKEN }} run: ./scripts/generate-docs.sh From 153314256b0b9361e49ea27f4e453a1516ed6028 Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Mon, 18 May 2026 10:32:38 -0700 Subject: [PATCH 10/19] fix: add lint test helmchart manifests --- testdata/chart-with-lint-errors/.replicated | 3 ++- .../chart-with-lint-errors/manifests/helmchart.yaml | 10 ++++++++++ testdata/chart-with-required-values/.replicated | 2 ++ .../manifests/helmchart.yaml | 10 ++++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 testdata/chart-with-lint-errors/manifests/helmchart.yaml create mode 100644 testdata/chart-with-required-values/manifests/helmchart.yaml diff --git a/testdata/chart-with-lint-errors/.replicated b/testdata/chart-with-lint-errors/.replicated index ae1cd37b6..6b380ad3f 100644 --- a/testdata/chart-with-lint-errors/.replicated +++ b/testdata/chart-with-lint-errors/.replicated @@ -4,6 +4,8 @@ charts: [ path: "./charts/broken-chart" }, ] +manifests: + - "./manifests/*.yaml" repl-lint: version: 1 linters: @@ -13,4 +15,3 @@ repl-lint: disabled: false support-bundle: disabled: true - diff --git a/testdata/chart-with-lint-errors/manifests/helmchart.yaml b/testdata/chart-with-lint-errors/manifests/helmchart.yaml new file mode 100644 index 000000000..7c6988a16 --- /dev/null +++ b/testdata/chart-with-lint-errors/manifests/helmchart.yaml @@ -0,0 +1,10 @@ +apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: broken-chart +spec: + chart: + name: broken-chart + chartVersion: 1.0.0 + weight: 1 + builder: {} diff --git a/testdata/chart-with-required-values/.replicated b/testdata/chart-with-required-values/.replicated index 3a97a7a8e..dc3b97770 100644 --- a/testdata/chart-with-required-values/.replicated +++ b/testdata/chart-with-required-values/.replicated @@ -4,6 +4,8 @@ charts: [ path: "./charts/required-values" }, ] +manifests: + - "./manifests/*.yaml" repl-lint: version: 1 linters: diff --git a/testdata/chart-with-required-values/manifests/helmchart.yaml b/testdata/chart-with-required-values/manifests/helmchart.yaml new file mode 100644 index 000000000..e2cee0887 --- /dev/null +++ b/testdata/chart-with-required-values/manifests/helmchart.yaml @@ -0,0 +1,10 @@ +apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: required-values +spec: + chart: + name: required-values + chartVersion: 0.1.0 + weight: 1 + builder: {} From a64aa7fc06f382fbef7098f7475140fe36c8e06a Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Mon, 18 May 2026 10:48:19 -0700 Subject: [PATCH 11/19] increase timeout --- .depot/workflows/pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.depot/workflows/pr.yml b/.depot/workflows/pr.yml index 0fbab55b9..78f26a582 100644 --- a/.depot/workflows/pr.yml +++ b/.depot/workflows/pr.yml @@ -113,7 +113,7 @@ jobs: goreleaser-dryrun: runs-on: ubuntu-latest - timeout-minutes: 10 + timeout-minutes: 30 steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 From a3e3e45be40c546b03caaae95c866187c3fc2b95 Mon Sep 17 00:00:00 2001 From: Replicated Release Pipeline Date: Mon, 18 May 2026 17:52:53 +0000 Subject: [PATCH 12/19] fix: resolve bugbot comments - Fix concurrency group for push events (github.head_ref || github.ref) - Fix docs link replacement to strip .md extension - Fix Docker build to inject version via LDFLAGS --- .depot/workflows/pr.yml | 2 +- .depot/workflows/release.yml | 7 +- PLAN-depot-ci-migration.md | 120 +++++++++++++++++++++++++++++++++++ scripts/generate-docs.sh | 2 +- 4 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 PLAN-depot-ci-migration.md diff --git a/.depot/workflows/pr.yml b/.depot/workflows/pr.yml index 78f26a582..eed13c5a4 100644 --- a/.depot/workflows/pr.yml +++ b/.depot/workflows/pr.yml @@ -8,7 +8,7 @@ on: name: "depot: PR" concurrency: - group: depot-pr-${{ github.head_ref }} + group: depot-pr-${{ github.head_ref || github.ref }} cancel-in-progress: true jobs: diff --git a/.depot/workflows/release.yml b/.depot/workflows/release.yml index 46e7c5c17..edb392a73 100644 --- a/.depot/workflows/release.yml +++ b/.depot/workflows/release.yml @@ -57,8 +57,11 @@ jobs: go-version-file: ./go.mod cache: true cache-dependency-path: ./go.sum - - name: Build binary - run: make build + - name: Build binary with version + run: | + VERSION=${GITHUB_REF_NAME#v} + LDFLAGS="-X github.com/replicatedhq/replicated/pkg/version.version=${VERSION}" + make build LDFLAGS="${LDFLAGS}" - name: Build Docker image run: | VERSION=${GITHUB_REF_NAME#v} diff --git a/PLAN-depot-ci-migration.md b/PLAN-depot-ci-migration.md new file mode 100644 index 000000000..78aaf728d --- /dev/null +++ b/PLAN-depot-ci-migration.md @@ -0,0 +1,120 @@ +# Plan: Migrate replicatedhq/replicated to Depot CI + +## Goal +Move completely off GitHub Actions and Dagger. All CI runs on Depot. All release automation runs on Depot. + +## Current State + +### `.github/workflows/main.yaml` — 6 jobs +| Job | What it does | +|-----|-------------| +| `make-unit-tests` | `go test` (excludes `/pact`, `/pkg/integration`) | +| `make-pact-tests` | `go test ./pact/...` + pact-broker publish + can-i-deploy (main only) | +| `make-integration-tests` | `go test ./pkg/integration/...` | +| `make-build` | `go build` the CLI binary | +| `dagger-build` | `make dagger-build` — builds binary via Dagger container | + +### Dagger (`dagger/` + `dagger.json`) +| Function | Purpose | +|----------|---------| +| `Build` | Compile binary in container | +| `Release` | Full release: version bump → git tag → Docker image publish → goreleaser → docs PR | +| `GoreleaserDryrun` | Snapshot build verification (used in CI) | +| `GenerateDocs` | Generate CLI docs, open PR against `replicatedhq/replicated-docs` | +| `Validate` | Semgrep security scan + unit tests (no-op compat/perf) | + +### Makefile targets to keep +- `test-unit`, `test-pact`, `test-integration`, `test-lint`, `build` +- `publish-pact`, `can-i-deploy`, `unpublish-past-versions` + +### Makefile targets to remove +- `dagger-build`, `dagger-goreleaser-dryrun`, `release`, `docs` + +--- + +## Proposed Changes (Single PR) + +### 1. Create `.depot/workflows/pr.yml` + +**Trigger:** `pull_request` on `main` (opened, synchronize, reopened, ready_for_review) + +**Jobs:** + +| Job | Purpose | Time | Depot Cost | +|-----|---------|------|------------| +| `lint` | go mod tidy check, go fmt check, `make build`, `make test-lint` | ~2 min | ~$0.01 | +| `unit-tests` | `make test-unit` | ~3 min | ~$0.02 | +| `pact-tests` | `make test-pact` + pact publish + can-i-deploy (main only) | ~3 min | ~$0.02 | +| `integration-tests` | `make build` + `make test-integration` | ~3 min | ~$0.02 | +| `build` | `make build` (verifies binary compiles) | ~1 min | ~$0.01 | +| `goreleaser-dryrun` | `goreleaser release --snapshot --clean` | ~2 min | ~$0.01 | +| `gate` | Aggregates all job results, fails if any failed | <1 min | ~$0.00 | + +**Outcome:** All PRs get a single required status check (`gate`). No path filters needed (single Go project). No matrix. Fast feedback — lint runs first, everything else in parallel. + +**Total PR wall clock:** ~3 min (parallel), dominated by unit tests + pact tests. +**Total PR cost:** ~$0.10. + +--- + +### 2. Create `.depot/workflows/release.yml` + +**Trigger:** `push` of tags matching `v*` (e.g. `v0.130.0`) + +**Jobs:** + +| Job | Purpose | Time | Depot Cost | +|-----|---------|------|------------| +| `build-and-test` | `make test-unit`, `make test-pact`, `make build` | ~5 min | ~$0.03 | +| `release` | goreleaser proper release (GitHub release + binaries + Homebrew) | ~3 min | ~$0.02 | +| `docker-publish` | Build `replicated/vendor-cli` image, tag latest/major/minor/patch, push to Docker Hub | ~3 min | ~$0.02 | +| `docs` | Generate CLI docs, clone `replicated-docs`, update sidebars, commit to branch, open PR | ~2 min | ~$0.01 | + +**Outcome:** Tag push → binaries on GitHub, Docker images on Hub, docs PR opened automatically. No manual `make release`. + +**Total release wall clock:** ~5 min (build-and-test blocks release/docker/docs, which can run in parallel after). +**Total release cost:** ~$0.08. + +--- + +### 3. Delete Dagger +- Remove `dagger/` directory entirely +- Remove `dagger.json` +- Remove from `.github/dependabot.yml` if present + +--- + +### 4. Delete `.github/workflows/main.yaml` +- Remove entirely (or disable triggers as transition) + +--- + +### 5. Update Makefile +- Remove: `dagger-build`, `dagger-goreleaser-dryrun`, `release`, `docs` +- No new make target needed — `make build` already does what `dagger-goreleaser-dryrun` verified (binary compiles). The `goreleaser-dryrun` job in PR CI runs `goreleaser release --snapshot --clean` directly, not via make. + +--- + +## Open Questions + +1. ~~Release trigger~~: **Resolved** — push a tag to release. No `workflow_dispatch`, no manual `make release`. + +2. ~~Docker image publishing~~: **Resolved** — separate `docker-publish` job with manual `docker build`/`docker push`. Simpler and faster than goreleaser Docker support. Build from `Dockerfile`, tag latest/major/minor/patch, push to Docker Hub. + +3. ~~Docs generation~~: **Resolved** — port to `scripts/generate-docs.sh`. The release workflow calls this script after goreleaser succeeds. Script handles: clone `replicated-docs`, clean old CLI docs, copy generated, update `sidebars.js`, commit to branch, push, open PR via GitHub API. + +4. ~~Pact can-i-deploy~~: **Resolved** — keep in PR validation. Run on every PR (not just main). + +5. ~~Semgrep~~: **Resolved** — keep in PR CI. Add a `security` job that runs `semgrep scan --config=p/golang .` directly (not via Dagger). + +6. ~~Secrets for Depot~~: **Resolved** — secrets ported from GitHub Actions to Depot with same names and values. No 1Password integration needed. + +--- + +## Why This Fits in One PR + +- No path filters needed (single Go project) +- No matrix of integration suites (3 test targets only) +- No frontend, no migrations, no multi-service builds +- Dagger removal is mechanical (delete files, delete Makefile targets) +- Release workflow is straightforward: test → goreleaser → Docker push → docs diff --git a/scripts/generate-docs.sh b/scripts/generate-docs.sh index a26573ebc..caec9f01a 100755 --- a/scripts/generate-docs.sh +++ b/scripts/generate-docs.sh @@ -55,7 +55,7 @@ for f in gen/docs/*.md; do for ref in gen/docs/*.md; do ref_name=$(basename "$ref" .md) dest_ref=$(echo "$ref_name" | sed 's/replicated_/replicated-cli-/g; s/_/-/g') - content="${content//${ref_name}/${dest_ref}}" + content="${content//${ref_name}.md/${dest_ref}}" done echo "$content" > "${DOCS_DIR}/docs/reference/${dest_name}" From ec41d986baa1fae79df4360f3afb0d9c073574cf Mon Sep 17 00:00:00 2001 From: Replicated Release Pipeline Date: Mon, 18 May 2026 17:53:33 +0000 Subject: [PATCH 13/19] chore: remove local planning doc from branch --- PLAN-depot-ci-migration.md | 120 ------------------------------------- 1 file changed, 120 deletions(-) delete mode 100644 PLAN-depot-ci-migration.md diff --git a/PLAN-depot-ci-migration.md b/PLAN-depot-ci-migration.md deleted file mode 100644 index 78aaf728d..000000000 --- a/PLAN-depot-ci-migration.md +++ /dev/null @@ -1,120 +0,0 @@ -# Plan: Migrate replicatedhq/replicated to Depot CI - -## Goal -Move completely off GitHub Actions and Dagger. All CI runs on Depot. All release automation runs on Depot. - -## Current State - -### `.github/workflows/main.yaml` — 6 jobs -| Job | What it does | -|-----|-------------| -| `make-unit-tests` | `go test` (excludes `/pact`, `/pkg/integration`) | -| `make-pact-tests` | `go test ./pact/...` + pact-broker publish + can-i-deploy (main only) | -| `make-integration-tests` | `go test ./pkg/integration/...` | -| `make-build` | `go build` the CLI binary | -| `dagger-build` | `make dagger-build` — builds binary via Dagger container | - -### Dagger (`dagger/` + `dagger.json`) -| Function | Purpose | -|----------|---------| -| `Build` | Compile binary in container | -| `Release` | Full release: version bump → git tag → Docker image publish → goreleaser → docs PR | -| `GoreleaserDryrun` | Snapshot build verification (used in CI) | -| `GenerateDocs` | Generate CLI docs, open PR against `replicatedhq/replicated-docs` | -| `Validate` | Semgrep security scan + unit tests (no-op compat/perf) | - -### Makefile targets to keep -- `test-unit`, `test-pact`, `test-integration`, `test-lint`, `build` -- `publish-pact`, `can-i-deploy`, `unpublish-past-versions` - -### Makefile targets to remove -- `dagger-build`, `dagger-goreleaser-dryrun`, `release`, `docs` - ---- - -## Proposed Changes (Single PR) - -### 1. Create `.depot/workflows/pr.yml` - -**Trigger:** `pull_request` on `main` (opened, synchronize, reopened, ready_for_review) - -**Jobs:** - -| Job | Purpose | Time | Depot Cost | -|-----|---------|------|------------| -| `lint` | go mod tidy check, go fmt check, `make build`, `make test-lint` | ~2 min | ~$0.01 | -| `unit-tests` | `make test-unit` | ~3 min | ~$0.02 | -| `pact-tests` | `make test-pact` + pact publish + can-i-deploy (main only) | ~3 min | ~$0.02 | -| `integration-tests` | `make build` + `make test-integration` | ~3 min | ~$0.02 | -| `build` | `make build` (verifies binary compiles) | ~1 min | ~$0.01 | -| `goreleaser-dryrun` | `goreleaser release --snapshot --clean` | ~2 min | ~$0.01 | -| `gate` | Aggregates all job results, fails if any failed | <1 min | ~$0.00 | - -**Outcome:** All PRs get a single required status check (`gate`). No path filters needed (single Go project). No matrix. Fast feedback — lint runs first, everything else in parallel. - -**Total PR wall clock:** ~3 min (parallel), dominated by unit tests + pact tests. -**Total PR cost:** ~$0.10. - ---- - -### 2. Create `.depot/workflows/release.yml` - -**Trigger:** `push` of tags matching `v*` (e.g. `v0.130.0`) - -**Jobs:** - -| Job | Purpose | Time | Depot Cost | -|-----|---------|------|------------| -| `build-and-test` | `make test-unit`, `make test-pact`, `make build` | ~5 min | ~$0.03 | -| `release` | goreleaser proper release (GitHub release + binaries + Homebrew) | ~3 min | ~$0.02 | -| `docker-publish` | Build `replicated/vendor-cli` image, tag latest/major/minor/patch, push to Docker Hub | ~3 min | ~$0.02 | -| `docs` | Generate CLI docs, clone `replicated-docs`, update sidebars, commit to branch, open PR | ~2 min | ~$0.01 | - -**Outcome:** Tag push → binaries on GitHub, Docker images on Hub, docs PR opened automatically. No manual `make release`. - -**Total release wall clock:** ~5 min (build-and-test blocks release/docker/docs, which can run in parallel after). -**Total release cost:** ~$0.08. - ---- - -### 3. Delete Dagger -- Remove `dagger/` directory entirely -- Remove `dagger.json` -- Remove from `.github/dependabot.yml` if present - ---- - -### 4. Delete `.github/workflows/main.yaml` -- Remove entirely (or disable triggers as transition) - ---- - -### 5. Update Makefile -- Remove: `dagger-build`, `dagger-goreleaser-dryrun`, `release`, `docs` -- No new make target needed — `make build` already does what `dagger-goreleaser-dryrun` verified (binary compiles). The `goreleaser-dryrun` job in PR CI runs `goreleaser release --snapshot --clean` directly, not via make. - ---- - -## Open Questions - -1. ~~Release trigger~~: **Resolved** — push a tag to release. No `workflow_dispatch`, no manual `make release`. - -2. ~~Docker image publishing~~: **Resolved** — separate `docker-publish` job with manual `docker build`/`docker push`. Simpler and faster than goreleaser Docker support. Build from `Dockerfile`, tag latest/major/minor/patch, push to Docker Hub. - -3. ~~Docs generation~~: **Resolved** — port to `scripts/generate-docs.sh`. The release workflow calls this script after goreleaser succeeds. Script handles: clone `replicated-docs`, clean old CLI docs, copy generated, update `sidebars.js`, commit to branch, push, open PR via GitHub API. - -4. ~~Pact can-i-deploy~~: **Resolved** — keep in PR validation. Run on every PR (not just main). - -5. ~~Semgrep~~: **Resolved** — keep in PR CI. Add a `security` job that runs `semgrep scan --config=p/golang .` directly (not via Dagger). - -6. ~~Secrets for Depot~~: **Resolved** — secrets ported from GitHub Actions to Depot with same names and values. No 1Password integration needed. - ---- - -## Why This Fits in One PR - -- No path filters needed (single Go project) -- No matrix of integration suites (3 test targets only) -- No frontend, no migrations, no multi-service builds -- Dagger removal is mechanical (delete files, delete Makefile targets) -- Release workflow is straightforward: test → goreleaser → Docker push → docs From 0031c0c4000f6bad3c2317651f34084c2a1e7c61 Mon Sep 17 00:00:00 2001 From: Replicated Release Pipeline Date: Mon, 18 May 2026 17:58:01 +0000 Subject: [PATCH 14/19] fix: speed up goreleaser - PR dryrun: use --single-target (linux/amd64 only) + 5min timeout - Release: add --parallelism 4 --- .depot/workflows/pr.yml | 8 ++++++-- .depot/workflows/release.yml | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.depot/workflows/pr.yml b/.depot/workflows/pr.yml index eed13c5a4..863716531 100644 --- a/.depot/workflows/pr.yml +++ b/.depot/workflows/pr.yml @@ -113,7 +113,7 @@ jobs: goreleaser-dryrun: runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 @@ -123,7 +123,11 @@ jobs: cache-dependency-path: ./go.sum - name: Install goreleaser run: go install github.com/goreleaser/goreleaser/v2@v2.14.3 - - run: goreleaser release --snapshot --clean + - name: Run goreleaser (linux-only snapshot) + env: + GGOOS: linux + GGOARCH: amd64 + run: goreleaser release --snapshot --clean --single-target security: runs-on: ubuntu-latest diff --git a/.depot/workflows/release.yml b/.depot/workflows/release.yml index edb392a73..473113577 100644 --- a/.depot/workflows/release.yml +++ b/.depot/workflows/release.yml @@ -45,7 +45,7 @@ jobs: - name: Run goreleaser env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: goreleaser release --clean + run: goreleaser release --clean --parallelism 4 docker-publish: needs: build-and-test From e4ff73be4a6a310d24f440e83ed75fa4fc66aef5 Mon Sep 17 00:00:00 2001 From: Replicated Release Pipeline Date: Mon, 18 May 2026 18:02:34 +0000 Subject: [PATCH 15/19] fix: correct GOOS/GOARCH env vars for goreleaser single-target; remove parallelism flag from release --- .depot/workflows/pr.yml | 4 ++-- .depot/workflows/release.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.depot/workflows/pr.yml b/.depot/workflows/pr.yml index 863716531..20d2879c7 100644 --- a/.depot/workflows/pr.yml +++ b/.depot/workflows/pr.yml @@ -125,8 +125,8 @@ jobs: run: go install github.com/goreleaser/goreleaser/v2@v2.14.3 - name: Run goreleaser (linux-only snapshot) env: - GGOOS: linux - GGOARCH: amd64 + GOOS: linux + GOARCH: amd64 run: goreleaser release --snapshot --clean --single-target security: diff --git a/.depot/workflows/release.yml b/.depot/workflows/release.yml index 473113577..edb392a73 100644 --- a/.depot/workflows/release.yml +++ b/.depot/workflows/release.yml @@ -45,7 +45,7 @@ jobs: - name: Run goreleaser env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: goreleaser release --clean --parallelism 4 + run: goreleaser release --clean docker-publish: needs: build-and-test From 6bdf99ede31b9d3fe59c6f881b86d22e46db1ea7 Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Mon, 18 May 2026 12:35:27 -0700 Subject: [PATCH 16/19] remove dryrun --- .depot/workflows/pr.yml | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/.depot/workflows/pr.yml b/.depot/workflows/pr.yml index 20d2879c7..75aefb5f8 100644 --- a/.depot/workflows/pr.yml +++ b/.depot/workflows/pr.yml @@ -111,24 +111,6 @@ jobs: cache-dependency-path: ./go.sum - run: make build - goreleaser-dryrun: - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version-file: ./go.mod - cache: true - cache-dependency-path: ./go.sum - - name: Install goreleaser - run: go install github.com/goreleaser/goreleaser/v2@v2.14.3 - - name: Run goreleaser (linux-only snapshot) - env: - GOOS: linux - GOARCH: amd64 - run: goreleaser release --snapshot --clean --single-target - security: runs-on: ubuntu-latest timeout-minutes: 10 @@ -140,7 +122,7 @@ jobs: gate: if: always() - needs: [lint, unit-tests, pact-tests, integration-tests, build, goreleaser-dryrun, security] + needs: [lint, unit-tests, pact-tests, integration-tests, build, security] runs-on: ubuntu-latest steps: - name: Check all results @@ -151,7 +133,6 @@ jobs: "${{ needs.pact-tests.result }}" "${{ needs.integration-tests.result }}" "${{ needs.build.result }}" - "${{ needs.goreleaser-dryrun.result }}" "${{ needs.security.result }}" ) for r in "${results[@]}"; do From cd83ade559d6db80832b26c90b499cc5ce255f4b Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Mon, 18 May 2026 13:04:59 -0700 Subject: [PATCH 17/19] fix: wrap LDFLAGS correctly and include root CLI doc in sidebar Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .depot/workflows/release.yml | 2 +- scripts/generate-docs.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.depot/workflows/release.yml b/.depot/workflows/release.yml index edb392a73..f526e7e74 100644 --- a/.depot/workflows/release.yml +++ b/.depot/workflows/release.yml @@ -60,7 +60,7 @@ jobs: - name: Build binary with version run: | VERSION=${GITHUB_REF_NAME#v} - LDFLAGS="-X github.com/replicatedhq/replicated/pkg/version.version=${VERSION}" + LDFLAGS="-ldflags \"-X github.com/replicatedhq/replicated/pkg/version.version=${VERSION}\"" make build LDFLAGS="${LDFLAGS}" - name: Build Docker image run: | diff --git a/scripts/generate-docs.sh b/scripts/generate-docs.sh index caec9f01a..758c3488c 100755 --- a/scripts/generate-docs.sh +++ b/scripts/generate-docs.sh @@ -70,7 +70,7 @@ let sidebar = fs.readFileSync('sidebars.js', 'utf8'); // Find the Replicated CLI section and replace its items const cliItems = ['reference/replicated-cli-installing']; const files = fs.readdirSync('docs/reference') - .filter(f => f.startsWith('replicated-cli-') && f !== 'replicated-cli-installing.mdx') + .filter(f => (f === 'replicated.mdx' || f.startsWith('replicated-cli-')) && f !== 'replicated-cli-installing.mdx') .map(f => 'reference/' + f.replace('.mdx', '')) .sort(); cliItems.push(...files); From 1d023a635756e6c03abcd4ffff37345748960dbd Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Mon, 18 May 2026 13:15:43 -0700 Subject: [PATCH 18/19] fix: change version from const to var so -X ldflags can override it Co-Authored-By: Claude Sonnet 4.6 (1M context) --- pkg/version/build.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/version/build.go b/pkg/version/build.go index 9865d720a..c5c65c7d7 100644 --- a/pkg/version/build.go +++ b/pkg/version/build.go @@ -1,3 +1,3 @@ package version -const version = "unknown" +var version = "unknown" From bd1320ebe3ddcb1e16e69b3e160e85662bf8a7be Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Mon, 18 May 2026 13:16:08 -0700 Subject: [PATCH 19/19] fix: use -f in curl so docs PR creation fails on HTTP errors Co-Authored-By: Claude Sonnet 4.6 (1M context) --- scripts/generate-docs.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/generate-docs.sh b/scripts/generate-docs.sh index 758c3488c..3d7a2bd53 100755 --- a/scripts/generate-docs.sh +++ b/scripts/generate-docs.sh @@ -90,7 +90,7 @@ git commit -m "Update Replicated CLI docs for ${VERSION}" git push origin "${BRANCH_NAME}" # Open PR -curl -s -X POST \ +response=$(curl -s -f -X POST \ -H "Authorization: Bearer ${GITHUB_TOKEN}" \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2022-11-28" \ @@ -99,4 +99,5 @@ curl -s -X POST \ \"title\": \"Update Replicated CLI docs for ${VERSION}\", \"head\": \"${BRANCH_NAME}\", \"base\": \"main\" - }" | jq -r '.html_url // .message' + }") +echo "${response}" | jq -r '.html_url'