From 1738fbf16f4654ea655701ab23d347d0512256db Mon Sep 17 00:00:00 2001 From: "Santiago M. Mola" Date: Tue, 13 Oct 2020 15:10:53 +0200 Subject: [PATCH 1/2] Add clang-format image --- clang-format/.dockerignore | 2 ++ clang-format/Dockerfile | 60 ++++++++++++++++++++++++++++++++++++ clang-format/README.md | 62 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 clang-format/.dockerignore create mode 100644 clang-format/Dockerfile create mode 100644 clang-format/README.md diff --git a/clang-format/.dockerignore b/clang-format/.dockerignore new file mode 100644 index 0000000..484eb50 --- /dev/null +++ b/clang-format/.dockerignore @@ -0,0 +1,2 @@ +# Ignore everything +** diff --git a/clang-format/Dockerfile b/clang-format/Dockerfile new file mode 100644 index 0000000..52d3ade --- /dev/null +++ b/clang-format/Dockerfile @@ -0,0 +1,60 @@ +# +# Dockerfile for clang-format +# +# It uses a multi-stage build process. The first stage clones the llvm-project +# repository and builds a statically linked clang-format. +# The second stage gets the clang-format binary into a clean base image +# so we don't have all the overhead from build tools. +# +# - LLVM is built with GCC. Building it directly with LLVM on an Alpine system +# is a quite involved process. You don't really want to do it, but you can +# read more about it here: https://wiki.musl-libc.org/building-llvm.html +# +# - We tried to find a set of LLVM build options that avoid building unnecessary +# stuff. It can probably be optimized some more. See an introduction to CMake +# options for LLVM here: https://llvm.org/docs/CMake.html#options-and-variables +# +# This process was inspired by https://github.com/angular/clang-format +# + +FROM alpine:3.12 AS builder + +# number of parallel build jobs, it should usually be the number of CPUs, but +# that may overload the system significantly +ARG PARALLEL_JOBS=4 + +# llvm-project git tag or branch to clone +ARG LLVM_TAG=llvmorg-10.0.1 + +ENV LLVM_REPO=https://github.com/llvm/llvm-project.git + +RUN apk add --no-cache git cmake make gcc g++ binutils python3 + +RUN git clone --depth 1 --branch "${LLVM_TAG}" "${LLVM_REPO}" + +RUN set -ex ;\ + cd llvm-project ;\ + mkdir build ;\ + cd build ;\ + cmake \ + -DCMAKE_BUILD_TYPE=MinSizeRel \ + -DLLVM_TARGETS_TO_BUILD=X86 \ + -DLLVM_BUILD_STATIC=true \ + -DLLVM_ENABLE_ZLIB=NO \ + -DLLVM_ENABLE_FFI=NO \ + -DLLVM_BUILD_DOCS=NO \ + -DLLVM_BUILD_EXAMPLES=NO \ + -DLLVM_ENABLE_PROJECTS=clang \ + -G "Unix Makefiles" \ + ../llvm ; \ + make -j ${PARALLEL_JOBS} clang-format + +FROM alpine:3.12 +LABEL io.whalebrew.name clang-format +LABEL io.whalebrew.config.volumes '["$PWD:$PWD"]' +LABEL io.whalebrew.config.working_dir '$PWD' +COPY --from=builder /llvm-project/build/bin/clang-format /usr/local/bin/clang-format +RUN mkdir /work +WORKDIR /work +ENTRYPOINT [ "clang-format" ] +CMD [ "-h" ] diff --git a/clang-format/README.md b/clang-format/README.md new file mode 100644 index 0000000..09ac067 --- /dev/null +++ b/clang-format/README.md @@ -0,0 +1,62 @@ + +# docker-clang-format + +Docker image for [clang-format](https://clang.llvm.org/docs/ClangFormat.html). + +## Overview + +- [Usage](#usage) + * [With Docker](#with-docker) + * [With Whalebrew](#with-whalebrew) +- [Build](#build) + +## Usage + +### With Docker + +When running with Docker, you'll need to mount the current directory and change the user. Otherwise, re-formatted files will be owned by root: + +```shell +docker run \ + --rm \ + -u "$(id -u):$(id -g)" \ + -v "$(pwd):$(pwd)" \ + -w "$(pwd)" \ + hdivsecurity/clang-format:latest \ + +``` + +For example, tormat all `.c` and `.h` files in the current directory, recursively: + +```shell +docker run \ + --rm \ + -u "$(id -u):$(id -g)" \ + -v "$(pwd):$(pwd)" \ + -w "$(pwd)" \ + hdivsecurity/clang-format:latest \ + -i --style=file $(find . -name '*.c' -o -name '*.h') +``` + +### With Whalebrew + +This image supports [Whalebrew](https://github.com/whalebrew/whalebrew): + +``` +whalebrew install hdivsecurity/clang-format +clang-format -i --style=file $(find . -name '*.c' -o -name '*.h') +``` + +## Build + +The image build process takes two optional [build arguments](https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg): + +* `PARALLEL_JOBS` (default: `4`): number of parallel jobs for the build, it should not be higher than the number of CPUs. Note that setting it to the number of CPUs may result in the build process hogging the system. +* `LLVM_TAG` (default: `llvmorg-10.0.1`): Git branch or tag from the [llvm-project](https://github.com/llvm/llvm-project) to use for the build. + +To build the image for clang-format 10 you would run: + +```shell +docker build --build-arg PARALLEL_JOBS=4 --build-arg LLVM_TAG=llvmorg-10.0.1 -t hdivsecurity/clang-format:10 . +``` + From 66c3b99720dbcdb5dc3224549a2ed10454fb8df4 Mon Sep 17 00:00:00 2001 From: "Santiago M. Mola" Date: Fri, 16 Oct 2020 14:08:37 +0200 Subject: [PATCH 2/2] Add GitHub Actions CI Builds and pushes all images when their source changed. --- .ci/changed-images | 39 ++++++++++++++++++++++++ .ci/git-changed | 52 +++++++++++++++++++++++++++++++ .ci/tags-to-json | 23 ++++++++++++++ .github/workflows/ci.yml | 66 +++++++++++++++++++++++++++++++++++++++- clang-format/tags | 2 ++ 5 files changed, 181 insertions(+), 1 deletion(-) create mode 100755 .ci/changed-images create mode 100755 .ci/git-changed create mode 100755 .ci/tags-to-json create mode 100644 clang-format/tags diff --git a/.ci/changed-images b/.ci/changed-images new file mode 100755 index 0000000..8370e46 --- /dev/null +++ b/.ci/changed-images @@ -0,0 +1,39 @@ +#!/bin/bash +set -euo pipefail + +PATH=".ci:$PATH" + +syntax=plain +if [[ "${1:-}" = "--matrix" ]]; then + syntax=matrix +fi + +images=() +for image in * ;do + if [[ ! -d "${image}" ]]; then + continue + fi + if ! git-changed "$image" &> /dev/null; then + continue + fi + images+=("$image") +done + +if [[ "$syntax" = plain ]]; then + for image in "${images[@]}"; do + echo "$image" + done +elif [[ "$syntax" = matrix ]]; then + output='{"image":[' + first=true + for image in "${images[@]}"; do + if [[ "$first" = true ]]; then + first=false + else + output="$output," + fi + output="$output"'"'"$image"'"' + done + output="$output]}" + echo -n "$output" +fi diff --git a/.ci/git-changed b/.ci/git-changed new file mode 100755 index 0000000..b5a30af --- /dev/null +++ b/.ci/git-changed @@ -0,0 +1,52 @@ +#!/bin/bash +set -euo pipefail + +path="${1:-$(pwd)}" +old_ref="origin/master" +new_ref="HEAD" + +if [[ "$(git rev-parse "$old_ref")" = "$(git rev-parse "$new_ref")" ]]; then + old_ref="HEAD~1" +fi + +excluded_patterns=( + '.*/README.md$' +) + +is_excluded() { + local path="$1" + for excl in "${excluded_patterns[@]}"; do + if [[ "${path}" =~ ${excl} ]]; then + return 1 + fi + done + return 0 +} + +filter_excluded() { + while read line; do + if ! is_excluded "$line"; then + echo "$line" + fi + done +} + +git_changed_files() { + local old="$1" + local new="$2" + local path="$3" + git diff --name-only "$old..$new" -- "$path" +} + +mapfile -t changed_files < <(git_changed_files "$old_ref" "$new_ref" "$path" | filter_excluded) + +if [[ ${#changed_files[@]} == 0 ]]; then + echo "Nothing changed." + exit 1 +else + echo "Changes:" + for file in "${changed_files[@]}"; do + echo " $file" + done + exit 0 +fi diff --git a/.ci/tags-to-json b/.ci/tags-to-json new file mode 100755 index 0000000..0cb80d3 --- /dev/null +++ b/.ci/tags-to-json @@ -0,0 +1,23 @@ +#!/bin/bash +set -euo pipefail + +path="$1" +tags_path="$path/tags" + +if [[ ! -f "$tags_path" ]]; then + echo "$tags_path does not exist" + exit 1 +fi + +output='"' +first=true +for tag in $(<"$tags_path"); do + if [[ "$first" = true ]]; then + first=false + output="$output$tag" + else + output="$output\n$tag" + fi +done +output="$output"'"' +echo -n "$output" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 43f9768..84299ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,71 @@ on: - staging pull_request: {} jobs: + set-matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - uses: actions/checkout@v2 + with: + # Fetch everything to be able to find changed images + fetch-depth: 0 + - run: '.ci/changed-images' + - id: set-matrix + run: 'echo "::set-output name=matrix::$(.ci/changed-images --matrix)"' build: + if: github.ref != 'refs/heads/master' + needs: set-matrix + runs-on: ubuntu-latest + strategy: + matrix: ${{fromJson(needs.set-matrix.outputs.matrix)}} + fail-fast: false + steps: + - uses: actions/checkout@v2 + - id: set-tags + name: Set tags + run: echo $(<${{ matrix.image }}/tags) | sed -e 's~[[:space:]][[:space:]]*~%0A~g' + - uses: docker/setup-buildx-action@v1 + id: buildx + with: + version: latest + install: true + id: docker_build + - uses: docker/build-push-action@v2 + id: docker_build + with: + push: false + tags: ${{ steps.set-tags.outputs.tags }} + context: ${{ matrix.image }} + file: ${{ matrix.image }}/Dockerfile + - name: Image digest + run: echo ${{ steps.docker_build.outputs.digest }} + build-and-push: + if: github.ref == 'refs/heads/master' + needs: set-matrix runs-on: ubuntu-latest + strategy: + matrix: ${{fromJson(needs.set-matrix.outputs.matrix)}} + fail-fast: false steps: - - run: echo "hello world" + - uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - uses: actions/checkout@v2 + - id: set-tags + run: 'echo "::set-output name=tags::$(.ci/tags-to-json "${{ matrix.image }}")"' + - uses: docker/setup-buildx-action@v1 + id: buildx + with: + version: latest + install: true + id: docker_build + - uses: docker/build-push-action@v2 + id: docker_build + with: + push: true + tags: ${{ steps.set-tags.outputs.tags }} + context: ${{ matrix.image }} + - name: Image digest + run: echo ${{ steps.docker_build.outputs.digest }} diff --git a/clang-format/tags b/clang-format/tags new file mode 100644 index 0000000..e25608b --- /dev/null +++ b/clang-format/tags @@ -0,0 +1,2 @@ +hdivsecurity/clang-format:latest +hdivsecurity/clang-format:10