Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8c653e6
feat(platform-server): add devspace k3d workflow
casey-brooks Mar 5, 2026
066dc17
fix(platform-server): align devspace release naming
casey-brooks Mar 5, 2026
f18137a
feat(platform-server): add optional port-forward profile
casey-brooks Mar 5, 2026
a83481a
refactor(platform-server): simplify devspace workflow
casey-brooks Mar 5, 2026
6b5acc8
docs(platform-server): streamline devspace prerequisites
casey-brooks Mar 5, 2026
1f6380a
docs(platform-server): remove gateway and argo steps
casey-brooks Mar 5, 2026
d2950a5
feat(devspace): add bootstrap-aware wrapper
casey-brooks Mar 5, 2026
8174172
feat(devspace): use prebuilt platform-server image
casey-brooks Mar 5, 2026
7d06c7d
chore(ci): publish single dev image tag
casey-brooks Mar 5, 2026
fde49bd
feat(devspace): introduce dev image workflow
casey-brooks Mar 5, 2026
7fe7551
chore(devspace): rely on synced sources
casey-brooks Mar 5, 2026
ab87a08
chore(devspace): remove wrapper script
casey-brooks Mar 5, 2026
06acdc3
chore(ci): rename dev image workflow
casey-brooks Mar 5, 2026
b16474f
fix(devspace): remove chart overrides
casey-brooks Mar 5, 2026
cd235b3
fix(devspace): use writable workspace mount
casey-brooks Mar 6, 2026
1611d9b
chore(devspace): harden dev workflow
casey-brooks Mar 6, 2026
a73e3e1
chore(devspace): order argocd predeploy
casey-brooks Mar 6, 2026
b853e21
chore(devspace): server-side patch sync policy
casey-brooks Mar 6, 2026
f7a5271
feat(devspace): add argocd sync-off job
casey-brooks Mar 7, 2026
e3c3aea
fix(devspace): scope caches to workspace
casey-brooks Mar 7, 2026
a03b1f4
fix(devspace): align validation flow requirements
casey-brooks Mar 9, 2026
a230ea7
fix(devspace): sandbox pnpm home directories
casey-brooks Mar 10, 2026
c9641c3
chore(devspace): guard pnpm bootstrap with sync wait
casey-brooks Mar 10, 2026
d450bbc
chore(devspace): sandbox dev command npm tooling
casey-brooks Mar 10, 2026
135351e
chore(devspace): use corepack pnpm shim
casey-brooks Mar 10, 2026
3ef50a9
chore(devspace): drop npm pnpm bootstrap
casey-brooks Mar 10, 2026
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
33 changes: 33 additions & 0 deletions .github/workflows/platform-server-dev-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Platform Server (Dev Image)

on:
push:
branches: [main]
paths:
- 'packages/platform-server/Dockerfile.dev'

permissions:
contents: read
packages: write

jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push image
uses: docker/build-push-action@v6
with:
context: .
file: packages/platform-server/Dockerfile
push: true
tags: ghcr.io/agynio/platform-server:dev
70 changes: 70 additions & 0 deletions packages/platform-server/DEVSPACE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# DevSpace workflow for platform-server

This guide walks through launching `platform-server` inside the
`bootstrap_v2` cluster with a single `devspace dev` invocation.

## Prerequisites

