api: move scripts to generate and validate swagger to api module

Signed-off-by: Rafael Camelo <rafaelcamelo31@gmail.com>

revert api/swagger.yaml to undo formatting

Signed-off-by: Rafael Camelo <rafaelcamelo31@gmail.com>

refactor(api): update Dockerfile and Makefile with newline at the EOF and use current best practices

Signed-off-by: Rafael Camelo <rafaelcamelo31@gmail.com>

refactor validations and swagger generation flow

Signed-off-by: Rafael Camelo <rafaelcamelo31@gmail.com>

shfmt

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
Signed-off-by: Rafael Camelo <rafaelcamelo31@gmail.com>

refactor generate-swagger-api.sh to use absolute path

Signed-off-by: Rafael Camelo <rafaelcamelo31@gmail.com>

add validate-api-swagger job for GitHub Actions

Signed-off-by: Rafael Camelo <rafaelcamelo31@gmail.com>

fix validate-api-swagger ci workflow

Signed-off-by: Rafael Camelo <rafaelcamelo31@gmail.com>

update go version and redoc in api module

Signed-off-by: Rafael Camelo <rafaelcamelo31@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Rafael Camelo
2025-10-26 17:20:28 -04:00
committed by Sebastiaan van Stijn
parent 4dc87c55c7
commit 0666108ebf
12 changed files with 193 additions and 86 deletions

View File

@@ -153,6 +153,29 @@ jobs:
run: |
make -o build validate-${{ matrix.script }}
validate-api-swagger:
runs-on: ubuntu-24.04
timeout-minutes: 10 # guardrails timeout for the whole job
defaults:
run:
working-directory: api
needs:
- validate-dco
name: validate (api-swagger)
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Build api module image
run: |
make build GO_VERSION=${{ env.GO_VERSION }}
-
name: Validate swagger
run: |
make validate-swagger
make validate-swagger-gen
smoke-prepare:
runs-on: ubuntu-24.04
timeout-minutes: 10 # guardrails timeout for the whole job

View File

@@ -83,22 +83,6 @@ RUN --mount=type=cache,sharing=locked,id=moby-criu-aptlib,target=/var/lib/apt \
FROM distribution/distribution:$REGISTRY_VERSION AS registry
RUN mkdir /build && mv /bin/registry /build/registry
# go-swagger
FROM base AS swagger
WORKDIR /go/src/github.com/go-swagger/go-swagger
ARG TARGETPLATFORM
# GO_SWAGGER_VERSION specifies the version of the go-swagger binary to install.
# Go-swagger is used in CI for generating types from swagger.yaml in
# hack/validate/swagger-gen
ARG GO_SWAGGER_VERSION=v0.33.1
RUN --mount=type=cache,target=/root/.cache/go-build,id=swagger-build-$TARGETPLATFORM \
--mount=type=cache,target=/go/pkg/mod \
--mount=type=tmpfs,target=/go/src/ <<EOT
set -e
GOBIN=/build CGO_ENABLED=0 xx-go install "github.com/go-swagger/go-swagger/cmd/swagger@${GO_SWAGGER_VERSION}"
xx-verify /build/swagger
EOT
# frozen-images
# See also frozenImages in "testutil/environment/protect.go" (which needs to
# be updated when adding images to this list)
@@ -421,7 +405,6 @@ FROM docker/compose-bin:${COMPOSE_VERSION} AS compose
FROM base AS dev-systemd-false
COPY --link --from=frozen-images /build/ /docker-frozen-images
COPY --link --from=swagger /build/ /usr/local/bin/
COPY --link --from=delve /build/ /usr/local/bin/
COPY --link --from=gowinres /build/ /usr/local/bin/
COPY --link --from=tini /build/ /usr/local/bin/

View File

@@ -118,11 +118,8 @@ DELVE_PORT_FORWARD := $(if $(DELVE_PORT),-p "$(DELVE_PORT)",)
DOCKER_FLAGS := $(DOCKER) run --rm --privileged $(DOCKER_CONTAINER_NAME) $(DOCKER_ENVS) $(DOCKER_MOUNT) $(DOCKER_PORT_FORWARD) $(DELVE_PORT_FORWARD)
SWAGGER_DOCS_PORT ?= 9000
define \n
endef
# if this session isn't interactive, then we don't want to allocate a
@@ -259,20 +256,12 @@ win: bundles ## cross build the binary for windows
$(BAKE_CMD) --set *.platform=windows/amd64 binary
.PHONY: swagger-gen
swagger-gen:
docker run --rm -v $(PWD):/go/src/github.com/docker/docker \
-w /go/src/github.com/docker/docker \
--entrypoint hack/generate-swagger-api.sh \
-e GOPATH=/go \
quay.io/goswagger/swagger:0.7.4
swagger-gen: ## generate swagger API types
$(MAKE) -C api swagger-gen
.PHONY: swagger-docs
swagger-docs: ## preview the API documentation
@echo "API docs preview will be running at http://localhost:$(SWAGGER_DOCS_PORT)"
@docker run --rm -v $(PWD)/api/swagger.yaml:/usr/share/nginx/html/swagger.yaml \
-e 'REDOC_OPTIONS=hide-hostname="true" lazy-rendering' \
-p $(SWAGGER_DOCS_PORT):80 \
bfirsh/redoc:1.14.0
$(MAKE) -C api swagger-docs
.PHONY: generate-files
generate-files:

