Merge branch 'main' into adilallo/Backend/BridgeCloudronEnv
This commit is contained in:
@@ -91,12 +91,12 @@ story. If you need a new global, update `preview.js`.
|
|||||||
|
|
||||||
# Interaction tests (`play`)
|
# Interaction tests (`play`)
|
||||||
|
|
||||||
Use `@storybook/test` for interaction assertions — not `@testing-library/*`
|
Use `storybook/test` for interaction assertions — not `@testing-library/*`
|
||||||
directly. This matches `Checkbox.stories.js` and stays compatible with the
|
directly. This matches `Checkbox.stories.js` and stays compatible with the
|
||||||
Vitest portable-stories runner in `.storybook/vitest.setup.js`.
|
Vitest portable-stories runner in `.storybook/vitest.setup.js`.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import { within, userEvent, expect } from "@storybook/test";
|
import { within, userEvent, expect } from "storybook/test";
|
||||||
|
|
||||||
export const Interactive = {
|
export const Interactive = {
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
|
|||||||
+2
-2
@@ -6,9 +6,9 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
addons: [
|
addons: [
|
||||||
// Removed @storybook/addon-essentials due to version mismatch with Storybook 10.x
|
// Removed @storybook/addon-essentials due to version mismatch with Storybook 10.x
|
||||||
// Using individual addons instead
|
// Using individual addons instead. Interaction helpers import from storybook/test
|
||||||
|
// (bundled with storybook@10); @storybook/addon-interactions was merged into SB 8 core.
|
||||||
"@storybook/addon-a11y",
|
"@storybook/addon-a11y",
|
||||||
"@storybook/addon-interactions",
|
|
||||||
],
|
],
|
||||||
framework: {
|
framework: {
|
||||||
name: "@storybook/nextjs",
|
name: "@storybook/nextjs",
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"manifestVersion": 2,
|
||||||
|
"id": "com.medlab.communityrule",
|
||||||
|
"title": "Community Rule",
|
||||||
|
"author": "MEDLab",
|
||||||
|
"description": "Community governance and rule-building app",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"httpPort": 3000,
|
||||||
|
"healthCheckPath": "/api/health",
|
||||||
|
"memoryLimit": 805306368,
|
||||||
|
"minBoxVersion": "9.0.0",
|
||||||
|
"addons": {
|
||||||
|
"postgresql": {},
|
||||||
|
"sendmail": {},
|
||||||
|
"localstorage": {}
|
||||||
|
},
|
||||||
|
"dockerimage": "git.medlab.host/communityrule/community-rule:0.1.0",
|
||||||
|
"website": "https://communityrule.info",
|
||||||
|
"contactEmail": "hello@communityrule.info"
|
||||||
|
}
|
||||||
+41
-13
@@ -1,6 +1,7 @@
|
|||||||
# Optional production image (Next.js standalone output + Prisma).
|
# Production image: Next.js standalone output + Prisma, packaged for Cloudron.
|
||||||
# Build: docker build -t community-rule .
|
# Build / push: ./scripts/docker-release.sh
|
||||||
# Run: pass CLOUDRON_POSTGRESQL_URL, CLOUDRON_MAIL_SMTP_*, SESSION_SECRET, etc. at runtime (see .env.example).
|
# Install: cloudron install (reads CloudronManifest.json from repo root)
|
||||||
|
# See docs/guides/ops-backend-deploy.md §9.
|
||||||
|
|
||||||
FROM node:20-bookworm-slim AS base
|
FROM node:20-bookworm-slim AS base
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
@@ -9,27 +10,54 @@ ENV NEXT_TELEMETRY_DISABLED=1
|
|||||||
FROM base AS deps
|
FROM base AS deps
|
||||||
RUN apt-get update -y && apt-get install -y openssl && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update -y && apt-get install -y openssl && rm -rf /var/lib/apt/lists/*
|
||||||
COPY package.json package-lock.json ./
|
COPY package.json package-lock.json ./
|
||||||
RUN npm ci --no-audit --fund=false
|
# Copy the Prisma schema so the project's `postinstall` (which runs
|
||||||
|
# `prisma generate`) succeeds during install.
|
||||||
|
COPY prisma ./prisma
|
||||||
|
# `npm install` rather than `npm ci`:
|
||||||
|
# 1. `npm ci` strictly validates the lockfile and refuses when sub-tree
|
||||||
|
# resolutions drift (a recurring nuisance because the lockfile is
|
||||||
|
# generated on darwin-arm64 by default).
|
||||||
|
# 2. `npm install` reuses the lockfile when it can but tolerates
|
||||||
|
# platform-specific reshuffles for Linux-only optional deps
|
||||||
|
# (`lightningcss-linux-*-gnu`, `@tailwindcss/oxide-linux-*-gnu`,
|
||||||
|
# `@next/swc-linux-*-gnu`, etc.) that Next.js needs at build time.
|
||||||
|
RUN npm install --no-audit --fund=false
|
||||||
|
|
||||||
FROM base AS builder
|
FROM base AS builder
|
||||||
RUN apt-get update -y && apt-get install -y openssl && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update -y && apt-get install -y openssl && rm -rf /var/lib/apt/lists/*
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN npx prisma generate
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
FROM base AS runner
|
FROM base AS runner
|
||||||
RUN apt-get update -y && apt-get install -y openssl && rm -rf /var/lib/apt/lists/*
|
# openssl: Prisma engines. gosu: privilege drop in start.sh after chown.
|
||||||
|
RUN apt-get update -y && apt-get install -y openssl gosu && rm -rf /var/lib/apt/lists/*
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
RUN groupadd --system --gid 1001 nodejs && useradd --system --uid 1001 nextjs
|
|
||||||
|
|
||||||
COPY --from=builder /app/public ./public
|
# Reuse the `node` user (uid/gid 1000) shipped in node:20-bookworm-slim.
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
# Cloudron's localstorage addon mounts /app/data with root:root ownership at
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
# runtime; start.sh chowns it to node:node before dropping privileges.
|
||||||
COPY --from=builder /app/prisma ./prisma
|
|
||||||
|
COPY --from=builder --chown=node:node /app/public ./public
|
||||||
|
COPY --from=builder --chown=node:node /app/.next/standalone ./
|
||||||
|
COPY --from=builder --chown=node:node /app/.next/static ./.next/static
|
||||||
|
COPY --from=builder --chown=node:node /app/prisma ./prisma
|
||||||
|
|
||||||
|
# Prisma CLI is in devDependencies and is not included in the Next.js
|
||||||
|
# standalone output. Copy it explicitly so start.sh can run migrations.
|
||||||
|
COPY --from=builder --chown=node:node /app/node_modules/prisma ./node_modules/prisma
|
||||||
|
COPY --from=builder --chown=node:node /app/node_modules/.bin/prisma ./node_modules/.bin/prisma
|
||||||
|
|
||||||
|
# Cloudron's runtime rootfs is read-only except /tmp, /run, /app/data.
|
||||||
|
# Three marketing routes use ISR (`revalidate`) and write to .next/cache;
|
||||||
|
# redirect that path to /tmp/next-cache via a baked-in symlink so writes land
|
||||||
|
# on a writable mount at runtime.
|
||||||
|
RUN mkdir -p .next && ln -sfn /tmp/next-cache .next/cache
|
||||||
|
|
||||||
|
COPY --chown=node:node scripts/start.sh /start.sh
|
||||||
|
RUN chmod +x /start.sh
|
||||||
|
|
||||||
USER nextjs
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
ENV PORT=3000
|
ENV PORT=3000
|
||||||
ENV HOSTNAME="0.0.0.0"
|
ENV HOSTNAME="0.0.0.0"
|
||||||
CMD ["node", "server.js"]
|
CMD ["/start.sh"]
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
This doc captures everything needed to deploy the new CommunityRule
|
This doc captures everything needed to deploy the new CommunityRule
|
||||||
(Next.js + Postgres) onto MEDLab's Cloudron and replace the legacy
|
(Next.js + Postgres) onto MEDLab's Cloudron and replace the legacy
|
||||||
LAMP-packaged service at `communityrule.info`. Cloudron admin access
|
LAMP-packaged service at `communityrule.info`. Cloudron admin access
|
||||||
has been granted; remaining open item is the registry decision in §6.
|
has been granted and the container registry is wired up (§6, §9); the
|
||||||
|
remaining gates are CR-96 (env bridging) and CR-98 (staging install).
|
||||||
|
|
||||||
> **For a plain-language summary to hand to MEDLab's Cloudron admin,
|
> **For a plain-language summary to hand to MEDLab's Cloudron admin,
|
||||||
> see [`../relaunch-brief.md`](../relaunch-brief.md).** This doc is the
|
> see [`../relaunch-brief.md`](../relaunch-brief.md).** This doc is the
|
||||||
@@ -148,11 +149,21 @@ Product decisions (closed):
|
|||||||
part of the CR-99 pre-cutover backup. Tracked in
|
part of the CR-99 pre-cutover backup. Tracked in
|
||||||
[CR-102](https://linear.app/community-rule/issue/CR-102/backend-decide-fate-of-legacy-rules-table-read-only-export).
|
[CR-102](https://linear.app/community-rule/issue/CR-102/backend-decide-fate-of-legacy-rules-table-read-only-export).
|
||||||
|
|
||||||
Infra decision still open:
|
Infra decision closed:
|
||||||
|
|
||||||
3. **Container registry** — GHCR (under your personal / org account,
|
3. **Container registry — Gitea Container Registry on `git.medlab.host`.**
|
||||||
lowest friction), Docker Hub, or MEDLab self-hosted. Tracked in
|
Same host as Cloudron (`193.46.198.90`); container package is set
|
||||||
[CR-97](https://linear.app/community-rule/issue/CR-97/backend-container-image-registry-choose-build-push).
|
**public** to sidestep the [same-host docker-login "socket hangup"
|
||||||
|
bug](https://forum.cloudron.io/topic/14572/private-docker-registry-in-cloudron),
|
||||||
|
so Cloudron pulls without credentials. Push auth from operator
|
||||||
|
laptops uses a Gitea personal access token (`read:package` +
|
||||||
|
`write:package`). Canonical image ref:
|
||||||
|
`git.medlab.host/communityrule/community-rule:<tag>`. Operator
|
||||||
|
build/push workflow lives in [§9](#9-build-and-push-image-workflow).
|
||||||
|
Tracked in [CR-97](https://linear.app/community-rule/issue/CR-97/backend-container-image-registry-choose-build-push).
|
||||||
|
Fallback if same-host pull ever breaks: install the **Cloudron
|
||||||
|
Container Registry** app and re-tag against its hostname; no other
|
||||||
|
changes required.
|
||||||
|
|
||||||
## 7. Old vs new deltas
|
## 7. Old vs new deltas
|
||||||
|
|
||||||
@@ -192,7 +203,10 @@ All filed in Linear, titled `[Backend] …`, assigned to me, in the
|
|||||||
`CLOUDRON_POSTGRESQL_URL` and `CLOUDRON_MAIL_SMTP_*` only).
|
`CLOUDRON_POSTGRESQL_URL` and `CLOUDRON_MAIL_SMTP_*` only).
|
||||||
2. [**CR-97**](https://linear.app/community-rule/issue/CR-97/backend-container-image-registry-choose-build-push)
|
2. [**CR-97**](https://linear.app/community-rule/issue/CR-97/backend-container-image-registry-choose-build-push)
|
||||||
— `[Backend] Container image registry: choose, build, push`.
|
— `[Backend] Container image registry: choose, build, push`.
|
||||||
Blocked by registry decision (§6.3).
|
Registry decided (§6.3); packaging + build/push workflow shipped
|
||||||
|
(§9). Closes after the first verified `docker pull` of the pushed
|
||||||
|
image (no Cloudron-side install required to close this ticket;
|
||||||
|
that's CR-98).
|
||||||
3. [**CR-98**](https://linear.app/community-rule/issue/CR-98/backend-cloudron-staging-install-smoke)
|
3. [**CR-98**](https://linear.app/community-rule/issue/CR-98/backend-cloudron-staging-install-smoke)
|
||||||
— `[Backend] Cloudron staging install + smoke` at
|
— `[Backend] Cloudron staging install + smoke` at
|
||||||
`staging.communityrule.info`. Blocked by CR-96 + CR-97.
|
`staging.communityrule.info`. Blocked by CR-96 + CR-97.
|
||||||
@@ -213,6 +227,90 @@ All filed in Linear, titled `[Backend] …`, assigned to me, in the
|
|||||||
Count rows + decide whether to publish a static archive before
|
Count rows + decide whether to publish a static archive before
|
||||||
CR-99 uninstalls the legacy MySQL. Priority: Low.
|
CR-99 uninstalls the legacy MySQL. Priority: Low.
|
||||||
|
|
||||||
|
## 9. Build and push image workflow
|
||||||
|
|
||||||
|
The repo is packaged as a Cloudron app via
|
||||||
|
[`CloudronManifest.json`](../../CloudronManifest.json),
|
||||||
|
[`Dockerfile`](../../Dockerfile),
|
||||||
|
[`scripts/start.sh`](../../scripts/start.sh), and
|
||||||
|
[`scripts/docker-release.sh`](../../scripts/docker-release.sh). The
|
||||||
|
manifest declares `httpPort 3000`, `healthCheckPath /api/health`,
|
||||||
|
`memoryLimit 768 MiB`, `minBoxVersion 9.0.0`, and the
|
||||||
|
`postgresql + sendmail + localstorage` addons. The Dockerfile reuses
|
||||||
|
the base image's `node` user (uid 1000), installs `gosu` for the
|
||||||
|
privilege drop, and symlinks `.next/cache → /tmp/next-cache` so
|
||||||
|
Next.js ISR works on Cloudron's read-only rootfs. `start.sh` runs as
|
||||||
|
root to chown `/app/data` (localstorage mount), then drops to
|
||||||
|
`node:node`, applies `prisma migrate deploy`, and execs the Next.js
|
||||||
|
standalone server.
|
||||||
|
|
||||||
|
### One-time setup (per operator)
|
||||||
|
|
||||||
|
1. **Generate a Gitea PAT.** In Gitea web UI: avatar → Settings →
|
||||||
|
Applications → Manage Access Tokens → Generate New Token. Check
|
||||||
|
`read:package` and `write:package`. Save in 1Password.
|
||||||
|
2. **`docker login git.medlab.host`** with your Gitea username and the
|
||||||
|
PAT as password. Expect `Login Succeeded`.
|
||||||
|
3. Confirm you have package-write rights on the `CommunityRule` org
|
||||||
|
(you do if you can push commits to the repo).
|
||||||
|
|
||||||
|
### Per-release workflow
|
||||||
|
|
||||||
|
1. **Bump the manifest version.** Edit
|
||||||
|
[`CloudronManifest.json`](../../CloudronManifest.json):
|
||||||
|
- increment `version` (e.g. `0.1.0` → `0.1.1`) — Cloudron requires
|
||||||
|
it to **increase** for `cloudron update --image` to be accepted;
|
||||||
|
- update `dockerimage` to the tag you're about to push (default tag
|
||||||
|
is the git short SHA).
|
||||||
|
2. **Run the release script** from the repo root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/docker-release.sh
|
||||||
|
# or, equivalently:
|
||||||
|
npm run docker:release
|
||||||
|
```
|
||||||
|
|
||||||
|
Override the tag with `TAG=v0.1.1 ./scripts/docker-release.sh` for
|
||||||
|
semver releases. The script prints the exact `dockerimage` line to
|
||||||
|
paste back into the manifest.
|
||||||
|
3. **First push only:** in Gitea, navigate to the `CommunityRule` org
|
||||||
|
→ Packages → `community-rule` → Settings → set **Visibility: Public**.
|
||||||
|
4. **Verify the pull works without credentials** (simulates Cloudron's
|
||||||
|
anonymous pull):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker logout git.medlab.host
|
||||||
|
docker pull git.medlab.host/communityrule/community-rule:<tag>
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Commit the manifest change** alongside any code changes that
|
||||||
|
shipped in this build, so the manifest and image stay in lockstep.
|
||||||
|
|
||||||
|
### Install / update on Cloudron
|
||||||
|
|
||||||
|
From the repo dir on the operator's machine, with `cloudron` CLI
|
||||||
|
logged in to `cloud.medlab.host`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# First install (staging):
|
||||||
|
cloudron install --location staging.communityrule.info
|
||||||
|
|
||||||
|
# Subsequent updates:
|
||||||
|
cloudron update --app <app-id>
|
||||||
|
```
|
||||||
|
|
||||||
|
`cloudron install` reads `dockerimage` from
|
||||||
|
[`CloudronManifest.json`](../../CloudronManifest.json); no `--image`
|
||||||
|
flag needed.
|
||||||
|
|
||||||
|
### CI — deferred (stretch goal)
|
||||||
|
|
||||||
|
CR-97 acceptance lists a stretch goal of building and pushing on merge
|
||||||
|
to `main` via Gitea Actions. Deferred: no hosted runners are available
|
||||||
|
today, and the manual workflow above is acceptable for v1 staging and
|
||||||
|
production. Revisit when runners return or when release cadence
|
||||||
|
justifies the runner cost.
|
||||||
|
|
||||||
## 10. Rate limiting (single-instance deploys)
|
## 10. Rate limiting (single-instance deploys)
|
||||||
|
|
||||||
The app uses an **in-memory** rate limiter in [`lib/server/rateLimit.ts`](../../lib/server/rateLimit.ts) (magic-link requests, organizer inquiry, etc.). This is sufficient for the current **single Cloudron container** per environment.
|
The app uses an **in-memory** rate limiter in [`lib/server/rateLimit.ts`](../../lib/server/rateLimit.ts) (magic-link requests, organizer inquiry, etc.). This is sufficient for the current **single Cloudron container** per environment.
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
"@types/mdx",
|
"@types/mdx",
|
||||||
"eslint-config-next",
|
"eslint-config-next",
|
||||||
"typescript-eslint",
|
"typescript-eslint",
|
||||||
"@storybook/react",
|
|
||||||
"@storybook/nextjs-vite",
|
"@storybook/nextjs-vite",
|
||||||
"@eslint/js",
|
"@eslint/js",
|
||||||
"@next/eslint-plugin-next",
|
"@next/eslint-plugin-next",
|
||||||
|
|||||||
Generated
+4563
-3311
File diff suppressed because it is too large
Load Diff
+6
-4
@@ -43,7 +43,8 @@
|
|||||||
"analyze:browser": "BUNDLE_ANALYZE=true npm run build",
|
"analyze:browser": "BUNDLE_ANALYZE=true npm run build",
|
||||||
"bundle:analyze": "node scripts/bundle-analyzer.js",
|
"bundle:analyze": "node scripts/bundle-analyzer.js",
|
||||||
"db:deploy": "prisma migrate deploy",
|
"db:deploy": "prisma migrate deploy",
|
||||||
"migrate:smoke": "./scripts/migrate-smoke-local.sh"
|
"migrate:smoke": "./scripts/migrate-smoke-local.sh",
|
||||||
|
"docker:release": "./scripts/docker-release.sh"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdx-js/loader": "^3.1.1",
|
"@mdx-js/loader": "^3.1.1",
|
||||||
@@ -54,7 +55,7 @@
|
|||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"jspdf": "^2.5.2",
|
"jspdf": "^2.5.2",
|
||||||
"next": "^16.0.0",
|
"next": "^16.0.0",
|
||||||
"next-intl": "^3.26.5",
|
"next-intl": "^4.0.0",
|
||||||
"nodemailer": "^8.0.4",
|
"nodemailer": "^8.0.4",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
@@ -66,10 +67,11 @@
|
|||||||
"@lhci/cli": "^0.15.1",
|
"@lhci/cli": "^0.15.1",
|
||||||
"@playwright/test": "^1.55.0",
|
"@playwright/test": "^1.55.0",
|
||||||
"@storybook/addon-a11y": "^10.2.0",
|
"@storybook/addon-a11y": "^10.2.0",
|
||||||
"@storybook/addon-interactions": "^8.6.14",
|
|
||||||
"@storybook/nextjs": "^10.2.0",
|
"@storybook/nextjs": "^10.2.0",
|
||||||
|
"@storybook/react": "^10.2.0",
|
||||||
"@svgr/webpack": "^8.1.0",
|
"@svgr/webpack": "^8.1.0",
|
||||||
"@tailwindcss/postcss": "^4.1.11",
|
"@tailwindcss/postcss": "^4.1.11",
|
||||||
|
"@testing-library/dom": "^10.4.0",
|
||||||
"@testing-library/jest-dom": "^6.8.0",
|
"@testing-library/jest-dom": "^6.8.0",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
"@testing-library/user-event": "^14.6.1",
|
"@testing-library/user-event": "^14.6.1",
|
||||||
@@ -82,7 +84,7 @@
|
|||||||
"@vitest/coverage-v8": "^3.2.4",
|
"@vitest/coverage-v8": "^3.2.4",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "^16.0.0",
|
"eslint-config-next": "^16.0.0",
|
||||||
"eslint-plugin-storybook": "^9.0.7",
|
"eslint-plugin-storybook": "^10.4.1",
|
||||||
"globals": "^17.1.0",
|
"globals": "^17.1.0",
|
||||||
"jest-axe": "^10.0.0",
|
"jest-axe": "^10.0.0",
|
||||||
"jsdom": "^26.1.0",
|
"jsdom": "^26.1.0",
|
||||||
|
|||||||
Executable
+38
@@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Build, tag, and push the community-rule image to the Gitea container
|
||||||
|
# registry on git.medlab.host. See docs/guides/ops-backend-deploy.md §9.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./scripts/docker-release.sh # tag = git short SHA
|
||||||
|
# TAG=v0.1.1 ./scripts/docker-release.sh # explicit tag
|
||||||
|
#
|
||||||
|
# Builds for linux/amd64 explicitly so the image runs on the Cloudron host
|
||||||
|
# (x86_64) even when this script runs on an Apple Silicon laptop (aarch64).
|
||||||
|
# buildx pushes directly to the registry — no intermediate local image.
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# - docker login git.medlab.host (Gitea PAT with read+write:package)
|
||||||
|
# - Push permission to the CommunityRule org's packages
|
||||||
|
# - docker buildx (ships with Docker Desktop)
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
IMAGE="${IMAGE:-git.medlab.host/communityrule/community-rule}"
|
||||||
|
TAG="${TAG:-$(git rev-parse --short HEAD)}"
|
||||||
|
PLATFORM="${PLATFORM:-linux/amd64}"
|
||||||
|
|
||||||
|
docker buildx build \
|
||||||
|
--platform "$PLATFORM" \
|
||||||
|
--tag "$IMAGE:$TAG" \
|
||||||
|
--push \
|
||||||
|
.
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Pushed: $IMAGE:$TAG ($PLATFORM)"
|
||||||
|
echo
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " 1. Update CloudronManifest.json 'version' (must increase) and"
|
||||||
|
echo " 'dockerimage' to:"
|
||||||
|
echo " \"dockerimage\": \"$IMAGE:$TAG\""
|
||||||
|
echo " 2. First install: cloudron install"
|
||||||
|
echo " Subsequent: cloudron update --app <app-id>"
|
||||||
Executable
+24
@@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Container entrypoint for Cloudron.
|
||||||
|
# Runs as root so we can chown the runtime volume mount, then drops to the
|
||||||
|
# node user (uid 1000) for the application process.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Bridge Cloudron's env name to Prisma's expected name so `prisma migrate
|
||||||
|
# deploy` works before CR-96 lands the in-app DATABASE_URL bridging.
|
||||||
|
export DATABASE_URL="${DATABASE_URL:-$CLOUDRON_POSTGRESQL_URL}"
|
||||||
|
|
||||||
|
# /app/data is created at runtime by Cloudron's localstorage addon as
|
||||||
|
# root:root; chown so the node user can write uploads.
|
||||||
|
chown -R node:node /app/data
|
||||||
|
|
||||||
|
# Next.js ISR cache lives at /app/.next/cache via a symlink baked into the
|
||||||
|
# Dockerfile. The target on /tmp is writable on Cloudron's read-only rootfs.
|
||||||
|
mkdir -p /tmp/next-cache
|
||||||
|
chown -R node:node /tmp/next-cache
|
||||||
|
|
||||||
|
# Drop privileges, apply any pending migrations, then exec the server.
|
||||||
|
# Inner `exec` ensures SIGTERM from Cloudron reaches node for clean shutdown.
|
||||||
|
exec gosu node:node sh -c \
|
||||||
|
'./node_modules/.bin/prisma migrate deploy && exec node server.js'
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import Button from "../../app/components/buttons/Button";
|
import Button from "../../app/components/buttons/Button";
|
||||||
import { within, userEvent } from "@storybook/test";
|
import { within, userEvent } from "storybook/test";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Components/Buttons/Button/Visual Regression",
|
title: "Components/Buttons/Button/Visual Regression",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Checkbox from "../../app/components/controls/Checkbox";
|
import Checkbox from "../../app/components/controls/Checkbox";
|
||||||
import { within, userEvent } from "@storybook/test";
|
import { within, userEvent, expect } from "storybook/test";
|
||||||
import { expect } from "@storybook/test";
|
|
||||||
|
|
||||||
// Interaction functions for Storybook play functions
|
// Interaction functions for Storybook play functions
|
||||||
const DefaultInteraction = {
|
const DefaultInteraction = {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Footer from "../../app/components/navigation/Footer";
|
import Footer from "../../app/components/navigation/Footer";
|
||||||
import { within, userEvent } from "@storybook/test";
|
import { within, userEvent } from "storybook/test";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Components/Navigation/Footer/Responsive",
|
title: "Components/Navigation/Footer/Responsive",
|
||||||
|
|||||||
Reference in New Issue
Block a user