mirror of
https://github.com/moby/moby.git
synced 2026-01-11 10:41:43 +00:00
Merge pull request #51300 from rafaelcamelo31/50486_api_move_scripts
api: move scripts to generate and validate swagger to api module
This commit is contained in:
25
api/Dockerfile
Normal file
25
api/Dockerfile
Normal 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
64
api/Makefile
Normal 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)
|
||||
127
api/scripts/generate-swagger-api.sh
Executable file
127
api/scripts/generate-swagger-api.sh
Executable file
@@ -0,0 +1,127 @@
|
||||
#!/bin/bash
|
||||
# vim: set noexpandtab:
|
||||
# -*- 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_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_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[@]}")
|
||||
}
|
||||
|
||||
# /==================================================================\
|
||||
# | |
|
||||
# | ATTENTION: |
|
||||
# | |
|
||||
# | Sort model package stanzas and model/operation names |
|
||||
# | *** ALPHABETICALLY *** |
|
||||
# | to reduce the likelihood of merge conflicts. |
|
||||
# | |
|
||||
# \==================================================================/
|
||||
|
||||
#region -------- Models --------
|
||||
|
||||
generate_model types/build <<- 'EOT'
|
||||
BuildCacheDiskUsage
|
||||
EOT
|
||||
|
||||
generate_model types/common <<- 'EOT'
|
||||
ErrorResponse
|
||||
IDResponse
|
||||
EOT
|
||||
|
||||
generate_model types/container <<- 'EOT'
|
||||
ChangeType
|
||||
ContainerCreateResponse
|
||||
ContainerTopResponse
|
||||
ContainerUpdateResponse
|
||||
ContainerWaitExitError
|
||||
ContainerWaitResponse
|
||||
ContainersDiskUsage
|
||||
FilesystemChange
|
||||
PortSummary
|
||||
EOT
|
||||
|
||||
generate_model types/image <<- 'EOT'
|
||||
ImageDeleteResponseItem
|
||||
ImagesDiskUsage
|
||||
EOT
|
||||
# ImageSummary
|
||||
# TODO: Restore when go-swagger is updated
|
||||
# See https://github.com/moby/moby/pull/47526#discussion_r1551800022
|
||||
|
||||
generate_model types/network --keep-spec-order --additional-initialism=IPAM <<- 'EOT'
|
||||
ConfigReference
|
||||
EndpointResource
|
||||
IPAMStatus
|
||||
Network
|
||||
NetworkConnectRequest
|
||||
NetworkCreateResponse
|
||||
NetworkDisconnectRequest
|
||||
NetworkInspect
|
||||
NetworkStatus
|
||||
NetworkSummary
|
||||
NetworkTaskInfo
|
||||
PeerInfo
|
||||
ServiceInfo
|
||||
SubnetStatus
|
||||
EOT
|
||||
|
||||
generate_model types/plugin <<- 'EOT'
|
||||
Plugin
|
||||
PluginDevice
|
||||
PluginEnv
|
||||
PluginMount
|
||||
EOT
|
||||
|
||||
generate_model types/registry <<- 'EOT'
|
||||
AuthResponse
|
||||
EOT
|
||||
|
||||
generate_model types/storage <<- 'EOT'
|
||||
DriverData
|
||||
RootFSStorage
|
||||
RootFSStorageSnapshot
|
||||
Storage
|
||||
EOT
|
||||
|
||||
generate_model types/swarm <<- 'EOT'
|
||||
ServiceCreateResponse
|
||||
ServiceUpdateResponse
|
||||
EOT
|
||||
|
||||
generate_model types/volume <<- 'EOT'
|
||||
Volume
|
||||
VolumeCreateRequest
|
||||
VolumeListResponse
|
||||
VolumesDiskUsage
|
||||
EOT
|
||||
|
||||
#endregion
|
||||
|
||||
#region -------- Operations --------
|
||||
|
||||
generate_operation --skip-responses --skip-parameters <<- 'EOT'
|
||||
Authenticate
|
||||
ImageHistory
|
||||
EOT
|
||||
|
||||
#endregion
|
||||
52
api/scripts/validate-swagger-gen.sh
Executable file
52
api/scripts/validate-swagger-gen.sh
Executable 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
16
api/scripts/validate-swagger.sh
Executable 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
|
||||
29
api/validate/yamllint
Executable file
29
api/validate/yamllint
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPTDIR}/.validate"
|
||||
|
||||
if [ -n "${TEST_FORCE_VALIDATE:-}" ]; then
|
||||
files=(docs/api/*.yaml)
|
||||
else
|
||||
IFS=$'\n'
|
||||
files=($(validate_diff --diff-filter=ACMR --name-only -- docs/*.yaml || true))
|
||||
unset IFS
|
||||
fi
|
||||
|
||||
# validate the yamllint configuration file before anything else
|
||||
if out=$(yamllint -f parsable -d "{extends: default, rules: {document-start: disable}}" "${SCRIPTDIR}"/yamllint.yaml); then
|
||||
echo "Congratulations! yamllint config file formatted correctly"
|
||||
else
|
||||
echo "${out}" >&2
|
||||
false
|
||||
fi
|
||||
|
||||
# Then validate GitHub actions workflows, and conditionally lint the swagger
|
||||
# files in the docs directory, as these are large files and take some time.
|
||||
if out=$(yamllint -f parsable -c "${SCRIPTDIR}"/yamllint.yaml .github/workflows/*.yml "${files[@]}"); then
|
||||
echo "Congratulations! YAML files are formatted correctly"
|
||||
else
|
||||
echo "${out}" >&2
|
||||
false
|
||||
fi
|
||||
10
api/validate/yamllint.yaml
Normal file
10
api/validate/yamllint.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
extends: default
|
||||
locale: C.UTF-8
|
||||
rules:
|
||||
document-start: disable
|
||||
line-length: disable
|
||||
# ignore "warning truthy value should be one of [false, true]" on GitHub
|
||||
# actions workflows, which use "on:" to specify when to run.
|
||||
truthy:
|
||||
ignore: |
|
||||
.github/workflows/
|
||||
Reference in New Issue
Block a user