25
api/Dockerfile Normal file
View File

@@ -0,0 +1,25 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.25.4
FROM golang:${GO_VERSION}-alpine AS base
RUN apk add --no-cache bash make yamllint
CMD ["/bin/bash"]
# go-swagger
FROM base AS swagger
WORKDIR /go/src/github.com/go-swagger/go-swagger
# GO_SWAGGER_VERSION specifies the version of the go-swagger binary to install.
# Go-swagger is used in CI for generating types from swagger.yaml in
# api/scripts/generate-swagger-api.sh
ARG GO_SWAGGER_VERSION=v0.33.1
ARG TARGETPLATFORM
RUN --mount=type=cache,target=/root/.cache/go-build,id=swagger-build-$TARGETPLATFORM \
--mount=type=cache,target=/go/pkg/mod \
CGO_ENABLED=0 go install "github.com/go-swagger/go-swagger/cmd/swagger@${GO_SWAGGER_VERSION}" && \
/go/bin/swagger version
# dev is a dev-environment to work with the api module.
FROM base AS dev
COPY --from=swagger /go/bin/swagger /usr/local/bin/swagger
WORKDIR /go/src/github.com/moby/moby/api

64
api/Makefile Normal file
View File

@@ -0,0 +1,64 @@
# API Module Makefile
# This Makefile provides targets for the swagger generation and validation
# which are specific to the API module.
DOCKER ?= docker
BUILDX ?= $(DOCKER) buildx
API_DIR := $(CURDIR)
PROJECT_PATH := /go/src/github.com/moby/moby
DOCKER_MOUNT := -v "$(API_DIR):$(PROJECT_PATH)/api"
DOCKER_IMAGE := docker-api-dev
DOCKER_WORKDIR := -w $(PROJECT_PATH)/api
DOCKER_FLAGS := $(DOCKER) run --rm $(DOCKER_MOUNT) $(DOCKER_WORKDIR)
DOCKER_RUN := $(DOCKER_FLAGS) "$(DOCKER_IMAGE)"
DOCKER_CONTAINER_NAME := $(if $(CONTAINER_NAME),--name $(CONTAINER_NAME),)
BUILD_CMD := $(BUILDX) build
GO_VERSION ?= 1.25.4
SWAGGER_VERSION ?= v0.33.1
SWAGGER_DOCS_PORT ?= 9000
.DEFAULT_GOAL := help
.PHONY: build
build:
$(BUILD_CMD) \
--build-arg GO_VERSION=$(GO_VERSION) \
--build-arg SWAGGER_VERSION=$(SWAGGER_VERSION) \
--target dev \
--load \
-t "$(DOCKER_IMAGE)" \
-f Dockerfile \
.
.PHONY: swagger-gen
swagger-gen: build ## generate swagger API types
$(DOCKER_RUN) ./scripts/generate-swagger-api.sh
.PHONY: swagger-docs
swagger-docs: ## preview the API documentation
@echo "API docs preview will be running at http://localhost:$(SWAGGER_DOCS_PORT)"
@docker run --rm \
-v $(PWD)/:/usr/share/nginx/html/swagger/ \
-e 'REDOC_OPTIONS=hide-hostname="true" lazy-rendering' \
-e SPEC_URL="swagger/swagger.yaml" \
-p $(SWAGGER_DOCS_PORT):80 \
redocly/redoc:v2.5.1
.PHONY: validate-swagger
validate-swagger: build ## validate the swagger.yaml file
$(DOCKER_RUN) ./scripts/validate-swagger.sh
.PHONY: validate-swagger-gen
validate-swagger-gen: build ## validate generated types are up-to-date
$(DOCKER_RUN) ./scripts/validate-swagger-gen.sh
.PHONY: help
help: ## display this help message
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9_-]+:.*?## / {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)

View File

