From a50747bfccbcace8821fb927f88b80ed9a522873 Mon Sep 17 00:00:00 2001 From: aoirint Date: Tue, 11 Aug 2026 10:36:20 +0900 Subject: [PATCH 1/3] ci: validate and build Docker changes Add pull-request and integration workflows that lint with checksum-verified hadolint before building. Publish releases only after the exact integrated image passes its required gates. Co-authored-by: Codex --- .github/actions/lint-docker/action.yml | 24 +++ .github/workflows/build.yml | 248 ------------------------- .github/workflows/main.yml | 174 +++++++++++++++++ .github/workflows/pull-request.yml | 47 +++++ Dockerfile | 2 +- 5 files changed, 246 insertions(+), 249 deletions(-) create mode 100644 .github/actions/lint-docker/action.yml delete mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/pull-request.yml diff --git a/.github/actions/lint-docker/action.yml b/.github/actions/lint-docker/action.yml new file mode 100644 index 0000000..31c1399 --- /dev/null +++ b/.github/actions/lint-docker/action.yml @@ -0,0 +1,24 @@ +name: Lint Docker source +description: Install a verified hadolint binary and lint the repository Dockerfile. + +runs: + using: composite + steps: + - name: Install checksum-verified hadolint + shell: bash + env: + HADOLINT_VERSION: v2.15.1 + HADOLINT_SHA256: c7187db94eeeeca956519a6af171adc31453941a1e777961f6e680f697c8c507 + run: |- + install_dir="${RUNNER_TEMP}/hadolint/bin" + asset="hadolint-linux-x86_64" + mkdir -p "${install_dir}" + curl --fail --location --silent --show-error \ + --output "${install_dir}/${asset}" \ + "https://github.com/hadolint/hadolint/releases/download/${HADOLINT_VERSION}/${asset}" + echo "${HADOLINT_SHA256} ${install_dir}/${asset}" | sha256sum --check --strict + chmod 0755 "${install_dir}/${asset}" + echo "${install_dir}" >> "${GITHUB_PATH}" + - name: Lint Dockerfile + shell: bash + run: hadolint Dockerfile diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index a6c5063..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,248 +0,0 @@ -name: Build - -on: - push: - branches: - - main - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: false - -env: - IMAGE_NAME: aoirint/sd_scripts - GHCR_IMAGE_NAME: ghcr.io/aoirint/sd_scripts - -jobs: - version: - runs-on: ubuntu-slim - - permissions: - contents: read - - outputs: - release_mode: ${{ steps.version.outputs.release_mode }} - tag: ${{ steps.version.outputs.tag }} - - steps: - - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - - name: Read release version - id: version - run: |- - version="$(tr -d '[:space:]' < VERSION)" - - if [[ ! "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then - echo "VERSION must be stable SemVer like 0.1.0 or prerelease SemVer like 0.1.0-rc.1" >&2 - exit 1 - fi - - tag="v${version}" - tag_exists=false - - if git ls-remote --exit-code --tags origin "refs/tags/${tag}" > /dev/null 2>&1; then - tag_exists=true - fi - - if [[ "${tag_exists}" == "true" ]]; then - release_mode=edge - elif [[ "${version}" == "0.0.0" ]]; then - release_mode=edge - elif [[ "${version}" == *-* ]]; then - release_mode=prerelease - else - release_mode=latest - fi - - { - echo "tag_exists=${tag_exists}" - echo "version=${version}" - echo "tag=${tag}" - echo "release_mode=${release_mode}" - } >> "$GITHUB_OUTPUT" - - release: - needs: - - version - if: ${{ needs.version.outputs.release_mode != 'edge' }} - - runs-on: ubuntu-slim - - permissions: - contents: write - - steps: - - name: Publish release - # Create immutable releases so an already-published version cannot be - # silently replaced by a later workflow run for the same tag. - uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1.21.0 - with: - tag: ${{ needs.version.outputs.tag }} - commit: ${{ github.sha }} - immutableCreate: true - prerelease: ${{ needs.version.outputs.release_mode == 'prerelease' }} - makeLatest: ${{ needs.version.outputs.release_mode == 'latest' }} - generateReleaseNotes: true - - build-docker: - needs: - - version - - release - # Keep the original release flow: create the GitHub release first, then - # publish Docker tags for that commit. Edge builds intentionally skip the - # release job, so the Docker publish path must accept either success or - # skipped here. - if: ${{ always() && needs.version.result == 'success' && (needs.release.result == 'success' || needs.release.result == 'skipped') }} - - runs-on: ubuntu-latest - - permissions: - contents: read - packages: write - - steps: - - name: Free disk space - run: |- - # https://github.com/actions/runner-images/issues/2840#issuecomment-2272410832 - sudo rm -rf \ - "$AGENT_TOOLSDIRECTORY" \ - /opt/google/chrome \ - /opt/microsoft/msedge \ - /opt/microsoft/powershell \ - /opt/pipx \ - /usr/lib/mono \ - /usr/local/julia* \ - /usr/local/lib/android \ - /usr/local/lib/node_modules \ - /usr/local/share/chromium \ - /usr/local/share/powershell \ - /usr/share/dotnet \ - /usr/share/swift - df -h / - - - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - - name: Setup Docker Buildx - id: buildx - uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 - - - name: Docker metadata - id: meta - uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 - with: - images: | - ${{ env.IMAGE_NAME }} - ${{ env.GHCR_IMAGE_NAME }} - flavor: | - latest=false - tags: | - type=raw,value=edge,enable=${{ needs.version.outputs.release_mode != 'latest' }} - type=raw,value=${{ needs.version.outputs.tag }},enable=${{ needs.version.outputs.release_mode == 'latest' }} - type=raw,value=latest,enable=${{ needs.version.outputs.release_mode == 'latest' }} - - - name: Derive build cache refs - id: cache - run: |- - if [[ "${{ needs.version.outputs.release_mode }}" == "latest" ]]; then - { - echo "cache_from<> "$GITHUB_OUTPUT" - else - { - echo "cache_from=type=registry,ref=${{ env.GHCR_IMAGE_NAME }}:edge-buildcache" - echo "cache_to=type=registry,ref=${{ env.GHCR_IMAGE_NAME }}:edge-buildcache,mode=max" - } >> "$GITHUB_OUTPUT" - fi - - - name: Login to ghcr.io - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Login to Docker Hub - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 - with: - username: ${{ vars.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Build and Deploy Docker image - uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 - with: - builder: ${{ steps.buildx.outputs.name }} - context: . - file: ./Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - cache-from: ${{ steps.cache.outputs.cache_from }} - cache-to: ${{ steps.cache.outputs.cache_to }} - labels: ${{ steps.meta.outputs.labels }} - annotations: ${{ steps.meta.outputs.annotations }} - - test-docker: - needs: - - version - - build-docker - if: ${{ always() && needs.version.result == 'success' && needs.build-docker.result == 'success' }} - - runs-on: ubuntu-latest - - permissions: - contents: read - packages: read - - steps: - - name: Free disk space - run: |- - # https://github.com/actions/runner-images/issues/2840#issuecomment-2272410832 - sudo rm -rf \ - "$AGENT_TOOLSDIRECTORY" \ - /opt/google/chrome \ - /opt/microsoft/msedge \ - /opt/microsoft/powershell \ - /opt/pipx \ - /usr/lib/mono \ - /usr/local/julia* \ - /usr/local/lib/android \ - /usr/local/lib/node_modules \ - /usr/local/share/chromium \ - /usr/local/share/powershell \ - /usr/share/dotnet \ - /usr/share/swift - df -h / - - - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - - name: Login to ghcr.io - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Pull built Docker image - id: image - run: |- - # Test the image tag that build-docker just pushed, not a second local - # build. Latest releases are immutable version tags; edge builds reuse - # the moving edge tag because VERSION either already exists or is 0.0.0. - if [[ "${{ needs.version.outputs.release_mode }}" == "latest" ]]; then - image="${{ env.GHCR_IMAGE_NAME }}:${{ needs.version.outputs.tag }}" - else - image="${{ env.GHCR_IMAGE_NAME }}:edge" - fi - - docker pull "${image}" - echo "image=${image}" >> "$GITHUB_OUTPUT" - - - name: Run upstream sd-scripts pytest release tests - run: |- - scripts/run-sd-scripts-release-tests.sh --image "${{ steps.image.outputs.image }}" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..b2e23d2 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,174 @@ +name: Main + +on: + push: + branches: [main] + +permissions: + contents: read + +concurrency: + group: main-${{ github.ref }} + cancel-in-progress: false + +env: + IMAGE_NAME: aoirint/sd_scripts + GHCR_IMAGE_NAME: ghcr.io/aoirint/sd_scripts + +jobs: + lint: + runs-on: ubuntu-slim + timeout-minutes: 5 + steps: + - name: Checkout integrated source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Lint Docker source + uses: ./.github/actions/lint-docker + + plan: + runs-on: ubuntu-slim + timeout-minutes: 5 + outputs: + release_mode: ${{ steps.version.outputs.release_mode }} + tag: ${{ steps.version.outputs.tag }} + steps: + - name: Checkout integrated source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Read release version + id: version + run: |- + version="$(tr -d '[:space:]' < VERSION)" + if [[ ! "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then + echo "VERSION must contain stable or prerelease SemVer" >&2 + exit 1 + fi + tag="v${version}" + if [[ "${version}" == "0.0.0" ]] || git ls-remote --exit-code --tags origin "refs/tags/${tag}" >/dev/null 2>&1; then + release_mode=edge + elif [[ "${version}" == *-* ]]; then + release_mode=prerelease + else + release_mode=latest + fi + echo "tag=${tag}" >> "$GITHUB_OUTPUT" + echo "release_mode=${release_mode}" >> "$GITHUB_OUTPUT" + + build: + needs: [lint, plan] + runs-on: ubuntu-24.04 + permissions: + contents: read + packages: write + steps: + - name: Free disk space for image build + run: |- + sudo rm -rf "$AGENT_TOOLSDIRECTORY" /opt/google/chrome /opt/microsoft/msedge /opt/microsoft/powershell /opt/pipx /usr/lib/mono /usr/local/julia* /usr/local/lib/android /usr/local/lib/node_modules /usr/local/share/chromium /usr/local/share/powershell /usr/share/dotnet /usr/share/swift + df -h / + - name: Checkout integrated source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 + - name: Derive Docker metadata + id: meta + uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 + with: + images: | + ${{ env.IMAGE_NAME }} + ${{ env.GHCR_IMAGE_NAME }} + flavor: latest=false + tags: | + type=raw,value=edge,enable=${{ needs.plan.outputs.release_mode != 'latest' }} + type=raw,value=${{ needs.plan.outputs.tag }},enable=${{ needs.plan.outputs.release_mode == 'latest' }} + type=raw,value=latest,enable=${{ needs.plan.outputs.release_mode == 'latest' }} + - name: Derive build cache references + id: cache + run: |- + if [[ "${{ needs.plan.outputs.release_mode }}" == "latest" ]]; then + echo "cache_from=type=registry,ref=${{ env.GHCR_IMAGE_NAME }}:edge-buildcache" >> "$GITHUB_OUTPUT" + echo "cache_to=type=registry,ref=${{ env.GHCR_IMAGE_NAME }}:buildcache,mode=max" >> "$GITHUB_OUTPUT" + else + echo "cache_from=type=registry,ref=${{ env.GHCR_IMAGE_NAME }}:edge-buildcache" >> "$GITHUB_OUTPUT" + echo "cache_to=type=registry,ref=${{ env.GHCR_IMAGE_NAME }}:edge-buildcache,mode=max" >> "$GITHUB_OUTPUT" + fi + - name: Log in to GitHub Container Registry + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Log in to Docker Hub + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + username: ${{ vars.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build and publish Docker image + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + with: + builder: ${{ steps.buildx.outputs.name }} + context: . + file: ./Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + cache-from: ${{ steps.cache.outputs.cache_from }} + cache-to: ${{ steps.cache.outputs.cache_to }} + labels: ${{ steps.meta.outputs.labels }} + annotations: ${{ steps.meta.outputs.annotations }} + + test: + needs: [build, plan] + runs-on: ubuntu-24.04 + permissions: + contents: read + packages: read + steps: + - name: Free disk space for release tests + run: |- + sudo rm -rf "$AGENT_TOOLSDIRECTORY" /opt/google/chrome /opt/microsoft/msedge /opt/microsoft/powershell /opt/pipx /usr/lib/mono /usr/local/julia* /usr/local/lib/android /usr/local/lib/node_modules /usr/local/share/chromium /usr/local/share/powershell /usr/share/dotnet /usr/share/swift + df -h / + - name: Checkout integrated source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Log in to GitHub Container Registry + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Pull the exact published image + id: image + run: |- + if [[ "${{ needs.plan.outputs.release_mode }}" == "latest" ]]; then + image="${{ env.GHCR_IMAGE_NAME }}:${{ needs.plan.outputs.tag }}" + else + image="${{ env.GHCR_IMAGE_NAME }}:edge" + fi + docker pull "${image}" + echo "image=${image}" >> "$GITHUB_OUTPUT" + - name: Run upstream sd-scripts release tests + run: scripts/run-sd-scripts-release-tests.sh --image "${{ steps.image.outputs.image }}" + + release: + needs: [plan, test] + if: ${{ needs.plan.outputs.release_mode != 'edge' }} + runs-on: ubuntu-slim + timeout-minutes: 5 + permissions: + contents: write + steps: + - name: Publish immutable GitHub release + uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1.21.0 + with: + tag: ${{ needs.plan.outputs.tag }} + commit: ${{ github.sha }} + immutableCreate: true + prerelease: ${{ needs.plan.outputs.release_mode == 'prerelease' }} + makeLatest: ${{ needs.plan.outputs.release_mode == 'latest' }} + generateReleaseNotes: true diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 0000000..2101fcd --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,47 @@ +name: Pull Request + +on: + pull_request: + branches: [main] + merge_group: + types: [checks_requested] + +permissions: + contents: read + +concurrency: + group: pull-request-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + lint: + runs-on: ubuntu-slim + timeout-minutes: 5 + steps: + - name: Checkout proposed source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Lint Docker source + uses: ./.github/actions/lint-docker + + build: + needs: [lint] + runs-on: ubuntu-24.04 + steps: + - name: Free disk space for image build + run: |- + sudo rm -rf "$AGENT_TOOLSDIRECTORY" /opt/google/chrome /opt/microsoft/msedge /opt/microsoft/powershell /opt/pipx /usr/lib/mono /usr/local/julia* /usr/local/lib/android /usr/local/lib/node_modules /usr/local/share/chromium /usr/local/share/powershell /usr/share/dotnet /usr/share/swift + df -h / + - name: Checkout proposed source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 + - name: Build Docker image without publishing + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + with: + context: . + file: ./Dockerfile + push: false diff --git a/Dockerfile b/Dockerfile index 08de417..96bc466 100644 --- a/Dockerfile +++ b/Dockerfile @@ -135,6 +135,6 @@ EOT chown -R trainer:trainer /huggingface /opt/sd-scripts SH -USER trainer +USER 1000:1000 ENTRYPOINT ["accelerate", "launch"] From 3b8645892eedcd2d76244d315b46c51cc26b4c6f Mon Sep 17 00:00:00 2001 From: aoirint Date: Tue, 11 Aug 2026 10:38:12 +0900 Subject: [PATCH 2/3] fix(ci): expose hadolint command Rename the verified release asset to the command name used by the following composite-action step. Co-authored-by: Codex --- .github/actions/lint-docker/action.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/lint-docker/action.yml b/.github/actions/lint-docker/action.yml index 31c1399..d572103 100644 --- a/.github/actions/lint-docker/action.yml +++ b/.github/actions/lint-docker/action.yml @@ -17,7 +17,8 @@ runs: --output "${install_dir}/${asset}" \ "https://github.com/hadolint/hadolint/releases/download/${HADOLINT_VERSION}/${asset}" echo "${HADOLINT_SHA256} ${install_dir}/${asset}" | sha256sum --check --strict - chmod 0755 "${install_dir}/${asset}" + mv "${install_dir}/${asset}" "${install_dir}/hadolint" + chmod 0755 "${install_dir}/hadolint" echo "${install_dir}" >> "${GITHUB_PATH}" - name: Lint Dockerfile shell: bash From a5e76793bdf9f3572470b0765abafdf46e363f12 Mon Sep 17 00:00:00 2001 From: aoirint Date: Tue, 11 Aug 2026 10:41:38 +0900 Subject: [PATCH 3/3] perf(ci): keep pull request checks lightweight Limit proposed-source validation to hadolint and reserve network-intensive Docker builds for integrated main-branch commits. Co-authored-by: Codex --- .github/workflows/pull-request.yml | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 2101fcd..209f6a0 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -2,9 +2,11 @@ name: Pull Request on: pull_request: - branches: [main] + branches: + - main merge_group: - types: [checks_requested] + types: + - checks_requested permissions: contents: read @@ -17,31 +19,12 @@ jobs: lint: runs-on: ubuntu-slim timeout-minutes: 5 + steps: - name: Checkout proposed source uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false + - name: Lint Docker source uses: ./.github/actions/lint-docker - - build: - needs: [lint] - runs-on: ubuntu-24.04 - steps: - - name: Free disk space for image build - run: |- - sudo rm -rf "$AGENT_TOOLSDIRECTORY" /opt/google/chrome /opt/microsoft/msedge /opt/microsoft/powershell /opt/pipx /usr/lib/mono /usr/local/julia* /usr/local/lib/android /usr/local/lib/node_modules /usr/local/share/chromium /usr/local/share/powershell /usr/share/dotnet /usr/share/swift - df -h / - - name: Checkout proposed source - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 - - name: Build Docker image without publishing - uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 - with: - context: . - file: ./Dockerfile - push: false