Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add clang-format image and continuous integration workflow #1

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .ci/changed-images
Original file line number Diff line number Diff line change
@@ -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
52 changes: 52 additions & 0 deletions .ci/git-changed
Original file line number Diff line number Diff line change
@@ -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
23 changes: 23 additions & 0 deletions .ci/tags-to-json
Original file line number Diff line number Diff line change
@@ -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"
66 changes: 65 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
2 changes: 2 additions & 0 deletions clang-format/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Ignore everything
**
60 changes: 60 additions & 0 deletions clang-format/Dockerfile
Original file line number Diff line number Diff line change
@@ -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" ]
62 changes: 62 additions & 0 deletions clang-format/README.md
Original file line number Diff line number Diff line change
@@ -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 \
<clang-format arguments>
```

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 .
```

2 changes: 2 additions & 0 deletions clang-format/tags
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
hdivsecurity/clang-format:latest
hdivsecurity/clang-format:10