diff --git a/.dockerignore b/.dockerignore index a5551df..d59c4e2 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,6 +2,7 @@ * # Include +!Dockerfile !LICENSE !README.md !show-versions.sh diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..99580d0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a624fb8..e78e16f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,6 +16,7 @@ updates: labels: - automatic + # Enable version updates for pip - package-ecosystem: "pip" directory: "/" @@ -24,4 +25,4 @@ updates: assignees: - "ChristophShyper" labels: - - automatic \ No newline at end of file + - automatic diff --git a/.github/workflows/CRON.yml b/.github/workflows/CRON.yml deleted file mode 100644 index 192d813..0000000 --- a/.github/workflows/CRON.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Weekly build - -on: - schedule: - # Run every week at 5.00 AM UTC - - cron: "0 5 */7 * *" - -jobs: - build_and_push: - name: Build and push images - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Build Docker image and push to registry - env: - DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TERM: xterm-256color - run: make build diff --git a/.github/workflows/PUSH-MASTER.yml b/.github/workflows/PUSH-MASTER.yml deleted file mode 100644 index 529bf37..0000000 --- a/.github/workflows/PUSH-MASTER.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Push to master - -on: - push: - branches: - - master - -jobs: - lint: - name: Run linters - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Docker Lint - uses: luke142367/Docker-Lint-Action@v1.1.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Haskell Dockerfile Linter (Hadolint) - uses: brpaz/hadolint-action@v1.5.0 - with: - dockerfile: Dockerfile - - build_and_push: - name: Build and push images - needs: lint - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Build Docker image and push to registry - env: - DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TERM: xterm-256color - run: make build push diff --git a/.github/workflows/PUSH-OTHER.yml b/.github/workflows/PUSH-OTHER.yml deleted file mode 100644 index 2f98654..0000000 --- a/.github/workflows/PUSH-OTHER.yml +++ /dev/null @@ -1,117 +0,0 @@ -name: Push to other branches - -on: - push: - branches-ignore: - - master - - release/* - -jobs: - lint: - name: Run linters - if: "!startsWith(github.ref, 'refs/heads/dependabot')" - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Docker Lint - uses: luke142367/Docker-Lint-Action@v1.1.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Haskell Dockerfile Linter (Hadolint) - uses: brpaz/hadolint-action@v1.5.0 - with: - dockerfile: Dockerfile - - build: - name: Build image - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Build Docker image - env: - TERM: xterm-256color - run: make build - - pull_request: - name: Create Pull Request - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Download Pull Request template - shell: bash - run: | - mkdir -p .tmp - curl -LsS https://raw.githubusercontent.com/devops-infra/.github/master/PULL_REQUEST_TEMPLATE.md -o .tmp/PULL_REQUEST_TEMPLATE.md - - - name: Create pull request - bugfix (conditional) - if: startsWith(github.ref, 'refs/heads/bugfix') - uses: devops-infra/action-pull-request@v1.0 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - assignee: ${{ github.actor }} - label: bugfix - template: .tmp/PULL_REQUEST_TEMPLATE.md - get_diff: true - - - name: Create pull request - dependency (conditional) - if: startsWith(github.ref, 'refs/heads/dependency') - uses: devops-infra/action-pull-request@v1.0 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - assignee: ${{ github.actor }} - label: dependency - template: .tmp/PULL_REQUEST_TEMPLATE.md - get_diff: true - - - name: Create pull request - documentation (conditional) - if: startsWith(github.ref, 'refs/heads/documentation') - uses: devops-infra/action-pull-request@v1.0 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - assignee: ${{ github.actor }} - label: documentation - template: .tmp/PULL_REQUEST_TEMPLATE.md - get_diff: true - - - name: Create pull request - feature (conditional) - if: startsWith(github.ref, 'refs/heads/feature') - uses: devops-infra/action-pull-request@v1.0 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - assignee: ${{ github.actor }} - label: feature - template: .tmp/PULL_REQUEST_TEMPLATE.md - get_diff: true - - - name: Create pull request - test (conditional) - if: startsWith(github.ref, 'refs/heads/test') - uses: devops-infra/action-pull-request@v1.0 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - assignee: ${{ github.actor }} - reviewer: ${{ github.actor }} - label: test - template: .tmp/PULL_REQUEST_TEMPLATE.md - draft: true - get_diff: true - - - name: Create pull request - other (conditional) - if: "!startsWith(github.ref, 'refs/heads/bugfix') && !startsWith(github.ref, 'refs/heads/dependabot') && !startsWith(github.ref, 'refs/heads/dependency') && !startsWith(github.ref, 'refs/heads/documentation') && !startsWith(github.ref, 'refs/heads/feature') && !startsWith(github.ref, 'refs/heads/test')" - uses: devops-infra/action-pull-request@v1.0 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - assignee: ${{ github.actor }} - label: feature - template: .tmp/PULL_REQUEST_TEMPLATE.md - get_diff: true diff --git a/.github/workflows/auto-create-pull-request.yml b/.github/workflows/auto-create-pull-request.yml new file mode 100644 index 0000000..8102c39 --- /dev/null +++ b/.github/workflows/auto-create-pull-request.yml @@ -0,0 +1,20 @@ +name: (Auto) Create Pull Request + +on: + push: + branches-ignore: + - master + - main + - dependabot/** + +permissions: + contents: read + packages: write + pull-requests: write + +jobs: + call: + uses: devops-infra/.github/.github/workflows/reusable-auto-create-pull-request.yml@v1 + with: + profile: dockerized + secrets: inherit diff --git a/.github/workflows/cron-check-dependencies.yml b/.github/workflows/cron-check-dependencies.yml new file mode 100644 index 0000000..a1aef97 --- /dev/null +++ b/.github/workflows/cron-check-dependencies.yml @@ -0,0 +1,19 @@ +name: (Cron) Weekly repository health + +on: + schedule: + - cron: 0 5 * * 1 + workflow_dispatch: + +permissions: + contents: read + issues: write + pull-requests: read + packages: write + +jobs: + call: + uses: devops-infra/.github/.github/workflows/reusable-cron-check-dependencies.yml@v1 + with: + profile: dockerized + secrets: inherit diff --git a/.github/workflows/manual-sync-common-files.yml b/.github/workflows/manual-sync-common-files.yml new file mode 100644 index 0000000..8699220 --- /dev/null +++ b/.github/workflows/manual-sync-common-files.yml @@ -0,0 +1,27 @@ +name: (Manual) Sync Common Files + +on: + workflow_dispatch: + inputs: + type: + description: File type to sync + required: true + default: all + type: choice + options: + - all + - configs + - ignores + - taskfiles + +permissions: + contents: write + pull-requests: write + +jobs: + call: + uses: devops-infra/.github/.github/workflows/reusable-manual-sync-common-files.yml@v1 + with: + sync-type: ${{ inputs.type }} + template-profile: dockerized + secrets: inherit diff --git a/.github/workflows/manual-update-version.yml b/.github/workflows/manual-update-version.yml new file mode 100644 index 0000000..8d43eea --- /dev/null +++ b/.github/workflows/manual-update-version.yml @@ -0,0 +1,39 @@ +name: (Manual) Update Version + +on: + workflow_dispatch: + inputs: + type: + description: Bump type + required: false + default: patch + type: choice + options: + - patch + - minor + - major + - set + version: + description: Explicit version when type="set" (e.g., v1.2.3) + required: false + default: '' + build_only: + description: Build and push artifacts without version bump + required: false + default: false + type: boolean + +permissions: + contents: write + packages: write + pull-requests: write + +jobs: + call: + uses: devops-infra/.github/.github/workflows/reusable-manual-update-version.yml@v1 + with: + bump-type: ${{ inputs.type }} + explicit-version: ${{ inputs.version }} + build-and-push-only: ${{ inputs.build_only }} + profile: dockerized + secrets: inherit diff --git a/.gitignore b/.gitignore index 894c62d..274f060 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,12 @@ # Intellij -.idea/ +/.idea/ *.iml # Custom +.DS_Store .tmp/ +.venv +.venv/ +.envrc +.env +.tmp diff --git a/.hadolint.yaml b/.hadolint.yaml new file mode 100644 index 0000000..7c16a46 --- /dev/null +++ b/.hadolint.yaml @@ -0,0 +1,24 @@ +failure-threshold: error +format: tty +strict-labels: false +no-color: false +no-fail: false +disable-ignore-pragma: false +trustedRegistries: + - docker.io + - ghcr.io + +# ignored: [string] +# label-schema: +# author: text +# contact: email +# created: rfc3339 +# version: semver +# documentation: url +# git-revision: hash +# license: spdx +# override: +# error: [string] +# warning: [string] +# info: [string] +# style: [string] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..113778a --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,46 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: check-added-large-files + - id: check-case-conflict + - id: check-executables-have-shebangs + - id: check-illegal-windows-names + - id: check-json + - id: check-merge-conflict + - id: check-shebang-scripts-are-executable + - id: check-symlinks + - id: check-xml + - id: check-yaml + - id: destroyed-symlinks + - id: detect-private-key + - id: end-of-file-fixer + - id: mixed-line-ending + args: [--fix=lf] + - id: no-commit-to-branch + args: [--branch, master, --branch, main] + - id: pretty-format-json + args: [--autofix] + - id: trailing-whitespace + - repo: local + hooks: + - id: actionlint + name: actionlint + entry: bash -lc 'docker run --rm -v "$PWD:/work" -w /work rhysd/actionlint:latest -color' + language: system + pass_filenames: false + - id: hadolint + name: hadolint + entry: bash -lc 'docker run --rm -v "$PWD:/work" -w /work hadolint/hadolint:latest-debian "$@"' -- + language: system + files: '(^|/)Dockerfile(\..*)?$' + - id: shellcheck + name: shellcheck + entry: bash -lc 'docker run --rm -v "$PWD:/work" -w /work koalaman/shellcheck:stable -x -S style "$@"' -- + language: system + files: '\.sh$' + - id: yamllint + name: yamllint + entry: bash -lc 'docker run --rm -v "$PWD:/work" -w /work cytopia/yamllint -c .yamllint.yml "$@"' -- + language: system + files: '\.(yml|yaml)$' diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 0000000..c05574a --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1,5 @@ +# shellcheck configuration +shell=bash +check-sourced=true +external-sources=true +source-path=SCRIPTDIR diff --git a/.yamllint.yml b/.yamllint.yml new file mode 100644 index 0000000..fb0e11a --- /dev/null +++ b/.yamllint.yml @@ -0,0 +1,24 @@ +extends: default +rules: + empty-lines: + max: 2 + max-end: 1 + document-end: + present: false + document-start: + present: false + indentation: + spaces: 2 + indent-sequences: true + check-multi-line-strings: false + line-length: + max: 220 + allow-non-breakable-inline-mappings: true + new-line-at-end-of-file: enable + new-lines: + type: unix + quoted-strings: disable + trailing-spaces: {} + truthy: + allowed-values: ['true', 'false', 'yes', 'no'] + check-keys: false diff --git a/Makefile b/Makefile deleted file mode 100644 index 8d044a0..0000000 --- a/Makefile +++ /dev/null @@ -1,53 +0,0 @@ -.PHONY: phony -phony: help - -# Provide version -VERSION=0.1.0 - -# GitHub Actions bogus variables -GITHUB_REF ?= refs/heads/null -GITHUB_SHA ?= aabbccddeeff - -# Other variables and constants -CURRENT_BRANCH := $(shell echo $(GITHUB_REF) | sed 's/refs\/heads\///') -GITHUB_SHORT_SHA := $(shell echo $(GITHUB_SHA) | cut -c1-7) -DOCKER_USER_ID := christophshyper -DOCKER_ORG_NAME := devopsinfra -DOCKER_IMAGE := docker-simple-runner -DOCKER_NAME := $(DOCKER_ORG_NAME)/$(DOCKER_IMAGE) -BUILD_DATE := $(shell date -u +"%Y-%m-%dT%H:%M:%SZ") - -# Some cosmetics -SHELL := bash -TXT_RED := $(shell tput setaf 1) -TXT_GREEN := $(shell tput setaf 2) -TXT_YELLOW := $(shell tput setaf 3) -TXT_RESET := $(shell tput sgr0) -define NL - - -endef - -# Main actions -.PHONY: help check build build-plain build-aws build-gcp build-azure push - -help: ## Display help prompt - $(info Available options:) - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "$(TXT_YELLOW)%-25s $(TXT_RESET) %s\n", $$1, $$2}' - -build: ## Build Docker images - $(info $(NL)$(TXT_GREEN)Building Docker image:$(TXT_YELLOW) $(DOCKER_NAME):$(VERSION)$(TXT_RESET)) - @docker build \ - --build-arg VCS_REF=$(GITHUB_SHORT_SHA) \ - --build-arg BUILD_DATE=$(BUILD_DATE) \ - --file=Dockerfile \ - --tag=$(DOCKER_NAME):$(VERSION) . - -push: ## Push to DockerHub - $(info $(NL)$(TXT_GREEN) == STARTING DEPLOYMENT == $(TXT_RESET)) - $(info $(NL)$(TXT_GREEN)Logging to DockerHub$(TXT_RESET)) - @echo $(DOCKER_TOKEN) | docker login -u $(DOCKER_USER_ID) --password-stdin - $(info $(NL)$(TXT_GREEN)Pushing image:$(TXT_YELLOW) $(DOCKER_NAME):$(VERSION)$(TXT_RESET)) - @docker tag $(DOCKER_NAME):$(VERSION) $(DOCKER_NAME):latest - @docker push $(DOCKER_NAME):$(VERSION) - @docker push $(DOCKER_NAME):latest diff --git a/Taskfile.cicd.yml b/Taskfile.cicd.yml new file mode 100644 index 0000000..7071bb2 --- /dev/null +++ b/Taskfile.cicd.yml @@ -0,0 +1,286 @@ +version: '3' + +silent: true + +vars: + PR_TEMPLATE: https://raw.githubusercontent.com/devops-infra/.github/refs/tags/v1/PULL_REQUEST_TEMPLATE.md + CONFIGS_BASE_URL: https://raw.githubusercontent.com/devops-infra/.github/refs/tags/v1/templates/dockerized/configs + TASKFILES_BASE_URL: https://raw.githubusercontent.com/devops-infra/.github/refs/tags/v1/templates/dockerized/taskfiles + +tasks: + pre-commit: + desc: Run all pre-commit hooks + cmds: + - pre-commit run --all-files + + pre-commit:install: + desc: Install pre-commit hooks + cmds: + - pre-commit install + + lint: + desc: Run all linters (Dockerfile, shell scripts, workflows, YAML) + cmds: + - task: lint:actionlint + - task: lint:hadolint + - task: lint:shellcheck + - task: lint:yamllint + + lint:actionlint: + desc: Lint GitHub Actions workflows with actionlint + cmds: + - | + echo "▶️ Running actionlint..." + set +e + docker run --rm -i -v "$PWD:/work" -w /work rhysd/actionlint:latest -color + rc=$? + set -e + if [ "$rc" -eq 0 ]; then + echo "✅ actionlint passed" + else + echo "❌ actionlint failed" + exit $rc + fi + + lint:hadolint: + desc: Lint Dockerfile with hadolint + cmds: + - | + echo "▶️ Running hadolint..." + set +e + docker run --rm -i -v "$PWD:/work" -w /work hadolint/hadolint:latest-debian < Dockerfile + rc=$? + set -e + if [ "$rc" -eq 0 ]; then + echo "✅ hadolint passed" + else + echo "❌ hadolint failed" + exit $rc + fi + + lint:shellcheck: + desc: Lint shell scripts with shellcheck + cmds: + - task: scripts:lint:shellcheck + + lint:yamllint: + desc: Lint YAML files with yamllint + cmds: + - | + echo "▶️ Running yamllint..." + set +e + docker run --rm -i -v "$PWD:/work" -w /work cytopia/yamllint -c .yamllint.yml . + rc=$? + set -e + if [ "$rc" -eq 0 ]; then + echo "✅ yamllint passed" + else + echo "❌ yamllint failed" + exit $rc + fi + + version:get: + desc: Get current version + cmds: + - echo "{{.VERSION}}" + + version:set: + desc: Validate version + cmds: + - | + if [ -z "{{.VERSION}}" ]; then + echo "❌ ERROR: VERSION is empty" + exit 1 + fi + if ! echo "{{.VERSION}}" | grep -Eq '^v?[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "❌ ERROR: VERSION '{{.VERSION}}' is not a valid semantic version (expected vX.Y.Z or X.Y.Z)" + exit 1 + fi + version:update:patch: + desc: Increment patch version (e.g., 1.2.3 -> 1.2.4) + cmds: + - task version:set VERSION=v{{.MAJOR}}.{{.MINOR}}.{{.NEXT_PATCH}} + + version:update:minor: + desc: Increment minor version (e.g., 1.2.3 -> 1.3.0) + cmds: + - task version:set VERSION=v{{.MAJOR}}.{{.NEXT_MINOR}}.0 + + version:update:major: + desc: Increment major version (e.g., 1.2.3 -> 2.0.0) + cmds: + - task version:set VERSION=v{{.NEXT_MAJOR}}.0.0 + + version:resolve-next: + desc: Resolve next version from bump type and profile + cmds: + - | + set -eu + bump_type="${BUMP_TYPE:-patch}" + input_version="${INPUT_VERSION:-}" + + normalize_version() { + candidate="${1#v}" + if ! printf "%s" "${candidate}" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then + return 1 + fi + printf "v%s" "${candidate}" + } + + current="$(task version:get 2>/dev/null || true)" + + case "$bump_type" in + set) + [ -n "$input_version" ] || { echo "Missing version for type=set"; exit 1; } + next="$(normalize_version "$input_version")" || { + echo "Invalid explicit version: $input_version. Expected vX.Y.Z or X.Y.Z" + exit 1 + } + ;; + patch|minor|major) + [ -n "$current" ] || { echo "Current version not found or invalid. Expected vX.Y.Z"; exit 1; } + current="$(normalize_version "$current")" || { echo "Current version not found or invalid. Expected vX.Y.Z"; exit 1; } + no_v="${current#v}" + major="$(printf "%s" "$no_v" | awk -F. '{print $1}')" + minor="$(printf "%s" "$no_v" | awk -F. '{print $2}')" + patch="$(printf "%s" "$no_v" | awk -F. '{print $3}')" + case "$bump_type" in + patch) next="v${major}.${minor}.$((patch + 1))" ;; + minor) next="v${major}.$((minor + 1)).0" ;; + major) next="v$((major + 1)).0.0" ;; + esac + ;; + *) + echo "Unknown type: $bump_type" + exit 1 + ;; + esac + + printf "%s" "$next" + + version:tag-release: + desc: Create set of git tags + cmds: + - | + set -eu + if (set -o | grep -q pipefail) 2>/dev/null; then set -o pipefail; fi + + REMOTE='origin' + FULL='{{.VERSION_FULL}}' + MINOR='{{.VERSION_MINOR}}' + MAJOR='{{.VERSION_MAJOR}}' + + # Validate vX.Y.Z + if ! printf "%s" "$FULL" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "❌ ERROR: VERSION '$FULL' must match vX.Y.Z" >&2 + exit 1 + fi + + tag_sha() { git rev-parse "refs/tags/$1" 2>/dev/null || true; } + remote_tag_sha() { git ls-remote --tags "$REMOTE" "refs/tags/$1" 2>/dev/null | awk '{print $1}' || true; } + + echo "ℹ️ INFO: Tags - Full: $FULL | Minor: $MINOR | Major: $MAJOR" + + # Full tag: must NOT exist on remote; fail fast if it does + full_remote_sha="$(remote_tag_sha "$FULL")" + if [ -n "$full_remote_sha" ]; then + echo "❌ ERROR: Full tag '$FULL' already exists on remote; aborting" >&2 + exit 1 + fi + + # Create full tag locally (if missing) and push + if git rev-parse --quiet --verify "refs/tags/$FULL" >/dev/null 2>&1; then + echo "ℹ️ INFO: Full tag '$FULL' exists locally but not on remote; pushing" + else + echo "ℹ️ INFO: Creating full tag '$FULL'" + git tag --annotate "$FULL" --message "$FULL" + fi + git push "$REMOTE" "refs/tags/$FULL" + echo "✅ OK: Pushed full tag '$FULL'" + + # Minor tag: create or update + git tag --force --annotate "$MINOR" --message "$FULL" + minor_local_sha="$(tag_sha "$MINOR")" + minor_remote_sha="$(remote_tag_sha "$MINOR")" + if [ -z "$minor_remote_sha" ]; then + git push "$REMOTE" "refs/tags/$MINOR" + echo "✅ OK: Created and pushed minor tag '$MINOR' -> $minor_local_sha" + else + if [ "$minor_local_sha" != "$minor_remote_sha" ]; then + echo "⚠️ WARN: Updating remote minor tag '$MINOR' to $minor_local_sha (was $minor_remote_sha)" + git push --force "$REMOTE" "refs/tags/$MINOR" + else + echo "ℹ️ INFO: Minor tag '$MINOR' already up-to-date" + fi + fi + + # Major tag: create or update + git tag --force --annotate "$MAJOR" --message "$FULL" + major_local_sha="$(tag_sha "$MAJOR")" + major_remote_sha="$(remote_tag_sha "$MAJOR")" + if [ -z "$major_remote_sha" ]; then + git push "$REMOTE" "refs/tags/$MAJOR" + echo "✅ OK: Created and pushed major tag '$MAJOR' -> $major_local_sha" + else + if [ "$major_local_sha" != "$major_remote_sha" ]; then + echo "⚠️ WARN: Updating remote major tag '$MAJOR' to $major_local_sha (was $major_remote_sha)" + git push --force "$REMOTE" "refs/tags/$MAJOR" + else + echo "ℹ️ INFO: Major tag '$MAJOR' already up-to-date" + fi + fi + + git:get-pr-template: + desc: Get pull request template + cmds: + - mkdir -p .tmp + - curl -LsS {{.PR_TEMPLATE}} -o .tmp/PULL_REQUEST_TEMPLATE.md + + git:set-config: + desc: Set git user config + cmds: + - git config user.name "github-actions[bot]" + - git config user.email "github-actions[bot]@users.noreply.github.com" + + sync:all: + desc: Sync all common files + cmds: + - task sync:configs + - task sync:ignores + - task sync:taskfiles + + sync:configs: + desc: Sync configuration files with devops-infra/.github + cmds: + - | + echo "▶️ Syncing configuration files from devops-infra/.github..." + curl -fsSL {{.CONFIGS_BASE_URL}}/.editorconfig -o ./.editorconfig + curl -fsSL {{.CONFIGS_BASE_URL}}/.hadolint.yaml -o ./.hadolint.yaml + curl -fsSL {{.CONFIGS_BASE_URL}}/.pre-commit-config.yaml -o ./.pre-commit-config.yaml + curl -fsSL {{.CONFIGS_BASE_URL}}/.shellcheckrc -o ./.shellcheckrc + curl -fsSL {{.CONFIGS_BASE_URL}}/.yamllint.yml -o ./.yamllint.yml + git add .editorconfig .hadolint.yaml .pre-commit-config.yaml .shellcheckrc .yamllint.yml + echo "✅ Synced configuration files" + + sync:ignores: + desc: Sync ignore files with devops-infra/.github + cmds: + - | + echo "▶️ Syncing ignore files from devops-infra/.github..." + curl -fsSL {{.CONFIGS_BASE_URL}}/.gitignore -o ./.gitignore + curl -fsSL {{.CONFIGS_BASE_URL}}/.dockerignore -o ./.dockerignore + git add .gitignore .dockerignore + echo "✅ Synced ignore files" + + sync:taskfiles: + desc: Sync Taskfiles with devops-infra/.github + cmds: + - | + echo "▶️ Syncing Taskfiles from devops-infra/.github..." + curl -fsSL {{.TASKFILES_BASE_URL}}/Taskfile.yml -o ./Taskfile.yml + curl -fsSL {{.TASKFILES_BASE_URL}}/Taskfile.cicd.yml -o ./Taskfile.cicd.yml + curl -fsSL {{.TASKFILES_BASE_URL}}/Taskfile.scripts.yml -o ./Taskfile.scripts.yml + curl -fsSL {{.TASKFILES_BASE_URL}}/Taskfile.docker.yml -o ./Taskfile.docker.yml + curl -fsSL {{.TASKFILES_BASE_URL}}/Taskfile.variables.yml -o ./Taskfile.variables.yml + git add Taskfile*.yml + echo "✅ Synced Taskfiles" diff --git a/Taskfile.docker.yml b/Taskfile.docker.yml new file mode 100644 index 0000000..9b66a81 --- /dev/null +++ b/Taskfile.docker.yml @@ -0,0 +1,104 @@ +version: '3' + +silent: true + +tasks: + docker:login: + desc: Login to hub.docker.com and ghcr.io + cmds: + - echo "Logging into Docker Hub as {{.DOCKER_USERNAME}}" + - echo "${DOCKER_TOKEN}" | docker login -u "{{.DOCKER_USERNAME}}" --password-stdin + - echo "Logging into GHCR as {{.GITHUB_USERNAME}}" + - echo "${GITHUB_TOKEN}" | docker login ghcr.io -u "{{.GITHUB_USERNAME}}" --password-stdin + + docker:cmds: + desc: Show full docker build command + cmds: + - echo -e '{{.DOCKER_BUILD_START}} {{.DOCKER_BUILD_FINISH}}' | {{.SED}} 's/--/ \\\n --/g' + + docker:build: + desc: Build Docker image + cmds: + - docker buildx create --use + - '{{.DOCKER_BUILD_START}} {{.DOCKER_BUILD_FINISH}}' + + docker:build:inspect: + desc: Inspect built Docker image + cmds: + - | + image_inspect_out=$(docker image inspect {{.DOCKER_NAME}}:{{.VERSION_FULL}}{{.VERSION_SUFFIX}} | jq -r) + echo -e "\nℹ️ Docker image inspect:" + echo "$image_inspect_out" | jq + + docker:push: + desc: Build and push Docker images + deps: + - task: docker:login + cmds: + - docker buildx create --use + - '{{.DOCKER_BUILD_START}} --push {{.DOCKER_BUILD_FINISH}}' + + docker:push:inspect: + desc: Inspect built Docker image + cmds: + - | + set -eu + image="{{.DOCKER_NAME}}:{{.VERSION_FULL}}{{.VERSION_SUFFIX}}" + + echo -e "\nℹ️ Trying local image inspect: $image" + set +e + image_inspect_out=$(docker image inspect "$image" 2>/dev/null || true) + rc=$? + set -e + + # Validate that docker inspect returned a non-empty array with an Id + has_local=0 + if [ "$rc" -eq 0 ] && [ -n "$image_inspect_out" ]; then + if echo "$image_inspect_out" | jq -e 'type=="array" and (length > 0) and \ + (.[0].Id != null and .[0].Id != "")' >/dev/null 2>&1; then + has_local=1 + fi + fi + + if [ "$has_local" -eq 1 ]; then + echo -e "\n✅ Local image found. Docker image inspect:" + echo "$image_inspect_out" | jq + image_sha=$(echo "$image_inspect_out" | jq -r '.[0].Id // empty') + if [ -n "$image_sha" ]; then + echo -e "\nℹ️ Docker manifest inspect (local):" + docker manifest inspect "${image}@${image_sha}" | jq || true + fi + exit 0 + fi + + echo -e "\nℹ️ Local image not found or inspect returned empty; inspecting remote with buildx imagetools..." + set +e + raw=$(docker buildx imagetools inspect --raw "$image" 2>/dev/null || true) + set -e + + if [ -z "$raw" ]; then + echo "❌ Failed to inspect remote image with buildx imagetools: $image" + exit 1 + fi + + echo -e "\n✅ Remote manifest/index (raw):" + echo "$raw" | jq + + echo -e "\nℹ️ Attempting to pull and inspect per-platform manifests:" + echo "$raw" | jq -r '.manifests[]?.digest' | while IFS= read -r digest; do + if [ -z "$digest" ] || [ "$digest" = "null" ]; then + continue + fi + ref="${image%@*}@${digest}" + echo -e "\nℹ️ Pulling $ref (may fail for some registries)..." + set +e + docker pull "$ref" >/dev/null 2>&1 || true + pulled_rc=$? + set -e + if [ "$pulled_rc" -eq 0 ]; then + echo "ℹ️ Inspecting pulled image $ref" + docker image inspect "$ref" | jq || true + else + echo "⚠️ Could not pull $ref; skipping image inspect" + fi + done diff --git a/Taskfile.scripts.yml b/Taskfile.scripts.yml new file mode 100644 index 0000000..b183f7d --- /dev/null +++ b/Taskfile.scripts.yml @@ -0,0 +1,192 @@ +version: '3' + +silent: true + +tasks: + help: + desc: Detailed help + cmds: + - | + echo "Tasks:" + task --list + echo DOCKER_BUILDKIT={{.DOCKER_BUILDKIT}} TERM={{.TERM}} + + lint:actionlint: + desc: Lint GitHub Actions workflows with actionlint + cmds: + - | + echo "▶️ Running actionlint..." + set +e + docker run --rm -i -v "$PWD:/work" -w /work rhysd/actionlint:latest -color + rc=$? + set -e + if [ "$rc" -eq 0 ]; then + echo "✅ actionlint passed" + else + echo "❌ actionlint failed" + exit $rc + fi + + lint:hadolint: + desc: Lint Dockerfile with hadolint + cmds: + - | + echo "▶️ Running hadolint..." + set +e + docker run --rm -i -v "$PWD:/work" -w /work hadolint/hadolint:latest-debian < Dockerfile + rc=$? + set -e + if [ "$rc" -eq 0 ]; then + echo "✅ hadolint passed" + else + echo "❌ hadolint failed" + exit $rc + fi + + lint:shellcheck: + desc: Lint shell scripts with shellcheck + cmds: + - | + echo "▶️ Running shellcheck..." + set +e + docker run --rm -i -v "$PWD:/work" -w /work koalaman/shellcheck:stable -x -S style show-versions.sh + rc=$? + set -e + if [ "$rc" -eq 0 ]; then + echo "✅ shellcheck passed" + else + echo "❌ shellcheck failed" + exit $rc + fi + + lint:yamllint: + desc: Lint YAML files with yamllint + cmds: + - | + echo "▶️ Running yamllint..." + set +e + docker run --rm -i -v "$PWD:/work" -w /work cytopia/yamllint -c .yamllint.yml . + rc=$? + set -e + if [ "$rc" -eq 0 ]; then + echo "✅ yamllint passed" + else + echo "❌ yamllint failed" + exit $rc + fi + + git:get-pr-template: + desc: Get pull request template + cmds: + - mkdir -p .tmp + - curl -LsS https://raw.githubusercontent.com/devops-infra/.github/refs/tags/v1/PULL_REQUEST_TEMPLATE.md -o .tmp/PULL_REQUEST_TEMPLATE.md + + git:set-config: + desc: Set git user config + cmds: + - git config user.name "github-actions[bot]" + - git config user.email "github-actions[bot]@users.noreply.github.com" + + version:get: + desc: Get current version + cmds: + - echo "{{.VERSION}}" + + version:set: + desc: Validate version + cmds: + - | + if [ -z "{{.VERSION}}" ]; then + echo "❌ ERROR: VERSION is empty" + exit 1 + fi + if ! echo "{{.VERSION}}" | grep -Eq '^v?[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "❌ ERROR: VERSION '{{.VERSION}}' is not a valid semantic version (expected vX.Y.Z or X.Y.Z)" + exit 1 + fi + version:update:patch: + desc: Increment patch version (e.g., 1.2.3 -> 1.2.4) + cmds: + - task version:set VERSION=v{{.MAJOR}}.{{.MINOR}}.{{.NEXT_PATCH}} + + version:update:minor: + desc: Increment minor version (e.g., 1.2.3 -> 1.3.0) + cmds: + - task version:set VERSION=v{{.MAJOR}}.{{.NEXT_MINOR}}.0 + + version:update:major: + desc: Increment major version (e.g., 1.2.3 -> 2.0.0) + cmds: + - task version:set VERSION=v{{.NEXT_MAJOR}}.0.0 + + version:tag-release: + desc: Create set of git tags + cmds: + - | + set -eu + if (set -o | grep -q pipefail) 2>/dev/null; then set -o pipefail; fi + + REMOTE='origin' + FULL='{{.VERSION_FULL}}' + MINOR='{{.VERSION_MINOR}}' + MAJOR='{{.VERSION_MAJOR}}' + + # Validate vX.Y.Z + if ! printf "%s" "$FULL" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "❌ ERROR: VERSION '$FULL' must match vX.Y.Z" >&2 + exit 1 + fi + + tag_sha() { git rev-parse "refs/tags/$1" 2>/dev/null || true; } + remote_tag_sha() { git ls-remote --tags "$REMOTE" "refs/tags/$1" 2>/dev/null | awk '{print $1}' || true; } + + echo "ℹ️ INFO: Tags - Full: $FULL | Minor: $MINOR | Major: $MAJOR" + + # Full tag: must NOT exist on remote; fail fast if it does + full_remote_sha="$(remote_tag_sha "$FULL")" + if [ -n "$full_remote_sha" ]; then + echo "❌ ERROR: Full tag '$FULL' already exists on remote; aborting" >&2 + exit 1 + fi + + # Create full tag locally (if missing) and push + if git rev-parse --quiet --verify "refs/tags/$FULL" >/dev/null 2>&1; then + echo "ℹ️ INFO: Full tag '$FULL' exists locally but not on remote; pushing" + else + echo "ℹ️ INFO: Creating full tag '$FULL'" + git tag --annotate "$FULL" --message "$FULL" + fi + git push "$REMOTE" "refs/tags/$FULL" + echo "✅ OK: Pushed full tag '$FULL'" + + # Minor tag: create or update + git tag --force --annotate "$MINOR" --message "$FULL" + minor_local_sha="$(tag_sha "$MINOR")" + minor_remote_sha="$(remote_tag_sha "$MINOR")" + if [ -z "$minor_remote_sha" ]; then + git push "$REMOTE" "refs/tags/$MINOR" + echo "✅ OK: Created and pushed minor tag '$MINOR' -> $minor_local_sha" + else + if [ "$minor_local_sha" != "$minor_remote_sha" ]; then + echo "⚠️ WARN: Updating remote minor tag '$MINOR' to $minor_local_sha (was $minor_remote_sha)" + git push --force "$REMOTE" "refs/tags/$MINOR" + else + echo "ℹ️ INFO: Minor tag '$MINOR' already up-to-date" + fi + fi + + # Major tag: create or update + git tag --force --annotate "$MAJOR" --message "$FULL" + major_local_sha="$(tag_sha "$MAJOR")" + major_remote_sha="$(remote_tag_sha "$MAJOR")" + if [ -z "$major_remote_sha" ]; then + git push "$REMOTE" "refs/tags/$MAJOR" + echo "✅ OK: Created and pushed major tag '$MAJOR' -> $major_local_sha" + else + if [ "$major_local_sha" != "$major_remote_sha" ]; then + echo "⚠️ WARN: Updating remote major tag '$MAJOR' to $major_local_sha (was $major_remote_sha)" + git push --force "$REMOTE" "refs/tags/$MAJOR" + else + echo "ℹ️ INFO: Major tag '$MAJOR' already up-to-date" + fi + fi diff --git a/Taskfile.variables.yml b/Taskfile.variables.yml new file mode 100644 index 0000000..8cb7b72 --- /dev/null +++ b/Taskfile.variables.yml @@ -0,0 +1,220 @@ +version: '3' + +silent: true + +env: + DOCKER_BUILDKIT: '1' + TERM: xterm-256color + +vars: + # System context + SED: + sh: | + if [ "$(uname -s)" = "Darwin" ]; then + if command -v gsed >/dev/null 2>&1; then + echo gsed + else + if command -v brew >/dev/null 2>&1; then + # Quietly ensure gnu-sed is installed + brew list gnu-sed >/dev/null 2>&1 || { + HOMEBREW_NO_ENV_HINTS=1 HOMEBREW_NO_ANALYTICS=1 brew update >/dev/null 2>&1 || true + HOMEBREW_NO_ENV_HINTS=1 HOMEBREW_NO_ANALYTICS=1 brew install gnu-sed >/dev/null 2>&1 + } + echo gsed + else + echo sed + fi + fi + else + echo sed + fi + PROJECT_DIR_NAME: + sh: basename "$PWD" + + # Container metadata + DOCKER_IMAGE: '{{.DOCKER_IMAGE | default .PROJECT_DIR_NAME}}' + GITHUB_REPO: '{{.GITHUB_REPO | default .PROJECT_DIR_NAME}}' + DOCKER_ORG_NAME: '{{.DOCKER_ORG_NAME | default "devopsinfra"}}' + GITHUB_ORG_NAME: '{{.GITHUB_ORG_NAME | default "devops-infra"}}' + DOCKER_USERNAME: '{{.DOCKER_USERNAME | default "christophshyper"}}' + GITHUB_USERNAME: '{{.GITHUB_USERNAME | default "ChristophShyper"}}' + DOCKER_NAME: '{{.DOCKER_ORG_NAME}}/{{.DOCKER_IMAGE}}' + GITHUB_NAME: '{{.GITHUB_ORG_NAME}}/{{.GITHUB_REPO}}' + GHRC_NAME: ghcr.io/{{.GITHUB_ORG_NAME}}/{{.GITHUB_REPO}} + DEFAULT_BRANCH: master + VERSION_FROM_ACTION_YML: + sh: 'grep "image: docker://{{.DOCKER_NAME}}:" action.yml 2>/dev/null | cut -d ":" -f 4' + AUTHOR_FROM_ACTION_YML: + sh: | + grep -e "^author:" action.yml 2>/dev/null | head -1 | awk -F": " '{print $2}' + NAME_FROM_ACTION_YML: + sh: | + grep -e "^name:" action.yml 2>/dev/null | head -1 | awk -F": " '{print $2}' + DESCRIPTION_FROM_ACTION_YML: + sh: | + grep -e "^description:" action.yml 2>/dev/null | head -1 | awk -F": " '{print $2}' + LABEL_AUTHOR: '{{.LABEL_AUTHOR | default .AUTHOR_FROM_ACTION_YML}}' + LABEL_NAME: '{{.LABEL_NAME | default .NAME_FROM_ACTION_YML}}' + LABEL_DESCRIPTION: '{{.LABEL_DESCRIPTION | default .DESCRIPTION_FROM_ACTION_YML}}' + LABEL_REPO_URL: '{{ default (printf "https://github.com/%s/%s" .GITHUB_ORG_NAME .DOCKER_IMAGE) .LABEL_REPO_URL }}' + LABEL_DOCS_URL: >- + {{ default (printf "https://github.com/%s/%s/blob/%s/README.md" .GITHUB_ORG_NAME .DOCKER_IMAGE + .DEFAULT_BRANCH) .LABEL_DOCS_URL }} + LABEL_HOMEPAGE: '{{.LABEL_HOMEPAGE | default "https://shyper.pro"}}' + LABEL_VENDOR: '{{.LABEL_VENDOR | default "DevOps-Infra"}}' + LABEL_LICENSE: '{{.LABEL_LICENSE | default "MIT"}}' + + # Build context + CONTEXT: '{{.CONTEXT | default "."}}' + DOCKERFILE: '{{.DOCKERFILE | default "Dockerfile"}}' + PLATFORMS: '{{.PLATFORMS | default "linux/amd64,linux/arm64"}}' + BUILD_DATE: + sh: date -u +"%Y-%m-%dT%H:%M:%SZ" + LAST_RELEASE: + sh: | + curl --silent "https://api.github.com/repos/{{.GITHUB_ORG_NAME}}/{{.GITHUB_REPO}}/releases/latest" | \ + grep '"tag_name":' | \ + {{.SED}} -E 's/.*"([^"]+)".*/\1/' + VERSION_SUFFIX: + sh: | + if [ "${VERSION_SUFFIX+x}" = "x" ]; then + printf "%s" "${VERSION_SUFFIX}" + else + printf "%s" "-test" + fi + VERSION_OVERRIDE: + sh: echo "${VERSION_OVERRIDE:-}" + VERSION: + sh: | + override="{{.VERSION_OVERRIDE}}" + if [ -n "$override" ]; then + echo "$override" + else + tag="$(git tag --sort=-v:refname 2>/dev/null | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1)" + if [ -n "$tag" ]; then + echo "$tag" + elif [ -n "{{.VERSION_FROM_ACTION_YML}}" ]; then + echo "{{.VERSION_FROM_ACTION_YML}}" + else + echo "{{.LAST_RELEASE}}" + fi + fi + VERSION_NO_V: + sh: v="{{.VERSION}}"; echo "${v#v}" + MAJOR: + sh: echo "{{.VERSION_NO_V}}" | awk -F\. '{print $1}' + MINOR: + sh: echo "{{.VERSION_NO_V}}" | awk -F\. '{print $2}' + PATCH: + sh: echo "{{.VERSION_NO_V}}" | awk -F\. '{print $3}' + VERSION_MAJOR: v{{.MAJOR}} + VERSION_MINOR: v{{.MAJOR}}.{{.MINOR}} + VERSION_FULL: v{{.MAJOR}}.{{.MINOR}}.{{.PATCH}} + NEXT_PATCH: + sh: echo $(( {{.PATCH}} + 1 )) + NEXT_MINOR: + sh: echo $(( {{.MINOR}} + 1 )) + NEXT_MAJOR: + sh: echo $(( {{.MAJOR}} + 1 )) + MAJOR_FROM_ACTION_YML: + sh: echo "{{.VERSION_FROM_ACTION_YML}}" | awk -F\. '{print $1}' + MINOR_FROM_ACTION_YML: + sh: echo "{{.VERSION_FROM_ACTION_YML}}" | awk -F\. '{print $1"."$2}' + + # Git metadata + GIT_SHA: + sh: git rev-parse HEAD 2>/dev/null || echo 0000000000000000000000000000000000000000 + GIT_SHORT_SHA: + sh: git rev-parse --short HEAD 2>/dev/null || echo 0000000 + GIT_BRANCH: + sh: | + if [ -n "${GITHUB_REF:-}" ]; then + echo "${GITHUB_REF#refs/heads/}" + else + git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown" + fi + + # Labels for http://label-schema.org/rc1/#build-time-labels + # And for https://github.com/opencontainers/image-spec/blob/master/annotations.md + ANNOTATIONS: >- + --annotation index:org.label-schema.schema-version="1.0" + --annotation index:org.label-schema.build-date="{{.BUILD_DATE}}" + --annotation index:org.label-schema.name="{{.LABEL_NAME}}" + --annotation index:org.label-schema.description="{{.LABEL_DESCRIPTION}}" + --annotation index:org.label-schema.usage="{{.LABEL_DOCS_URL}}" + --annotation index:org.label-schema.url="{{.LABEL_HOMEPAGE}}" + --annotation index:org.label-schema.vcs-url="{{.LABEL_REPO_URL}}" + --annotation index:org.label-schema.vcs-ref="{{.GIT_SHA}}" + --annotation index:org.label-schema.vendor="{{.LABEL_VENDOR}}" + --annotation index:org.label-schema.version="{{.VERSION_FULL}}{{.VERSION_SUFFIX}}" + --annotation index:org.opencontainers.image.created="{{.BUILD_DATE}}" + --annotation index:org.opencontainers.image.authors="{{.LABEL_AUTHOR}}" + --annotation index:org.opencontainers.image.url="{{.LABEL_HOMEPAGE}}" + --annotation index:org.opencontainers.image.documentation="{{.LABEL_DOCS_URL}}" + --annotation index:org.opencontainers.image.source="{{.LABEL_REPO_URL}}" + --annotation index:org.opencontainers.image.version="{{.VERSION_FULL}}{{.VERSION_SUFFIX}}" + --annotation index:org.opencontainers.image.revision="{{.GIT_SHA}}" + --annotation index:org.opencontainers.image.vendor="{{.LABEL_VENDOR}}" + --annotation index:org.opencontainers.image.licenses="{{.LABEL_LICENSE}}" + --annotation index:org.opencontainers.image.title="{{.LABEL_NAME}}" + --annotation index:org.opencontainers.image.description="{{.LABEL_DESCRIPTION}}" + --annotation manifest:org.label-schema.schema-version="1.0" + --annotation manifest:org.label-schema.build-date="{{.BUILD_DATE}}" + --annotation manifest:org.label-schema.name="{{.LABEL_NAME}}" + --annotation manifest:org.label-schema.description="{{.LABEL_DESCRIPTION}}" + --annotation manifest:org.label-schema.usage="{{.LABEL_DOCS_URL}}" + --annotation manifest:org.label-schema.url="{{.LABEL_HOMEPAGE}}" + --annotation manifest:org.label-schema.vcs-url="{{.LABEL_REPO_URL}}" + --annotation manifest:org.label-schema.vcs-ref="{{.GIT_SHA}}" + --annotation manifest:org.label-schema.vendor="{{.LABEL_VENDOR}}" + --annotation manifest:org.label-schema.version="{{.VERSION_FULL}}{{.VERSION_SUFFIX}}" + --annotation manifest:org.opencontainers.image.created="{{.BUILD_DATE}}" + --annotation manifest:org.opencontainers.image.authors="{{.LABEL_AUTHOR}}" + --annotation manifest:org.opencontainers.image.url="{{.LABEL_HOMEPAGE}}" + --annotation manifest:org.opencontainers.image.documentation="{{.LABEL_DOCS_URL}}" + --annotation manifest:org.opencontainers.image.source="{{.LABEL_REPO_URL}}" + --annotation manifest:org.opencontainers.image.version="{{.VERSION_FULL}}{{.VERSION_SUFFIX}}" + --annotation manifest:org.opencontainers.image.revision="{{.GIT_SHA}}" + --annotation manifest:org.opencontainers.image.vendor="{{.LABEL_VENDOR}}" + --annotation manifest:org.opencontainers.image.licenses="{{.LABEL_LICENSE}}" + --annotation manifest:org.opencontainers.image.title="{{.LABEL_NAME}}" + --annotation manifest:org.opencontainers.image.description="{{.LABEL_DESCRIPTION}}" + LABELS: >- + --label org.label-schema.schema-version="1.0" + --label org.label-schema.build-date="{{.BUILD_DATE}}" + --label org.label-schema.name="{{.LABEL_NAME}}" + --label org.label-schema.description="{{.LABEL_DESCRIPTION}}" + --label org.label-schema.usage="{{.LABEL_DOCS_URL}}" + --label org.label-schema.url="{{.LABEL_HOMEPAGE}}" + --label org.label-schema.vcs-url="{{.LABEL_REPO_URL}}" + --label org.label-schema.vcs-ref="{{.GIT_SHA}}" + --label org.label-schema.vendor="{{.LABEL_VENDOR}}" + --label org.label-schema.version="{{.VERSION_FULL}}{{.VERSION_SUFFIX}}" + --label org.opencontainers.image.created="{{.BUILD_DATE}}" + --label org.opencontainers.image.authors="{{.LABEL_AUTHOR}}" + --label org.opencontainers.image.url="{{.LABEL_HOMEPAGE}}" + --label org.opencontainers.image.documentation="{{.LABEL_DOCS_URL}}" + --label org.opencontainers.image.source="{{.LABEL_REPO_URL}}" + --label org.opencontainers.image.version="{{.VERSION_FULL}}{{.VERSION_SUFFIX}}" + --label org.opencontainers.image.revision="{{.GIT_SHA}}" + --label org.opencontainers.image.vendor="{{.LABEL_VENDOR}}" + --label org.opencontainers.image.licenses="{{.LABEL_LICENSE}}" + --label org.opencontainers.image.title="{{.LABEL_NAME}}" + --label org.opencontainers.image.description="{{.LABEL_DESCRIPTION}}" + TAGS: >- + --tag {{.DOCKER_NAME}}:{{.VERSION_FULL}}{{.VERSION_SUFFIX}} + --tag {{.DOCKER_NAME}}:{{.VERSION_MINOR}}{{.VERSION_SUFFIX}} + --tag {{.DOCKER_NAME}}:{{.VERSION_MAJOR}}{{.VERSION_SUFFIX}} + --tag {{.DOCKER_NAME}}:latest{{.VERSION_SUFFIX}} + --tag {{.GHRC_NAME}}:{{.VERSION_FULL}}{{.VERSION_SUFFIX}} + --tag {{.GHRC_NAME}}:{{.VERSION_MINOR}}{{.VERSION_SUFFIX}} + --tag {{.GHRC_NAME}}:{{.VERSION_MAJOR}}{{.VERSION_SUFFIX}} + --tag {{.GHRC_NAME}}:latest{{.VERSION_SUFFIX}} + DOCKER_BUILD_START: + sh: | + if docker buildx version >/dev/null 2>&1; then + echo "docker buildx build --platform {{.PLATFORMS}}" + else + echo "docker build" + fi + DOCKER_BUILD_FINISH: '{{.ANNOTATIONS}} {{.LABELS}} {{.TAGS}} --file={{.DOCKERFILE}} .' diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..03c95c1 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,24 @@ +version: '3' + +silent: true + +includes: + variables: ./Taskfile.variables.yml + scripts: ./Taskfile.scripts.yml + docker: + taskfile: ./Taskfile.docker.yml + flatten: true + cicd: + taskfile: ./Taskfile.cicd.yml + flatten: true + +tasks: + default: + desc: List tasks + cmds: + - task help + + help: + desc: Detailed help + cmds: + - task: scripts:help