diff --git a/.github/workflows/.build.yml b/.github/workflows/.build.yml new file mode 100644 index 0000000..9c9dda0 --- /dev/null +++ b/.github/workflows/.build.yml @@ -0,0 +1,43 @@ +name: Build + +description: Build and Push docker image. + +inputs: + docker_username: + description: 'Docker auth username' + required: true + docker_password: + description: 'Docker auth password' + required: true + image: + description: 'Image name / Taskfile command' + required: true + +runs: + using: "composite" + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ inputs.docker_username }} + password: ${{ inputs.docker_password }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Buildx + uses: docker/setup-buildx-action@v3 + with: + install: true + + - name: Invoke Taskfile + uses: Mad-Pixels/github-workflows/.github/actions/taskfile-runner@main + with: + command: ${{ inputs.image }} + diff --git a/.github/workflows/.checks.yml b/.github/workflows/.checks.yml new file mode 100644 index 0000000..19906cc --- /dev/null +++ b/.github/workflows/.checks.yml @@ -0,0 +1,29 @@ +name: Checks + +on: + workflow_call: + secrets: + gitleaks: + required: true + +jobs: + lint: + name: Lint + runs-on: ${{ vars.RUNS_ON }} + steps: + - name: Invoke + uses: Mad-Pixels/github-workflows/.github/actions/taskfile-runner@main + with: + command: docker/lint + + leaks: + name: GitLeaks + runs-on: ${{ vars.RUNS_ON }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: gitleaks/gitleaks-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITLEAKS_LICENSE: ${{ secrets.gitleaks }} diff --git a/.github/workflows/base_commint.yml b/.github/workflows/base_commint.yml new file mode 100644 index 0000000..8a27be3 --- /dev/null +++ b/.github/workflows/base_commint.yml @@ -0,0 +1,26 @@ +name: Base commit + +on: + push: + branches-ignore: + - main + +concurrency: + group: base-commit-${{ github.ref }} + cancel-in-progress: true + +jobs: + checks: + name: (Base) Commit Checks + uses: ./.github/workflows/.checks.yml + secrets: + gitleaks: ${{ secrets.GITLEAKS_LICENSE }} + + commit-check: + name: (Base) Commit Check + needs: checks + runs-on: ${{ vars.RUNS_ON }} + steps: + - name: All checks passed + run: | + echo "✅ All Commit checks completed successfully!" \ No newline at end of file diff --git a/.github/workflows/main_commit.yml b/.github/workflows/main_commit.yml new file mode 100644 index 0000000..60873aa --- /dev/null +++ b/.github/workflows/main_commit.yml @@ -0,0 +1,39 @@ +name: Main commit + +on: + push: + branches: + - main + +jobs: + detect-changes: + name: Detect changed directories + runs-on: ${{ vars.RUNS_ON }} + outputs: + dirs: ${{ steps.get-changes.outputs.changes }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get changes + id: get-changes + uses: Mad-Pixels/github-workflows/.github/actions/taskfile-runner@main + with: + command: changes + + build: + needs: detect-changes + if: ${{ needs.detect-changes.outputs.dirs != '' }} + runs-on: ${{ vars.RUNS_ON }} + strategy: + matrix: + dir: ${{ fromJson('["' + join('","', needs.detect-changes.outputs.dirs.split(' ')) + '"]') }} + + steps: + - uses: ./.github/workflows/.build.yml + with: + docker_username: ${{ github.actor }} + docker_password: ${{ secrets.GITHUB_TOKEN }} + image: ${{ matrix.dir }} + diff --git a/Taskfile.yaml b/Taskfile.yaml new file mode 100644 index 0000000..a0e1873 --- /dev/null +++ b/Taskfile.yaml @@ -0,0 +1,127 @@ +version: '3' + +vars: + git_root: + sh: git rev-parse --show-toplevel + + default_platforms: linux/amd64,linux/arm64 + registry: "ghcr.io/mad-pixels" + image_alpine_ver: "3.20" + +tasks: + default: + desc: Show usage + cmds: + - echo "Please enter a task or use '-l' or '--list-all' to list all available tasks" + silent: true + +# ================================================# +# ---------------------INTERNAL-------------------# +# ================================================# + + _docker/buildx: + desc: Internal task to build multiarch images. + internal: true + silent: true + dir: "{{.git_root}}" + cmds: + - | + docker buildx build \ + --platform {{.platforms | default .default_platforms}} \ + --file {{.dockerfile | default (print .CONTEXT "/Dockerfile")}} \ + {{.BUILD_ARGS}} \ + -t {{.registry}}/{{.CONTEXT}}:{{.TAG}} \ + -t {{.registry}}/{{.CONTEXT}}:latest \ + --push \ + {{.context_path | default (print "./" .CONTEXT)}} + + _docker/run: + desc: Internal task to run secure container. + internal: true + silent: true + dir: "{{.git_root}}" + cmd: | + docker run --rm --init {{if .TTY}}-it{{end}} \ + --cap-drop=ALL \ + --security-opt no-new-privileges \ + --user $(id -u):$(id -g) \ + --workdir /workspace \ + {{if .ENVS}}{{range $env := .ENVS}}--env {{$env}} {{end}}{{end}}\ + {{if .PORTS}}{{range $port := .PORTS}}--publish {{$port}} {{end}}{{end}}\ + --volume "{{.git_root}}/{{.MOUNT_DIR}}:/workspace:rw" \ + {{.IMAGE}} \ + {{.CMD}} + requires: + vars: [IMAGE, CMD, MOUNT_DIR] + + _docker/lint: + desc: Execute hadolint on single Dockerfile. + internal: true + silent: true + dir: "{{.git_root}}" + cmds: + - task: _docker/run + vars: + IMAGE: "hadolint/hadolint" + MOUNT_DIR: "{{.DOCKERFILE_PATH | dir}}" + CMD: "hadolint {{.DOCKERFILE_PATH | base}}" + +# ================================================# +# ---------------------IMAGES---------------------# +# ================================================# + + actionlint: + desc: Build actionlint image. + vars: + VERSION: v1.7.7 + cmds: + - task: _docker/buildx + vars: + TAG: "{{.VERSION}}" + CONTEXT: actionlint + BUILD_ARGS: >- + --build-arg ALPINE_VERSION={{.image_alpine_ver}} + --build-arg ACTIONLINT_VER={{.VERSION}} + silent: true + + taskfile: + desc: Build taskfile image. + vars: + VERSION: v3.44.1 + cmds: + - task: _docker/buildx + vars: + TAG: "{{.VERSION}}" + CONTEXT: taskfile + BUILD_ARGS: >- + --build-arg ALPINE_VERSION={{.image_alpine_ver}} + --build-arg TASK_VERSION={{.VERSION}} + silent: true + +# ================================================# +# ----------------------CI/CD---------------------# +# ================================================# + + docker/lint: + desc: Run hadolint for all Dockerfiles in the repo. + dir: "{{.git_root}}" + vars: + files: + sh: find . -type f -name Dockerfile + cmds: + - for: { var: files } + task: _docker/lint + vars: + DOCKERFILE_PATH: "{{.ITEM}}" + + changes: + desc: Get changes Docker directories. + silent: true + cmds: + - | + CHANGES=$(git diff --name-only origin/main HEAD \ + | grep '^' \ + | cut -d '/' -f1 \ + | sort -u \ + | grep -E '^(actionlint|taskfile)' || true) + echo "changes=$(echo $CHANGES | xargs)" >> $GITHUB_OUTPUT diff --git a/actionlint/Dockerfile b/actionlint/Dockerfile new file mode 100644 index 0000000..27cf6f3 --- /dev/null +++ b/actionlint/Dockerfile @@ -0,0 +1,16 @@ +ARG ALPINE_VERSION + +# build image -> +FROM --platform=$BUILDPLATFORM alpine:${ALPINE_VERSION} AS builder +ARG ACTIONLINT_VER + +RUN apk add --no-cache go=latest git=latest +ENV GOPATH=/go \ + GOCACHE=/tmp/.cache \ + PATH=/go/bin:/usr/local/go/bin:$PATH +RUN go install github.com/rhysd/actionlint/cmd/actionlint@${ACTIONLINT_VER} + +# final image -> +FROM --platform=$TARGETPLATFORM scratch +COPY --from=builder /go/bin/actionlint /actionlint +ENTRYPOINT ["/actionlint"] diff --git a/taskfile/Dockerfile b/taskfile/Dockerfile new file mode 100644 index 0000000..aacd0d5 --- /dev/null +++ b/taskfile/Dockerfile @@ -0,0 +1,14 @@ +ARG ALPINE_VERSION + +# build stage -> +FROM --platform=$BUILDPLATFORM alpine:${ALPINE_VERSION} AS builder +ARG TASK_VERSION + +SHELL ["/bin/ash", "-eo", "pipefail"] +RUN curl -sSL "https://github.com/go-task/task/releases/download/v${TASK_VERSION}/task_linux_$(uname -m).tar.gz" \ + | tar -xz -C /usr/local/bin task + +# final image -> +FROM --platform=$TARGETPLATFORM alpine:${ALPINE_VERSION} +COPY --from=builder /usr/local/bin/task /usr/local/bin/task +ENTRYPOINT ["task"] \ No newline at end of file