diff --git a/.depot/workflows/main.yml b/.depot/workflows/main.yml new file mode 100644 index 0000000000..3541756e79 --- /dev/null +++ b/.depot/workflows/main.yml @@ -0,0 +1,86 @@ +name: "depot: main" + +on: + push: + branches: [main] + workflow_dispatch: + inputs: + target: + description: Automation to run + required: true + type: choice + default: all + options: + - all + - algolia + - factory + +concurrency: + group: depot-main-${{ github.ref }} + cancel-in-progress: true + +jobs: + algolia: + name: Trigger Algolia crawl + if: >- + github.event_name == 'push' + || inputs.target == 'all' + || inputs.target == 'algolia' + runs-on: depot-ubuntu-latest + timeout-minutes: 5 + steps: + - name: Trigger crawler + env: + CRAWLER_ID: ${{ secrets.CRAWLER_ID }} + CRAWLER_USER_ID: ${{ secrets.CRAWLER_USER_ID }} + CRAWLER_API_KEY: ${{ secrets.CRAWLER_API_KEY }} + run: | + set -euo pipefail + curl \ + --fail-with-body \ + --request POST \ + --header 'Content-Type: application/json' \ + --user "${CRAWLER_USER_ID}:${CRAWLER_API_KEY}" \ + "https://crawler.algolia.com/api/1/crawlers/${CRAWLER_ID}/reindex" + + factory: + name: Publish Factory workflow + if: >- + github.event_name == 'push' + || inputs.target == 'all' + || inputs.target == 'factory' + runs-on: depot-ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + fetch-depth: 0 + + - name: Detect Factory changes + id: changes + env: + BEFORE_SHA: ${{ github.event.before }} + FORCE_PUBLISH: ${{ github.event_name == 'workflow_dispatch' }} + run: | + set -euo pipefail + changed=false + if [[ "$FORCE_PUBLISH" == "true" ]]; then + changed=true + elif [[ -z "$BEFORE_SHA" || "$BEFORE_SHA" == "0000000000000000000000000000000000000000" ]]; then + changed=true + elif ! git diff --quiet "$BEFORE_SHA...$GITHUB_SHA" -- .factory-workflows; then + changed=true + fi + echo "changed=$changed" >> "$GITHUB_OUTPUT" + + - name: Publish dependency workflow + if: steps.changes.outputs.changed == 'true' + uses: elasticclaw/actions/publish-workflow@0a880e0bb75a9957a101962671af55267b778ed3 + with: + hub-endpoint: https://factory.repldev.com + token: ${{ secrets.ELASTICCLAW_TOKEN }} + workspace: replicated-docs + path: .factory-workflows/dependency-update.yaml diff --git a/.depot/workflows/pr.yml b/.depot/workflows/pr.yml new file mode 100644 index 0000000000..a75629a7b0 --- /dev/null +++ b/.depot/workflows/pr.yml @@ -0,0 +1,73 @@ +name: "depot: PR" + +on: + pull_request: + branches: [main] + types: [opened, synchronize, reopened, ready_for_review] + +concurrency: + group: depot-pr-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + label: + name: Label PR + if: >- + github.event.pull_request.head.repo.full_name == github.repository + && !startsWith(github.head_ref, 'deps/') + runs-on: depot-ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + pull-requests: write + steps: + - name: Add documentation labels + env: + GH_TOKEN: ${{ secrets.REPLICATED_GH_PAT }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + set -euo pipefail + jq -n '{labels: ["type::feature", "type::docs"]}' \ + | gh api \ + --method POST \ + "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/labels" \ + --input - + + vale: + name: Vale + if: github.event.pull_request.head.repo.full_name == github.repository + runs-on: depot-ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + fetch-depth: 0 + + - name: Review changed documentation + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: scripts/ci/vale-changed.sh + + factory-workflow: + name: Validate Factory workflow + if: github.event.pull_request.head.repo.full_name == github.repository + runs-on: depot-ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + + - name: Validate dependency workflow + uses: elasticclaw/actions/publish-workflow@0a880e0bb75a9957a101962671af55267b778ed3 + with: + hub-endpoint: https://factory.repldev.com + token: dry-run + workspace: replicated-docs + path: .factory-workflows/dependency-update.yaml + dry-run: true diff --git a/.depot/workflows/release-notes.yml b/.depot/workflows/release-notes.yml new file mode 100644 index 0000000000..0891a9a6bd --- /dev/null +++ b/.depot/workflows/release-notes.yml @@ -0,0 +1,79 @@ +name: "depot: release notes" + +on: + repository_dispatch: + types: + - app-manager-release-notes + - embedded-cluster-release-notes + - embedded-cluster-release-notes-v3 + - kubernetes-installer-release-notes + - replicated-sdk-release-notes + - vendor-portal-release-notes + workflow_dispatch: + inputs: + event_type: + description: Release-notes automation to run + required: true + type: choice + options: + - app-manager-release-notes + - embedded-cluster-release-notes + - embedded-cluster-release-notes-v3 + - kubernetes-installer-release-notes + - replicated-sdk-release-notes + - vendor-portal-release-notes + version: + description: Released version or tag + required: true + type: string + prev_version: + description: Previous version (Replicated SDK only) + required: false + type: string + dry_run: + description: Generate the documentation diff without creating a PR or notifying Slack + required: true + type: boolean + default: true + +concurrency: + group: >- + depot-release-notes-${{ + github.event.action || inputs.event_type + }}-${{ + github.event.client_payload.version || inputs.version + }} + cancel-in-progress: false + +permissions: + contents: write + pull-requests: write + +jobs: + generate: + name: Generate release-notes PR + runs-on: depot-ubuntu-latest + timeout-minutes: 15 + env: + EVENT_TYPE: ${{ github.event.action || inputs.event_type }} + VERSION: ${{ github.event.client_payload.version || inputs.version }} + PREV_VERSION: ${{ github.event.client_payload.prev_version || inputs.prev_version }} + DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || false }} + GH_TOKEN: ${{ secrets.REPLICATED_GH_PAT }} + PUBLIC_REPO_TOKEN: ${{ secrets.REPLICATED_GH_PAT }} + EC_READ_PAT: ${{ secrets.EC_READ_PAT }} + VENDOR_PORTAL_PAT: ${{ secrets.VENDOR_PORTAL_PAT }} + KOTS_RELEASE_NOTES_SLACK_WEBHOOK: ${{ secrets.KOTS_RELEASE_NOTES_SLACK_WEBHOOK }} + KURL_RELEASE_NOTES_SLACK_WEBHOOK: ${{ secrets.KURL_RELEASE_NOTES_SLACK_WEBHOOK }} + EMBEDDED_CLUSTER_RELEASE_NOTES_SLACK_WEBHOOK: ${{ secrets.EMBEDDED_CLUSTER_RELEASE_NOTES_SLACK_WEBHOOK }} + REPLICATED_SDK_RELEASE_NOTES_SLACK_WEBHOOK: ${{ secrets.REPLICATED_SDK_RELEASE_NOTES_SLACK_WEBHOOK }} + VENDOR_PORTAL_RELEASE_NOTES_SLACK_WEBHOOK: ${{ secrets.VENDOR_PORTAL_RELEASE_NOTES_SLACK_WEBHOOK }} + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + fetch-depth: 0 + token: ${{ secrets.REPLICATED_GH_PAT }} + + - name: Generate release notes and create or update the PR + run: scripts/ci/release-notes.sh diff --git a/.factory-workflows/dependency-update.yaml b/.factory-workflows/dependency-update.yaml new file mode 100644 index 0000000000..6410288eb5 --- /dev/null +++ b/.factory-workflows/dependency-update.yaml @@ -0,0 +1,145 @@ +schema_version: v1 +name: dependency-update-replicated-docs +enabled: true + +trigger: + cron: + schedule: "0 21 * * 0" + timezone: "America/Chicago" + overlap_policy: skip + timeout: 2h + +enable_manual_trigger: true + +tags: ["cron", "dependencies", "npm"] +color: cyan + +stages: + - id: start + label: Start + entry: true + on_enter: + inject: | + Workflow task: update npm dependencies for replicated-docs. + + This is an unattended dependency maintenance workflow. Before making + changes, check for an open PR with branch "deps/dependency-update". If + it exists, fetch that branch, rebase it onto current main, and reuse it. + Otherwise, create that branch from current main. + + Do not ask for permission to start. Say exactly [PROCEED] after the + branch is ready. + + - id: working + label: Working + triggers: + - message_contains: "[PROCEED]" + on_enter: + dependency_updates: + ecosystems: [npm] + paths: ["replicated-docs"] + output: dependency_updates + timeout: 30m + inject: | + npm dependency updates were applied to replicated-docs. + + Keep the change scoped to package.json, package-lock.json, and narrowly + required compatibility fixes. Do not modify CI dependencies or files + outside the replicated-docs repository. + + Review {{ .Outputs.dependency_updates.files_changed }}. Run these exact + validation commands from the replicated-docs directory: + + - npm ci + - npm run build + + Fix reasonable compatibility failures. Open or update one grouped PR: + + - Branch: deps/dependency-update + - Title: [deps] Update replicated-docs npm dependencies + - Labels: dependencies, npm, type::security + - Body starts with: "Generated by the replicated-docs dependency-update Factory workflow." + + Say [DONE] with the PR URL only after validation passes and the PR is + open. If {{ .Outputs.dependency_updates.files_changed }} is empty, say + [NO_CHANGES]. + + - id: no-changes + label: No Changes + triggers: + - message_contains: "[NO_CHANGES]" + terminal: true + + - id: validate + label: Validate + triggers: + - message_contains: "[DONE]" + on_enter: + run: + command: | + bash -lc ' + set +e + tmpdir="$(mktemp -d)" + cd replicated-docs + + npm ci >"$tmpdir/npm-ci.log" 2>&1 + install_status=$? + + npm run build >"$tmpdir/build.log" 2>&1 + build_status=$? + + jq -n \ + --argjson install_status "$install_status" \ + --argjson build_status "$build_status" \ + --arg install_log "$(tail -c 12000 "$tmpdir/npm-ci.log")" \ + --arg build_log "$(tail -c 12000 "$tmpdir/build.log")" \ + '"'"'{ + status: (if $install_status == 0 and $build_status == 0 then "passed" else "failed" end), + checks: [ + {name: "npm ci", exit_code: $install_status, log_tail: $install_log}, + {name: "npm run build", exit_code: $build_status, log_tail: $build_log} + ] + }'"'"' + ' + output: validation + timeout: 45m + gate: + output: validation + pass: + path: status + values: [passed] + fail: + path: status + values: [failed, error] + required: true + + - id: fix-validation + label: Fix Validation + triggers: + - gate_result: + stage: validate + verdict: fail + on_enter: + inject: | + Validation failed: + + {{ .Outputs.validation.checks }} + + Fix the scoped dependency compatibility failures, commit and push to + the same PR branch, then say [DONE] with the PR URL again. If the + failure cannot be fixed safely within scope, explain why and say + [FAILED]. + + - id: failed + label: Failed + triggers: + - message_contains: "[FAILED]" + terminal: true + + - id: complete + label: Complete + triggers: + - gate_result: + stage: validate + verdict: pass + terminal: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 4bd82e89ff..0000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,22 +0,0 @@ -version: 2 -updates: - # Update npm dependencies based on package.json - - package-ecosystem: "npm" - directory: "/" # Root directory - schedule: - interval: "weekly" - open-pull-requests-limit: 10 - labels: - - "dependencies" - - "npm" - - "type::security" - - # Keep Dependabot itself up to date - - package-ecosystem: "github-actions" - directory: "/" # Root directory (or specify if actions are elsewhere) - schedule: - interval: "weekly" - labels: - - "dependencies" - - "github-actions" - - "type::security" diff --git a/.github/workflows/algolia-crawl.yml b/.github/workflows/algolia-crawl.yml deleted file mode 100644 index 1d58e8db79..0000000000 --- a/.github/workflows/algolia-crawl.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Algolia Crawler - -concurrency: algolia-crawl - -on: - push: - branches: [ main ] - workflow_dispatch: - -jobs: - trigger_crawl: - name: Trigger Algolia Crawl - runs-on: ubuntu-latest - steps: - - name: Trigger Algolia Crawler - run: | - curl -X POST \ - 'https://crawler.algolia.com/api/1/crawlers/${{ secrets.CRAWLER_ID }}/reindex' \ - -H 'Content-Type: application/json' \ - --user '${{ secrets.CRAWLER_USER_ID }}:${{ secrets.CRAWLER_API_KEY }}' \ No newline at end of file diff --git a/.github/workflows/app-manager-release-notes.yml b/.github/workflows/app-manager-release-notes.yml deleted file mode 100644 index c6af24dbf9..0000000000 --- a/.github/workflows/app-manager-release-notes.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: app-manager-release-notes -on: - repository_dispatch: - types: [app-manager-release-notes] - inputs: - version: - description: KOTS version - required: true - -jobs: - generate-release-notes-pr: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - - - name: Generate Release Notes - id: release-notes - env: - KOTS_VERSION: ${{ github.event.client_payload.version }} - uses: replicatedhq/release-notes-generator@main - with: - owner-repo: replicatedhq/kots - head: $KOTS_VERSION - title: ${KOTS_VERSION#v} - description: 'Support for Kubernetes: 1.31, 1.32, 1.33, and 1.34' - include-pr-links: false - github-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Update Release Notes - env: - PATTERN: ".+RELEASE_NOTES_PLACEHOLDER.+" - run: | - cat <> /tmp/release-notes.txt - - ${{ steps.release-notes.outputs.release-notes }} - EOT - sed -i -E "/$PATTERN/r /tmp/release-notes.txt" docs/release-notes/rn-app-manager.md - rm -rf /tmp/release-notes.txt - - - name: Create Pull Request # creates a PR if there are differences - uses: peter-evans/create-pull-request@v8 - id: cpr - with: - token: ${{ secrets.REPLICATED_GH_PAT }} - commit-message: App Manager ${{ github.event.client_payload.version }} release notes - title: App Manager ${{ github.event.client_payload.version }} release notes - branch: automation/app-manager-release-notes-${{ github.event.client_payload.version }} - delete-branch: true - base: "main" - body: "Automated changes by the [app-manager-release-notes](https://github.com/replicatedhq/replicated-docs/blob/main/.github/workflows/app-manager-release-notes.yml) GitHub action" - - - name: Check outputs - run: | - echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" - echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}" - - - name: Slack Notification - uses: slackapi/slack-github-action@v4.0.0 - with: - webhook: ${{ secrets.KOTS_RELEASE_NOTES_SLACK_WEBHOOK }} - webhook-type: webhook-trigger - payload: | - version: "${{ github.event.client_payload.version }}" - pull_request_url: "${{steps.cpr.outputs.pull-request-url}}" diff --git a/.github/workflows/auto-label.yml b/.github/workflows/auto-label.yml deleted file mode 100644 index bace41db3c..0000000000 --- a/.github/workflows/auto-label.yml +++ /dev/null @@ -1,26 +0,0 @@ -on: - pull_request_target: - types: [opened] - -name: Auto-label new PRs - -permissions: - pull-requests: write - -jobs: - label: - # Dependabot PRs are already labeled via .github/dependabot.yml - # (dependencies / type::security), and pull_request_target runs - # them with a read-only token from a separate secret store. - if: github.actor != 'dependabot[bot]' - runs-on: ubuntu-latest - steps: - - uses: actions/github-script@v9 - with: - script: | - const labels = ['type::feature', 'type::docs'] - await github.rest.issues.addLabels({ - ...context.repo, - issue_number: context.issue.number, - labels - }) diff --git a/.github/workflows/embedded-cluster-release-notes-v3.yml b/.github/workflows/embedded-cluster-release-notes-v3.yml deleted file mode 100644 index 692f066026..0000000000 --- a/.github/workflows/embedded-cluster-release-notes-v3.yml +++ /dev/null @@ -1,91 +0,0 @@ -name: embedded-cluster-release-notes-v3 -on: - repository_dispatch: - types: [embedded-cluster-release-notes-v3] - inputs: - version: - description: Embedded Cluster version - required: true - -jobs: - generate-release-notes-pr: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - - - name: Generate Release Notes - id: release-notes - env: - EC_VERSION: ${{ github.event.client_payload.version }} - uses: replicatedhq/release-notes-generator@main - with: - owner-repo: replicatedhq/ec - head: $EC_VERSION - title: ${EC_VERSION#v} - description: '' - include-pr-links: false - github-token: ${{ secrets.EC_READ_PAT }} - - - name: Update Release Notes - env: - PATTERN: ".+RELEASE_NOTES_PLACEHOLDER.+" - EC_VERSION: ${{ github.event.client_payload.version }} - run: | - # Extract EC version without v prefix if it exists - EC_VERSION_CLEAN=${EC_VERSION#v} - - # Create the full release notes content - cat > /tmp/release-notes.txt << EOF - - ## ${EC_VERSION_CLEAN} - - Released on $(date +'%B %-d, %Y') - - - - ${{ steps.release-notes.outputs.release-notes }} - EOF - - sed -i -E "/$PATTERN/r /tmp/release-notes.txt" docs/release-notes/rn-embedded-cluster-v3.md - rm -f /tmp/release-notes.txt - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v8 - id: cpr - with: - token: ${{ secrets.REPLICATED_GH_PAT }} - commit-message: Embedded Cluster ${{ github.event.client_payload.version }} release notes - title: Embedded Cluster ${{ github.event.client_payload.version }} release notes - branch: automation/embedded-cluster-release-notes-v3-${{ github.event.client_payload.version }} - delete-branch: true - base: "main" - body: "Automated changes by the [embedded-cluster-release-notes-v3](https://github.com/replicatedhq/replicated-docs/blob/main/.github/workflows/embedded-cluster-release-notes-v3.yml) GitHub action. Please manually replace the version table placeholder." - - - name: Check outputs - run: | - echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" - echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}" - - - name: Slack Notification - uses: slackapi/slack-github-action@v4.0.0 - with: - webhook: ${{ secrets.EMBEDDED_CLUSTER_RELEASE_NOTES_SLACK_WEBHOOK }} - webhook-type: webhook-trigger - payload: | - version: "${{ github.event.client_payload.version }}" - pull_request_url: "${{steps.cpr.outputs.pull-request-url}}" diff --git a/.github/workflows/embedded-cluster-release-notes.yml b/.github/workflows/embedded-cluster-release-notes.yml deleted file mode 100644 index aed7cc68f8..0000000000 --- a/.github/workflows/embedded-cluster-release-notes.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: embedded-cluster-release-notes -on: - repository_dispatch: - types: [embedded-cluster-release-notes] - inputs: - version: - description: Embedded Cluster version - required: true - -jobs: - generate-release-notes-pr: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - - - name: Generate Release Notes - id: release-notes - env: - EC_VERSION: ${{ github.event.client_payload.version }} - uses: replicatedhq/release-notes-generator@main - with: - owner-repo: replicatedhq/embedded-cluster - head: $EC_VERSION - title: ${EC_VERSION#v} - description: '' - include-pr-links: false - github-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Update Release Notes - env: - PATTERN: ".+RELEASE_NOTES_PLACEHOLDER.+" - EC_VERSION: ${{ github.event.client_payload.version }} - run: | - # Extract EC version without v prefix if it exists - EC_VERSION_CLEAN=${EC_VERSION#v} - - # Create the full release notes content - cat > /tmp/release-notes.txt << EOF - - ## ${EC_VERSION_CLEAN} - - Released on $(date +'%B %-d, %Y') - - - - ${{ steps.release-notes.outputs.release-notes }} - EOF - - sed -i -E "/$PATTERN/r /tmp/release-notes.txt" docs/release-notes/rn-embedded-cluster.md - rm -f /tmp/release-notes.txt - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v8 - id: cpr - with: - token: ${{ secrets.REPLICATED_GH_PAT }} - commit-message: Embedded Cluster ${{ github.event.client_payload.version }} release notes - title: Embedded Cluster ${{ github.event.client_payload.version }} release notes - branch: automation/embedded-cluster-release-notes-${{ github.event.client_payload.version }} - delete-branch: true - base: "main" - body: "Automated changes by the [embedded-cluster-release-notes](https://github.com/replicatedhq/replicated-docs/blob/main/.github/workflows/embedded-cluster-release-notes.yml) GitHub action. Please manually replace the version table placeholder." - - - name: Check outputs - run: | - echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" - echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}" - - - name: Slack Notification - uses: slackapi/slack-github-action@v4.0.0 - with: - webhook: ${{ secrets.EMBEDDED_CLUSTER_RELEASE_NOTES_SLACK_WEBHOOK }} - webhook-type: webhook-trigger - payload: | - version: "${{ github.event.client_payload.version }}" - pull_request_url: "${{steps.cpr.outputs.pull-request-url}}" diff --git a/.github/workflows/kubernetes-installer-release-notes.yml b/.github/workflows/kubernetes-installer-release-notes.yml deleted file mode 100644 index 5c40e86ef7..0000000000 --- a/.github/workflows/kubernetes-installer-release-notes.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: kubernetes-installer-release-notes -on: - repository_dispatch: - types: [kubernetes-installer-release-notes] - inputs: - version: - description: kURL version - required: true - -jobs: - generate-release-notes-pr: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - - - name: Generate Release Notes - id: release-notes - env: - KURL_VERSION: ${{ github.event.client_payload.version }} - uses: replicatedhq/release-notes-generator@main - with: - owner-repo: replicatedhq/kurl - head: $KURL_VERSION - title: $KURL_VERSION - include-pr-links: false - github-token: ${{ secrets.GITHUB_TOKEN }} - feature-type-labels: type::feature,kurl::type::feature - - - name: Update Release Notes - env: - PATTERN: ".+RELEASE_NOTES_PLACEHOLDER.+" - run: | - cat <> /tmp/release-notes.txt - - ${{ steps.release-notes.outputs.release-notes }} - EOT - sed -i -E "/$PATTERN/r /tmp/release-notes.txt" docs/release-notes/rn-kubernetes-installer.md - rm -rf /tmp/release-notes.txt - - - name: Create Pull Request # creates a PR if there are differences - uses: peter-evans/create-pull-request@v8 - id: cpr - with: - token: ${{ secrets.REPLICATED_GH_PAT }} - commit-message: Kubernetes Installer ${{ github.event.client_payload.version }} release notes - title: Kubernetes Installer ${{ github.event.client_payload.version }} release notes - branch: automation/kubernetes-installer-release-notes-${{ github.event.client_payload.version }} - delete-branch: true - base: "main" - body: "Automated changes by the [kubernetes-installer-release-notes](https://github.com/replicatedhq/replicated-docs/blob/main/.github/workflows/kubernetes-installer-release-notes.yml) GitHub action" - - - name: Check outputs - run: | - echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" - echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}" - - - name: Slack Notification - uses: slackapi/slack-github-action@v4.0.0 - with: - webhook: ${{ secrets.KURL_RELEASE_NOTES_SLACK_WEBHOOK }} - webhook-type: webhook-trigger - payload: | - version: "${{ github.event.client_payload.version }}" - pull_request_url: "${{steps.cpr.outputs.pull-request-url}}" diff --git a/.github/workflows/replicated-sdk-release-notes.yml b/.github/workflows/replicated-sdk-release-notes.yml deleted file mode 100644 index 1e5155c1b7..0000000000 --- a/.github/workflows/replicated-sdk-release-notes.yml +++ /dev/null @@ -1,68 +0,0 @@ -name: replicated-sdk-release-notes -on: - repository_dispatch: - types: [replicated-sdk-release-notes] - inputs: - version: - description: Replicated SDK version - required: true - prev_version: - description: Previous Replicated SDK version - required: true - -jobs: - generate-release-notes-pr: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v7 - - - name: Generate Release Notes - id: release-notes - env: - REPLICATED_SDK_VERSION: ${{ github.event.client_payload.version }} - PREV_REPLICATED_SDK_VERSION: ${{ github.event.client_payload.prev_version }} - uses: replicatedhq/release-notes-generator@main - with: - owner-repo: replicatedhq/replicated-sdk - base: $PREV_REPLICATED_SDK_VERSION - head: $REPLICATED_SDK_VERSION - title: $REPLICATED_SDK_VERSION - include-pr-links: false - github-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Update Release Notes - env: - PATTERN: ".+RELEASE_NOTES_PLACEHOLDER.+" - run: | - cat <> /tmp/release-notes.txt - - ${{ steps.release-notes.outputs.release-notes }} - EOT - sed -i -E "/$PATTERN/r /tmp/release-notes.txt" docs/release-notes/rn-replicated-sdk.md - rm -rf /tmp/release-notes.txt - - - name: Create Pull Request # creates a PR if there are differences - uses: peter-evans/create-pull-request@v8 - id: cpr - with: - token: ${{ secrets.REPLICATED_GH_PAT }} - commit-message: Replicated SDK ${{ github.event.client_payload.version }} release notes - title: Replicated SDK ${{ github.event.client_payload.version }} release notes - branch: automation/replicated-sdk-release-notes-${{ github.event.client_payload.version }} - delete-branch: true - base: "main" - body: "Automated changes by the [replicated-sdk-release-notes](https://github.com/replicatedhq/replicated-docs/blob/main/.github/workflows/replicated-sdk-release-notes.yml) GitHub action" - - - name: Check outputs - run: | - echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" - echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}" - - - name: Slack Notification - uses: slackapi/slack-github-action@v4.0.0 - with: - webhook: ${{ secrets.REPLICATED_SDK_RELEASE_NOTES_SLACK_WEBHOOK }} - webhook-type: webhook-trigger - payload: | - version: "${{ github.event.client_payload.version }}" - pull_request_url: "${{steps.cpr.outputs.pull-request-url}}" diff --git a/.github/workflows/vale-pr-review.yml b/.github/workflows/vale-pr-review.yml deleted file mode 100644 index dd95e589df..0000000000 --- a/.github/workflows/vale-pr-review.yml +++ /dev/null @@ -1,40 +0,0 @@ -# Run Vale linter only on changed docs when a PR is opened or updated against main. -name: Vale PR review - -on: - pull_request: - branches: [main] - types: [opened, synchronize] - -jobs: - vale: - name: Vale - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v7 - with: - fetch-depth: 0 - - - name: Get changed doc files - id: changed - uses: tj-actions/changed-files@v47 - with: - files: | - docs/**/*.md - docs/**/*.mdx - files_ignore: | - docs/reference/kots-cli-* - docs/reference/replicated-cli-* - docs/reference/notifications-* - docs/release-notes/rn-* - separator: " " - - - name: Run Vale on changed files - if: steps.changed.outputs.all_changed_files_count != '0' - uses: errata-ai/vale-action@v3 - with: - files: ${{ steps.changed.outputs.all_changed_files }} - separator: " " - fail_on_error: false - reporter: github-pr-review diff --git a/.github/workflows/vendor-portal-release-notes.yml b/.github/workflows/vendor-portal-release-notes.yml deleted file mode 100644 index 980736483f..0000000000 --- a/.github/workflows/vendor-portal-release-notes.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: vendor-portal-release-notes -on: - repository_dispatch: - types: [vendor-portal-release-notes] - inputs: - version: - description: Vendor Portal version - required: true - -jobs: - generate-release-notes: - runs-on: ubuntu-latest - outputs: - releaseNotes: ${{ steps.release-notes.outputs.release-notes }} - steps: - - uses: actions/checkout@v7 - - - name: Generate Release Notes - id: release-notes - env: - VENDOR_PORTAL_VERSION: ${{ github.event.client_payload.version }} - uses: replicatedhq/release-notes-generator@main - with: - owner-repo: replicatedhq/vandoor - head: $VENDOR_PORTAL_VERSION - title: $VENDOR_PORTAL_VERSION - include-pr-links: false - github-token: ${{ secrets.VENDOR_PORTAL_PAT }} - feature-type-labels: type::feature - generate-release-notes-pr: - runs-on: ubuntu-latest - needs: generate-release-notes - if: ${{ needs.generate-release-notes.outputs.releaseNotes != '' || needs.generate-release-notes.outputs.releaseNotes != null }} - steps: - - uses: actions/checkout@v7 - - name: Update Release Notes - env: - PATTERN: ".+RELEASE_NOTES_PLACEHOLDER.+" - run: | - cat <> /tmp/release-notes.txt - - ${{ needs.generate-release-notes.outputs.releaseNotes }} - EOT - sed -i -E "/$PATTERN/r /tmp/release-notes.txt" docs/release-notes/rn-vendor-platform.md - rm -rf /tmp/release-notes.txt - - - name: Create Pull Request # creates a PR if there are differences - uses: peter-evans/create-pull-request@v8 - id: cpr - with: - token: ${{ secrets.REPLICATED_GH_PAT }} - commit-message: Vendor Portal ${{ github.event.client_payload.version }} release notes - title: Vendor Portal ${{ github.event.client_payload.version }} release notes - branch: automation/vendor-portal-release-notes-${{ github.event.client_payload.version }} - delete-branch: true - base: "main" - body: "Automated changes by the [vendor-portal-release-notes](https://github.com/replicatedhq/replicated-docs/blob/main/.github/workflows/vendor-portal-release-notes.yml) GitHub action" - - - name: Check outputs - run: | - echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" - echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}" - - - name: Slack Notification - uses: slackapi/slack-github-action@v4.0.0 - with: - webhook: ${{ secrets.VENDOR_PORTAL_RELEASE_NOTES_SLACK_WEBHOOK }} - webhook-type: webhook-trigger - payload: | - version: "${{ github.event.client_payload.version }}" - pull_request_url: "${{ steps.cpr.outputs.pull-request-url }}" diff --git a/docs/release-notes/rn-replicated-sdk.md b/docs/release-notes/rn-replicated-sdk.md index ff4c035a6b..f1bffb48bc 100644 --- a/docs/release-notes/rn-replicated-sdk.md +++ b/docs/release-notes/rn-replicated-sdk.md @@ -8,6 +8,8 @@ pagination_prev: null This topic contains release notes for the [Replicated SDK](/vendor/replicated-sdk-overview). The release notes list new features, improvements, bug fixes, known issues, and breaking changes. + + ## 1.19.8 Released on July 31, 2026 diff --git a/scripts/ci/release-notes.sh b/scripts/ci/release-notes.sh new file mode 100755 index 0000000000..9cc8a6d6cf --- /dev/null +++ b/scripts/ci/release-notes.sh @@ -0,0 +1,270 @@ +#!/usr/bin/env bash + +set -euo pipefail + +: "${EVENT_TYPE:?EVENT_TYPE is required}" +: "${VERSION:?VERSION is required}" + +generator_ref="942ca6279047ce728af73d2c30bc73c946a48476" +base_version="" +description="" +feature_labels="type::feature" +manual_table="none" +clean_version="${VERSION#v}" + +case "$EVENT_TYPE" in + app-manager-release-notes) + source_repo="replicatedhq/kots" + target_file="docs/release-notes/rn-app-manager.md" + title="$clean_version" + product_name="App Manager" + description="Support for Kubernetes: 1.31, 1.32, 1.33, and 1.34" + source_token="${PUBLIC_REPO_TOKEN:-}" + slack_webhook="${KOTS_RELEASE_NOTES_SLACK_WEBHOOK:-}" + ;; + embedded-cluster-release-notes) + source_repo="replicatedhq/embedded-cluster" + target_file="docs/release-notes/rn-embedded-cluster.md" + title="$clean_version" + product_name="Embedded Cluster" + manual_table="embedded-cluster-v2" + source_token="${PUBLIC_REPO_TOKEN:-}" + slack_webhook="${EMBEDDED_CLUSTER_RELEASE_NOTES_SLACK_WEBHOOK:-}" + ;; + embedded-cluster-release-notes-v3) + source_repo="replicatedhq/ec" + target_file="docs/release-notes/rn-embedded-cluster-v3.md" + title="$clean_version" + product_name="Embedded Cluster" + manual_table="embedded-cluster-v3" + source_token="${EC_READ_PAT:-}" + slack_webhook="${EMBEDDED_CLUSTER_RELEASE_NOTES_SLACK_WEBHOOK:-}" + ;; + kubernetes-installer-release-notes) + source_repo="replicatedhq/kurl" + target_file="docs/release-notes/rn-kubernetes-installer.md" + title="$VERSION" + product_name="Kubernetes Installer" + feature_labels="type::feature,kurl::type::feature" + source_token="${PUBLIC_REPO_TOKEN:-}" + slack_webhook="${KURL_RELEASE_NOTES_SLACK_WEBHOOK:-}" + ;; + replicated-sdk-release-notes) + : "${PREV_VERSION:?PREV_VERSION is required for Replicated SDK release notes}" + source_repo="replicatedhq/replicated-sdk" + target_file="docs/release-notes/rn-replicated-sdk.md" + title="$VERSION" + product_name="Replicated SDK" + base_version="$PREV_VERSION" + source_token="${PUBLIC_REPO_TOKEN:-}" + slack_webhook="${REPLICATED_SDK_RELEASE_NOTES_SLACK_WEBHOOK:-}" + ;; + vendor-portal-release-notes) + source_repo="replicatedhq/vandoor" + target_file="docs/release-notes/rn-vendor-platform.md" + title="$VERSION" + product_name="Vendor Portal" + source_token="${VENDOR_PORTAL_PAT:-}" + slack_webhook="${VENDOR_PORTAL_RELEASE_NOTES_SLACK_WEBHOOK:-}" + ;; + *) + echo "Unsupported release-notes event: $EVENT_TYPE" >&2 + exit 1 + ;; +esac + +if [[ -z "$source_token" ]]; then + echo "The source repository token for $EVENT_TYPE is missing" >&2 + exit 1 +fi + +if [[ ! -f "$target_file" ]]; then + echo "Target release-notes file does not exist: $target_file" >&2 + exit 1 +fi + +if grep -Fq "## $title" "$target_file"; then + echo "$target_file already contains release $title; nothing to do." + exit 0 +fi + +tmp_dir="$(mktemp -d)" +trap 'rm -rf "$tmp_dir"' EXIT +generated_file="$tmp_dir/generated.md" +insert_file="$tmp_dir/insert.md" + +generator_args=( + --owner-repo "$source_repo" + --head "$VERSION" + --title "$title" + --description "$description" + --include-pr-links=false + --feature-type-labels "$feature_labels" +) +if [[ -n "$base_version" ]]; then + generator_args+=(--base "$base_version") +fi + +if [[ -n "${RELEASE_NOTES_GENERATOR_BIN:-}" ]]; then + GITHUB_AUTH_TOKEN="$source_token" \ + "$RELEASE_NOTES_GENERATOR_BIN" "${generator_args[@]}" > "$generated_file" +else + if ! command -v go >/dev/null 2>&1; then + echo "Go is required to run the pinned release-notes generator" >&2 + exit 1 + fi + GITHUB_AUTH_TOKEN="$source_token" \ + go run "github.com/replicatedhq/release-notes-generator@${generator_ref}" \ + "${generator_args[@]}" > "$generated_file" +fi + +if [[ "$manual_table" == "none" && ! -s "$generated_file" ]]; then + echo "The release-notes generator returned no notes; no PR is needed." + exit 0 +fi + +case "$manual_table" in + none) + cp "$generated_file" "$insert_file" + ;; + embedded-cluster-v2|embedded-cluster-v3) + { + echo + echo "## $title" + echo + echo "Released on $(date +'%B %-d, %Y')" + echo + echo '' + awk ' + found { if (started || NF) { started=1; print } } + /^Released on / { found=1 } + ' "$generated_file" + } > "$insert_file" + ;; +esac + +awk -v insert_file="$insert_file" ' + { print } + !inserted && /RELEASE_NOTES_PLACEHOLDER/ { + while ((getline line < insert_file) > 0) { + print line + } + close(insert_file) + inserted=1 + } + END { + if (!inserted) { + exit 42 + } + } +' "$target_file" > "$tmp_dir/updated.md" || { + status=$? + if [[ "$status" == "42" ]]; then + echo "Release-notes placeholder not found in $target_file" >&2 + fi + exit "$status" +} +mv "$tmp_dir/updated.md" "$target_file" + +if git diff --quiet -- "$target_file"; then + echo "Release-note generation did not change $target_file." + exit 0 +fi + +if [[ "${GITHUB_STEP_SUMMARY:-}" ]]; then + { + echo "## $product_name $VERSION release notes" + echo + echo "Target: \`$target_file\`" + } >> "$GITHUB_STEP_SUMMARY" +fi + +if [[ "${DRY_RUN:-false}" == "true" || "${DRY_RUN:-false}" == "1" ]]; then + echo "Dry run: generated release-notes diff follows." + git --no-pager diff -- "$target_file" + exit 0 +fi + +: "${GH_TOKEN:?GH_TOKEN is required to push the release-notes branch}" +if [[ -z "$slack_webhook" ]]; then + echo "The Slack webhook for $EVENT_TYPE is missing" >&2 + exit 1 +fi + +branch_version="$(printf '%s' "$VERSION" | tr -c 'A-Za-z0-9._+-' '-')" +branch="automation/${EVENT_TYPE}-${branch_version}" +commit_title="$product_name $VERSION release notes" +pr_body="Automated changes generated by the [Depot release-notes workflow](https://github.com/replicatedhq/replicated-docs/blob/main/.depot/workflows/release-notes.yml)." +if [[ "$manual_table" != "none" ]]; then + pr_body="$pr_body Please manually replace the version table placeholder." +fi + +git config user.name replicated-ci +git config user.email replicated-ci@users.noreply.github.com +git switch -C "$branch" +git add "$target_file" +git commit -m "$commit_title" + +remote_sha="$(git ls-remote --heads origin "refs/heads/$branch" | awk '{print $1}')" +if [[ -n "$remote_sha" ]]; then + git push \ + --force-with-lease="refs/heads/$branch:$remote_sha" \ + origin \ + "HEAD:refs/heads/$branch" +else + git push origin "HEAD:refs/heads/$branch" +fi + +pr_url="$( + gh pr list \ + --repo "$GITHUB_REPOSITORY" \ + --head "$branch" \ + --base main \ + --state open \ + --json url \ + --jq '.[0].url // empty' +)" +if [[ -z "$pr_url" ]]; then + pr_url="$( + gh pr create \ + --repo "$GITHUB_REPOSITORY" \ + --base main \ + --head "$branch" \ + --title "$commit_title" \ + --body "$pr_body" + )" +fi + +jq -n \ + --arg version "$VERSION" \ + --arg pull_request_url "$pr_url" \ + '{version: $version, pull_request_url: $pull_request_url}' \ + | curl \ + --fail-with-body \ + --request POST \ + --header 'Content-Type: application/json' \ + --data-binary @- \ + "$slack_webhook" + +echo "Release-notes PR: $pr_url" diff --git a/scripts/ci/vale-changed.sh b/scripts/ci/vale-changed.sh new file mode 100755 index 0000000000..df74cb75a9 --- /dev/null +++ b/scripts/ci/vale-changed.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash + +set -euo pipefail + +: "${BASE_SHA:?BASE_SHA is required}" +: "${HEAD_SHA:?HEAD_SHA is required}" + +vale_version="3.17.1" +vale_archive="vale_${vale_version}_Linux_64-bit.tar.gz" +vale_checksum="db947f89f2292e6a0381a61de155f6a5f5cb4cb460ca178ea412ef605559cefd" +tool_dir="${RUNNER_TEMP:-/tmp}/vale-${vale_version}" +mkdir -p "$tool_dir" + +if [[ ! -x "$tool_dir/vale" ]]; then + curl \ + --fail \ + --location \ + --silent \ + --show-error \ + --output "$tool_dir/$vale_archive" \ + "https://github.com/errata-ai/vale/releases/download/v${vale_version}/${vale_archive}" + echo "$vale_checksum $tool_dir/$vale_archive" | sha256sum --check + tar -xzf "$tool_dir/$vale_archive" -C "$tool_dir" vale +fi + +changed_files=() +while IFS= read -r -d '' changed_file; do + changed_files+=("$changed_file") +done < <( + git diff \ + --name-only \ + --diff-filter=ACMR \ + -z \ + "$BASE_SHA...$HEAD_SHA" \ + -- \ + ':(glob)docs/**/*.md' \ + ':(glob)docs/**/*.mdx' +) + +if (( ${#changed_files[@]} == 0 )); then + echo "No changed documentation files require Vale review." + if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then + echo "## Vale" >> "$GITHUB_STEP_SUMMARY" + echo >> "$GITHUB_STEP_SUMMARY" + echo "No changed documentation files require Vale review." >> "$GITHUB_STEP_SUMMARY" + fi + exit 0 +fi + +"$tool_dir/vale" sync + +results_file="${RUNNER_TEMP:-/tmp}/vale-results.json" +"$tool_dir/vale" \ + --no-exit \ + --output=JSON \ + "${changed_files[@]}" > "$results_file" + +if ! jq -e 'type == "object"' "$results_file" >/dev/null; then + echo "Vale returned invalid JSON output" >&2 + exit 1 +fi + +escape_property() { + local value="$1" + value="${value//'%'/'%25'}" + value="${value//$'\r'/'%0D'}" + value="${value//$'\n'/'%0A'}" + value="${value//':'/'%3A'}" + value="${value//','/'%2C'}" + printf '%s' "$value" +} + +escape_message() { + local value="$1" + value="${value//'%'/'%25'}" + value="${value//$'\r'/'%0D'}" + value="${value//$'\n'/'%0A'}" + printf '%s' "$value" +} + +while IFS= read -r encoded; do + decoded="$(printf '%s' "$encoded" | base64 --decode)" + file="$(jq -r '.[0]' <<< "$decoded")" + line="$(jq -r '.[1]' <<< "$decoded")" + column="$(jq -r '.[2]' <<< "$decoded")" + check="$(jq -r '.[3]' <<< "$decoded")" + message="$(jq -r '.[4]' <<< "$decoded")" + printf '::warning file=%s,line=%s,col=%s,title=%s::%s\n' \ + "$(escape_property "$file")" \ + "$line" \ + "$column" \ + "$(escape_property "$check")" \ + "$(escape_message "$message")" +done < <( + jq -r ' + to_entries[] + | .key as $file + | .value[] + | [ + $file, + (.Line // 1 | tostring), + (.Span[0] // 1 | tostring), + (.Check // "Vale"), + (.Message // "") + ] + | @base64 + ' "$results_file" +) + +alert_count="$(jq '[to_entries[].value[]] | length' "$results_file")" +echo "Vale reported $alert_count alert(s) across ${#changed_files[@]} changed file(s)." + +if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then + { + echo "## Vale" + echo + echo "Reviewed ${#changed_files[@]} changed documentation file(s) and reported $alert_count alert(s)." + if (( alert_count > 0 )); then + echo + echo "| Rule | Severity | Count |" + echo "| --- | --- | ---: |" + jq -r ' + [to_entries[].value[]] + | group_by([.Check, .Severity]) + | .[] + | "| \(.[0].Check) | \(.[0].Severity) | \(length) |" + ' "$results_file" + fi + } >> "$GITHUB_STEP_SUMMARY" +fi