Skip to content

Dev

Dev #63

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:
test:
name: Run Tests and Checks
runs-on: ubuntu-latest
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
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: Compute release version (accepts .version=2.3 / 2.3.0 / v2.3.0)
id: version
run: |
node <<'NODE'
const cp = require('node:child_process');
const fs = require('node:fs');
function parseSemver(input) {
const m = String(input || '').trim().replace(/^v/, '').match(/^(\d+)\.(\d+)(?:\.(\d+))?$/);
if (!m) return null;
return { major:+m[1], minor:+m[2], patch: m[3] !== undefined ? +m[3] : 0 };
}
function cmp(a, b) {
if (a.major !== b.major) return a.major - b.major;
if (a.minor !== b.minor) return a.minor - b.minor;
return a.patch - b.patch;
}
function latestTag() {
try {
const out = cp.execSync(
"git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -n1",
{ encoding: 'utf8' }
).trim();
return out || null;
} catch { return null; }
}
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
const fromPkg = parseSemver(pkg.version) || { major:0, minor:1, patch:0 };
const lt = latestTag();
const current = lt ? parseSemver(lt) : fromPkg;
let target;
let reason = 'defaulting to patch bump';
if (fs.existsSync('.version')) {
const raw = fs.readFileSync('.version', 'utf8').trim();
const desired = parseSemver(raw);
if (desired) {
if (cmp(desired, current) > 0) {
target = `${desired.major}.${desired.minor}.${desired.patch}`;
reason = `using override ${raw} -> ${target}`;
} else {
reason = `override "${raw}" is not greater than current ${current.major}.${current.minor}.${current.patch}`;
}
} else if (raw) {
reason = `ignoring invalid override "${raw}"`;
}
}
if (!target) {
target = `${current.major}.${current.minor}.${current.patch + 1}`;
}
console.log(`Release version: ${target} (${reason})`);
require('fs').appendFileSync(process.env.GITHUB_OUTPUT, `version=${target}\n`);
NODE
- 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