Compare commits
184 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31177a5730 | ||
|
|
6fc99896d4 | ||
|
|
89b2952c28 | ||
|
|
6cd22ed7b9 | ||
|
|
8f7785dc89 | ||
|
|
97dd599afb | ||
|
|
bf812ac2ad | ||
|
|
e533d68e86 | ||
|
|
12dec0e71d | ||
|
|
13b97b3243 | ||
|
|
a1ddca00ba | ||
|
|
113e47c3ab | ||
|
|
ccdad284ee | ||
|
|
b36716b1c7 | ||
|
|
b5a08e3326 | ||
|
|
1e0be40561 | ||
|
|
61ac061332 | ||
|
|
9a52161aec | ||
|
|
9ed5415e9b | ||
|
|
be2bc126c2 | ||
|
|
2944b16114 | ||
|
|
aba40f40ad | ||
|
|
a538f0e6fe | ||
|
|
0153187a2c | ||
|
|
9215f56d61 | ||
|
|
442b8b1b81 | ||
|
|
1e178d8636 | ||
|
|
36a62d1c7c | ||
|
|
da147538dc | ||
|
|
a87ed18893 | ||
|
|
0d2ca470ad | ||
|
|
985f222ab2 | ||
|
|
cbb816bba4 | ||
|
|
69684a43f1 | ||
|
|
40e6178802 | ||
|
|
381e8fb8c1 | ||
|
|
ef5f2f7086 | ||
|
|
3f81539a2a | ||
|
|
97c11b668b | ||
|
|
fcaf24f92e | ||
|
|
a02d83c3fb | ||
|
|
c1207db63f | ||
|
|
13535a18f5 | ||
|
|
7bb1cc3d58 | ||
|
|
887ae6c640 | ||
|
|
fda24ff9f2 | ||
|
|
23d0080374 | ||
|
|
97ad5a8417 | ||
|
|
13afe84fdd | ||
|
|
e99f01498f | ||
|
|
343d7ffffa | ||
|
|
75c164f8bd | ||
|
|
823e1a5dfe | ||
|
|
1d991de4b0 | ||
|
|
0e652b9e71 | ||
|
|
0e8a882951 | ||
|
|
2c54ee70ee | ||
|
|
0b807b36b9 | ||
|
|
13217c602e | ||
|
|
430da379fc | ||
|
|
271a1ddf2c | ||
|
|
63af7ec84a | ||
|
|
a9b68e8917 | ||
|
|
05e11a873d | ||
|
|
4f6acbf995 | ||
|
|
54bf5b6433 | ||
|
|
ee238c9716 | ||
|
|
35dcd41443 | ||
|
|
17e99f3e11 | ||
|
|
d414420303 | ||
|
|
6aef528e49 | ||
|
|
53f36bcb27 | ||
|
|
578043f049 | ||
|
|
c3d7ab8c04 | ||
|
|
24798197de | ||
|
|
c4e5ad40db | ||
|
|
05869e3a21 | ||
|
|
974f2d28d5 | ||
|
|
6ae66f3cfd | ||
|
|
1171b48f7b | ||
|
|
7bd062f5de | ||
|
|
bdf7a0e18f | ||
|
|
e241739610 | ||
|
|
ccc09813e4 | ||
|
|
ec7f744220 | ||
|
|
05b2463668 | ||
|
|
673e298721 | ||
|
|
ce291603e9 | ||
|
|
27ceca0870 | ||
|
|
d74f96d58c | ||
|
|
f5064066be | ||
|
|
91ea6b51a9 | ||
|
|
ac6e4f5793 | ||
|
|
4ce8afaa97 | ||
|
|
f1e31ded41 | ||
|
|
cd66361895 | ||
|
|
9dfb2b1fc9 | ||
|
|
e8671790b5 | ||
|
|
496ede44b7 | ||
|
|
db798dfe16 | ||
|
|
ec4d406460 | ||
|
|
fd8247018f | ||
|
|
af4ca1b8ba | ||
|
|
e81afe5274 | ||
|
|
cf84679133 | ||
|
|
099cf10e98 | ||
|
|
213d89b1e4 | ||
|
|
76b9f03818 | ||
|
|
a0bae9b0a9 | ||
|
|
21ed5fb738 | ||
|
|
4ae48c6ea0 | ||
|
|
b8929712d4 | ||
|
|
ff00bcb218 | ||
|
|
580aa892de | ||
|
|
f9183bbff2 | ||
|
|
5dbb2951bb | ||
|
|
4346a18194 | ||
|
|
0cea642dd9 | ||
|
|
f86b2cb1cb | ||
|
|
641be4334d | ||
|
|
e13b9d33f9 | ||
|
|
bec6b9f14a | ||
|
|
f896c731f2 | ||
|
|
3bb21b6987 | ||
|
|
e9160db769 | ||
|
|
f4d27be7a6 | ||
|
|
411cb88796 | ||
|
|
25d96f3f23 | ||
|
|
da92b3721f | ||
|
|
34b19f99b9 | ||
|
|
214c8cc2e1 | ||
|
|
94c43f6dd8 | ||
|
|
59d880e3f9 | ||
|
|
14013c8a7c | ||
|
|
3493869fa4 | ||
|
|
d003bfaf0e | ||
|
|
b08f00a105 | ||
|
|
c8101aa2a7 | ||
|
|
a561a5069e | ||
|
|
5d802e3cf4 | ||
|
|
fec872711a | ||
|
|
44dd5e9eea | ||
|
|
64b104a2a3 | ||
|
|
ee11fa2c63 | ||
|
|
84f744f61f | ||
|
|
c7b8e50d8e | ||
|
|
a5603744ab | ||
|
|
b0f5b764bd | ||
|
|
5a517dde30 | ||
|
|
3bf6c72451 | ||
|
|
d635a49d6f | ||
|
|
5925efedc7 | ||
|
|
035c021485 | ||
|
|
a3538bbb66 | ||
|
|
2b5d2e10ea | ||
|
|
bc5acce55e | ||
|
|
fd9e06d56b | ||
|
|
f9186ca1fd | ||
|
|
e30b6d2ac4 | ||
|
|
6ce48992f3 | ||
|
|
3feedd8786 | ||
|
|
99b949da4a | ||
|
|
90e6be84a2 | ||
|
|
aee447b384 | ||
|
|
56313edd32 | ||
|
|
aa6a2e8cc3 | ||
|
|
631620b1df | ||
|
|
866c4a2939 | ||
|
|
b45c86dcc9 | ||
|
|
64a5b1517a | ||
|
|
79921d75d6 | ||
|
|
9517353df4 | ||
|
|
09c17e1653 | ||
|
|
80ebaada77 | ||
|
|
f9de91fc4b | ||
|
|
961bbca6d8 | ||
|
|
955f0a8f6b | ||
|
|
f960dc5856 | ||
|
|
ff529543a9 | ||
|
|
cdb6c1c62e | ||
|
|
6a01a4f731 | ||
|
|
ac95225809 | ||
|
|
953b6e0a16 | ||
|
|
c3a30a8879 |
14
.github/workflows/ci.yml
vendored
14
.github/workflows/ci.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
skip: ${{ steps.check-commit.outputs.skip }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: set up qemu
|
||||
uses: docker/setup-qemu-action@v4
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
timeout-minutes: 20
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: build-image
|
||||
uses: ./.github/actions/build-image
|
||||
@@ -101,7 +101,7 @@ jobs:
|
||||
timeout-minutes: 20
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: build-image
|
||||
uses: ./.github/actions/build-image
|
||||
@@ -117,7 +117,7 @@ jobs:
|
||||
matrix: ${{fromJson(needs.build.outputs.matrix)}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: download packages
|
||||
uses: actions/download-artifact@v8
|
||||
@@ -156,7 +156,7 @@ jobs:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: download packages
|
||||
uses: actions/download-artifact@v8
|
||||
@@ -190,7 +190,7 @@ jobs:
|
||||
GITHUB_NODE_INDEX: ${{ matrix.index }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: download packages
|
||||
uses: actions/download-artifact@v8
|
||||
|
||||
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -42,7 +42,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
# workaround for https://github.com/github/codeql/issues/14235
|
||||
- name: Remove go.work file
|
||||
|
||||
4
.github/workflows/dependency-updates.yml
vendored
4
.github/workflows/dependency-updates.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
matrix: ${{ steps.matrix.outputs.matrix }}
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Build dependency matrix
|
||||
id: matrix
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Resolve latest release
|
||||
id: latest
|
||||
|
||||
2
.github/workflows/docs.yml
vendored
2
.github/workflows/docs.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Cloning repo
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
12
.github/workflows/lint.yml
vendored
12
.github/workflows/lint.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
- name: Run hadolint
|
||||
uses: hadolint/hadolint-action@2332a7b74a6de0dda2e2221d575162eba76ba5e5
|
||||
# v3.0.0 => 4b5806eb9c6bee4954fc0e0cc3ad6175fc9782c1
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
if: github.event.pull_request.user.login != 'dependabot[bot]'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
- name: Setup packer
|
||||
uses: hashicorp/setup-packer@main
|
||||
with:
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
- name: Run shellcheck
|
||||
uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38
|
||||
# 1.1.0 => 94e0aab03ca135d11a35e5bfc14e6746dc56e7e9
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
- name: Run shfmt
|
||||
uses: luizm/action-sh-checker@883217215b11c1fabbf00eb1a9a041f62d74c744
|
||||
# v0.5.0 => edd0e45ecff35b05f162052b50df50976c1b74fc
|
||||
@@ -91,7 +91,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
- name: Run yamllint
|
||||
uses: ibiqlik/action-yamllint@2576378a8e339169678f9939646ee3ee325e845c
|
||||
# v3.1.1 => 2576378a8e339169678f9939646ee3ee325e845c
|
||||
|
||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
needs: release
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup packer
|
||||
uses: hashicorp/setup-packer@main
|
||||
@@ -120,7 +120,7 @@ jobs:
|
||||
needs: release
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
- name: Bump Azure Template
|
||||
env:
|
||||
BOT_GITHUB_USERNAME: ${{ secrets.HOMEBREW_GITHUB_USERNAME }}
|
||||
|
||||
283
HISTORY.md
283
HISTORY.md
@@ -1,5 +1,288 @@
|
||||
# History
|
||||
|
||||
## 0.38.20
|
||||
|
||||
Install/update via the bootstrap script:
|
||||
|
||||
```shell
|
||||
wget -NP . https://dokku.com/install/v0.38.20/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.20 bash bootstrap.sh
|
||||
```
|
||||
|
||||
### Tests
|
||||
|
||||
- #8773: @dependabot[bot] chore(deps): bump python from 3.15.0b2-bookworm to 3.15.0b3-bookworm in /tests/apps/dockerfile-release
|
||||
- #8756: @dependabot[bot] chore(deps): bump sass from 1.100.0 to 1.101.0 in /tests/apps/multi
|
||||
- #8753: @dependabot[bot] chore(deps-dev): bump heroku/heroku-buildpack-php from 291 to 292 in /tests/apps/php
|
||||
|
||||
### Dependencies
|
||||
|
||||
- #8766: @dependabot[bot] chore(deps): bump helm.sh/helm/v3 from 3.21.1 to 3.21.2 in /plugins/scheduler-k3s
|
||||
- #8777: @dependabot[bot] chore(deps): bump github.com/onsi/gomega from 1.41.0 to 1.42.1 in /plugins/common
|
||||
- #8778: @dependabot[bot] chore(deps): bump byjg/easy-haproxy from 6.1.0 to 6.1.1 in /plugins/haproxy-vhosts
|
||||
- #8748: @dependabot[bot] chore(deps): bump golang.org/x/crypto from 0.52.0 to 0.53.0 in /plugins/common
|
||||
- #8745: @dependabot[bot] chore(deps): bump golang.org/x/sync from 0.20.0 to 0.21.0 in /plugins/common
|
||||
- #8774: @dependabot[bot] chore(deps): bump python from 3.15.0b2-alpine to 3.15.0b3-alpine in /docs/_build
|
||||
- #8769: @dependabot[bot] chore(deps): bump pymdown-extensions from 10.21.3 to 11.0 in /docs/_build
|
||||
- #8775: @dependabot[bot] chore(deps): bump click from 8.4.1 to 8.4.2 in /docs/_build
|
||||
- #8776: @dependabot[bot] chore(deps): bump byjg/easy-haproxy from 6.0.1 to 6.1.0 in /plugins/haproxy-vhosts
|
||||
- #8772: @dependabot[bot] chore(deps): bump github.com/onsi/gomega from 1.42.0 to 1.42.1 in /plugins/buildpacks
|
||||
- #8770: @dokku-bot chore: bump pack to 0.40.7
|
||||
- #8767: @dependabot[bot] chore(deps): bump github.com/containerd/containerd from 1.7.32 to 1.7.33 in /plugins/scheduler-k3s
|
||||
- #8765: @dependabot[bot] chore(deps): bump github.com/onsi/gomega from 1.41.0 to 1.42.0 in /plugins/scheduler-k3s
|
||||
- #8764: @dependabot[bot] chore(deps): bump actions/checkout from 6 to 7
|
||||
- #8762: @dependabot[bot] chore(deps): bump k8s.io/kubernetes from 1.36.1 to 1.36.2 in /plugins/scheduler-k3s
|
||||
- #8758: @dependabot[bot] chore(deps): bump github.com/onsi/gomega from 1.41.0 to 1.42.0 in /plugins/buildpacks
|
||||
- #8760: @dependabot[bot] chore(deps): bump k8s.io/apimachinery from 0.36.1 to 0.36.2 in /plugins/scheduler-k3s
|
||||
- #8757: @dependabot[bot] chore(deps): bump helm.sh/helm/v3 from 3.21.0 to 3.21.1 in /plugins/scheduler-k3s
|
||||
- #8755: @dependabot[bot] chore(deps): bump traefik from v3.7.4 to v3.7.5 in /plugins/traefik-vhosts
|
||||
- #8754: @dependabot[bot] chore(deps): bump github.com/traefik/traefik/v2 from 2.11.49 to 2.11.50 in /plugins/scheduler-k3s
|
||||
- #8749: @dependabot[bot] chore(deps): bump github.com/traefik/traefik/v2 from 2.11.47 to 2.11.49 in /plugins/scheduler-k3s
|
||||
- #8743: @dependabot[bot] chore(deps): bump beautifulsoup4 from 4.14.3 to 4.15.0 in /docs/_build
|
||||
- #8744: @dependabot[bot] chore(deps): bump traefik from v3.7.3 to v3.7.4 in /plugins/traefik-vhosts
|
||||
- #8747: @dependabot[bot] chore(deps): bump golang.org/x/sync from 0.20.0 to 0.21.0 in /plugins/scheduler-k3s
|
||||
- #8746: @dependabot[bot] chore(deps): bump golang.org/x/sync from 0.20.0 to 0.21.0 in /plugins/scheduler-docker-local
|
||||
|
||||
## 0.38.19
|
||||
|
||||
Install/update via the bootstrap script:
|
||||
|
||||
```shell
|
||||
wget -NP . https://dokku.com/install/v0.38.19/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.19 bash bootstrap.sh
|
||||
```
|
||||
|
||||
## 0.38.18
|
||||
|
||||
Install/update via the bootstrap script:
|
||||
|
||||
```shell
|
||||
wget -NP . https://dokku.com/install/v0.38.18/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.18 bash bootstrap.sh
|
||||
```
|
||||
|
||||
### Tests
|
||||
|
||||
- #8734: @dependabot[bot] chore(deps): bump golang from 1.26.3 to 1.26.4 in /tests/apps/gogrpc
|
||||
- #8731: @dependabot[bot] chore(deps): bump golang from 1.26.3 to 1.26.4 in /tests/apps/go-fail-predeploy
|
||||
- #8733: @dependabot[bot] chore(deps): bump golang from 1.26.3 to 1.26.4 in /tests/apps/zombies-dockerfile-tini
|
||||
- #8735: @dependabot[bot] chore(deps): bump golang from 1.26.3 to 1.26.4 in /tests/apps/go-fail-postdeploy
|
||||
- #8736: @dependabot[bot] chore(deps): bump golang from 1.26.3 to 1.26.4 in /tests/apps/zombies-dockerfile-no-tini
|
||||
- #8738: @dependabot[bot] chore(deps): bump python from 3.15.0b1-bookworm to 3.15.0b2-bookworm in /tests/apps/dockerfile-release
|
||||
- #8730: @dependabot[bot] chore(deps): bump django from 5.2.14 to 5.2.15 in /tests/apps/dockerfile-release
|
||||
|
||||
### Dependencies
|
||||
|
||||
- #8727: @dependabot[bot] chore(deps): bump github.com/melbahja/goph from 1.5.0 to 1.5.1 in /plugins/common
|
||||
- #8739: @dependabot[bot] chore(deps): bump github.com/traefik/traefik/v2 from 2.11.46 to 2.11.47 in /plugins/scheduler-k3s
|
||||
- #8737: @dependabot[bot] chore(deps): bump dokku/openresty-docker-proxy from 0.12.0 to 0.12.1 in /plugins/openresty-vhosts
|
||||
- #8732: @dependabot[bot] chore(deps): bump python from 3.15.0b1-alpine to 3.15.0b2-alpine in /docs/_build
|
||||
- #8740: @dependabot[bot] chore(deps): bump timberio/vector from 0.55.0-debian to 0.56.0-debian in /plugins/logs
|
||||
- #8741: @dependabot[bot] chore(deps): bump traefik from v3.7.1 to v3.7.3 in /plugins/traefik-vhosts
|
||||
|
||||
## 0.38.17
|
||||
|
||||
Install/update via the bootstrap script:
|
||||
|
||||
```shell
|
||||
wget -NP . https://dokku.com/install/v0.38.17/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.17 bash bootstrap.sh
|
||||
```
|
||||
|
||||
### New Features
|
||||
|
||||
- #8729: @josegonzalez Add scheduler-k3s:preview subcommand to allow previewing helm chart changes
|
||||
|
||||
### Dependencies
|
||||
|
||||
- #8728: @dependabot[bot] chore(deps): bump dokku/openresty-docker-proxy from 0.11.0 to 0.12.0 in /plugins/openresty-vhosts
|
||||
|
||||
## 0.38.16
|
||||
|
||||
Install/update via the bootstrap script:
|
||||
|
||||
```shell
|
||||
wget -NP . https://dokku.com/install/v0.38.16/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.16 bash bootstrap.sh
|
||||
```
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- #8723: @josegonzalez Match control-plane nodes by their modern label
|
||||
- #8724: @josegonzalez Stop truncating existing files in common.TouchFile
|
||||
|
||||
## 0.38.15
|
||||
|
||||
Install/update via the bootstrap script:
|
||||
|
||||
```shell
|
||||
wget -NP . https://dokku.com/install/v0.38.15/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.15 bash bootstrap.sh
|
||||
```
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- #8718: @josegonzalez Persist scheduler-k3s chart overrides as JSON maps
|
||||
|
||||
### Other
|
||||
|
||||
- #8720: @josegonzalez Persist scheduler-k3s annotations and labels as JSON maps
|
||||
|
||||
## 0.38.14
|
||||
|
||||
Install/update via the bootstrap script:
|
||||
|
||||
```shell
|
||||
wget -NP . https://dokku.com/install/v0.38.14/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.14 bash bootstrap.sh
|
||||
```
|
||||
|
||||
### New Features
|
||||
|
||||
- #8716: @josegonzalez Add scheduler-k3s annotations:report and labels:report
|
||||
- #8715: @josegonzalez Add scheduler-k3s:charts:set and :charts:report
|
||||
|
||||
## 0.38.13
|
||||
|
||||
Install/update via the bootstrap script:
|
||||
|
||||
```shell
|
||||
wget -NP . https://dokku.com/install/v0.38.13/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.13 bash bootstrap.sh
|
||||
```
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- #8713: @josegonzalez Make storage:mount upsert the existing attachment
|
||||
|
||||
### New Features
|
||||
|
||||
- #8714: @josegonzalez Expose per-attachment fields in storage:report
|
||||
- #8712: @josegonzalez Expose attachment readonly and volume_options in storage:list json
|
||||
- #8711: @josegonzalez Add --volume-options flag to storage:mount
|
||||
|
||||
## 0.38.12
|
||||
|
||||
Install/update via the bootstrap script:
|
||||
|
||||
```shell
|
||||
wget -NP . https://dokku.com/install/v0.38.12/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.12 bash bootstrap.sh
|
||||
```
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- #8706: @josegonzalez Align apps:set/apps:report with what dokku actually reads
|
||||
|
||||
## 0.38.11
|
||||
|
||||
Install/update via the bootstrap script:
|
||||
|
||||
```shell
|
||||
wget -NP . https://dokku.com/install/v0.38.11/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.11 bash bootstrap.sh
|
||||
```
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- #8704: @josegonzalez Pass storage entry basename to chown-storage-dir
|
||||
|
||||
## 0.38.10
|
||||
|
||||
Install/update via the bootstrap script:
|
||||
|
||||
```shell
|
||||
wget -NP . https://dokku.com/install/v0.38.10/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.10 bash bootstrap.sh
|
||||
```
|
||||
|
||||
### New Features
|
||||
|
||||
- #8702: @josegonzalez Prompt for confirmation on storage:destroy
|
||||
|
||||
### Tests
|
||||
|
||||
- #8698: @dependabot[bot] chore(deps-dev): bump org.apache.maven.plugins:maven-dependency-plugin from 3.10.0 to 3.11.0 in /tests/apps/java
|
||||
|
||||
## 0.38.9
|
||||
|
||||
Install/update via the bootstrap script:
|
||||
|
||||
```shell
|
||||
wget -NP . https://dokku.com/install/v0.38.9/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.9 bash bootstrap.sh
|
||||
```
|
||||
|
||||
### New Features
|
||||
|
||||
- #8697: @josegonzalez Split openresty report into raw/computed/global
|
||||
|
||||
### Refactors
|
||||
|
||||
- #8696: @josegonzalez Treat empty ps restart-policy as an unset
|
||||
|
||||
## 0.38.8
|
||||
|
||||
Install/update via the bootstrap script:
|
||||
|
||||
```shell
|
||||
wget -NP . https://dokku.com/install/v0.38.8/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.8 bash bootstrap.sh
|
||||
```
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- #8690: @josegonzalez Unset bind-all-interfaces on empty value
|
||||
- #8689: @josegonzalez Expose remaining settable properties via :report
|
||||
|
||||
### New Features
|
||||
|
||||
- #8692: @Mordred Enable compression middleware for traefik when using scheduler-k3s
|
||||
- #8691: @josegonzalez Align Go-plugin :report JSON keys with bash-strip convention
|
||||
- #8688: @josegonzalez Promote registry image-repo-template to app + global
|
||||
|
||||
### Dependencies
|
||||
|
||||
- #8693: @dependabot[bot] chore(deps): bump helm.sh/helm/v3 from 3.20.2 to 3.21.0 in /plugins/scheduler-k3s
|
||||
|
||||
## 0.38.7
|
||||
|
||||
Install/update via the bootstrap script:
|
||||
|
||||
```shell
|
||||
wget -NP . https://dokku.com/install/v0.38.7/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.7 bash bootstrap.sh
|
||||
```
|
||||
|
||||
### Security
|
||||
|
||||
- #8672: @josegonzalez Prevent host shell injection from app.json cron commands
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- #8669: @josegonzalez Recover deployed image from registry on local miss
|
||||
- #8679: @josegonzalez Align ps/cron :report --global keys with plugin convention
|
||||
- #8678: @josegonzalez Split openresty report keys into global and computed pairs
|
||||
- #8676: @immanuwell Use SYSTEM for shfmt Darwin detection
|
||||
|
||||
### New Features
|
||||
|
||||
- #8677: @josegonzalez Warn on deprecated listen http2 in custom nginx templates
|
||||
|
||||
### Tests
|
||||
|
||||
- #8683: @dependabot[bot] chore(deps-dev): bump heroku/heroku-buildpack-php from 290 to 291 in /tests/apps/php
|
||||
- #8671: @josegonzalez Qualify scheduler-k3s ingressroute kubectl lookups
|
||||
|
||||
### Dependencies
|
||||
|
||||
- #8682: @dependabot[bot] chore(deps): bump k8s.io/apimachinery from 0.36.0 to 0.36.1 in /plugins/scheduler-k3s
|
||||
- #8681: @dependabot[bot] chore(deps): bump soupsieve from 2.8.3 to 2.8.4 in /docs/_build
|
||||
|
||||
### Other
|
||||
|
||||
- #8680: @josegonzalez Split more report global keys into raw and computed
|
||||
|
||||
## 0.38.6
|
||||
|
||||
Install/update via the bootstrap script:
|
||||
|
||||
@@ -85,8 +85,8 @@ Otherwise, you will need to import the keypair manually after installation using
|
||||
To install the latest stable release, run the following commands as a user who has access to `sudo`:
|
||||
|
||||
```shell
|
||||
wget -NP . https://dokku.com/install/v0.38.6/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.6 bash bootstrap.sh
|
||||
wget -NP . https://dokku.com/install/v0.38.20/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.20 bash bootstrap.sh
|
||||
```
|
||||
|
||||
You can then proceed to configure your server domain (via `dokku domains:set-global`) and user access (via `dokku ssh-keys:add`) to complete the installation.
|
||||
|
||||
@@ -34,10 +34,10 @@
|
||||
},
|
||||
{
|
||||
"name": "pack",
|
||||
"version": "0.40.6",
|
||||
"version": "0.40.7",
|
||||
"urls": {
|
||||
"amd64": "https://github.com/buildpacks/pack/releases/download/v0.40.6/pack-v0.40.6-linux.tgz",
|
||||
"arm64": "https://github.com/buildpacks/pack/releases/download/v0.40.6/pack-v0.40.6-linux-arm64.tgz"
|
||||
"amd64": "https://github.com/buildpacks/pack/releases/download/v0.40.7/pack-v0.40.7-linux.tgz",
|
||||
"arm64": "https://github.com/buildpacks/pack/releases/download/v0.40.7/pack-v0.40.7-linux-arm64.tgz"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
2
debian/control
vendored
2
debian/control
vendored
@@ -1,5 +1,5 @@
|
||||
Package: dokku
|
||||
Version: 0.38.6
|
||||
Version: 0.38.20
|
||||
Section: web
|
||||
Priority: optional
|
||||
Architecture: amd64
|
||||
|
||||
2
docs/_build/Dockerfile
vendored
2
docs/_build/Dockerfile
vendored
@@ -1,4 +1,4 @@
|
||||
FROM python:3.15.0b1-alpine
|
||||
FROM python:3.15.0b3-alpine
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
|
||||
8
docs/_build/requirements.txt
vendored
8
docs/_build/requirements.txt
vendored
@@ -1,5 +1,5 @@
|
||||
beautifulsoup4==4.14.3
|
||||
click==8.4.1
|
||||
beautifulsoup4==4.15.0
|
||||
click==8.4.2
|
||||
ghp-import==2.1.0
|
||||
importlib-metadata==9.0.0
|
||||
Jinja2==3.1.6
|
||||
@@ -12,12 +12,12 @@ mkdocs-material==9.7.6
|
||||
mkdocs-material-extensions==1.3.1
|
||||
packaging==26.2
|
||||
Pygments==2.20.0
|
||||
pymdown-extensions==10.21.3
|
||||
pymdown-extensions==11.0
|
||||
pyparsing==3.3.2
|
||||
python-dateutil==2.9.0.post0
|
||||
PyYAML==6.0.3
|
||||
pyyaml_env_tag==1.1
|
||||
six==1.17.0
|
||||
soupsieve==2.8.3
|
||||
soupsieve==2.8.4
|
||||
watchdog==6.0.0
|
||||
zipp==4.1.0
|
||||
|
||||
@@ -174,6 +174,9 @@ Available flags:
|
||||
|
||||
### Settable properties
|
||||
|
||||
> [!NOTE]
|
||||
> The `Report flags` column lists the CLI argument names accepted by `builds:report`. The JSON keys emitted by `builds:report --format json` are the same names with the leading `--builds-` stripped (e.g. `retention`, `global-retention`, `computed-retention`). Legacy keys with the `builds-` prefix (e.g. `builds-retention`) are also emitted during the 0.38.x deprecation window and will be removed in a future major release. Status keys (`build-id`, `build-status`, etc.) have no plugin prefix and are unaffected.
|
||||
|
||||
| Property | Scope | Default | Report flags | Description |
|
||||
|---|---|---|---|---|
|
||||
| `retention` | app + global | `20` | `--builds-retention`, `--builds-global-retention`, `--builds-computed-retention` | Number of recent build records kept per app; older finalized records are pruned at the end of each deploy |
|
||||
|
||||
@@ -7,7 +7,7 @@ The preferred method to attach persistent storage to a Dokku-managed container i
|
||||
|
||||
```
|
||||
storage:create <name> [<path>] [flags] # Register a named storage entry
|
||||
storage:destroy <name> # Remove a named storage entry (must be unmounted from every app first)
|
||||
storage:destroy <name> [--force] # Remove a named storage entry (must be unmounted from every app first)
|
||||
storage:ensure-directory [--chown option] <directory> # [DEPRECATED] use storage:create instead
|
||||
storage:exec <name> [-- <cmd>...] # Run a command (or shell) in a temporary container that mounts the entry
|
||||
storage:info <name> [--format text|json] # Show details for one storage entry
|
||||
@@ -57,9 +57,23 @@ dokku storage:list node-js-app --format json
|
||||
```
|
||||
[
|
||||
{
|
||||
"entry_name": "node-js-app",
|
||||
"host_path": "/var/lib/dokku/data/storage/node-js-app",
|
||||
"container_path": "/app/storage"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Each entry mirrors the underlying attachment. `readonly` (boolean) and `volume_options` (string) reflect `Attachment.Readonly` and `Attachment.VolumeOptions` directly and are only present when set, so external tooling can drift-detect attachments against the raw attachment fields. For example, a mount created with `--volume-options noexec,nosuid --volume-readonly` renders as:
|
||||
|
||||
```
|
||||
[
|
||||
{
|
||||
"entry_name": "node-js-data",
|
||||
"host_path": "/var/lib/dokku/data/storage/node-js-data",
|
||||
"container_path": "/app/storage",
|
||||
"volume_options": ""
|
||||
"readonly": true,
|
||||
"volume_options": "noexec,nosuid"
|
||||
}
|
||||
]
|
||||
```
|
||||
@@ -95,6 +109,8 @@ By default, permissions are set for usage with Herokuish buildpacks. These permi
|
||||
|
||||
Users deploying via Dockerfile will want to specify `--chown false` and manually `chown` the created directory if the user and/or group id of the runnning process in the deployed container do not correspond to any of the above options.
|
||||
|
||||
The `--chown` flag - whether on `storage:create` or `storage:ensure-directory` - only manages the default `/var/lib/dokku/data/storage/<name>` location. If a custom `<path>` is passed to `storage:create`, the chown call is refused and the operator must chown the path themselves.
|
||||
|
||||
> [!WARNING]
|
||||
> Failing to set the correct directory ownership may result in issues in persisting files written to the mounted storage directory.
|
||||
|
||||
@@ -116,6 +132,17 @@ In the first example, Dokku will then mount the shared contents of `/var/lib/dok
|
||||
|
||||
> If the `/storage` path within the container had pre-existing content, the container files will be over-written. This may be an issue for users that create assets at build time but then mount a directory at the same place during runtime. Files are not merged.
|
||||
|
||||
For named storage entries, additional Docker mount options can be passed via `--volume-options`. The value is a comma-separated mount-options string stored verbatim on the attachment and rendered into the `-v` flag at deploy time. This is useful for SELinux labels (`Z`, `z`) or hardening flags (`noexec,nosuid`):
|
||||
|
||||
```shell
|
||||
dokku storage:create node-js-data
|
||||
dokku storage:mount node-js-app node-js-data --container-dir /app/storage --volume-options Z
|
||||
```
|
||||
|
||||
When combined with `--volume-readonly`, the rendered options become `ro,<volume-options>` - for example, `--volume-options noexec,nosuid --volume-readonly` renders as `:ro,noexec,nosuid`.
|
||||
|
||||
Re-running `storage:mount` against a named entry with the same `--container-dir` and `--process-type` updates the existing attachment's mount-time attributes (`--phase`, `--volume-subpath`, `--volume-readonly`, `--volume-chown`, `--volume-options`) in place rather than appending a duplicate. This is the idempotent equivalent of `storage:set` for entries, and lets declarative tooling change a mount-time attribute without an unmount-then-remount dance that would briefly drop the volume from `storage:report`. Mount-time fields are rewritten wholesale, not merged - omitting a flag on a re-mount clears any previously-set value. The legacy `host:container[:opts]` form still rejects duplicates with `Mount path already exists.`.
|
||||
|
||||
Once persistent storage is mounted, the app requires a restart. See the [process scaling documentation](/docs/processes/process-management.md) for more information.
|
||||
|
||||
```shell
|
||||
@@ -140,6 +167,36 @@ Once persistent storage is unmounted, the app requires a restart. See the [proce
|
||||
dokku ps:restart app-name
|
||||
```
|
||||
|
||||
### Destroying storage entries
|
||||
|
||||
A named storage entry can be removed with the `storage:destroy` command. The entry must first be unmounted from every app that mounts it.
|
||||
|
||||
```shell
|
||||
dokku storage:destroy rdmtest-entry
|
||||
```
|
||||
|
||||
As the command is destructive - removing the registry entry and, depending on the scheduler and reclaim policy, the underlying volume - it will default to asking for confirmation before executing the removal.
|
||||
|
||||
```
|
||||
! WARNING: Potentially Destructive Action
|
||||
! This command will destroy storage entry rdmtest-entry.
|
||||
! To proceed, type "rdmtest-entry"
|
||||
> rdmtest-entry
|
||||
-----> Storage entry rdmtest-entry destroyed
|
||||
```
|
||||
|
||||
The confirmation may be avoided by providing the `--force` flag, which is useful for non-interactive or automated callers:
|
||||
|
||||
```shell
|
||||
dokku storage:destroy rdmtest-entry --force
|
||||
```
|
||||
|
||||
The global `--force` flag is also supported:
|
||||
|
||||
```shell
|
||||
dokku --force storage:destroy rdmtest-entry
|
||||
```
|
||||
|
||||
### Displaying storage reports for an app
|
||||
|
||||
> [!IMPORTANT]
|
||||
@@ -185,6 +242,60 @@ You can pass flags which will output only the value of the specific information
|
||||
dokku storage:report node-js-app --storage-deploy-mounts
|
||||
```
|
||||
|
||||
In addition to the aggregated `Storage build/deploy/run mounts:` lines, the report emits one flat dotted key per attachment field, indexed from `1`. The key shape is `--storage-attachment.<index>.<field>` for each of `entry-name`, `host-path`, `container-path`, `phases`, `process-type`, `subpath`, `readonly`, `volume-options`, and `volume-chown`. Fields render as empty strings when unset, and attachments are ordered by lex-sort of the index (so `10` sorts before `2`):
|
||||
|
||||
```shell
|
||||
dokku storage:create node-js-data
|
||||
dokku storage:mount node-js-app node-js-data --container-dir /app/storage --volume-options Z --volume-chown herokuish --volume-subpath uploads
|
||||
dokku storage:report node-js-app
|
||||
```
|
||||
|
||||
```
|
||||
=====> node-js-app storage information
|
||||
Storage attachment 1 container path: /app/storage
|
||||
Storage attachment 1 entry name: node-js-data
|
||||
Storage attachment 1 host path: /var/lib/dokku/data/storage/node-js-data
|
||||
Storage attachment 1 phases: deploy,run
|
||||
Storage attachment 1 process type: _default_
|
||||
Storage attachment 1 readonly: false
|
||||
Storage attachment 1 subpath: uploads
|
||||
Storage attachment 1 volume chown: herokuish
|
||||
Storage attachment 1 volume options: Z
|
||||
Storage build mounts:
|
||||
Storage deploy mounts: -v /var/lib/dokku/data/storage/node-js-data:/app/storage:Z
|
||||
Storage run mounts: -v /var/lib/dokku/data/storage/node-js-data:/app/storage:Z
|
||||
```
|
||||
|
||||
The same keys are exposed in JSON output, both in the stripped (`attachment.1.volume-options`) and legacy (`storage-attachment.1.volume-options`) forms:
|
||||
|
||||
```shell
|
||||
dokku storage:report node-js-app --format json | jq '. | with_entries(select(.key | startswith("attachment.")))'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"attachment.1.container-path": "/app/storage",
|
||||
"attachment.1.entry-name": "node-js-data",
|
||||
"attachment.1.host-path": "/var/lib/dokku/data/storage/node-js-data",
|
||||
"attachment.1.phases": "deploy,run",
|
||||
"attachment.1.process-type": "_default_",
|
||||
"attachment.1.readonly": "false",
|
||||
"attachment.1.subpath": "uploads",
|
||||
"attachment.1.volume-chown": "herokuish",
|
||||
"attachment.1.volume-options": "Z"
|
||||
}
|
||||
```
|
||||
|
||||
A single attachment field can be fetched directly via the info-flag form:
|
||||
|
||||
```shell
|
||||
dokku storage:report node-js-app --storage-attachment.1.volume-options
|
||||
```
|
||||
|
||||
```
|
||||
Z
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Sharing storage across deploys
|
||||
|
||||
@@ -36,35 +36,35 @@ dokku plugin:list
|
||||
|
||||
```
|
||||
plugn: dev
|
||||
00_dokku-standard 0.38.6 enabled dokku core standard plugin
|
||||
20_events 0.38.6 enabled dokku core events logging plugin
|
||||
app-json 0.38.6 enabled dokku core app-json plugin
|
||||
apps 0.38.6 enabled dokku core apps plugin
|
||||
build-env 0.38.6 enabled dokku core build-env plugin
|
||||
buildpacks 0.38.6 enabled dokku core buildpacks plugin
|
||||
certs 0.38.6 enabled dokku core certificate management plugin
|
||||
checks 0.38.6 enabled dokku core checks plugin
|
||||
common 0.38.6 enabled dokku core common plugin
|
||||
config 0.38.6 enabled dokku core config plugin
|
||||
docker-options 0.38.6 enabled dokku core docker-options plugin
|
||||
domains 0.38.6 enabled dokku core domains plugin
|
||||
enter 0.38.6 enabled dokku core enter plugin
|
||||
git 0.38.6 enabled dokku core git plugin
|
||||
logs 0.38.6 enabled dokku core logs plugin
|
||||
network 0.38.6 enabled dokku core network plugin
|
||||
nginx-vhosts 0.38.6 enabled dokku core nginx-vhosts plugin
|
||||
plugin 0.38.6 enabled dokku core plugin plugin
|
||||
proxy 0.38.6 enabled dokku core proxy plugin
|
||||
ps 0.38.6 enabled dokku core ps plugin
|
||||
repo 0.38.6 enabled dokku core repo plugin
|
||||
resource 0.38.6 enabled dokku core resource plugin
|
||||
scheduler-docker-local 0.38.6 enabled dokku core scheduler-docker-local plugin
|
||||
shell 0.38.6 enabled dokku core shell plugin
|
||||
ssh-keys 0.38.6 enabled dokku core ssh-keys plugin
|
||||
storage 0.38.6 enabled dokku core storage plugin
|
||||
tags 0.38.6 enabled dokku core tags plugin
|
||||
tar 0.38.6 enabled dokku core tar plugin
|
||||
trace 0.38.6 enabled dokku core trace plugin
|
||||
00_dokku-standard 0.38.20 enabled dokku core standard plugin
|
||||
20_events 0.38.20 enabled dokku core events logging plugin
|
||||
app-json 0.38.20 enabled dokku core app-json plugin
|
||||
apps 0.38.20 enabled dokku core apps plugin
|
||||
build-env 0.38.20 enabled dokku core build-env plugin
|
||||
buildpacks 0.38.20 enabled dokku core buildpacks plugin
|
||||
certs 0.38.20 enabled dokku core certificate management plugin
|
||||
checks 0.38.20 enabled dokku core checks plugin
|
||||
common 0.38.20 enabled dokku core common plugin
|
||||
config 0.38.20 enabled dokku core config plugin
|
||||
docker-options 0.38.20 enabled dokku core docker-options plugin
|
||||
domains 0.38.20 enabled dokku core domains plugin
|
||||
enter 0.38.20 enabled dokku core enter plugin
|
||||
git 0.38.20 enabled dokku core git plugin
|
||||
logs 0.38.20 enabled dokku core logs plugin
|
||||
network 0.38.20 enabled dokku core network plugin
|
||||
nginx-vhosts 0.38.20 enabled dokku core nginx-vhosts plugin
|
||||
plugin 0.38.20 enabled dokku core plugin plugin
|
||||
proxy 0.38.20 enabled dokku core proxy plugin
|
||||
ps 0.38.20 enabled dokku core ps plugin
|
||||
repo 0.38.20 enabled dokku core repo plugin
|
||||
resource 0.38.20 enabled dokku core resource plugin
|
||||
scheduler-docker-local 0.38.20 enabled dokku core scheduler-docker-local plugin
|
||||
shell 0.38.20 enabled dokku core shell plugin
|
||||
ssh-keys 0.38.20 enabled dokku core ssh-keys plugin
|
||||
storage 0.38.20 enabled dokku core storage plugin
|
||||
tags 0.38.20 enabled dokku core tags plugin
|
||||
tar 0.38.20 enabled dokku core tar plugin
|
||||
trace 0.38.20 enabled dokku core trace plugin
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
|
||||
@@ -146,13 +146,27 @@ dokku registry:set node-js-app image-repo
|
||||
|
||||
### Templating the image repository name
|
||||
|
||||
Instead of setting the image repository name on a per-app basis, it can be set via a template globally with the `image-repo-template` property:
|
||||
Instead of setting the image repository name on a per-app basis, it can be set via a template with the `image-repo-template` property. The property can be set globally or for a specific app, with the per-app value overriding the global value when both are present:
|
||||
|
||||
```shell
|
||||
# globally
|
||||
dokku registry:set --global image-repo-template "my-awesome-prefix/{{ .AppName }}"
|
||||
|
||||
# per-app
|
||||
dokku registry:set node-js-app image-repo-template "my-awesome-prefix/{{ .AppName }}-prod"
|
||||
```
|
||||
|
||||
Dokku uses a Golang template and has access to the `AppName` variable as shown above.
|
||||
Dokku uses a Golang template and has access to the `AppName` variable as shown above. The per-app `image-repo` property always takes precedence over the rendered template when both are set.
|
||||
|
||||
Setting the property value to an empty string will reset the value to the system default. Resetting the value can be done per app or globally.
|
||||
|
||||
```shell
|
||||
# per-app
|
||||
dokku registry:set node-js-app image-repo-template
|
||||
|
||||
# globally
|
||||
dokku registry:set --global image-repo-template
|
||||
```
|
||||
|
||||
### Pushing images on build
|
||||
|
||||
@@ -178,6 +192,10 @@ dokku registry:set node-js-app push-on-release
|
||||
dokku registry:set --global push-on-release
|
||||
```
|
||||
|
||||
#### Recovering from local image GC
|
||||
|
||||
When `push-on-release` is enabled, Dokku treats the remote registry as the canonical store for app images. If a local image disappears - for example because a `docker image prune` cron ran, the host rebooted, or an operator removed it manually - subsequent commands that need the image (`ps:restart`, `ps:scale`, `dokku run`, `domains:add`, `certs:add`, etc.) will pull the missing tag back from the registry automatically. Per-app registry credentials configured via `registry:login` are honored during the pull. This recovery covers the deployed numeric tag pushed by the registry plugin - a missing `latest` tag will be ignored.
|
||||
|
||||
### Push extra tags
|
||||
|
||||
To push the image on release with extra tags, set the `push-extra-tags` to a comma-separated list of tags via the `registry:set` command. The default value for this property is empty. Setting the property will result in the image being tagged with extra tags every release.
|
||||
@@ -213,10 +231,13 @@ dokku registry:set --global push-extra-tags
|
||||
|
||||
### Settable properties
|
||||
|
||||
> [!NOTE]
|
||||
> The `Report flags` column lists the CLI argument names accepted by `registry:report`. The JSON keys emitted by `registry:report --format json` are the same names with the leading `--registry-` stripped (e.g. `image-repo`, `global-server`, `computed-push-on-release`). Legacy keys with the `registry-` prefix (e.g. `registry-image-repo`) are also emitted during the 0.38.x deprecation window and will be removed in a future major release.
|
||||
|
||||
| Property | Scope | Default | Report flags | Description |
|
||||
|---|---|---|---|---|
|
||||
| `image-repo` | app only | `dokku/<app>` | `--registry-image-repo`, `--registry-computed-image-repo` | Repository name used when pushing the app's image (overrides the global template) |
|
||||
| `image-repo-template` | global only | `dokku/{{ .AppName }}` | `--registry-global-image-repo-template` | Go template used to compute the per-app image repository when `image-repo` is unset |
|
||||
| `push-extra-tags` | app + global | none | `--registry-push-extra-tags` | Comma-separated list of additional tags pushed alongside the deploy tag |
|
||||
| `image-repo-template` | app + global | none | `--registry-image-repo-template`, `--registry-global-image-repo-template`, `--registry-computed-image-repo-template` | Go template used to compute the per-app image repository when `image-repo` is unset |
|
||||
| `push-extra-tags` | app + global | none | `--registry-push-extra-tags`, `--registry-global-push-extra-tags`, `--registry-computed-push-extra-tags` | Comma-separated list of additional tags pushed alongside the deploy tag |
|
||||
| `push-on-release` | app + global | `false` | `--registry-push-on-release`, `--registry-global-push-on-release`, `--registry-computed-push-on-release` | When `true`, pushes the image to the registry on every successful build |
|
||||
| `server` | app + global | none | `--registry-server`, `--registry-global-server`, `--registry-computed-server` | Registry server host (e.g. `ghcr.io`) used when pushing images |
|
||||
|
||||
@@ -21,7 +21,10 @@
|
||||
- The `docker-local` scheduler now sends `SIGTERM` to old containers immediately after a successful deploy, rather than waiting `wait-to-retire` seconds before signaling. This matches Heroku's graceful-shutdown contract and lets applications begin draining in-flight work as soon as proxy traffic switches. The `wait-to-retire` grace period and `stop-timeout-seconds` hard-stop continue to apply as before. See the [zero downtime deploys documentation](/docs/deployment/zero-downtime-deploys.md#wait-to-retire) for more details.
|
||||
- The `docker-local` scheduler no longer queues an image for retirement when another running container of the same app still uses it. This fixes the case where a `ps:rebuild` against an image-based deploy (`git:from-image`) produced an identical-SHA image and the `dokku-retire` cron timer would log `Image ... has running containers, skipping rm` on every run. Stuck entries from prior versions are pruned automatically on the next `ps:retire` run.
|
||||
- All `:report` subcommands now accept the `--global` flag, which scopes the report to globally-configured properties. The flag composes with `--format json`, so a JSON report of global properties can be obtained via, for example, `dokku scheduler:report --global --format json`. Previously, combining `--global` with `--format json` was rejected with an "info flag" error, and `--global` on its own was treated as an unknown flag.
|
||||
- Every `:report` key of the form `<plugin>-global-<property>` now returns the raw stored global value (empty when the property has never been set) instead of the resolved value with the built-in default substituted in. A new `<plugin>-computed-<property>` key has been added wherever a default existed and returns the effective value (per-app value, falling back to the global value, then to the built-in default). This affects `caddy`, `haproxy`, `traefik`, `app-json`, `builder-dockerfile`, `builder-herokuish`, `builder-lambda`, `builder-nixpacks`, `builder-pack`, `builder-railpack`, `checks`, `git`, `logs`, `network`, `proxy`, and `scheduler`. External tooling that read `<plugin>-global-<property>` and depended on the default value should switch to `<plugin>-computed-<property>`. The bare `<plugin>-<property>` keys for caddy/haproxy/traefik global-only properties (`caddy-image`, `haproxy-log-level`, `traefik-api-enabled`, etc.) have also been replaced by the `global-` and `computed-` pair.
|
||||
- Every `:report` key of the form `<plugin>-global-<property>` now returns the raw stored global value (empty when the property has never been set) instead of the resolved value with the built-in default substituted in. A new `<plugin>-computed-<property>` key has been added wherever a default existed and returns the effective value (per-app value, falling back to the global value, then to the built-in default). This affects `app-json`, `builder-dockerfile`, `builder-herokuish`, `builder-lambda`, `builder-nixpacks`, `builder-pack`, `builder-railpack`, `caddy`, `checks`, `git`, `haproxy`, `logs`, `network`, `nginx`, `openresty`, `proxy`, `registry`, `scheduler`, `scheduler-k3s` and `traefik`. External tooling that read `<plugin>-global-<property>` and depended on the default value should switch to `<plugin>-computed-<property>`. The bare `<plugin>-<property>` keys for caddy/haproxy/traefik/openresty global-only properties (`caddy-image`, `haproxy-log-level`, `traefik-api-enabled`, `openresty-image`, `openresty-letsencrypt-email`, `openresty-letsencrypt-server`, `openresty-allowed-letsencrypt-domains-func-base64`, etc.) have also been replaced by the `global-` and `computed-` pair.
|
||||
- The `ps` and `cron` plugins now follow the same `<plugin>-global-<property>` / `<plugin>-computed-<property>` convention. The `ps:report` keys `stop-timeout-seconds`, `global-stop-timeout-seconds`, and `computed-stop-timeout-seconds` have been renamed to `ps-stop-timeout-seconds`, `ps-global-stop-timeout-seconds`, and `ps-computed-stop-timeout-seconds`. The `cron:report` keys `cron-mailfrom` and `cron-mailto` have been renamed to `cron-global-mailfrom` and `cron-global-mailto`, and new `cron-computed-mailfrom` and `cron-computed-mailto` keys have been added. The corresponding user-facing `--<flag>` arguments to `ps:report` and `cron:report` were renamed alongside the JSON keys; no aliases are kept. Additionally, both `ps:report --global` and `cron:report --global` now emit the `<plugin>-computed-<property>` keys for every settable global property with a default (`ps-computed-procfile-path`, `ps-computed-stop-timeout-seconds`, `cron-computed-maintenance`, `cron-computed-mailfrom`, `cron-computed-mailto`), matching the shape used by the other plugins.
|
||||
- A second round of `:report` additions surfaces every remaining settable-but-unreported property under the same raw/global/computed convention so external tooling can verify drift via `:report --format json` without falling back to a generic bash task. The `ps` plugin gains `--ps-dockerfile-start-cmd` and `--ps-computed-dockerfile-start-cmd`, `--ps-start-cmd` and `--ps-computed-start-cmd`, and the `--ps-skip-deploy` / `--ps-global-skip-deploy` / `--ps-computed-skip-deploy` triple (default `false`). The `builder` plugin gains the `--builder-skip-cleanup` triple (default `false`). The `scheduler` plugin gains the `--scheduler-shell` triple. The `proxy` plugin gains the `--proxy-proxy-port` and `--proxy-proxy-ssl-port` triples and exposes the raw `disabled` property as `--proxy-disabled` / `--proxy-computed-disabled`, alongside the existing inverted `--proxy-enabled`. The `openresty` plugin gains `--openresty-global-log-level` and `--openresty-computed-log-level` (default `ERROR`). The `nginx` plugin gains the `--nginx-nginx-service-command` triple. The `scheduler-k3s` plugin gains `--scheduler-k3s-global-token`, but the value is masked as `*******` in default stdout output; the raw value is returned only when the report is requested via `--format json` or when this flag is queried explicitly by name. The traefik `dns-provider-<env_var>` keys now follow the same explicit-query rule - previously they were unmasked only for `--format json`, but a query like `dokku traefik:report --traefik-dns-provider-cf_api_key` now returns the actual value instead of `*******`.
|
||||
- All Go-implemented plugins (`app-json`, `apps`, `builder`, `buildpacks`, `builds`, `cron`, `docker-options`, `logs`, `network`, `ports`, `proxy`, `ps`, `registry`, `resource`, `scheduler`, `scheduler-k3s`, `storage`) now emit JSON keys from `:report --format json` without the `<plugin>-` head segment, matching the shape bash plugins have always emitted. For example, `dokku ps:report myapp --format json` now contains `stop-timeout-seconds`, `global-stop-timeout-seconds`, and `computed-stop-timeout-seconds` keys. The CLI flag names (`--ps-stop-timeout-seconds`, etc.) are unchanged, and `:set` semantics are unchanged. For backwards compatibility during the 0.38.x patch series, the old `<plugin>-<property>` JSON keys are emitted side-by-side with the new keys, so external scripts reading either shape continue to work. The legacy keys will be dropped in a future major release. External JSON consumers should migrate to the new key shape.
|
||||
- The `scheduler-k3s` plugin now manages env config and the dokku-generated image pull Secret as their own helm releases with stable names (`config-{app}` and `pull-secret-{app}`) rather than bundling them into the app helm chart with a per-deploy timestamp suffix (`env-{app}.{ts}` / `ims-{app}.{ts}`). This fixes two bugs: a helm rollback of the app chart no longer deletes Secrets that older ReplicaSets still reference, and the Deployment's `imagePullSecrets` list no longer accumulates references to nonexistent Secrets across deploys. The next deploy of an app switches the Deployment's `envFrom` and `imagePullSecrets` references to the stable names and prunes any leaked entries; existing live Deployments do not need to be patched manually. App rename now also uninstalls the old `tls-{app}`, `config-{app}`, and `pull-secret-{app}` releases under the previous app name; the new name's releases are recreated on the next deploy or certs sync.
|
||||
- The storage plugin now treats persistent volumes as named, scheduler-aware first-class resources via `storage:create`, `storage:mount`, `storage:set`, and `storage:destroy`. The legacy `storage:mount <app> <host>:<container>` colon form continues to work on docker-local apps but is deprecated; on k3s apps it is rejected. Existing colon-form mounts are migrated automatically the first time the new storage plugin runs (during the install trigger) - they appear as `legacy-<hash>` entries in `storage:list-entries`. The migration is idempotent and tied to a per-app flag file at `$DOKKU_LIB_ROOT/config/storage/.migrated/<app>`; deleting that file forces a re-scan on the next install. The `storage:ensure-directory` command keeps working but now emits a deprecation warning - prefer `storage:create <name> [<path>]` (the path defaults to the same `$DOKKU_LIB_ROOT/data/storage/<name>` location). Storage entry names must now be DNS-1123 labels of 45 characters or less so they can be used verbatim as Helm release and Kubernetes resource names; underscores and uppercase characters that the older `ensure-directory` validator accepted are rejected for new names. The migration synthesizer always uses lowercase hex hashes so existing data is never locked out.
|
||||
|
||||
|
||||
@@ -293,6 +293,9 @@ An autoscaling trigger consists of the following properties:
|
||||
|
||||
### Settable properties
|
||||
|
||||
> [!NOTE]
|
||||
> The `Report flags` column lists the CLI argument names accepted by `app-json:report`. The JSON keys emitted by `app-json:report --format json` are the same names with the leading `--app-json-` stripped (e.g. `appjson-path`, `global-appjson-path`, `computed-appjson-path`). Legacy keys with the `app-json-` prefix are also emitted during the 0.38.x deprecation window and will be removed in a future major release.
|
||||
|
||||
| Property | Scope | Default | Report flags | Description |
|
||||
|---|---|---|---|---|
|
||||
| `appjson-path` | app + global | `app.json` | `--app-json-appjson-path`, `--app-json-global-appjson-path`, `--app-json-computed-appjson-path` | Path within the app to the `app.json` manifest, relative to the build root |
|
||||
|
||||
@@ -7,3 +7,22 @@ The `nginx.conf.sigil` file is used to configure the nginx server for an applica
|
||||
A custom `nginx.conf.sigil` is pre-validated at the start of every deploy, immediately after it is extracted from the source tree and before the build phase runs. Pre-validation renders the template via sigil with the same parameters used at deploy time, wraps the rendered config in a minimal `events`/`http` scaffold, and runs `nginx -t` against the result. The deploy is aborted if either the sigil render or the `nginx -t` check fails, so build work is not wasted on a syntactically invalid template. When no app listeners exist yet (typical for first deploys), pre-validation injects a placeholder `127.0.0.1:5000` listener for `DOKKU_APP_WEB_LISTENERS` so that the rendered upstream block has a static server entry and `nginx -t` does not bail out on "host not found in upstream".
|
||||
|
||||
Pre-validation is skipped when the proxy type is not `nginx` or when `disable-custom-config` is set to `true` for the app.
|
||||
|
||||
## HTTP/2
|
||||
|
||||
nginx 1.25.1 deprecated the `http2` parameter on the `listen` directive in favor of a standalone `http2 on;` directive. Custom `nginx.conf.sigil` templates that hardcode `listen ... ssl http2;` will produce `nginx: [warn] the "listen ... http2" directive is deprecated` warnings when run against nginx 1.25.1 or newer.
|
||||
|
||||
Dokku exposes the `HTTP2_DIRECTIVE_SUPPORTED` template variable, set to `"true"` when the host nginx is 1.25.1 or newer, so a single template can render the correct syntax against either version. The default template uses this pattern:
|
||||
|
||||
```
|
||||
{{ if eq $.HTTP2_DIRECTIVE_SUPPORTED "true" }}
|
||||
listen [{{ $.NGINX_BIND_ADDRESS_IP6 }}]:{{ $listen_port }} ssl;
|
||||
listen {{ if $.NGINX_BIND_ADDRESS_IP4 }}{{ $.NGINX_BIND_ADDRESS_IP4 }}:{{end}}{{ $listen_port }} ssl;
|
||||
http2 on;
|
||||
{{ else }}
|
||||
listen [{{ $.NGINX_BIND_ADDRESS_IP6 }}]:{{ $listen_port }} ssl http2;
|
||||
listen {{ if $.NGINX_BIND_ADDRESS_IP4 }}{{ $.NGINX_BIND_ADDRESS_IP4 }}:{{end}}{{ $listen_port }} ssl http2;
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
Dokku logs a deprecation warning during deploys when a custom template still uses the `listen ... http2` form, so the offending template can be located via the deploy output.
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square70x70logo src="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/mstile-70x70.png"/>
|
||||
<square150x150logo src="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/mstile-150x150.png"/>
|
||||
<square310x310logo src="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/mstile-310x310.png"/>
|
||||
<wide310x150logo src="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/mstile-310x150.png"/>
|
||||
<square70x70logo src="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/mstile-70x70.png"/>
|
||||
<square150x150logo src="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/mstile-150x150.png"/>
|
||||
<square310x310logo src="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/mstile-310x310.png"/>
|
||||
<wide310x150logo src="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/mstile-310x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
|
||||
@@ -2,37 +2,37 @@
|
||||
"name": "Dokku",
|
||||
"icons": [
|
||||
{
|
||||
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.38.6\/docs\/assets\/favicons\/android-chrome-36x36.png",
|
||||
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.38.20\/docs\/assets\/favicons\/android-chrome-36x36.png",
|
||||
"sizes": "36x36",
|
||||
"type": "image\/png",
|
||||
"density": "0.75"
|
||||
},
|
||||
{
|
||||
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.38.6\/docs\/assets\/favicons\/android-chrome-48x48.png",
|
||||
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.38.20\/docs\/assets\/favicons\/android-chrome-48x48.png",
|
||||
"sizes": "48x48",
|
||||
"type": "image\/png",
|
||||
"density": "1.0"
|
||||
},
|
||||
{
|
||||
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.38.6\/docs\/assets\/favicons\/android-chrome-72x72.png",
|
||||
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.38.20\/docs\/assets\/favicons\/android-chrome-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image\/png",
|
||||
"density": "1.5"
|
||||
},
|
||||
{
|
||||
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.38.6\/docs\/assets\/favicons\/android-chrome-96x96.png",
|
||||
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.38.20\/docs\/assets\/favicons\/android-chrome-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image\/png",
|
||||
"density": "2.0"
|
||||
},
|
||||
{
|
||||
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.38.6\/docs\/assets\/favicons\/android-chrome-144x144.png",
|
||||
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.38.20\/docs\/assets\/favicons\/android-chrome-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image\/png",
|
||||
"density": "3.0"
|
||||
},
|
||||
{
|
||||
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.38.6\/docs\/assets\/favicons\/android-chrome-192x192.png",
|
||||
"src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.38.20\/docs\/assets\/favicons\/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image\/png",
|
||||
"density": "4.0"
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
|
||||
/* Avatar section darkmode fade */
|
||||
--avatar-before-gradient: rgba(0, 0, 0, 0) linear-gradient(90deg, white 2.52%, rgba(255, 255, 255, 0) 100%) repeat scroll 0% 0%;
|
||||
--avatar-darkmode-before-gradient: rgba(0, 0, 0, 0) linear-gradient(90deg, #0.38.6 2.52%, rgba(255, 255, 255, 0) 100%) repeat scroll 0% 0%;
|
||||
--avatar-darkmode-before-gradient: rgba(0, 0, 0, 0) linear-gradient(90deg, #0.38.20 2.52%, rgba(255, 255, 255, 0) 100%) repeat scroll 0% 0%;
|
||||
--avatar-after-gradient: rgba(0, 0, 0, 0) linear-gradient(270deg, white 2.52%, rgba(255, 255, 255, 0) 100%) repeat scroll 0% 0%;
|
||||
--avatar-darkmode-after-gradient: rgba(0, 0, 0, 0) linear-gradient(270deg, #0.38.6 2.52%, rgba(255, 255, 255, 0) 100%) repeat scroll 0% 0%;
|
||||
--avatar-darkmode-after-gradient: rgba(0, 0, 0, 0) linear-gradient(270deg, #0.38.20 2.52%, rgba(255, 255, 255, 0) 100%) repeat scroll 0% 0%;
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -1191,15 +1191,15 @@ ul.fas li .fa-large:before {
|
||||
|
||||
.getting-started::after {
|
||||
background: linear-gradient(0deg, rgba(52, 52, 87, 0.2) -40.97%, rgba(0, 0, 0, 0) 103.19%),
|
||||
#0.38.6;
|
||||
#0.38.20;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
background: #0.38.6;
|
||||
background: #0.38.20;
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
background: #0.38.6;
|
||||
background: #0.38.20;
|
||||
}
|
||||
|
||||
.powered-by_brands {
|
||||
|
||||
@@ -35,6 +35,6 @@
|
||||
"0.35.20",
|
||||
"0.36.11",
|
||||
"0.37.10",
|
||||
"0.38.6"
|
||||
"0.38.20"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -315,6 +315,15 @@ dokku apps:report node-js-app --app-dir
|
||||
|
||||
## Properties
|
||||
|
||||
### Settable properties
|
||||
|
||||
> [!NOTE]
|
||||
> The `Report flags` column lists the CLI argument names accepted by `apps:report`. The JSON keys emitted by `apps:report --format json` are the same names with the leading `--app-` stripped (e.g. `global-disable-autocreation`). Legacy keys with the `app-` prefix (e.g. `app-global-disable-autocreation`) are also emitted during the 0.38.x deprecation window and will be removed in a future major release.
|
||||
|
||||
| Property | Scope | Default | Report flags | Description |
|
||||
|---|---|---|---|---|
|
||||
| `disable-autocreation` | global only | `false` | `--app-global-disable-autocreation` | When `true`, pushes to a remote for an app that does not yet exist are rejected instead of auto-creating the app |
|
||||
|
||||
### Read-only flags
|
||||
|
||||
The following flags surface in `apps:report` but are not managed by `apps:set`:
|
||||
|
||||
@@ -177,11 +177,14 @@ For a simple example of how to implement this trigger, see `builder-pack`, which
|
||||
|
||||
### Settable properties
|
||||
|
||||
> [!NOTE]
|
||||
> The `Report flags` column lists the CLI argument names accepted by `builder:report`. The JSON keys emitted by `builder:report --format json` are the same names with the leading `--builder-` stripped (e.g. `selected`, `global-selected`, `computed-selected`). Legacy keys with the `builder-` prefix are also emitted during the 0.38.x deprecation window and will be removed in a future major release.
|
||||
|
||||
| Property | Scope | Default | Report flags | Description |
|
||||
|---|---|---|---|---|
|
||||
| `build-dir` | app + global | none | `--builder-build-dir`, `--builder-global-build-dir`, `--builder-computed-build-dir` | Subdirectory within the repository to use as the build context |
|
||||
| `selected` | app + global | none | `--builder-selected`, `--builder-global-selected`, `--builder-computed-selected` | Builder plugin to use; overrides automatic detection |
|
||||
| `skip-cleanup` | app + global | none | (not in report) | When `true`, leaves intermediate build artifacts in place after the build |
|
||||
| `skip-cleanup` | app + global | `false` | `--builder-skip-cleanup`, `--builder-global-skip-cleanup`, `--builder-computed-skip-cleanup` | When `true`, leaves intermediate build artifacts in place after the build |
|
||||
|
||||
### Read-only flags
|
||||
|
||||
|
||||
@@ -197,6 +197,9 @@ dokku buildpacks:report node-js-app --buildpacks-list
|
||||
|
||||
These properties are managed via `buildpacks:set-property` (the legacy command name for this plugin).
|
||||
|
||||
> [!NOTE]
|
||||
> The `Report flags` column lists the CLI argument names accepted by `buildpacks:report`. The JSON keys emitted by `buildpacks:report --format json` are the same names with the leading `--buildpacks-` stripped (e.g. `stack`, `global-stack`, `computed-stack`). Legacy keys with the `buildpacks-` prefix are also emitted during the 0.38.x deprecation window and will be removed in a future major release.
|
||||
|
||||
| Property | Scope | Default | Report flags | Description |
|
||||
|---|---|---|---|---|
|
||||
| `stack` | app + global | none | `--buildpacks-stack`, `--buildpacks-global-stack`, `--buildpacks-computed-stack` | Herokuish stack image used to compile the app (e.g. `heroku/heroku:24`) |
|
||||
|
||||
@@ -316,6 +316,9 @@ dokku logs:set --global app-label-alias
|
||||
|
||||
### Settable properties
|
||||
|
||||
> [!NOTE]
|
||||
> The `Report flags` column lists the CLI argument names accepted by `logs:report`. The JSON keys emitted by `logs:report --format json` are the same names with the leading `--logs-` stripped (e.g. `max-size`, `global-max-size`, `computed-max-size`). Legacy keys with the `logs-` prefix are also emitted during the 0.38.x deprecation window and will be removed in a future major release.
|
||||
|
||||
| Property | Scope | Default | Report flags | Description |
|
||||
|---|---|---|---|---|
|
||||
| `app-label-alias` | app + global | `com.dokku.app-name` | `--logs-app-label-alias`, `--logs-global-app-label-alias`, `--logs-computed-app-label-alias` | Docker label key whose value is used to identify the app when shipping logs |
|
||||
|
||||
@@ -5,14 +5,19 @@
|
||||
|
||||
```
|
||||
scheduler-k3s:annotations:set <app|--global> <property> (<value>) [--process-type PROCESS_TYPE] <--resource-type RESOURCE_TYPE>, Set or clear an annotation for a given app/process-type/resource-type combination
|
||||
scheduler-k3s:annotations:report [<app>|--global] [--format stdout|json] [--process-type PROCESS_TYPE] [--resource-type RESOURCE_TYPE] # Displays a scheduler-k3s annotations report for one or more apps
|
||||
scheduler-k3s:autoscaling-auth:set <app|--global> <trigger> [<--metadata key=value>...], Set or clear a scheduler-k3s autoscaling keda trigger authentication resource for an app
|
||||
scheduler-k3s:autoscaling-auth:report <app|--global> [--format stdout|json] [--include-metadata] # Displays a scheduler-k3s autoscaling auth report for an app
|
||||
scheduler-k3s:autoscaling-auth:report [<app>|--global] [--format stdout|json] [--include-metadata] # Displays a scheduler-k3s autoscaling auth report for one or more apps
|
||||
scheduler-k3s:charts:report [<chart>] [--format stdout|json] # Displays a scheduler-k3s chart override report
|
||||
scheduler-k3s:charts:set <chart-name.property> (<value>) # Set or clear a chart-specific helm value
|
||||
scheduler-k3s:cluster:add [ssh://user@host:port] # Adds a server node to a Dokku-managed cluster
|
||||
scheduler-k3s:cluster:list # Lists all nodes in a Dokku-managed cluster
|
||||
scheduler-k3s:cluster:remove [node-id] # Removes client node to a Dokku-managed cluster
|
||||
scheduler-k3s:ensure-charts # Ensures the k3s charts are installed
|
||||
scheduler-k3s:initialize # Initializes a cluster
|
||||
scheduler-k3s:labels:set <app|--global> <property> (<value>) [--process-type PROCESS_TYPE] <--resource-type RESOURCE_TYPE> # Set or clear a label for a given app/process-type/resource-type combination
|
||||
scheduler-k3s:labels:report [<app>|--global] [--format stdout|json] [--process-type PROCESS_TYPE] [--resource-type RESOURCE_TYPE] # Displays a scheduler-k3s labels report for one or more apps
|
||||
scheduler-k3s:preview <app> [--context N] [--show-secrets] [--show-secrets-decoded] # Displays a diff between the current and next deployment for an app
|
||||
scheduler-k3s:profiles:add <profile> [--role ROLE] [--insecure-allow-unknown-hosts] [--taint-scheduling] [--kubelet-args KUBELET_ARGS] Adds a node profile to the k3s cluster
|
||||
scheduler-k3s:profiles:list [--format json|stdout] # Lists all node profiles in the k3s cluster
|
||||
scheduler-k3s:profiles:remove <profile> # Removes a node profile from the k3s cluster
|
||||
@@ -232,6 +237,38 @@ The global default value may be set by passing an empty value for the option.
|
||||
dokku scheduler-k3s:set --global deploy-timeout
|
||||
```
|
||||
|
||||
### Previewing deployment changes
|
||||
|
||||
Before triggering a deploy, the `scheduler-k3s:preview` command can be used to display a unified diff between the manifests currently stored in the live Helm release for an app and the manifests that the next deploy would roll out:
|
||||
|
||||
```shell
|
||||
dokku scheduler-k3s:preview node-js-app
|
||||
```
|
||||
|
||||
A clean redeploy with no property changes produces no output. After modifying a property that affects the main app chart (for example `dokku resource:limit --memory 256m node-js-app`), the command shows the unified diff for each changed resource. For an app that has never been deployed, the entire proposed manifest is rendered as added lines.
|
||||
|
||||
By default each change is shown with 3 lines of surrounding context, git-diff style. The `--context` flag overrides this; pass `-1` to render the full resource around every change:
|
||||
|
||||
```shell
|
||||
dokku scheduler-k3s:preview node-js-app --context 0
|
||||
dokku scheduler-k3s:preview node-js-app --context -1
|
||||
```
|
||||
|
||||
By default any `kind: Secret` resources have their `data` values redacted so secret bytes never appear in terminal scrollback or CI logs. Two flags adjust this behavior:
|
||||
|
||||
```shell
|
||||
dokku scheduler-k3s:preview node-js-app --show-secrets
|
||||
dokku scheduler-k3s:preview node-js-app --show-secrets-decoded
|
||||
```
|
||||
|
||||
`--show-secrets` keeps the raw base64-encoded values, and `--show-secrets-decoded` renders them base64-decoded. In current dokku, the only Secret objects in the main app chart come from KEDA autoscaling auth resources configured via [`scheduler-k3s:autoscaling-auth:set`](#workload-autoscaling-authentication); for apps without autoscaling auth, these flags are no-ops.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The preview only covers the main app helm release (the release named after the app). Dokku installs three auxiliary helm releases per app for config-var storage, image-pull credentials, and TLS certificates, and changes to those releases do **not** appear in this preview. In particular, a `dokku config:set` change followed by `scheduler-k3s:preview` will show no diff because the new config-var value lives in the separate `config-<app>` helm release.
|
||||
|
||||
> [!NOTE]
|
||||
> Helm renders the proposed manifest using a client-side dry-run. The `lookup` template function returns empty values in this mode, so charts overridden via `scheduler-k3s:charts:set` that depend on `lookup` may render differently here than at actual deploy time. Dokku's bundled chart templates do not use `lookup`.
|
||||
|
||||
### Exposing services on the network
|
||||
|
||||
Dokku will automatically expose the `web` process as a Kubernetes Service, with all others being treated as background processes. In some cases, it may be useful to have other processes exposed as Kubernetes Service objects so as to segregate internal http endpoints from public http endpoints. This can be done by modifying the `app.json` Formation entry for your process type.
|
||||
@@ -372,6 +409,8 @@ The following resource types are supported:
|
||||
- `traefik_ingressroute`
|
||||
- `traefik_middleware`
|
||||
|
||||
Annotation keys may contain `/` (e.g. Kubernetes-style keys such as `prometheus.io/scrape`) and values may span multiple lines; both are preserved verbatim.
|
||||
|
||||
A `ps:restart` is required after setting annotations in order to have them apply to running resources.
|
||||
|
||||
#### Removing an annotation
|
||||
@@ -385,6 +424,35 @@ dokku scheduler-k3s:annotations:set node-js-app annotation.key --resource-type d
|
||||
|
||||
A `ps:restart` is required after removing annotations in order to remove them from running resources.
|
||||
|
||||
#### Displaying annotations
|
||||
|
||||
Configured annotations can be inspected with the `scheduler-k3s:annotations:report` command. Without arguments, it iterates every app and prints all annotations. Passing an app name (or `--global`) scopes the report:
|
||||
|
||||
```shell
|
||||
dokku scheduler-k3s:annotations:report
|
||||
dokku scheduler-k3s:annotations:report node-js-app
|
||||
dokku scheduler-k3s:annotations:report --global
|
||||
```
|
||||
|
||||
`--process-type` and `--resource-type` flags narrow the output to a specific scope, matching the flags accepted by `scheduler-k3s:annotations:set`:
|
||||
|
||||
```shell
|
||||
dokku scheduler-k3s:annotations:report node-js-app --resource-type deployment
|
||||
dokku scheduler-k3s:annotations:report node-js-app --process-type web --resource-type deployment
|
||||
```
|
||||
|
||||
JSON output emits flat keys of the form `{process_type}.{resource_type}.{annotation_key}`. The literal `--global` process type is rendered as `global` to keep keys free of leading dashes:
|
||||
|
||||
```shell
|
||||
dokku scheduler-k3s:annotations:report node-js-app --format json
|
||||
```
|
||||
|
||||
A single value can also be read directly with a flag of the form `--scheduler-k3s-annotations.{process_type}.{resource_type}.{annotation_key}`:
|
||||
|
||||
```shell
|
||||
dokku scheduler-k3s:annotations:report node-js-app --scheduler-k3s-annotations.global.deployment.annotation.key
|
||||
```
|
||||
|
||||
#### Setting Labels
|
||||
|
||||
Dokku injects certain labels into each created resource by default, but it may be necessary to inject others for tighter integration with third-party tools. The `scheduler-k3s:labels:set` command can be used to perform this task. The command takes an app name and a required `--resource-type` flag.
|
||||
@@ -413,6 +481,8 @@ The following resource types are supported:
|
||||
- `traefik_ingressroute`
|
||||
- `traefik_middleware`
|
||||
|
||||
Label keys may contain `/` (e.g. Kubernetes-style keys such as `app.kubernetes.io/part-of`) and values may span multiple lines; both are preserved verbatim.
|
||||
|
||||
A `ps:restart` is required after setting labels in order to have them apply to running resources.
|
||||
|
||||
#### Removing a label
|
||||
@@ -426,6 +496,20 @@ dokku scheduler-k3s:labels:set node-js-app label.key --resource-type deployment
|
||||
|
||||
A `ps:restart` is required after removing labels in order to remove them from running resources.
|
||||
|
||||
#### Displaying labels
|
||||
|
||||
Configured labels can be inspected with the `scheduler-k3s:labels:report` command. The surface mirrors `scheduler-k3s:annotations:report`:
|
||||
|
||||
```shell
|
||||
dokku scheduler-k3s:labels:report
|
||||
dokku scheduler-k3s:labels:report node-js-app
|
||||
dokku scheduler-k3s:labels:report --global
|
||||
dokku scheduler-k3s:labels:report node-js-app --resource-type deployment
|
||||
dokku scheduler-k3s:labels:report node-js-app --process-type web --resource-type deployment
|
||||
dokku scheduler-k3s:labels:report node-js-app --format json
|
||||
dokku scheduler-k3s:labels:report node-js-app --scheduler-k3s-labels.global.deployment.label.key
|
||||
```
|
||||
|
||||
### Autoscaling
|
||||
|
||||
#### Workload Autoscaling
|
||||
@@ -564,10 +648,12 @@ dokku scheduler-k3s:autoscaling-auth:set $APP $TRIGGER_TYPE
|
||||
|
||||
##### Displaying an Authentication Resource report
|
||||
|
||||
To see a list of authentication resources managed by Dokku, run the `scheduler-k3s:autoscaling-auth:report` command.
|
||||
To see a list of authentication resources managed by Dokku, run the `scheduler-k3s:autoscaling-auth:report` command. Without arguments, the report iterates every app; passing an app name (or `--global`) scopes the report:
|
||||
|
||||
```shell
|
||||
dokku scheduler-k3s:autoscaling-auth:report
|
||||
dokku scheduler-k3s:autoscaling-auth:report node-js-app
|
||||
dokku scheduler-k3s:autoscaling-auth:report --global
|
||||
```
|
||||
|
||||
```
|
||||
@@ -658,21 +744,34 @@ The default value for the `kube-context` is an empty string, and will result in
|
||||
|
||||
### Customizing Helm Chart Properties
|
||||
|
||||
Dokku includes a number of helm charts by default with settings that are optimized for Dokku. That said, it may be useful to further customize the charts for a given environment. Users can customize which charts are installed by setting properties prefixed with `chart.$CHART_NAME.` with the `--global` flag.
|
||||
Dokku includes a number of helm charts by default with settings that are optimized for Dokku. That said, it may be useful to further customize the charts for a given environment. Chart overrides are managed via the `scheduler-k3s:charts:set` command, which takes a `<chart-name>.<property>` argument and a value. Chart overrides are always global because helm charts are not managed on a per-app basis.
|
||||
|
||||
```shell
|
||||
dokku scheduler-k3s:set --global chart.cert-manager.version 1.13.3
|
||||
dokku scheduler-k3s:charts:set cert-manager.version 1.13.3
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Properties follow dot-notation, and are expanded according to Helm's internal logic. See the [Helm documentation](https://helm.sh/docs/helm/helm_install/#helm-install) for `helm install` for further details.
|
||||
|
||||
To unset a chart property, omit the value from the `scheduler-k3s:set` call:
|
||||
Property names may contain `/` (e.g. for Kubernetes-style annotation keys such as `service.annotations.prometheus.io/scrape`) and values may span multiple lines; both are preserved verbatim.
|
||||
|
||||
To unset a chart property, omit the value from the `scheduler-k3s:charts:set` call:
|
||||
|
||||
```shell
|
||||
dokku scheduler-k3s:set --global chart.cert-manager.version
|
||||
dokku scheduler-k3s:charts:set cert-manager.version
|
||||
```
|
||||
|
||||
Configured chart overrides can be inspected with the `scheduler-k3s:charts:report` command. Without an argument, it lists every chart known to Dokku along with any configured overrides; passing a chart name scopes the report to that chart:
|
||||
|
||||
```shell
|
||||
dokku scheduler-k3s:charts:report
|
||||
dokku scheduler-k3s:charts:report cert-manager
|
||||
dokku scheduler-k3s:charts:report --format json
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> The legacy form `dokku scheduler-k3s:set --global chart.<chart>.<property> <value>` continues to work but is deprecated and will be removed in a future major release. Migrate to `scheduler-k3s:charts:set` instead.
|
||||
|
||||
A `scheduler-k3s:ensure-charts` command with the `--force` flag is required after changing any chart properties in order to have them apply. This will install all charts, not just the ones that have changed.
|
||||
|
||||
```shell
|
||||
@@ -792,20 +891,23 @@ If unspecified for any task, the default reservation will be `.1` CPU and `128Mi
|
||||
|
||||
### Settable properties
|
||||
|
||||
> [!NOTE]
|
||||
> The `Report flags` column lists the CLI argument names accepted by `scheduler-k3s:report`. The JSON keys emitted by `scheduler-k3s:report --format json` are the same names with the leading `--scheduler-k3s-` stripped (e.g. `deploy-timeout`, `global-deploy-timeout`, `computed-deploy-timeout`). Legacy keys with the `scheduler-k3s-` prefix are also emitted during the 0.38.x deprecation window and will be removed in a future major release.
|
||||
|
||||
| Property | Scope | Default | Report flags | Description |
|
||||
|---|---|---|---|---|
|
||||
| `deploy-timeout` | app + global | `300s` | `--scheduler-k3s-deploy-timeout`, `--scheduler-k3s-global-deploy-timeout`, `--scheduler-k3s-computed-deploy-timeout` | Timeout for a single helm install/upgrade cycle |
|
||||
| `image-pull-secrets` | app + global | none | `--scheduler-k3s-image-pull-secrets`, `--scheduler-k3s-global-image-pull-secrets`, `--scheduler-k3s-computed-image-pull-secrets` | Comma-separated list of Kubernetes secret names used to pull private images |
|
||||
| `ingress-class` | global only | `nginx` | `--scheduler-k3s-global-ingress-class` | IngressClass name used for app ingresses (e.g. `nginx`, `traefik`) |
|
||||
| `kube-context` | global only | none | `--scheduler-k3s-global-kube-context` | Kube context name used by helm and kubectl invocations |
|
||||
| `kubeconfig-path` | global only | `/etc/rancher/k3s/k3s.yaml` | `--scheduler-k3s-global-kubeconfig-path` | Filesystem path to the kubeconfig used to talk to the cluster |
|
||||
| `kustomize-root-path` | app + global | none | `--scheduler-k3s-kustomize-root-path`, `--scheduler-k3s-global-kustomize-root-path`, `--scheduler-k3s-computed-kustomize-root-path` | Path within the app to a kustomize root applied after the helm install |
|
||||
| `letsencrypt-email-prod` | global only | none | `--scheduler-k3s-global-letsencrypt-email-prod` | Contact email for the production cert-manager ClusterIssuer |
|
||||
| `letsencrypt-email-stag` | global only | none | `--scheduler-k3s-global-letsencrypt-email-stag` | Contact email for the staging cert-manager ClusterIssuer |
|
||||
| `ingress-class` | global only | `nginx` | `--scheduler-k3s-global-ingress-class`, `--scheduler-k3s-computed-ingress-class` | IngressClass name used for app ingresses (e.g. `nginx`, `traefik`) |
|
||||
| `kube-context` | global only | none | `--scheduler-k3s-global-kube-context`, `--scheduler-k3s-computed-kube-context` | Kube context name used by helm and kubectl invocations |
|
||||
| `kubeconfig-path` | global only | `/etc/rancher/k3s/k3s.yaml` | `--scheduler-k3s-global-kubeconfig-path`, `--scheduler-k3s-computed-kubeconfig-path` | Filesystem path to the kubeconfig used to talk to the cluster |
|
||||
| `kustomize-root-path` | app + global | `config/kustomize` | `--scheduler-k3s-kustomize-root-path`, `--scheduler-k3s-global-kustomize-root-path`, `--scheduler-k3s-computed-kustomize-root-path` | Path within the app to a kustomize root applied after the helm install |
|
||||
| `letsencrypt-email-prod` | global only | none | `--scheduler-k3s-global-letsencrypt-email-prod`, `--scheduler-k3s-computed-letsencrypt-email-prod` | Contact email for the production cert-manager ClusterIssuer |
|
||||
| `letsencrypt-email-stag` | global only | none | `--scheduler-k3s-global-letsencrypt-email-stag`, `--scheduler-k3s-computed-letsencrypt-email-stag` | Contact email for the staging cert-manager ClusterIssuer |
|
||||
| `letsencrypt-server` | app + global | `prod` | `--scheduler-k3s-letsencrypt-server`, `--scheduler-k3s-global-letsencrypt-server`, `--scheduler-k3s-computed-letsencrypt-server` | ACME directory (`prod` or `staging`) used for app certificates |
|
||||
| `namespace` | app + global | `default` | `--scheduler-k3s-namespace`, `--scheduler-k3s-global-namespace`, `--scheduler-k3s-computed-namespace` | Kubernetes namespace into which the app's resources are installed |
|
||||
| `network-interface` | global only | none | `--scheduler-k3s-global-network-interface` | Host network interface used by k3s |
|
||||
| `network-interface` | global only | `eth0` | `--scheduler-k3s-global-network-interface`, `--scheduler-k3s-computed-network-interface` | Host network interface used by k3s |
|
||||
| `rollback-on-failure` | app + global | `false` | `--scheduler-k3s-rollback-on-failure`, `--scheduler-k3s-global-rollback-on-failure`, `--scheduler-k3s-computed-rollback-on-failure` | When `true`, helm rolls back the release if a deploy fails |
|
||||
| `shm-size` | app + global | none | `--scheduler-k3s-shm-size`, `--scheduler-k3s-global-shm-size`, `--scheduler-k3s-computed-shm-size` | `/dev/shm` size override applied to app containers |
|
||||
| `token` | global only | none | (not in report) | Cluster join token used by `scheduler-k3s:cluster-add` |
|
||||
| `chart.<chart-name>.<property>` | global only | none | `--scheduler-k3s-global-chart.<chart-name>.<property>` (dynamic per chart/property) | Override a value injected into the helm chart named `<chart-name>` (one row per chart/property pair) |
|
||||
| `token` | global only | none | `--scheduler-k3s-global-token` (masked as `*******` in default stdout output; the raw value is returned when queried via `--format json` or when this flag is requested explicitly) | Cluster join token used by `scheduler-k3s:cluster-add` |
|
||||
| `chart.<chart-name>.<property>` | global only | none | `--scheduler-k3s-global-chart.<chart-name>.<property>` (dynamic per chart/property) | Override a value injected into the helm chart named `<chart-name>` (one row per chart/property pair). Manage these via the dedicated `scheduler-k3s:charts:set` / `scheduler-k3s:charts:report` commands; the `scheduler-k3s:set`/`scheduler-k3s:report` form is deprecated. |
|
||||
|
||||
@@ -152,7 +152,10 @@ Deployment tasks are currently executed directly on the primary Dokku server.
|
||||
|
||||
### Settable properties
|
||||
|
||||
> [!NOTE]
|
||||
> The `Report flags` column lists the CLI argument names accepted by `scheduler:report`. The JSON keys emitted by `scheduler:report --format json` are the same names with the leading `--scheduler-` stripped (e.g. `selected`, `global-selected`, `computed-selected`). Legacy keys with the `scheduler-` prefix are also emitted during the 0.38.x deprecation window and will be removed in a future major release.
|
||||
|
||||
| Property | Scope | Default | Report flags | Description |
|
||||
|---|---|---|---|---|
|
||||
| `selected` | app + global | `docker-local` | `--scheduler-selected`, `--scheduler-global-selected`, `--scheduler-computed-selected` | Scheduler plugin used to deploy this app (`docker-local`, `k3s`, etc.) |
|
||||
| `shell` | app + global | none | (not in report) | Shell used by `enter`/`run` commands when entering a container |
|
||||
| `shell` | app + global | none | `--scheduler-shell`, `--scheduler-global-shell`, `--scheduler-computed-shell` | Shell used by `enter`/`run` commands when entering a container |
|
||||
|
||||
@@ -29,7 +29,7 @@ The workflow looks like this:
|
||||
```shell
|
||||
# having dokku-arch in ../dokku-arch
|
||||
vagrant up build-arch
|
||||
# wait for "==> build-arch: ==> Finished making: dokku 0.38.6-2 (Mon Feb 22 23:20:37 CET 2016)"
|
||||
# wait for "==> build-arch: ==> Finished making: dokku 0.38.20-2 (Mon Feb 22 23:20:37 CET 2016)"
|
||||
cd ../dokku-arch
|
||||
git add PKGBUILD .SRCINFO
|
||||
git commit -m 'Update to dokku 0.9.9'
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Pull the dokku/dokku image:
|
||||
|
||||
```shell
|
||||
docker pull dokku/dokku:0.38.6
|
||||
docker pull dokku/dokku:0.38.20
|
||||
```
|
||||
|
||||
Next, run the image.
|
||||
@@ -19,7 +19,7 @@ docker container run -d \
|
||||
--publish 8443:443 \
|
||||
--volume /var/lib/dokku:/mnt/dokku \
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
dokku/dokku:0.38.6
|
||||
dokku/dokku:0.38.20
|
||||
```
|
||||
|
||||
Alternatively, you can use `docker-compose.yml`:
|
||||
@@ -27,7 +27,7 @@ Alternatively, you can use `docker-compose.yml`:
|
||||
```yaml
|
||||
services:
|
||||
dokku:
|
||||
image: dokku/dokku:0.38.6
|
||||
image: dokku/dokku:0.38.20
|
||||
container_name: dokku
|
||||
network_mode: bridge
|
||||
ports:
|
||||
@@ -67,7 +67,7 @@ Compose dependents can gate on the healthcheck via `depends_on` with `condition:
|
||||
```yaml
|
||||
services:
|
||||
dokku:
|
||||
image: dokku/dokku:0.38.6
|
||||
image: dokku/dokku:0.38.20
|
||||
# ...
|
||||
bootstrap:
|
||||
image: alpine:3.20
|
||||
@@ -114,7 +114,7 @@ redis: https://github.com/dokku/dokku-redis.git
|
||||
The alternative is to build a custom docker image via a custom Dockerfile. This Dockerfile can run any `plugin:install` command, though the `install` trigger must be skipped via the `--skip-install-trigger`. The version installed at that time will be the one that persists. Below is an example Dockerfile showing this method.
|
||||
|
||||
```Dockerfile
|
||||
FROM dokku/dokku:0.38.6
|
||||
FROM dokku/dokku:0.38.20
|
||||
RUN dokku plugin:install https://github.com/dokku/dokku-postgres.git --skip-install-trigger
|
||||
RUN dokku plugin:install https://github.com/dokku/dokku-redis.git --skip-install-trigger
|
||||
```
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
# - `DOKKU_IP`
|
||||
# - `FORWARDED_PORT`.
|
||||
cd path/to/dokku
|
||||
|
||||
|
||||
# for most users
|
||||
vagrant up
|
||||
|
||||
|
||||
# windows users must instead use the following in an elevated command prompt
|
||||
vagrant up dokku-windows
|
||||
```
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
```shell
|
||||
# usually your key is already available under the current user's `~/.ssh/authorized_keys` file
|
||||
cat ~/.ssh/authorized_keys | dokku ssh-keys:add admin
|
||||
cat ~/.ssh/authorized_keys | sudo dokku ssh-keys:add admin
|
||||
|
||||
# you can use any domain you already have access to
|
||||
dokku domains:set-global dokku.me
|
||||
|
||||
@@ -31,8 +31,8 @@ To install the latest stable version of Dokku, you can run the following shell c
|
||||
|
||||
```shell
|
||||
# for debian systems, installs Dokku via apt-get
|
||||
wget -NP . https://dokku.com/install/v0.38.6/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.6 bash bootstrap.sh
|
||||
wget -NP . https://dokku.com/install/v0.38.20/bootstrap.sh
|
||||
sudo DOKKU_TAG=v0.38.20 bash bootstrap.sh
|
||||
```
|
||||
|
||||
The installation process takes about 5-10 minutes, depending upon internet connection speed.
|
||||
|
||||
@@ -10,26 +10,26 @@
|
||||
<meta name="author" content="">
|
||||
<title>Dokku - The smallest PaaS implementation you've ever seen</title>
|
||||
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-72x72.png">
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-76x76.png">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-114x114.png">
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-180x180.png">
|
||||
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/android-chrome-192x192.png" sizes="192x192">
|
||||
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/favicon-96x96.png" sizes="96x96">
|
||||
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/manifest.json">
|
||||
<link rel="shortcut icon" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/favicon.ico">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-72x72.png">
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-76x76.png">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-114x114.png">
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-180x180.png">
|
||||
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/android-chrome-192x192.png" sizes="192x192">
|
||||
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/favicon-96x96.png" sizes="96x96">
|
||||
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/manifest.json">
|
||||
<link rel="shortcut icon" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/favicon.ico">
|
||||
<meta name="apple-mobile-web-app-title" content="Dokku">
|
||||
<meta name="application-name" content="Dokku">
|
||||
<meta name="msapplication-TileColor" content="#da532c">
|
||||
<meta name="msapplication-TileImage" content="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/mstile-144x144.png">
|
||||
<meta name="msapplication-config" content="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/browserconfig.xml">
|
||||
<meta name="msapplication-TileImage" content="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/mstile-144x144.png">
|
||||
<meta name="msapplication-config" content="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/browserconfig.xml">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
|
||||
@@ -38,7 +38,7 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700;900&display=swap" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/style.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/style.css" rel="stylesheet">
|
||||
<!-- <link href="./assets/style.css" rel="stylesheet"> -->
|
||||
</head>
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
<main class="container px-4 px-lg-5">
|
||||
<div class="row">
|
||||
<div class="col-lg-7">
|
||||
<a class="badge-link" href="https://github.com/dokku/dokku/releases"><span class="badge py-2 d-flex fit-content"><div class="circle align-self-center me-2"></div>Latest release <span id="releaseVersion" class="ms-1">v0.38.6</span></span></a>
|
||||
<a class="badge-link" href="https://github.com/dokku/dokku/releases"><span class="badge py-2 d-flex fit-content"><div class="circle align-self-center me-2"></div>Latest release <span id="releaseVersion" class="ms-1">v0.38.20</span></span></a>
|
||||
<h1 class="text-white hero-heading mt-5">An open source PAAS alternative to Heroku.</h1>
|
||||
<h4 class="text-white mt-5">Dokku helps you build and manage the lifecycle of applications from building to scaling.</h4>
|
||||
<div class="d-flex mt-5">
|
||||
@@ -125,7 +125,7 @@
|
||||
</p>
|
||||
<p class="line">
|
||||
<span class="prompt">$</span>
|
||||
<span class="command">sudo DOKKU_TAG=v0.38.6 bash bootstrap.sh</span>
|
||||
<span class="command">sudo DOKKU_TAG=v0.38.20 bash bootstrap.sh</span>
|
||||
</p>
|
||||
<br>
|
||||
|
||||
|
||||
@@ -516,6 +516,9 @@ dokku network:report node-js-app --network-bind-all-interfaces
|
||||
|
||||
### Settable properties
|
||||
|
||||
> [!NOTE]
|
||||
> The `Report flags` column lists the CLI argument names accepted by `network:report`. The JSON keys emitted by `network:report --format json` are the same names with the leading `--network-` stripped (e.g. `attach-post-create`, `global-attach-post-create`, `computed-attach-post-create`). Legacy keys with the `network-` prefix (e.g. `network-attach-post-create`) are also emitted during the 0.38.x deprecation window and will be removed in a future major release.
|
||||
|
||||
| Property | Scope | Default | Report flags | Description |
|
||||
|---|---|---|---|---|
|
||||
| `attach-post-create` | app + global | none | `--network-attach-post-create`, `--network-global-attach-post-create`, `--network-computed-attach-post-create` | Networks attached to a container immediately after creation, before the deploy phase |
|
||||
|
||||
@@ -645,7 +645,7 @@ All nginx-vhosts properties are settable at both the app and global scope.
|
||||
| `keepalive-timeout` | app + global | `75s` | `--nginx-keepalive-timeout`, `--nginx-global-keepalive-timeout`, `--nginx-computed-keepalive-timeout` | Time an idle keep-alive connection stays open |
|
||||
| `lingering-timeout` | app + global | `5s` | `--nginx-lingering-timeout`, `--nginx-global-lingering-timeout`, `--nginx-computed-lingering-timeout` | Time nginx waits for more client data when closing a connection |
|
||||
| `nginx-conf-sigil-path` | app + global | `nginx.conf.sigil` | `--nginx-nginx-conf-sigil-path`, `--nginx-global-nginx-conf-sigil-path`, `--nginx-computed-nginx-conf-sigil-path` | Path within the app to a custom `nginx.conf.sigil` template |
|
||||
| `nginx-service-command` | app + global | none | (not in report) | Override command used by `nginx:start`/`nginx:stop`/`nginx:reload` |
|
||||
| `nginx-service-command` | app + global | none | `--nginx-nginx-service-command`, `--nginx-global-nginx-service-command`, `--nginx-computed-nginx-service-command` | Override command used by `nginx:start`/`nginx:stop`/`nginx:reload` |
|
||||
| `proxy-buffer-size` | app + global | _system pagesize_ | `--nginx-proxy-buffer-size`, `--nginx-global-proxy-buffer-size`, `--nginx-computed-proxy-buffer-size` | Buffer size for reading the first part of the upstream response |
|
||||
| `proxy-buffering` | app + global | `on` | `--nginx-proxy-buffering`, `--nginx-global-proxy-buffering`, `--nginx-computed-proxy-buffering` | Whether nginx buffers upstream responses (`on` or `off`) |
|
||||
| `proxy-buffers` | app + global | _`8 {pagesize}`_ | `--nginx-proxy-buffers`, `--nginx-global-proxy-buffers`, `--nginx-computed-proxy-buffers` | Number and size of buffers used for an upstream response |
|
||||
|
||||
@@ -277,48 +277,50 @@ dokku openresty:report node-js-app
|
||||
You can pass flags which will output only the value of the specific information you want. For example:
|
||||
|
||||
```shell
|
||||
dokku openresty:report node-js-app --openresty-letsencrypt-email
|
||||
dokku openresty:report node-js-app --openresty-computed-letsencrypt-email
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
### Settable properties
|
||||
|
||||
Five properties (`image`, `log-level`, `letsencrypt-email`, `letsencrypt-server`, `allowed-letsencrypt-domains-func-base64`) are global only. The rest are app only - they cannot be set with `--global`.
|
||||
Five properties (`image`, `log-level`, `letsencrypt-email`, `letsencrypt-server`, `allowed-letsencrypt-domains-func-base64`) are global only. The rest may be set per-app or globally with `--global`; a global value applies to any app that has no per-app value, otherwise the built-in default is used.
|
||||
|
||||
`openresty:report` currently exposes a single raw flag per property (no `--global-` / `--computed-` split) except for `hsts`, which has all three flag shapes.
|
||||
Global-only properties expose two report flags: `--openresty-global-<property>` returns the raw stored value (empty when the property has never been set), while `--openresty-computed-<property>` returns the effective value (the global value if set, otherwise the built-in default).
|
||||
|
||||
App-or-global properties expose three report flags: `--openresty-<property>` returns the raw per-app value (empty when unset), `--openresty-global-<property>` returns the raw global value (empty when unset), and `--openresty-computed-<property>` returns the effective value, resolving the per-app value first, then the global value, then the built-in default.
|
||||
|
||||
| Property | Scope | Default | Report flags | Description |
|
||||
|---|---|---|---|---|
|
||||
| `access-log-format` | app only | none | `--openresty-access-log-format` | Custom nginx `log_format` directive used for the access log |
|
||||
| `access-log-path` | app only | _`/var/log/nginx/{app}-access.log`_ | `--openresty-access-log-path` | Path inside the openresty container where access logs are written |
|
||||
| `allowed-letsencrypt-domains-func-base64` | global only | _allow-all stub_ | `--openresty-allowed-letsencrypt-domains-func-base64` | Base64-encoded Lua function deciding which domains may request a letsencrypt certificate |
|
||||
| `bind-address-ipv4` | app only | none | `--openresty-bind-address-ipv4` | IPv4 address the openresty server block binds to |
|
||||
| `bind-address-ipv6` | app only | `::` | `--openresty-bind-address-ipv6` | IPv6 address the openresty server block binds to |
|
||||
| `client-body-timeout` | app only | `60s` | `--openresty-client-body-timeout` | Time allowed to read the request body from the client |
|
||||
| `client-header-timeout` | app only | `60s` | `--openresty-client-header-timeout` | Time allowed to read the request header from the client |
|
||||
| `client-max-body-size` | app only | `1m` | `--openresty-client-max-body-size` | Maximum allowed request body size |
|
||||
| `error-log-path` | app only | _`/var/log/nginx/{app}-error.log`_ | `--openresty-error-log-path` | Path inside the openresty container where error logs are written |
|
||||
| `hsts` | app only | `true` | `--openresty-hsts`, `--openresty-global-hsts`, `--openresty-computed-hsts` | When `true`, emits a `Strict-Transport-Security` header on HTTPS responses |
|
||||
| `hsts-include-subdomains` | app only | `true` | `--openresty-hsts-include-subdomains` | Adds the `includeSubDomains` directive to the HSTS header |
|
||||
| `hsts-max-age` | app only | `15724800` | `--openresty-hsts-max-age` | `max-age` value (seconds) in the HSTS header |
|
||||
| `hsts-preload` | app only | `false` | `--openresty-hsts-preload` | Adds the `preload` directive to the HSTS header |
|
||||
| `image` | global only | _parsed from `plugins/openresty-vhosts/Dockerfile`_ | `--openresty-image` | Docker image used to run the openresty container |
|
||||
| `keepalive-timeout` | app only | `75s` | `--openresty-keepalive-timeout` | Time an idle keep-alive connection stays open |
|
||||
| `letsencrypt-email` | global only | none | `--openresty-letsencrypt-email` | Contact email enabling letsencrypt; empty disables https issuance |
|
||||
| `letsencrypt-server` | global only | `https://acme-v02.api.letsencrypt.org/directory` | `--openresty-letsencrypt-server` | ACME directory used when requesting certificates |
|
||||
| `lingering-timeout` | app only | `5s` | `--openresty-lingering-timeout` | Time openresty waits for more client data when closing a connection |
|
||||
| `log-level` | global only | `ERROR` | (not in report) | Openresty log level |
|
||||
| `proxy-buffer-size` | app only | _system pagesize_ | `--openresty-proxy-buffer-size` | Buffer size for reading the first part of the upstream response |
|
||||
| `proxy-buffering` | app only | `on` | `--openresty-proxy-buffering` | Whether openresty buffers upstream responses (`on` or `off`) |
|
||||
| `proxy-buffers` | app only | _`8 {pagesize}`_ | `--openresty-proxy-buffers` | Number and size of buffers used for an upstream response |
|
||||
| `proxy-busy-buffers-size` | app only | _`2 * pagesize`_ | `--openresty-proxy-busy-buffers-size` | Maximum buffer size that can be busy sending a response to the client |
|
||||
| `proxy-connect-timeout` | app only | `60s` | `--openresty-proxy-connect-timeout` | Time to establish a connection to the upstream |
|
||||
| `proxy-read-timeout` | app only | `60s` | `--openresty-proxy-read-timeout` | Time to read a response from the upstream |
|
||||
| `proxy-send-timeout` | app only | `60s` | `--openresty-proxy-send-timeout` | Time to transmit a request to the upstream |
|
||||
| `send-timeout` | app only | `60s` | `--openresty-send-timeout` | Time between two successive write operations to the client |
|
||||
| `underscore-in-headers` | app only | `off` | `--openresty-underscore-in-headers` | Whether to allow underscores in client request header field names |
|
||||
| `x-forwarded-for-value` | app only | `$remote_addr` | `--openresty-x-forwarded-for-value` | Value used for the `X-Forwarded-For` header |
|
||||
| `x-forwarded-port-value` | app only | `$server_port` | `--openresty-x-forwarded-port-value` | Value used for the `X-Forwarded-Port` header |
|
||||
| `x-forwarded-proto-value` | app only | `$scheme` | `--openresty-x-forwarded-proto-value` | Value used for the `X-Forwarded-Proto` header |
|
||||
| `x-forwarded-ssl` | app only | none | `--openresty-x-forwarded-ssl` | Value used for the `X-Forwarded-Ssl` header (e.g. `on`/`off`) |
|
||||
| `access-log-format` | app or global | none | `--openresty-access-log-format`, `--openresty-global-access-log-format`, `--openresty-computed-access-log-format` | Custom nginx `log_format` directive used for the access log |
|
||||
| `access-log-path` | app or global | _`/var/log/nginx/{app}-access.log`_ | `--openresty-access-log-path`, `--openresty-global-access-log-path`, `--openresty-computed-access-log-path` | Path inside the openresty container where access logs are written |
|
||||
| `allowed-letsencrypt-domains-func-base64` | global only | _allow-all stub_ | `--openresty-global-allowed-letsencrypt-domains-func-base64`, `--openresty-computed-allowed-letsencrypt-domains-func-base64` | Base64-encoded Lua function deciding which domains may request a letsencrypt certificate |
|
||||
| `bind-address-ipv4` | app or global | none | `--openresty-bind-address-ipv4`, `--openresty-global-bind-address-ipv4`, `--openresty-computed-bind-address-ipv4` | IPv4 address the openresty server block binds to |
|
||||
| `bind-address-ipv6` | app or global | `::` | `--openresty-bind-address-ipv6`, `--openresty-global-bind-address-ipv6`, `--openresty-computed-bind-address-ipv6` | IPv6 address the openresty server block binds to |
|
||||
| `client-body-timeout` | app or global | `60s` | `--openresty-client-body-timeout`, `--openresty-global-client-body-timeout`, `--openresty-computed-client-body-timeout` | Time allowed to read the request body from the client |
|
||||
| `client-header-timeout` | app or global | `60s` | `--openresty-client-header-timeout`, `--openresty-global-client-header-timeout`, `--openresty-computed-client-header-timeout` | Time allowed to read the request header from the client |
|
||||
| `client-max-body-size` | app or global | `1m` | `--openresty-client-max-body-size`, `--openresty-global-client-max-body-size`, `--openresty-computed-client-max-body-size` | Maximum allowed request body size |
|
||||
| `error-log-path` | app or global | _`/var/log/nginx/{app}-error.log`_ | `--openresty-error-log-path`, `--openresty-global-error-log-path`, `--openresty-computed-error-log-path` | Path inside the openresty container where error logs are written |
|
||||
| `hsts` | app or global | `true` | `--openresty-hsts`, `--openresty-global-hsts`, `--openresty-computed-hsts` | When `true`, emits a `Strict-Transport-Security` header on HTTPS responses |
|
||||
| `hsts-include-subdomains` | app or global | `true` | `--openresty-hsts-include-subdomains`, `--openresty-global-hsts-include-subdomains`, `--openresty-computed-hsts-include-subdomains` | Adds the `includeSubDomains` directive to the HSTS header |
|
||||
| `hsts-max-age` | app or global | `15724800` | `--openresty-hsts-max-age`, `--openresty-global-hsts-max-age`, `--openresty-computed-hsts-max-age` | `max-age` value (seconds) in the HSTS header |
|
||||
| `hsts-preload` | app or global | `false` | `--openresty-hsts-preload`, `--openresty-global-hsts-preload`, `--openresty-computed-hsts-preload` | Adds the `preload` directive to the HSTS header |
|
||||
| `image` | global only | _parsed from `plugins/openresty-vhosts/Dockerfile`_ | `--openresty-global-image`, `--openresty-computed-image` | Docker image used to run the openresty container |
|
||||
| `keepalive-timeout` | app or global | `75s` | `--openresty-keepalive-timeout`, `--openresty-global-keepalive-timeout`, `--openresty-computed-keepalive-timeout` | Time an idle keep-alive connection stays open |
|
||||
| `letsencrypt-email` | global only | none | `--openresty-global-letsencrypt-email`, `--openresty-computed-letsencrypt-email` | Contact email enabling letsencrypt; empty disables https issuance |
|
||||
| `letsencrypt-server` | global only | `https://acme-v02.api.letsencrypt.org/directory` | `--openresty-global-letsencrypt-server`, `--openresty-computed-letsencrypt-server` | ACME directory used when requesting certificates |
|
||||
| `lingering-timeout` | app or global | `5s` | `--openresty-lingering-timeout`, `--openresty-global-lingering-timeout`, `--openresty-computed-lingering-timeout` | Time openresty waits for more client data when closing a connection |
|
||||
| `log-level` | global only | `ERROR` | `--openresty-global-log-level`, `--openresty-computed-log-level` | Openresty log level |
|
||||
| `proxy-buffer-size` | app or global | _system pagesize_ | `--openresty-proxy-buffer-size`, `--openresty-global-proxy-buffer-size`, `--openresty-computed-proxy-buffer-size` | Buffer size for reading the first part of the upstream response |
|
||||
| `proxy-buffering` | app or global | `on` | `--openresty-proxy-buffering`, `--openresty-global-proxy-buffering`, `--openresty-computed-proxy-buffering` | Whether openresty buffers upstream responses (`on` or `off`) |
|
||||
| `proxy-buffers` | app or global | _`8 {pagesize}`_ | `--openresty-proxy-buffers`, `--openresty-global-proxy-buffers`, `--openresty-computed-proxy-buffers` | Number and size of buffers used for an upstream response |
|
||||
| `proxy-busy-buffers-size` | app or global | _`2 * pagesize`_ | `--openresty-proxy-busy-buffers-size`, `--openresty-global-proxy-busy-buffers-size`, `--openresty-computed-proxy-busy-buffers-size` | Maximum buffer size that can be busy sending a response to the client |
|
||||
| `proxy-connect-timeout` | app or global | `60s` | `--openresty-proxy-connect-timeout`, `--openresty-global-proxy-connect-timeout`, `--openresty-computed-proxy-connect-timeout` | Time to establish a connection to the upstream |
|
||||
| `proxy-read-timeout` | app or global | `60s` | `--openresty-proxy-read-timeout`, `--openresty-global-proxy-read-timeout`, `--openresty-computed-proxy-read-timeout` | Time to read a response from the upstream |
|
||||
| `proxy-send-timeout` | app or global | `60s` | `--openresty-proxy-send-timeout`, `--openresty-global-proxy-send-timeout`, `--openresty-computed-proxy-send-timeout` | Time to transmit a request to the upstream |
|
||||
| `send-timeout` | app or global | `60s` | `--openresty-send-timeout`, `--openresty-global-send-timeout`, `--openresty-computed-send-timeout` | Time between two successive write operations to the client |
|
||||
| `underscore-in-headers` | app or global | `off` | `--openresty-underscore-in-headers`, `--openresty-global-underscore-in-headers`, `--openresty-computed-underscore-in-headers` | Whether to allow underscores in client request header field names |
|
||||
| `x-forwarded-for-value` | app or global | `$remote_addr` | `--openresty-x-forwarded-for-value`, `--openresty-global-x-forwarded-for-value`, `--openresty-computed-x-forwarded-for-value` | Value used for the `X-Forwarded-For` header |
|
||||
| `x-forwarded-port-value` | app or global | `$server_port` | `--openresty-x-forwarded-port-value`, `--openresty-global-x-forwarded-port-value`, `--openresty-computed-x-forwarded-port-value` | Value used for the `X-Forwarded-Port` header |
|
||||
| `x-forwarded-proto-value` | app or global | `$scheme` | `--openresty-x-forwarded-proto-value`, `--openresty-global-x-forwarded-proto-value`, `--openresty-computed-x-forwarded-proto-value` | Value used for the `X-Forwarded-Proto` header |
|
||||
| `x-forwarded-ssl` | app or global | none | `--openresty-x-forwarded-ssl`, `--openresty-global-x-forwarded-ssl`, `--openresty-computed-x-forwarded-ssl` | Value used for the `X-Forwarded-Ssl` header (e.g. `on`/`off`) |
|
||||
|
||||
@@ -408,7 +408,7 @@ All traefik properties are global only. Set with `traefik:set --global <property
|
||||
| `challenge-mode` | global only | `tls` | `--traefik-global-challenge-mode`, `--traefik-computed-challenge-mode` | ACME challenge method used by Traefik (`tls`, `http`, or `dns`) |
|
||||
| `dashboard-enabled` | global only | `false` | `--traefik-global-dashboard-enabled`, `--traefik-computed-dashboard-enabled` | When `true`, enables the Traefik dashboard |
|
||||
| `dns-provider` | global only | none | `--traefik-global-dns-provider`, `--traefik-computed-dns-provider` | Lego DNS provider name used when `challenge-mode` is `dns` |
|
||||
| `dns-provider-<ENV_VAR>` | global only | none | (no report flag) | Per-provider environment variables passed to the Traefik container; `<ENV_VAR>` is the upstream variable name (e.g. `dns-provider-cloudflare-api-token`) |
|
||||
| `dns-provider-<ENV_VAR>` | global only | none | `--traefik-dns-provider-<env_var>` (masked as `*******` in the default stdout report; the raw value is returned when queried via `--format json` or when this flag is requested explicitly) | Per-provider environment variables passed to the Traefik container; `<ENV_VAR>` is the upstream variable name (e.g. `dns-provider-cloudflare-api-token`) |
|
||||
| `http-entry-point` | global only | `http` | `--traefik-global-http-entry-point`, `--traefik-computed-http-entry-point` | Entry point name handling plaintext HTTP traffic |
|
||||
| `https-entry-point` | global only | `https` | `--traefik-global-https-entry-point`, `--traefik-computed-https-entry-point` | Entry point name handling TLS-terminated HTTPS traffic |
|
||||
| `image` | global only | _parsed from `plugins/traefik-vhosts/Dockerfile`_ | `--traefik-global-image`, `--traefik-computed-image` | Docker image used to run the Traefik container |
|
||||
|
||||
@@ -241,11 +241,14 @@ Finally, proxy implementations _may_ install extra software needed for the proxy
|
||||
|
||||
### Settable properties
|
||||
|
||||
> [!NOTE]
|
||||
> The `Report flags` column lists the CLI argument names accepted by `proxy:report`. The JSON keys emitted by `proxy:report --format json` are the same names with the leading `--proxy-` stripped (e.g. `type`, `global-type`, `computed-type`). Legacy keys with the `proxy-` prefix (e.g. `proxy-type`) are also emitted during the 0.38.x deprecation window and will be removed in a future major release.
|
||||
|
||||
| Property | Scope | Default | Report flags | Description |
|
||||
|---|---|---|---|---|
|
||||
| `disabled` | app only | `false` | (exposed inverted as `--proxy-enabled`) | When `true`, disables proxy integration for this app (`proxy:enable`/`proxy:disable` write this) |
|
||||
| `proxy-port` | app + global | none | (not in report) | Override port used for the HTTP listener in the generated proxy config |
|
||||
| `proxy-ssl-port` | app + global | none | (not in report) | Override port used for the HTTPS listener in the generated proxy config |
|
||||
| `disabled` | app only | `false` | `--proxy-disabled`, `--proxy-computed-disabled` (also exposed inverted as `--proxy-enabled`) | When `true`, disables proxy integration for this app (`proxy:enable`/`proxy:disable` write this) |
|
||||
| `proxy-port` | app + global | none | `--proxy-proxy-port`, `--proxy-global-proxy-port`, `--proxy-computed-proxy-port` | Override port used for the HTTP listener in the generated proxy config |
|
||||
| `proxy-ssl-port` | app + global | none | `--proxy-proxy-ssl-port`, `--proxy-global-proxy-ssl-port`, `--proxy-computed-proxy-ssl-port` | Override port used for the HTTPS listener in the generated proxy config |
|
||||
| `type` | app + global | `nginx` | `--proxy-type`, `--proxy-global-type`, `--proxy-computed-type` | Proxy implementation handling traffic for the app (`nginx`, `caddy`, `haproxy`, `traefik`, `openresty`, or a custom plugin) |
|
||||
|
||||
### Read-only flags
|
||||
|
||||
@@ -367,6 +367,20 @@ dokku ps:set node-js-app restart-policy on-failure
|
||||
dokku ps:set node-js-app restart-policy on-failure:20
|
||||
```
|
||||
|
||||
The default policy (`on-failure:10`) may be restored by passing an empty value:
|
||||
|
||||
```shell
|
||||
dokku ps:set node-js-app restart-policy
|
||||
```
|
||||
|
||||
A global default may also be set, and is used by any app that does not have an app-specific restart policy:
|
||||
|
||||
```shell
|
||||
dokku ps:set --global restart-policy always
|
||||
```
|
||||
|
||||
The effective policy applied to an app's containers is resolved as the app-specific value, then the global value, then the built-in `on-failure:10` default. This computed value can be inspected via the `--ps-computed-restart-policy` report flag.
|
||||
|
||||
Restart policies have no bearing on server reboot, and Dokku will always attempt to restart your apps at that point unless they were manually stopped.
|
||||
|
||||
Dokku also runs `dokku-event-listener` in the background via the system's init service. This monitors container state, performing the following actions:
|
||||
@@ -391,8 +405,10 @@ dokku ps:report
|
||||
Processes: 0
|
||||
Ps can scale: true
|
||||
Ps computed procfile path: Procfile2
|
||||
Ps computed restart policy: on-failure:10
|
||||
Ps global procfile path: Procfile
|
||||
Ps restart policy: on-failure:10
|
||||
Ps global restart policy:
|
||||
Ps restart policy:
|
||||
Ps procfile path: Procfile2
|
||||
Restore: true
|
||||
Running: false
|
||||
@@ -401,8 +417,10 @@ dokku ps:report
|
||||
Processes: 0
|
||||
Ps can scale: true
|
||||
Ps computed procfile path: Procfile
|
||||
Ps computed restart policy: on-failure:10
|
||||
Ps global procfile path: Procfile
|
||||
Ps restart policy: on-failure:10
|
||||
Ps global restart policy:
|
||||
Ps restart policy:
|
||||
Ps procfile path:
|
||||
Restore: true
|
||||
Running: false
|
||||
@@ -411,8 +429,10 @@ dokku ps:report
|
||||
Processes: 0
|
||||
Ps can scale: true
|
||||
Ps computed procfile path: Procfile
|
||||
Ps computed restart policy: on-failure:10
|
||||
Ps global procfile path: Procfile
|
||||
Ps restart policy: on-failure:10
|
||||
Ps global restart policy:
|
||||
Ps restart policy:
|
||||
Ps procfile path:
|
||||
Restore: true
|
||||
Running: false
|
||||
@@ -429,7 +449,9 @@ dokku ps:report node-js-app
|
||||
Deployed: false
|
||||
Processes: 0
|
||||
Ps can scale: true
|
||||
Ps restart policy: on-failure:10
|
||||
Ps computed restart policy: on-failure:10
|
||||
Ps global restart policy:
|
||||
Ps restart policy:
|
||||
Restore: true
|
||||
Running: false
|
||||
```
|
||||
@@ -468,15 +490,18 @@ dokku ps:set node-js-app restore
|
||||
|
||||
### Settable properties
|
||||
|
||||
> [!NOTE]
|
||||
> The `Report flags` column lists the CLI argument names accepted by `ps:report`. The JSON keys emitted by `ps:report --format json` are the same names with the leading `--ps-` stripped (e.g. `procfile-path`, `global-procfile-path`, `computed-procfile-path`). Legacy keys with the `ps-` prefix (e.g. `ps-procfile-path`) are also emitted during the 0.38.x deprecation window and will be removed in a future major release.
|
||||
|
||||
| Property | Scope | Default | Report flags | Description |
|
||||
|---|---|---|---|---|
|
||||
| `dockerfile-start-cmd` | app only | none | (not in report) | Override `CMD` for Dockerfile-based apps |
|
||||
| `dockerfile-start-cmd` | app only | none | `--ps-dockerfile-start-cmd`, `--ps-computed-dockerfile-start-cmd` | Override `CMD` for Dockerfile-based apps |
|
||||
| `procfile-path` | app + global | `Procfile` | `--ps-procfile-path`, `--ps-global-procfile-path`, `--ps-computed-procfile-path` | Path to the app's Procfile, relative to the build root |
|
||||
| `restart-policy` | app only | `on-failure:10` | `--ps-restart-policy` | Docker restart policy applied to deployed containers (`no`, `always`, `unless-stopped`, `on-failure[:max-retries]`) |
|
||||
| `restart-policy` | app + global | `on-failure:10` | `--ps-restart-policy`, `--ps-global-restart-policy`, `--ps-computed-restart-policy` | Docker restart policy applied to deployed containers (`no`, `always`, `unless-stopped`, `on-failure[:max-retries]`) |
|
||||
| `restore` | app only | `true` | `--restore` | When `true`, the app is restarted automatically by `ps:retire` after a host reboot |
|
||||
| `skip-deploy` | app + global | `false` | (not in report) | When `true`, skips the deploy phase after a successful build |
|
||||
| `start-cmd` | app only | none | (not in report) | Override start command for buildpack apps |
|
||||
| `stop-timeout-seconds` | app + global | `30` | `--stop-timeout-seconds`, `--global-stop-timeout-seconds`, `--computed-stop-timeout-seconds` | Seconds Docker waits before SIGKILLing a container on stop |
|
||||
| `skip-deploy` | app + global | `false` | `--ps-skip-deploy`, `--ps-global-skip-deploy`, `--ps-computed-skip-deploy` | When `true`, skips the deploy phase after a successful build |
|
||||
| `start-cmd` | app only | none | `--ps-start-cmd`, `--ps-computed-start-cmd` | Override start command for buildpack apps |
|
||||
| `stop-timeout-seconds` | app + global | `30` | `--ps-stop-timeout-seconds`, `--ps-global-stop-timeout-seconds`, `--ps-computed-stop-timeout-seconds` | Seconds Docker waits before SIGKILLing a container on stop |
|
||||
|
||||
### Read-only flags
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ When running scheduled cron tasks, there are a few items to be aware of:
|
||||
- Each scheduled task is executed within a one-off `run` container, and thus inherit any docker-options specified for `run` containers. Resources are never shared between scheduled tasks.
|
||||
- Scheduled cron tasks are supported on a per-scheduler basis, and are currently only implemented by the `docker-local` scheduler.
|
||||
- Tasks for _all_ apps managed by the `docker-local` scheduler are written to a single crontab file owned by the `dokku` user. The `dokku` user's crontab should be considered reserved for this purpose.
|
||||
- The `command` is tokenized and exec'd directly inside the container. Shell features such as `;`, `&&`, `|`, and `>` are _not_ interpreted. Commands that contain a bare shell operator are rejected when `app.json` is validated at deploy time, so a malformed cron command will fail the deploy rather than silently fail to run. If shell semantics are required, wrap the command explicitly, for example `"sh -c 'do-thing > /var/log/x.log'"`.
|
||||
|
||||
|
||||
### Changing cron management settings
|
||||
@@ -300,9 +301,12 @@ SHELL=/bin/bash
|
||||
|
||||
### Settable properties
|
||||
|
||||
> [!NOTE]
|
||||
> The `Report flags` column lists the CLI argument names accepted by `cron:report`. The JSON keys emitted by `cron:report --format json` are the same names with the leading `--cron-` stripped (e.g. `global-mailto`, `computed-mailto`, `maintenance`). Legacy keys with the `cron-` prefix (e.g. `cron-global-mailto`) are also emitted during the 0.38.x deprecation window and will be removed in a future major release.
|
||||
|
||||
| Property | Scope | Default | Report flags | Description |
|
||||
|---|---|---|---|---|
|
||||
| `mailfrom` | app + global | none | `--cron-mailfrom` | `From:` address used in cron failure emails |
|
||||
| `mailto` | app + global | none | `--cron-mailto` | Recipient address for cron failure emails; empty disables email |
|
||||
| `mailfrom` | global only | none | `--cron-global-mailfrom`, `--cron-computed-mailfrom` | `From:` address used in cron failure emails |
|
||||
| `mailto` | global only | none | `--cron-global-mailto`, `--cron-computed-mailto` | Recipient address for cron failure emails; empty disables email |
|
||||
| `maintenance` | app + global | `false` | `--cron-maintenance`, `--cron-global-maintenance`, `--cron-computed-maintenance` | When `true`, suspends all cron tasks for the app (or globally) |
|
||||
| `maintenance.<cron-id>` | app only | `false` | `--cron-maintenance-<cron-id>` (dynamic per task) | Suspends an individual cron task by its computed ID (one row per task); written by `cron:suspend`/`cron:resume` |
|
||||
|
||||
@@ -9,26 +9,26 @@
|
||||
<meta name="author" content="">
|
||||
<title>Dokku - {{DOC_TITLE}}</title>
|
||||
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-72x72.png">
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-76x76.png">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-114x114.png">
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/apple-touch-icon-180x180.png">
|
||||
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/android-chrome-192x192.png" sizes="192x192">
|
||||
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/favicon-96x96.png" sizes="96x96">
|
||||
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/manifest.json">
|
||||
<link rel="shortcut icon" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/favicon.ico">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-72x72.png">
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-76x76.png">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-114x114.png">
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/apple-touch-icon-180x180.png">
|
||||
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/android-chrome-192x192.png" sizes="192x192">
|
||||
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/favicon-96x96.png" sizes="96x96">
|
||||
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/manifest.json">
|
||||
<link rel="shortcut icon" href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/favicon.ico">
|
||||
<meta name="apple-mobile-web-app-title" content="Dokku">
|
||||
<meta name="application-name" content="Dokku">
|
||||
<meta name="msapplication-TileColor" content="#da532c">
|
||||
<meta name="msapplication-TileImage" content="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/mstile-144x144.png">
|
||||
<meta name="msapplication-config" content="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/favicons/browserconfig.xml">
|
||||
<meta name="msapplication-TileImage" content="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/mstile-144x144.png">
|
||||
<meta name="msapplication-config" content="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/favicons/browserconfig.xml">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<script>
|
||||
@@ -48,7 +48,7 @@
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700;900&display=swap" rel="stylesheet">
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.6/docs/assets/style.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/gh/dokku/dokku@v0.38.20/docs/assets/style.css" rel="stylesheet">
|
||||
<!-- <link href="./assets/style.css" rel="stylesheet"> -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core standard plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -28,7 +28,7 @@ cmd-report() {
|
||||
dokku_log_info1 "herokuish version: "
|
||||
if [[ "$ARCHITECTURE" == "arm64" ]]; then
|
||||
dokku_log_warn "herokuish not supported on $ARCHITECTURE architecture"
|
||||
elif verify_image "$DOKKU_IMAGE"; then
|
||||
elif fn-verify-app-image "" "$DOKKU_IMAGE"; then
|
||||
"$DOCKER_BIN" container run $DOKKU_GLOBAL_RUN_ARGS --rm "$DOKKU_IMAGE" herokuish version | sed "s/^/ /"
|
||||
else
|
||||
dokku_log_warn "Herokuish image $DOKKU_IMAGE is not available"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core events logging plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -18,15 +18,15 @@ require (
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/melbahja/goph v1.5.0 // indirect
|
||||
github.com/melbahja/goph v1.5.1 // indirect
|
||||
github.com/otiai10/copy v1.14.1 // indirect
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/sftp v1.13.10 // indirect
|
||||
github.com/ryanuber/columnize v2.1.2+incompatible // indirect
|
||||
golang.org/x/crypto v0.52.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
golang.org/x/crypto v0.53.0 // indirect
|
||||
golang.org/x/sync v0.21.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/dokku/dokku/plugins/common => ../common
|
||||
|
||||
@@ -18,10 +18,10 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
|
||||
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||
github.com/melbahja/goph v1.5.0 h1:RQUBpLvfg3i7fjfG8rTcSWyMjVRfdhwrrfQhjYee4dQ=
|
||||
github.com/melbahja/goph v1.5.0/go.mod h1:dDwo+44cmvfDLdiVpc6fJxexf5BA5yEDUeE5YgtuDO4=
|
||||
github.com/onsi/gomega v1.41.0 h1:OwKp4pXNgVxf6sCplzYo794OFNuoL2q2SBMU5NSWOjA=
|
||||
github.com/onsi/gomega v1.41.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A=
|
||||
github.com/melbahja/goph v1.5.1 h1:gdZyjn5nM6FO6mAZrv7XqE2gArQbb+V6FdIyH3Ru3vA=
|
||||
github.com/melbahja/goph v1.5.1/go.mod h1:dDwo+44cmvfDLdiVpc6fJxexf5BA5yEDUeE5YgtuDO4=
|
||||
github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I=
|
||||
github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg=
|
||||
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
||||
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
|
||||
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
||||
@@ -42,18 +42,18 @@ github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a h1:a6TNDN9CgG+cYj
|
||||
github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a/go.mod h1:EbW0wDK/qEUYI0A5bqq0C2kF8JTQwWONmGDBbzsxxHo=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ=
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core app-json plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -31,10 +31,18 @@ func ReportSingleApp(appName string, format string, infoFlag string) error {
|
||||
flagKeys = append(flagKeys, flagKey)
|
||||
}
|
||||
|
||||
trimPrefix := false
|
||||
uppercaseFirstCharacter := true
|
||||
infoFlags := common.CollectReport(appName, infoFlag, flags)
|
||||
return common.ReportSingleApp("app-json", appName, infoFlag, infoFlags, flagKeys, format, trimPrefix, uppercaseFirstCharacter)
|
||||
return common.ReportSingleApp(common.ReportSingleAppInput{
|
||||
ReportType: "app-json",
|
||||
AppName: appName,
|
||||
InfoFlag: infoFlag,
|
||||
InfoFlags: infoFlags,
|
||||
InfoFlagKeys: flagKeys,
|
||||
Format: format,
|
||||
TrimPrefix: true,
|
||||
UppercaseFirstCharacter: true,
|
||||
EmitLegacyPrefix: true,
|
||||
})
|
||||
}
|
||||
|
||||
func reportComputedAppjsonpath(appName string) string {
|
||||
|
||||
@@ -10,8 +10,6 @@ var (
|
||||
|
||||
// GlobalProperties is a map of all valid global apps properties
|
||||
GlobalProperties = map[string]bool{
|
||||
"deploy-source": true,
|
||||
"deploy-source-metadata": true,
|
||||
"disable-autocreation": true,
|
||||
"disable-autocreation": true,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -15,15 +15,15 @@ require (
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/melbahja/goph v1.5.0 // indirect
|
||||
github.com/melbahja/goph v1.5.1 // indirect
|
||||
github.com/otiai10/copy v1.14.1 // indirect
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/sftp v1.13.10 // indirect
|
||||
github.com/ryanuber/columnize v2.1.2+incompatible // indirect
|
||||
golang.org/x/crypto v0.52.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
golang.org/x/crypto v0.53.0 // indirect
|
||||
golang.org/x/sync v0.21.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/dokku/dokku/plugins/common => ../common
|
||||
|
||||
@@ -16,10 +16,10 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/melbahja/goph v1.5.0 h1:RQUBpLvfg3i7fjfG8rTcSWyMjVRfdhwrrfQhjYee4dQ=
|
||||
github.com/melbahja/goph v1.5.0/go.mod h1:dDwo+44cmvfDLdiVpc6fJxexf5BA5yEDUeE5YgtuDO4=
|
||||
github.com/onsi/gomega v1.41.0 h1:OwKp4pXNgVxf6sCplzYo794OFNuoL2q2SBMU5NSWOjA=
|
||||
github.com/onsi/gomega v1.41.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A=
|
||||
github.com/melbahja/goph v1.5.1 h1:gdZyjn5nM6FO6mAZrv7XqE2gArQbb+V6FdIyH3Ru3vA=
|
||||
github.com/melbahja/goph v1.5.1/go.mod h1:dDwo+44cmvfDLdiVpc6fJxexf5BA5yEDUeE5YgtuDO4=
|
||||
github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I=
|
||||
github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg=
|
||||
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
||||
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
|
||||
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
||||
@@ -38,18 +38,18 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core apps plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -17,14 +17,17 @@ func ReportSingleApp(appName string, format string, infoFlag string) error {
|
||||
|
||||
var flags map[string]common.ReportFunc
|
||||
if appName == "--global" {
|
||||
flags = map[string]common.ReportFunc{}
|
||||
flags = map[string]common.ReportFunc{
|
||||
"--app-global-disable-autocreation": reportGlobalDisableAutocreation,
|
||||
}
|
||||
} else {
|
||||
flags = map[string]common.ReportFunc{
|
||||
"--app-created-at": reportCreatedAt,
|
||||
"--app-deploy-source": reportDeploySource,
|
||||
"--app-deploy-source-metadata": reportDeploySourceMetadata,
|
||||
"--app-dir": reportDir,
|
||||
"--app-locked": reportLocked,
|
||||
"--app-created-at": reportCreatedAt,
|
||||
"--app-deploy-source": reportDeploySource,
|
||||
"--app-deploy-source-metadata": reportDeploySourceMetadata,
|
||||
"--app-dir": reportDir,
|
||||
"--app-global-disable-autocreation": reportGlobalDisableAutocreation,
|
||||
"--app-locked": reportLocked,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,10 +36,18 @@ func ReportSingleApp(appName string, format string, infoFlag string) error {
|
||||
flagKeys = append(flagKeys, flagKey)
|
||||
}
|
||||
|
||||
trimPrefix := false
|
||||
uppercaseFirstCharacter := true
|
||||
infoFlags := common.CollectReport(appName, infoFlag, flags)
|
||||
return common.ReportSingleApp("app", appName, infoFlag, infoFlags, flagKeys, format, trimPrefix, uppercaseFirstCharacter)
|
||||
return common.ReportSingleApp(common.ReportSingleAppInput{
|
||||
ReportType: "app",
|
||||
AppName: appName,
|
||||
InfoFlag: infoFlag,
|
||||
InfoFlags: infoFlags,
|
||||
InfoFlagKeys: flagKeys,
|
||||
Format: format,
|
||||
TrimPrefix: true,
|
||||
UppercaseFirstCharacter: true,
|
||||
EmitLegacyPrefix: true,
|
||||
})
|
||||
}
|
||||
|
||||
func reportCreatedAt(appName string) string {
|
||||
@@ -59,6 +70,10 @@ func reportDir(appName string) string {
|
||||
return common.AppRoot(appName)
|
||||
}
|
||||
|
||||
func reportGlobalDisableAutocreation(appName string) string {
|
||||
return common.PropertyGet("apps", "--global", "disable-autocreation")
|
||||
}
|
||||
|
||||
func reportLocked(appName string) string {
|
||||
locked := "false"
|
||||
if appIsLocked(appName) {
|
||||
|
||||
@@ -228,6 +228,9 @@ func CommandReport(appName string, format string, infoFlag string) error {
|
||||
|
||||
// CommandSet set or clear an apps property for an app
|
||||
func CommandSet(appName string, property string, value string) error {
|
||||
if appName != "--global" && property == "disable-autocreation" {
|
||||
common.LogFail("Property can only be specified globally")
|
||||
}
|
||||
common.CommandPropertySet("apps", appName, property, value, DefaultProperties, GlobalProperties)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ trigger-builder-dockerfile-builder-build() {
|
||||
plugn trigger pre-build "$BUILDER_TYPE" "$APP" "$SOURCECODE_WORK_DIR"
|
||||
|
||||
local DOCKER_ARGS=$(: | plugn trigger docker-args-build "$APP" "$BUILDER_TYPE")
|
||||
DOCKER_ARGS+=$(: | plugn trigger docker-args-process-build "$APP" "$BUILDER_TYPE")
|
||||
DOCKER_ARGS+=" $(: | plugn trigger docker-args-process-build "$APP" "$BUILDER_TYPE")"
|
||||
DOCKER_ARGS+=" $DOKKU_GLOBAL_BUILD_ARGS"
|
||||
|
||||
DOCKER_ARGS=" $DOCKER_ARGS "
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core builder-dockerfile plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -69,7 +69,7 @@ trigger-builder-herokuish-builder-build() {
|
||||
DOCKER_ARGS+=" --env=TRACE=true "
|
||||
DOCKER_ARGS+=" --env=BUILDPACK_XTRACE=1 "
|
||||
fi
|
||||
DOCKER_ARGS+=$(: | plugn trigger docker-args-process-build "$APP" "$BUILDER_TYPE")
|
||||
DOCKER_ARGS+=" $(: | plugn trigger docker-args-process-build "$APP" "$BUILDER_TYPE")"
|
||||
|
||||
declare -a ARG_ARRAY
|
||||
eval "ARG_ARRAY=($DOCKER_ARGS)"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core builder-herokuish plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core builder-lambda plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -34,7 +34,7 @@ trigger-builder-nixpacks-builder-build() {
|
||||
fi
|
||||
|
||||
local DOCKER_ARGS=$(: | plugn trigger docker-args-build "$APP" "$BUILDER_TYPE")
|
||||
DOCKER_ARGS+=$(: | plugn trigger docker-args-process-build "$APP" "$BUILDER_TYPE")
|
||||
DOCKER_ARGS+=" $(: | plugn trigger docker-args-process-build "$APP" "$BUILDER_TYPE")"
|
||||
DOCKER_ARGS+=" $DOKKU_GLOBAL_BUILD_ARGS"
|
||||
|
||||
DOCKER_ARGS=" $DOCKER_ARGS "
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core builder-nixpacks plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core builder-null plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -38,7 +38,7 @@ trigger-builder-pack-builder-build() {
|
||||
plugn trigger pre-build "$BUILDER_TYPE" "$APP" "$SOURCECODE_WORK_DIR"
|
||||
|
||||
local DOCKER_ARGS=$(: | plugn trigger docker-args-build "$APP" "$BUILDER_TYPE")
|
||||
DOCKER_ARGS+=$(: | plugn trigger docker-args-process-build "$APP" "$BUILDER_TYPE")
|
||||
DOCKER_ARGS+=" $(: | plugn trigger docker-args-process-build "$APP" "$BUILDER_TYPE")"
|
||||
[[ "$DOKKU_TRACE" ]] && DOCKER_ARGS+=" --env=TRACE=true "
|
||||
|
||||
DOCKER_ARGS=" $DOCKER_ARGS "
|
||||
|
||||
@@ -23,7 +23,7 @@ trigger-builder-pack-docker-args-process-deploy() {
|
||||
output=" --entrypoint launcher"
|
||||
fi
|
||||
|
||||
echo -n "$STDIN$output"
|
||||
echo -n "$STDIN $output"
|
||||
}
|
||||
|
||||
trigger-builder-pack-docker-args-process-deploy "$@"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core builder-pack plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -34,7 +34,7 @@ trigger-builder-railpack-builder-build() {
|
||||
fi
|
||||
|
||||
local DOCKER_ARGS=$(: | plugn trigger docker-args-build "$APP" "$BUILDER_TYPE")
|
||||
DOCKER_ARGS+=$(: | plugn trigger docker-args-process-build "$APP" "$BUILDER_TYPE")
|
||||
DOCKER_ARGS+=" $(: | plugn trigger docker-args-process-build "$APP" "$BUILDER_TYPE")"
|
||||
|
||||
DOCKER_ARGS=" $DOCKER_ARGS "
|
||||
eval set -- "$DOCKER_ARGS"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core builder-railpack plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -15,15 +15,15 @@ require (
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/melbahja/goph v1.5.0 // indirect
|
||||
github.com/melbahja/goph v1.5.1 // indirect
|
||||
github.com/otiai10/copy v1.14.1 // indirect
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/sftp v1.13.10 // indirect
|
||||
github.com/ryanuber/columnize v2.1.2+incompatible // indirect
|
||||
golang.org/x/crypto v0.52.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
golang.org/x/crypto v0.53.0 // indirect
|
||||
golang.org/x/sync v0.21.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/dokku/dokku/plugins/common => ../common
|
||||
|
||||
@@ -16,10 +16,10 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/melbahja/goph v1.5.0 h1:RQUBpLvfg3i7fjfG8rTcSWyMjVRfdhwrrfQhjYee4dQ=
|
||||
github.com/melbahja/goph v1.5.0/go.mod h1:dDwo+44cmvfDLdiVpc6fJxexf5BA5yEDUeE5YgtuDO4=
|
||||
github.com/onsi/gomega v1.41.0 h1:OwKp4pXNgVxf6sCplzYo794OFNuoL2q2SBMU5NSWOjA=
|
||||
github.com/onsi/gomega v1.41.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A=
|
||||
github.com/melbahja/goph v1.5.1 h1:gdZyjn5nM6FO6mAZrv7XqE2gArQbb+V6FdIyH3Ru3vA=
|
||||
github.com/melbahja/goph v1.5.1/go.mod h1:dDwo+44cmvfDLdiVpc6fJxexf5BA5yEDUeE5YgtuDO4=
|
||||
github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I=
|
||||
github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg=
|
||||
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
||||
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
|
||||
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
||||
@@ -38,18 +38,18 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core builder plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -15,18 +15,23 @@ func ReportSingleApp(appName string, format string, infoFlag string) error {
|
||||
var flags map[string]common.ReportFunc
|
||||
if appName == "--global" {
|
||||
flags = map[string]common.ReportFunc{
|
||||
"--builder-global-selected": reportGlobalSelected,
|
||||
"--builder-global-build-dir": reportGlobalBuildDir,
|
||||
"--builder-global-selected": reportGlobalSelected,
|
||||
"--builder-global-build-dir": reportGlobalBuildDir,
|
||||
"--builder-global-skip-cleanup": reportGlobalSkipCleanup,
|
||||
"--builder-computed-skip-cleanup": reportComputedSkipCleanup,
|
||||
}
|
||||
} else {
|
||||
flags = map[string]common.ReportFunc{
|
||||
"--builder-computed-selected": reportComputedSelected,
|
||||
"--builder-global-selected": reportGlobalSelected,
|
||||
"--builder-selected": reportSelected,
|
||||
"--builder-detected": reportDetected,
|
||||
"--builder-computed-build-dir": reportComputedBuildDir,
|
||||
"--builder-global-build-dir": reportGlobalBuildDir,
|
||||
"--builder-build-dir": reportBuildDir,
|
||||
"--builder-computed-selected": reportComputedSelected,
|
||||
"--builder-global-selected": reportGlobalSelected,
|
||||
"--builder-selected": reportSelected,
|
||||
"--builder-detected": reportDetected,
|
||||
"--builder-computed-build-dir": reportComputedBuildDir,
|
||||
"--builder-global-build-dir": reportGlobalBuildDir,
|
||||
"--builder-build-dir": reportBuildDir,
|
||||
"--builder-computed-skip-cleanup": reportComputedSkipCleanup,
|
||||
"--builder-global-skip-cleanup": reportGlobalSkipCleanup,
|
||||
"--builder-skip-cleanup": reportSkipCleanup,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,10 +40,18 @@ func ReportSingleApp(appName string, format string, infoFlag string) error {
|
||||
flagKeys = append(flagKeys, flagKey)
|
||||
}
|
||||
|
||||
trimPrefix := false
|
||||
uppercaseFirstCharacter := true
|
||||
infoFlags := common.CollectReport(appName, infoFlag, flags)
|
||||
return common.ReportSingleApp("builder", appName, infoFlag, infoFlags, flagKeys, format, trimPrefix, uppercaseFirstCharacter)
|
||||
return common.ReportSingleApp(common.ReportSingleAppInput{
|
||||
ReportType: "builder",
|
||||
AppName: appName,
|
||||
InfoFlag: infoFlag,
|
||||
InfoFlags: infoFlags,
|
||||
InfoFlagKeys: flagKeys,
|
||||
Format: format,
|
||||
TrimPrefix: true,
|
||||
UppercaseFirstCharacter: true,
|
||||
EmitLegacyPrefix: true,
|
||||
})
|
||||
}
|
||||
|
||||
func reportComputedSelected(appName string) string {
|
||||
@@ -77,3 +90,23 @@ func reportGlobalBuildDir(appName string) string {
|
||||
func reportBuildDir(appName string) string {
|
||||
return common.PropertyGet("builder", appName, "build-dir")
|
||||
}
|
||||
|
||||
func reportComputedSkipCleanup(appName string) string {
|
||||
value := reportSkipCleanup(appName)
|
||||
if value == "" {
|
||||
value = reportGlobalSkipCleanup(appName)
|
||||
}
|
||||
if value == "" {
|
||||
value = "false"
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func reportGlobalSkipCleanup(appName string) string {
|
||||
return common.PropertyGet("builder", "--global", "skip-cleanup")
|
||||
}
|
||||
|
||||
func reportSkipCleanup(appName string) string {
|
||||
return common.PropertyGet("builder", appName, "skip-cleanup")
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ go 1.26.2
|
||||
require (
|
||||
github.com/dokku/dokku/plugins/app-json v0.0.0-00010101000000-000000000000
|
||||
github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000
|
||||
github.com/onsi/gomega v1.41.0
|
||||
github.com/onsi/gomega v1.42.1
|
||||
github.com/spf13/pflag v1.0.10
|
||||
)
|
||||
|
||||
@@ -19,7 +19,7 @@ require (
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.22 // indirect
|
||||
github.com/melbahja/goph v1.5.0 // indirect
|
||||
github.com/melbahja/goph v1.5.1 // indirect
|
||||
github.com/otiai10/copy v1.14.1 // indirect
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
@@ -27,11 +27,11 @@ require (
|
||||
github.com/ryanuber/columnize v2.1.2+incompatible // indirect
|
||||
github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.52.0 // indirect
|
||||
golang.org/x/net v0.54.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
golang.org/x/text v0.37.0 // indirect
|
||||
golang.org/x/crypto v0.53.0 // indirect
|
||||
golang.org/x/net v0.56.0 // indirect
|
||||
golang.org/x/sync v0.21.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
golang.org/x/text v0.38.0 // indirect
|
||||
k8s.io/utils v0.0.0-20240102154912-e7106e64919e // indirect
|
||||
)
|
||||
|
||||
|
||||
@@ -14,18 +14,14 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
|
||||
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||
github.com/melbahja/goph v1.5.0 h1:RQUBpLvfg3i7fjfG8rTcSWyMjVRfdhwrrfQhjYee4dQ=
|
||||
github.com/melbahja/goph v1.5.0/go.mod h1:dDwo+44cmvfDLdiVpc6fJxexf5BA5yEDUeE5YgtuDO4=
|
||||
github.com/onsi/gomega v1.41.0 h1:OwKp4pXNgVxf6sCplzYo794OFNuoL2q2SBMU5NSWOjA=
|
||||
github.com/onsi/gomega v1.41.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A=
|
||||
github.com/melbahja/goph v1.5.1 h1:gdZyjn5nM6FO6mAZrv7XqE2gArQbb+V6FdIyH3Ru3vA=
|
||||
github.com/melbahja/goph v1.5.1/go.mod h1:dDwo+44cmvfDLdiVpc6fJxexf5BA5yEDUeE5YgtuDO4=
|
||||
github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I=
|
||||
github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg=
|
||||
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
||||
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
|
||||
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
||||
@@ -36,8 +32,6 @@ github.com/pkg/sftp v1.13.10 h1:+5FbKNTe5Z9aspU88DPIKJ9z2KZoaGCu6Sr6kKR/5mU=
|
||||
github.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1HbeGA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk=
|
||||
github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
@@ -48,21 +42,20 @@ github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a h1:a6TNDN9CgG+cYj
|
||||
github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a/go.mod h1:EbW0wDK/qEUYI0A5bqq0C2kF8JTQwWONmGDBbzsxxHo=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ=
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core buildpacks plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -34,10 +34,18 @@ func ReportSingleApp(appName string, format string, infoFlag string) error {
|
||||
flagKeys = append(flagKeys, flagKey)
|
||||
}
|
||||
|
||||
trimPrefix := false
|
||||
uppercaseFirstCharacter := true
|
||||
infoFlags := common.CollectReport(appName, infoFlag, flags)
|
||||
return common.ReportSingleApp("buildpacks", appName, infoFlag, infoFlags, flagKeys, format, trimPrefix, uppercaseFirstCharacter)
|
||||
return common.ReportSingleApp(common.ReportSingleAppInput{
|
||||
ReportType: "buildpacks",
|
||||
AppName: appName,
|
||||
InfoFlag: infoFlag,
|
||||
InfoFlags: infoFlags,
|
||||
InfoFlagKeys: flagKeys,
|
||||
Format: format,
|
||||
TrimPrefix: true,
|
||||
UppercaseFirstCharacter: true,
|
||||
EmitLegacyPrefix: true,
|
||||
})
|
||||
}
|
||||
|
||||
func reportComputedStack(appName string) string {
|
||||
|
||||
@@ -17,14 +17,14 @@ require (
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/melbahja/goph v1.5.0 // indirect
|
||||
github.com/melbahja/goph v1.5.1 // indirect
|
||||
github.com/otiai10/copy v1.14.1 // indirect
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/sftp v1.13.10 // indirect
|
||||
golang.org/x/crypto v0.52.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
golang.org/x/crypto v0.53.0 // indirect
|
||||
golang.org/x/sync v0.21.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/dokku/dokku/plugins/common => ../common
|
||||
|
||||
@@ -16,12 +16,12 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/melbahja/goph v1.5.0 h1:RQUBpLvfg3i7fjfG8rTcSWyMjVRfdhwrrfQhjYee4dQ=
|
||||
github.com/melbahja/goph v1.5.0/go.mod h1:dDwo+44cmvfDLdiVpc6fJxexf5BA5yEDUeE5YgtuDO4=
|
||||
github.com/melbahja/goph v1.5.1 h1:gdZyjn5nM6FO6mAZrv7XqE2gArQbb+V6FdIyH3Ru3vA=
|
||||
github.com/melbahja/goph v1.5.1/go.mod h1:dDwo+44cmvfDLdiVpc6fJxexf5BA5yEDUeE5YgtuDO4=
|
||||
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
|
||||
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
|
||||
github.com/onsi/gomega v1.41.0 h1:OwKp4pXNgVxf6sCplzYo794OFNuoL2q2SBMU5NSWOjA=
|
||||
github.com/onsi/gomega v1.41.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A=
|
||||
github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I=
|
||||
github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg=
|
||||
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
||||
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
|
||||
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
||||
@@ -40,18 +40,18 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -42,7 +42,17 @@ func ReportSingleApp(appName, format, infoFlag string) error {
|
||||
}
|
||||
|
||||
infoFlags := common.CollectReport(appName, infoFlag, flags)
|
||||
return common.ReportSingleApp("builds", appName, infoFlag, infoFlags, flagKeys, format, false, true)
|
||||
return common.ReportSingleApp(common.ReportSingleAppInput{
|
||||
ReportType: "builds",
|
||||
AppName: appName,
|
||||
InfoFlag: infoFlag,
|
||||
InfoFlags: infoFlags,
|
||||
InfoFlagKeys: flagKeys,
|
||||
Format: format,
|
||||
TrimPrefix: true,
|
||||
UppercaseFirstCharacter: true,
|
||||
EmitLegacyPrefix: true,
|
||||
})
|
||||
}
|
||||
|
||||
func mostRecentBuild(appName string) (Build, bool) {
|
||||
|
||||
@@ -137,7 +137,7 @@ trigger-caddy-vhosts-docker-args-process-deploy() {
|
||||
done <"$proxy_labels_file_path"
|
||||
fi
|
||||
|
||||
echo -n "$STDIN$output"
|
||||
echo -n "$STDIN $output"
|
||||
}
|
||||
|
||||
trigger-caddy-vhosts-docker-args-process-deploy "$@"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core caddy-vhosts plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core certificate management plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core checks plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -776,22 +776,99 @@ func ParseScaleOutput(b []byte) (map[string]int32, error) {
|
||||
return scale, nil
|
||||
}
|
||||
|
||||
// ReportSingleApp is an internal function that displays a report for an app
|
||||
func ReportSingleApp(reportType string, appName string, infoFlag string, infoFlags map[string]string, infoFlagKeys []string, format string, trimPrefix bool, uppercaseFirstCharacter bool) error {
|
||||
if format != "stdout" && infoFlag != "" {
|
||||
// ReportSingleAppInput configures a call to ReportSingleApp.
|
||||
type ReportSingleAppInput struct {
|
||||
// ReportType is the plugin name used as the JSON key prefix (e.g. "ps", "registry").
|
||||
// Must be non-empty, contain no whitespace, and not begin with "--".
|
||||
ReportType string
|
||||
|
||||
// AppName is the app being reported on, or "--global" for the global report.
|
||||
AppName string
|
||||
|
||||
// InfoFlag is an optional single flag to extract (e.g. "--ps-stop-timeout-seconds").
|
||||
// Only valid when Format is "stdout" (or empty, which defaults to "stdout").
|
||||
InfoFlag string
|
||||
|
||||
// InfoFlags maps flag-name to value (e.g. "--ps-stop-timeout-seconds" -> "30").
|
||||
InfoFlags map[string]string
|
||||
|
||||
// InfoFlagKeys is the list of valid flag names surfaced in InfoFlag lookup errors.
|
||||
InfoFlagKeys []string
|
||||
|
||||
// Format is "stdout" or "json". Empty defaults to "stdout".
|
||||
Format string
|
||||
|
||||
// TrimPrefix, when true, strips the "--<ReportType>-" prefix from JSON keys so
|
||||
// each report's JSON shape is independent of which plugin emitted it. Stdout
|
||||
// output is not affected by this flag.
|
||||
TrimPrefix bool
|
||||
|
||||
// UppercaseFirstCharacter capitalizes the first character of each stdout key.
|
||||
UppercaseFirstCharacter bool
|
||||
|
||||
// EmitLegacyPrefix, when true alongside TrimPrefix, also emits the legacy
|
||||
// "<ReportType>-<property>" keys in JSON output for backwards compatibility
|
||||
// during the deprecation window. Has no effect when TrimPrefix is false.
|
||||
EmitLegacyPrefix bool
|
||||
}
|
||||
|
||||
// Validate returns an error when the input is malformed.
|
||||
func (i ReportSingleAppInput) Validate() error {
|
||||
if i.ReportType == "" {
|
||||
return errors.New("ReportType is required")
|
||||
}
|
||||
if strings.ContainsAny(i.ReportType, " \t\n") {
|
||||
return fmt.Errorf("ReportType must not contain whitespace: %q", i.ReportType)
|
||||
}
|
||||
if strings.HasPrefix(i.ReportType, "--") {
|
||||
return fmt.Errorf("ReportType must not begin with --: %q", i.ReportType)
|
||||
}
|
||||
if i.AppName == "" {
|
||||
return errors.New("AppName is required (use --global for the global report)")
|
||||
}
|
||||
if i.InfoFlags == nil {
|
||||
return errors.New("InfoFlags is required")
|
||||
}
|
||||
format := i.Format
|
||||
if format == "" {
|
||||
format = "stdout"
|
||||
}
|
||||
if format != "stdout" && format != "json" {
|
||||
return fmt.Errorf("Format must be \"stdout\" or \"json\": %q", i.Format)
|
||||
}
|
||||
if format != "stdout" && i.InfoFlag != "" {
|
||||
return errors.New("--format flag cannot be specified when specifying an info flag")
|
||||
}
|
||||
if i.EmitLegacyPrefix && !i.TrimPrefix {
|
||||
return errors.New("EmitLegacyPrefix has no effect when TrimPrefix is false")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReportSingleApp is an internal function that displays a report for an app.
|
||||
func ReportSingleApp(input ReportSingleAppInput) error {
|
||||
if err := input.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
format := input.Format
|
||||
if format == "" {
|
||||
format = "stdout"
|
||||
}
|
||||
|
||||
if format == "json" {
|
||||
data := map[string]string{}
|
||||
for key, value := range infoFlags {
|
||||
prefix := "--"
|
||||
if trimPrefix {
|
||||
prefix = fmt.Sprintf("--%v-", reportType)
|
||||
pluginPrefix := fmt.Sprintf("--%v-", input.ReportType)
|
||||
for key, value := range input.InfoFlags {
|
||||
legacyKey := strings.TrimPrefix(key, "--")
|
||||
if input.TrimPrefix && strings.HasPrefix(key, pluginPrefix) {
|
||||
data[strings.TrimPrefix(key, pluginPrefix)] = value
|
||||
if input.EmitLegacyPrefix {
|
||||
data[legacyKey] = value
|
||||
}
|
||||
} else {
|
||||
data[legacyKey] = value
|
||||
}
|
||||
|
||||
// key = strings.Replace(strings.Replace(strings.TrimPrefix(key, prefix), "-", " ", -1), ".", " ", -1)
|
||||
data[strings.TrimPrefix(key, prefix)] = value
|
||||
}
|
||||
out, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
@@ -803,7 +880,7 @@ func ReportSingleApp(reportType string, appName string, infoFlag string, infoFla
|
||||
|
||||
length := 0
|
||||
flags := []string{}
|
||||
for key := range infoFlags {
|
||||
for key := range input.InfoFlags {
|
||||
if len(key) > length {
|
||||
length = len(key)
|
||||
}
|
||||
@@ -814,22 +891,17 @@ func ReportSingleApp(reportType string, appName string, infoFlag string, infoFla
|
||||
length = 31
|
||||
}
|
||||
|
||||
if len(infoFlag) == 0 {
|
||||
LogInfo2Quiet(fmt.Sprintf("%s %v information", appName, reportType))
|
||||
if len(input.InfoFlag) == 0 {
|
||||
LogInfo2Quiet(fmt.Sprintf("%s %v information", input.AppName, input.ReportType))
|
||||
for _, k := range flags {
|
||||
v, ok := infoFlags[k]
|
||||
v, ok := input.InfoFlags[k]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
prefix := "--"
|
||||
if trimPrefix {
|
||||
prefix = fmt.Sprintf("--%v-", reportType)
|
||||
}
|
||||
key := strings.Replace(strings.Replace(strings.TrimPrefix(k, "--"), "-", " ", -1), ".", " ", -1)
|
||||
|
||||
key := strings.Replace(strings.Replace(strings.TrimPrefix(k, prefix), "-", " ", -1), ".", " ", -1)
|
||||
|
||||
if uppercaseFirstCharacter {
|
||||
if input.UppercaseFirstCharacter {
|
||||
key = UcFirst(key)
|
||||
}
|
||||
|
||||
@@ -839,8 +911,8 @@ func ReportSingleApp(reportType string, appName string, infoFlag string, infoFla
|
||||
}
|
||||
|
||||
for _, k := range flags {
|
||||
if infoFlag == k {
|
||||
v, ok := infoFlags[k]
|
||||
if input.InfoFlag == k {
|
||||
v, ok := input.InfoFlags[k]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@@ -849,6 +921,7 @@ func ReportSingleApp(reportType string, appName string, infoFlag string, infoFla
|
||||
}
|
||||
}
|
||||
|
||||
infoFlagKeys := append([]string(nil), input.InfoFlagKeys...)
|
||||
sort.Strings(infoFlagKeys)
|
||||
return fmt.Errorf("Invalid flag passed, valid flags: %s", strings.Join(infoFlagKeys, ", "))
|
||||
}
|
||||
|
||||
@@ -279,8 +279,9 @@ verify_app_name() {
|
||||
}
|
||||
|
||||
verify_image() {
|
||||
declare desc="verify image existence"
|
||||
declare desc="[deprecated] verify image existence. Use 'fn-verify-app-image' instead."
|
||||
local IMAGE="$1"
|
||||
dokku_log_warn "Deprecated: please use 'fn-verify-app-image' instead of 'verify_image'"
|
||||
if "$DOCKER_BIN" image inspect "$IMAGE" &>/dev/null; then
|
||||
return 0
|
||||
else
|
||||
@@ -288,6 +289,46 @@ verify_image() {
|
||||
fi
|
||||
}
|
||||
|
||||
fn-verify-app-image() {
|
||||
declare desc="verify image existence, pulling from the registry when push-on-release is enabled"
|
||||
declare APP="$1" IMAGE="$2"
|
||||
|
||||
if "$DOCKER_BIN" image inspect "$IMAGE" &>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ "$IMAGE" == *:latest ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local DEPLOYED_TAG
|
||||
DEPLOYED_TAG="$(plugn trigger deployed-app-image-tag "$APP")"
|
||||
if [[ -z "$DEPLOYED_TAG" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local DOCKER_CONFIG
|
||||
DOCKER_CONFIG="$(fn-registry-docker-config-dir "$APP")"
|
||||
[[ -n "$DOCKER_CONFIG" ]] && export DOCKER_CONFIG
|
||||
|
||||
local REMOTE_REPO DEPLOYED_REPO TAG
|
||||
REMOTE_REPO="$(plugn trigger deployed-app-repository "$APP")"
|
||||
DEPLOYED_REPO="$(plugn trigger deployed-app-image-repo "$APP")"
|
||||
TAG="${IMAGE##*:}"
|
||||
|
||||
dokku_log_info1 "Image $IMAGE not found locally, pulling from registry" 1>&2
|
||||
if [[ -n "$DEPLOYED_REPO" ]] && [[ "$IMAGE" == "${REMOTE_REPO}${DEPLOYED_REPO}:"* ]]; then
|
||||
"$DOCKER_BIN" image pull "$IMAGE" 1>&2 && return 0
|
||||
return 1
|
||||
fi
|
||||
|
||||
local REMOTE_IMAGE="${REMOTE_REPO}${DEPLOYED_REPO:-${IMAGE%:*}}:${TAG}"
|
||||
if "$DOCKER_BIN" image pull "$REMOTE_IMAGE" 1>&2; then
|
||||
"$DOCKER_BIN" image tag "$REMOTE_IMAGE" "$IMAGE" 1>&2 && return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
get_app_image_repo() {
|
||||
declare desc="central definition of image repo pattern"
|
||||
local APP="$1"
|
||||
@@ -309,7 +350,7 @@ get_deploying_app_image_name() {
|
||||
[[ -z "$IMAGE_REPO" ]] && IMAGE_REPO=$(get_app_image_repo "$APP")
|
||||
|
||||
local IMAGE="${IMAGE_REMOTE_REPOSITORY}${IMAGE_REPO}:${IMAGE_TAG}"
|
||||
verify_image "$IMAGE" || dokku_log_fail "App image ($IMAGE) not found"
|
||||
fn-verify-app-image "$APP" "$IMAGE" || dokku_log_fail "App image ($IMAGE) not found"
|
||||
echo "$IMAGE"
|
||||
}
|
||||
|
||||
@@ -325,7 +366,7 @@ get_app_image_name() {
|
||||
|
||||
if [[ -n "$IMAGE_TAG" ]]; then
|
||||
local IMAGE="$IMAGE_REPO:$IMAGE_TAG"
|
||||
verify_image "$IMAGE" || dokku_log_fail "App image ($IMAGE) not found"
|
||||
fn-verify-app-image "$APP" "$IMAGE" || dokku_log_fail "App image ($IMAGE) not found"
|
||||
else
|
||||
local IMAGE="$IMAGE_REPO:latest"
|
||||
fi
|
||||
@@ -627,7 +668,7 @@ release_and_deploy() {
|
||||
local IMAGE=$(get_app_image_name "$APP" "$IMAGE_TAG")
|
||||
local exit_code=0
|
||||
|
||||
if verify_image "$IMAGE"; then
|
||||
if fn-verify-app-image "$APP" "$IMAGE"; then
|
||||
if ! declare -f -F fn-plugin-property-get-default >/dev/null; then
|
||||
source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions"
|
||||
fi
|
||||
@@ -752,23 +793,6 @@ get_ipv6_regex() {
|
||||
echo "${RE_IPV6}\$"
|
||||
}
|
||||
|
||||
get_entrypoint_from_image() {
|
||||
declare desc="return .Config.Entrypoint from passed image name"
|
||||
local IMAGE="$1"
|
||||
verify_image "$IMAGE"
|
||||
local DOCKER_IMAGE_ENTRYPOINT="$("$DOCKER_BIN" image inspect --format '{{range .Config.Entrypoint}}{{.}} {{end}}' "$IMAGE")"
|
||||
echo "ENTRYPOINT $DOCKER_IMAGE_ENTRYPOINT"
|
||||
}
|
||||
|
||||
get_cmd_from_image() {
|
||||
declare desc="return .Config.Cmd from passed image name"
|
||||
local IMAGE="$1"
|
||||
verify_image "$IMAGE"
|
||||
local DOCKER_IMAGE_CMD="$("$DOCKER_BIN" image inspect --format '{{range .Config.Cmd}}{{.}} {{end}}' "$IMAGE")"
|
||||
DOCKER_IMAGE_CMD="${DOCKER_IMAGE_CMD/\/bin\/sh -c/}"
|
||||
echo "CMD $DOCKER_IMAGE_CMD"
|
||||
}
|
||||
|
||||
extract_directive_from_dockerfile() {
|
||||
declare desc="return requested directive from passed file path"
|
||||
local FILE_PATH="$1"
|
||||
|
||||
@@ -6,27 +6,26 @@ require (
|
||||
github.com/alexellis/go-execute/v2 v2.2.1
|
||||
github.com/fatih/color v1.19.0
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/melbahja/goph v1.5.0
|
||||
github.com/onsi/gomega v1.41.0
|
||||
github.com/melbahja/goph v1.5.1
|
||||
github.com/onsi/gomega v1.42.1
|
||||
github.com/otiai10/copy v1.14.1
|
||||
github.com/ryanuber/columnize v2.1.2+incompatible
|
||||
github.com/spf13/pflag v1.0.10
|
||||
golang.org/x/crypto v0.52.0
|
||||
golang.org/x/sync v0.20.0
|
||||
golang.org/x/crypto v0.53.0
|
||||
golang.org/x/sync v0.21.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/sftp v1.13.10 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/net v0.54.0 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
golang.org/x/text v0.37.0 // indirect
|
||||
golang.org/x/net v0.56.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
golang.org/x/text v0.38.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
github.com/alexellis/go-execute/v2 v2.2.1 h1:4Ye3jiCKQarstODOEmqDSRCqxMHLkC92Bhse743RdOI=
|
||||
github.com/alexellis/go-execute/v2 v2.2.1/go.mod h1:FMdRnUTiFAmYXcv23txrp3VYZfLo24nMpiIneWgKHTQ=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
||||
@@ -13,18 +12,14 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/melbahja/goph v1.5.0 h1:RQUBpLvfg3i7fjfG8rTcSWyMjVRfdhwrrfQhjYee4dQ=
|
||||
github.com/melbahja/goph v1.5.0/go.mod h1:dDwo+44cmvfDLdiVpc6fJxexf5BA5yEDUeE5YgtuDO4=
|
||||
github.com/onsi/gomega v1.41.0 h1:OwKp4pXNgVxf6sCplzYo794OFNuoL2q2SBMU5NSWOjA=
|
||||
github.com/onsi/gomega v1.41.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A=
|
||||
github.com/melbahja/goph v1.5.1 h1:gdZyjn5nM6FO6mAZrv7XqE2gArQbb+V6FdIyH3Ru3vA=
|
||||
github.com/melbahja/goph v1.5.1/go.mod h1:dDwo+44cmvfDLdiVpc6fJxexf5BA5yEDUeE5YgtuDO4=
|
||||
github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I=
|
||||
github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg=
|
||||
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
||||
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
|
||||
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
||||
@@ -35,8 +30,6 @@ github.com/pkg/sftp v1.13.10 h1:+5FbKNTe5Z9aspU88DPIKJ9z2KZoaGCu6Sr6kKR/5mU=
|
||||
github.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1HbeGA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk=
|
||||
github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
@@ -45,21 +38,20 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -212,10 +212,12 @@ func TouchDir(filename string) error {
|
||||
return os.MkdirAll(filename, mode)
|
||||
}
|
||||
|
||||
// TouchFile creates an empty file at the specified path
|
||||
// TouchFile ensures a file exists at the specified path without truncating
|
||||
// its contents. It matches the semantics of unix touch(1): create the file if
|
||||
// it does not exist, otherwise leave the existing contents intact.
|
||||
func TouchFile(filename string) error {
|
||||
mode := os.FileMode(0600)
|
||||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode)
|
||||
file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, mode)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error opening file %v for creation: %v", filename, err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
@@ -31,3 +34,47 @@ func TestCommonReadFirstLine(t *testing.T) {
|
||||
Expect(line).To(Equal(testEnvLine))
|
||||
teardownTestApp()
|
||||
}
|
||||
|
||||
// setupTouchFileTest pins DOKKU_SYSTEM_USER/GROUP to the running test user so
|
||||
// SetPermissions' chown succeeds for non-root test runs.
|
||||
func setupTouchFileTest(t *testing.T) {
|
||||
t.Helper()
|
||||
u, err := user.Current()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
g, err := user.LookupGroupId(u.Gid)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
t.Setenv("DOKKU_SYSTEM_USER", u.Username)
|
||||
t.Setenv("DOKKU_SYSTEM_GROUP", g.Name)
|
||||
}
|
||||
|
||||
func TestCommonTouchFileCreatesMissingFile(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
setupTouchFileTest(t)
|
||||
|
||||
path := filepath.Join(t.TempDir(), "new-file")
|
||||
Expect(FileExists(path)).To(BeFalse())
|
||||
Expect(TouchFile(path)).To(Succeed())
|
||||
|
||||
info, err := os.Stat(path)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(info.Size()).To(BeNumerically("==", 0))
|
||||
Expect(info.Mode().Perm()).To(Equal(os.FileMode(0600)))
|
||||
}
|
||||
|
||||
// TestCommonTouchFilePreservesExistingContents is a regression test for #8722:
|
||||
// scheduler-k3s:cluster:add used to zero out the dokku user's ~/.ssh/known_hosts
|
||||
// because TouchFile opened the file with O_TRUNC.
|
||||
func TestCommonTouchFilePreservesExistingContents(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
setupTouchFileTest(t)
|
||||
|
||||
path := filepath.Join(t.TempDir(), "existing-file")
|
||||
contents := []byte("host ssh-rsa AAAA...\n")
|
||||
Expect(os.WriteFile(path, contents, 0600)).To(Succeed())
|
||||
|
||||
Expect(TouchFile(path)).To(Succeed())
|
||||
|
||||
got, err := os.ReadFile(path)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(got).To(Equal(contents))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core common plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -2,6 +2,7 @@ package common
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -514,6 +515,99 @@ func PropertyWrite(pluginName string, appName string, property string, value str
|
||||
return nil
|
||||
}
|
||||
|
||||
// PropertyMapWrite persists a string→string map for a given property as a single
|
||||
// JSON file. Unlike PropertyWrite/PropertyListWrite, map *keys* may contain any
|
||||
// bytes (including '/' or '\n') because they live inside file content rather
|
||||
// than in the on-disk filename. A nil map is persisted as "{}".
|
||||
func PropertyMapWrite(pluginName string, appName string, property string, m map[string]string) error {
|
||||
if err := propertyTouch(pluginName, appName, property); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
m = map[string]string{}
|
||||
}
|
||||
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to marshal %s map property %s.%s: %w", pluginName, appName, property, err)
|
||||
}
|
||||
|
||||
propertyPath := getPropertyPath(pluginName, appName, property)
|
||||
if err := os.WriteFile(propertyPath, b, 0600); err != nil {
|
||||
return fmt.Errorf("Unable to write %s map property %s.%s: %w", pluginName, appName, property, err)
|
||||
}
|
||||
|
||||
if err := SetPermissions(SetPermissionInput{
|
||||
Filename: propertyPath,
|
||||
Mode: os.FileMode(0600),
|
||||
}); err != nil {
|
||||
return fmt.Errorf("Unable to set permissions for %s map property %s.%s: %w", pluginName, appName, property, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PropertyMapGet returns the string→string map persisted for a property. A
|
||||
// missing or empty-file property is reported as an empty map without error.
|
||||
func PropertyMapGet(pluginName string, appName string, property string) (map[string]string, error) {
|
||||
m := map[string]string{}
|
||||
if !PropertyExists(pluginName, appName, property) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
b, err := os.ReadFile(getPropertyPath(pluginName, appName, property))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to read %s map property %s.%s: %w", pluginName, appName, property, err)
|
||||
}
|
||||
|
||||
if len(b) == 0 {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(b, &m); err != nil {
|
||||
return nil, fmt.Errorf("Unable to parse %s map property %s.%s as JSON: %w", pluginName, appName, property, err)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// PropertyMapSet reads the current map, sets one key, and writes the map back.
|
||||
func PropertyMapSet(pluginName string, appName string, property string, key string, value string) error {
|
||||
m, err := PropertyMapGet(pluginName, appName, property)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m[key] = value
|
||||
return PropertyMapWrite(pluginName, appName, property, m)
|
||||
}
|
||||
|
||||
// PropertyMapDelete reads the current map, removes one key, and writes the map
|
||||
// back. Removing a key that is not present is a no-op.
|
||||
func PropertyMapDelete(pluginName string, appName string, property string, key string) error {
|
||||
if !PropertyExists(pluginName, appName, property) {
|
||||
return nil
|
||||
}
|
||||
m, err := PropertyMapGet(pluginName, appName, property)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := m[key]; !ok {
|
||||
return nil
|
||||
}
|
||||
delete(m, key)
|
||||
return PropertyMapWrite(pluginName, appName, property, m)
|
||||
}
|
||||
|
||||
// PropertyMapLength returns the number of entries in the map persisted for a
|
||||
// property. A missing property is reported as length 0 without error.
|
||||
func PropertyMapLength(pluginName string, appName string, property string) (int, error) {
|
||||
m, err := PropertyMapGet(pluginName, appName, property)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(m), nil
|
||||
}
|
||||
|
||||
// PropertySetup creates the plugin config root
|
||||
func PropertySetup(pluginName string) error {
|
||||
configRoot := filepath.Join(MustGetEnv("DOKKU_LIB_ROOT"), "config")
|
||||
|
||||
170
plugins/common/properties_test.go
Normal file
170
plugins/common/properties_test.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
const (
|
||||
propertyTestPlugin = "test-plugin"
|
||||
propertyTestApp = "--global"
|
||||
propertyTestName = "chart-overrides.traefik"
|
||||
)
|
||||
|
||||
func setupPropertyMapTest(t *testing.T) {
|
||||
t.Helper()
|
||||
RegisterTestingT(t)
|
||||
Expect(setupTests()).To(Succeed())
|
||||
t.Setenv("DOKKU_LIB_ROOT", t.TempDir())
|
||||
t.Setenv("DOKKU_SYSTEM_USER", "root")
|
||||
t.Setenv("DOKKU_SYSTEM_GROUP", "root")
|
||||
Expect(PropertySetup(propertyTestPlugin)).To(Succeed())
|
||||
}
|
||||
|
||||
func TestPropertyMapWriteAndGetRoundTrip(t *testing.T) {
|
||||
setupPropertyMapTest(t)
|
||||
|
||||
want := map[string]string{"installCRDs": "false", "version": "1.13.3"}
|
||||
Expect(PropertyMapWrite(propertyTestPlugin, propertyTestApp, propertyTestName, want)).To(Succeed())
|
||||
|
||||
got, err := PropertyMapGet(propertyTestPlugin, propertyTestApp, propertyTestName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(got).To(Equal(want))
|
||||
}
|
||||
|
||||
func TestPropertyMapHandlesSlashInKey(t *testing.T) {
|
||||
setupPropertyMapTest(t)
|
||||
|
||||
key := "service.annotations.prometheus.io/scrape"
|
||||
Expect(PropertyMapSet(propertyTestPlugin, propertyTestApp, propertyTestName, key, "true")).To(Succeed())
|
||||
|
||||
got, err := PropertyMapGet(propertyTestPlugin, propertyTestApp, propertyTestName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(got).To(HaveKeyWithValue(key, "true"))
|
||||
}
|
||||
|
||||
func TestPropertyMapHandlesNewlineInValue(t *testing.T) {
|
||||
setupPropertyMapTest(t)
|
||||
|
||||
value := "line one\nline two\nline three"
|
||||
Expect(PropertyMapSet(propertyTestPlugin, propertyTestApp, propertyTestName, "controller.config", value)).To(Succeed())
|
||||
|
||||
got, err := PropertyMapGet(propertyTestPlugin, propertyTestApp, propertyTestName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(got).To(HaveKeyWithValue("controller.config", value))
|
||||
}
|
||||
|
||||
func TestPropertyMapEmptyMap(t *testing.T) {
|
||||
setupPropertyMapTest(t)
|
||||
|
||||
Expect(PropertyMapWrite(propertyTestPlugin, propertyTestApp, propertyTestName, map[string]string{})).To(Succeed())
|
||||
|
||||
got, err := PropertyMapGet(propertyTestPlugin, propertyTestApp, propertyTestName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(got).To(BeEmpty())
|
||||
}
|
||||
|
||||
func TestPropertyMapNilMapPersistsAsEmptyJSON(t *testing.T) {
|
||||
setupPropertyMapTest(t)
|
||||
|
||||
Expect(PropertyMapWrite(propertyTestPlugin, propertyTestApp, propertyTestName, nil)).To(Succeed())
|
||||
|
||||
b, err := os.ReadFile(getPropertyPath(propertyTestPlugin, propertyTestApp, propertyTestName))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(string(b)).To(Equal("{}"))
|
||||
}
|
||||
|
||||
func TestPropertyMapGetMissingReturnsEmpty(t *testing.T) {
|
||||
setupPropertyMapTest(t)
|
||||
|
||||
got, err := PropertyMapGet(propertyTestPlugin, propertyTestApp, "chart-overrides.never-written")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(got).To(BeEmpty())
|
||||
}
|
||||
|
||||
func TestPropertyMapSetCreatesMissingFile(t *testing.T) {
|
||||
setupPropertyMapTest(t)
|
||||
|
||||
Expect(PropertyExists(propertyTestPlugin, propertyTestApp, propertyTestName)).To(BeFalse())
|
||||
Expect(PropertyMapSet(propertyTestPlugin, propertyTestApp, propertyTestName, "version", "1.0")).To(Succeed())
|
||||
Expect(PropertyExists(propertyTestPlugin, propertyTestApp, propertyTestName)).To(BeTrue())
|
||||
|
||||
got, err := PropertyMapGet(propertyTestPlugin, propertyTestApp, propertyTestName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(got).To(Equal(map[string]string{"version": "1.0"}))
|
||||
}
|
||||
|
||||
func TestPropertyMapDeleteRemovesKeyOnly(t *testing.T) {
|
||||
setupPropertyMapTest(t)
|
||||
|
||||
Expect(PropertyMapSet(propertyTestPlugin, propertyTestApp, propertyTestName, "version", "1.0")).To(Succeed())
|
||||
Expect(PropertyMapSet(propertyTestPlugin, propertyTestApp, propertyTestName, "installCRDs", "false")).To(Succeed())
|
||||
|
||||
Expect(PropertyMapDelete(propertyTestPlugin, propertyTestApp, propertyTestName, "version")).To(Succeed())
|
||||
|
||||
got, err := PropertyMapGet(propertyTestPlugin, propertyTestApp, propertyTestName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(got).To(Equal(map[string]string{"installCRDs": "false"}))
|
||||
}
|
||||
|
||||
func TestPropertyMapDeleteMissingKeyIsNoOp(t *testing.T) {
|
||||
setupPropertyMapTest(t)
|
||||
|
||||
Expect(PropertyMapSet(propertyTestPlugin, propertyTestApp, propertyTestName, "version", "1.0")).To(Succeed())
|
||||
|
||||
Expect(PropertyMapDelete(propertyTestPlugin, propertyTestApp, propertyTestName, "missing")).To(Succeed())
|
||||
Expect(PropertyMapDelete(propertyTestPlugin, propertyTestApp, "chart-overrides.never-written", "anything")).To(Succeed())
|
||||
|
||||
got, err := PropertyMapGet(propertyTestPlugin, propertyTestApp, propertyTestName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(got).To(Equal(map[string]string{"version": "1.0"}))
|
||||
}
|
||||
|
||||
func TestPropertyMapLength(t *testing.T) {
|
||||
setupPropertyMapTest(t)
|
||||
|
||||
length, err := PropertyMapLength(propertyTestPlugin, propertyTestApp, "chart-overrides.never-written")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(length).To(Equal(0))
|
||||
|
||||
Expect(PropertyMapSet(propertyTestPlugin, propertyTestApp, propertyTestName, "a", "1")).To(Succeed())
|
||||
Expect(PropertyMapSet(propertyTestPlugin, propertyTestApp, propertyTestName, "b", "2")).To(Succeed())
|
||||
|
||||
length, err = PropertyMapLength(propertyTestPlugin, propertyTestApp, propertyTestName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(length).To(Equal(2))
|
||||
}
|
||||
|
||||
func TestPropertyMapRewriteIsStable(t *testing.T) {
|
||||
setupPropertyMapTest(t)
|
||||
|
||||
Expect(PropertyMapSet(propertyTestPlugin, propertyTestApp, propertyTestName, "version", "1.0")).To(Succeed())
|
||||
Expect(PropertyMapSet(propertyTestPlugin, propertyTestApp, propertyTestName, "version", "1.0")).To(Succeed())
|
||||
|
||||
got, err := PropertyMapGet(propertyTestPlugin, propertyTestApp, propertyTestName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(got).To(Equal(map[string]string{"version": "1.0"}))
|
||||
}
|
||||
|
||||
func TestPropertyMapFileMode(t *testing.T) {
|
||||
setupPropertyMapTest(t)
|
||||
|
||||
Expect(PropertyMapSet(propertyTestPlugin, propertyTestApp, propertyTestName, "version", "1.0")).To(Succeed())
|
||||
|
||||
fi, err := os.Stat(filepath.Clean(getPropertyPath(propertyTestPlugin, propertyTestApp, propertyTestName)))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(fi.Mode().Perm()).To(Equal(os.FileMode(0600)))
|
||||
}
|
||||
|
||||
func TestPropertyMapMalformedJSONReturnsError(t *testing.T) {
|
||||
setupPropertyMapTest(t)
|
||||
|
||||
Expect(PropertyMapWrite(propertyTestPlugin, propertyTestApp, propertyTestName, map[string]string{"k": "v"})).To(Succeed())
|
||||
Expect(os.WriteFile(getPropertyPath(propertyTestPlugin, propertyTestApp, propertyTestName), []byte("not-json"), 0600)).To(Succeed())
|
||||
|
||||
_, err := PropertyMapGet(propertyTestPlugin, propertyTestApp, propertyTestName)
|
||||
Expect(err).To(HaveOccurred())
|
||||
}
|
||||
275
plugins/common/report_test.go
Normal file
275
plugins/common/report_test.go
Normal file
@@ -0,0 +1,275 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReportSingleAppInputValidate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
baseValid := ReportSingleAppInput{
|
||||
ReportType: "ps",
|
||||
AppName: "myapp",
|
||||
InfoFlags: map[string]string{},
|
||||
Format: "stdout",
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input ReportSingleAppInput
|
||||
wantError string
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
input: ReportSingleAppInput{
|
||||
ReportType: "ps",
|
||||
AppName: "myapp",
|
||||
InfoFlags: map[string]string{"--ps-restart-policy": "always"},
|
||||
Format: "stdout",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid empty format defaults to stdout",
|
||||
input: ReportSingleAppInput{
|
||||
ReportType: "ps",
|
||||
AppName: "myapp",
|
||||
InfoFlags: map[string]string{},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing ReportType",
|
||||
input: mut(baseValid, func(i *ReportSingleAppInput) { i.ReportType = "" }),
|
||||
wantError: "ReportType is required",
|
||||
},
|
||||
{
|
||||
name: "ReportType with space rejected",
|
||||
input: mut(baseValid, func(i *ReportSingleAppInput) { i.ReportType = "docker options" }),
|
||||
wantError: "ReportType must not contain whitespace",
|
||||
},
|
||||
{
|
||||
name: "ReportType with tab rejected",
|
||||
input: mut(baseValid, func(i *ReportSingleAppInput) { i.ReportType = "foo\tbar" }),
|
||||
wantError: "ReportType must not contain whitespace",
|
||||
},
|
||||
{
|
||||
name: "ReportType beginning with -- rejected",
|
||||
input: mut(baseValid, func(i *ReportSingleAppInput) { i.ReportType = "--ps" }),
|
||||
wantError: "ReportType must not begin with --",
|
||||
},
|
||||
{
|
||||
name: "missing AppName",
|
||||
input: mut(baseValid, func(i *ReportSingleAppInput) { i.AppName = "" }),
|
||||
wantError: "AppName is required",
|
||||
},
|
||||
{
|
||||
name: "nil InfoFlags",
|
||||
input: mut(baseValid, func(i *ReportSingleAppInput) { i.InfoFlags = nil }),
|
||||
wantError: "InfoFlags is required",
|
||||
},
|
||||
{
|
||||
name: "invalid Format",
|
||||
input: mut(baseValid, func(i *ReportSingleAppInput) { i.Format = "yaml" }),
|
||||
wantError: "Format must be",
|
||||
},
|
||||
{
|
||||
name: "json with info flag rejected",
|
||||
input: ReportSingleAppInput{
|
||||
ReportType: "ps",
|
||||
AppName: "myapp",
|
||||
InfoFlags: map[string]string{},
|
||||
Format: "json",
|
||||
InfoFlag: "--ps-restart-policy",
|
||||
},
|
||||
wantError: "--format flag cannot be specified when specifying an info flag",
|
||||
},
|
||||
{
|
||||
name: "EmitLegacyPrefix without TrimPrefix rejected",
|
||||
input: ReportSingleAppInput{
|
||||
ReportType: "ps",
|
||||
AppName: "myapp",
|
||||
InfoFlags: map[string]string{},
|
||||
Format: "stdout",
|
||||
EmitLegacyPrefix: true,
|
||||
TrimPrefix: false,
|
||||
},
|
||||
wantError: "EmitLegacyPrefix has no effect when TrimPrefix is false",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := tc.input.Validate()
|
||||
if tc.wantError == "" {
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatalf("expected error %q, got nil", tc.wantError)
|
||||
}
|
||||
if !strings.Contains(err.Error(), tc.wantError) {
|
||||
t.Fatalf("expected error to contain %q, got %q", tc.wantError, err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReportSingleAppJSONKeys(t *testing.T) {
|
||||
infoFlags := map[string]string{
|
||||
"--ps-stop-timeout-seconds": "30",
|
||||
"--ps-restart-policy": "always",
|
||||
"--deployed": "true",
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
trimPrefix bool
|
||||
emitLegacyPrefix bool
|
||||
wantKeys map[string]string
|
||||
}{
|
||||
{
|
||||
name: "trimPrefix=false: prefixed keys only",
|
||||
trimPrefix: false,
|
||||
emitLegacyPrefix: false,
|
||||
wantKeys: map[string]string{
|
||||
"ps-stop-timeout-seconds": "30",
|
||||
"ps-restart-policy": "always",
|
||||
"deployed": "true",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "trimPrefix=true emitLegacy=false: stripped keys only",
|
||||
trimPrefix: true,
|
||||
emitLegacyPrefix: false,
|
||||
wantKeys: map[string]string{
|
||||
"stop-timeout-seconds": "30",
|
||||
"restart-policy": "always",
|
||||
"deployed": "true",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "trimPrefix=true emitLegacy=true: both shapes",
|
||||
trimPrefix: true,
|
||||
emitLegacyPrefix: true,
|
||||
wantKeys: map[string]string{
|
||||
"stop-timeout-seconds": "30",
|
||||
"restart-policy": "always",
|
||||
"deployed": "true",
|
||||
"ps-stop-timeout-seconds": "30",
|
||||
"ps-restart-policy": "always",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
out := captureStdout(t, func() {
|
||||
err := ReportSingleApp(ReportSingleAppInput{
|
||||
ReportType: "ps",
|
||||
AppName: "myapp",
|
||||
InfoFlags: infoFlags,
|
||||
Format: "json",
|
||||
TrimPrefix: tc.trimPrefix,
|
||||
EmitLegacyPrefix: tc.emitLegacyPrefix,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("ReportSingleApp returned error: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
var got map[string]string
|
||||
if err := json.Unmarshal([]byte(strings.TrimSpace(out)), &got); err != nil {
|
||||
t.Fatalf("output is not valid JSON: %v\noutput: %s", err, out)
|
||||
}
|
||||
|
||||
if len(got) != len(tc.wantKeys) {
|
||||
t.Fatalf("expected %d keys, got %d\nwant: %v\ngot: %v", len(tc.wantKeys), len(got), tc.wantKeys, got)
|
||||
}
|
||||
for k, v := range tc.wantKeys {
|
||||
if got[k] != v {
|
||||
t.Errorf("expected %q=%q, got %q=%q", k, v, k, got[k])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReportSingleAppJSONNonPluginPrefixKey(t *testing.T) {
|
||||
// Keys that don't start with --<reportType>- should pass through with
|
||||
// only the leading "--" stripped, regardless of TrimPrefix.
|
||||
infoFlags := map[string]string{
|
||||
"--build-id": "abc123",
|
||||
"--builds-retention": "5",
|
||||
}
|
||||
|
||||
out := captureStdout(t, func() {
|
||||
err := ReportSingleApp(ReportSingleAppInput{
|
||||
ReportType: "builds",
|
||||
AppName: "myapp",
|
||||
InfoFlags: infoFlags,
|
||||
Format: "json",
|
||||
TrimPrefix: true,
|
||||
EmitLegacyPrefix: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("ReportSingleApp returned error: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
var got map[string]string
|
||||
if err := json.Unmarshal([]byte(strings.TrimSpace(out)), &got); err != nil {
|
||||
t.Fatalf("output is not valid JSON: %v", err)
|
||||
}
|
||||
|
||||
if got["build-id"] != "abc123" {
|
||||
t.Errorf("expected build-id=abc123, got %q", got["build-id"])
|
||||
}
|
||||
if got["retention"] != "5" {
|
||||
t.Errorf("expected retention=5 (stripped), got %q", got["retention"])
|
||||
}
|
||||
if got["builds-retention"] != "5" {
|
||||
t.Errorf("expected builds-retention=5 (legacy), got %q", got["builds-retention"])
|
||||
}
|
||||
if _, present := got["uilds-retention"]; present {
|
||||
t.Error("found malformed key uilds-retention - prefix stripping was too aggressive")
|
||||
}
|
||||
}
|
||||
|
||||
func captureStdout(t *testing.T, fn func()) string {
|
||||
t.Helper()
|
||||
orig := os.Stdout
|
||||
r, w, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatalf("os.Pipe: %v", err)
|
||||
}
|
||||
os.Stdout = w
|
||||
|
||||
done := make(chan string)
|
||||
go func() {
|
||||
var buf bytes.Buffer
|
||||
_, _ = io.Copy(&buf, r)
|
||||
done <- buf.String()
|
||||
}()
|
||||
|
||||
fn()
|
||||
|
||||
w.Close()
|
||||
os.Stdout = orig
|
||||
return <-done
|
||||
}
|
||||
|
||||
func mut(base ReportSingleAppInput, f func(*ReportSingleAppInput)) ReportSingleAppInput {
|
||||
cp := base
|
||||
f(&cp)
|
||||
return cp
|
||||
}
|
||||
@@ -25,7 +25,11 @@ func setupTests() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Setenv("PLUGIN_ENABLED_PATH", "/var/lib/dokku/plugins/enabled")
|
||||
if err := os.Setenv("PLUGIN_ENABLED_PATH", "/var/lib/dokku/plugins/enabled"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Setenv("PLUGIN_CORE_AVAILABLE_PATH", "/var/lib/dokku/core-plugins/available")
|
||||
}
|
||||
|
||||
func setupTestApp() (err error) {
|
||||
|
||||
@@ -5,7 +5,7 @@ go 1.26.2
|
||||
require (
|
||||
github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/onsi/gomega v1.41.0
|
||||
github.com/onsi/gomega v1.42.1
|
||||
github.com/ryanuber/columnize v2.1.2+incompatible
|
||||
github.com/spf13/pflag v1.0.10
|
||||
)
|
||||
@@ -19,17 +19,17 @@ require (
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/melbahja/goph v1.5.0 // indirect
|
||||
github.com/melbahja/goph v1.5.1 // indirect
|
||||
github.com/otiai10/copy v1.14.1 // indirect
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/sftp v1.13.10 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.52.0 // indirect
|
||||
golang.org/x/net v0.54.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
golang.org/x/text v0.37.0 // indirect
|
||||
golang.org/x/crypto v0.53.0 // indirect
|
||||
golang.org/x/net v0.56.0 // indirect
|
||||
golang.org/x/sync v0.21.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
golang.org/x/text v0.38.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/dokku/dokku/plugins/common => ../common
|
||||
|
||||
@@ -14,18 +14,14 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/melbahja/goph v1.5.0 h1:RQUBpLvfg3i7fjfG8rTcSWyMjVRfdhwrrfQhjYee4dQ=
|
||||
github.com/melbahja/goph v1.5.0/go.mod h1:dDwo+44cmvfDLdiVpc6fJxexf5BA5yEDUeE5YgtuDO4=
|
||||
github.com/onsi/gomega v1.41.0 h1:OwKp4pXNgVxf6sCplzYo794OFNuoL2q2SBMU5NSWOjA=
|
||||
github.com/onsi/gomega v1.41.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A=
|
||||
github.com/melbahja/goph v1.5.1 h1:gdZyjn5nM6FO6mAZrv7XqE2gArQbb+V6FdIyH3Ru3vA=
|
||||
github.com/melbahja/goph v1.5.1/go.mod h1:dDwo+44cmvfDLdiVpc6fJxexf5BA5yEDUeE5YgtuDO4=
|
||||
github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I=
|
||||
github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg=
|
||||
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
||||
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
|
||||
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
||||
@@ -36,8 +32,6 @@ github.com/pkg/sftp v1.13.10 h1:+5FbKNTe5Z9aspU88DPIKJ9z2KZoaGCu6Sr6kKR/5mU=
|
||||
github.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1HbeGA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk=
|
||||
github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
@@ -46,21 +40,20 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[plugin]
|
||||
description = "dokku core config plugin"
|
||||
version = "0.38.6"
|
||||
version = "0.38.20"
|
||||
[plugin.config]
|
||||
|
||||
@@ -10,8 +10,20 @@ import (
|
||||
|
||||
"github.com/multiformats/go-base36"
|
||||
cronparser "github.com/robfig/cron/v3"
|
||||
"mvdan.cc/sh/v3/shell"
|
||||
)
|
||||
|
||||
// ValidateCronCommand returns an error if the command cannot be tokenised
|
||||
// as a sequence of shell words. cron:run dispatches with the same parser,
|
||||
// so a command that fails here will also fail to execute - validating at
|
||||
// deploy time surfaces the error before the schedule fires.
|
||||
func ValidateCronCommand(command string) error {
|
||||
_, err := shell.Fields(command, func(name string) string {
|
||||
return ""
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
// DefaultProperties is a map of all valid cron properties with corresponding default property values
|
||||
DefaultProperties = map[string]string{
|
||||
@@ -75,7 +87,7 @@ func (t CronTask) DokkuRunCommand() string {
|
||||
return t.AltCommand
|
||||
}
|
||||
|
||||
return fmt.Sprintf("dokku run --concurrency-policy %s --cron-id %s %s %s", t.ConcurrencyPolicy, t.ID, t.App, t.Command)
|
||||
return fmt.Sprintf("dokku cron:run %s %s", t.App, t.ID)
|
||||
}
|
||||
|
||||
// FetchCronTasksInput is the input for the FetchCronTasks function
|
||||
@@ -138,6 +150,10 @@ func FetchCronTasks(input FetchCronTasksInput) ([]CronTask, error) {
|
||||
return tasks, fmt.Errorf("Invalid cron schedule for app %s (schedule %s): %s", appName, c.Schedule, err.Error())
|
||||
}
|
||||
|
||||
if err := ValidateCronCommand(c.Command); err != nil {
|
||||
return tasks, fmt.Errorf("Invalid cron command for app %s (command %q): %s", appName, c.Command, err.Error())
|
||||
}
|
||||
|
||||
cronID := GenerateCommandID(appName, c)
|
||||
maintenance := c.Maintenance
|
||||
if value, ok := properties[MaintenancePropertyPrefix+cronID]; ok {
|
||||
|
||||
102
plugins/cron/cron_test.go
Normal file
102
plugins/cron/cron_test.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDokkuRunCommandAppTaskDispatchesViaCronRun(t *testing.T) {
|
||||
task := CronTask{
|
||||
App: "myapp",
|
||||
ID: "abc123",
|
||||
Command: "echo CRON_OK; echo hi > /tmp/appjson-test.txt",
|
||||
Schedule: "* * * * *",
|
||||
ConcurrencyPolicy: "allow",
|
||||
}
|
||||
|
||||
got := task.DokkuRunCommand()
|
||||
want := "dokku cron:run myapp abc123"
|
||||
if got != want {
|
||||
t.Errorf("DokkuRunCommand() = %q, want %q", got, want)
|
||||
}
|
||||
|
||||
if strings.Contains(got, task.Command) {
|
||||
t.Errorf("DokkuRunCommand() leaked user command into crontab line: %q", got)
|
||||
}
|
||||
if strings.ContainsAny(got, ";>|&`$") {
|
||||
t.Errorf("DokkuRunCommand() contains shell metacharacters: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDokkuRunCommandPlainCommandStillUsesCronRun(t *testing.T) {
|
||||
task := CronTask{
|
||||
App: "myapp",
|
||||
ID: "abc123",
|
||||
Command: "npm run send-email",
|
||||
Schedule: "@daily",
|
||||
ConcurrencyPolicy: "forbid",
|
||||
}
|
||||
|
||||
got := task.DokkuRunCommand()
|
||||
want := "dokku cron:run myapp abc123"
|
||||
if got != want {
|
||||
t.Errorf("DokkuRunCommand() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDokkuRunCommandAltCommandUnchanged(t *testing.T) {
|
||||
task := CronTask{
|
||||
ID: "abc123",
|
||||
AltCommand: "/usr/bin/some-internal-task --flag",
|
||||
}
|
||||
|
||||
got := task.DokkuRunCommand()
|
||||
want := "/usr/bin/some-internal-task --flag"
|
||||
if got != want {
|
||||
t.Errorf("DokkuRunCommand() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateCronCommandAcceptsValidCommands(t *testing.T) {
|
||||
cases := []string{
|
||||
"python3 task.py schedule",
|
||||
"npm run send-email",
|
||||
"sh -c 'echo CRON_OK; echo hi > /tmp/x.txt'",
|
||||
`node -e 'console.log(1)'`,
|
||||
"true",
|
||||
}
|
||||
for _, cmd := range cases {
|
||||
if err := ValidateCronCommand(cmd); err != nil {
|
||||
t.Errorf("ValidateCronCommand(%q) returned error: %v", cmd, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateCronCommandRejectsShellOperators(t *testing.T) {
|
||||
cases := []string{
|
||||
"echo CRON_OK; echo hi > /tmp/x.txt",
|
||||
"cmd1 && cmd2",
|
||||
"cmd | other",
|
||||
"cmd > file",
|
||||
"cmd $(other)",
|
||||
}
|
||||
for _, cmd := range cases {
|
||||
if err := ValidateCronCommand(cmd); err == nil {
|
||||
t.Errorf("ValidateCronCommand(%q) accepted a command containing a shell operator", cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDokkuRunCommandAltCommandWithLogFile(t *testing.T) {
|
||||
task := CronTask{
|
||||
ID: "abc123",
|
||||
AltCommand: "/usr/bin/some-internal-task",
|
||||
LogFile: "/var/log/dokku/internal-task.log",
|
||||
}
|
||||
|
||||
got := task.DokkuRunCommand()
|
||||
want := "/usr/bin/some-internal-task &>> /var/log/dokku/internal-task.log"
|
||||
if got != want {
|
||||
t.Errorf("DokkuRunCommand() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
@@ -21,15 +21,15 @@ require (
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.22 // indirect
|
||||
github.com/melbahja/goph v1.5.0 // indirect
|
||||
github.com/melbahja/goph v1.5.1 // indirect
|
||||
github.com/otiai10/copy v1.14.1 // indirect
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/sftp v1.13.10 // indirect
|
||||
github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a // indirect
|
||||
golang.org/x/crypto v0.52.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
golang.org/x/crypto v0.53.0 // indirect
|
||||
golang.org/x/sync v0.21.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
k8s.io/utils v0.0.0-20240102154912-e7106e64919e // indirect
|
||||
)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user