- macOS or Linux workstation with Docker (or k3d) installed
- [`devspace`](https://devspace.sh/docs/cli/installation)
- Local cluster provisioned via
[`agynio/bootstrap_v2`](https://github.com/agynio/bootstrap_v2) (follow the
Quickstart in its README; it provides the kubeconfig path—usually
`bootstrap_v2/k8s/.kube/agyn-local-kubeconfig.yaml`).

## 1. Prepare the cluster

Run `agynio/bootstrap_v2` according to its Quickstart to provision the cluster
and supporting services. The Quickstart configures kubectl with the
`agyn-local` context (or provides the kubeconfig path). Ensure your shell has
the appropriate kubeconfig exported before proceeding.

## 2. Start DevSpace

Change into `packages/platform-server` and run:

```bash
cd packages/platform-server
devspace dev
```

DevSpace deploys the prebuilt dev image `ghcr.io/agynio/platform-server:dev`,
which bundles the tooling needed for `pnpm` but no application sources. The
repository is synced into `/opt/app/data/workspace` (an `emptyDir` exposed by
the chart), and the container now runs a minimal startup script:

```sh
corepack pnpm install
corepack pnpm --filter @agyn/platform-server dev
```

Environment variables, volume mounts, and the pod security context are
provided by `bootstrap_v2`; DevSpace leaves them as-is. Production images
continue to be built by the existing CI pipeline and are separate from this
workflow.

Ahead of the Helm release, DevSpace applies a dev-only Kubernetes Job plus
scoped RBAC inside the `argocd` namespace. The Job authenticates against the
Kubernetes API using its service account token and issues a JSON merge-patch
that sets `spec.syncPolicy.automated` to `null` on the
`Application/platform-server` resource. No `kubectl` or Argo CD CLI calls run
inside the container—the patch is fully in-cluster. The Job is tracked as a
DevSpace deployment, so `devspace purge` removes the Job, Role, RoleBinding,
and ServiceAccount along with the Helm release.

The Helm release still requests 2 GiB of memory to keep `pnpm` installs from
being OOM-killed.

To confirm the deployment is ready, check the pod status:

```bash
kubectl get pods -n platform -l app.kubernetes.io/name=platform-server
```

## 3. Cleanup

1. Terminate DevSpace (`Ctrl+C`).
2. Purge the dev release:
```bash
devspace purge
```
32 changes: 32 additions & 0 deletions packages/platform-server/Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# syntax=docker/dockerfile:1.7

FROM node:20-slim

ENV PNPM_HOME=/pnpm \
PNPM_STORE_PATH=/pnpm-store \
PATH=/pnpm:$PATH \
NODE_ENV=development

RUN corepack enable \
&& corepack prepare pnpm@10.5.0 --activate

RUN apt-get update \
&& apt-get install -y --no-install-recommends \
git \
bash \
openssh-client \
ca-certificates \
inotify-tools \
&& rm -rf /var/lib/apt/lists/*

RUN npm install --global tsx@^4.20.5 \
&& npm cache clean --force

WORKDIR /opt/app

RUN mkdir -p /pnpm /pnpm-store \
&& chown -R 1000:1000 /opt/app /pnpm /pnpm-store

USER 1000:1000

CMD ["sleep", "infinity"]
121 changes: 121 additions & 0 deletions packages/platform-server/devspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
version: v2beta1

vars:
PLATFORM_NAMESPACE: platform

pipelines:
dev: |-
if kubectl get application platform-server -n argocd >/dev/null 2>&1; then
kubectl patch application platform-server -n argocd --type merge -p '{"spec":{"syncPolicy":{"automated":null}}}'
fi
kubectl set resources deployment platform-server -n ${PLATFORM_NAMESPACE} --containers=platform-server --limits=cpu=2000m,memory=4Gi --requests=cpu=500m,memory=2Gi
start_dev platform-server

hooks:
- name: enable-argocd-auto-sync
command: sh
args:
- -lc
- |
set -eu
if kubectl get application platform-server -n argocd >/dev/null 2>&1; then
kubectl patch application platform-server -n argocd --type merge -p '{"spec":{"syncPolicy":{"automated":{"prune":true,"selfHeal":true}}}}'
fi
events:
- after:dev:platform-server

dev:
platform-server:
namespace: ${PLATFORM_NAMESPACE}
container: platform-server
labelSelector:
app.kubernetes.io/name: platform-server
patches:
- op: replace
path: spec.template.spec.securityContext
value:
runAsUser: 1000
fsGroup: 1000
- op: replace
path: spec.template.spec.containers[0].resources.limits.cpu
value: "2000m"
- op: replace
path: spec.template.spec.containers[0].resources.limits.memory
value: "4Gi"
- op: replace
path: spec.template.spec.containers[0].resources.requests.cpu
value: "500m"
- op: replace
path: spec.template.spec.containers[0].resources.requests.memory
value: "2Gi"
containers:
platform-server:
devImage: ghcr.io/agynio/platform-server:dev
command:
- bash
- -lc
- |
set -euo pipefail
export HOME=/opt/app/data/workspace/home
export TMPDIR=/tmp
mkdir -p "$HOME" "$HOME/.local/bin"
chown -R 1000:1000 "$HOME" >/dev/null 2>&1 || true
export PATH="$HOME/.local/bin:/usr/local/bin:$PATH"

# Configure pnpm store: prefer /pnpm-store if available, otherwise fall back to HOME
if mkdir -p /pnpm-store 2>/dev/null; then
export PNPM_STORE_DIR=/pnpm-store
else
export PNPM_STORE_DIR="$HOME/.pnpm-store"
mkdir -p "$PNPM_STORE_DIR"
fi

# Fail fast if any /Users path leaks via env or npm config
bad="$( (printenv; npm config list -l 2>/dev/null) | grep -E '(^|: )/Users(/|$)' || true )"
if [ -n "$bad" ]; then
echo "Refusing to run due to /Users leak:"
echo "$bad"
exit 1
fi

# Ensure pnpm shim from corepack is available without npm install
if ! command -v pnpm >/dev/null 2>&1; then
if [ -x /usr/local/lib/node_modules/corepack/dist/pnpm.js ]; then
ln -sf /usr/local/lib/node_modules/corepack/dist/pnpm.js "$HOME/.local/bin/pnpm"
else
echo "pnpm shim missing from corepack distribution"
exit 1
fi
fi
chmod +x "$HOME/.local/bin/pnpm" 2>/dev/null || true

# Wait for sources to sync
while [ ! -f /opt/app/data/workspace/pnpm-workspace.yaml ] || [ ! -f /opt/app/data/workspace/packages/platform-server/package.json ]; do
echo "waiting for source sync..."
sleep 1
done

cd /opt/app/data/workspace
echo "== preflight =="
which pnpm || true
pnpm -v
pnpm install
pnpm --filter @agyn/platform-server dev
sync:
- localSubPath: '../../'
containerPath: /opt/app/data/workspace
twoWay: true
excludePaths:
- '.git/'
- 'node_modules/'
- '.pnpm-store/'
- 'devspace-dev.log'
uploadExcludePaths:
- '.git/'
- 'node_modules/'
- '.pnpm-store/'
- 'devspace-dev.log'
downloadExcludePaths:
- '.git/'
- 'node_modules/'
- '.pnpm-store/'
38 changes: 38 additions & 0 deletions packages/platform-server/devspace/argocd-sync-off/job.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
apiVersion: batch/v1
kind: Job
metadata:
name: argocd-sync-off
namespace: argocd
spec:
backoffLimit: 0
template:
metadata:
labels:
job: argocd-sync-off
spec:
serviceAccountName: argocd-sync-off-sa
restartPolicy: Never
containers:
- name: sync-off
image: curlimages/curl:8.6.0
command:
- /bin/sh
- -c
- |
set -euo pipefail
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
PATCH='{"spec":{"syncPolicy":{"automated":null}}}'
STATUS=$(curl -sS -o /tmp/response.json -w "%{http_code}" \
--cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
-X PATCH \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/merge-patch+json" \
--data "${PATCH}" \
"https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}/apis/argoproj.io/v1alpha1/namespaces/argocd/applications/platform-server")
cat /tmp/response.json
if [ "${STATUS}" -ge 200 ] && [ "${STATUS}" -lt 300 ]; then
echo "Patch succeeded with status ${STATUS}"
else
echo "Patch failed with status ${STATUS}" >&2
exit 1
fi
13 changes: 13 additions & 0 deletions packages/platform-server/devspace/argocd-sync-off/role.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: argocd-sync-off-role
namespace: argocd
rules:
- apiGroups:
- argoproj.io
resources:
- applications
verbs:
- get
- patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: argocd-sync-off-binding
namespace: argocd
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: argocd-sync-off-role
subjects:
- kind: ServiceAccount
name: argocd-sync-off-sa
namespace: argocd
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: argocd-sync-off-sa
namespace: argocd