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
5 changes: 5 additions & 0 deletions .changeset/sandbox-image-json-pin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@truefoundry/trueforge-core': patch
---

Load SANDBOX_IMAGE_URI from sandboxImage.json so CI can rewrite the pin.
71 changes: 65 additions & 6 deletions .github/workflows/push-sandbox-image.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
name: Push sandbox image

# Build and push the Daytona sandbox base image to JFrog, tagged with the
# commit SHA of the ref selected at dispatch time.
# main / dispatch: push trueforge-sandbox:<sha>, then open a PR that updates sandboxImage.json.
on:
workflow_dispatch:
push:
branches: [main]
paths:
- 'packages/trueforge-core/scripts/sandbox/**'

concurrency:
group: push-sandbox-image
Expand Down Expand Up @@ -39,11 +42,67 @@ jobs:
needs: [build]
runs-on: ubuntu-latest
steps:
- name: Print image URI
env:
- env:
IMAGE_URI: ${{ vars.TRUEFOUNDRY_ARTIFACTORY_PUBLIC_REPOSITORY }}/trueforge-sandbox:${{ github.sha }}
run: |
set -euo pipefail
echo "Pushed image URI: $IMAGE_URI"
echo "### Sandbox image" >> "$GITHUB_STEP_SUMMARY"
echo "\`$IMAGE_URI\`" >> "$GITHUB_STEP_SUMMARY"

pin:
name: Open SANDBOX_IMAGE_URI pin PR
if: github.ref == 'refs/heads/main'
needs: [build]
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.TRUEFORGE_GENERATE_SDK_APP_ID }}
private-key: ${{ secrets.TRUEFORGE_GENERATE_SDK_APP_PRIVATE_KEY }}

- uses: actions/checkout@v4
with:
token: ${{ steps.app-token.outputs.token }}

- id: image
env:
REPO: ${{ vars.TRUEFOUNDRY_ARTIFACTORY_PUBLIC_REPOSITORY }}
SHA: ${{ github.sha }}
run: echo "uri=${REPO}/trueforge-sandbox:${SHA}" >> "$GITHUB_OUTPUT"

- name: Write sandboxImage.json
id: pin-uri
env:
IMAGE_URI: ${{ steps.image.outputs.uri }}
run: |
set -euo pipefail
path=packages/trueforge-core/src/core/sandbox/sandboxImage.json
jq -n --arg uri "$IMAGE_URI" '{uri: $uri}' >"$path"
if git diff --quiet -- "$path"; then
echo "changed=false" >>"$GITHUB_OUTPUT"
else
echo "changed=true" >>"$GITHUB_OUTPUT"
fi

- if: steps.pin-uri.outputs.changed == 'true'
run: bash scripts/changeset-sandbox-image.sh

- uses: peter-evans/create-pull-request@v7
with:
# GitHub will not start new workflow runs from GITHUB_TOKEN (anti-recursion).
# ci.yml would never run (or sit approval-required); required checks stay
# pending and this pin PR cannot merge. An App token is treated as a user.
token: ${{ steps.app-token.outputs.token }}
branch: release-sandbox-image
base: main
commit-message: 'chore: update sandbox image ${{ github.sha }}'
title: 'chore: update sandbox image ${{ github.sha }}'
body: |
Pushed `${{ steps.image.outputs.uri }}`. Merge so `release.yml` can publish the pin.
Existing tenants stay on `build_metadata`.
add-paths: |
packages/trueforge-core/src/core/sandbox/sandboxImage.json
.changeset
22 changes: 12 additions & 10 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
# Releasing

This repo ships npm packages, a production container image, a Helm chart, and
optional from-source **dev** images.
This repo ships npm packages, a production container image, a Helm chart, a
sandbox image, and optional from-source **dev** images.

| What | Trigger | Workflow |
| ----------------------------------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| npm packages | Push to `main` (Changesets) | [`release.yml`](.github/workflows/release.yml) |
| Prod image + chart-release PR | After `@truefoundry/trueforge` npm publish (reusable workflow), or manual dispatch | [`build-and-prepare-chart-release.yml`](.github/workflows/build-and-prepare-chart-release.yml) |
| Chart tag, GitHub Release, OCI push | Merge of `release-chart/trueforge`, or push/dispatch of `charts/trueforge@*` | [`release-chart.yml`](.github/workflows/release-chart.yml) |
| Sandbox image + pin PR | Push to `main` when `scripts/sandbox/**` changes, or dispatch | [`push-sandbox-image.yml`](.github/workflows/push-sandbox-image.yml) |
| Dev (from-source) image | Manual `workflow_dispatch` | [`build-dev-image.yml`](.github/workflows/build-dev-image.yml) |

