diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 684f8de..12ce1d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,10 +43,6 @@ jobs: # job, which also blocks a release. Changes the CLI does not depend on only warn. api-compatibility: runs-on: ubuntu-latest - permissions: - contents: read - # The nightly run opens an issue for operations no command covers. - issues: write steps: - uses: actions/checkout@v7 - uses: actions/setup-go@v7 @@ -73,48 +69,6 @@ jobs: echo '```' } >> "$GITHUB_STEP_SUMMARY" fi - # Every operation is called by a command, deprecated, or listed with a reason in - # internal/tools/spec/coverage.go. Production is the live spec fetched above; dev - # shows what is coming. A pull request only reports it; the nightly run keeps one - # issue open while anything is uncovered, and closes it once nothing is. - - name: Operations no command covers - if: always() - env: - GH_TOKEN: ${{ github.token }} - run: | - go build -o "$RUNNER_TEMP/spec" ./internal/tools/spec - status=0 - "$RUNNER_TEMP/spec" coverage openapi/platform-api.json >production.md 2>&1 || status=1 - "$RUNNER_TEMP/spec" coverage https://platform.dev.steadybit.com/api/spec >dev.md 2>&1 || status=1 - { - echo "Every platform operation is called by a command, deprecated, or listed with a reason in" - echo "[\`internal/tools/spec/coverage.go\`]($GITHUB_SERVER_URL/$GITHUB_REPOSITORY/blob/main/internal/tools/spec/coverage.go)." - echo "Those that are not need a command, or a line there saying why not." - echo - echo "**Production** (platform.steadybit.com)" - echo - cat production.md - echo - echo "**Dev** (platform.dev.steadybit.com, what is coming)" - echo - cat dev.md - } >coverage.md - cat coverage.md >> "$GITHUB_STEP_SUMMARY" - [ "$status" -eq 0 ] || echo "::warning::The platform has operations no CLI command covers; see the job summary." - [ "$GITHUB_EVENT_NAME" = schedule ] || exit 0 - title="Platform API operations no CLI command covers" - issue=$(gh issue list --state open --search "in:title \"$title\"" --json number,title --jq ".[] | select(.title == \"$title\") | .number" | head -n 1) - if [ "$status" -ne 0 ]; then - echo >>coverage.md - echo "Updated by the nightly run $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID." >>coverage.md - if [ -n "$issue" ]; then - gh issue edit "$issue" --body-file coverage.md - else - gh issue create --title "$title" --body-file coverage.md - fi - elif [ -n "$issue" ]; then - gh issue close "$issue" --comment "Every platform operation is covered or listed again, as of $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID." - fi # Nightly, a quick look that the CLI still talks to the real platform: the test team's # token works and the answers still decode. The whole flow runs weekly, in diff --git a/.github/workflows/spec-check.yml b/.github/workflows/spec-check.yml new file mode 100644 index 0000000..a3671f0 --- /dev/null +++ b/.github/workflows/spec-check.yml @@ -0,0 +1,70 @@ +name: Check Open API Spec + +# Checks the CLI against one platform's live API, for spec-diff-platform-prod.yml and +# spec-diff-platform-dev.yml, whose results the development dashboard shows. It fails +# when the API changed in a way the CLI no longer builds against, or has an operation no +# command calls and internal/tools/spec/coverage.go does not list with a reason. +on: + workflow_call: + inputs: + spec-url: + required: true + type: string + # Keeps one issue open while operations are uncovered, and closes it after. Only + # one of the callers does, so the two do not overwrite each other's issue. + issue: + type: boolean + default: false + +jobs: + check: + timeout-minutes: 10 + runs-on: ubuntu-latest + permissions: + contents: read + issues: write + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-go@v7 + with: + go-version-file: go.mod + - name: Fetch the platform's spec + run: go run ./internal/tools/spec fetch + env: + STEADYBIT_SPEC_URL: ${{ inputs.spec-url }} + - run: go generate ./api + - name: The CLI builds against this API + run: go build ./... && go vet ./... + - name: Every operation is called by a command or listed + if: always() + env: + GH_TOKEN: ${{ github.token }} + SPEC_URL: ${{ inputs.spec-url }} + OPEN_ISSUE: ${{ inputs.issue && github.event_name == 'schedule' }} + run: | + go build -o "$RUNNER_TEMP/spec" ./internal/tools/spec + status=0 + "$RUNNER_TEMP/spec" coverage openapi/platform-api.json >result.md 2>&1 || status=1 + { + echo "Every operation of $SPEC_URL is called by a command, deprecated, or listed with a reason in" + echo "[\`internal/tools/spec/coverage.go\`]($GITHUB_SERVER_URL/$GITHUB_REPOSITORY/blob/main/internal/tools/spec/coverage.go)." + echo "Those that are not need a command, or a line there saying why not." + echo + cat result.md + } >coverage.md + cat coverage.md >>"$GITHUB_STEP_SUMMARY" + if [ "$OPEN_ISSUE" = true ]; then + title="Platform API operations no CLI command covers" + issue=$(gh issue list --state open --search "in:title \"$title\"" --json number,title --jq ".[] | select(.title == \"$title\") | .number" | head -n 1) + if [ "$status" -ne 0 ]; then + printf '\nUpdated by %s/%s/actions/runs/%s.\n' "$GITHUB_SERVER_URL" "$GITHUB_REPOSITORY" "$GITHUB_RUN_ID" >>coverage.md + if [ -n "$issue" ]; then + gh issue edit "$issue" --body-file coverage.md + else + gh issue create --title "$title" --body-file coverage.md + fi + elif [ -n "$issue" ]; then + gh issue close "$issue" --comment "Every platform operation is covered or listed again, as of $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID." + fi + fi + exit "$status" diff --git a/.github/workflows/spec-diff-platform-dev.yml b/.github/workflows/spec-diff-platform-dev.yml new file mode 100644 index 0000000..751a9d9 --- /dev/null +++ b/.github/workflows/spec-diff-platform-dev.yml @@ -0,0 +1,26 @@ +name: Check Open API Spec (platform vs cli / DEV) + +# The CLI against the API of https://platform.dev.steadybit.com/api/spec, +# what is coming, before it reaches customers. Shown on the development dashboard; see spec-check.yml. +on: + push: + branches: + - main + workflow_dispatch: {} + # A change to the check is run before it is merged. + pull_request: + paths: + - .github/workflows/spec-*.yml + - internal/tools/spec/** + schedule: + - cron: '0 7 * * 1-5' + +jobs: + check: + permissions: + contents: read + issues: write + uses: ./.github/workflows/spec-check.yml + with: + spec-url: https://platform.dev.steadybit.com/api/spec + issue: false diff --git a/.github/workflows/spec-diff-platform-prod.yml b/.github/workflows/spec-diff-platform-prod.yml new file mode 100644 index 0000000..327249b --- /dev/null +++ b/.github/workflows/spec-diff-platform-prod.yml @@ -0,0 +1,26 @@ +name: Check Open API Spec (platform vs cli / PROD) + +# The CLI against the API of https://platform.steadybit.com/api/spec, +# what customers run. Shown on the development dashboard; see spec-check.yml. +on: + push: + branches: + - main + workflow_dispatch: {} + # A change to the check is run before it is merged. + pull_request: + paths: + - .github/workflows/spec-*.yml + - internal/tools/spec/** + schedule: + - cron: '0 7 * * 1-5' + +jobs: + check: + permissions: + contents: read + issues: write + uses: ./.github/workflows/spec-check.yml + with: + spec-url: https://platform.steadybit.com/api/spec + issue: true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c9ccaf7..8f81af4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -110,9 +110,11 @@ the live spec daily and before each release. Every operation of the spec is called by a command, deprecated, or listed with a reason in `internal/tools/spec/coverage.go`; a test fails otherwise, so a spec refresh that brings a -new endpoint asks for a command or a line saying why there is none. CI runs the same check -against the live production and dev specs, and the nightly run keeps an issue open while -the platform has operations no command covers: +new endpoint asks for a command or a line saying why there is none. The workflows *Check +Open API Spec (platform vs cli / PROD)* and *(… / DEV)* run the same check, and the build, +against the live production and dev APIs every weekday morning, and the development +dashboard shows their result. The production one keeps an issue open while the platform +has operations no command covers: ```sh go run ./internal/tools/spec coverage # the committed spec