diff --git a/.github/workflows/cron-check-dependencies.yml b/.github/workflows/cron-check-dependencies.yml index 6598743..e028662 100644 --- a/.github/workflows/cron-check-dependencies.yml +++ b/.github/workflows/cron-check-dependencies.yml @@ -43,6 +43,9 @@ jobs: run: task docker:cmds - name: Build and push test image + env: + DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: task docker:push - name: Inspect image diff --git a/.github/workflows/manual-update-version.yml b/.github/workflows/manual-update-version.yml index f7f573c..467ee91 100644 --- a/.github/workflows/manual-update-version.yml +++ b/.github/workflows/manual-update-version.yml @@ -21,12 +21,11 @@ on: permissions: contents: write pull-requests: write - packages: write jobs: update: name: Update version and push release branch - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm steps: - name: Checkout uses: actions/checkout@v5 @@ -70,29 +69,6 @@ jobs: esac echo "REL_VERSION=$(task version:get)" >> "$GITHUB_OUTPUT" - - name: Install Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - install: true - - - name: Install QEMU - uses: docker/setup-qemu-action@v3 - with: - image: tonistiigi/binfmt:latest - platforms: amd64,arm64 - - - name: Get Docker commands - run: task docker:cmds - - - name: Build and push test image - env: - DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: task docker:push - - - name: Inspect image - run: task docker:push:inspect - - name: Get template env: VERSION_SUFFIX: "" diff --git a/.gitignore b/.gitignore index b46b6a4..b75cdc1 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ .venv/ .envrc .env +.tmp diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f1c4ce1..32ffeba 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,23 +24,23 @@ repos: - id: trailing-whitespace - repo: local hooks: - - id: action - name: action + - 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: docker - name: docker + - id: hadolint + name: hadolint entry: bash -lc 'docker run --rm -v "$PWD:/work" -w /work hadolint/hadolint:latest-debian hadolint Dockerfile' language: system pass_filenames: false - - id: shell - name: shell + - id: shellcheck + name: shellcheck entry: bash -lc 'docker run --rm -v "$PWD:/work" -w /work koalaman/shellcheck:stable -x -S style entrypoint.sh' language: system pass_filenames: false - - id: yaml - name: yaml + - id: yamllint + name: yamllint entry: bash -lc 'docker run --rm -v "$PWD:/work" -w /work cytopia/yamllint -c .yamllint.yml .' language: system pass_filenames: false diff --git a/Taskfile.scripts.yml b/Taskfile.cicd.yml similarity index 65% rename from Taskfile.scripts.yml rename to Taskfile.cicd.yml index 9940206..f21c9c8 100644 --- a/Taskfile.scripts.yml +++ b/Taskfile.cicd.yml @@ -3,111 +3,23 @@ version: '3' silent: true tasks: - help: - desc: Detailed help + pre-commit: + desc: Run all pre-commit hooks cmds: - - | - echo "Tasks:" - task --list - echo "" - echo "Environment:" - echo " DOCKER_NAME={{.DOCKER_NAME}} DOCKER_USERNAME={{.DOCKER_USERNAME}}" - echo " GHRC_NAME={{.GHRC_NAME}} GITHUB_USERNAME={{.GITHUB_USERNAME}}" - echo " LAST_RELEASE={{.LAST_RELEASE}}" VERSION={{.VERSION}} VERSION_FULL={{.VERSION_FULL}} - echo " BRANCH={{.GIT_BRANCH}} GIT_SHORT_SHA={{.GIT_SHORT_SHA}}" GIT_SHA={{.GIT_SHA}} + - pre-commit run --all-files - version:tag-release: - internal: true - desc: Create set of git tags + pre-commit:install: + desc: Install pre-commit hooks cmds: - - | - set -eu - if (set -o | grep -q pipefail) 2>/dev/null; then set -o pipefail; fi + - pre-commit install - 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 - - version:set: - desc: Update version in README.md and action.yml + lint: + desc: Run all linters (Dockerfile, shell scripts, workflows, YAML) cmds: - - | - # check if VERSION if different than VERSION_FROM_ACTION_YML - if [ "{{.VERSION}}" = "{{.VERSION_FROM_ACTION_YML}}" ]; then - echo "❌ ERROR: VERSION is same as VERSION_FROM_ACTION_YML ({{.VERSION}})" - exit 1 - fi - - echo Updating full version from {{.VERSION_FROM_ACTION_YML}} to {{.VERSION}} - - echo Updating minor version from {{.MINOR_FROM_ACTION_YML}} to {{.VERSION_MINOR}} - - echo Updating major version from {{.MAJOR_FROM_ACTION_YML}} to {{.VERSION_MAJOR}} - - "{{.SED}} -i 's#{{.DOCKER_NAME}}:{{.VERSION_FROM_ACTION_YML}}#{{.DOCKER_NAME}}:{{.VERSION}}#g' action.yml" - - "{{.SED}} -i 's#{{.DOCKER_NAME}}@{{.VERSION_FROM_ACTION_YML}}#{{.DOCKER_NAME}}@{{.VERSION}}#g' README.md" - - "{{.SED}} -i 's#{{.GITHUB_NAME}}@{{.VERSION_FROM_ACTION_YML}}#{{.GITHUB_NAME}}@{{.VERSION}}#g' README.md" - - "{{.SED}} -i 's#{{.DOCKER_NAME}}@{{.MINOR_FROM_ACTION_YML}}#{{.DOCKER_NAME}}@{{.VERSION_MINOR}}#g' README.md" - - "{{.SED}} -i 's#{{.GITHUB_NAME}}@{{.MINOR_FROM_ACTION_YML}}#{{.GITHUB_NAME}}@{{.VERSION_MINOR}}#g' README.md" - - "{{.SED}} -i 's#{{.DOCKER_NAME}}@{{.MAJOR_FROM_ACTION_YML}}#{{.DOCKER_NAME}}@{{.VERSION_MAJOR}}#g' README.md" - - "{{.SED}} -i 's#{{.GITHUB_NAME}}@{{.MAJOR_FROM_ACTION_YML}}#{{.GITHUB_NAME}}@{{.VERSION_MAJOR}}#g' README.md" + - task: lint:actionlint + - task: lint:hadolint + - task: lint:shellcheck + - task: lint:yamllint lint:actionlint: desc: Lint GitHub Actions workflows with actionlint @@ -173,87 +85,181 @@ tasks: exit $rc fi - docker:build:inspect: - desc: Inspect built Docker image + version:get: + desc: Get current version + cmds: + - echo "{{.VERSION}}" + + version:set: + desc: Update version in README.md and action.yml 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 + # check if VERSION if different than VERSION_FROM_ACTION_YML + if [ "{{.VERSION}}" = "{{.VERSION_FROM_ACTION_YML}}" ]; then + echo "❌ ERROR: VERSION is same as VERSION_FROM_ACTION_YML ({{.VERSION}})" + exit 1 + fi + - echo Updating full version from {{.VERSION_FROM_ACTION_YML}} to {{.VERSION}} + - echo Updating minor version from {{.MINOR_FROM_ACTION_YML}} to {{.VERSION_MINOR}} + - echo Updating major version from {{.MAJOR_FROM_ACTION_YML}} to {{.VERSION_MAJOR}} + - "{{.SED}} -i 's#{{.DOCKER_NAME}}:{{.VERSION_FROM_ACTION_YML}}#{{.DOCKER_NAME}}:{{.VERSION}}#g' action.yml" + - "{{.SED}} -i 's#{{.DOCKER_NAME}}@{{.VERSION_FROM_ACTION_YML}}#{{.DOCKER_NAME}}@{{.VERSION}}#g' README.md" + - "{{.SED}} -i 's#{{.GITHUB_NAME}}@{{.VERSION_FROM_ACTION_YML}}#{{.GITHUB_NAME}}@{{.VERSION}}#g' README.md" + - "{{.SED}} -i 's#{{.DOCKER_NAME}}@{{.MINOR_FROM_ACTION_YML}}#{{.DOCKER_NAME}}@{{.VERSION_MINOR}}#g' README.md" + - "{{.SED}} -i 's#{{.GITHUB_NAME}}@{{.MINOR_FROM_ACTION_YML}}#{{.GITHUB_NAME}}@{{.VERSION_MINOR}}#g' README.md" + - "{{.SED}} -i 's#{{.DOCKER_NAME}}@{{.MAJOR_FROM_ACTION_YML}}#{{.DOCKER_NAME}}@{{.VERSION_MAJOR}}#g' README.md" + - "{{.SED}} -i 's#{{.GITHUB_NAME}}@{{.MAJOR_FROM_ACTION_YML}}#{{.GITHUB_NAME}}@{{.VERSION_MAJOR}}#g' README.md" - docker:push:inspect: - desc: Inspect built Docker image + 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: + internal: true + desc: Create set of git tags cmds: - | set -eu - image="{{.DOCKER_NAME}}:{{.VERSION_FULL}}{{.VERSION_SUFFIX}}" + if (set -o | grep -q pipefail) 2>/dev/null; then set -o pipefail; fi - echo -e "\nℹ️ Trying local image inspect: $image" - set +e - image_inspect_out=$(docker image inspect "$image" 2>/dev/null || true) - rc=$? - set -e + REMOTE='origin' + FULL='{{.VERSION_FULL}}' + MINOR='{{.VERSION_MINOR}}' + MAJOR='{{.VERSION_MAJOR}}' - # 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 + # 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 - 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 + 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 -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 + echo "ℹ️ INFO: Tags - Full: $FULL | Minor: $MINOR | Major: $MAJOR" - if [ -z "$raw" ]; then - echo "❌ Failed to inspect remote image with buildx imagetools: $image" + # 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 - echo -e "\n✅ Remote manifest/index (raw):" - echo "$raw" | jq + # 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'" - 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 + # 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 - 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 + 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 "⚠️ Could not pull $ref; skipping image inspect" + echo "ℹ️ INFO: Major tag '$MAJOR' already up-to-date" fi - done + fi git:get-pr-template: desc: Get pull request template cmds: - mkdir -p .tmp - - curl -LsS https://raw.githubusercontent.com/devops-infra/.github/master/PULL_REQUEST_TEMPLATE.md -o .tmp/PULL_REQUEST_TEMPLATE.md + - 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 files with template-action + cmds: + - task sync:configs + - task sync:ignores + - task sync:taskfiles + - task sync:workflows + + sync:configs: + desc: Sync configuration files with template-action + cmds: + - | + echo "▶️ Syncing configuration files from template-action..." + curl -sL {{.TEMPLATE_REPO_BASE_URL}}/.editorconfig -o ./.editorconfig + curl -sL {{.TEMPLATE_REPO_BASE_URL}}/.hadolint.yaml -o ./.hadolint.yaml + curl -sL {{.TEMPLATE_REPO_BASE_URL}}/.pre-commit-config.yaml -o ./.pre-commit-config.yaml + curl -sL {{.TEMPLATE_REPO_BASE_URL}}/.shellcheckrc -o ./.shellcheckrc + curl -sL {{.TEMPLATE_REPO_BASE_URL}}/.yamllint.yml -o ./.yamllint.yml + echo "✅ Synced configuration files" + + sync:ignores: + desc: Sync ignore files with template-action + cmds: + - | + echo "▶️ Syncing ignore files from template-action..." + curl -sL {{.TEMPLATE_REPO_BASE_URL}}/.gitignore -o ./.gitignore + curl -sL {{.TEMPLATE_REPO_BASE_URL}}/.dockerignore -o ./.dockerignore + echo "✅ Synced ignore files" + + sync:taskfiles: + desc: Sync Taskfiles with template-action + cmds: + - | + echo "▶️ Syncing Taskfiles from template-action..." + curl -sL {{.TEMPLATE_REPO_BASE_URL}}/Taskfile.yml -o ./Taskfile.yml + curl -sL {{.TEMPLATE_REPO_BASE_URL}}/Taskfile.cicd.yml -o ./Taskfile.cicd.yml + curl -sL {{.TEMPLATE_REPO_BASE_URL}}/Taskfile.docker.yml -o ./Taskfile.docker.yml + curl -sL {{.TEMPLATE_REPO_BASE_URL}}/Taskfile.variables.yml -o ./Taskfile.variables.yml + echo "✅ Synced Taskfiles" + + sync:workflows: + desc: Sync GitHub workflows with template-action + cmds: + - | + echo "▶️ Syncing GitHub workflows from template-action..." + mkdir -p .github/workflows + curl -sL {{.TEMPLATE_REPO_BASE_URL}}/.github/workflows/auto-create-pull-request.yml \ + -o ./.github/workflows/auto-create-pull-request.yml + curl -sL {{.TEMPLATE_REPO_BASE_URL}}/.github/workflows/auto-create-release.yml -o ./.github/workflows/auto-create-release.yml + curl -sL {{.TEMPLATE_REPO_BASE_URL}}/.github/workflows/cron-check-dependencies.yml \ + -o ./.github/workflows/cron-check-dependencies.yml + curl -sL {{.TEMPLATE_REPO_BASE_URL}}/.github/workflows/manual-update-version.yml -o ./.github/workflows/manual-update-version.yml + echo "✅ Synced GitHub workflows" 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.variables.yml b/Taskfile.variables.yml index f597bf6..e0ed9fa 100644 --- a/Taskfile.variables.yml +++ b/Taskfile.variables.yml @@ -127,6 +127,7 @@ vars: else git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown" fi + PR_TEMPLATE: https://raw.githubusercontent.com/devops-infra/.github/master/PULL_REQUEST_TEMPLATE.md # Labels for http://label-schema.org/rc1/#build-time-labels # And for https://github.com/opencontainers/image-spec/blob/master/annotations.md diff --git a/Taskfile.yml b/Taskfile.yml index 7adeaef..354f1f3 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -4,7 +4,13 @@ silent: true includes: variables: ./Taskfile.variables.yml - scripts: ./Taskfile.scripts.yml + cicd: + taskfile: ./Taskfile.cicd.yml + flatten: true + docker: + taskfile: ./Taskfile.docker.yml + flatten: true + tasks: default: @@ -15,121 +21,12 @@ tasks: help: desc: Detailed help cmds: - - task: scripts:help - - 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: - - task: scripts:lint:actionlint - - lint:hadolint: - desc: Lint Dockerfile with hadolint - cmds: - - task: scripts:lint:hadolint - - lint:shellcheck: - desc: Lint shell scripts with shellcheck - cmds: - - task: scripts:lint:shellcheck - - lint:yamllint: - desc: Lint YAML files with yamllint - cmds: - - task: scripts:lint:yamllint - - docker:login: - desc: Login to hub.docker.com and ghcr.io - internal: true - env: - DOCKER_USERNAME: '{{.DOCKER_USERNAME}}' - GITHUB_USERNAME: '{{.GITHUB_USERNAME}}' - 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:build: - desc: Build Docker image - cmds: - - '{{.DOCKER_BUILD_START}} {{.DOCKER_BUILD_FINISH}}' - - docker:build:inspect: - desc: Inspect built Docker image - cmds: - - task: scripts:docker:build:inspect - - docker:push: - desc: Build and push Docker images - deps: - - task: docker:login - cmds: - - '{{.DOCKER_BUILD_START}} --push {{.DOCKER_BUILD_FINISH}}' - - docker:push:inspect: - desc: Inspect built Docker image - cmds: - - task: scripts:docker:push:inspect - - docker:cmds: - desc: Show full docker build command - cmds: - - echo -e '{{.DOCKER_BUILD_START}} {{.DOCKER_BUILD_FINISH}}' | {{.SED}} 's/--/ \\\n --/g' - - version:get: - desc: Get current version - cmds: - - echo "{{.VERSION}}" - - version:set: - desc: Update version in README.md and action.yml to specified VERSION - cmds: - - task: scripts:version:set - - version:tag-release: - desc: Create set of git tags - cmds: - - task: scripts:version:tag-release - - 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 - - git:get-pr-template: - desc: Get pull request template - cmds: - - task: scripts:git:get-pr-template - - git:set-config: - desc: Set git user config - cmds: - - task: scripts:git:set-config + - | + echo "Tasks:" + task --list + echo "" + echo "Environment:" + echo " DOCKER_NAME={{.DOCKER_NAME}} DOCKER_USERNAME={{.DOCKER_USERNAME}}" + echo " GHRC_NAME={{.GHRC_NAME}} GITHUB_USERNAME={{.GITHUB_USERNAME}}" + echo " LAST_RELEASE={{.LAST_RELEASE}}" VERSION={{.VERSION}} VERSION_FULL={{.VERSION_FULL}} + echo " BRANCH={{.GIT_BRANCH}} GIT_SHORT_SHA={{.GIT_SHORT_SHA}}" GIT_SHA={{.GIT_SHA}}