Skip to content

Merge pull request #26 from hfcRed/dev #87

Merge pull request #26 from hfcRed/dev

Merge pull request #26 from hfcRed/dev #87

Workflow file for this run

name: CI
on:
pull_request:
branches: [ main ]
push:
branches: [ main ]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
version-check:
name: Verify .version is ahead of latest release
runs-on: ubuntu-latest
outputs:
release_version: ${{ steps.validate.outputs.version }}
steps:
- name: Checkout (with tags)
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Validate .version bump uses file-as-truth
id: validate
run: |
set -euo pipefail
parse_semver() {
local input="${1#v}"
if [[ "$input" =~ ^([0-9]+)\.([0-9]+)(\.[0-9]+)?$ ]]; then
local major="${BASH_REMATCH[1]}"
local minor="${BASH_REMATCH[2]}"
local patch="${BASH_REMATCH[3]#.}"
if [[ -z "$patch" ]]; then patch=0; fi
echo "${major} ${minor} ${patch}"
else
return 1
fi
}
if [ ! -f .version ]; then
echo ".version file is missing."
exit 1
fi
RAW=$(tr -d '[:space:]' < .version)
if [ -z "$RAW" ]; then
echo ".version file is empty."
exit 1
fi
if ! desired_parts="$(parse_semver "$RAW")"; then
echo ".version must be a valid semver string (e.g. 1.2, 1.2.3, v1.2.3)."
exit 1
fi
read -r desired_major desired_minor desired_patch <<<"$desired_parts"
version="${desired_major}.${desired_minor}.${desired_patch}"
tag=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -n1)
if [ -z "$tag" ]; then
echo "No previous release tags found; accepting .version=$version."
echo "version=${version}" >> "$GITHUB_OUTPUT"
exit 0
fi
if ! current_parts="$(parse_semver "$tag")"; then
echo "Latest tag \"$tag\" is not a valid semver tag."
exit 1
fi
read -r current_major current_minor current_patch <<<"$current_parts"
echo "Latest release tag: $tag"
echo "Proposed .version: $version"
if (( desired_major > current_major )) || (( desired_major == current_major && desired_minor > current_minor )) || (( desired_major == current_major && desired_minor == current_minor && desired_patch > current_patch )); then
echo ".version (${version}) is greater than the latest release tag (${tag})."
echo "version=${version}" >> "$GITHUB_OUTPUT"
exit 0
fi
echo ".version must be greater than the latest release tag (${tag})."
exit 1
test:
name: Run Tests and Checks
runs-on: ubuntu-latest
needs: version-check
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.12.4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run Biome check
run: pnpm biome check
- name: TypeScript type check
run: pnpm tsc --noEmit
- name: Run tests
run: pnpm test
build-and-push:
name: Build and Push Image
needs:
- test
- version-check
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && github.actor != 'github-actions[bot]'
runs-on: ubuntu-latest
environment: production
# Expose next version to the release job
outputs:
release_version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Use release version from .version (propagated)
id: version
shell: bash
run: |
VERSION="${{ needs.version-check.outputs.release_version }}"
if [ -z "$VERSION" ]; then
echo "Version not provided by version-check job."
exit 1
fi
echo "Release version: ${VERSION} (from .version)"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- name: Install doctl
uses: digitalocean/action-doctl@v2
with:
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
- name: Log in to DigitalOcean Container Registry
run: doctl registry login --expiry-seconds 1800
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push image
id: build-image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
build-args: |
APP_VERSION=${{ steps.version.outputs.version }}
tags: |
${{ secrets.DO_REGISTRY_REPOSITORY }}/agent8s:latest
${{ secrets.DO_REGISTRY_REPOSITORY }}/agent8s:${{ github.sha }}
${{ secrets.DO_REGISTRY_REPOSITORY }}/agent8s:v${{ steps.version.outputs.version }}
release:
name: Tag repo and create GitHub Release
needs: build-and-push
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && github.actor != 'github-actions[bot]'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Configure git (for annotated tag)
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Create and push tag (idempotent)
env:
REL: ${{ needs.build-and-push.outputs.release_version }}
run: |
TAG="v${REL}"
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
echo "Tag $TAG already exists; skipping push."
else
git tag -a "$TAG" -m "release: $TAG"
git push origin "$TAG"
fi
- name: Create GitHub release (idempotent)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REL: ${{ needs.build-and-push.outputs.release_version }}
run: |
TAG="v${REL}"
if gh release view "$TAG" >/dev/null 2>&1; then
echo "Release $TAG already exists; nothing to do."
else
gh release create "$TAG" \
--repo="$GITHUB_REPOSITORY" \
--title="Agent 8s v${REL}" \
--generate-notes
fi