Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/auto-release-create.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ on:
branches:
- master
- main
workflow_dispatch:
inputs:
release_branch:
description: Release branch to publish from (e.g. release/v1.3.0)
required: false
default: ''
type: string
release_version:
description: Explicit release version override (e.g. v1.3.0)
required: false
default: ''
type: string

permissions:
contents: write
Expand All @@ -17,4 +29,6 @@ jobs:
runs-on: ubuntu-24.04-arm
task-version: 3.x
profile: other
release-branch: ${{ inputs.release_branch }}
release-version: ${{ inputs.release_version }}
secrets: inherit
42 changes: 38 additions & 4 deletions .github/workflows/reusable-auto-release-create.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ on:
description: Repository profile (actions, dockerized, other, static)
type: string
default: actions
release-branch:
description: Explicit release branch to publish from (for manual dispatch), e.g. release/v1.3.0
type: string
required: false
default: ''
release-version:
description: Explicit release version (for manual dispatch), e.g. v1.3.0
type: string
required: false
default: ''

permissions:
contents: write
Expand All @@ -41,6 +51,31 @@ jobs:
uses: actions/github-script@v9
with:
script: |
const inputReleaseBranch = `${{ inputs.release-branch }}`.trim()
const inputReleaseVersion = `${{ inputs.release-version }}`.trim()

const emitVersion = (branchRef, explicitVersion) => {
const rawVersion = explicitVersion || branchRef.replace(/^release\//, '')
if (!/^v\d+\.\d+\.\d+$/.test(rawVersion)) {
core.setFailed(`Resolved release version '${rawVersion}' is invalid. Expected 'vX.Y.Z'.`)
return false
}
core.setOutput('should_release', 'true')
core.setOutput('release_branch', branchRef)
core.setOutput('release_version', rawVersion)
return true
}

if (inputReleaseBranch) {
if (!inputReleaseBranch.startsWith('release/')) {
core.setFailed(`Input release-branch '${inputReleaseBranch}' must start with 'release/'`)
return
}
core.info(`Using manual dispatch release branch '${inputReleaseBranch}'`)
emitVersion(inputReleaseBranch, inputReleaseVersion)
return
}

const owner = context.repo.owner
const repo = context.repo.repo
const sha = context.sha
Expand All @@ -61,15 +96,14 @@ jobs:
}

core.info(`Matched merged release PR #${match.number} from ${match.head.ref} to ${match.base.ref}`)
core.setOutput('should_release', 'true')
core.setOutput('release_branch', match.head.ref)
emitVersion(match.head.ref, inputReleaseVersion)

- name: Resolve release version
- name: Resolve release version from release branch
id: version
if: steps.release-pr.outputs.should_release == 'true'
run: |
set -eu
version="$(task version:get)"
version='${{ steps.release-pr.outputs.release_version }}'
if [ -z "$version" ]; then
echo "❌ ERROR: Resolved version is empty"
exit 1
Expand Down
32 changes: 4 additions & 28 deletions Taskfile.cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ tasks:
desc: Run all linters (Dockerfile, shell scripts, workflows, YAML)
cmds:
- task: lint:actionlint
- task: lint:hadolint
- task: lint:shellcheck
- task: lint:yamllint

Expand All @@ -42,26 +41,6 @@ tasks:
exit $rc
fi

lint:hadolint:
desc: Lint Dockerfile with hadolint
cmds:
- |
echo "▶️ Running hadolint..."
if [ ! -f Dockerfile ]; then
echo "ℹ️ No Dockerfile found, skipping hadolint"
exit 0
fi
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
shell: bash
Expand Down Expand Up @@ -95,12 +74,8 @@ tasks:
cmds:
- |
echo "▶️ Running yamllint..."
config_file=".yamllint.yml"
if [ ! -f "$config_file" ]; then
config_file="templates/other/configs/.yamllint.yml"
fi
set +e
docker run --rm -i -v "$PWD:/work" -w /work cytopia/yamllint -c "$config_file" .github .github/workflows templates
docker run --rm -i -v "$PWD:/work" -w /work cytopia/yamllint -c .yamllint.yml .
rc=$?
set -e
if [ "$rc" -eq 0 ]; then
Expand All @@ -114,9 +89,9 @@ tasks:
desc: Check main dependency not covered by dependabot
cmds:
- |
echo "ℹ️ No dedicated dependency updater configured for this repository."
echo "ℹ️ No dedicated dependency updater configured for this repository profile."
echo "ℹ️ Dependabot handles GitHub Actions and package metadata updates."
echo "ℹ️ Keeping dependency checks as a safe no-op for now."
echo "ℹ️ Keep this task as a safe no-op until a repo-specific dependency updater is defined."

version:set:
desc: Validate version
Expand All @@ -130,6 +105,7 @@ tasks:
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:
Expand Down
208 changes: 208 additions & 0 deletions Taskfile.scripts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
version: '3'

silent: true

tasks:
help:
desc: Detailed help
cmds:
- |
echo "Tasks:"
task --list

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..."
if [ ! -f Dockerfile ]; then
echo "ℹ️ No Dockerfile found, skipping hadolint"
exit 0
fi
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
shell: bash
cmds:
- |
echo "▶️ Running shellcheck..."
mapfile -t files < <(git ls-files '*.sh')
existing_files=()
for f in "${files[@]}"; do
if [ -f "$f" ]; then
existing_files+=("$f")
fi
done
if [ "${#existing_files[@]}" -eq 0 ]; then
echo "ℹ️ No shell scripts found, skipping shellcheck"
exit 0
fi
set +e
docker run --rm -i -v "$PWD:/work" -w /work koalaman/shellcheck:stable -x -S style -- "${existing_files[@]}"
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/master/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
Loading
Loading