just-shield 0.2.0

Pre-execution supply-chain scanner for GitHub Actions workflows
Documentation
# just-shield 릴리스 자동화 (ADR-0005 단계 ⓪).
# v* 태그 푸시 → ① 채점표 게이트 → ② 6종 대상 병렬 빌드 → ③ SHA256SUMS + 빌드 출처 증명 → ④ 릴리스 첨부.
# 서드파티 빌드 도구·액션 없이 직접 작성한다 — 공식 바이너리를 만드는 사슬에 서드파티를 넣지 않는다.
# 이 워크플로 자체가 우리 규칙(SHA 핀, 최소 권한)을 지켜 dogfood 검사를 통과해야 한다.
name: Release
on:
  push:
    tags: ["v*"]
permissions:
  contents: read
jobs:
  # ① 게이트가 깨지면 빌드도 릴리스도 없다 — 테스트 깨진 커밋에 태그를 붙여도 안전.
  gate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
      - name: 태그와 Cargo.toml 버전 일치 확인
        run: |

          crate_version="$(grep -m1 '^version' Cargo.toml | cut -d '"' -f 2)"
          if [ "v${crate_version}" != "${GITHUB_REF_NAME}" ]; then
            echo "태그 ${GITHUB_REF_NAME} ≠ Cargo.toml v${crate_version} — 버전을 맞추고 다시 태그하세요" >&2
            exit 1
          fi
      - run: cargo fmt --check
      - run: cargo clippy --all-targets -- -D warnings
      - name: 채점표 게이트 (유닛 + 통합 + 코퍼스)
        run: cargo test
      - name: 자기 검사 (dogfood)
        run: cargo run --quiet -- scan . --strict

  # ② 6종 대상 병렬 빌드. Linux는 musl 정적 링크 — 시스템 라이브러리 의존 0.
  build:
    needs: gate
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-unknown-linux-musl
            runner: ubuntu-latest
          - target: aarch64-unknown-linux-musl
            runner: ubuntu-24.04-arm
          - target: x86_64-apple-darwin
            runner: macos-latest
          - target: aarch64-apple-darwin
            runner: macos-latest
          - target: x86_64-pc-windows-msvc
            runner: windows-latest
          - target: aarch64-pc-windows-msvc
            runner: windows-latest
    runs-on: ${{ matrix.runner }}
    steps:
      - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
      - name: musl 링커 설치 (Linux)
        if: contains(matrix.target, 'musl')
        run: sudo apt-get update && sudo apt-get install -y musl-tools
      - run: rustup target add ${{ matrix.target }}
      - run: cargo build --release --locked --target ${{ matrix.target }}
      - name: 아카이브 작성 (tar.gz)
        if: runner.os != 'Windows'
        run: |

          name="just-shield-${GITHUB_REF_NAME}-${{ matrix.target }}"
          mkdir "${name}"
          cp "target/${{ matrix.target }}/release/just-shield" README.md LICENSE-MIT LICENSE-APACHE "${name}/"
          tar -czf "${name}.tar.gz" "${name}"
      - name: 아카이브 작성 (zip)
        if: runner.os == 'Windows'
        shell: pwsh
        run: |

          $name = "just-shield-$env:GITHUB_REF_NAME-${{ matrix.target }}"
          New-Item -ItemType Directory $name | Out-Null
          Copy-Item "target/${{ matrix.target }}/release/just-shield.exe", "README.md", "LICENSE-MIT", "LICENSE-APACHE" $name
          Compress-Archive -Path $name -DestinationPath "$name.zip"
      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: ${{ matrix.target }}
          path: |

            just-shield-*.tar.gz
            just-shield-*.zip
          if-no-files-found: error

  # ③+④ 체크섬 → 출처 증명 → 릴리스. 쓰기 권한은 이 잡에만 부여한다.
  release:
    needs: build
    runs-on: ubuntu-latest
    permissions:
      contents: write
      id-token: write
      attestations: write
    steps:
      - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          path: dist
          merge-multiple: true
      - name: SHA256SUMS 생성
        working-directory: dist
        run: sha256sum * > SHA256SUMS
      - name: 빌드 출처 증명 발급
        uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
        with:
          subject-path: dist/*
      - name: GitHub 릴리스 생성
        env:
          GH_TOKEN: ${{ github.token }}
        run: |

          gh release create "${GITHUB_REF_NAME}" dist/* \
            --repo "${GITHUB_REPOSITORY}" \
            --title "${GITHUB_REF_NAME}" \
            --generate-notes

  # ③' 컨테이너 이미지 (ADR-0005 단계 ③) — FROM scratch + musl 정적 바이너리.
  # 서드파티 액션 없이 러너 내장 docker buildx CLI만 쓴다. RUN 없는 COPY 전용
  # Dockerfile이라 에뮬레이션 없이 amd64/arm64 멀티 아치 빌드가 된다.
  image:
    needs: release
    runs-on: ubuntu-latest
    outputs:
      digest: ${{ steps.push.outputs.digest }}
    permissions:
      contents: write # 릴리스 노트에 다이제스트 기록
      packages: write # ghcr.io 푸시
    steps:
      - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
      - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          path: dist
          merge-multiple: true
      - name: 빌드 컨텍스트 구성 (아치별 바이너리 배치)
        run: |

          mkdir -p ctx/amd64 ctx/arm64
          for pair in "x86_64:amd64" "aarch64:arm64"; do
            triple="${pair%%:*}-unknown-linux-musl"
            arch="${pair##*:}"
            name="just-shield-${GITHUB_REF_NAME}-${triple}"
            tar -xzf "dist/${name}.tar.gz"
            mv "${name}/just-shield" "ctx/${arch}/just-shield"
          done
          cp Dockerfile ctx/
      - name: ghcr.io 로그인
        run: echo "${{ github.token }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
      - name: 멀티 아치 빌드 + 푸시
        id: push
        run: |

          docker buildx create --use
          docker buildx build \
            --platform linux/amd64,linux/arm64 \
            --tag "ghcr.io/kihyun1998/just-shield:${GITHUB_REF_NAME}" \
            --tag "ghcr.io/kihyun1998/just-shield:latest" \
            --provenance=false \
            --metadata-file meta.json \
            --push \
            ctx
          digest="$(jq -r '."containerimage.digest"' meta.json)"
          echo "digest=${digest}" >> "$GITHUB_OUTPUT"
      - name: 릴리스 노트에 다이제스트 기록
        env:
          GH_TOKEN: ${{ github.token }}
          DIGEST: ${{ steps.push.outputs.digest }}
        run: |

          gh release view "${GITHUB_REF_NAME}" --json body --jq .body > notes.md
          printf '\n## 컨테이너 이미지\n\n`ghcr.io/kihyun1998/just-shield@%s` (linux amd64·arm64, FROM scratch 단일 바이너리)\n' "$DIGEST" >> notes.md
          gh release edit "${GITHUB_REF_NAME}" --notes-file notes.md

  # 이미지 검증 — 두 아치의 실제 러너가 다이제스트로 pull해 자기 저장소를 검사한다.
  image-verify:
    needs: image
    strategy:
      matrix:
        runner: [ubuntu-latest, ubuntu-24.04-arm]
    runs-on: ${{ matrix.runner }}
    steps:
      - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
      - name: 다이제스트 핀 pull + scan (우리 R4를 우리가 지킨다)
        run: |

          echo "${{ github.token }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
          docker run --rm -v "${PWD}:/work" \
            "ghcr.io/kihyun1998/just-shield@${{ needs.image.outputs.digest }}" \
            scan /work --strict