commit ffc4152fe6f3fe28cc265f2a6cdb06a12f35d035 Author: xRain Date: Wed Oct 21 12:21:45 2026 +0800 first diff --git a/README.md b/README.md new file mode 100644 index 0000000..119c3f8 --- /dev/null +++ b/README.md @@ -0,0 +1,95 @@ +# magic-docker + +一个 action 搞定 Docker 镜像的构建与推送,支持: + +- `context`:指定构建目录(前端/后端在子目录时用) +- `dockerfile`:指定 Dockerfile 的完整路径 +- `preset`:内置方案,对应 action 根目录下的 `vue3.Dockerfile`、`vue3-hash.Dockerfile`、`dotnet.Dockerfile` + +优先级:**`dockerfile` > `preset` > `context` 下的默认 `Dockerfile`**。 +设置了 `dockerfile` 时完全忽略 `preset`;设置了 `preset` 时用对应的 Dockerfile 构建,构建完自动 push 到 registry。 + +## 内置 preset + +| preset | 对应 Dockerfile | 说明 | +| --- | --- | --- | +| `vue3` | `vue3.Dockerfile` | Vue3 + history 路由,nginx 做 `try_files` 回退并带跨域头 | +| `vue3-hash` | `vue3-hash.Dockerfile` | Vue3 + hash 路由,nginx 默认配置 | +| `dotnet` | `dotnet.Dockerfile` | .NET self-contained 发布(linux-x64) | + +- 编译全部写在 Dockerfile 里(多阶段构建),action 本身只负责传 `--build-arg VERSION=`: + - vue:`npm ci` → `npm run build-only`(没有该脚本时回退 `npm run build`)→ `npm pkg set version=` + - dotnet:`dotnet publish -r linux-x64 -c Release --self-contained -p:AssemblyName=App -p:Version=` +- `vue3` 的 nginx 配置也内联在 Dockerfile 里:`context` 下有 `nginx.conf` 就用项目的,否则用内置默认配置(history 回退 + 跨域头)。 + +## 参数 + +| 参数 | 必填 | 默认值 | 说明 | +| --- | --- | --- | --- | +| `registry` | 是 | `${{ vars.DOCKER_REGISTRY }}` | Docker registry 地址 | +| `user` | 是 | `${{ vars.DOCKER_USER }}` | registry 用户名 | +| `pass` | 是 | `${{ vars.DOCKER_PASS }}` | registry 密码 | +| `image` | 是 | `${{ github.repository }}` | 镜像名 | +| `tag` | 否 | `${{ github.ref_name }}` | 镜像 tag | +| `platform` | 否 | `linux/amd64` | 构建平台 | +| `context` | 否 | `.` | 构建目录,相对仓库根目录 | +| `dockerfile` | 否 | 空 | Dockerfile 完整路径(相对仓库根目录或绝对路径) | +| `preset` | 否 | 空 | 内置方案:`vue3` / `vue3-hash` / `dotnet` | +| `proxy` | 否 | 空 | 构建时使用的 HTTP(S) 代理 | +| `no_proxy` | 否 | 见 action.yml | 不使用代理的地址 | + +## 示例 + +### 使用 preset + +```yaml +- uses: actions/magic-docker@main + with: + context: vue + preset: vue3 +``` + +```yaml +- uses: actions/magic-docker@main + with: + context: vue + preset: vue3-hash +``` + +```yaml +- uses: actions/magic-docker@main + with: + context: backend + preset: dotnet +``` + +### 使用自己的 Dockerfile + +指定 `dockerfile` 后 `preset` 会被忽略: + +```yaml +- uses: actions/magic-docker@main + with: + context: backend + dockerfile: backend/Dockerfile + platform: linux/arm64 +``` + +### 只给 context(默认用 context/Dockerfile) + +```yaml +- uses: actions/magic-docker@main + with: + context: . +``` + +### 指定镜像名与 tag + +```yaml +- uses: actions/magic-docker@main + with: + context: vue + preset: vue3 + image: simcu/web + tag: ${{ github.ref_name }} +``` diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..485c24d --- /dev/null +++ b/action.yml @@ -0,0 +1,104 @@ +# action.yml +name: "magic docker build and push" +description: "一键构建并推送 Docker 镜像:支持自定义 context / dockerfile,或使用内置 preset(vue3、vue3-hash、dotnet)" + +inputs: + registry: + description: "Docker registry URL" + required: true + default: "${{ vars.DOCKER_REGISTRY }}" + user: + description: "Docker registry username" + required: true + default: "${{ vars.DOCKER_USER }}" + pass: + description: "Docker registry password" + required: true + default: "${{ vars.DOCKER_PASS }}" + image: + description: "Docker image name" + required: true + default: "${{ github.repository }}" + tag: + description: "Docker image tag (default: github.ref_name)" + required: false + default: "${{ github.ref_name }}" + platform: + description: "Build platform (default: linux/amd64)" + required: false + default: "linux/amd64" + context: + description: "构建目录,相对仓库根目录(默认 .)" + required: false + default: "." + dockerfile: + description: "Dockerfile 的完整路径,相对仓库根目录或绝对路径。设置后忽略 preset(默认留空)" + required: false + default: "" + preset: + description: "内置方案:vue3 / vue3-hash / dotnet。未设置 dockerfile 时生效(默认留空)" + required: false + default: "" + +runs: + using: "composite" + steps: + # 编译、nginx 配置都在 preset 的 Dockerfile 里(多阶段构建) + # 优先级:dockerfile > preset > context 下的默认 Dockerfile + - name: build docker image and push + uses: docker://docker:git + env: + DOCKER_BUILDKIT: 1 + REGISTRY: ${{ inputs.registry }} + REG_USER: ${{ inputs.user }} + REG_PASS: ${{ inputs.pass }} + IMAGE: ${{ inputs.image }} + TAG: ${{ inputs.tag }} + PLATFORM: ${{ inputs.platform }} + CONTEXT_DIR: ${{ inputs.context }} + DOCKERFILE: ${{ inputs.dockerfile }} + PRESET: ${{ inputs.preset }} + ACTION_PATH: ${{ github.action_path }} + with: + entrypoint: "/bin/sh" + args: | + -c ' + set -e + image="$REGISTRY/$IMAGE:$TAG" + dockerfile_path="${DOCKERFILE:-}" + preset="${PRESET:-}" + build_args="" + + if [ -n "$dockerfile_path" ]; then + if [ -n "$preset" ]; then + echo "已指定 dockerfile($dockerfile_path),忽略 preset($preset)" + fi + elif [ -n "$preset" ]; then + dockerfile_path="$ACTION_PATH/$preset.Dockerfile" + if [ ! -f "$dockerfile_path" ]; then + echo "未知 preset:$preset(可选:vue3 / vue3-hash / dotnet)" + exit 1 + fi + # preset 的 Dockerfile 用 VERSION 覆盖 package.json 版本 / dotnet Version + build_args="VERSION=$TAG" + echo "使用 preset:$preset" + else + dockerfile_path="$CONTEXT_DIR/Dockerfile" + fi + + echo "构建目录:$CONTEXT_DIR" + echo "Dockerfile:$dockerfile_path" + echo "镜像:$image" + + set -- --platform "$PLATFORM" -t "$image" -f "$dockerfile_path" + if [ -n "$build_args" ]; then + set -- --build-arg "$build_args" "$@" + fi + + docker ps && docker info && \ + docker build "$@" "$CONTEXT_DIR" && \ + echo "$image 已经构建完了" && \ + docker login "$REGISTRY" -u "$REG_USER" -p "$REG_PASS" && \ + docker push "$image" && \ + echo "$image 推送成功" + ' diff --git a/dotnet.Dockerfile b/dotnet.Dockerfile new file mode 100644 index 0000000..3ed78ba --- /dev/null +++ b/dotnet.Dockerfile @@ -0,0 +1,17 @@ +# 多阶段构建:先在 SDK 里 self-contained 发布,最终镜像只保留 runtime + 发布产物 +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build + +# 由 magic-docker 传入:--build-arg VERSION= +ARG VERSION=0.0.0 +WORKDIR /src + +# 构建上下文就是 context 目录 +COPY . . +RUN dotnet publish -r linux-x64 -c Release --self-contained -o /out \ + -p:AssemblyName=App -p:Version="$VERSION" + +FROM mcr.microsoft.com/dotnet/runtime:10.0 +ENV TZ Asia/Shanghai +COPY --from=build /out /home +WORKDIR /home +ENTRYPOINT ["./App"] diff --git a/vue3-hash.Dockerfile b/vue3-hash.Dockerfile new file mode 100644 index 0000000..7693b67 --- /dev/null +++ b/vue3-hash.Dockerfile @@ -0,0 +1,21 @@ +# 多阶段构建:hash 路由不需要 nginx 回退,最终镜像只保留 nginx + dist +FROM node:24 AS build + +# 由 magic-docker 传入:--build-arg VERSION= +ARG VERSION=0.0.0 +WORKDIR /app + +# 构建上下文就是 context 目录 +COPY . . +RUN npm ci +RUN npm pkg set version="$VERSION" + +# 有 build-only 脚本就用它(兼容老项目),否则用 build +RUN if npm run 2>/dev/null | grep -q "build-only"; then \ + npm run build-only; \ + else \ + npm run build; \ + fi + +FROM nginx:alpine +COPY --from=build /app/dist /usr/share/nginx/html diff --git a/vue3.Dockerfile b/vue3.Dockerfile new file mode 100644 index 0000000..00dd86b --- /dev/null +++ b/vue3.Dockerfile @@ -0,0 +1,51 @@ +# 多阶段构建:先在 node 里编译,最终镜像只保留 nginx + dist +FROM node:24 AS build + +# 由 magic-docker 传入:--build-arg VERSION= +ARG VERSION=0.0.0 +WORKDIR /app + +# 构建上下文就是 context 目录 +COPY . . +RUN npm ci +RUN npm pkg set version="$VERSION" + +# 有 build-only 脚本就用它(兼容老项目),否则用 build +RUN if npm run 2>/dev/null | grep -q "build-only"; then \ + npm run build-only; \ + else \ + npm run build; \ + fi + +# history 路由回退 + 跨域:context 里有 nginx.conf 就用项目的,否则用内置默认配置 +RUN set -e; \ + if [ -f nginx.conf ]; then \ + cp nginx.conf /app/nginx.conf; \ + echo "使用项目自带的 nginx.conf"; \ + else \ + printf '%s\n' \ + 'server {' \ + ' listen 80;' \ + ' root /usr/share/nginx/html;' \ + ' index index.html;' \ + '' \ + ' add_header Access-Control-Allow-Origin * always;' \ + ' add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE" always;' \ + ' add_header Access-Control-Allow-Headers "*, Origin, X-Requested-With, Content-Type, Accept, Authorization, X-Token" always;' \ + ' add_header Access-Control-Allow-Credentials "true" always;' \ + '' \ + ' if ($request_method = "OPTIONS") {' \ + ' return 204;' \ + ' }' \ + '' \ + ' location / {' \ + ' try_files $uri $uri/ /index.html;' \ + ' }' \ + '}' \ + > /app/nginx.conf; \ + echo "使用内置的 nginx.conf"; \ + fi + +FROM nginx:alpine +COPY --from=build /app/dist /usr/share/nginx/html +COPY --from=build /app/nginx.conf /etc/nginx/conf.d/default.conf