## Versioning

| Artifact | Identity |
| ---------------------------- | ---------------------------------------------------------------------------------- |
| npm `@truefoundry/trueforge` | SemVer `X.Y.Z` — source of truth for app bits |
| Chart `appVersion` | A **published** npm version |
| Prod image | Root [`Dockerfile`](Dockerfile): `npm install @truefoundry/trueforge@$APP_VERSION` |
| Prod image tag | `{appVersion}-{shortSha}` (shortSha of the build commit) |
| Chart `version` | Independent SemVer; git tag `charts/trueforge@A.B.C` must match |
| Dev image | [`Dockerfile.dev`](Dockerfile.dev); tag = full commit SHA |
| Artifact | Identity |
| ---------------------------- | --------------------------------------------------------------------------------------------------------- |
| npm `@truefoundry/trueforge` | SemVer `X.Y.Z` — source of truth for app bits |
| Chart `appVersion` | A **published** npm version |
| Prod image | Root [`Dockerfile`](Dockerfile): `npm install @truefoundry/trueforge@$APP_VERSION` |
| Prod image tag | `{appVersion}-{shortSha}` (shortSha of the build commit) |
| Chart `version` | Independent SemVer; git tag `charts/trueforge@A.B.C` must match |
| Sandbox image | [`sandbox.Dockerfile`](packages/trueforge-core/scripts/sandbox/sandbox.Dockerfile); tag = full commit SHA |
| Dev image | [`Dockerfile.dev`](Dockerfile.dev); tag = full commit SHA |

Install a published chart:

Expand Down
3 changes: 3 additions & 0 deletions packages/trueforge-core/src/core/sandbox/sandboxImage.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"uri": "tfy.jfrog.io/tfy-images/trueforge-sandbox:ce9e802ff68514dfc6ff138ff81c8f26dbaaf724"
}
9 changes: 4 additions & 5 deletions packages/trueforge-core/src/core/sandbox/sandboxImage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/**
* Release-owned sandbox image. In-memory for now; CI will later replace this
* with the tag it pushes on each release.
*/
export const SANDBOX_IMAGE_URI = 'tfy.jfrog.io/tfy-images/trueforge-sandbox:ce9e802ff68514dfc6ff138ff81c8f26dbaaf724';
import sandboxImage from './sandboxImage.json' with { type: 'json' };

/** Release-owned sandbox image URI; CI rewrites `sandboxImage.json`. */
export const SANDBOX_IMAGE_URI = sandboxImage.uri;
Comment thread
cursor[bot] marked this conversation as resolved.
15 changes: 14 additions & 1 deletion packages/trueforge-core/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { esbuildPluginFilePathExtensions } from 'esbuild-plugin-file-path-extensions';
import { join } from 'node:path';
import { defineConfig } from 'tsup';

// every src module compiles to its own .js (CJS) + .mjs (ESM) pair so consumers can deep-import real file paths.
Expand All @@ -9,7 +10,19 @@ export default defineConfig({
// specifiers per format ('./foo' -> './foo.mjs' / './foo.js'). It requires
// bundle mode, but with every file as an entry nothing actually inlines.
bundle: true,
esbuildPlugins: [esbuildPluginFilePathExtensions({ esmExtension: 'mjs', cjsExtension: 'js' })],
esbuildPlugins: [
// file-path-extensions treats .json as extensionless and would emit
// `./sandboxImage.json.mjs` (broken); resolve to an absolute path first so esbuild inlines it.
{
name: 'bundle-sandbox-image-json',
setup(build) {
build.onResolve({ filter: /^\.\/sandboxImage\.json$/ }, args => ({
path: join(args.resolveDir, args.path),
}));
},
},
esbuildPluginFilePathExtensions({ esmExtension: 'mjs', cjsExtension: 'js' }),
],
outExtension({ format }) {
return { js: format === 'cjs' ? '.js' : '.mjs' };
},
Expand Down
16 changes: 16 additions & 0 deletions scripts/changeset-sandbox-image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
# Always write: this lands on a separate pin PR; pending main files may be consumed first.
set -euo pipefail
cd "$(dirname "$0")/.."

slug="update-sandbox-image"
outfile=".changeset/$(date -u +%Y%m%d%H%M%S)-${slug}.md"
cat >"$outfile" <<'EOF'
---
"@truefoundry/trueforge-core": patch
---

Update SANDBOX_IMAGE_URI to the image pushed by CI.
EOF

echo "Wrote $outfile"
Loading