diff --git a/.github/workflows/reusable-auto-pull-request-create.yml b/.github/workflows/reusable-auto-pull-request-create.yml index 81b5caf..b1e5a22 100644 --- a/.github/workflows/reusable-auto-pull-request-create.yml +++ b/.github/workflows/reusable-auto-pull-request-create.yml @@ -138,16 +138,12 @@ jobs: echo "EOF" } >> "$GITHUB_OUTPUT" - - name: Resolve CST image + - name: Get test image ref id: cst-image if: steps.cst-configs.outputs.has_tests == 'true' run: | - if task --list | grep -q "docker:image:test:ref"; then - IMAGE_REF="$(task docker:image:test:ref)" - else - VERSION="$(task version:get)" - IMAGE_REF="devopsinfra/${{ github.event.repository.name }}:${VERSION}-test" - fi + VERSION="$(task version:get)" + IMAGE_REF="devopsinfra/${{ github.event.repository.name }}:${VERSION}-test" echo "image=$IMAGE_REF" >> "$GITHUB_OUTPUT" - name: Run container structure tests diff --git a/.github/workflows/reusable-cron-dependency-update.yml b/.github/workflows/reusable-cron-dependency-update.yml index 22d6f96..c744006 100644 --- a/.github/workflows/reusable-cron-dependency-update.yml +++ b/.github/workflows/reusable-cron-dependency-update.yml @@ -145,17 +145,13 @@ jobs: echo "EOF" } >> "$GITHUB_OUTPUT" - - name: Resolve CST image + - name: Get test image ref id: cst-image if: (inputs.profile == 'actions' || inputs.profile == 'dockerized') && steps.cst-configs.outputs.has_tests == 'true' continue-on-error: true run: | - if task --list | grep -q "docker:image:test:ref"; then - IMAGE_REF="$(task docker:image:test:ref)" - else - VERSION="$(task version:get)" - IMAGE_REF="devopsinfra/${{ github.event.repository.name }}:${VERSION}-test" - fi + VERSION="$(task version:get)" + IMAGE_REF="devopsinfra/${{ github.event.repository.name }}:${VERSION}-test" echo "image=$IMAGE_REF" >> "$GITHUB_OUTPUT" - name: Run container structure tests diff --git a/.github/workflows/reusable-manual-release-create.yml b/.github/workflows/reusable-manual-release-create.yml index 83727fe..7a2e9f6 100644 --- a/.github/workflows/reusable-manual-release-create.yml +++ b/.github/workflows/reusable-manual-release-create.yml @@ -119,18 +119,14 @@ jobs: echo "EOF" } >> "$GITHUB_OUTPUT" - - name: Resolve CST image + - name: Get test image ref id: cst-image if: ${{ inputs.build-and-push-only && (inputs.profile == 'actions' || inputs.profile == 'dockerized') && steps.cst-configs.outputs.has_tests == 'true' }} env: VERSION_SUFFIX: '' run: | - if task --list | grep -q "docker:image:test:ref"; then - IMAGE_REF="$(task docker:image:test:ref)" - else - VERSION="$(task version:get)" - IMAGE_REF="devopsinfra/${{ github.event.repository.name }}:${VERSION}" - fi + VERSION="$(task version:get)" + IMAGE_REF="devopsinfra/${{ github.event.repository.name }}:${VERSION}" echo "image=$IMAGE_REF" >> "$GITHUB_OUTPUT" - name: Run container structure tests diff --git a/Taskfile.cicd.yml b/Taskfile.cicd.yml index 26a6e1b..f21bc3f 100644 --- a/Taskfile.cicd.yml +++ b/Taskfile.cicd.yml @@ -110,11 +110,6 @@ tasks: exit $rc fi - version:get: - desc: Get current version - cmds: - - echo "{{.VERSION}}" - dependency:update: desc: Check main dependency not covered by dependabot cmds: @@ -281,42 +276,7 @@ tasks: - 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}}/.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 .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 - git add .gitignore - echo "✅ Synced ignore files" - - sync:taskfiles: - desc: Sync Taskfiles with devops-infra/.github + version:get: + desc: Get current version 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.variables.yml -o ./Taskfile.variables.yml - git add Taskfile*.yml - echo "✅ Synced Taskfiles" + - echo "{{.VERSION}}" diff --git a/Taskfile.yml b/Taskfile.yml index d3428d8..fa10a3a 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -11,7 +11,6 @@ includes: taskfile: ./Taskfile.cicd.yml flatten: true - tasks: default: desc: List tasks diff --git a/templates/actions/configs/.dockerignore b/templates/actions/configs/.dockerignore index ef1376f..1bc823c 100644 --- a/templates/actions/configs/.dockerignore +++ b/templates/actions/configs/.dockerignore @@ -2,6 +2,7 @@ * # Include +!alpine-packages.txt !Dockerfile !LICENSE !README.md diff --git a/templates/actions/configs/alpine-packages.txt b/templates/actions/configs/alpine-packages.txt new file mode 100644 index 0000000..5137600 --- /dev/null +++ b/templates/actions/configs/alpine-packages.txt @@ -0,0 +1 @@ +bash~=5.3 diff --git a/templates/actions/taskfiles/Taskfile.cicd.yml b/templates/actions/taskfiles/Taskfile.cicd.yml index 17a702b..9b8ca64 100644 --- a/templates/actions/taskfiles/Taskfile.cicd.yml +++ b/templates/actions/taskfiles/Taskfile.cicd.yml @@ -90,11 +90,6 @@ tasks: exit $rc fi - version:get: - desc: Get current version - cmds: - - echo "{{.VERSION}}" - dependency:update: desc: Check main dependency not covered by dependabot cmds: @@ -272,44 +267,7 @@ tasks: - 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 + version:get: + desc: Get current version 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.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" + - echo "{{.VERSION}}" diff --git a/templates/actions/taskfiles/Taskfile.docker.yml b/templates/actions/taskfiles/Taskfile.docker.yml index a05bc36..f0f0baa 100644 --- a/templates/actions/taskfiles/Taskfile.docker.yml +++ b/templates/actions/taskfiles/Taskfile.docker.yml @@ -3,11 +3,6 @@ version: '3' silent: true tasks: - docker:image:test:ref: - desc: Print test image reference for CST - cmds: - - echo "{{.DOCKER_NAME}}:{{.VERSION_FULL}}{{.VERSION_SUFFIX}}" - docker:login: desc: Login to hub.docker.com and ghcr.io cmds: @@ -48,7 +43,7 @@ tasks: docker:cmds: desc: Show full docker build command cmds: - - echo -e '{{.DOCKER_BUILD_START}} {{.DOCKER_BUILD_FINISH}}' | {{.SED}} 's/--/ \\\n --/g' + - echo -e '{{.DOCKER_BUILD_START}} {{.DOCKER_BUILD_FINISH}}' | {{.SED}} 's/--/ \\\n+ --/g' docker:build: desc: Build Docker image @@ -85,7 +80,6 @@ tasks: 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 \ diff --git a/templates/actions/taskfiles/Taskfile.scripts.yml b/templates/actions/taskfiles/Taskfile.scripts.yml index 3a24938..3c405b3 100644 --- a/templates/actions/taskfiles/Taskfile.scripts.yml +++ b/templates/actions/taskfiles/Taskfile.scripts.yml @@ -86,19 +86,23 @@ tasks: - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - scripts:dependency:update: - desc: Update Alpine apk package constraints in Dockerfile + packages:update: + desc: Update Alpine package pins in alpine-packages.txt cmds: - | set -eu if [ ! -f Dockerfile ]; then - echo "ℹ️ Dockerfile not found; nothing to update" + echo "INFO: Dockerfile not found; nothing to update" + exit 0 + fi + if [ ! -f alpine-packages.txt ]; then + echo "INFO: alpine-packages.txt not found; nothing to update" exit 0 fi base_image="$(sed -nE 's/^FROM[[:space:]]+([^[:space:]]+).*/\1/p' Dockerfile | head -1)" if [ -z "$base_image" ]; then - echo "ℹ️ Could not resolve base image; nothing to update" + echo "INFO: Could not resolve base image; nothing to update" exit 0 fi @@ -107,44 +111,108 @@ tasks: : ;; *) - echo "ℹ️ Base image is '$base_image', not Alpine; nothing to update" + echo "INFO: Base image is '$base_image', not Alpine; nothing to update" exit 0 ;; esac + alpine_line="${base_image#alpine:}" + if [ "$alpine_line" = "$base_image" ] || [ -z "$alpine_line" ]; then + echo "INFO: Could not parse Alpine version from '$base_image'; nothing to update" + exit 0 + fi + alpine_minor="$(printf '%s' "$alpine_line" | awk -F. '{print $1 "." $2}')" + if ! printf '%s' "$alpine_minor" | grep -Eq '^[0-9]+\.[0-9]+$'; then + echo "INFO: Unsupported Alpine version '$alpine_line'; nothing to update" + exit 0 + fi + alpine_repo="v${alpine_minor}" + arch="x86_64" + normalize_minor() { version="$1" - printf '%s' "$version" | awk -F. '{print $1 "." $2}' + printf '%s' "$version" | sed -E 's/^([0-9]+\.[0-9]+).*/\1/' + } + + fetch_index() { + repo="$1" + out="$2" + url="https://dl-cdn.alpinelinux.org/alpine/${alpine_repo}/${repo}/${arch}/APKINDEX.tar.gz" + curl --fail --silent --show-error "$url" | tar -O -zx APKINDEX > "$out" + } + + lookup_latest() { + pkg="$1" + for index in "$index_main" "$index_community"; do + found="$(awk -v pkg="$pkg" ' + BEGIN { RS=""; FS="\n" } + { + p=""; v="" + for (i=1; i<=NF; i++) { + if ($i ~ /^P:/) p=substr($i,3) + if ($i ~ /^V:/) v=substr($i,3) + } + if (p==pkg) { print v; exit } + } + ' "$index")" + if [ -n "$found" ]; then + printf '%s' "$found" + return 0 + fi + done + return 1 } - list_file=".tmp/dependency-update-apk-list.txt" mkdir -p .tmp + index_main=".tmp/apkindex-main-${alpine_repo}-${arch}.txt" + index_community=".tmp/apkindex-community-${alpine_repo}-${arch}.txt" + fetch_index main "$index_main" + fetch_index community "$index_community" - sed -nE 's/^\s*([a-zA-Z0-9+_.-]+)=~=?([0-9]+\.[0-9]+).*$/\1 \2/p' Dockerfile > "$list_file" - if [ ! -s "$list_file" ]; then - echo "ℹ️ No pinned apk constraints (~=) found in Dockerfile" + if ! grep -Eq '^[a-zA-Z0-9+_.-]+(=~|~=)[0-9]+\.[0-9]+$' alpine-packages.txt; then + echo "INFO: No pinned Alpine packages (~=X.Y) found in alpine-packages.txt" exit 0 fi + tmp_out=".tmp/alpine-packages.updated.txt" + : > "$tmp_out" updated=0 - while read -r pkg current_minor; do - [ -n "$pkg" ] || continue - latest_full="$(docker run --rm "$base_image" sh -lc "apk update >/dev/null && apk list --all '$pkg' 2>/dev/null | head -1 | awk -F'[- ]' '{print \\$2}'")" + while IFS= read -r line || [ -n "$line" ]; do + if [ -z "$line" ] || printf '%s' "$line" | grep -Eq '^[[:space:]]*#'; then + echo "$line" >> "$tmp_out" + continue + fi + if ! printf '%s' "$line" | grep -Eq '^[a-zA-Z0-9+_.-]+(=~|~=)[0-9]+\.[0-9]+$'; then + echo "$line" >> "$tmp_out" + continue + fi + + pkg="$(printf '%s' "$line" | sed -E 's/^([a-zA-Z0-9+_.-]+)(=~|~=).*/\1/')" + current_minor="$(printf '%s' "$line" | sed -E 's/^[a-zA-Z0-9+_.-]+(=~|~=)([0-9]+\.[0-9]+).*$/\2/')" + latest_full="$(lookup_latest "$pkg" || true)" if [ -z "$latest_full" ]; then - echo "⚠️ Could not resolve latest version for $pkg; skipping" + echo "WARN: Could not resolve latest version for $pkg; keeping $line" + echo "$line" >> "$tmp_out" continue fi + latest_minor="$(normalize_minor "$latest_full")" if [ "$latest_minor" = "$current_minor" ]; then - echo "✅ $pkg already up to date at $current_minor" + echo "OK: $pkg already up to date at $current_minor" + echo "$pkg~=$current_minor" >> "$tmp_out" continue fi - echo "⬆️ $pkg: $current_minor -> $latest_minor" - {{.SED}} -i "s#\<$pkg\>=~=$current_minor#$pkg~=$latest_minor#g" Dockerfile - {{.SED}} -i "s#\<$pkg\>~=$current_minor#$pkg~=$latest_minor#g" Dockerfile + echo "UPDATE: $pkg $current_minor -> $latest_minor" + echo "$pkg~=$latest_minor" >> "$tmp_out" updated=1 - done < "$list_file" + done < alpine-packages.txt + + if ! cmp -s alpine-packages.txt "$tmp_out"; then + mv "$tmp_out" alpine-packages.txt + else + rm -f "$tmp_out" + fi if [ "$updated" -eq 0 ]; then - echo "ℹ️ No apk dependency updates were required" + echo "INFO: No Alpine package updates were required" fi diff --git a/templates/actions/taskfiles/Taskfile.yml b/templates/actions/taskfiles/Taskfile.yml index 279ab9f..a065cbc 100644 --- a/templates/actions/taskfiles/Taskfile.yml +++ b/templates/actions/taskfiles/Taskfile.yml @@ -15,7 +15,6 @@ includes: taskfile: ./Taskfile.docker.yml flatten: true - tasks: default: desc: List tasks diff --git a/templates/dockerized/configs/.dockerignore b/templates/dockerized/configs/.dockerignore index ef1376f..1bc823c 100644 --- a/templates/dockerized/configs/.dockerignore +++ b/templates/dockerized/configs/.dockerignore @@ -2,6 +2,7 @@ * # Include +!alpine-packages.txt !Dockerfile !LICENSE !README.md diff --git a/templates/dockerized/configs/alpine-packages.txt b/templates/dockerized/configs/alpine-packages.txt new file mode 100644 index 0000000..5137600 --- /dev/null +++ b/templates/dockerized/configs/alpine-packages.txt @@ -0,0 +1 @@ +bash~=5.3 diff --git a/templates/dockerized/taskfiles/Taskfile.cicd.yml b/templates/dockerized/taskfiles/Taskfile.cicd.yml index af424b9..aee3248 100644 --- a/templates/dockerized/taskfiles/Taskfile.cicd.yml +++ b/templates/dockerized/taskfiles/Taskfile.cicd.yml @@ -90,11 +90,6 @@ tasks: exit $rc fi - version:get: - desc: Get current version - cmds: - - echo "{{.VERSION}}" - dependency:update: desc: Check main dependency not covered by dependabot cmds: @@ -261,45 +256,7 @@ tasks: - 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 + version:get: + desc: Get current version 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" + - echo "{{.VERSION}}" diff --git a/templates/dockerized/taskfiles/Taskfile.docker.yml b/templates/dockerized/taskfiles/Taskfile.docker.yml index a05bc36..f0f0baa 100644 --- a/templates/dockerized/taskfiles/Taskfile.docker.yml +++ b/templates/dockerized/taskfiles/Taskfile.docker.yml @@ -3,11 +3,6 @@ version: '3' silent: true tasks: - docker:image:test:ref: - desc: Print test image reference for CST - cmds: - - echo "{{.DOCKER_NAME}}:{{.VERSION_FULL}}{{.VERSION_SUFFIX}}" - docker:login: desc: Login to hub.docker.com and ghcr.io cmds: @@ -48,7 +43,7 @@ tasks: docker:cmds: desc: Show full docker build command cmds: - - echo -e '{{.DOCKER_BUILD_START}} {{.DOCKER_BUILD_FINISH}}' | {{.SED}} 's/--/ \\\n --/g' + - echo -e '{{.DOCKER_BUILD_START}} {{.DOCKER_BUILD_FINISH}}' | {{.SED}} 's/--/ \\\n+ --/g' docker:build: desc: Build Docker image @@ -85,7 +80,6 @@ tasks: 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 \ diff --git a/templates/dockerized/taskfiles/Taskfile.scripts.yml b/templates/dockerized/taskfiles/Taskfile.scripts.yml index ea927c5..21e4e63 100644 --- a/templates/dockerized/taskfiles/Taskfile.scripts.yml +++ b/templates/dockerized/taskfiles/Taskfile.scripts.yml @@ -87,19 +87,23 @@ tasks: - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - scripts:dependency:update: - desc: Update Alpine apk package constraints in Dockerfile + packages:update: + desc: Update Alpine package pins in alpine-packages.txt cmds: - | set -eu if [ ! -f Dockerfile ]; then - echo "ℹ️ Dockerfile not found; nothing to update" + echo "INFO: Dockerfile not found; nothing to update" + exit 0 + fi + if [ ! -f alpine-packages.txt ]; then + echo "INFO: alpine-packages.txt not found; nothing to update" exit 0 fi base_image="$(sed -nE 's/^FROM[[:space:]]+([^[:space:]]+).*/\1/p' Dockerfile | head -1)" if [ -z "$base_image" ]; then - echo "ℹ️ Could not resolve base image; nothing to update" + echo "INFO: Could not resolve base image; nothing to update" exit 0 fi @@ -108,46 +112,110 @@ tasks: : ;; *) - echo "ℹ️ Base image is '$base_image', not Alpine; nothing to update" + echo "INFO: Base image is '$base_image', not Alpine; nothing to update" exit 0 ;; esac + alpine_line="${base_image#alpine:}" + if [ "$alpine_line" = "$base_image" ] || [ -z "$alpine_line" ]; then + echo "INFO: Could not parse Alpine version from '$base_image'; nothing to update" + exit 0 + fi + alpine_minor="$(printf '%s' "$alpine_line" | awk -F. '{print $1 "." $2}')" + if ! printf '%s' "$alpine_minor" | grep -Eq '^[0-9]+\.[0-9]+$'; then + echo "INFO: Unsupported Alpine version '$alpine_line'; nothing to update" + exit 0 + fi + alpine_repo="v${alpine_minor}" + arch="x86_64" + normalize_minor() { version="$1" - printf '%s' "$version" | awk -F. '{print $1 "." $2}' + printf '%s' "$version" | sed -E 's/^([0-9]+\.[0-9]+).*/\1/' + } + + fetch_index() { + repo="$1" + out="$2" + url="https://dl-cdn.alpinelinux.org/alpine/${alpine_repo}/${repo}/${arch}/APKINDEX.tar.gz" + curl --fail --silent --show-error "$url" | tar -O -zx APKINDEX > "$out" + } + + lookup_latest() { + pkg="$1" + for index in "$index_main" "$index_community"; do + found="$(awk -v pkg="$pkg" ' + BEGIN { RS=""; FS="\n" } + { + p=""; v="" + for (i=1; i<=NF; i++) { + if ($i ~ /^P:/) p=substr($i,3) + if ($i ~ /^V:/) v=substr($i,3) + } + if (p==pkg) { print v; exit } + } + ' "$index")" + if [ -n "$found" ]; then + printf '%s' "$found" + return 0 + fi + done + return 1 } - list_file=".tmp/dependency-update-apk-list.txt" mkdir -p .tmp + index_main=".tmp/apkindex-main-${alpine_repo}-${arch}.txt" + index_community=".tmp/apkindex-community-${alpine_repo}-${arch}.txt" + fetch_index main "$index_main" + fetch_index community "$index_community" - sed -nE 's/^\s*([a-zA-Z0-9+_.-]+)=~=?([0-9]+\.[0-9]+).*$/\1 \2/p' Dockerfile > "$list_file" - if [ ! -s "$list_file" ]; then - echo "ℹ️ No pinned apk constraints (~=) found in Dockerfile" + if ! grep -Eq '^[a-zA-Z0-9+_.-]+(=~|~=)[0-9]+\.[0-9]+$' alpine-packages.txt; then + echo "INFO: No pinned Alpine packages (~=X.Y) found in alpine-packages.txt" exit 0 fi + tmp_out=".tmp/alpine-packages.updated.txt" + : > "$tmp_out" updated=0 - while read -r pkg current_minor; do - [ -n "$pkg" ] || continue - latest_full="$(docker run --rm "$base_image" sh -lc "apk update >/dev/null && apk list --all '$pkg' 2>/dev/null | head -1 | awk -F'[- ]' '{print \\$2}'")" + while IFS= read -r line || [ -n "$line" ]; do + if [ -z "$line" ] || printf '%s' "$line" | grep -Eq '^[[:space:]]*#'; then + echo "$line" >> "$tmp_out" + continue + fi + if ! printf '%s' "$line" | grep -Eq '^[a-zA-Z0-9+_.-]+(=~|~=)[0-9]+\.[0-9]+$'; then + echo "$line" >> "$tmp_out" + continue + fi + + pkg="$(printf '%s' "$line" | sed -E 's/^([a-zA-Z0-9+_.-]+)(=~|~=).*/\1/')" + current_minor="$(printf '%s' "$line" | sed -E 's/^[a-zA-Z0-9+_.-]+(=~|~=)([0-9]+\.[0-9]+).*$/\2/')" + latest_full="$(lookup_latest "$pkg" || true)" if [ -z "$latest_full" ]; then - echo "⚠️ Could not resolve latest version for $pkg; skipping" + echo "WARN: Could not resolve latest version for $pkg; keeping $line" + echo "$line" >> "$tmp_out" continue fi + latest_minor="$(normalize_minor "$latest_full")" if [ "$latest_minor" = "$current_minor" ]; then - echo "✅ $pkg already up to date at $current_minor" + echo "OK: $pkg already up to date at $current_minor" + echo "$pkg~=$current_minor" >> "$tmp_out" continue fi - echo "⬆️ $pkg: $current_minor -> $latest_minor" - {{.SED}} -i "s#\<$pkg\>=~=$current_minor#$pkg~=$latest_minor#g" Dockerfile - {{.SED}} -i "s#\<$pkg\>~=$current_minor#$pkg~=$latest_minor#g" Dockerfile + echo "UPDATE: $pkg $current_minor -> $latest_minor" + echo "$pkg~=$latest_minor" >> "$tmp_out" updated=1 - done < "$list_file" + done < alpine-packages.txt + + if ! cmp -s alpine-packages.txt "$tmp_out"; then + mv "$tmp_out" alpine-packages.txt + else + rm -f "$tmp_out" + fi if [ "$updated" -eq 0 ]; then - echo "ℹ️ No apk dependency updates were required" + echo "INFO: No Alpine package updates were required" fi version:get: diff --git a/templates/other/taskfiles/Taskfile.cicd.yml b/templates/other/taskfiles/Taskfile.cicd.yml index 08e3794..05c86cb 100644 --- a/templates/other/taskfiles/Taskfile.cicd.yml +++ b/templates/other/taskfiles/Taskfile.cicd.yml @@ -85,11 +85,6 @@ tasks: exit $rc fi - version:get: - desc: Get current version - cmds: - - echo "{{.VERSION}}" - dependency:update: desc: Check main dependency not covered by dependabot cmds: @@ -256,42 +251,7 @@ tasks: - 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}}/.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 .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 - git add .gitignore - echo "✅ Synced ignore files" - - sync:taskfiles: - desc: Sync Taskfiles with devops-infra/.github + version:get: + desc: Get current version 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.variables.yml -o ./Taskfile.variables.yml - git add Taskfile*.yml - echo "✅ Synced Taskfiles" + - echo "{{.VERSION}}" diff --git a/templates/static/taskfiles/Taskfile.cicd.yml b/templates/static/taskfiles/Taskfile.cicd.yml index 577491c..5233735 100644 --- a/templates/static/taskfiles/Taskfile.cicd.yml +++ b/templates/static/taskfiles/Taskfile.cicd.yml @@ -40,11 +40,6 @@ tasks: cmds: - task scripts:lint:yamllint - version:get: - desc: Get current version - cmds: - - task scripts:version:get - dependency:update: desc: Check main dependency not covered by dependabot cmds: @@ -135,39 +130,7 @@ tasks: cmds: - task scripts:git:set-config - sync:all: - desc: Sync all common files - cmds: - - task sync:configs - - task sync:ignores - - task sync:taskfiles - - sync:configs: - desc: Sync configuration files from .github static template - cmds: - - | - echo "Syncing configuration files from devops-infra/.github..." - curl -fsSL {{.CONFIGS_BASE_URL}}/.editorconfig -o ./.editorconfig - 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 .pre-commit-config.yaml .shellcheckrc .yamllint.yml - - sync:ignores: - desc: Sync ignore files from .github static template - cmds: - - | - echo "Syncing ignore files from devops-infra/.github..." - curl -fsSL {{.CONFIGS_BASE_URL}}/.gitignore -o ./.gitignore - git add .gitignore - - sync:taskfiles: - desc: Sync Taskfiles from .github static template + version:get: + desc: Get current version 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.variables.yml -o ./Taskfile.variables.yml - curl -fsSL {{.TASKFILES_BASE_URL}}/Taskfile.scripts.yml -o ./Taskfile.scripts.yml - git add Taskfile*.yml + - task scripts:version:get