@@ -3,24 +3,26 @@
# -*- indent-tabs-mode: t -*-
set -eu
API_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
generate_model() {
local package="$1"
shift
mapfile
swagger generate model --spec=api/swagger.yaml \
--target=api --model-package="$package" \
--config-file=api/swagger-gen.yaml \
--template-dir=api/templates --allow-template-override \
swagger generate model --spec="${API_DIR}/swagger.yaml" \
--target="${API_DIR}" --model-package="$package" \
--config-file="${API_DIR}/swagger-gen.yaml" \
--template-dir="${API_DIR}/templates" --allow-template-override \
"$@" \
$(printf -- '--name=%s ' "${MAPFILE[@]}")
}
generate_operation() {
mapfile
swagger generate operation --spec=api/swagger.yaml \
--target=api --api-package=types --model-package=types \
--config-file=api/swagger-gen.yaml \
--template-dir=api/templates --allow-template-override \
swagger generate operation --spec="${API_DIR}/swagger.yaml" \
--target="${API_DIR}" --api-package=types --model-package=types \
--config-file="${API_DIR}/swagger-gen.yaml" \
--template-dir="${API_DIR}/templates" --allow-template-override \
"$@" \
$(printf -- '--name=%s ' "${MAPFILE[@]}")
}

View File

@@ -0,0 +1,52 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
API_DIR="${SCRIPT_DIR}/.."
TMP_DIR="$(mktemp -d)"
trap "rm -rf ${TMP_DIR}" EXIT
GEN_FILES=()
echo "Validating generated code..."
echo "Separating generated files from handwritten files..."
while IFS= read -r file; do
GEN_FILES+=("$file")
done < <(grep -rl "// Code generated" "${API_DIR}/types" || true)
echo "Copying generated files into temporary folder..."
for f in "${GEN_FILES[@]}"; do
mkdir -p "${TMP_DIR}/$(dirname "${f#${API_DIR}/}")"
cp "$f" "${TMP_DIR}/${f#${API_DIR}/}"
done
cp "${API_DIR}/swagger.yaml" "${TMP_DIR}/"
cp "${API_DIR}/swagger-gen.yaml" "${TMP_DIR}/"
cp -r "${API_DIR}/templates" "${TMP_DIR}/" 2> /dev/null || true
echo "Generating swagger types in temporary folder..."
(
cd "${TMP_DIR}"
"${SCRIPT_DIR}/generate-swagger-api.sh" > /dev/null 2>&1
)
echo "Run diff for all generated files..."
DIFF_FOUND=false
for f in "${GEN_FILES[@]}"; do
REL="${f#${API_DIR}/}"
if ! diff -q "${TMP_DIR}/${REL}" "${API_DIR}/${REL}" > /dev/null 2>&1; then
echo "Difference found in ${REL}"
diff -u "${TMP_DIR}/${REL}" "${API_DIR}/${REL}" || true
DIFF_FOUND=true
fi
done
if [ "$DIFF_FOUND" = true ]; then
echo
echo "Swagger validation failed. Please run:"
echo " ./scripts/generate-swagger-api.sh"
echo "and commit updated generated files."
exit 1
fi
echo "Swagger file is up to date."

16
api/scripts/validate-swagger.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -e
# Expected to be in api directory
cd "$(dirname "${BASH_SOURCE[0]}")/.."
echo "Validating swagger.yaml..."
yamllint -f parsable -c validate/yamllint.yaml swagger.yaml
if out=$(swagger validate swagger.yaml); then
echo "Validation done! ${out}"
else
echo "${out}" >&2
false
fi

View File

@@ -1,18 +0,0 @@
#!/usr/bin/env bash
set -e
SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPTDIR}/.validate"
IFS=$'\n'
files=($(validate_diff --diff-filter=ACMR --name-only -- 'api/swagger.yaml' || true))
unset IFS
if [ -n "${TEST_FORCE_VALIDATE:-}" ] || [ ${#files[@]} -gt 0 ]; then
yamllint -f parsable -c "${SCRIPTDIR}"/yamllint.yaml api/swagger.yaml
if out=$(swagger validate api/swagger.yaml); then
echo "Congratulations! ${out}"
else
echo "${out}" >&2
false
fi
fi

View File

@@ -1,29 +0,0 @@
#!/usr/bin/env bash
SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPTDIR}/.validate"
IFS=$'\n'
files=($(validate_diff --diff-filter=ACMR --name-only -- 'api/types/' 'api/swagger.yaml' || true))
unset IFS
if [ -n "${TEST_FORCE_VALIDATE:-}" ] || [ ${#files[@]} -gt 0 ]; then
"${SCRIPTDIR}"/../generate-swagger-api.sh 2> /dev/null
# Let see if the working directory is clean
diffs="$(git diff -- api/types/)"
if [ "$diffs" ]; then
{
echo 'The result of hack/generate-swagger-api.sh differs'
echo
echo "$diffs"
echo
echo 'Please update api/swagger.yaml with any API changes, then '
echo 'run hack/generate-swagger-api.sh.'
} >&2
false
else
echo 'Congratulations! All API changes are done the right way.'
fi
else
echo 'No api/types/ or api/swagger.yaml changes in diff.'
fi