Compare commits
132 Commits
version-10
...
compat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09060341c7 | ||
|
|
e1f32b7476 | ||
|
|
cc5c0b6485 | ||
|
|
66fca59f74 | ||
|
|
3ba01027de | ||
|
|
8e34d092f8 | ||
|
|
05853825d8 | ||
|
|
b02191f3f8 | ||
|
|
55a4dc9269 | ||
|
|
7404bcadee | ||
|
|
18a16372b2 | ||
|
|
42bfd485f8 | ||
|
|
52f91b318c | ||
|
|
a46a97dc81 | ||
|
|
d573ba8769 | ||
|
|
4daa31e1cf | ||
|
|
c2b1543128 | ||
|
|
adaa753d89 | ||
|
|
e665b014ca | ||
|
|
6b93efdf3c | ||
|
|
407ac2f836 | ||
|
|
db7823a2e2 | ||
|
|
3171f212ab | ||
|
|
0b861de117 | ||
|
|
be7c6254f8 | ||
|
|
1c9c4bd780 | ||
|
|
fb07581595 | ||
|
|
8b4dfce4f0 | ||
|
|
f8b7b7af05 | ||
|
|
c4772bc5b5 | ||
|
|
3e4e66769b | ||
|
|
9f0d602c72 | ||
|
|
2ac2b7ce43 | ||
|
|
65f6228be8 | ||
|
|
e629dd313e | ||
|
|
399414bcab | ||
|
|
6378473c43 | ||
|
|
442c767338 | ||
|
|
a48ae2d12e | ||
|
|
81ec8da94f | ||
|
|
180b9ababc | ||
|
|
9b49391dbb | ||
|
|
c87ee80a22 | ||
|
|
aadb0a8139 | ||
|
|
0af1302485 | ||
|
|
9a986575f6 | ||
|
|
5aae67117c | ||
|
|
d049e33b40 | ||
|
|
a9c3f6e9b9 | ||
|
|
e4ab6945a4 | ||
|
|
1427907a82 | ||
|
|
91648299ba | ||
|
|
d68bcdc8bd | ||
|
|
8fc3fbb27e | ||
|
|
4f7108c3a8 | ||
|
|
cb2943bdf7 | ||
|
|
318f65170a | ||
|
|
3295c8070e | ||
|
|
6b6a1b9ad8 | ||
|
|
9bfb3abde4 | ||
|
|
593dfe444a | ||
|
|
abe6d670c4 | ||
|
|
56c3341d74 | ||
|
|
c04edf8b6b | ||
|
|
8634460315 | ||
|
|
ef31d8e025 | ||
|
|
2fa61e9868 | ||
|
|
ccc3fc2707 | ||
|
|
b7d990b2a5 | ||
|
|
d320db2d04 | ||
|
|
02d8ce6d21 | ||
|
|
cbf646b326 | ||
|
|
a355d8a48f | ||
|
|
6a8440be34 | ||
|
|
c0a4079cdf | ||
|
|
2c97ffd579 | ||
|
|
7c157aa487 | ||
|
|
f0997dbf23 | ||
|
|
5a38d66a70 | ||
|
|
a2db23e68e | ||
|
|
5254e2aad3 | ||
|
|
b57371edf3 | ||
|
|
fe47a4b165 | ||
|
|
f74f730b5e | ||
|
|
358293afc9 | ||
|
|
d1ed880c78 | ||
|
|
5aa6e4ec4a | ||
|
|
e4df6e5aec | ||
|
|
457ecc513b | ||
|
|
9d41a6d24e | ||
|
|
de04eb032c | ||
|
|
089a2dc206 | ||
|
|
daa1926f03 | ||
|
|
ce1ed546ab | ||
|
|
8ad84719ea | ||
|
|
a8ab724212 | ||
|
|
c808ad1767 | ||
|
|
9a4aa8d226 | ||
|
|
9fa3e1b0e0 | ||
|
|
d2b18a3b48 | ||
|
|
38c7f1e269 | ||
|
|
de255d8052 | ||
|
|
915885bb05 | ||
|
|
3c0f08652e | ||
|
|
e685dc5ebc | ||
|
|
a4c115f96d | ||
|
|
33ec8cafc3 | ||
|
|
63da01c702 | ||
|
|
2da9c02d58 | ||
|
|
37b8ccece1 | ||
|
|
08a542978f | ||
|
|
7d36be56f4 | ||
|
|
4c79d6d7c6 | ||
|
|
107f393277 | ||
|
|
47b2d34dd1 | ||
|
|
1daec15268 | ||
|
|
7518c2d374 | ||
|
|
20a119d7aa | ||
|
|
b18119574b | ||
|
|
a3cf159439 | ||
|
|
078a7f5602 | ||
|
|
1cbb845af1 | ||
|
|
c149a93f31 | ||
|
|
90e973090e | ||
|
|
401613900f | ||
|
|
9d3e66528d | ||
|
|
48e9947580 | ||
|
|
428b8abffe | ||
|
|
7fc0f06610 | ||
|
|
08442253c8 | ||
|
|
911c4ca6ce | ||
|
|
f5d9832ae1 |
19
.editorconfig
Normal file
19
.editorconfig
Normal file
@@ -0,0 +1,19 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{py, pyi}]
|
||||
indent_size = 4
|
||||
|
||||
[*Dockerfile*]
|
||||
indent_size = 4
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
@@ -9,4 +9,4 @@ Welcome to the frappe_docker issue tracker! Before creating an issue, please hee
|
||||
|
||||
1. Use the search function before creating a new issue. Duplicates will be closed and directed to the original discussion.
|
||||
2. Please write extensively, clearly and in detail.
|
||||
-->
|
||||
-->
|
||||
|
||||
41
.github/dependabot.yml
vendored
Normal file
41
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: build/bench
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: build/erpnext-nginx
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: build/erpnext-worker
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: build/frappe-nginx
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: build/frappe-socketio
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: build/frappe-worker
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: npm
|
||||
directory: build/frappe-socketio
|
||||
schedule:
|
||||
interval: daily
|
||||
20
.github/scripts/get-latest-tags.sh
vendored
Executable file
20
.github/scripts/get-latest-tags.sh
vendored
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
get_tag() {
|
||||
tags=$(git ls-remote --refs --tags --sort='v:refname' "https://github.com/$1" "v$2.*")
|
||||
tag=$(echo "$tags" | tail -n1 | sed 's/.*\///')
|
||||
echo "$tag"
|
||||
}
|
||||
|
||||
FRAPPE_VERSION=$(get_tag frappe/frappe "$VERSION")
|
||||
ERPNEXT_VERSION=$(get_tag frappe/erpnext "$VERSION")
|
||||
|
||||
cat <<EOL >>"$GITHUB_ENV"
|
||||
FRAPPE_VERSION=$FRAPPE_VERSION
|
||||
ERPNEXT_VERSION=$ERPNEXT_VERSION
|
||||
GIT_BRANCH=version-$VERSION
|
||||
VERSION=$VERSION
|
||||
EOL
|
||||
46
.github/workflows/build_bench.yml
vendored
Normal file
46
.github/workflows/build_bench.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Bench
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- build/bench/**
|
||||
- docker-bake.hcl
|
||||
|
||||
schedule:
|
||||
# Every day at 12:00 pm
|
||||
- cron: 0 0 * * *
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Build and test
|
||||
uses: docker/bake-action@v1.7.0
|
||||
with:
|
||||
files: docker-bake.hcl
|
||||
targets: bench-test
|
||||
|
||||
- name: Login
|
||||
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Push
|
||||
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
|
||||
uses: docker/bake-action@v1.7.0
|
||||
with:
|
||||
targets: bench
|
||||
files: docker-bake.hcl
|
||||
push: true
|
||||
83
.github/workflows/build_develop.yml
vendored
Normal file
83
.github/workflows/build_develop.yml
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
name: Build Develop
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- .github/workflows/build_develop.yml
|
||||
- build/**
|
||||
- installation/**
|
||||
- tests/**
|
||||
- .dockerignore
|
||||
- docker-bake.hcl
|
||||
- env-example
|
||||
|
||||
schedule:
|
||||
# Every day at 12:00 pm
|
||||
- cron: 0 0 * * *
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
IS_AUTHORIZED_RUN: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Frappe & ERPNext
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
driver-opts: network=host
|
||||
|
||||
- name: Login
|
||||
uses: docker/login-action@v1
|
||||
if: env.IS_AUTHORIZED_RUN == 'true'
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build Frappe
|
||||
uses: docker/bake-action@v1.7.0
|
||||
with:
|
||||
files: docker-bake.hcl
|
||||
targets: frappe-develop-test
|
||||
load: true
|
||||
|
||||
- name: Push Frappe to local registry
|
||||
uses: docker/bake-action@v1.7.0
|
||||
with:
|
||||
files: docker-bake.hcl
|
||||
targets: frappe-develop-test-local
|
||||
push: true
|
||||
|
||||
- name: Test Frappe
|
||||
run: ./tests/test-frappe.sh
|
||||
|
||||
- name: Build ERPNext
|
||||
uses: docker/bake-action@v1.7.0
|
||||
with:
|
||||
files: docker-bake.hcl
|
||||
targets: erpnext-develop-test
|
||||
load: true
|
||||
|
||||
- name: Test ERPNext
|
||||
run: ./tests/test-erpnext.sh
|
||||
|
||||
- name: Push
|
||||
if: env.IS_AUTHORIZED_RUN == 'true'
|
||||
uses: docker/bake-action@v1.7.0
|
||||
with:
|
||||
files: docker-bake.hcl
|
||||
targets: frappe-develop,erpnext-develop
|
||||
push: true
|
||||
141
.github/workflows/build_stable.yml
vendored
Normal file
141
.github/workflows/build_stable.yml
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
name: Build Stable
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- .github/workflows/build_stable.yml
|
||||
- .github/scripts/get-latest-tags.sh
|
||||
- build/**
|
||||
- installation/**
|
||||
- tests/**
|
||||
- .dockerignore
|
||||
- docker-bake.hcl
|
||||
- env-example
|
||||
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- .github/workflows/build_stable.yml
|
||||
- .github/scripts/get-latest-tags.sh
|
||||
- build/**
|
||||
- installation/**
|
||||
- tests/**
|
||||
- .dockerignore
|
||||
- docker-bake.hcl
|
||||
- env-example
|
||||
|
||||
# Triggered from frappe/frappe and frappe/erpnext on releases
|
||||
repository_dispatch:
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
IS_AUTHORIZED_RUN: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Frappe & ERPNext
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
version: [12, 13]
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
driver-opts: network=host
|
||||
|
||||
- name: Login
|
||||
uses: docker/login-action@v1
|
||||
if: env.IS_AUTHORIZED_RUN == 'true'
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Get latest versions
|
||||
run: ./.github/scripts/get-latest-tags.sh
|
||||
env:
|
||||
VERSION: ${{ matrix.version }}
|
||||
|
||||
- name: Build Frappe
|
||||
uses: docker/bake-action@v1.7.0
|
||||
with:
|
||||
files: docker-bake.hcl
|
||||
targets: frappe-stable-test
|
||||
load: true
|
||||
|
||||
- name: Push Frappe to local registry
|
||||
uses: docker/bake-action@v1.7.0
|
||||
with:
|
||||
files: docker-bake.hcl
|
||||
targets: frappe-stable-test-local
|
||||
push: true
|
||||
|
||||
- name: Test Frappe
|
||||
if: github.event_name == 'pull_request'
|
||||
run: ./tests/test-frappe.sh
|
||||
|
||||
- name: Build ERPNext
|
||||
uses: docker/bake-action@v1.7.0
|
||||
with:
|
||||
files: docker-bake.hcl
|
||||
targets: erpnext-stable-test
|
||||
load: true
|
||||
|
||||
- name: Test ERPNext
|
||||
if: github.event_name == 'pull_request'
|
||||
run: ./tests/test-erpnext.sh
|
||||
|
||||
- name: Push Frappe
|
||||
if: env.IS_AUTHORIZED_RUN == 'true'
|
||||
uses: docker/bake-action@v1.7.0
|
||||
with:
|
||||
files: docker-bake.hcl
|
||||
targets: frappe-stable
|
||||
push: true
|
||||
env:
|
||||
GIT_TAG: ${{ env.FRAPPE_VERSION }}
|
||||
|
||||
- name: Push ERPNext
|
||||
if: env.IS_AUTHORIZED_RUN == 'true'
|
||||
uses: docker/bake-action@v1.7.0
|
||||
with:
|
||||
files: docker-bake.hcl
|
||||
targets: erpnext-stable
|
||||
push: true
|
||||
env:
|
||||
GIT_TAG: ${{ env.ERPNEXT_VERSION }}
|
||||
|
||||
release_helm:
|
||||
name: Release Helm
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request'
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- name: Setup deploy key
|
||||
uses: webfactory/ssh-agent@v0.5.4
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.HELM_DEPLOY_KEY }}
|
||||
|
||||
- name: Setup Git Credentials
|
||||
run: |
|
||||
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git config --global user.name "github-actions[bot]"
|
||||
|
||||
- name: Release
|
||||
run: |
|
||||
git clone git@github.com:frappe/helm.git && cd helm
|
||||
pip install -r release_wizard/requirements.txt
|
||||
./release_wizard/wizard 13 patch --remote origin --ci
|
||||
41
.github/workflows/lint.yml
vendored
Normal file
41
.github/workflows/lint.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v3
|
||||
|
||||
# For shfmt pre-commit hook
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "^1.14"
|
||||
|
||||
- name: Cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cache/pre-commit
|
||||
~/.cache/pip
|
||||
key: lint-${{ hashFiles('.pre-commit-config.yaml') }}
|
||||
|
||||
- name: Install pre-commit
|
||||
run: pip install -U pre-commit
|
||||
|
||||
- name: Lint
|
||||
run: pre-commit run --color=always --all-files
|
||||
env:
|
||||
GO111MODULE: on
|
||||
26
.github/workflows/pre-commit-autoupdate.yml
vendored
Normal file
26
.github/workflows/pre-commit-autoupdate.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Autoupdate pre-commit hooks
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Every day at 7 am
|
||||
- cron: 0 7 * * *
|
||||
|
||||
jobs:
|
||||
pre-commit-autoupdate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Update pre-commit hooks
|
||||
uses: vrslev/pre-commit-autoupdate@v1.0.0
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v3
|
||||
with:
|
||||
branch: pre-commit-autoupdate
|
||||
title: "chore(deps): Update pre-commit hooks"
|
||||
commit-message: "chore(deps): Update pre-commit hooks"
|
||||
body: Update pre-commit hooks
|
||||
labels: dependencies,development
|
||||
delete-branch: True
|
||||
19
.github/workflows/stale.yml
vendored
19
.github/workflows/stale.yml
vendored
@@ -2,18 +2,17 @@ name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
# Every day at 12:00 pm
|
||||
- cron: 0 0 * * *
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue has been automatically marked as stale. You have a week to explain why you believe this is an error.'
|
||||
stale-pr-message: 'This PR has been automatically marked as stale. You have a week to explain why you believe this is an error.'
|
||||
stale-issue-label: 'no-issue-activity'
|
||||
stale-pr-label: 'no-pr-activity'
|
||||
- uses: actions/stale@v4
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: This issue has been automatically marked as stale. You have a week to explain why you believe this is an error.
|
||||
stale-pr-message: This PR has been automatically marked as stale. You have a week to explain why you believe this is an error.
|
||||
stale-issue-label: no-issue-activity
|
||||
stale-pr-label: no-pr-activity
|
||||
|
||||
62
.github/workflows/test.yml
vendored
Normal file
62
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
name: Integration Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- .github/workflows/test.yml
|
||||
- .github/scripts/get-latest-tags.sh
|
||||
- build/**
|
||||
- installation/**
|
||||
- tests/**
|
||||
- .dockerignore
|
||||
- docker-bake.hcl
|
||||
- env-example
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- .github/workflows/test.yml
|
||||
- .github/scripts/get-latest-tags.sh
|
||||
- build/**
|
||||
- installation/**
|
||||
- tests/**
|
||||
- .dockerignore
|
||||
- docker-bake.hcl
|
||||
- env-example
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
schedule:
|
||||
# Every day at 01:00 am
|
||||
# Develop images are built at 12:00 pm, we want to use them
|
||||
# Also, we don't build new images on this event
|
||||
- cron: 0 1 * * *
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Get latest versions
|
||||
if: github.event_name != 'schedule'
|
||||
run: ./.github/scripts/get-latest-tags.sh
|
||||
env:
|
||||
VERSION: 13
|
||||
|
||||
- name: Build
|
||||
if: github.event_name != 'schedule'
|
||||
uses: docker/bake-action@v1.7.0
|
||||
with:
|
||||
files: docker-bake.hcl
|
||||
targets: frappe-develop,frappe-stable
|
||||
load: true
|
||||
env:
|
||||
GIT_TAG: ${{ env.FRAPPE_VERSION }}
|
||||
|
||||
- name: Test
|
||||
run: ./tests/integration-test.sh
|
||||
48
.pre-commit-config.yaml
Normal file
48
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,48 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.1.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.31.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py37-plus]
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.1.0
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.10.1
|
||||
hooks:
|
||||
- id: isort
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v2.5.1
|
||||
hooks:
|
||||
- id: prettier
|
||||
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.1.0
|
||||
hooks:
|
||||
- id: codespell
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: shfmt
|
||||
name: shfmt
|
||||
language: golang
|
||||
additional_dependencies: [mvdan.cc/sh/v3/cmd/shfmt@latest]
|
||||
entry: shfmt
|
||||
args: [-w]
|
||||
types: [shell]
|
||||
|
||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||
rev: v0.8.0.4
|
||||
hooks:
|
||||
- id: shellcheck
|
||||
args: [-x]
|
||||
1
.prettierignore
Normal file
1
.prettierignore
Normal file
@@ -0,0 +1 @@
|
||||
installation/docker-compose-custom.yml
|
||||
1
.shellcheckrc
Normal file
1
.shellcheckrc
Normal file
@@ -0,0 +1 @@
|
||||
external-sources=true
|
||||
187
.travis.yml
187
.travis.yml
@@ -1,187 +0,0 @@
|
||||
sudo: required
|
||||
|
||||
dist: bionic
|
||||
|
||||
language: python
|
||||
|
||||
python:
|
||||
- '3.6'
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
before_install:
|
||||
- if [[ "$BUILD" != "development" ]]; then
|
||||
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin;
|
||||
sudo apt-get update && sudo apt-get -y install git;
|
||||
fi
|
||||
- if [[ $BUILD == "development" ]];then
|
||||
sudo apt-get update && sudo apt-get -y install docker-compose;
|
||||
fi
|
||||
- chmod u+x ./travis.py
|
||||
|
||||
after_success:
|
||||
- docker --version
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: "Build Frappe bench development environment (latest)"
|
||||
if: branch = develop AND type != pull_request
|
||||
script:
|
||||
- docker build -t frappe/bench:latest -f build/bench/Dockerfile .
|
||||
- docker push frappe/bench:latest
|
||||
- stage: "Frappe (edge)"
|
||||
if: branch = develop AND type != pull_request
|
||||
script:
|
||||
- ./travis.py frappe --worker --tag latest
|
||||
- ./travis.py frappe --worker --tag edge --tag-only
|
||||
- ./travis.py frappe --worker --tag develop --tag-only
|
||||
- stage: "Frappe (edge)"
|
||||
if: branch = develop AND type != pull_request
|
||||
script:
|
||||
- ./travis.py frappe --nginx --tag latest
|
||||
- ./travis.py frappe --nginx --tag edge --tag-only
|
||||
- ./travis.py frappe --nginx --tag develop --tag-only
|
||||
- stage: "Frappe (edge)"
|
||||
if: branch = develop AND type != pull_request
|
||||
script:
|
||||
- ./travis.py frappe --socketio --tag latest
|
||||
- ./travis.py frappe --socketio --tag edge --tag-only
|
||||
- ./travis.py frappe --socketio --tag develop --tag-only
|
||||
- stage: "ERPNext (edge)"
|
||||
if: branch = develop AND type != pull_request
|
||||
script:
|
||||
- ./travis.py erpnext --worker --tag latest
|
||||
- ./travis.py erpnext --worker --tag edge --tag-only
|
||||
- ./travis.py erpnext --worker --tag develop --tag-only
|
||||
- stage: "ERPNext (edge)"
|
||||
if: branch = develop AND type != pull_request
|
||||
script:
|
||||
- ./travis.py erpnext --nginx --tag latest
|
||||
- ./travis.py erpnext --nginx --tag edge --tag-only
|
||||
- ./travis.py erpnext --nginx --tag develop --tag-only
|
||||
- stage: "Frappe (v13)"
|
||||
if: branch = master AND type != pull_request
|
||||
script:
|
||||
- ./travis.py frappe --worker --git-version 13
|
||||
- ./travis.py frappe --worker --tag v13 --tag-only
|
||||
- ./travis.py frappe --worker --tag version-13 --tag-only
|
||||
- stage: "Frappe (v13)"
|
||||
if: branch = master AND type != pull_request
|
||||
script:
|
||||
- ./travis.py frappe --nginx --git-version 13
|
||||
- ./travis.py frappe --nginx --tag v13 --tag-only
|
||||
- ./travis.py frappe --nginx --tag version-13 --tag-only
|
||||
- stage: "Frappe (v13)"
|
||||
if: branch = master AND type != pull_request
|
||||
script:
|
||||
- ./travis.py frappe --socketio --git-version 13
|
||||
- ./travis.py frappe --socketio --tag v13 --tag-only
|
||||
- ./travis.py frappe --socketio --tag version-13 --tag-only
|
||||
- stage: "ERPNext (v13)"
|
||||
if: branch = master AND type != pull_request
|
||||
script:
|
||||
- ./travis.py erpnext --worker --git-version 13
|
||||
- ./travis.py erpnext --worker --tag v13 --tag-only
|
||||
- ./travis.py erpnext --worker --tag version-13 --tag-only
|
||||
- stage: "ERPNext (v13)"
|
||||
if: branch = master AND type != pull_request
|
||||
script:
|
||||
- ./travis.py erpnext --nginx --git-version 13
|
||||
- ./travis.py erpnext --nginx --tag v13 --tag-only
|
||||
- ./travis.py erpnext --nginx --tag version-13 --tag-only
|
||||
- stage: "Frappe (v12)"
|
||||
if: branch = master AND type != pull_request
|
||||
script:
|
||||
- ./travis.py frappe --worker --git-version 12
|
||||
- ./travis.py frappe --worker --tag v12 --tag-only
|
||||
- ./travis.py frappe --worker --tag version-12 --tag-only
|
||||
- stage: "Frappe (v12)"
|
||||
if: branch = master AND type != pull_request
|
||||
script:
|
||||
- ./travis.py frappe --nginx --git-version 12
|
||||
- ./travis.py frappe --nginx --tag v12 --tag-only
|
||||
- ./travis.py frappe --nginx --tag version-12 --tag-only
|
||||
- stage: "Frappe (v12)"
|
||||
if: branch = master AND type != pull_request
|
||||
script:
|
||||
- ./travis.py frappe --socketio --git-version 12
|
||||
- ./travis.py frappe --socketio --tag v12 --tag-only
|
||||
- ./travis.py frappe --socketio --tag version-12 --tag-only
|
||||
- stage: "ERPNext (v12)"
|
||||
if: branch = master AND type != pull_request
|
||||
script:
|
||||
- ./travis.py erpnext --worker --git-version 12
|
||||
- ./travis.py erpnext --worker --tag v12 --tag-only
|
||||
- ./travis.py erpnext --worker --tag version-12 --tag-only
|
||||
- stage: "ERPNext (v12)"
|
||||
if: branch = master AND type != pull_request
|
||||
script:
|
||||
- ./travis.py erpnext --nginx --git-version 12
|
||||
- ./travis.py erpnext --nginx --tag v12 --tag-only
|
||||
- ./travis.py erpnext --nginx --tag version-12 --tag-only
|
||||
- stage: "Frappe (v11)"
|
||||
if: branch = master AND type != pull_request
|
||||
script:
|
||||
- ./travis.py frappe --worker --git-version 11
|
||||
- ./travis.py frappe --worker --tag v11 --tag-only
|
||||
- ./travis.py frappe --worker --tag version-11 --tag-only
|
||||
- stage: "Frappe (v11)"
|
||||
if: branch = master AND type != pull_request
|
||||
script:
|
||||
- ./travis.py frappe --nginx --git-version 11
|
||||
- ./travis.py frappe --nginx --tag v11 --tag-only
|
||||
- ./travis.py frappe --nginx --tag version-11 --tag-only
|
||||
- stage: "Frappe (v11)"
|
||||
if: branch = master AND type != pull_request
|
||||
script:
|
||||
- ./travis.py frappe --socketio --git-version 11
|
||||
- ./travis.py frappe --socketio --tag v11 --tag-only
|
||||
- ./travis.py frappe --socketio --tag version-11 --tag-only
|
||||
- stage: "ERPNext (v11)"
|
||||
if: branch = master AND type != pull_request
|
||||
script:
|
||||
- ./travis.py erpnext --worker --git-version 11
|
||||
- ./travis.py erpnext --worker --tag v11 --tag-only
|
||||
- ./travis.py erpnext --worker --tag version-11 --tag-only
|
||||
- stage: "ERPNext (v11)"
|
||||
if: branch = master AND type != pull_request
|
||||
script:
|
||||
- ./travis.py erpnext --nginx --git-version 11
|
||||
- ./travis.py erpnext --nginx --tag v11 --tag-only
|
||||
- ./travis.py erpnext --nginx --tag version-11 --tag-only
|
||||
- stage: "Build and test edge images"
|
||||
if: type = pull_request
|
||||
before_install:
|
||||
- sudo curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
- sudo chmod +x /usr/local/bin/docker-compose
|
||||
- sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
|
||||
- sudo apt-get update && sudo apt-get -y install w3m shellcheck
|
||||
script:
|
||||
- ./tests/check-format.sh
|
||||
- docker build -t frappe/frappe-socketio:edge -f build/frappe-socketio/Dockerfile .
|
||||
- docker build -t frappe/frappe-worker:develop -f build/frappe-worker/Dockerfile .
|
||||
- docker build -t frappe/erpnext-worker:edge -f build/erpnext-worker/Dockerfile .
|
||||
- docker build -t frappe/frappe-nginx:develop -f build/frappe-nginx/Dockerfile .
|
||||
- docker build -t frappe/erpnext-nginx:edge -f build/erpnext-nginx/Dockerfile .
|
||||
- ./tests/docker-test.sh
|
||||
- stage: "Pull and test edge images"
|
||||
if: branch = develop AND type != pull_request
|
||||
before_install:
|
||||
- sudo apt-get update && sudo apt-get -y install docker-compose w3m
|
||||
script:
|
||||
- ./tests/docker-test.sh
|
||||
- stage: "Helm Chart Release"
|
||||
if: branch = master AND type != pull_request
|
||||
env:
|
||||
- GIT_SSH_COMMAND="ssh -i ${TRAVIS_BUILD_DIR}/deploy_key"
|
||||
before_install:
|
||||
- openssl aes-256-cbc -K $encrypted_189e52c2c347_key -iv $encrypted_189e52c2c347_iv -in deploy_key.enc -out deploy_key -d;
|
||||
chmod 400 deploy_key;
|
||||
- ssh-keyscan github.com >> $HOME/.ssh/known_hosts 2>/dev/null;
|
||||
install:
|
||||
- pip install --upgrade pip
|
||||
script:
|
||||
- git clone git@github.com:frappe/helm.git && cd helm
|
||||
- pip install -r release_wizard/requirements.txt
|
||||
- ./release_wizard/wizard 13 patch --remote origin --ci
|
||||
18
.vscode/extensions.json
vendored
18
.vscode/extensions.json
vendored
@@ -1,13 +1,9 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
"ms-vscode-remote.remote-containers"
|
||||
],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": [
|
||||
|
||||
]
|
||||
}
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": ["ms-vscode-remote.remote-containers"],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": []
|
||||
}
|
||||
|
||||
@@ -14,22 +14,22 @@ appearance, race, religion, or sexual identity and orientation.
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
- The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
|
||||
106
CONTRIBUTING.md
106
CONTRIBUTING.md
@@ -1,33 +1,77 @@
|
||||
# Contribution Guidelines
|
||||
|
||||
## Branches
|
||||
Before publishing a PR, please test builds locally:
|
||||
|
||||
* *master*: images on the master branch are built monthly and with github action triggered by ERPNext release.
|
||||
* *develop*: images on this branch are built daily and when PR is merged into develop.
|
||||
- with docker-compose for production,
|
||||
- with and without nginx proxy,
|
||||
- with VSCode for testing environments (only for frappe/bench image).
|
||||
|
||||
# Pull Requests
|
||||
On each PR that contains changes relevant to Docker builds, images are being built and tested in our CI (GitHub Actions).
|
||||
|
||||
Please **send all pull request exclusively to the *develop*** branch.
|
||||
When the PR are merged, the merge will trigger the image build automatically.
|
||||
> :evergreen_tree: Please be considerate when pushing commits and opening PR for multiple branches, as the process of building images uses energy and contributes to global warming.
|
||||
|
||||
Please test all PR as extensively as you can, considering that the software can be run in different modes:
|
||||
## Lint
|
||||
|
||||
* with docker-compose for production
|
||||
* with or without Nginx proxy
|
||||
* with VScode for testing environments
|
||||
We use `pre-commit` framework to lint the codebase before committing.
|
||||
First, you need to install pre-commit with pip:
|
||||
|
||||
Every once in a while (or with monthly release) develop will be merged into master.
|
||||
```shell
|
||||
pip install pre-commit
|
||||
```
|
||||
|
||||
There is Github Action is configured on ERPNext repository. Whenever there is a ERPNext release it will trigger a build on master branch of frappe_docker repo to generate images for released version.
|
||||
Also you can use brew if you're on Mac:
|
||||
|
||||
When a PR is sent, the images are built and all commands are tested.
|
||||
```shell
|
||||
brew install pre-commit
|
||||
```
|
||||
|
||||
If update or fixes to documentation are pushed use `[skip travis]` anywhere in commit message to skip travis.
|
||||
To setup _pre-commit_ hook, run:
|
||||
|
||||
## Reducing the number of branching and builds :evergreen_tree: :evergreen_tree: :evergreen_tree:
|
||||
```shell
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
Please be considerate when pushing commits and opening PR for multiple branches, as the process of building images (triggered on push and PR branch push) uses energy and contributes to global warming.
|
||||
To run all the files in repository, run:
|
||||
|
||||
```shell
|
||||
pre-commit run --all-files
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
```shell
|
||||
# *...* — targets from docker-bake.hcl,
|
||||
# e.g. bench-build, frappe-socketio-develop or erpnext-nginx-stable.
|
||||
# Stable builds require GIT_BRANCH (e.g. v13.15.0), IMAGE_TAG (version-13), VERSION (13)
|
||||
# environment variables set.
|
||||
docker buildx bake -f docker-bake.hcl *...*
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
### Ping site
|
||||
|
||||
Lightweight test that just checks if site will be available after creation.
|
||||
|
||||
Frappe:
|
||||
|
||||
```shell
|
||||
./tests/test-frappe.sh
|
||||
```
|
||||
|
||||
ERPNext:
|
||||
|
||||
```shell
|
||||
./tests/test-erpnext.sh
|
||||
```
|
||||
|
||||
### Integration test
|
||||
|
||||
Tests frappe-bench-like commands, for example, `backup` and `restore`.
|
||||
|
||||
```shell
|
||||
./tests/integration-test.sh
|
||||
```
|
||||
|
||||
# Documentation
|
||||
|
||||
@@ -37,32 +81,6 @@ Place relevant markdown file(s) in the `docs` directory and index them in README
|
||||
|
||||
Add alternatives that can be used optionally along with frappe_docker. Add articles to list on home page as well.
|
||||
|
||||
# Prerequisites to pass CI
|
||||
# Frappe and ERPNext updates
|
||||
|
||||
### Check shell script format
|
||||
|
||||
Use the following script
|
||||
|
||||
```shell
|
||||
./tests/check-format.sh
|
||||
```
|
||||
|
||||
### Build images locally
|
||||
|
||||
Use the following commands
|
||||
|
||||
```shell
|
||||
docker build -t frappe/frappe-socketio:edge -f build/frappe-socketio/Dockerfile .
|
||||
docker build -t frappe/frappe-worker:develop -f build/frappe-worker/Dockerfile .
|
||||
docker build -t frappe/erpnext-worker:edge -f build/erpnext-worker/Dockerfile .
|
||||
docker build -t frappe/frappe-nginx:develop -f build/frappe-nginx/Dockerfile .
|
||||
docker build -t frappe/erpnext-nginx:edge -f build/erpnext-nginx/Dockerfile .
|
||||
```
|
||||
|
||||
### Test running docker containers
|
||||
|
||||
Use the following script
|
||||
|
||||
```shell
|
||||
./tests/docker-test.sh
|
||||
```
|
||||
Each Frappe/ERPNext release triggers new stable images builds as well as bump to helm chart.
|
||||
|
||||
25
README.md
25
README.md
@@ -1,12 +1,11 @@
|
||||
| Develop | [](https://travis-ci.com/frappe/frappe_docker) |
|
||||
|---------|-----------------------------------------------------------------------------------------------------------------------------|
|
||||
| Master | [](https://travis-ci.com/frappe/frappe_docker) |
|
||||
[](https://github.com/frappe/frappe_docker/actions/workflows/build_stable.yml)
|
||||
[](https://github.com/frappe/frappe_docker/actions/workflows/build_develop.yml)
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Try in Play With Docker
|
||||
|
||||
<a href="https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/frappe/frappe_docker/develop/tests/pwd.yml">
|
||||
<a href="https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/frappe/frappe_docker/main/pwd.yml">
|
||||
<img src="https://raw.githubusercontent.com/play-with-docker/stacks/master/assets/images/button.png" alt="Try in PWD"/>
|
||||
</a>
|
||||
|
||||
@@ -31,21 +30,21 @@ cd frappe_docker
|
||||
|
||||
It takes care of the following:
|
||||
|
||||
* Setting up the desired version of Frappe/ERPNext.
|
||||
* Setting up all the system requirements: eg. MariaDB, Node, Redis.
|
||||
* Configure networking for remote access and setting up LetsEncrypt.
|
||||
- Setting up the desired version of Frappe/ERPNext.
|
||||
- Setting up all the system requirements: eg. MariaDB, Node, Redis.
|
||||
- Configure networking for remote access and setting up LetsEncrypt.
|
||||
|
||||
It doesn't take care of the following:
|
||||
|
||||
* Cron Job to backup sites is not created by default.
|
||||
* Use `CronJob` on k8s or refer wiki for alternatives.
|
||||
- Cron Job to backup sites is not created by default.
|
||||
- Use `CronJob` on k8s or refer wiki for alternatives.
|
||||
|
||||
1. Single Server Installs
|
||||
1. [Single bench](docs/single-bench.md). Easiest Install!
|
||||
2. [Multi bench](docs/multi-bench.md)
|
||||
1. [Single bench](docs/single-bench.md). Easiest Install!
|
||||
2. [Multi bench](docs/multi-bench.md)
|
||||
2. Multi Server Installs
|
||||
1. [Docker Swarm](docs/docker-swarm.md)
|
||||
2. [Kubernetes](https://helm.erpnext.com)
|
||||
1. [Docker Swarm](docs/docker-swarm.md)
|
||||
2. [Kubernetes](https://helm.erpnext.com)
|
||||
3. [Site Operations](docs/site-operations.md)
|
||||
4. [Environment Variables](docs/environment-variables.md)
|
||||
5. [Custom apps for production](docs/custom-apps-for-production.md)
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
# Frappe Bench Dockerfile
|
||||
FROM debian:buster-slim
|
||||
FROM debian:bullseye-slim as bench
|
||||
|
||||
LABEL author=frappé
|
||||
|
||||
ARG GIT_REPO=https://github.com/frappe/bench.git
|
||||
ARG GIT_BRANCH=develop
|
||||
|
||||
LABEL author=frappé
|
||||
|
||||
RUN apt-get update -y && apt-get install \
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
|
||||
# For frappe framework
|
||||
git \
|
||||
wkhtmltopdf \
|
||||
mariadb-client \
|
||||
postgresql-client \
|
||||
gettext-base \
|
||||
wget \
|
||||
# for ERPNext v12
|
||||
# TODO: Remove after v12 is deprecated
|
||||
python2 \
|
||||
# for PDF
|
||||
libssl-dev \
|
||||
fonts-cantarell \
|
||||
@@ -29,6 +32,7 @@ RUN apt-get update -y && apt-get install \
|
||||
watch \
|
||||
tree \
|
||||
nano \
|
||||
less \
|
||||
software-properties-common \
|
||||
bash-completion \
|
||||
# For psycopg2
|
||||
@@ -37,7 +41,7 @@ RUN apt-get update -y && apt-get install \
|
||||
libffi-dev \
|
||||
liblcms2-dev \
|
||||
libldap2-dev \
|
||||
libmariadbclient-dev \
|
||||
libmariadb-dev \
|
||||
libsasl2-dev \
|
||||
libtiff5-dev \
|
||||
libwebp-dev \
|
||||
@@ -47,76 +51,91 @@ RUN apt-get update -y && apt-get install \
|
||||
ssh-client \
|
||||
# VSCode container requirements
|
||||
net-tools \
|
||||
# PYTHON
|
||||
python3-dev \
|
||||
python3-pip \
|
||||
python3-setuptools \
|
||||
python3-tk \
|
||||
python-virtualenv \
|
||||
less -y && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \
|
||||
&& dpkg-reconfigure --frontend=noninteractive locales
|
||||
&& dpkg-reconfigure --frontend=noninteractive locales
|
||||
|
||||
# Detect arch, download and install wkhtmltox
|
||||
RUN if [ `uname -m` = 'aarch64' ]; then export ARCH=arm64; fi \
|
||||
&& if [ `uname -m` = 'x86_64' ]; then export ARCH=amd64; fi \
|
||||
&& wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.buster_${ARCH}.deb \
|
||||
&& dpkg -i wkhtmltox_0.12.6-1.buster_${ARCH}.deb && rm wkhtmltox_0.12.6-1.buster_${ARCH}.deb
|
||||
# Detect arch and install wkhtmltopdf
|
||||
ENV WKHTMLTOPDF_VERSION 0.12.6-1
|
||||
RUN if [ "$(uname -m)" = "aarch64" ]; then export ARCH=arm64; fi \
|
||||
&& if [ "$(uname -m)" = "x86_64" ]; then export ARCH=amd64; fi \
|
||||
&& downloaded_file=wkhtmltox_$WKHTMLTOPDF_VERSION.buster_${ARCH}.deb \
|
||||
&& wget -q https://github.com/wkhtmltopdf/packaging/releases/download/$WKHTMLTOPDF_VERSION/$downloaded_file \
|
||||
&& dpkg -i $downloaded_file \
|
||||
&& rm $downloaded_file
|
||||
|
||||
# Create new user with home directory, improve docker compatibility with UID/GID 1000,
|
||||
# add user to sudo group, allow passwordless sudo, switch to that user
|
||||
# and change directory to user home directory
|
||||
RUN groupadd -g 1000 frappe \
|
||||
&& useradd --no-log-init -r -m -u 1000 -g 1000 -G sudo frappe \
|
||||
&& echo "frappe ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||
|
||||
# Create new user with home directory, improve docker compatibility with UID/GID 1000, add user to sudo group, allow passwordless sudo, switch to that user and change directory to user home directory
|
||||
RUN groupadd -g 1000 frappe
|
||||
RUN useradd --no-log-init -r -m -u 1000 -g 1000 -G sudo frappe
|
||||
RUN echo "frappe ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||
USER frappe
|
||||
WORKDIR /home/frappe
|
||||
|
||||
# Install Python via pyenv
|
||||
# Python 3.7 sits here for ERPNext version-12
|
||||
# TODO: Remove Python 3.7 when version-12 will not be supported
|
||||
ENV PYTHON_VERSION_V12=3.7.12
|
||||
ENV PYTHON_VERSION=3.9.9
|
||||
ENV PYENV_ROOT /home/frappe/.pyenv
|
||||
ENV PATH $PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH
|
||||
|
||||
# From https://github.com/pyenv/pyenv#basic-github-checkout
|
||||
RUN git clone --depth 1 https://github.com/pyenv/pyenv.git .pyenv \
|
||||
&& pyenv install $PYTHON_VERSION_V12 \
|
||||
&& pyenv install $PYTHON_VERSION \
|
||||
&& pyenv global $PYTHON_VERSION $PYTHON_VERSION_V12 \
|
||||
&& sed -Ei -e '/^([^#]|$)/ {a export PYENV_ROOT="/home/frappe/.pyenv" a export PATH="$PYENV_ROOT/bin:$PATH" a ' -e ':a' -e '$!{n;ba};}' ~/.profile \
|
||||
&& echo 'eval "$(pyenv init --path)"' >>~/.profile \
|
||||
&& echo 'eval "$(pyenv init -)"' >>~/.bashrc
|
||||
|
||||
# Clone and install bench in the local user home directory
|
||||
# For development, bench source is located in ~/.bench
|
||||
RUN git clone ${GIT_REPO} -b ${GIT_BRANCH} .bench \
|
||||
&& pip3 install --user -e .bench
|
||||
ENV PATH /home/frappe/.local/bin:$PATH
|
||||
# Skip editable-bench warning
|
||||
# https://github.com/frappe/bench/commit/20560c97c4246b2480d7358c722bc9ad13606138
|
||||
RUN git clone ${GIT_REPO} --depth 1 -b ${GIT_BRANCH} .bench \
|
||||
&& pip install --user -e .bench \
|
||||
&& echo "export PATH=/home/frappe/.local/bin:\$PATH" >>/home/frappe/.bashrc \
|
||||
&& echo "export BENCH_DEVELOPER=1" >>/home/frappe/.bashrc
|
||||
|
||||
# Export python executables for Dockerfile
|
||||
ENV PATH=/home/frappe/.local/bin:$PATH
|
||||
# Export python executables for interactive shell
|
||||
RUN echo "export PATH=/home/frappe/.local/bin:\$PATH" >> /home/frappe/.bashrc
|
||||
|
||||
# Print version and verify bashrc is properly sourced so that everything works in the Dockerfile
|
||||
RUN bench --version
|
||||
# Print version and verify bashrc is properly sourced so that everything works in the interactive shell
|
||||
RUN bash -c "bench --version"
|
||||
|
||||
# !!! UPDATE NODEJS PERIODICALLY WITH LATEST VERSIONS !!!
|
||||
# https://nodejs.org/en/about/releases/
|
||||
# https://nodejs.org/download/release/latest-v10.x/
|
||||
# https://nodejs.org/download/release/latest-v14.x/
|
||||
ENV NODE_VERSION=14.17.0
|
||||
# Install Node via nvm
|
||||
ENV NODE_VERSION=14.18.1
|
||||
ENV NODE_VERSION_FRAPPEV11=10.24.1
|
||||
ENV NVM_DIR /home/frappe/.nvm
|
||||
ENV PATH ${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
|
||||
|
||||
# Install nvm with node
|
||||
RUN wget https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh
|
||||
RUN chmod +x install.sh
|
||||
RUN ./install.sh
|
||||
ENV NVM_DIR=/home/frappe/.nvm
|
||||
RUN wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash \
|
||||
&& . ${NVM_DIR}/nvm.sh \
|
||||
&& nvm install ${NODE_VERSION_FRAPPEV11} \
|
||||
&& nvm use v${NODE_VERSION_FRAPPEV11} \
|
||||
&& npm install -g yarn \
|
||||
&& nvm install ${NODE_VERSION} \
|
||||
&& nvm use v${NODE_VERSION} \
|
||||
&& npm install -g yarn \
|
||||
&& nvm alias default v${NODE_VERSION} \
|
||||
&& rm -rf ${NVM_DIR}/.cache \
|
||||
&& echo 'export NVM_DIR="/home/frappe/.nvm"' >>~/.bashrc \
|
||||
&& echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm' >> ~/.bashrc \
|
||||
&& echo '[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion' >> ~/.bashrc
|
||||
|
||||
# Install node for Frappe V11, install yarn
|
||||
RUN . "$NVM_DIR/nvm.sh" && nvm install ${NODE_VERSION_FRAPPEV11}
|
||||
RUN . "$NVM_DIR/nvm.sh" && nvm use v${NODE_VERSION_FRAPPEV11} && npm install -g yarn
|
||||
# Install node for latest frappe, set as default, install node
|
||||
RUN . "$NVM_DIR/nvm.sh" && nvm install ${NODE_VERSION}
|
||||
RUN . "$NVM_DIR/nvm.sh" && nvm use v${NODE_VERSION} && npm install -g yarn
|
||||
RUN . "$NVM_DIR/nvm.sh" && nvm alias default v${NODE_VERSION}
|
||||
ENV PATH="/home/frappe/.nvm/versions/node/v${NODE_VERSION}/bin/:${PATH}"
|
||||
|
||||
# Print version and verify bashrc is properly sourced so that everything works in the Dockerfile
|
||||
RUN node --version \
|
||||
&& npm --version \
|
||||
&& yarn --version
|
||||
# Print version and verify bashrc is properly sourced so that everything works in the interactive shell
|
||||
RUN bash -c "node --version" \
|
||||
&& bash -c "npm --version" \
|
||||
&& bash -c "yarn --version"
|
||||
|
||||
EXPOSE 8000-8005 9000-9005 6787
|
||||
|
||||
FROM bench as bench-test
|
||||
|
||||
# Print version and verify bashrc is properly sourced so that everything works
|
||||
# in the interactive shell and Dockerfile
|
||||
|
||||
RUN node --version \
|
||||
&& npm --version \
|
||||
&& yarn --version \
|
||||
&& bench --help
|
||||
|
||||
RUN bash -c "node --version" \
|
||||
&& bash -c "npm --version" \
|
||||
&& bash -c "yarn --version" \
|
||||
&& bash -c "bench --help"
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
from frappe.utils.scheduler import start_scheduler
|
||||
|
||||
|
||||
def main():
|
||||
print("Starting background scheduler . . .")
|
||||
start_scheduler()
|
||||
exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,32 +0,0 @@
|
||||
import sys
|
||||
import frappe
|
||||
import IPython
|
||||
|
||||
from frappe.utils import get_sites
|
||||
|
||||
|
||||
def console(site):
|
||||
"Start ipython console for a site"
|
||||
if site not in get_sites():
|
||||
print("Site {0} does not exist on the current bench".format(site))
|
||||
return
|
||||
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
frappe.local.lang = frappe.db.get_default("lang")
|
||||
all_apps = frappe.get_installed_apps()
|
||||
for app in all_apps:
|
||||
locals()[app] = __import__(app)
|
||||
print("Apps in this namespace:\n{}".format(", ".join(all_apps)))
|
||||
IPython.embed(display_banner="", header="")
|
||||
|
||||
|
||||
def main():
|
||||
site = sys.argv[-1]
|
||||
console(site)
|
||||
if frappe.redis_server:
|
||||
frappe.redis_server.connection_pool.disconnect()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,13 +0,0 @@
|
||||
REDIS_QUEUE_KEY = 'redis_queue'
|
||||
REDIS_CACHE_KEY = 'redis_cache'
|
||||
REDIS_SOCKETIO_KEY = 'redis_socketio'
|
||||
DB_HOST_KEY = 'db_host'
|
||||
DB_PORT_KEY = 'db_port'
|
||||
DB_PORT = 3306
|
||||
APP_VERSIONS_JSON_FILE = 'app_versions.json'
|
||||
APPS_TXT_FILE = 'apps.txt'
|
||||
COMMON_SITE_CONFIG_FILE = 'common_site_config.json'
|
||||
DATE_FORMAT = "%Y%m%d_%H%M%S"
|
||||
RDS_DB = 'rds_db'
|
||||
RDS_PRIVILEGES = "SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, CREATE TEMPORARY TABLES, CREATE VIEW, EVENT, TRIGGER, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, EXECUTE, LOCK TABLES"
|
||||
ARCHIVE_SITES_PATH = '/home/frappe/frappe-bench/sites/archive_sites'
|
||||
@@ -1,39 +0,0 @@
|
||||
import os
|
||||
import frappe
|
||||
|
||||
from frappe.commands.site import _drop_site
|
||||
from constants import ARCHIVE_SITES_PATH
|
||||
from utils import get_password
|
||||
|
||||
|
||||
def main():
|
||||
site_name = os.environ.get("SITE_NAME", 'site1.localhost')
|
||||
db_root_username = os.environ.get("DB_ROOT_USER", 'root')
|
||||
mariadb_root_password = get_password("MYSQL_ROOT_PASSWORD", 'admin')
|
||||
postgres_root_password = get_password("POSTGRES_PASSWORD")
|
||||
db_root_password = mariadb_root_password
|
||||
|
||||
if postgres_root_password:
|
||||
db_root_password = postgres_root_password
|
||||
|
||||
force = True if os.environ.get("FORCE", None) else False
|
||||
no_backup = True if os.environ.get("NO_BACKUP", None) else False
|
||||
frappe.init(site_name, new_site=True)
|
||||
|
||||
_drop_site(
|
||||
site=site_name,
|
||||
root_login=db_root_username,
|
||||
root_password=db_root_password,
|
||||
archived_sites_path=ARCHIVE_SITES_PATH,
|
||||
force=force,
|
||||
no_backup=no_backup
|
||||
)
|
||||
|
||||
if frappe.redis_server:
|
||||
frappe.redis_server.connection_pool.disconnect()
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,12 +0,0 @@
|
||||
import os
|
||||
from frappe.utils.background_jobs import start_worker
|
||||
|
||||
|
||||
def main():
|
||||
queue = os.environ.get("WORKER_TYPE", "default")
|
||||
start_worker(queue, False)
|
||||
exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/home/frappe/frappe-bench/env/bin/python
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
bench_dir = os.path.join(os.sep, 'home', 'frappe', 'frappe-bench')
|
||||
sites_dir = os.path.join(bench_dir, 'sites')
|
||||
bench_helper = os.path.join(
|
||||
bench_dir, 'apps', 'frappe',
|
||||
'frappe', 'utils', 'bench_helper.py',
|
||||
)
|
||||
cwd = os.getcwd()
|
||||
os.chdir(sites_dir)
|
||||
subprocess.check_call(
|
||||
[sys.executable, bench_helper, 'frappe'] + sys.argv[1:],
|
||||
)
|
||||
@@ -1,173 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
function configureEnv() {
|
||||
if [[ ! -f /home/frappe/frappe-bench/sites/common_site_config.json ]]; then
|
||||
|
||||
if [[ -z "${MARIADB_HOST}" && -z "${POSTGRES_HOST}" ]]; then
|
||||
echo "MARIADB_HOST or POSTGRES_HOST is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${REDIS_CACHE}" ]]; then
|
||||
echo "REDIS_CACHE is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${REDIS_QUEUE}" ]]; then
|
||||
echo "REDIS_QUEUE is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${REDIS_SOCKETIO}" ]]; then
|
||||
echo "REDIS_SOCKETIO is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${SOCKETIO_PORT}" ]]; then
|
||||
echo "SOCKETIO_PORT is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${DB_PORT}" ]]; then
|
||||
export DB_PORT=3306
|
||||
fi
|
||||
|
||||
export DB_HOST="${MARIADB_HOST:-$POSTGRES_HOST}"
|
||||
|
||||
envsubst '${DB_HOST}
|
||||
${DB_PORT}
|
||||
${REDIS_CACHE}
|
||||
${REDIS_QUEUE}
|
||||
${REDIS_SOCKETIO}
|
||||
${SOCKETIO_PORT}' </opt/frappe/common_site_config.json.template >/home/frappe/frappe-bench/sites/common_site_config.json
|
||||
fi
|
||||
}
|
||||
|
||||
function checkConnection() {
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/check_connection.py
|
||||
}
|
||||
|
||||
function checkConfigExists() {
|
||||
COUNTER=0
|
||||
while [[ ! -e /home/frappe/frappe-bench/sites/common_site_config.json && ${COUNTER} -le 30 ]]; do
|
||||
sleep 1
|
||||
((COUNTER = COUNTER + 1))
|
||||
echo "config file not created, retry ${COUNTER}" >&2
|
||||
done
|
||||
|
||||
if [[ ! -e /home/frappe/frappe-bench/sites/common_site_config.json ]]; then
|
||||
echo "timeout: config file not created" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ ! -e /home/frappe/frappe-bench/sites/apps.txt ]]; then
|
||||
find /home/frappe/frappe-bench/apps -mindepth 1 -maxdepth 1 -type d -printf '%f\n' |
|
||||
sort -r >/home/frappe/frappe-bench/sites/apps.txt
|
||||
fi
|
||||
|
||||
# symlink node_modules
|
||||
ln -sfn /home/frappe/frappe-bench/sites/assets/frappe/node_modules \
|
||||
/home/frappe/frappe-bench/apps/frappe/node_modules
|
||||
|
||||
case "$1" in
|
||||
|
||||
start)
|
||||
configureEnv
|
||||
checkConnection
|
||||
|
||||
[[ -z "${WORKERS}" ]] && WORKERS='2'
|
||||
|
||||
[[ -z "${FRAPPE_PORT}" ]] && FRAPPE_PORT='8000'
|
||||
|
||||
[[ -z "${WORKER_CLASS}" ]] && WORKER_CLASS='gthread'
|
||||
|
||||
LOAD_CONFIG_FILE=""
|
||||
[[ "${WORKER_CLASS}" == "gevent" ]] &&
|
||||
LOAD_CONFIG_FILE="-c /home/frappe/frappe-bench/commands/gevent_patch.py"
|
||||
|
||||
if [[ -n "${AUTO_MIGRATE}" ]]; then
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/auto_migrate.py
|
||||
fi
|
||||
|
||||
/home/frappe/frappe-bench/env/bin/gunicorn ${LOAD_CONFIG_FILE} -b 0.0.0.0:${FRAPPE_PORT} \
|
||||
--worker-tmp-dir /dev/shm \
|
||||
--threads=4 \
|
||||
--workers ${WORKERS} \
|
||||
--worker-class=${WORKER_CLASS} \
|
||||
--log-file=- \
|
||||
-t 120 frappe.app:application --preload
|
||||
;;
|
||||
|
||||
worker)
|
||||
checkConfigExists
|
||||
checkConnection
|
||||
# default WORKER_TYPE=default
|
||||
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/worker.py
|
||||
;;
|
||||
|
||||
schedule)
|
||||
checkConfigExists
|
||||
checkConnection
|
||||
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/background.py
|
||||
|
||||
;;
|
||||
|
||||
new)
|
||||
checkConfigExists
|
||||
checkConnection
|
||||
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/new.py
|
||||
exit
|
||||
;;
|
||||
|
||||
drop)
|
||||
checkConfigExists
|
||||
checkConnection
|
||||
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/drop.py
|
||||
exit
|
||||
;;
|
||||
|
||||
migrate)
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/migrate.py
|
||||
exit
|
||||
;;
|
||||
|
||||
doctor)
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/doctor.py "${@:2}"
|
||||
exit
|
||||
;;
|
||||
|
||||
backup)
|
||||
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/backup.py
|
||||
exit
|
||||
;;
|
||||
|
||||
console)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "Need to specify a sitename with the command:" >&2
|
||||
echo "console <sitename>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/console.py "$2"
|
||||
exit
|
||||
;;
|
||||
|
||||
push-backup)
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/push_backup.py
|
||||
exit
|
||||
;;
|
||||
|
||||
restore-backup)
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/restore_backup.py
|
||||
exit
|
||||
;;
|
||||
*)
|
||||
exec "$@"
|
||||
;;
|
||||
esac
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
APP_NAME=${1}
|
||||
APP_REPO=${2}
|
||||
APP_BRANCH=${3}
|
||||
|
||||
[[ -n "${APP_BRANCH}" ]] && BRANCH="-b ${APP_BRANCH}"
|
||||
|
||||
git clone --depth 1 -o upstream ${APP_REPO} ${BRANCH} /home/frappe/frappe-bench/apps/${APP_NAME}
|
||||
/home/frappe/frappe-bench/env/bin/pip install --no-cache-dir -e /home/frappe/frappe-bench/apps/${APP_NAME}
|
||||
@@ -1,14 +1,32 @@
|
||||
ARG IMAGE_TAG=v10
|
||||
ARG NODE_IMAGE_TAG=14-bullseye-slim
|
||||
ARG DOCKER_REGISTRY_PREFIX=frappe
|
||||
FROM ${DOCKER_REGISTRY_PREFIX}/erpnext-worker:${IMAGE_TAG}
|
||||
ARG IMAGE_TAG=develop
|
||||
|
||||
FROM node:${NODE_IMAGE_TAG} as builder
|
||||
|
||||
ARG GIT_REPO=https://github.com/frappe/erpnext
|
||||
ARG GIT_BRANCH=develop
|
||||
|
||||
ARG FRAPPE_BRANCH=${GIT_BRANCH}
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
python2 \
|
||||
git \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY build/erpnext-nginx/install_app.sh /install_app
|
||||
RUN chmod +x /install_app \
|
||||
&& /install_app erpnext ${GIT_REPO} ${GIT_BRANCH} ${FRAPPE_BRANCH}
|
||||
|
||||
RUN /install_app
|
||||
|
||||
FROM ${DOCKER_REGISTRY_PREFIX}/frappe-nginx:${IMAGE_TAG}
|
||||
|
||||
COPY --from=0 /home/frappe/sites /var/www/html
|
||||
COPY --from=builder --chown=1000:1000 /home/frappe/frappe-bench/sites/ /var/www/html/
|
||||
COPY --from=builder /rsync /rsync
|
||||
|
||||
RUN echo "erpnext" >> /var/www/html/apps.txt
|
||||
|
||||
VOLUME [ "/assets" ]
|
||||
|
||||
@@ -1,12 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
npm i yarn -g
|
||||
cd /home/frappe/frappe-bench/apps/frappe
|
||||
rm -fr node_modules && yarn
|
||||
cd /home/frappe/frappe-bench/sites
|
||||
echo "frappe" > /home/frappe/frappe-bench/sites/apps.txt
|
||||
echo "erpnext" >> /home/frappe/frappe-bench/sites/apps.txt
|
||||
/home/frappe/frappe-bench/env/bin/python -c "import frappe; frappe.init(''); import frappe.build; frappe.build.setup(); frappe.build.make_asset_dirs(make_copy=True)"
|
||||
node --use_strict ../apps/frappe/frappe/build.js --build
|
||||
mkdir -p /home/frappe/sites
|
||||
cp -R assets /home/frappe/sites
|
||||
set -e
|
||||
|
||||
APP_NAME=${1}
|
||||
APP_REPO=${2}
|
||||
APP_BRANCH=${3}
|
||||
FRAPPE_BRANCH=${4}
|
||||
|
||||
[ "${APP_BRANCH}" ] && BRANCH="-b ${APP_BRANCH}"
|
||||
|
||||
mkdir -p /home/frappe/frappe-bench
|
||||
cd /home/frappe/frappe-bench
|
||||
mkdir -p apps "sites/assets/${APP_NAME}"
|
||||
echo -ne "frappe\n${APP_NAME}" >sites/apps.txt
|
||||
|
||||
git clone --depth 1 -b "${FRAPPE_BRANCH}" https://github.com/frappe/frappe apps/frappe
|
||||
# shellcheck disable=SC2086
|
||||
git clone --depth 1 ${BRANCH} ${APP_REPO} apps/${APP_NAME}
|
||||
|
||||
echo "Install frappe NodeJS dependencies . . ."
|
||||
cd apps/frappe
|
||||
yarn --pure-lockfile
|
||||
|
||||
echo "Install ${APP_NAME} NodeJS dependencies . . ."
|
||||
yarn --pure-lockfile --cwd "../${APP_NAME}"
|
||||
|
||||
echo "Build ${APP_NAME} assets . . ."
|
||||
yarn production --app "${APP_NAME}"
|
||||
|
||||
cd /home/frappe/frappe-bench
|
||||
# shellcheck disable=SC2086
|
||||
cp -R apps/${APP_NAME}/${APP_NAME}/public/* sites/assets/${APP_NAME}
|
||||
|
||||
# Add frappe and all the apps available under in frappe-bench here
|
||||
echo "rsync -a --delete /var/www/html/assets/frappe /assets" >/rsync
|
||||
echo "rsync -a --delete /var/www/html/assets/${APP_NAME} /assets" >>/rsync
|
||||
chmod +x /rsync
|
||||
|
||||
rm sites/apps.txt
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
ARG IMAGE_TAG=v10
|
||||
ARG IMAGE_TAG=develop
|
||||
ARG DOCKER_REGISTRY_PREFIX=frappe
|
||||
FROM ${DOCKER_REGISTRY_PREFIX}/frappe-worker:${IMAGE_TAG}
|
||||
|
||||
ARG GIT_REPO=https://github.com/frappe/erpnext
|
||||
ARG GIT_BRANCH=v10.x.x
|
||||
ARG GIT_BRANCH=develop
|
||||
|
||||
USER root
|
||||
RUN apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
gcc \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
USER frappe
|
||||
RUN install_app erpnext ${GIT_REPO} ${GIT_BRANCH}
|
||||
|
||||
@@ -2,33 +2,74 @@
|
||||
# This is done to ensures that node-sass binary remains common.
|
||||
# node-sass is required to enable website theme feature used
|
||||
# by Website Manager role in Frappe Framework
|
||||
ARG IMAGE_TAG=v10
|
||||
ARG DOCKER_REGISTRY_PREFIX=frappe
|
||||
FROM ${DOCKER_REGISTRY_PREFIX}/frappe-worker:${IMAGE_TAG}
|
||||
ARG PYTHON_VERSION=3.9
|
||||
FROM python:${PYTHON_VERSION}-slim-bullseye as builder
|
||||
|
||||
RUN npm i yarn -g \
|
||||
&& cd /home/frappe/frappe-bench/apps/frappe \
|
||||
&& rm -fr node_modules && yarn \
|
||||
&& cd /home/frappe/frappe-bench/sites \
|
||||
&& echo "frappe" > /home/frappe/frappe-bench/sites/apps.txt \
|
||||
&& /home/frappe/frappe-bench/env/bin/python -c "import frappe; frappe.init(''); import frappe.build; frappe.build.setup(); frappe.build.make_asset_dirs(make_copy=True)" \
|
||||
&& node --use_strict ../apps/frappe/frappe/build.js --build \
|
||||
&& mkdir -p /home/frappe/sites \
|
||||
&& cp -R assets /home/frappe/sites
|
||||
ARG GIT_REPO=https://github.com/frappe/frappe
|
||||
ARG GIT_BRANCH=develop
|
||||
|
||||
ENV NODE_VERSION=14.18.1
|
||||
ENV NVM_DIR=/root/.nvm
|
||||
ENV PATH ${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
git \
|
||||
build-essential \
|
||||
wget \
|
||||
# python2 for version-12 builds
|
||||
python2 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install nvm with node and yarn
|
||||
RUN wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash \
|
||||
&& . ${NVM_DIR}/nvm.sh \
|
||||
&& nvm install ${NODE_VERSION} \
|
||||
&& npm install -g yarn \
|
||||
&& rm -rf ${NVM_DIR}/.cache
|
||||
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
|
||||
RUN mkdir -p apps sites/assets/css sites/assets/frappe /var/www/error_pages
|
||||
RUN echo "frappe" > sites/apps.txt
|
||||
|
||||
RUN git clone --depth 1 -b ${GIT_BRANCH} ${GIT_REPO} apps/frappe
|
||||
RUN cd apps/frappe \
|
||||
&& yarn \
|
||||
&& yarn run production \
|
||||
&& yarn install --production=true
|
||||
|
||||
RUN git clone --depth 1 https://github.com/frappe/bench /tmp/bench \
|
||||
&& mkdir -p /home/frappe/www/error_pages \
|
||||
&& cp -r /tmp/bench/bench/config/templates /home/frappe/www/error_pages
|
||||
&& cp -r /tmp/bench/bench/config/templates /var/www/error_pages
|
||||
|
||||
FROM nginx:latest
|
||||
COPY --from=0 /home/frappe/sites /var/www/html
|
||||
COPY --from=0 /home/frappe/www/error_pages /var/www/
|
||||
COPY build/common/nginx-default.conf.template /etc/nginx/conf.d/default.conf.template
|
||||
RUN cp -R apps/frappe/frappe/public/* sites/assets/frappe \
|
||||
&& cp -R apps/frappe/node_modules sites/assets/frappe/
|
||||
|
||||
FROM nginxinc/nginx-unprivileged:latest
|
||||
|
||||
USER root
|
||||
|
||||
RUN usermod -u 1000 nginx && groupmod -g 1000 nginx
|
||||
|
||||
COPY --from=builder --chown=1000:1000 /home/frappe/frappe-bench/sites /var/www/html/
|
||||
COPY --from=builder --chown=1000:1000 /var/www/error_pages /var/www/
|
||||
COPY build/frappe-nginx/nginx-default.conf.template /etc/nginx/conf.d/default.conf.template
|
||||
COPY build/frappe-nginx/docker-entrypoint.sh /
|
||||
|
||||
RUN apt-get update && apt-get install -y rsync && apt-get clean
|
||||
RUN apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
rsync \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN echo "#!/bin/bash" > /rsync \
|
||||
&& chmod +x /rsync
|
||||
|
||||
RUN mkdir /assets
|
||||
VOLUME [ "/assets" ]
|
||||
|
||||
RUN chown -R nginx:nginx /assets /etc/nginx/conf.d/ /var/www/html/
|
||||
|
||||
USER nginx
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
rsync -a --delete /var/www/html/assets/* /assets
|
||||
|
||||
/rsync
|
||||
|
||||
# shellcheck disable=SC2012
|
||||
touch /var/www/html/sites/.build -r "$(ls -td /assets/* | head -n 1)"
|
||||
|
||||
[[ -z "${FRAPPE_PY}" ]] && FRAPPE_PY='0.0.0.0'
|
||||
@@ -28,12 +31,15 @@ touch /var/www/html/sites/.build -r "$(ls -td /assets/* | head -n 1)"
|
||||
|
||||
[[ -z "${HTTP_HOST}" ]] && HTTP_HOST="\$http_host"
|
||||
|
||||
[[ -z "${WEB_PORT}" ]] && WEB_PORT="8080"
|
||||
|
||||
[[ -z "${SKIP_NGINX_TEMPLATE_GENERATION}" ]] && SKIP_NGINX_TEMPLATE_GENERATION='0'
|
||||
|
||||
if [[ ${SKIP_NGINX_TEMPLATE_GENERATION} == 1 ]]; then
|
||||
echo "Skipping default NGINX template generation. Please mount your own NGINX config file inside /etc/nginx/conf.d"
|
||||
else
|
||||
echo "Generating default template"
|
||||
# shellcheck disable=SC2016
|
||||
envsubst '${FRAPPE_PY}
|
||||
${FRAPPE_PY_PORT}
|
||||
${FRAPPE_SOCKETIO}
|
||||
@@ -43,14 +49,17 @@ else
|
||||
${UPSTREAM_REAL_IP_RECURSIVE}
|
||||
${FRAPPE_SITE_NAME_HEADER}
|
||||
${HTTP_HOST}
|
||||
${UPSTREAM_REAL_IP_HEADER}' \
|
||||
${UPSTREAM_REAL_IP_HEADER}
|
||||
${WEB_PORT}' \
|
||||
</etc/nginx/conf.d/default.conf.template >/etc/nginx/conf.d/default.conf
|
||||
fi
|
||||
|
||||
echo "Waiting for frappe-python to be available on ${FRAPPE_PY} port ${FRAPPE_PY_PORT}"
|
||||
# shellcheck disable=SC2016
|
||||
timeout 10 bash -c 'until printf "" 2>>/dev/null >>/dev/tcp/$0/$1; do sleep 1; done' ${FRAPPE_PY} ${FRAPPE_PY_PORT}
|
||||
echo "Frappe-python available on ${FRAPPE_PY} port ${FRAPPE_PY_PORT}"
|
||||
echo "Waiting for frappe-socketio to be available on ${FRAPPE_SOCKETIO} port ${SOCKETIO_PORT}"
|
||||
# shellcheck disable=SC2016
|
||||
timeout 10 bash -c 'until printf "" 2>>/dev/null >>/dev/tcp/$0/$1; do sleep 1; done' ${FRAPPE_SOCKETIO} ${SOCKETIO_PORT}
|
||||
echo "Frappe-socketio available on ${FRAPPE_SOCKETIO} port ${SOCKETIO_PORT}"
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ map $http_x_forwarded_proto $proxy_x_forwarded_proto {
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen ${WEB_PORT};
|
||||
server_name $http_host;
|
||||
root /var/www/html;
|
||||
|
||||
@@ -89,7 +89,7 @@ server {
|
||||
client_body_buffer_size 16K;
|
||||
client_header_buffer_size 1k;
|
||||
|
||||
# enable gzip compresion
|
||||
# enable gzip compression
|
||||
# based on https://mattstauffer.co/blog/enabling-gzip-on-nginx-servers-including-laravel-forge
|
||||
gzip on;
|
||||
gzip_http_version 1.1;
|
||||
@@ -1,35 +1,35 @@
|
||||
FROM alpine/git
|
||||
FROM alpine/git as builder
|
||||
|
||||
ARG GIT_REPO=https://github.com/frappe/frappe.git
|
||||
ARG GIT_BRANCH=v10.x.x
|
||||
ARG GIT_BRANCH=develop
|
||||
|
||||
RUN git clone ${GIT_REPO} /opt/frappe -b ${GIT_BRANCH} --depth 1
|
||||
RUN git clone --depth 1 -b ${GIT_BRANCH} ${GIT_REPO} /opt/frappe
|
||||
|
||||
FROM node:8-slim
|
||||
FROM node:bullseye-slim
|
||||
|
||||
# Add frappe user
|
||||
RUN useradd -ms /bin/bash frappe
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
|
||||
# Create bench directories and set ownership
|
||||
RUN mkdir -p /home/frappe/frappe-bench/sites /home/frappe/frappe-bench/apps/frappe \
|
||||
RUN mkdir -p sites apps/frappe \
|
||||
&& chown -R frappe:frappe /home/frappe
|
||||
|
||||
# Download socketio and purge curl package
|
||||
COPY --from=0 /opt/frappe/socketio.js /home/frappe/frappe-bench/apps/frappe/socketio.js
|
||||
COPY --from=0 /opt/frappe/package.json /home/frappe/frappe-bench/apps/frappe/package.json
|
||||
# Download socketio
|
||||
COPY build/frappe-socketio/package.json apps/frappe
|
||||
COPY --from=builder /opt/frappe/socketio.js apps/frappe/socketio.js
|
||||
COPY --from=builder /opt/frappe/node_utils.js apps/frappe/node_utils.js
|
||||
|
||||
RUN cd /home/frappe/frappe-bench/apps/frappe \
|
||||
&& npm install --only=production \
|
||||
&& node --version \
|
||||
&& npm --version
|
||||
RUN cd apps/frappe \
|
||||
&& npm install --only=prod
|
||||
|
||||
# Setup docker-entrypoint
|
||||
COPY build/frappe-socketio/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||
RUN ln -s /usr/local/bin/docker-entrypoint.sh / # backwards compat
|
||||
# backwards compat
|
||||
RUN ln -s /usr/local/bin/docker-entrypoint.sh /
|
||||
|
||||
USER frappe
|
||||
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
WORKDIR /home/frappe/frappe-bench/sites
|
||||
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
CMD ["start"]
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
#!/bin/bash -e
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
function checkConfigExists() {
|
||||
COUNTER=0
|
||||
while [[ ! -e /home/frappe/frappe-bench/sites/common_site_config.json && ${COUNTER} -le 30 ]]; do
|
||||
((COUNTER = COUNTER + 1))
|
||||
echo "config file not created, retry ${COUNTER}"
|
||||
sleep 1
|
||||
((COUNTER = COUNTER + 1))
|
||||
echo "config file not created, retry ${COUNTER}" >&2
|
||||
done
|
||||
|
||||
if [[ ! -e /home/frappe/frappe-bench/sites/common_site_config.json ]]; then
|
||||
echo "timeout: config file not created"
|
||||
echo "timeout: config file not created" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
@@ -17,11 +19,6 @@ function checkConfigExists() {
|
||||
if [[ "$1" == 'start' ]]; then
|
||||
checkConfigExists
|
||||
node /home/frappe/frappe-bench/apps/frappe/socketio.js
|
||||
|
||||
elif [[ "$1" == 'doctor' ]]; then
|
||||
node /home/frappe/frappe-bench/apps/frappe/health.js
|
||||
|
||||
else
|
||||
exec -c "$@"
|
||||
|
||||
fi
|
||||
|
||||
@@ -1,26 +1,31 @@
|
||||
FROM python:2.7-stretch
|
||||
ARG PYTHON_VERSION=3.9
|
||||
FROM python:${PYTHON_VERSION}-slim-bullseye
|
||||
|
||||
# Add non root user without password
|
||||
RUN useradd -ms /bin/bash frappe
|
||||
|
||||
ARG GIT_REPO=https://github.com/frappe/frappe
|
||||
ARG GIT_BRANCH=v10.x.x
|
||||
ARG GIT_BRANCH=develop
|
||||
|
||||
ARG ARCH=amd64
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
ENV NVM_DIR=/home/frappe/.nvm
|
||||
ENV NODE_VERSION=8.17.0
|
||||
ENV PATH="/home/frappe/.nvm/versions/node/v${NODE_VERSION}/bin/:${PATH}"
|
||||
|
||||
# Install dependencies
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
RUN apt-get update -y && apt-get install \
|
||||
# for frappe framework
|
||||
ENV NODE_VERSION=14.18.1
|
||||
ENV NVM_DIR /home/frappe/.nvm
|
||||
ENV PATH ${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
|
||||
ENV WKHTMLTOPDF_VERSION 0.12.6-1
|
||||
|
||||
# Install apt dependencies
|
||||
RUN apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
# For frappe framework
|
||||
git \
|
||||
mariadb-client \
|
||||
postgresql-client \
|
||||
gettext-base \
|
||||
wget \
|
||||
wait-for-it \
|
||||
# for PDF
|
||||
# For PDF
|
||||
libjpeg62-turbo \
|
||||
libx11-6 \
|
||||
libxcb1 \
|
||||
@@ -37,46 +42,62 @@ RUN apt-get update -y && apt-get install \
|
||||
# For psycopg2
|
||||
libpq-dev \
|
||||
# For arm64 python wheel builds
|
||||
&& if [ "$(uname -m)" = "aarch64" ]; then \
|
||||
apt-get install --no-install-recommends -y \
|
||||
gcc \
|
||||
g++ -y \
|
||||
# Detect arch, download and install wkhtmltox
|
||||
&& if [ `uname -m` = 'aarch64' ]; then export ARCH=arm64; fi \
|
||||
&& if [ `uname -m` = 'x86_64' ]; then export ARCH=amd64; fi \
|
||||
&& wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.stretch_${ARCH}.deb \
|
||||
&& dpkg -i wkhtmltox_0.12.6-1.stretch_${ARCH}.deb && rm wkhtmltox_0.12.6-1.stretch_${ARCH}.deb \
|
||||
&& wget https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh \
|
||||
&& chown -R frappe:frappe /home/frappe
|
||||
g++; \
|
||||
fi \
|
||||
# Install additional requirements for develop branch
|
||||
&& if [ "${GIT_BRANCH}" = 'develop' ]; then \
|
||||
apt-get install --no-install-recommends -y \
|
||||
libcairo2 \
|
||||
python3-cffi \
|
||||
python3-brotli \
|
||||
libpango-1.0-0 \
|
||||
libpangoft2-1.0-0 \
|
||||
libpangocairo-1.0-0; \
|
||||
fi \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Detect arch, download and install wkhtmltopdf
|
||||
RUN if [ "$(uname -m)" = "aarch64" ]; then export ARCH=arm64; fi \
|
||||
&& if [ "$(uname -m)" = "x86_64" ]; then export ARCH=amd64; fi \
|
||||
&& downloaded_file=wkhtmltox_$WKHTMLTOPDF_VERSION.buster_${ARCH}.deb \
|
||||
&& wget -q https://github.com/wkhtmltopdf/packaging/releases/download/$WKHTMLTOPDF_VERSION/$downloaded_file \
|
||||
&& dpkg -i $downloaded_file \
|
||||
&& rm $downloaded_file
|
||||
|
||||
# Setup docker-entrypoint
|
||||
COPY build/common/worker/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||
COPY build/frappe-worker/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||
RUN ln -s /usr/local/bin/docker-entrypoint.sh / # backwards compat
|
||||
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
RUN chown -R frappe:frappe /home/frappe
|
||||
USER frappe
|
||||
# Install nvm with node
|
||||
RUN bash install.sh \
|
||||
&& . "$NVM_DIR/nvm.sh" \
|
||||
&& nvm install ${NODE_VERSION} \
|
||||
&& nvm use v${NODE_VERSION} \
|
||||
&& nvm alias default v${NODE_VERSION}
|
||||
|
||||
# Create frappe-bench directories
|
||||
RUN mkdir -p apps logs commands sites /home/frappe/backups
|
||||
|
||||
# Setup python environment
|
||||
RUN virtualenv env \
|
||||
&& . env/bin/activate \
|
||||
&& pip install --upgrade pip \
|
||||
&& pip install gevent \
|
||||
&& cd apps \
|
||||
&& git clone --depth 1 -o upstream ${GIT_REPO} --branch ${GIT_BRANCH} \
|
||||
&& pip install --no-cache-dir -e /home/frappe/frappe-bench/apps/frappe
|
||||
RUN python -m venv env
|
||||
RUN env/bin/pip install --no-cache-dir wheel gevent
|
||||
|
||||
# Install nvm with node
|
||||
RUN wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash \
|
||||
&& . ${NVM_DIR}/nvm.sh \
|
||||
&& nvm install ${NODE_VERSION} \
|
||||
&& rm -rf ${NVM_DIR}/.cache
|
||||
|
||||
# Install Frappe
|
||||
RUN git clone --depth 1 -o upstream -b ${GIT_BRANCH} ${GIT_REPO} apps/frappe \
|
||||
&& env/bin/pip install --no-cache-dir -e apps/frappe
|
||||
|
||||
# Copy scripts and templates
|
||||
COPY build/common/commands/* /home/frappe/frappe-bench/commands/
|
||||
COPY build/common/common_site_config.json.template /opt/frappe/common_site_config.json.template
|
||||
COPY build/common/worker/install_app.sh /usr/local/bin/install_app
|
||||
COPY build/common/worker/bench /usr/local/bin/bench
|
||||
COPY build/common/worker/healthcheck.sh /usr/local/bin/healthcheck.sh
|
||||
COPY build/frappe-worker/commands/* /home/frappe/frappe-bench/commands/
|
||||
COPY build/frappe-worker/common_site_config.json.template /opt/frappe/common_site_config.json.template
|
||||
COPY build/frappe-worker/install_app.sh /usr/local/bin/install_app
|
||||
COPY build/frappe-worker/bench /usr/local/bin/bench
|
||||
COPY build/frappe-worker/healthcheck.sh /usr/local/bin/healthcheck.sh
|
||||
|
||||
# Use sites volume as working directory
|
||||
WORKDIR /home/frappe/frappe-bench/sites
|
||||
|
||||
22
build/frappe-worker/bench
Executable file
22
build/frappe-worker/bench
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/home/frappe/frappe-bench/env/bin/python
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
bench_dir = os.path.join(os.sep, "home", "frappe", "frappe-bench")
|
||||
sites_dir = os.path.join(bench_dir, "sites")
|
||||
bench_helper = os.path.join(
|
||||
bench_dir,
|
||||
"apps",
|
||||
"frappe",
|
||||
"frappe",
|
||||
"utils",
|
||||
"bench_helper.py",
|
||||
)
|
||||
cwd = os.getcwd()
|
||||
os.chdir(sites_dir)
|
||||
subprocess.check_call(
|
||||
[sys.executable, bench_helper, "frappe"] + sys.argv[1:],
|
||||
)
|
||||
@@ -1,14 +1,14 @@
|
||||
import os
|
||||
import semantic_version
|
||||
import git
|
||||
|
||||
import git
|
||||
import semantic_version
|
||||
from migrate import migrate_sites
|
||||
from utils import (
|
||||
save_version_file,
|
||||
get_apps,
|
||||
get_config,
|
||||
get_container_versions,
|
||||
get_version_file,
|
||||
get_config
|
||||
save_version_file,
|
||||
)
|
||||
|
||||
|
||||
@@ -30,12 +30,12 @@ def main():
|
||||
version_file_hash = None
|
||||
container_hash = None
|
||||
|
||||
repo = git.Repo(os.path.join('..', 'apps', app))
|
||||
repo = git.Repo(os.path.join("..", "apps", app))
|
||||
branch = repo.active_branch.name
|
||||
|
||||
if branch == 'develop':
|
||||
version_file_hash = version_file.get(app+'_git_hash')
|
||||
container_hash = container_versions.get(app+'_git_hash')
|
||||
if branch == "develop":
|
||||
version_file_hash = version_file.get(app + "_git_hash")
|
||||
container_hash = container_versions.get(app + "_git_hash")
|
||||
if container_hash and version_file_hash:
|
||||
if container_hash != version_file_hash:
|
||||
is_ready = True
|
||||
@@ -54,7 +54,7 @@ def main():
|
||||
|
||||
config = get_config()
|
||||
|
||||
if is_ready and config.get('maintenance_mode') != 1:
|
||||
if is_ready and config.get("maintenance_mode") != 1:
|
||||
migrate_sites(maintenance_mode=True)
|
||||
version_file = container_versions
|
||||
save_version_file(version_file)
|
||||
@@ -1,7 +1,8 @@
|
||||
import os
|
||||
|
||||
import frappe
|
||||
from frappe.utils.backups import scheduled_backup
|
||||
from frappe.utils import cint, get_sites, now
|
||||
from frappe.utils.backups import scheduled_backup
|
||||
|
||||
|
||||
def backup(sites, with_files=False):
|
||||
@@ -13,12 +14,17 @@ def backup(sites, with_files=False):
|
||||
backup_path_db=None,
|
||||
backup_path_files=None,
|
||||
backup_path_private_files=None,
|
||||
force=True
|
||||
force=True,
|
||||
)
|
||||
print("database backup taken -", odb.backup_path_db, "- on", now())
|
||||
if with_files:
|
||||
print("files backup taken -", odb.backup_path_files, "- on", now())
|
||||
print("private files backup taken -", odb.backup_path_private_files, "- on", now())
|
||||
print(
|
||||
"private files backup taken -",
|
||||
odb.backup_path_private_files,
|
||||
"- on",
|
||||
now(),
|
||||
)
|
||||
frappe.destroy()
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import socket
|
||||
import time
|
||||
|
||||
from constants import (
|
||||
DB_HOST_KEY,
|
||||
DB_PORT,
|
||||
DB_PORT_KEY,
|
||||
REDIS_CACHE_KEY,
|
||||
REDIS_QUEUE_KEY,
|
||||
REDIS_SOCKETIO_KEY,
|
||||
)
|
||||
from six.moves.urllib.parse import urlparse
|
||||
from utils import get_config
|
||||
from constants import (
|
||||
REDIS_QUEUE_KEY,
|
||||
REDIS_CACHE_KEY,
|
||||
REDIS_SOCKETIO_KEY,
|
||||
DB_HOST_KEY,
|
||||
DB_PORT_KEY,
|
||||
DB_PORT
|
||||
)
|
||||
|
||||
|
||||
def is_open(ip, port, timeout=30):
|
||||
@@ -29,7 +30,7 @@ def check_host(ip, port, retry=10, delay=3, print_attempt=True):
|
||||
ipup = False
|
||||
for i in range(retry):
|
||||
if print_attempt:
|
||||
print("Attempt {i} to connect to {ip}:{port}".format(ip=ip, port=port, i=i+1))
|
||||
print(f"Attempt {i+1} to connect to {ip}:{port}")
|
||||
if is_open(ip, port):
|
||||
ipup = True
|
||||
break
|
||||
@@ -40,30 +41,26 @@ def check_host(ip, port, retry=10, delay=3, print_attempt=True):
|
||||
|
||||
# Check service
|
||||
def check_service(
|
||||
retry=10,
|
||||
delay=3,
|
||||
print_attempt=True,
|
||||
service_name=None,
|
||||
service_port=None):
|
||||
retry=10, delay=3, print_attempt=True, service_name=None, service_port=None
|
||||
):
|
||||
|
||||
config = get_config()
|
||||
if not service_name:
|
||||
service_name = config.get(DB_HOST_KEY, 'mariadb')
|
||||
service_name = config.get(DB_HOST_KEY, "mariadb")
|
||||
if not service_port:
|
||||
service_port = config.get(DB_PORT_KEY, DB_PORT)
|
||||
|
||||
is_db_connected = False
|
||||
is_db_connected = check_host(
|
||||
service_name,
|
||||
service_port,
|
||||
retry,
|
||||
delay,
|
||||
print_attempt)
|
||||
service_name, service_port, retry, delay, print_attempt
|
||||
)
|
||||
if not is_db_connected:
|
||||
print("Connection to {service_name}:{service_port} timed out".format(
|
||||
service_name=service_name,
|
||||
service_port=service_port,
|
||||
))
|
||||
print(
|
||||
"Connection to {service_name}:{service_port} timed out".format(
|
||||
service_name=service_name,
|
||||
service_port=service_port,
|
||||
)
|
||||
)
|
||||
exit(1)
|
||||
|
||||
|
||||
@@ -71,14 +68,13 @@ def check_service(
|
||||
def check_redis_queue(retry=10, delay=3, print_attempt=True):
|
||||
check_redis_queue = False
|
||||
config = get_config()
|
||||
redis_queue_url = urlparse(config.get(REDIS_QUEUE_KEY, "redis://redis-queue:6379")).netloc
|
||||
redis_queue_url = urlparse(
|
||||
config.get(REDIS_QUEUE_KEY, "redis://redis-queue:6379")
|
||||
).netloc
|
||||
redis_queue, redis_queue_port = redis_queue_url.split(":")
|
||||
check_redis_queue = check_host(
|
||||
redis_queue,
|
||||
redis_queue_port,
|
||||
retry,
|
||||
delay,
|
||||
print_attempt)
|
||||
redis_queue, redis_queue_port, retry, delay, print_attempt
|
||||
)
|
||||
if not check_redis_queue:
|
||||
print("Connection to redis queue timed out")
|
||||
exit(1)
|
||||
@@ -88,14 +84,13 @@ def check_redis_queue(retry=10, delay=3, print_attempt=True):
|
||||
def check_redis_cache(retry=10, delay=3, print_attempt=True):
|
||||
check_redis_cache = False
|
||||
config = get_config()
|
||||
redis_cache_url = urlparse(config.get(REDIS_CACHE_KEY, "redis://redis-cache:6379")).netloc
|
||||
redis_cache_url = urlparse(
|
||||
config.get(REDIS_CACHE_KEY, "redis://redis-cache:6379")
|
||||
).netloc
|
||||
redis_cache, redis_cache_port = redis_cache_url.split(":")
|
||||
check_redis_cache = check_host(
|
||||
redis_cache,
|
||||
redis_cache_port,
|
||||
retry,
|
||||
delay,
|
||||
print_attempt)
|
||||
redis_cache, redis_cache_port, retry, delay, print_attempt
|
||||
)
|
||||
if not check_redis_cache:
|
||||
print("Connection to redis cache timed out")
|
||||
exit(1)
|
||||
@@ -105,14 +100,13 @@ def check_redis_cache(retry=10, delay=3, print_attempt=True):
|
||||
def check_redis_socketio(retry=10, delay=3, print_attempt=True):
|
||||
check_redis_socketio = False
|
||||
config = get_config()
|
||||
redis_socketio_url = urlparse(config.get(REDIS_SOCKETIO_KEY, "redis://redis-socketio:6379")).netloc
|
||||
redis_socketio_url = urlparse(
|
||||
config.get(REDIS_SOCKETIO_KEY, "redis://redis-socketio:6379")
|
||||
).netloc
|
||||
redis_socketio, redis_socketio_port = redis_socketio_url.split(":")
|
||||
check_redis_socketio = check_host(
|
||||
redis_socketio,
|
||||
redis_socketio_port,
|
||||
retry,
|
||||
delay,
|
||||
print_attempt)
|
||||
redis_socketio, redis_socketio_port, retry, delay, print_attempt
|
||||
)
|
||||
if not check_redis_socketio:
|
||||
print("Connection to redis socketio timed out")
|
||||
exit(1)
|
||||
@@ -123,7 +117,7 @@ def main():
|
||||
check_redis_queue()
|
||||
check_redis_cache()
|
||||
check_redis_socketio()
|
||||
print('Connections OK')
|
||||
print("Connections OK")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
13
build/frappe-worker/commands/constants.py
Normal file
13
build/frappe-worker/commands/constants.py
Normal file
@@ -0,0 +1,13 @@
|
||||
REDIS_QUEUE_KEY = "redis_queue"
|
||||
REDIS_CACHE_KEY = "redis_cache"
|
||||
REDIS_SOCKETIO_KEY = "redis_socketio"
|
||||
DB_HOST_KEY = "db_host"
|
||||
DB_PORT_KEY = "db_port"
|
||||
DB_PORT = 3306
|
||||
APP_VERSIONS_JSON_FILE = "app_versions.json"
|
||||
APPS_TXT_FILE = "apps.txt"
|
||||
COMMON_SITE_CONFIG_FILE = "common_site_config.json"
|
||||
DATE_FORMAT = "%Y%m%d_%H%M%S"
|
||||
RDS_DB = "rds_db"
|
||||
RDS_PRIVILEGES = "SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, CREATE TEMPORARY TABLES, CREATE VIEW, EVENT, TRIGGER, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, EXECUTE, LOCK TABLES"
|
||||
ARCHIVE_SITES_PATH = "/home/frappe/frappe-bench/sites/archive_sites"
|
||||
@@ -1,20 +1,20 @@
|
||||
import argparse
|
||||
|
||||
from check_connection import (
|
||||
check_service,
|
||||
check_redis_cache,
|
||||
check_redis_queue,
|
||||
check_redis_socketio,
|
||||
check_service,
|
||||
)
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
'-p',
|
||||
'--ping-service',
|
||||
dest='ping_services',
|
||||
action='append',
|
||||
"-p",
|
||||
"--ping-service",
|
||||
dest="ping_services",
|
||||
action="append",
|
||||
type=str,
|
||||
help='list of services to ping, e.g. doctor -p "postgres:5432" --ping-service "mariadb:3306"',
|
||||
)
|
||||
@@ -33,15 +33,15 @@ def main():
|
||||
check_redis_socketio(retry=1, delay=0, print_attempt=False)
|
||||
print("Redis SocketIO Connected")
|
||||
|
||||
if(args.ping_services):
|
||||
if args.ping_services:
|
||||
for service in args.ping_services:
|
||||
service_name = None
|
||||
service_port = None
|
||||
|
||||
try:
|
||||
service_name, service_port = service.split(':')
|
||||
service_name, service_port = service.split(":")
|
||||
except ValueError:
|
||||
print('Service should be in format host:port, e.g postgres:5432')
|
||||
print("Service should be in format host:port, e.g postgres:5432")
|
||||
exit(1)
|
||||
|
||||
check_service(
|
||||
@@ -51,7 +51,7 @@ def main():
|
||||
service_name=service_name,
|
||||
service_port=service_port,
|
||||
)
|
||||
print("{0}:{1} Connected".format(service_name, service_port))
|
||||
print(f"{service_name}:{service_port} Connected")
|
||||
|
||||
print("Health check successful")
|
||||
exit(0)
|
||||
@@ -1,2 +1,3 @@
|
||||
import gevent.monkey
|
||||
|
||||
gevent.monkey.patch_all()
|
||||
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
import frappe
|
||||
|
||||
import frappe
|
||||
from frappe.utils import cint, get_sites
|
||||
from utils import get_config, save_config
|
||||
|
||||
@@ -27,11 +27,12 @@ def migrate_sites(maintenance_mode=False):
|
||||
set_maintenance_mode(True)
|
||||
|
||||
for site in sites:
|
||||
print('Migrating', site)
|
||||
print("Migrating", site)
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
try:
|
||||
from frappe.migrate import migrate
|
||||
|
||||
migrate()
|
||||
finally:
|
||||
frappe.destroy()
|
||||
@@ -1,15 +1,10 @@
|
||||
import os
|
||||
|
||||
import frappe
|
||||
import semantic_version
|
||||
|
||||
from frappe.installer import update_site_config
|
||||
from constants import COMMON_SITE_CONFIG_FILE, RDS_DB, RDS_PRIVILEGES
|
||||
from utils import (
|
||||
run_command,
|
||||
get_config,
|
||||
get_site_config,
|
||||
get_password,
|
||||
)
|
||||
from frappe.installer import update_site_config
|
||||
from utils import get_config, get_password, get_site_config, run_command
|
||||
|
||||
# try to import _new_site from frappe, which could possibly
|
||||
# exist in either commands.py or installer.py, and so we need
|
||||
@@ -100,15 +95,15 @@ def main():
|
||||
|
||||
mysql_command = [
|
||||
"mysql",
|
||||
"-h{db_host}".format(db_host=db_host),
|
||||
"-u{db_root_username}".format(db_root_username=db_root_username),
|
||||
"-p{mariadb_root_password}".format(mariadb_root_password=mariadb_root_password),
|
||||
f"-h{db_host}",
|
||||
f"-u{db_root_username}",
|
||||
f"-p{mariadb_root_password}",
|
||||
"-e",
|
||||
]
|
||||
|
||||
# Drop User if exists
|
||||
command = mysql_command + [
|
||||
"DROP USER IF EXISTS '{db_name}'; FLUSH PRIVILEGES;".format(db_name=db_name)
|
||||
f"DROP USER IF EXISTS '{db_name}'; FLUSH PRIVILEGES;"
|
||||
]
|
||||
run_command(command)
|
||||
|
||||
@@ -120,14 +115,10 @@ def main():
|
||||
grant_privileges = RDS_PRIVILEGES
|
||||
|
||||
command = mysql_command + [
|
||||
"\
|
||||
f"\
|
||||
CREATE USER IF NOT EXISTS '{db_name}'@'%' IDENTIFIED BY '{db_password}'; \
|
||||
GRANT {grant_privileges} ON `{db_name}`.* TO '{db_name}'@'%'; \
|
||||
FLUSH PRIVILEGES;".format(
|
||||
db_name=db_name,
|
||||
db_password=db_password,
|
||||
grant_privileges=grant_privileges,
|
||||
)
|
||||
FLUSH PRIVILEGES;"
|
||||
]
|
||||
run_command(command)
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import datetime
|
||||
import os
|
||||
import time
|
||||
import boto3
|
||||
|
||||
import datetime
|
||||
from glob import glob
|
||||
from frappe.utils import get_sites
|
||||
|
||||
import boto3
|
||||
from constants import DATE_FORMAT
|
||||
from utils import (
|
||||
get_s3_config,
|
||||
upload_file_to_s3,
|
||||
check_s3_environment_variables,
|
||||
)
|
||||
from frappe.utils import get_sites
|
||||
from utils import check_s3_environment_variables, get_s3_config, upload_file_to_s3
|
||||
|
||||
|
||||
def get_file_ext():
|
||||
@@ -18,7 +14,7 @@ def get_file_ext():
|
||||
"database": "-database.sql.gz",
|
||||
"private_files": "-private-files.tar",
|
||||
"public_files": "-files.tar",
|
||||
"site_config": "-site_config_backup.json"
|
||||
"site_config": "-site_config_backup.json",
|
||||
}
|
||||
|
||||
|
||||
@@ -31,19 +27,26 @@ def get_backup_details(sitename):
|
||||
|
||||
if os.path.exists(site_backup_path):
|
||||
for filetype, ext in file_ext.items():
|
||||
site_slug = sitename.replace('.', '_')
|
||||
pattern = site_backup_path + '*-' + site_slug + ext
|
||||
site_slug = sitename.replace(".", "_")
|
||||
pattern = site_backup_path + "*-" + site_slug + ext
|
||||
backup_files = list(filter(os.path.isfile, glob(pattern)))
|
||||
|
||||
if len(backup_files) > 0:
|
||||
backup_files.sort(key=lambda file: os.stat(os.path.join(site_backup_path, file)).st_ctime)
|
||||
backup_date = datetime.datetime.strptime(time.ctime(os.path.getmtime(backup_files[0])), "%a %b %d %H:%M:%S %Y")
|
||||
backup_files.sort(
|
||||
key=lambda file: os.stat(
|
||||
os.path.join(site_backup_path, file)
|
||||
).st_ctime
|
||||
)
|
||||
backup_date = datetime.datetime.strptime(
|
||||
time.ctime(os.path.getmtime(backup_files[0])),
|
||||
"%a %b %d %H:%M:%S %Y",
|
||||
)
|
||||
backup_details[filetype] = {
|
||||
"sitename": sitename,
|
||||
"file_size_in_bytes": os.stat(backup_files[-1]).st_size,
|
||||
"file_path": os.path.abspath(backup_files[-1]),
|
||||
"filename": os.path.basename(backup_files[-1]),
|
||||
"backup_date": backup_date.date().strftime("%Y-%m-%d %H:%M:%S")
|
||||
"backup_date": backup_date.date().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
}
|
||||
|
||||
return backup_details
|
||||
@@ -54,31 +57,34 @@ def delete_old_backups(limit, bucket, site_name):
|
||||
all_backup_dates = list()
|
||||
backup_limit = int(limit)
|
||||
check_s3_environment_variables()
|
||||
bucket_dir = os.environ.get('BUCKET_DIR')
|
||||
bucket_dir = os.environ.get("BUCKET_DIR")
|
||||
oldest_backup_date = None
|
||||
|
||||
s3 = boto3.resource(
|
||||
's3',
|
||||
region_name=os.environ.get('REGION'),
|
||||
aws_access_key_id=os.environ.get('ACCESS_KEY_ID'),
|
||||
aws_secret_access_key=os.environ.get('SECRET_ACCESS_KEY'),
|
||||
endpoint_url=os.environ.get('ENDPOINT_URL')
|
||||
"s3",
|
||||
region_name=os.environ.get("REGION"),
|
||||
aws_access_key_id=os.environ.get("ACCESS_KEY_ID"),
|
||||
aws_secret_access_key=os.environ.get("SECRET_ACCESS_KEY"),
|
||||
endpoint_url=os.environ.get("ENDPOINT_URL"),
|
||||
)
|
||||
|
||||
bucket = s3.Bucket(bucket)
|
||||
objects = bucket.meta.client.list_objects_v2(
|
||||
Bucket=bucket.name,
|
||||
Delimiter='/')
|
||||
objects = bucket.meta.client.list_objects_v2(Bucket=bucket.name, Delimiter="/")
|
||||
|
||||
if objects:
|
||||
for obj in objects.get('CommonPrefixes'):
|
||||
if obj.get('Prefix') == bucket_dir + '/':
|
||||
for backup_obj in bucket.objects.filter(Prefix=obj.get('Prefix')):
|
||||
for obj in objects.get("CommonPrefixes"):
|
||||
if obj.get("Prefix") == bucket_dir + "/":
|
||||
for backup_obj in bucket.objects.filter(Prefix=obj.get("Prefix")):
|
||||
if backup_obj.get()["ContentType"] == "application/x-directory":
|
||||
continue
|
||||
try:
|
||||
# backup_obj.key is bucket_dir/site/date_time/backupfile.extension
|
||||
bucket_dir, site_slug, date_time, backupfile = backup_obj.key.split('/')
|
||||
(
|
||||
bucket_dir,
|
||||
site_slug,
|
||||
date_time,
|
||||
backupfile,
|
||||
) = backup_obj.key.split("/")
|
||||
date_time_object = datetime.datetime.strptime(
|
||||
date_time, DATE_FORMAT
|
||||
)
|
||||
@@ -98,7 +104,7 @@ def delete_old_backups(limit, bucket, site_name):
|
||||
for backup in all_backups:
|
||||
try:
|
||||
# backup is bucket_dir/site/date_time/backupfile.extension
|
||||
backup_dir, site_slug, backup_dt_string, filename = backup.split('/')
|
||||
backup_dir, site_slug, backup_dt_string, filename = backup.split("/")
|
||||
backup_datetime = datetime.datetime.strptime(
|
||||
backup_dt_string, DATE_FORMAT
|
||||
)
|
||||
@@ -113,7 +119,7 @@ def delete_old_backups(limit, bucket, site_name):
|
||||
for obj in bucket.objects.filter(Prefix=oldest_backup):
|
||||
# delete all keys that are inside the oldest_backup
|
||||
if bucket_dir in obj.key:
|
||||
print('Deleteing ' + obj.key)
|
||||
print("Deleting " + obj.key)
|
||||
s3.Object(bucket.name, obj.key).delete()
|
||||
|
||||
|
||||
@@ -124,31 +130,52 @@ def main():
|
||||
|
||||
for site in sites:
|
||||
details = get_backup_details(site)
|
||||
db_file = details.get('database', {}).get('file_path')
|
||||
folder = os.environ.get('BUCKET_DIR') + '/' + site + '/'
|
||||
db_file = details.get("database", {}).get("file_path")
|
||||
folder = os.environ.get("BUCKET_DIR") + "/" + site + "/"
|
||||
if db_file:
|
||||
folder = os.environ.get('BUCKET_DIR') + '/' + site + '/' + os.path.basename(db_file)[:15] + '/'
|
||||
folder = (
|
||||
os.environ.get("BUCKET_DIR")
|
||||
+ "/"
|
||||
+ site
|
||||
+ "/"
|
||||
+ os.path.basename(db_file)[:15]
|
||||
+ "/"
|
||||
)
|
||||
upload_file_to_s3(db_file, folder, conn, bucket)
|
||||
|
||||
# Archive site_config.json
|
||||
site_config_file = details.get('site_config', {}).get('file_path')
|
||||
site_config_file = details.get("site_config", {}).get("file_path")
|
||||
if not site_config_file:
|
||||
site_config_file = os.path.join(os.getcwd(), site, 'site_config.json')
|
||||
site_config_file = os.path.join(os.getcwd(), site, "site_config.json")
|
||||
upload_file_to_s3(site_config_file, folder, conn, bucket)
|
||||
|
||||
public_files = details.get('public_files', {}).get('file_path')
|
||||
public_files = details.get("public_files", {}).get("file_path")
|
||||
if public_files:
|
||||
folder = os.environ.get('BUCKET_DIR') + '/' + site + '/' + os.path.basename(public_files)[:15] + '/'
|
||||
folder = (
|
||||
os.environ.get("BUCKET_DIR")
|
||||
+ "/"
|
||||
+ site
|
||||
+ "/"
|
||||
+ os.path.basename(public_files)[:15]
|
||||
+ "/"
|
||||
)
|
||||
upload_file_to_s3(public_files, folder, conn, bucket)
|
||||
|
||||
private_files = details.get('private_files', {}).get('file_path')
|
||||
private_files = details.get("private_files", {}).get("file_path")
|
||||
if private_files:
|
||||
folder = os.environ.get('BUCKET_DIR') + '/' + site + '/' + os.path.basename(private_files)[:15] + '/'
|
||||
folder = (
|
||||
os.environ.get("BUCKET_DIR")
|
||||
+ "/"
|
||||
+ site
|
||||
+ "/"
|
||||
+ os.path.basename(private_files)[:15]
|
||||
+ "/"
|
||||
)
|
||||
upload_file_to_s3(private_files, folder, conn, bucket)
|
||||
|
||||
delete_old_backups(os.environ.get('BACKUP_LIMIT', '3'), bucket, site)
|
||||
delete_old_backups(os.environ.get("BACKUP_LIMIT", "3"), bucket, site)
|
||||
|
||||
print('push-backup complete')
|
||||
print("push-backup complete")
|
||||
exit(0)
|
||||
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import os
|
||||
import datetime
|
||||
import tarfile
|
||||
import hashlib
|
||||
import frappe
|
||||
import boto3
|
||||
import os
|
||||
import tarfile
|
||||
|
||||
from frappe.utils import get_sites, random_string
|
||||
import boto3
|
||||
import frappe
|
||||
from constants import COMMON_SITE_CONFIG_FILE, DATE_FORMAT, RDS_DB, RDS_PRIVILEGES
|
||||
from frappe.installer import (
|
||||
make_conf,
|
||||
get_conf_params,
|
||||
make_conf,
|
||||
make_site_dirs,
|
||||
update_site_config,
|
||||
)
|
||||
from constants import COMMON_SITE_CONFIG_FILE, DATE_FORMAT, RDS_DB, RDS_PRIVILEGES
|
||||
from frappe.utils import get_sites, random_string
|
||||
from utils import (
|
||||
run_command,
|
||||
list_directories,
|
||||
set_key_in_site_config,
|
||||
get_site_config,
|
||||
check_s3_environment_variables,
|
||||
get_config,
|
||||
get_password,
|
||||
check_s3_environment_variables,
|
||||
get_site_config,
|
||||
list_directories,
|
||||
run_command,
|
||||
set_key_in_site_config,
|
||||
)
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ def get_backup_dir():
|
||||
def decompress_db(database_file, site):
|
||||
command = ["gunzip", "-c", database_file]
|
||||
with open(database_file.replace(".gz", ""), "w") as db_file:
|
||||
print("Extract Database GZip for site {}".format(site))
|
||||
print(f"Extract Database GZip for site {site}")
|
||||
run_command(command, stdout=db_file)
|
||||
|
||||
|
||||
@@ -60,6 +60,11 @@ def restore_database(files_base, site_config_path, site):
|
||||
config=config, site_config=site_config, database_file=database_file
|
||||
)
|
||||
is_database_restored = True
|
||||
elif db_type == "postgres":
|
||||
restore_postgres(
|
||||
config=config, site_config=site_config, database_file=database_file
|
||||
)
|
||||
is_database_restored = True
|
||||
|
||||
if is_database_restored:
|
||||
# Set encryption_key if it exists in backup site_config.json
|
||||
@@ -70,14 +75,14 @@ def restore_files(files_base):
|
||||
public_files = files_base + "-files.tar"
|
||||
# extract tar
|
||||
public_tar = tarfile.open(public_files)
|
||||
print("Extracting {}".format(public_files))
|
||||
print(f"Extracting {public_files}")
|
||||
public_tar.extractall()
|
||||
|
||||
|
||||
def restore_private_files(files_base):
|
||||
private_files = files_base + "-private-files.tar"
|
||||
private_tar = tarfile.open(private_files)
|
||||
print("Extracting {}".format(private_files))
|
||||
print(f"Extracting {private_files}")
|
||||
private_tar.extractall()
|
||||
|
||||
|
||||
@@ -131,12 +136,71 @@ def pull_backup_from_s3():
|
||||
if backup in backup_file:
|
||||
if not os.path.exists(os.path.dirname(backup_file)):
|
||||
os.makedirs(os.path.dirname(backup_file))
|
||||
print("Downloading {}".format(backup_file))
|
||||
print(f"Downloading {backup_file}")
|
||||
bucket.download_file(bucket_dir + "/" + backup_file, backup_file)
|
||||
|
||||
os.chdir(os.path.join(os.path.expanduser("~"), "frappe-bench", "sites"))
|
||||
|
||||
|
||||
def restore_postgres(config, site_config, database_file):
|
||||
# common config
|
||||
common_site_config_path = os.path.join(os.getcwd(), COMMON_SITE_CONFIG_FILE)
|
||||
|
||||
db_root_user = config.get("root_login")
|
||||
if not db_root_user:
|
||||
postgres_user = os.environ.get("DB_ROOT_USER")
|
||||
if not postgres_user:
|
||||
print("Variable DB_ROOT_USER not set")
|
||||
exit(1)
|
||||
|
||||
db_root_user = postgres_user
|
||||
update_site_config(
|
||||
"root_login",
|
||||
db_root_user,
|
||||
validate=False,
|
||||
site_config_path=common_site_config_path,
|
||||
)
|
||||
|
||||
db_root_password = config.get("root_password")
|
||||
if not db_root_password:
|
||||
root_password = get_password("POSTGRES_PASSWORD")
|
||||
if not root_password:
|
||||
print("Variable POSTGRES_PASSWORD not set")
|
||||
exit(1)
|
||||
|
||||
db_root_password = root_password
|
||||
update_site_config(
|
||||
"root_password",
|
||||
db_root_password,
|
||||
validate=False,
|
||||
site_config_path=common_site_config_path,
|
||||
)
|
||||
|
||||
# site config
|
||||
db_host = site_config.get("db_host")
|
||||
db_port = site_config.get("db_port", 5432)
|
||||
db_name = site_config.get("db_name")
|
||||
db_password = site_config.get("db_password")
|
||||
|
||||
psql_command = ["psql"]
|
||||
psql_uri = f"postgres://{db_root_user}:{db_root_password}@{db_host}:{db_port}"
|
||||
|
||||
print("Restoring PostgreSQL")
|
||||
run_command(psql_command + [psql_uri, "-c", f'DROP DATABASE IF EXISTS "{db_name}"'])
|
||||
run_command(psql_command + [psql_uri, "-c", f"DROP USER IF EXISTS {db_name}"])
|
||||
run_command(psql_command + [psql_uri, "-c", f'CREATE DATABASE "{db_name}"'])
|
||||
run_command(
|
||||
psql_command
|
||||
+ [psql_uri, "-c", f"CREATE user {db_name} password '{db_password}'"]
|
||||
)
|
||||
run_command(
|
||||
psql_command
|
||||
+ [psql_uri, "-c", f'GRANT ALL PRIVILEGES ON DATABASE "{db_name}" TO {db_name}']
|
||||
)
|
||||
with open(database_file.replace(".gz", "")) as db_file:
|
||||
run_command(psql_command + [f"{psql_uri}/{db_name}", "<"], stdin=db_file)
|
||||
|
||||
|
||||
def restore_mariadb(config, site_config, database_file):
|
||||
db_root_password = get_password("MYSQL_ROOT_PASSWORD")
|
||||
if not db_root_password:
|
||||
@@ -153,30 +217,27 @@ def restore_mariadb(config, site_config, database_file):
|
||||
# mysql command prefix
|
||||
mysql_command = [
|
||||
"mysql",
|
||||
"-u{db_root_user}".format(db_root_user=db_root_user),
|
||||
"-h{db_host}".format(db_host=db_host),
|
||||
"-p{db_root_password}".format(db_root_password=db_root_password),
|
||||
"-P{db_port}".format(db_port=db_port),
|
||||
f"-u{db_root_user}",
|
||||
f"-h{db_host}",
|
||||
f"-p{db_root_password}",
|
||||
f"-P{db_port}",
|
||||
]
|
||||
|
||||
# drop db if exists for clean restore
|
||||
drop_database = mysql_command + ["-e", "DROP DATABASE IF EXISTS `{db_name}`;".format(db_name=db_name)]
|
||||
drop_database = mysql_command + ["-e", f"DROP DATABASE IF EXISTS `{db_name}`;"]
|
||||
run_command(drop_database)
|
||||
|
||||
# create db
|
||||
create_database = mysql_command + [
|
||||
"-e",
|
||||
"CREATE DATABASE IF NOT EXISTS `{db_name}`;".format(db_name=db_name),
|
||||
f"CREATE DATABASE IF NOT EXISTS `{db_name}`;",
|
||||
]
|
||||
run_command(create_database)
|
||||
|
||||
# create user
|
||||
create_user = mysql_command + [
|
||||
"-e",
|
||||
"CREATE USER IF NOT EXISTS '{db_name}'@'%' IDENTIFIED BY '{db_password}'; FLUSH PRIVILEGES;".format(
|
||||
db_name=db_name,
|
||||
db_password=db_password,
|
||||
),
|
||||
f"CREATE USER IF NOT EXISTS '{db_name}'@'%' IDENTIFIED BY '{db_password}'; FLUSH PRIVILEGES;",
|
||||
]
|
||||
run_command(create_user)
|
||||
|
||||
@@ -190,17 +251,13 @@ def restore_mariadb(config, site_config, database_file):
|
||||
|
||||
grant_privileges_command = mysql_command + [
|
||||
"-e",
|
||||
"GRANT {grant_privileges} ON `{db_name}`.* TO '{db_name}'@'%' IDENTIFIED BY '{db_password}'; FLUSH PRIVILEGES;".format(
|
||||
grant_privileges=grant_privileges,
|
||||
db_name=db_name,
|
||||
db_password=db_password,
|
||||
),
|
||||
f"GRANT {grant_privileges} ON `{db_name}`.* TO '{db_name}'@'%' IDENTIFIED BY '{db_password}'; FLUSH PRIVILEGES;",
|
||||
]
|
||||
run_command(grant_privileges_command)
|
||||
|
||||
print("Restoring MariaDB")
|
||||
with open(database_file.replace(".gz", ""), "r") as db_file:
|
||||
run_command(mysql_command + ["{db_name}".format(db_name=db_name)], stdin=db_file)
|
||||
with open(database_file.replace(".gz", "")) as db_file:
|
||||
run_command(mysql_command + [f"{db_name}"], stdin=db_file)
|
||||
|
||||
|
||||
def main():
|
||||
@@ -222,7 +279,7 @@ def main():
|
||||
if not os.path.exists(site_config_path):
|
||||
site_config_path = os.path.join(backup_dir, site, "site_config.json")
|
||||
if site in get_sites():
|
||||
print("Overwrite site {}".format(site))
|
||||
print(f"Overwrite site {site}")
|
||||
restore_database(files_base, site_config_path, site)
|
||||
restore_private_files(files_base)
|
||||
restore_files(files_base)
|
||||
@@ -241,7 +298,7 @@ def main():
|
||||
)
|
||||
make_site_dirs()
|
||||
|
||||
print("Create site {}".format(site))
|
||||
print(f"Create site {site}")
|
||||
restore_database(files_base, site_config_path, site)
|
||||
restore_private_files(files_base)
|
||||
restore_files(files_base)
|
||||
@@ -1,15 +1,12 @@
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import boto3
|
||||
import git
|
||||
|
||||
from constants import APP_VERSIONS_JSON_FILE, APPS_TXT_FILE, COMMON_SITE_CONFIG_FILE
|
||||
from frappe.installer import update_site_config
|
||||
from constants import (
|
||||
APP_VERSIONS_JSON_FILE,
|
||||
APPS_TXT_FILE,
|
||||
COMMON_SITE_CONFIG_FILE
|
||||
)
|
||||
|
||||
|
||||
def run_command(command, stdout=None, stdin=None, stderr=None):
|
||||
stdout = stdout or subprocess.PIPE
|
||||
@@ -19,14 +16,14 @@ def run_command(command, stdout=None, stdin=None, stderr=None):
|
||||
out, error = process.communicate()
|
||||
if process.returncode:
|
||||
print("Something went wrong:")
|
||||
print("return code: {returncode}".format(returncode=process.returncode))
|
||||
print("stdout:\n{out}".format(out=out))
|
||||
print("\nstderr:\n{error}".format(error=error))
|
||||
print(f"return code: {process.returncode}")
|
||||
print(f"stdout:\n{out}")
|
||||
print(f"\nstderr:\n{error}")
|
||||
exit(process.returncode)
|
||||
|
||||
|
||||
def save_version_file(versions):
|
||||
with open(APP_VERSIONS_JSON_FILE, 'w') as f:
|
||||
with open(APP_VERSIONS_JSON_FILE, "w") as f:
|
||||
return json.dump(versions, f, indent=1, sort_keys=True)
|
||||
|
||||
|
||||
@@ -58,10 +55,10 @@ def get_container_versions(apps):
|
||||
pass
|
||||
|
||||
try:
|
||||
path = os.path.join('..', 'apps', app)
|
||||
path = os.path.join("..", "apps", app)
|
||||
repo = git.Repo(path)
|
||||
commit_hash = repo.head.object.hexsha
|
||||
versions.update({app+'_git_hash': commit_hash})
|
||||
versions.update({app + "_git_hash": commit_hash})
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -94,18 +91,22 @@ def get_config():
|
||||
|
||||
def get_site_config(site_name):
|
||||
site_config = None
|
||||
with open('{site_name}/site_config.json'.format(site_name=site_name)) as site_config_file:
|
||||
with open(f"{site_name}/site_config.json") as site_config_file:
|
||||
site_config = json.load(site_config_file)
|
||||
return site_config
|
||||
|
||||
|
||||
def save_config(config):
|
||||
with open(COMMON_SITE_CONFIG_FILE, 'w') as f:
|
||||
with open(COMMON_SITE_CONFIG_FILE, "w") as f:
|
||||
return json.dump(config, f, indent=1, sort_keys=True)
|
||||
|
||||
|
||||
def get_password(env_var, default=None):
|
||||
return os.environ.get(env_var) or get_password_from_secret("{env_var}_FILE".format(env_var=env_var)) or default
|
||||
return (
|
||||
os.environ.get(env_var)
|
||||
or get_password_from_secret(f"{env_var}_FILE")
|
||||
or default
|
||||
)
|
||||
|
||||
|
||||
def get_password_from_secret(env_var):
|
||||
@@ -128,14 +129,14 @@ def get_password_from_secret(env_var):
|
||||
|
||||
def get_s3_config():
|
||||
check_s3_environment_variables()
|
||||
bucket = os.environ.get('BUCKET_NAME')
|
||||
bucket = os.environ.get("BUCKET_NAME")
|
||||
|
||||
conn = boto3.client(
|
||||
's3',
|
||||
region_name=os.environ.get('REGION'),
|
||||
aws_access_key_id=os.environ.get('ACCESS_KEY_ID'),
|
||||
aws_secret_access_key=os.environ.get('SECRET_ACCESS_KEY'),
|
||||
endpoint_url=os.environ.get('ENDPOINT_URL')
|
||||
"s3",
|
||||
region_name=os.environ.get("REGION"),
|
||||
aws_access_key_id=os.environ.get("ACCESS_KEY_ID"),
|
||||
aws_secret_access_key=os.environ.get("SECRET_ACCESS_KEY"),
|
||||
endpoint_url=os.environ.get("ENDPOINT_URL"),
|
||||
)
|
||||
|
||||
return conn, bucket
|
||||
@@ -164,7 +165,7 @@ def list_directories(path):
|
||||
def get_site_config_from_path(site_config_path):
|
||||
site_config = dict()
|
||||
if os.path.exists(site_config_path):
|
||||
with open(site_config_path, 'r') as sc:
|
||||
with open(site_config_path) as sc:
|
||||
site_config = json.load(sc)
|
||||
return site_config
|
||||
|
||||
@@ -173,32 +174,35 @@ def set_key_in_site_config(key, site, site_config_path):
|
||||
site_config = get_site_config_from_path(site_config_path)
|
||||
value = site_config.get(key)
|
||||
if value:
|
||||
print('Set {key} in site config for site: {site}'.format(key=key, site=site))
|
||||
update_site_config(key, value,
|
||||
site_config_path=os.path.join(os.getcwd(), site, "site_config.json"))
|
||||
print(f"Set {key} in site config for site: {site}")
|
||||
update_site_config(
|
||||
key,
|
||||
value,
|
||||
site_config_path=os.path.join(os.getcwd(), site, "site_config.json"),
|
||||
)
|
||||
|
||||
|
||||
def check_s3_environment_variables():
|
||||
if 'BUCKET_NAME' not in os.environ:
|
||||
print('Variable BUCKET_NAME not set')
|
||||
if "BUCKET_NAME" not in os.environ:
|
||||
print("Variable BUCKET_NAME not set")
|
||||
exit(1)
|
||||
|
||||
if 'ACCESS_KEY_ID' not in os.environ:
|
||||
print('Variable ACCESS_KEY_ID not set')
|
||||
if "ACCESS_KEY_ID" not in os.environ:
|
||||
print("Variable ACCESS_KEY_ID not set")
|
||||
exit(1)
|
||||
|
||||
if 'SECRET_ACCESS_KEY' not in os.environ:
|
||||
print('Variable SECRET_ACCESS_KEY not set')
|
||||
if "SECRET_ACCESS_KEY" not in os.environ:
|
||||
print("Variable SECRET_ACCESS_KEY not set")
|
||||
exit(1)
|
||||
|
||||
if 'ENDPOINT_URL' not in os.environ:
|
||||
print('Variable ENDPOINT_URL not set')
|
||||
if "ENDPOINT_URL" not in os.environ:
|
||||
print("Variable ENDPOINT_URL not set")
|
||||
exit(1)
|
||||
|
||||
if 'BUCKET_DIR' not in os.environ:
|
||||
print('Variable BUCKET_DIR not set')
|
||||
if "BUCKET_DIR" not in os.environ:
|
||||
print("Variable BUCKET_DIR not set")
|
||||
exit(1)
|
||||
|
||||
if 'REGION' not in os.environ:
|
||||
print('Variable REGION not set')
|
||||
if "REGION" not in os.environ:
|
||||
print("Variable REGION not set")
|
||||
exit(1)
|
||||
191
build/frappe-worker/docker-entrypoint.sh
Executable file
191
build/frappe-worker/docker-entrypoint.sh
Executable file
@@ -0,0 +1,191 @@
|
||||
#!/bin/bash
|
||||
|
||||
function configureEnv() {
|
||||
if [[ ! -f /home/frappe/frappe-bench/sites/common_site_config.json ]]; then
|
||||
|
||||
if [[ -z "${MARIADB_HOST}" && -z "${POSTGRES_HOST}" ]]; then
|
||||
echo "MARIADB_HOST or POSTGRES_HOST is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${REDIS_CACHE}" ]]; then
|
||||
echo "REDIS_CACHE is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${REDIS_QUEUE}" ]]; then
|
||||
echo "REDIS_QUEUE is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${REDIS_SOCKETIO}" ]]; then
|
||||
echo "REDIS_SOCKETIO is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${SOCKETIO_PORT}" ]]; then
|
||||
echo "SOCKETIO_PORT is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${DB_PORT}" ]]; then
|
||||
export DB_PORT=3306
|
||||
fi
|
||||
|
||||
export DB_HOST="${MARIADB_HOST:-$POSTGRES_HOST}"
|
||||
|
||||
# shellcheck disable=SC2016
|
||||
envsubst '${DB_HOST}
|
||||
${DB_PORT}
|
||||
${REDIS_CACHE}
|
||||
${REDIS_QUEUE}
|
||||
${REDIS_SOCKETIO}
|
||||
${SOCKETIO_PORT}' </opt/frappe/common_site_config.json.template >/home/frappe/frappe-bench/sites/common_site_config.json
|
||||
fi
|
||||
}
|
||||
|
||||
function checkConnection() {
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/check_connection.py
|
||||
}
|
||||
|
||||
function checkConfigExists() {
|
||||
COUNTER=0
|
||||
while [[ ! -e /home/frappe/frappe-bench/sites/common_site_config.json && ${COUNTER} -le 30 ]]; do
|
||||
sleep 1
|
||||
((COUNTER = COUNTER + 1))
|
||||
echo "config file not created, retry ${COUNTER}" >&2
|
||||
done
|
||||
|
||||
if [[ ! -e /home/frappe/frappe-bench/sites/common_site_config.json ]]; then
|
||||
echo "timeout: config file not created" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ ! -e /home/frappe/frappe-bench/sites/apps.txt ]]; then
|
||||
find /home/frappe/frappe-bench/apps -mindepth 1 -maxdepth 1 -type d -printf '%f\n' |
|
||||
sort -r >/home/frappe/frappe-bench/sites/apps.txt
|
||||
fi
|
||||
|
||||
# symlink node_modules
|
||||
ln -sfn /home/frappe/frappe-bench/sites/assets/frappe/node_modules \
|
||||
/home/frappe/frappe-bench/apps/frappe/node_modules
|
||||
|
||||
case "$1" in
|
||||
|
||||
start)
|
||||
configureEnv
|
||||
checkConnection
|
||||
|
||||
[[ -z "${WORKERS}" ]] && WORKERS='2'
|
||||
|
||||
[[ -z "${FRAPPE_PORT}" ]] && FRAPPE_PORT='8000'
|
||||
|
||||
[[ -z "${WORKER_CLASS}" ]] && WORKER_CLASS='gthread'
|
||||
|
||||
LOAD_CONFIG_FILE=""
|
||||
[[ "${WORKER_CLASS}" == "gevent" ]] &&
|
||||
LOAD_CONFIG_FILE="-c /home/frappe/frappe-bench/commands/gevent_patch.py"
|
||||
|
||||
if [[ -n "${AUTO_MIGRATE}" ]]; then
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/auto_migrate.py
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
/home/frappe/frappe-bench/env/bin/gunicorn ${LOAD_CONFIG_FILE} -b 0.0.0.0:${FRAPPE_PORT} \
|
||||
--worker-tmp-dir /dev/shm \
|
||||
--threads=4 \
|
||||
--workers ${WORKERS} \
|
||||
--worker-class=${WORKER_CLASS} \
|
||||
--log-file=- \
|
||||
-t 120 frappe.app:application --preload
|
||||
;;
|
||||
|
||||
worker)
|
||||
checkConfigExists
|
||||
checkConnection
|
||||
|
||||
: "${WORKER_TYPE:=default}"
|
||||
bench worker --queue $WORKER_TYPE
|
||||
;;
|
||||
|
||||
schedule)
|
||||
checkConfigExists
|
||||
checkConnection
|
||||
|
||||
bench schedule
|
||||
;;
|
||||
|
||||
new)
|
||||
checkConfigExists
|
||||
checkConnection
|
||||
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/new.py
|
||||
exit
|
||||
;;
|
||||
|
||||
drop)
|
||||
checkConfigExists
|
||||
checkConnection
|
||||
|
||||
: "${SITE_NAME:=site1.localhost}"
|
||||
: "${DB_ROOT_USER:=root}"
|
||||
: "${DB_ROOT_PASSWORD:=$POSTGRES_PASSWORD}"
|
||||
: "${DB_ROOT_PASSWORD:=$MYSQL_ROOT_PASSWORD}"
|
||||
: "${DB_ROOT_PASSWORD:=admin}"
|
||||
|
||||
FLAGS=
|
||||
if [[ ${NO_BACKUP} == 1 ]]; then
|
||||
FLAGS="${FLAGS} --no-backup"
|
||||
fi
|
||||
if [[ ${FORCE} == 1 ]]; then
|
||||
FLAGS="${FLAGS} --force"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
bench drop-site \
|
||||
${SITE_NAME} \
|
||||
--root-login ${DB_ROOT_USER} \
|
||||
--root-password ${DB_ROOT_PASSWORD} \
|
||||
--archived-sites-path /home/frappe/frappe-bench/sites/archive_sites \
|
||||
${FLAGS}
|
||||
;;
|
||||
|
||||
migrate)
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/migrate.py
|
||||
exit
|
||||
;;
|
||||
|
||||
doctor)
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/doctor.py "${@:2}"
|
||||
exit
|
||||
;;
|
||||
|
||||
backup)
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/backup.py
|
||||
exit
|
||||
;;
|
||||
|
||||
console)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "Need to specify a sitename with the command:" >&2
|
||||
echo "console <sitename>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bench --site "$2" console
|
||||
;;
|
||||
|
||||
push-backup)
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/push_backup.py
|
||||
exit
|
||||
;;
|
||||
|
||||
restore-backup)
|
||||
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/commands/restore_backup.py
|
||||
exit
|
||||
;;
|
||||
*)
|
||||
exec "$@"
|
||||
;;
|
||||
esac
|
||||
@@ -17,17 +17,17 @@ fi
|
||||
# Set REDIS host:port
|
||||
REDIS_CACHE=$(getUrl "${COMMON_SITE_CONFIG_JSON}" "redis_cache" | sed 's|redis://||g')
|
||||
if [[ "${REDIS_CACHE}" == *"/"* ]]; then
|
||||
REDIS_CACHE=$(echo ${REDIS_CACHE} | cut -f1 -d"/")
|
||||
REDIS_CACHE=$(echo "${REDIS_CACHE}" | cut -f1 -d"/")
|
||||
fi
|
||||
|
||||
REDIS_QUEUE=$(getUrl "${COMMON_SITE_CONFIG_JSON}" "redis_queue" | sed 's|redis://||g')
|
||||
if [[ "${REDIS_QUEUE}" == *"/"* ]]; then
|
||||
REDIS_QUEUE=$(echo ${REDIS_QUEUE} | cut -f1 -d"/")
|
||||
REDIS_QUEUE=$(echo "${REDIS_QUEUE}" | cut -f1 -d"/")
|
||||
fi
|
||||
|
||||
REDIS_SOCKETIO=$(getUrl "${COMMON_SITE_CONFIG_JSON}" "redis_socketio" | sed 's|redis://||g')
|
||||
if [[ "${REDIS_SOCKETIO}" == *"/"* ]]; then
|
||||
REDIS_SOCKETIO=$(echo ${REDIS_SOCKETIO} | cut -f1 -d"/")
|
||||
REDIS_SOCKETIO=$(echo "${REDIS_SOCKETIO}" | cut -f1 -d"/")
|
||||
fi
|
||||
|
||||
echo "Check ${DB_HOST}:${DB_PORT}"
|
||||
11
build/frappe-worker/install_app.sh
Executable file
11
build/frappe-worker/install_app.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
APP_NAME=${1}
|
||||
APP_REPO=${2}
|
||||
APP_BRANCH=${3}
|
||||
|
||||
[[ -n "${APP_BRANCH}" ]] && BRANCH="-b ${APP_BRANCH}"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
git clone --depth 1 -o upstream "${APP_REPO}" ${BRANCH} "/home/frappe/frappe-bench/apps/${APP_NAME}"
|
||||
/home/frappe/frappe-bench/env/bin/pip3 install --no-cache-dir -e "/home/frappe/frappe-bench/apps/${APP_NAME}"
|
||||
BIN
deploy_key.enc
BIN
deploy_key.enc
Binary file not shown.
@@ -1,12 +1,19 @@
|
||||
version: "3.7"
|
||||
services:
|
||||
mariadb:
|
||||
image: mariadb:10.3
|
||||
image: mariadb:10.6
|
||||
command:
|
||||
- --character-set-server=utf8mb4
|
||||
- --collation-server=utf8mb4_unicode_ci
|
||||
- --skip-character-set-client-handshake
|
||||
- --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=123
|
||||
- MYSQL_USER=root
|
||||
# Sometimes db initialization takes longer than 10 seconds and site-creator goes away.
|
||||
# Frappe doesn't use CONVERT_TZ() function that requires time zone info, so we can just skip it.
|
||||
- MYSQL_INITDB_SKIP_TZINFO=1
|
||||
volumes:
|
||||
- ../installation/frappe-mariadb.cnf:/etc/mysql/conf.d/frappe.cnf
|
||||
- mariadb-vol:/var/lib/mysql
|
||||
|
||||
# Enable PostgreSQL only if you use it, see development/README.md for more information.
|
||||
@@ -30,6 +37,8 @@ services:
|
||||
frappe:
|
||||
image: frappe/bench:latest
|
||||
command: sleep infinity
|
||||
environment:
|
||||
- SHELL=/bin/bash
|
||||
volumes:
|
||||
- ..:/workspace:cached
|
||||
working_dir: /workspace/development
|
||||
|
||||
71
development/.vscode/launch.json
vendored
71
development/.vscode/launch.json
vendored
@@ -1,71 +0,0 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Bench Web",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
|
||||
"args": [
|
||||
"frappe", "serve", "--port", "8000", "--noreload", "--nothreading"
|
||||
],
|
||||
"cwd": "${workspaceFolder}/frappe-bench/sites",
|
||||
"env": {
|
||||
"DEV_SERVER": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Bench Default Worker",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
|
||||
"args": [
|
||||
"frappe", "worker", "--queue", "default"
|
||||
],
|
||||
"cwd": "${workspaceFolder}/frappe-bench/sites",
|
||||
"env": {
|
||||
"DEV_SERVER": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Bench Short Worker",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
|
||||
"args": [
|
||||
"frappe", "worker", "--queue", "short"
|
||||
],
|
||||
"cwd": "${workspaceFolder}/frappe-bench/sites",
|
||||
"env": {
|
||||
"DEV_SERVER": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Bench Long Worker",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
|
||||
"args": [
|
||||
"frappe", "worker", "--queue", "long"
|
||||
],
|
||||
"cwd": "${workspaceFolder}/frappe-bench/sites",
|
||||
"env": {
|
||||
"DEV_SERVER": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Honcho SocketIO Watch Schedule Worker",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "/home/frappe/.local/bin/honcho",
|
||||
"cwd": "${workspaceFolder}/frappe-bench",
|
||||
"console": "internalConsole",
|
||||
"args": [
|
||||
"start", "socketio", "watch", "schedule", "worker_short", "worker_long", "worker_default"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
4
development/.vscode/settings.json
vendored
4
development/.vscode/settings.json
vendored
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"python.pythonPath": "frappe-bench/env/bin/python",
|
||||
"debug.node.autoAttach": "disabled"
|
||||
}
|
||||
@@ -28,6 +28,12 @@ Copy example devcontainer config from `devcontainer-example` to `.devcontainer`
|
||||
cp -R devcontainer-example .devcontainer
|
||||
```
|
||||
|
||||
Copy example vscode config for devcontainer from `development/vscode-example` to `development/.vscode`. This will setup basic configuration for debugging.
|
||||
|
||||
```shell
|
||||
cp -R development/vscode-example development/.vscode
|
||||
```
|
||||
|
||||
## Use VSCode Remote Containers extension
|
||||
|
||||
For most people getting started with Frappe development, the best solution is to use [VSCode Remote - Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers).
|
||||
@@ -38,14 +44,14 @@ If you want to use PostgreSQL instead, edit `.devcontainer/docker-compose.yml` a
|
||||
VSCode should automatically inquire you to install the required extensions, that can also be installed manually as follows:
|
||||
|
||||
- Install Remote - Containers for VSCode
|
||||
- through command line `code --install-extension ms-vscode-remote.remote-containers`
|
||||
- clicking on the Install button in the Vistual Studio Marketplace: [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
|
||||
- View: Extensions command in VSCode (Windows: Ctrl+Shift+X; macOS: Cmd+Shift+X) then search for extension `ms-vscode-remote.remote-containers`
|
||||
- through command line `code --install-extension ms-vscode-remote.remote-containers`
|
||||
- clicking on the Install button in the Vistual Studio Marketplace: [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
|
||||
- View: Extensions command in VSCode (Windows: Ctrl+Shift+X; macOS: Cmd+Shift+X) then search for extension `ms-vscode-remote.remote-containers`
|
||||
|
||||
After the extensions are installed, you can:
|
||||
|
||||
- Open frappe_docker folder in VS Code.
|
||||
- `code .`
|
||||
- `code .`
|
||||
- Launch the command, from Command Palette (Ctrl + Shift + P) `Execute Remote Containers : Reopen in Container`. You can also click in the bottom left corner to access the remote container menu.
|
||||
|
||||
Notes:
|
||||
@@ -58,10 +64,12 @@ Notes:
|
||||
Run the following commands in the terminal inside the container. You might need to create a new terminal in VSCode.
|
||||
|
||||
```shell
|
||||
bench init --skip-redis-config-generation --frappe-branch version-12 frappe-bench
|
||||
bench init --skip-redis-config-generation --frappe-branch version-13 frappe-bench
|
||||
cd frappe-bench
|
||||
```
|
||||
|
||||
Note: For version 12 use python 3.7 by passing option to `bench init` command, e.g. `bench init --skip-redis-config-generation --frappe-branch version-12 --python python3.7 frappe-bench`
|
||||
|
||||
### Setup hosts
|
||||
|
||||
We need to tell bench to use the right containers instead of localhost. Run the following commands inside the container:
|
||||
@@ -86,6 +94,7 @@ code Procfile
|
||||
```
|
||||
|
||||
Or running the following command:
|
||||
|
||||
```shell
|
||||
sed -i '/redis/d' ./Procfile
|
||||
```
|
||||
@@ -97,6 +106,7 @@ You can create a new site with the following command:
|
||||
```shell
|
||||
bench new-site sitename --no-mariadb-socket
|
||||
```
|
||||
|
||||
sitename MUST end with .localhost for trying deployments locally.
|
||||
|
||||
for example:
|
||||
@@ -206,6 +216,8 @@ You can now login with user `Administrator` and the password you choose when cre
|
||||
|
||||
To debug workers, skip starting worker with honcho and start it with VSCode debugger.
|
||||
|
||||
For advance vscode configuration in the devcontainer, change the config files in `development/.vscode`.
|
||||
|
||||
## Developing using the interactive console
|
||||
|
||||
You can launch a simple interactive shell console in the terminal with:
|
||||
@@ -224,7 +236,7 @@ The first step is installing and updating the required software. Usually the fra
|
||||
/workspace/development/frappe-bench/env/bin/python -m pip install --upgrade jupyter ipykernel ipython
|
||||
```
|
||||
|
||||
Then, run the commmand `Python: Show Python interactive window` from the VSCode command palette.
|
||||
Then, run the command `Python: Show Python interactive window` from the VSCode command palette.
|
||||
|
||||
Replace `mysite.localhost` with your site and run the following code in a Jupyter cell:
|
||||
|
||||
@@ -249,7 +261,6 @@ Example shows the queries to be executed for site `localhost`
|
||||
|
||||
Open sites/localhost/site_config.json:
|
||||
|
||||
|
||||
```shell
|
||||
code sites/localhost/site_config.json
|
||||
```
|
||||
@@ -265,7 +276,7 @@ mysql -uroot -p123 -hmariadb
|
||||
Execute following queries replacing `db_name` and `db_password` with the values found in site_config.json.
|
||||
|
||||
```sql
|
||||
UPDATE mysql.user SET Host = '%' where User = 'db_name'; FLUSH PRIVILEGES;
|
||||
UPDATE mysql.global_priv SET Host = '%' where User = 'db_name'; FLUSH PRIVILEGES;
|
||||
SET PASSWORD FOR 'db_name'@'%' = PASSWORD('db_password'); FLUSH PRIVILEGES;
|
||||
GRANT ALL PRIVILEGES ON `db_name`.* TO 'db_name'@'%'; FLUSH PRIVILEGES;
|
||||
EXIT;
|
||||
@@ -276,6 +287,7 @@ EXIT;
|
||||
In case you don't use VSCode, you may start the containers manually with the following command:
|
||||
|
||||
### Running the containers
|
||||
|
||||
```shell
|
||||
docker-compose -f .devcontainer/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
81
development/vscode-example/launch.json
Normal file
81
development/vscode-example/launch.json
Normal file
@@ -0,0 +1,81 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Bench Web",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
|
||||
"args": [
|
||||
"frappe",
|
||||
"serve",
|
||||
"--port",
|
||||
"8000",
|
||||
"--noreload",
|
||||
"--nothreading"
|
||||
],
|
||||
"pythonPath": "${workspaceFolder}/frappe-bench/env/bin/python",
|
||||
"cwd": "${workspaceFolder}/frappe-bench/sites",
|
||||
"env": {
|
||||
"DEV_SERVER": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Bench Default Worker",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
|
||||
"args": ["frappe", "worker", "--queue", "default"],
|
||||
"pythonPath": "${workspaceFolder}/frappe-bench/env/bin/python",
|
||||
"cwd": "${workspaceFolder}/frappe-bench/sites",
|
||||
"env": {
|
||||
"DEV_SERVER": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Bench Short Worker",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
|
||||
"args": ["frappe", "worker", "--queue", "short"],
|
||||
"pythonPath": "${workspaceFolder}/frappe-bench/env/bin/python",
|
||||
"cwd": "${workspaceFolder}/frappe-bench/sites",
|
||||
"env": {
|
||||
"DEV_SERVER": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Bench Long Worker",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
|
||||
"args": ["frappe", "worker", "--queue", "long"],
|
||||
"pythonPath": "${workspaceFolder}/frappe-bench/env/bin/python",
|
||||
"cwd": "${workspaceFolder}/frappe-bench/sites",
|
||||
"env": {
|
||||
"DEV_SERVER": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Honcho SocketIO Watch Schedule Worker",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "/home/frappe/.local/bin/honcho",
|
||||
"pythonPath": "${workspaceFolder}/frappe-bench/env/bin/python",
|
||||
"cwd": "${workspaceFolder}/frappe-bench",
|
||||
"console": "internalConsole",
|
||||
"args": [
|
||||
"start",
|
||||
"socketio",
|
||||
"watch",
|
||||
"schedule",
|
||||
"worker_short",
|
||||
"worker_long",
|
||||
"worker_default"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
5
development/vscode-example/settings.json
Normal file
5
development/vscode-example/settings.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"debug.node.autoAttach": "disabled",
|
||||
"python.pythonPath": "/workspace/frappe-bench/env/bin/python",
|
||||
"python.analysis.extraPaths": ["./frappe-bench/apps/frappe"]
|
||||
}
|
||||
272
docker-bake.hcl
Normal file
272
docker-bake.hcl
Normal file
@@ -0,0 +1,272 @@
|
||||
# Docker Buildx Bake build definition file
|
||||
# Reference: https://github.com/docker/buildx/blob/master/docs/reference/buildx_bake.md
|
||||
|
||||
|
||||
# Bench image
|
||||
|
||||
target "bench" {
|
||||
context = "build/bench"
|
||||
target = "bench"
|
||||
tags = ["frappe/bench:latest"]
|
||||
}
|
||||
|
||||
target "bench-test" {
|
||||
inherits = ["bench"]
|
||||
target = "bench-test"
|
||||
}
|
||||
|
||||
# Main images
|
||||
# Base for all other targets
|
||||
|
||||
target "frappe-nginx" {
|
||||
dockerfile = "build/frappe-nginx/Dockerfile"
|
||||
}
|
||||
|
||||
target "frappe-worker" {
|
||||
dockerfile = "build/frappe-worker/Dockerfile"
|
||||
}
|
||||
|
||||
target "frappe-socketio" {
|
||||
dockerfile = "build/frappe-socketio/Dockerfile"
|
||||
}
|
||||
|
||||
target "erpnext-nginx" {
|
||||
dockerfile = "build/erpnext-nginx/Dockerfile"
|
||||
}
|
||||
|
||||
target "erpnext-worker" {
|
||||
dockerfile = "build/erpnext-worker/Dockerfile"
|
||||
}
|
||||
|
||||
|
||||
# Helpers
|
||||
|
||||
target "develop-args" {
|
||||
args = {
|
||||
GIT_BRANCH = "develop"
|
||||
IMAGE_TAG = "develop"
|
||||
}
|
||||
}
|
||||
|
||||
function "set_develop_tags" {
|
||||
params = [repo]
|
||||
result = ["${repo}:latest", "${repo}:edge", "${repo}:develop"]
|
||||
}
|
||||
|
||||
# NOTE: Variable are used only for stable builds
|
||||
variable "GIT_TAG" {} # git tag, e.g. v13.15.0
|
||||
variable "GIT_BRANCH" {} # git branch, e.g. version-13
|
||||
variable "VERSION" {} # Frappe and ERPNext version, e.g. 13
|
||||
|
||||
target "stable-args" {
|
||||
args = {
|
||||
GIT_BRANCH = "${GIT_BRANCH}"
|
||||
IMAGE_TAG = "${GIT_BRANCH}"
|
||||
# ERPNext build fails on v12
|
||||
# TODO: Remove PYTHON_VERSION argument when v12 will stop being supported
|
||||
PYTHON_VERSION = "${VERSION}" == "12" ? "3.7" : "3.9"
|
||||
}
|
||||
}
|
||||
|
||||
function "set_stable_tags" {
|
||||
# e.g. base_image:v13.15.0, base_image:v13, base_image:version-13
|
||||
params = [repo]
|
||||
result = ["${repo}:${GIT_TAG}", "${repo}:v${VERSION}", "${repo}:${GIT_BRANCH}"]
|
||||
}
|
||||
|
||||
target "test-erpnext-args" {
|
||||
args = {
|
||||
IMAGE_TAG = "test"
|
||||
DOCKER_REGISTRY_PREFIX = "localhost:5000/frappe"
|
||||
}
|
||||
}
|
||||
|
||||
function "set_local_test_tags" {
|
||||
params = [repo]
|
||||
result = ["localhost:5000/${repo}:test"]
|
||||
}
|
||||
|
||||
function "set_test_tags" {
|
||||
params = [repo]
|
||||
result = ["${repo}:test"]
|
||||
}
|
||||
|
||||
|
||||
# Develop images
|
||||
|
||||
target "frappe-nginx-develop" {
|
||||
inherits = ["frappe-nginx", "develop-args"]
|
||||
tags = set_develop_tags("frappe/frappe-nginx")
|
||||
}
|
||||
|
||||
target "frappe-worker-develop" {
|
||||
inherits = ["frappe-worker", "develop-args"]
|
||||
tags = set_develop_tags("frappe/frappe-worker")
|
||||
}
|
||||
|
||||
target "frappe-socketio-develop" {
|
||||
inherits = ["frappe-socketio", "develop-args"]
|
||||
tags = set_develop_tags("frappe/frappe-socketio")
|
||||
}
|
||||
|
||||
target "erpnext-nginx-develop" {
|
||||
inherits = ["erpnext-nginx", "develop-args"]
|
||||
tags = set_develop_tags("frappe/erpnext-nginx")
|
||||
}
|
||||
|
||||
target "erpnext-worker-develop" {
|
||||
inherits = ["erpnext-worker", "develop-args"]
|
||||
tags = set_develop_tags("frappe/erpnext-worker")
|
||||
}
|
||||
|
||||
group "frappe-develop" {
|
||||
targets = ["frappe-nginx-develop", "frappe-worker-develop", "frappe-socketio-develop"]
|
||||
}
|
||||
|
||||
group "erpnext-develop" {
|
||||
targets = ["erpnext-nginx-develop", "erpnext-worker-develop"]
|
||||
}
|
||||
|
||||
# Test develop images
|
||||
|
||||
target "frappe-nginx-develop-test-local" {
|
||||
inherits = ["frappe-nginx-develop"]
|
||||
tags = set_local_test_tags("frappe/frappe-nginx")
|
||||
}
|
||||
|
||||
target "frappe-worker-develop-test-local" {
|
||||
inherits = ["frappe-worker-develop"]
|
||||
tags = set_local_test_tags("frappe/frappe-worker")
|
||||
}
|
||||
|
||||
target "frappe-socketio-develop-test-local" {
|
||||
inherits = ["frappe-socketio-develop"]
|
||||
tags = set_local_test_tags("frappe/frappe-socketio")
|
||||
}
|
||||
|
||||
target "frappe-nginx-develop-test" {
|
||||
inherits = ["frappe-nginx-develop"]
|
||||
tags = set_test_tags("frappe/frappe-nginx")
|
||||
}
|
||||
|
||||
target "frappe-worker-develop-test" {
|
||||
inherits = ["frappe-worker-develop"]
|
||||
tags = set_test_tags("frappe/frappe-worker")
|
||||
}
|
||||
|
||||
target "frappe-socketio-develop-test" {
|
||||
inherits = ["frappe-socketio-develop"]
|
||||
tags = set_test_tags("frappe/frappe-socketio")
|
||||
}
|
||||
|
||||
target "erpnext-nginx-develop-test" {
|
||||
inherits = ["erpnext-nginx-develop", "test-erpnext-args"]
|
||||
tags = set_test_tags("frappe/erpnext-nginx")
|
||||
}
|
||||
|
||||
target "erpnext-worker-develop-test" {
|
||||
inherits = ["erpnext-worker-develop", "test-erpnext-args"]
|
||||
tags = set_test_tags("frappe/erpnext-worker")
|
||||
}
|
||||
|
||||
group "frappe-develop-test-local" {
|
||||
targets = ["frappe-nginx-develop-test-local", "frappe-worker-develop-test-local", "frappe-socketio-develop-test-local"]
|
||||
}
|
||||
|
||||
group "frappe-develop-test" {
|
||||
targets = ["frappe-nginx-develop-test", "frappe-worker-develop-test", "frappe-socketio-develop-test"]
|
||||
}
|
||||
|
||||
group "erpnext-develop-test" {
|
||||
targets = ["erpnext-nginx-develop-test", "erpnext-worker-develop-test"]
|
||||
}
|
||||
|
||||
|
||||
# Stable images
|
||||
|
||||
target "frappe-nginx-stable" {
|
||||
inherits = ["frappe-nginx", "stable-args"]
|
||||
tags = set_stable_tags("frappe/frappe-nginx")
|
||||
}
|
||||
|
||||
target "frappe-worker-stable" {
|
||||
inherits = ["frappe-worker", "stable-args"]
|
||||
tags = set_stable_tags("frappe/frappe-worker")
|
||||
}
|
||||
|
||||
target "frappe-socketio-stable" {
|
||||
inherits = ["frappe-socketio", "stable-args"]
|
||||
tags = set_stable_tags("frappe/frappe-socketio")
|
||||
}
|
||||
|
||||
target "erpnext-nginx-stable" {
|
||||
inherits = ["erpnext-nginx", "stable-args"]
|
||||
tags = set_stable_tags("frappe/erpnext-nginx")
|
||||
}
|
||||
|
||||
target "erpnext-worker-stable" {
|
||||
inherits = ["erpnext-worker", "stable-args"]
|
||||
tags = set_stable_tags("frappe/erpnext-worker")
|
||||
}
|
||||
|
||||
group "frappe-stable" {
|
||||
targets = ["frappe-nginx-stable", "frappe-worker-stable", "frappe-socketio-stable"]
|
||||
}
|
||||
|
||||
group "erpnext-stable" {
|
||||
targets = ["erpnext-nginx-stable", "erpnext-worker-stable"]
|
||||
}
|
||||
|
||||
# Test stable images
|
||||
|
||||
target "frappe-nginx-stable-test-local" {
|
||||
inherits = ["frappe-nginx-stable"]
|
||||
tags = set_local_test_tags("frappe/frappe-nginx")
|
||||
}
|
||||
|
||||
target "frappe-worker-stable-test-local" {
|
||||
inherits = ["frappe-worker-stable"]
|
||||
tags = set_local_test_tags("frappe/frappe-worker")
|
||||
}
|
||||
|
||||
target "frappe-socketio-stable-test-local" {
|
||||
inherits = ["frappe-socketio-stable"]
|
||||
tags = set_local_test_tags("frappe/frappe-socketio")
|
||||
}
|
||||
|
||||
target "frappe-nginx-stable-test" {
|
||||
inherits = ["frappe-nginx-stable"]
|
||||
tags = set_test_tags("frappe/frappe-nginx")
|
||||
}
|
||||
|
||||
target "frappe-worker-stable-test" {
|
||||
inherits = ["frappe-worker-stable"]
|
||||
tags = set_test_tags("frappe/frappe-worker")
|
||||
}
|
||||
|
||||
target "frappe-socketio-stable-test" {
|
||||
inherits = ["frappe-socketio-stable"]
|
||||
tags = set_test_tags("frappe/frappe-socketio")
|
||||
}
|
||||
|
||||
target "erpnext-nginx-stable-test" {
|
||||
inherits = ["erpnext-nginx-stable", "test-erpnext-args"]
|
||||
tags = set_test_tags("frappe/erpnext-nginx")
|
||||
}
|
||||
|
||||
target "erpnext-worker-stable-test" {
|
||||
inherits = ["erpnext-worker-stable", "test-erpnext-args"]
|
||||
tags = set_test_tags("frappe/erpnext-worker")
|
||||
}
|
||||
|
||||
group "frappe-stable-test-local" {
|
||||
targets = ["frappe-nginx-stable-test-local", "frappe-worker-stable-test-local", "frappe-socketio-stable-test-local"]
|
||||
}
|
||||
|
||||
group "frappe-stable-test" {
|
||||
targets = ["frappe-nginx-stable-test", "frappe-worker-stable-test", "frappe-socketio-stable-test"]
|
||||
}
|
||||
|
||||
group "erpnext-stable-test" {
|
||||
targets = ["erpnext-nginx-stable-test", "erpnext-worker-stable-test"]
|
||||
}
|
||||
@@ -4,7 +4,6 @@ services:
|
||||
traefik:
|
||||
image: "traefik:v2.2"
|
||||
command:
|
||||
- "--log.level=DEBUG"
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--entrypoints.web.address=:80"
|
||||
@@ -39,12 +38,19 @@ services:
|
||||
- FRAPPE_SOCKETIO=frappe-socketio
|
||||
- SOCKETIO_PORT=9000
|
||||
- SKIP_NGINX_TEMPLATE_GENERATION=${SKIP_NGINX_TEMPLATE_GENERATION}
|
||||
# For refactored images
|
||||
- BACKEND=erpnext-python:8000
|
||||
- SOCKETIO=frappe-socketio:9000
|
||||
- FRAPPE_SITE_NAME_HEADER=${FRAPPE_SITE_NAME_HEADER:-$$host}
|
||||
- UPSTREAM_REAL_IP_ADDRESS=${UPSTREAM_REAL_IP_ADDRESS:-127.0.0.1}
|
||||
- UPSTREAM_REAL_IP_HEADER=${UPSTREAM_REAL_IP_HEADER:-X-Forwarded-For}
|
||||
- UPSTREAM_REAL_IP_RECURSIVE=${UPSTREAM_REAL_IP_RECURSIVE:-off}
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.erpnext-nginx.rule=Host(${SITES})"
|
||||
- "${ENTRYPOINT_LABEL}"
|
||||
- "${CERT_RESOLVER_LABEL}"
|
||||
- "traefik.http.services.erpnext-nginx.loadbalancer.server.port=80"
|
||||
- "traefik.http.services.erpnext-nginx.loadbalancer.server.port=8080"
|
||||
volumes:
|
||||
- sites-vol:/var/www/html/sites:rw
|
||||
- assets-vol:/assets:rw
|
||||
@@ -76,7 +82,7 @@ services:
|
||||
erpnext-worker-default:
|
||||
image: frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
restart: on-failure
|
||||
command: worker
|
||||
command: ["bench", "worker", "--queue", "default"]
|
||||
depends_on:
|
||||
- redis-queue
|
||||
- redis-cache
|
||||
@@ -87,9 +93,7 @@ services:
|
||||
erpnext-worker-short:
|
||||
image: frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
restart: on-failure
|
||||
command: worker
|
||||
environment:
|
||||
- WORKER_TYPE=short
|
||||
command: ["bench", "worker", "--queue", "short"]
|
||||
depends_on:
|
||||
- redis-queue
|
||||
- redis-cache
|
||||
@@ -100,9 +104,7 @@ services:
|
||||
erpnext-worker-long:
|
||||
image: frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
restart: on-failure
|
||||
command: worker
|
||||
environment:
|
||||
- WORKER_TYPE=long
|
||||
command: ["bench", "worker", "--queue", "long"]
|
||||
depends_on:
|
||||
- redis-queue
|
||||
- redis-cache
|
||||
@@ -112,7 +114,7 @@ services:
|
||||
erpnext-schedule:
|
||||
image: frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
restart: on-failure
|
||||
command: schedule
|
||||
command: ["bench", "schedule"]
|
||||
depends_on:
|
||||
- redis-queue
|
||||
- redis-cache
|
||||
@@ -139,30 +141,21 @@ services:
|
||||
- redis-socketio-vol:/data
|
||||
|
||||
mariadb:
|
||||
image: mariadb:10.3
|
||||
image: mariadb:10.6
|
||||
restart: on-failure
|
||||
command:
|
||||
- --character-set-server=utf8mb4
|
||||
- --collation-server=utf8mb4_unicode_ci
|
||||
- --skip-character-set-client-handshake
|
||||
- --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
|
||||
# Sometimes db initialization takes longer than 10 seconds and site creation can cause error.
|
||||
# Frappe doesn't use CONVERT_TZ() function that requires time zone info, so we can just skip it.
|
||||
- MYSQL_INITDB_SKIP_TZINFO=1
|
||||
volumes:
|
||||
- ./installation/frappe-mariadb.cnf:/etc/mysql/conf.d/frappe.cnf
|
||||
- mariadb-vol:/var/lib/mysql
|
||||
|
||||
site-creator:
|
||||
image: frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
restart: "no"
|
||||
command: new
|
||||
depends_on:
|
||||
- erpnext-python
|
||||
environment:
|
||||
- SITE_NAME=${SITE_NAME}
|
||||
- DB_ROOT_USER=${DB_ROOT_USER}
|
||||
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
|
||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
|
||||
- INSTALL_APPS=${INSTALL_APPS}
|
||||
volumes:
|
||||
- sites-vol:/home/frappe/frappe-bench/sites:rw
|
||||
- logs-vol:/home/frappe/frappe-bench/logs:rw
|
||||
|
||||
volumes:
|
||||
mariadb-vol:
|
||||
redis-cache-vol:
|
||||
|
||||
@@ -42,7 +42,7 @@ version: "3.7"
|
||||
|
||||
services:
|
||||
mariadb-master:
|
||||
image: 'bitnami/mariadb:10.3'
|
||||
image: "bitnami/mariadb:10.3"
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
@@ -54,7 +54,7 @@ services:
|
||||
secrets:
|
||||
- frappe-mariadb-root-password
|
||||
volumes:
|
||||
- 'mariadb_master_data:/bitnami/mariadb'
|
||||
- "mariadb_master_data:/bitnami/mariadb"
|
||||
environment:
|
||||
- MARIADB_REPLICATION_MODE=master
|
||||
- MARIADB_REPLICATION_USER=repl_user
|
||||
@@ -62,7 +62,7 @@ services:
|
||||
- MARIADB_ROOT_PASSWORD_FILE=/run/secrets/frappe-mariadb-root-password
|
||||
|
||||
mariadb-slave:
|
||||
image: 'bitnami/mariadb:10.3'
|
||||
image: "bitnami/mariadb:10.3"
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
@@ -74,7 +74,7 @@ services:
|
||||
secrets:
|
||||
- frappe-mariadb-root-password
|
||||
volumes:
|
||||
- 'mariadb_slave_data:/bitnami/mariadb'
|
||||
- "mariadb_slave_data:/bitnami/mariadb"
|
||||
environment:
|
||||
- MARIADB_REPLICATION_MODE=slave
|
||||
- MARIADB_REPLICATION_USER=repl_user
|
||||
@@ -167,7 +167,7 @@ services:
|
||||
- "traefik.http.routers.erpnext-nginx-https.entrypoints=https"
|
||||
- "traefik.http.routers.erpnext-nginx-https.tls=true"
|
||||
- "traefik.http.routers.erpnext-nginx-https.tls.certresolver=le"
|
||||
- "traefik.http.services.erpnext-nginx.loadbalancer.server.port=80"
|
||||
- "traefik.http.services.erpnext-nginx.loadbalancer.server.port=8080"
|
||||
|
||||
erpnext-python:
|
||||
image: frappe/erpnext-worker:${ERPNEXT_VERSION?Variable ERPNEXT_VERSION not set}
|
||||
@@ -265,6 +265,7 @@ Use environment variables:
|
||||
- `FRAPPE_VERSION` variable to be set to desired version of Frappe Framework. e.g. 12.7.0
|
||||
- `MARIADB_HOST=frappe-mariadb_mariadb-master`
|
||||
- `SITES` variable is list of sites in back tick and separated by comma
|
||||
|
||||
```
|
||||
SITES=`site1.example.com`,`site2.example.com`
|
||||
```
|
||||
@@ -275,11 +276,11 @@ SITES=`site1.example.com`,`site2.example.com`
|
||||
2. Select Image frappe/erpnext-worker:v13
|
||||
3. Set command as `new`
|
||||
4. Select network `frappe-network`
|
||||
5. Select Volume `frappe-bench-v13_sites_vol` and mount in container `/home/frappe/frappe-bench/sites`
|
||||
5. Select Volume `frappe-bench-v13_sites-vol` and mount in container `/home/frappe/frappe-bench/sites`
|
||||
6. Env variables:
|
||||
- MYSQL_ROOT_PASSWORD=longsecretpassword
|
||||
- SITE_NAME=site1.example.com
|
||||
- INSTALL_APPS=erpnext
|
||||
- MYSQL_ROOT_PASSWORD=longsecretpassword
|
||||
- SITE_NAME=site1.example.com
|
||||
- INSTALL_APPS=erpnext
|
||||
7. Start container
|
||||
|
||||
### Migrate Sites job
|
||||
@@ -288,8 +289,7 @@ SITES=`site1.example.com`,`site2.example.com`
|
||||
2. Select Image frappe/erpnext-worker:v13
|
||||
3. Set command as `migrate`
|
||||
4. Select network `frappe-network`
|
||||
5. Select Volume `frappe-bench-v13_sites_vol` and mount in container `/home/frappe/frappe-bench/sites`
|
||||
5. Select Volume `frappe-bench-v13_sites-vol` and mount in container `/home/frappe/frappe-bench/sites`
|
||||
6. Env variables:
|
||||
- MAINTENANCE_MODE=1
|
||||
- MAINTENANCE_MODE=1
|
||||
7. Start container
|
||||
|
||||
|
||||
@@ -17,17 +17,17 @@ cp env-example .env
|
||||
To get started, copy the existing `env-example` file to `.env`. By default, the file will contain the following variables:
|
||||
|
||||
- `VERSION=edge`
|
||||
- In this case, `edge` corresponds to `develop`. To setup any other version, you may use the branch name or version specific tags. (eg. v13.0.0, version-12, v11.1.15, v11)
|
||||
- In this case, `edge` corresponds to `develop`. To setup any other version, you may use the branch name or version specific tags. (eg. v13.0.0, version-12, v11.1.15, v11)
|
||||
- `MYSQL_ROOT_PASSWORD=admin`
|
||||
- Bootstraps a MariaDB container with this value set as the root password. If a managed MariaDB instance is used, there is no need to set the password here.
|
||||
- Bootstraps a MariaDB container with this value set as the root password. If a managed MariaDB instance is used, there is no need to set the password here.
|
||||
- `MARIADB_HOST=mariadb`
|
||||
- Sets the hostname to `mariadb`. This is required if the database is managed by the containerized MariaDB instance.
|
||||
- In case of a separately managed database setups, set the value to the database's hostname/IP/domain.
|
||||
- Sets the hostname to `mariadb`. This is required if the database is managed by the containerized MariaDB instance.
|
||||
- In case of a separately managed database setups, set the value to the database's hostname/IP/domain.
|
||||
- `SITES=site1.domain.com,site2.domain.com`
|
||||
- List of sites that are part of the deployment "bench" Each site is separated by a comma(,).
|
||||
- If LetsEncrypt is being setup, make sure that the DNS for all the site's domains correctly point to the current instance.
|
||||
- List of sites that are part of the deployment "bench" Each site is separated by a comma(,).
|
||||
- If LetsEncrypt is being setup, make sure that the DNS for all the site's domains correctly point to the current instance.
|
||||
- `LETSENCRYPT_EMAIL=your.email@your.domain.com`
|
||||
- Email for LetsEncrypt expiry notification. This is only required if you are setting up LetsEncrypt.
|
||||
- Email for LetsEncrypt expiry notification. This is only required if you are setting up LetsEncrypt.
|
||||
|
||||
Notes:
|
||||
|
||||
@@ -123,46 +123,49 @@ Notes:
|
||||
## Docker containers
|
||||
|
||||
This repository contains the following docker-compose files, each one containing the described images:
|
||||
* docker-compose-common.yml
|
||||
* redis-cache
|
||||
* volume: redis-cache-vol
|
||||
* redis-queue
|
||||
* volume: redis-queue-vol
|
||||
* redis-socketio
|
||||
* volume: redis-socketio-vol
|
||||
* mariadb: main database
|
||||
* volume: mariadb-vol
|
||||
* docker-compose-erpnext.yml
|
||||
* erpnext-nginx: serves static assets and proxies web request to the appropriate container, allowing to offer all services on the same port.
|
||||
* volume: assets-vol
|
||||
* erpnext-python: main application code
|
||||
* frappe-socketio: enables realtime communication to the user interface through websockets
|
||||
* frappe-worker-default: background runner
|
||||
* frappe-worker-short: background runner for short-running jobs
|
||||
* frappe-worker-long: background runner for long-running jobs
|
||||
* frappe-schedule
|
||||
|
||||
* docker-compose-frappe.yml
|
||||
* frappe-nginx: serves static assets and proxies web request to the appropriate container, allowing to offer all services on the same port.
|
||||
* volume: assets-vol, sites-vol
|
||||
* erpnext-python: main application code
|
||||
* volume: sites-vol
|
||||
* frappe-socketio: enables realtime communication to the user interface through websockets
|
||||
* volume: sites-vol
|
||||
* frappe-worker-default: background runner
|
||||
* volume: sites-vol
|
||||
* frappe-worker-short: background runner for short-running jobs
|
||||
* volume: sites-vol
|
||||
* frappe-worker-long: background runner for long-running jobs
|
||||
* volume: sites-vol
|
||||
* frappe-schedule
|
||||
* volume: sites-vol
|
||||
- docker-compose-common.yml
|
||||
- redis-cache
|
||||
- volume: redis-cache-vol
|
||||
- redis-queue
|
||||
- volume: redis-queue-vol
|
||||
- redis-socketio
|
||||
- volume: redis-socketio-vol
|
||||
- mariadb: main database
|
||||
- volume: mariadb-vol
|
||||
- docker-compose-erpnext.yml
|
||||
|
||||
* docker-compose-networks.yml: this yaml define the network to communicate with *Letsencrypt Nginx Proxy Companion*.
|
||||
- erpnext-nginx: serves static assets and proxies web request to the appropriate container, allowing to offer all services on the same port.
|
||||
- volume: assets-vol
|
||||
- erpnext-python: main application code
|
||||
- frappe-socketio: enables realtime communication to the user interface through websockets
|
||||
- frappe-worker-default: background runner
|
||||
- frappe-worker-short: background runner for short-running jobs
|
||||
- frappe-worker-long: background runner for long-running jobs
|
||||
- frappe-schedule
|
||||
|
||||
* erpnext-publish.yml: this yml extends erpnext-nginx service to publish port 80, can only be used with docker-compose-erpnext.yml
|
||||
- docker-compose-frappe.yml
|
||||
|
||||
* frappe-publish.yml: this yml extends frappe-nginx service to publish port 80, can only be used with docker-compose-frappe.yml
|
||||
- frappe-nginx: serves static assets and proxies web request to the appropriate container, allowing to offer all services on the same port.
|
||||
- volume: assets-vol, sites-vol
|
||||
- erpnext-python: main application code
|
||||
- volume: sites-vol
|
||||
- frappe-socketio: enables realtime communication to the user interface through websockets
|
||||
- volume: sites-vol
|
||||
- frappe-worker-default: background runner
|
||||
- volume: sites-vol
|
||||
- frappe-worker-short: background runner for short-running jobs
|
||||
- volume: sites-vol
|
||||
- frappe-worker-long: background runner for long-running jobs
|
||||
- volume: sites-vol
|
||||
- frappe-schedule
|
||||
- volume: sites-vol
|
||||
|
||||
- docker-compose-networks.yml: this yaml define the network to communicate with _Letsencrypt Nginx Proxy Companion_.
|
||||
|
||||
- erpnext-publish.yml: this yml extends erpnext-nginx service to publish port 80, can only be used with docker-compose-erpnext.yml
|
||||
|
||||
- frappe-publish.yml: this yml extends frappe-nginx service to publish port 80, can only be used with docker-compose-frappe.yml
|
||||
|
||||
## Updating and Migrating Sites
|
||||
|
||||
@@ -191,4 +194,4 @@ docker run \
|
||||
-v <project-name>_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network <project-name>_default \
|
||||
frappe/erpnext-worker:$VERSION migrate
|
||||
```
|
||||
```
|
||||
|
||||
@@ -26,39 +26,39 @@ cp env-production .env
|
||||
To get started, copy the existing `env-local` or `env-production` file to `.env`. By default, the file will contain the following variables:
|
||||
|
||||
- `ERPNEXT_VERSION=edge`
|
||||
- In this case, `edge` corresponds to `develop`. To setup any other version, you may use the branch name or version specific tags. (eg. v13.0.0, version-12, v11.1.15, v11).
|
||||
- In this case, `edge` corresponds to `develop`. To setup any other version, you may use the branch name or version specific tags. (eg. v13.0.0, version-12, v11.1.15, v11).
|
||||
- `FRAPPE_VERSION=edge`
|
||||
- In this case, `edge` corresponds to `develop`. To setup any other version, you may use the branch name or version specific tags. (eg. v13.0.0, version-12, v11.1.15, v11).
|
||||
- In this case, `edge` corresponds to `develop`. To setup any other version, you may use the branch name or version specific tags. (eg. v13.0.0, version-12, v11.1.15, v11).
|
||||
- `MARIADB_HOST=mariadb`
|
||||
- Sets the hostname to `mariadb`. This is required if the database is managed by the containerized MariaDB instance.
|
||||
- Sets the hostname to `mariadb`. This is required if the database is managed by the containerized MariaDB instance.
|
||||
- `MYSQL_ROOT_PASSWORD=admin`
|
||||
- Bootstraps a MariaDB container with this value set as the root password. If a managed MariaDB instance is used, there is no need to set the password here.
|
||||
- In case of a separately managed database setups, set the value to the database's hostname/IP/domain.
|
||||
- Bootstraps a MariaDB container with this value set as the root password. If a managed MariaDB instance is used, there is no need to set the password here.
|
||||
- In case of a separately managed database setups, set the value to the database's hostname/IP/domain.
|
||||
- `SITE_NAME=erp.example.com`
|
||||
- Creates this site after starting all services and installs ERPNext. Site name must be resolvable by users machines and the ERPNext components. e.g. `erp.example.com` or `mysite.localhost`.
|
||||
- ``SITES=`erp.example.com` ``
|
||||
- List of sites that are part of the deployment "bench" Each site is separated by a comma(,) and quoted in backtick (`). By default site created by ``SITE_NAME`` variable is added here.
|
||||
- If LetsEncrypt is being setup, make sure that the DNS for all the site's domains correctly point to the current instance.
|
||||
- Creates this site after starting all services and installs ERPNext. Site name must be resolvable by users machines and the ERPNext components. e.g. `erp.example.com` or `mysite.localhost`.
|
||||
- `` SITES=`erp.example.com` ``
|
||||
- List of sites that are part of the deployment "bench" Each site is separated by a comma(,) and quoted in backtick (`). By default site created by `SITE_NAME` variable is added here.
|
||||
- If LetsEncrypt is being setup, make sure that the DNS for all the site's domains correctly point to the current instance.
|
||||
- `DB_ROOT_USER=root`
|
||||
- MariaDB root username
|
||||
- MariaDB root username
|
||||
- `ADMIN_PASSWORD=admin`
|
||||
- Password for the `Administrator` user, credentials after install `Administrator:$ADMIN_PASSWORD`.
|
||||
- Password for the `Administrator` user, credentials after install `Administrator:$ADMIN_PASSWORD`.
|
||||
- `INSTALL_APPS=erpnext`
|
||||
- Apps to install, the app must be already in the container image, to install other application read the [instructions on installing custom apps](./custom-apps-for-production.md).
|
||||
- Apps to install, the app must be already in the container image, to install other application read the [instructions on installing custom apps](./custom-apps-for-production.md).
|
||||
- `LETSENCRYPT_EMAIL=email@example.com`
|
||||
- Email for LetsEncrypt expiry notification. This is only required if you are setting up LetsEncrypt.
|
||||
- Email for LetsEncrypt expiry notification. This is only required if you are setting up LetsEncrypt.
|
||||
- `ENTRYPOINT_LABEL=traefik.http.routers.erpnext-nginx.entrypoints=websecure`
|
||||
- Related to the traefik configuration, says all traffic from outside should come from HTTP or HTTPS, for local development should be web, for production websecure. if redirection is needed, read below.
|
||||
- Related to the traefik configuration, says all traffic from outside should come from HTTP or HTTPS, for local development should be web, for production websecure. if redirection is needed, read below.
|
||||
- `CERT_RESOLVER_LABEL=traefik.http.routers.erpnext-nginx.tls.certresolver=myresolver`
|
||||
- Which traefik resolver to use to get TLS certificate, sets `erpnext.local.no-cert-resolver` for local setup.
|
||||
- ``HTTPS_REDIRECT_RULE_LABEL=traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`) ``
|
||||
- Related to the traefik https redirection configuration, sets `erpnext.local.no-redirect-rule` for local setup.
|
||||
- Which traefik resolver to use to get TLS certificate, sets `erpnext.local.no-cert-resolver` for local setup.
|
||||
- `` HTTPS_REDIRECT_RULE_LABEL=traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`) ``
|
||||
- Related to the traefik https redirection configuration, sets `erpnext.local.no-redirect-rule` for local setup.
|
||||
- `HTTPS_REDIRECT_ENTRYPOINT_LABEL=traefik.http.routers.http-catchall.entrypoints=web`
|
||||
- Related to the traefik https redirection configuration, sets `erpnext.local.no-entrypoint` for local setup.
|
||||
- Related to the traefik https redirection configuration, sets `erpnext.local.no-entrypoint` for local setup.
|
||||
- `HTTPS_REDIRECT_MIDDLEWARE_LABEL=traefik.http.routers.http-catchall.middlewares=redirect-to-https`
|
||||
- Related to the traefik https redirection configuration, sets `erpnext.local.no-middleware` for local setup.
|
||||
- Related to the traefik https redirection configuration, sets `erpnext.local.no-middleware` for local setup.
|
||||
- `HTTPS_USE_REDIRECT_MIDDLEWARE_LABEL=traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https`
|
||||
- Related to the traefik https redirection configuration, sets `erpnext.local-no-redirect-middleware` for local setup.
|
||||
- Related to the traefik https redirection configuration, sets `erpnext.local-no-redirect-middleware` for local setup.
|
||||
|
||||
Notes:
|
||||
|
||||
@@ -77,41 +77,61 @@ Make sure to replace `<project-name>` with the desired name you wish to set for
|
||||
|
||||
Notes:
|
||||
|
||||
- If it is the first time running and site is being initialized, *it can take multiple minutes for the site to be up*. Monitor `site-creator` container logs to check progress. Use command `docker logs <project-name>_site-creator_1 -f`
|
||||
- If it is the first time running and site is being initialized, _it can take multiple minutes for the site to be up_. Monitor `site-creator` container logs to check progress. Use command `docker logs <project-name>_site-creator_1 -f`
|
||||
- After the site is ready the username is `Administrator` and the password is `$ADMIN_PASSWORD`
|
||||
- The local deployment is for testing and REST API development purpose only
|
||||
- A complete development environment is available [here](../development)
|
||||
|
||||
## Create Site
|
||||
|
||||
Set environment variables
|
||||
|
||||
```sh
|
||||
export $(cat .env | xargs)
|
||||
```
|
||||
|
||||
Create site as per use and install apps optionally.
|
||||
|
||||
```sh
|
||||
docker-compose exec erpnext-python bench new-site ${SITE_NAME} --mariadb-root-username=${DB_ROOT_USER} --mariadb-root-password=${MYSQL_ROOT_PASSWORD} --admin-password=${ADMIN_PASSWORD} --install-app=${INSTALL_APPS} --db-type=mariadb --no-mariadb-socket
|
||||
```
|
||||
|
||||
If this is the first site created then remove `currentsite.txt` to unset default site.
|
||||
|
||||
```sh
|
||||
docker-compose exec erpnext-python rm -f currentsite.txt
|
||||
```
|
||||
|
||||
## Docker containers
|
||||
|
||||
The docker-compose file contains following services:
|
||||
|
||||
* traefik: manages letsencrypt
|
||||
* volume: cert-vol
|
||||
* redis-cache: cache store
|
||||
* volume: redis-cache-vol
|
||||
* redis-queue: used by workers
|
||||
* volume: redis-queue-vol
|
||||
* redis-socketio: used by socketio service
|
||||
* volume: redis-socketio-vol
|
||||
* mariadb: main database
|
||||
* volume: mariadb-vol
|
||||
* erpnext-nginx: serves static assets and proxies web request to the appropriate container, allowing to offer all services on the same port.
|
||||
* volume: assets-vol and sites-vol
|
||||
* erpnext-python: main application code
|
||||
* volume: sites-vol and sites-vol
|
||||
* frappe-socketio: enables realtime communication to the user interface through websockets
|
||||
* volume: sites-vol
|
||||
* erpnext-worker-default: background runner
|
||||
* volume: sites-vol
|
||||
* erpnext-worker-short: background runner for short-running jobs
|
||||
* volume: sites-vol
|
||||
* erpnext-worker-long: background runner for long-running jobs
|
||||
* volume: sites-vol
|
||||
* erpnext-schedule
|
||||
* volume: sites-vol
|
||||
* site-creator: run once container to create new site.
|
||||
* volume: sites-vol
|
||||
- traefik: manages letsencrypt
|
||||
- volume: cert-vol
|
||||
- redis-cache: cache store
|
||||
- volume: redis-cache-vol
|
||||
- redis-queue: used by workers
|
||||
- volume: redis-queue-vol
|
||||
- redis-socketio: used by socketio service
|
||||
- volume: redis-socketio-vol
|
||||
- mariadb: main database
|
||||
- volume: mariadb-vol
|
||||
- erpnext-nginx: serves static assets and proxies web request to the appropriate container, allowing to offer all services on the same port.
|
||||
- volume: assets-vol and sites-vol
|
||||
- erpnext-python: main application code
|
||||
- volume: assets-vol and sites-vol
|
||||
- frappe-socketio: enables realtime communication to the user interface through websockets
|
||||
- volume: sites-vol
|
||||
- erpnext-worker-default: background runner
|
||||
- volume: sites-vol
|
||||
- erpnext-worker-short: background runner for short-running jobs
|
||||
- volume: sites-vol
|
||||
- erpnext-worker-long: background runner for long-running jobs
|
||||
- volume: sites-vol
|
||||
- erpnext-schedule
|
||||
- volume: sites-vol
|
||||
- site-creator: run once container to create new site.
|
||||
- volume: sites-vol
|
||||
|
||||
## Updating and Migrating Sites
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ Or specify environment variables instead of passing secrets as command arguments
|
||||
Note:
|
||||
|
||||
- Wait for the database service to start before trying to create a new site.
|
||||
- If new site creation fails, retry after the MariaDB container is up and running.
|
||||
- If you're using a managed database instance, make sure that the database is running before setting up a new site.
|
||||
- If new site creation fails, retry after the MariaDB container is up and running.
|
||||
- If you're using a managed database instance, make sure that the database is running before setting up a new site.
|
||||
|
||||
#### MariaDB Site
|
||||
|
||||
@@ -28,7 +28,7 @@ docker run \
|
||||
-e "INSTALL_APPS=erpnext" \
|
||||
-v <project-name>_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network <project-name>_default \
|
||||
frappe/erpnext-worker:$VERSION new
|
||||
frappe/erpnext-worker:$ERPNEXT_VERSION new
|
||||
```
|
||||
|
||||
#### PostgreSQL Site
|
||||
@@ -46,7 +46,7 @@ docker run \
|
||||
-e "ADMIN_PASSWORD=$ADMIN_PASSWORD" \
|
||||
-v <project-name>_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network <project-name>_default \
|
||||
frappe/erpnext-worker:$VERSION new
|
||||
frappe/erpnext-worker:$ERPNEXT_VERSION new
|
||||
```
|
||||
|
||||
Environment Variables needed:
|
||||
@@ -72,7 +72,7 @@ Notes:
|
||||
|
||||
## Add sites to proxy
|
||||
|
||||
Change `SITES` variable to the list of sites created encapsulated in backtick and separated by comma with no space. e.g. ``SITES=`site1.example.com`,`site2.example.com` ``.
|
||||
Change `SITES` variable to the list of sites created encapsulated in backtick and separated by comma with no space. e.g. `` SITES=`site1.example.com`,`site2.example.com` ``.
|
||||
|
||||
Reload variables with following command.
|
||||
|
||||
@@ -96,7 +96,7 @@ docker run \
|
||||
-e "WITH_FILES=1" \
|
||||
-v <project-name>_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network <project-name>_default \
|
||||
frappe/erpnext-worker:$VERSION backup
|
||||
frappe/erpnext-worker:$ERPNEXT_VERSION backup
|
||||
```
|
||||
|
||||
The backup will be available in the `sites-vol` volume.
|
||||
@@ -123,7 +123,7 @@ Environment Variables
|
||||
-e "BUCKET_DIR=frappe-bench" \
|
||||
-v <project-name>_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network <project-name>_default \
|
||||
frappe/frappe-worker:$VERSION push-backup
|
||||
frappe/frappe-worker:$FRAPPE_VERSION push-backup
|
||||
```
|
||||
|
||||
Note:
|
||||
@@ -157,7 +157,7 @@ docker run \
|
||||
-v <project-name>_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
-v ./backups:/home/frappe/backups \
|
||||
--network <project-name>_default \
|
||||
frappe/frappe-worker:$VERSION restore-backup
|
||||
frappe/frappe-worker:$FRAPPE_VERSION restore-backup
|
||||
```
|
||||
|
||||
Note:
|
||||
@@ -165,19 +165,19 @@ Note:
|
||||
- Volume must be mounted at location `/home/frappe/backups` for restoring sites
|
||||
- If no backup files are found in volume, it will use s3 credentials to pull backups
|
||||
- Backup structure for mounted volume or downloaded from s3:
|
||||
- /home/frappe/backups
|
||||
- site1.domain.com
|
||||
- 20200420_162000
|
||||
- 20200420_162000-site1_domain_com-*
|
||||
- site2.domain.com
|
||||
- 20200420_162000
|
||||
- 20200420_162000-site2_domain_com-*
|
||||
- /home/frappe/backups
|
||||
- site1.domain.com
|
||||
- 20200420_162000
|
||||
- 20200420_162000-site1_domain_com-\*
|
||||
- site2.domain.com
|
||||
- 20200420_162000
|
||||
- 20200420_162000-site2_domain_com-\*
|
||||
|
||||
## Edit configs
|
||||
|
||||
Editing config manually might be required in some cases,
|
||||
one such case is to use Amazon RDS (or any other DBaaS).
|
||||
For full instructions, refer to the [wiki](https://github.com/frappe/frappe/wiki/Using-Frappe-with-Amazon-RDS-(or-any-other-DBaaS)). Common question can be found in Issues and on forum.
|
||||
For full instructions, refer to the [wiki](<https://github.com/frappe/frappe/wiki/Using-Frappe-with-Amazon-RDS-(or-any-other-DBaaS)>). Common question can be found in Issues and on forum.
|
||||
|
||||
`common_site_config.json` or `site_config.json` from `sites-vol` volume has to be edited using following command:
|
||||
|
||||
@@ -213,7 +213,7 @@ To execute commands using bench helper.
|
||||
-v <project-name>_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network <project-name>_default \
|
||||
--user frappe \
|
||||
frappe/frappe-worker:$VERSION bench --help
|
||||
frappe/frappe-worker:$FRAPPE_VERSION bench --help
|
||||
```
|
||||
|
||||
Example command to clear cache
|
||||
@@ -223,7 +223,7 @@ Example command to clear cache
|
||||
-v <project-name>_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network <project-name>_default \
|
||||
--user frappe \
|
||||
frappe/frappe-worker:$VERSION bench --site erp.mysite.com clear-cache
|
||||
frappe/frappe-worker:$FRAPPE_VERSION bench --site erp.mysite.com clear-cache
|
||||
```
|
||||
|
||||
Notes:
|
||||
@@ -231,7 +231,6 @@ Notes:
|
||||
- Use it to install/uninstall custom apps, add system manager user, etc.
|
||||
- To run the command as non root user add the command option `--user frappe`.
|
||||
|
||||
|
||||
## Delete/Drop Site
|
||||
|
||||
#### MariaDB Site
|
||||
@@ -244,7 +243,7 @@ docker run \
|
||||
-e "MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD" \
|
||||
-v <project-name>_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network <project-name>_default \
|
||||
frappe/erpnext-worker:$VERSION drop
|
||||
frappe/erpnext-worker:$ERPNEXT_VERSION drop
|
||||
```
|
||||
|
||||
#### PostgreSQL Site
|
||||
@@ -257,7 +256,7 @@ docker run \
|
||||
-e "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" \
|
||||
-v <project-name>_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network <project-name>_default \
|
||||
frappe/erpnext-worker:$VERSION drop
|
||||
frappe/erpnext-worker:$ERPNEXT_VERSION drop
|
||||
```
|
||||
|
||||
Environment Variables needed:
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# Tips for moving deployments
|
||||
|
||||
- Take regular automatic backups and push the files to S3 compatible cloud. Setup backup and push with cronjobs
|
||||
- Use regular cron for single machine installs
|
||||
- Use [swarm-cronjob](https://github.com/crazy-max/swarm-cronjob) for docker swarm
|
||||
- Use Kubernetes CronJob
|
||||
- Use regular cron for single machine installs
|
||||
- Use [swarm-cronjob](https://github.com/crazy-max/swarm-cronjob) for docker swarm
|
||||
- Use Kubernetes CronJob
|
||||
- It makes it easy to transfer data from cloud to any new deployment.
|
||||
- They are just [site operations](site-operations.md) that can be manually pipelined as per need.
|
||||
- Remember to restore encryption keys and other custom configuration from `site_config.json`.
|
||||
- Steps to move deployment:
|
||||
- [Take backup](site-operations.md#backup-sites)
|
||||
- [Push backup to cloud](site-operations.md#push-backup-to-s3-compatible-storage)
|
||||
- Create new deployment type anywhere
|
||||
- [Restore backup from cloud](site-operations.md#restore-backups)
|
||||
- [Restore `site_config.json` from cloud](site-operations.md#edit-configs)
|
||||
- [Take backup](site-operations.md#backup-sites)
|
||||
- [Push backup to cloud](site-operations.md#push-backup-to-s3-compatible-storage)
|
||||
- Create new deployment type anywhere
|
||||
- [Restore backup from cloud](site-operations.md#restore-backups)
|
||||
- [Restore `site_config.json` from cloud](site-operations.md#edit-configs)
|
||||
|
||||
255
frappe-installer
255
frappe-installer
@@ -1,255 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
[[ -z "${DEBUG}" && "${DEBUG}" == 1 ]] && set -o xtrace
|
||||
|
||||
__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$__dir"
|
||||
|
||||
env_url="https://raw.githubusercontent.com/frappe/frappe_docker/master/installation/env-example"
|
||||
docker_nginx_url="https://github.com/evertramos/docker-compose-letsencrypt-nginx-proxy-companion"
|
||||
frappe_docker_url="https://github.com/frappe/frappe_docker"
|
||||
env_file="$__dir/.env"
|
||||
|
||||
function check_root() {
|
||||
if [[ $EUID != 0 ]]; then
|
||||
echo "This script must be run as root. Login as root or use sudo." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function check_git() {
|
||||
if [[ ! -x "$(command -v git)" ]]; then
|
||||
echo "Git is not installed. Please install git before continuing." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function check_docker() {
|
||||
if [[ ! -x "$(command -v docker)" ]]; then
|
||||
read -rp "No docker installation found. Press Enter to install docker or ctrl+c to exit." >&2
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
fi
|
||||
if [[ ! -x "$(command -v docker)" ]]; then
|
||||
echo "Docker installation failed. Exiting." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function check_env() {
|
||||
if [[ ! -f "$env_file" ]]; then
|
||||
cat <<CHOOSE >&2
|
||||
No environment file found. This file is required for setting up Frappe/ERPNext Docker.
|
||||
Would you like to fetch the default environment file?
|
||||
(NOTE: You will be prompted to set it up later)
|
||||
CHOOSE
|
||||
read -rp "Press Enter to fetch the configuration file, or create a .env file and re-run the script."
|
||||
curl -fsSL "$env_url" -o "$env_file"
|
||||
fi
|
||||
}
|
||||
|
||||
function clone_repository() {
|
||||
echo "Cloning Repository: $1"
|
||||
git clone "$2"
|
||||
}
|
||||
|
||||
function get_config() {
|
||||
if [[ -n "$2" ]]; then
|
||||
config_file="$2"
|
||||
else
|
||||
config_file="$env_file"
|
||||
fi
|
||||
line=$(grep -E "^$=" "$config_file")
|
||||
line_result=$(echo "$line" | awk -F"=" '{print $2}')
|
||||
}
|
||||
|
||||
function get_install_version() {
|
||||
cat <<CHOOSE >&2
|
||||
Choose a version you would like to setup [current: $1]:
|
||||
1. develop (edge)
|
||||
2. version-12
|
||||
3. version-11
|
||||
Please enter your choice [1-3]:
|
||||
CHOOSE
|
||||
select choice in "1" "2" "3"; do
|
||||
case ${choice} in
|
||||
1) version="edge" ;;
|
||||
2) version="version-12" ;;
|
||||
3) version="version-11" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
function prompt_config() {
|
||||
# inspired by discourse_docker
|
||||
get_config "VERSION"
|
||||
local install_version=$line_result
|
||||
get_config "MYSQL_ROOT_PASSWORD"
|
||||
local mysql_password=$line_result
|
||||
get_config "SITES"
|
||||
local sites=$line_result
|
||||
get_config "LETSENCRYPT_EMAIL"
|
||||
local letsencrypt_email=$line_result
|
||||
|
||||
echo "Would you like to setup networking for docker? [y/n]"
|
||||
echo "This is required if you wish to access the instance from other machines."
|
||||
select choice in "y" "n"; do
|
||||
case $choice in
|
||||
y) setup_networking=1 ;;
|
||||
n)
|
||||
setup_networking=0
|
||||
setup_letsencrypt=0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
if [[ -n "$letsencrypt_email" && "$setup_networking" -ne "0" ]]; then
|
||||
echo "Would you like to setup LetsEncrypt? [y/n]"
|
||||
select choice in "y" "n"; do
|
||||
case $choice in
|
||||
y)
|
||||
setup_letsencrypt=1
|
||||
echo "Please ensure that all the required domains point to this IP address."
|
||||
read -rp "Enter an Email Address to setup LetsEncrypt with: " letsencrypt_email
|
||||
;;
|
||||
n)
|
||||
setup_letsencrypt=0
|
||||
echo "Skipping LetsEncrypt Setup."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
local new_value=""
|
||||
local config_state="n"
|
||||
|
||||
echo
|
||||
|
||||
get_install_version "$install_version"
|
||||
install_version="$version"
|
||||
|
||||
while [[ "$config_state" == "n" ]]; do
|
||||
if [[ -n "$mysql_password" ]]; then
|
||||
read -srp "Enter MySQL Password [$mysql_password]: " new_value
|
||||
if [[ -n "$new_value" ]]; then
|
||||
mysql_password="$new_value"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$sites" ]]; then
|
||||
read -rp "Enter sitename to setup [$sites]: " new_value
|
||||
if [[ -n "$new_value" ]]; then
|
||||
sites="$new_value"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$setup_letsencrypt" != "0" ]]; then
|
||||
read -rp "Enter email address for LetsEncrypt [$letsencrypt_email]: " new_value
|
||||
if [[ -n "$new_value" ]]; then
|
||||
letsencrypt_email=$new_value
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Current Configuration:"
|
||||
echo "Version: $([[ "$install_version" == "edge" ]] && echo "develop" || echo "$install_version")"
|
||||
echo "MySQL Root Password: $mysql_password"
|
||||
echo "Sites: $sites"
|
||||
|
||||
if [[ "$setup_letsencrypt" != "0" ]]; then
|
||||
echo "LetsEncrypt Email Address: $letsencrypt_email"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Does this configuration look okay?"
|
||||
read -rp "Press Enter to continue, 'n' to try again, or ctrl+c to exit: " config_state
|
||||
done
|
||||
|
||||
echo "Saving the current configuration file to $env_file"
|
||||
|
||||
cat <<EOF >"$env_file"
|
||||
VERSION=$install_version
|
||||
MYSQL_ROOT_PASSWORD=$mysql_password
|
||||
SITES=$sites
|
||||
$([ "$setup_letsencrypt" -ne "0" ] && echo "LETSENCRYPT_EMAIL=$letsencrypt_email")
|
||||
EOF
|
||||
setup_configuration=$(<"$env_file")
|
||||
}
|
||||
|
||||
setup_user() {
|
||||
echo "The rest of the setup requires a user account."
|
||||
echo "You may use an existing account, or set up a new one right away."
|
||||
read -rp "Enter username: " username
|
||||
if grep -E "^$username" /etc/passwd >/dev/null; then
|
||||
echo "User $username already exists."
|
||||
else
|
||||
read -rsp "Enter password: " password
|
||||
password="$(perl -e 'print crypt($ARGV[0], "password")' "$password")"
|
||||
if useradd -m -p "$password" "$username" -s "$(command -v bash)"; then
|
||||
echo "User $username has been added to the system."
|
||||
else
|
||||
echo "Failed to add user to the system."
|
||||
echo "Please add a user manually and re-run the script."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! getent group docker >/dev/null 2>&1; then
|
||||
echo "Creating group: docker"
|
||||
groupadd docker
|
||||
fi
|
||||
echo "Adding user $username to group: docker"
|
||||
usermod -aG docker "$username"
|
||||
newgrp docker
|
||||
}
|
||||
|
||||
install() {
|
||||
if [[ "$setup_letsencrypt" != "0" && "$setup_networking" != "0" ]]; then
|
||||
echo "Setting up NGINX Proxy for LetsEncrypt"
|
||||
clone_repository "Docker Compose LetsEncrypt NGINX Proxy Companion" "$docker_nginx_url"
|
||||
cd "$(basename "$docker_nginx_url")"
|
||||
if [[ -f .env.sample ]]; then
|
||||
cp .env.sample env
|
||||
fi
|
||||
./start.sh >/dev/null 2>&1
|
||||
cd "$(eval echo ~"$username")"
|
||||
fi
|
||||
|
||||
echo "Setting up Frappe/ERPNext"
|
||||
clone_repository "Frappe/ERPNext Docker" "$frappe_docker_url"
|
||||
cd "$(basename "$frappe_docker_url")"
|
||||
echo "$setup_configuration" >.env
|
||||
echo "Enter a name for the project."
|
||||
read -rp "This project name will be used to setup the docker instance: [erpnext_docker]" project_name
|
||||
if [[ -z "$project_name" ]]; then
|
||||
echo "Setting the project name to erpnext_docker"
|
||||
project_name="erpnext_docker"
|
||||
fi
|
||||
|
||||
docker-compose \
|
||||
--project-name "$project_name" \
|
||||
--project-directory . up -d \
|
||||
-f installation/docker-compose-frappe.yml \
|
||||
-f installation/docker-compose-erpnext.yml \
|
||||
-f installation/docker-compose-common.yml \
|
||||
"$( ((setup_networking == 1)) && printf %s '-f installation/docker-compose-networks.yml')"
|
||||
|
||||
get_config "SITES" "$(pwd)/.env"
|
||||
local sites=$line_result
|
||||
|
||||
docker exec \
|
||||
-e "SITE_NAME=$sites" \
|
||||
-e "INSTALL_ERPNEXT=1" \
|
||||
-it "$project_name"_erpnext-python_1 docker-entrypoint.sh new
|
||||
|
||||
echo "Installation Complete!"
|
||||
}
|
||||
|
||||
check_root
|
||||
check_git
|
||||
check_docker
|
||||
check_env
|
||||
|
||||
prompt_config
|
||||
setup_user
|
||||
install
|
||||
@@ -1,15 +0,0 @@
|
||||
name: Greetings
|
||||
|
||||
on: [pull_request, issues]
|
||||
|
||||
jobs:
|
||||
greeting:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/first-interaction@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-message: |
|
||||
Hello! We're very happy to see your first issue. If your issue is about a problem, go back and check you have copy-pasted all the debug logs you can so we can help you as fast as possible!
|
||||
pr-message: |
|
||||
Hello! Thank you about this PR. Since this is your first PR, please make sure you have described the improvements and your code is well documented.
|
||||
@@ -1,4 +1,4 @@
|
||||
version: '3'
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
redis-cache:
|
||||
@@ -20,12 +20,19 @@ services:
|
||||
- redis-socketio-vol:/data
|
||||
|
||||
mariadb:
|
||||
image: mariadb:10.3
|
||||
image: mariadb:10.6
|
||||
restart: on-failure
|
||||
command:
|
||||
- --character-set-server=utf8mb4
|
||||
- --collation-server=utf8mb4_unicode_ci
|
||||
- --skip-character-set-client-handshake
|
||||
- --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
|
||||
# Sometimes db initialization takes longer than 10 seconds and site-creator goes away.
|
||||
# Frappe doesn't use CONVERT_TZ() function that requires time zone info, so we can just skip it.
|
||||
- MYSQL_INITDB_SKIP_TZINFO=1
|
||||
volumes:
|
||||
- ./frappe-mariadb.cnf:/etc/mysql/conf.d/frappe.cnf
|
||||
- mariadb-vol:/var/lib/mysql
|
||||
|
||||
volumes:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: '3'
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
erpnext-nginx:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: '3'
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
frappe-nginx:
|
||||
@@ -117,4 +117,4 @@ services:
|
||||
volumes:
|
||||
assets-vol:
|
||||
sites-vol:
|
||||
logs-vol:
|
||||
logs-vol:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: '3'
|
||||
version: "3"
|
||||
|
||||
networks:
|
||||
default:
|
||||
|
||||
@@ -3,4 +3,4 @@ version: "3"
|
||||
services:
|
||||
erpnext-nginx:
|
||||
ports:
|
||||
- "80:80"
|
||||
- "80:8080"
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
[mysqld]
|
||||
character-set-client-handshake = FALSE
|
||||
character-set-server = utf8mb4
|
||||
collation-server = utf8mb4_unicode_ci
|
||||
|
||||
[mysql]
|
||||
default-character-set = utf8mb4
|
||||
@@ -4,7 +4,6 @@ services:
|
||||
traefik:
|
||||
image: "traefik:v2.2"
|
||||
command:
|
||||
- "--log.level=DEBUG"
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--entrypoints.web.address=:80"
|
||||
@@ -42,7 +41,7 @@ services:
|
||||
- "traefik.http.routers.frappe-nginx.rule=Host(${SITES})"
|
||||
- "${ENTRYPOINT_LABEL}"
|
||||
- "${CERT_RESOLVER_LABEL}"
|
||||
- "traefik.http.services.frappe-nginx.loadbalancer.server.port=80"
|
||||
- "traefik.http.services.frappe-nginx.loadbalancer.server.port=8080"
|
||||
volumes:
|
||||
- sites-vol:/var/www/html/sites:rw
|
||||
- assets-vol:/assets:rw
|
||||
|
||||
@@ -3,4 +3,4 @@ version: "3"
|
||||
services:
|
||||
frappe-nginx:
|
||||
ports:
|
||||
- "80:80"
|
||||
- "80:8080"
|
||||
|
||||
@@ -11,7 +11,6 @@ services:
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
command:
|
||||
- "--log.level=DEBUG"
|
||||
- "--providers.docker"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--entrypoints.web.address=:80"
|
||||
@@ -37,7 +36,7 @@ services:
|
||||
- "traefik.http.middlewares.erpnext-nginx.headers.customrequestheaders.Host=erpnext-nginx"
|
||||
- "traefik.http.routers.erpnext-nginx.middlewares=erpnext-nginx"
|
||||
- "traefik.http.routers.erpnext-nginx.entrypoints=web"
|
||||
- "traefik.http.services.erpnext-nginx.loadbalancer.server.port=80"
|
||||
- "traefik.http.services.erpnext-nginx.loadbalancer.server.port=8080"
|
||||
volumes:
|
||||
- sites-vol:/var/www/html/sites:rw
|
||||
- assets-vol:/assets:rw
|
||||
@@ -151,36 +150,21 @@ services:
|
||||
volumes:
|
||||
- sites-vol:/sites:rw
|
||||
|
||||
mariadb-configurator:
|
||||
image: mariadb:10.3
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: none
|
||||
command:
|
||||
- "bash"
|
||||
- "-c"
|
||||
- >
|
||||
echo -e "[mysqld]\n
|
||||
skip-host-cache\n
|
||||
skip-name-resolve\n
|
||||
character-set-client-handshake = FALSE\n
|
||||
character-set-server = utf8mb4\n
|
||||
collation-server = utf8mb4_unicode_ci\n
|
||||
[mysql]\n
|
||||
default-character-set = utf8mb4\n
|
||||
[mysqld_safe]\n
|
||||
skip_log_error\n
|
||||
syslog\n" > /data/frappe.cnf
|
||||
volumes:
|
||||
- mariadb-conf-vol:/data:rw
|
||||
|
||||
mariadb:
|
||||
image: mariadb:10.3
|
||||
image: mariadb:10.6
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- --character-set-server=utf8mb4
|
||||
- --collation-server=utf8mb4_unicode_ci
|
||||
- --skip-character-set-client-handshake
|
||||
- --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=admin
|
||||
# Sometimes db initialization takes longer than 10 seconds and site-creator goes away.
|
||||
# Frappe doesn't use CONVERT_TZ() function that requires time zone info, so we can just skip it.
|
||||
- MYSQL_INITDB_SKIP_TZINFO=1
|
||||
volumes:
|
||||
- mariadb-conf-vol:/etc/mysql/conf.d
|
||||
- mariadb-vol:/var/lib/mysql
|
||||
9
setup.cfg
Normal file
9
setup.cfg
Normal file
@@ -0,0 +1,9 @@
|
||||
# Config file for isort, codespell and other Python projects.
|
||||
# In this case it is not used for packaging.
|
||||
|
||||
[isort]
|
||||
profile = black
|
||||
known_third_party = frappe
|
||||
|
||||
[codespell]
|
||||
skip = build/bench/Dockerfile
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Checking bash scripts with shellcheck" >&2
|
||||
|
||||
while IFS= read -r shellfile; do
|
||||
shellcheck --check-sourced --severity=style --color=always --exclude=SC2164,SC2086,SC2012,SC2016 ${shellfile}
|
||||
done < <(find ./build -name "*.sh")
|
||||
@@ -1,400 +0,0 @@
|
||||
#!/bin/bash
|
||||
ULINE='\e[1m\e[4m'
|
||||
ENDULINE='\e[0m'
|
||||
NEWLINE='\n'
|
||||
|
||||
function checkMigrationComplete() {
|
||||
echo "Check Migration"
|
||||
CONTAINER_ID=$(docker-compose \
|
||||
--project-name frappebench00 \
|
||||
-f installation/docker-compose-common.yml \
|
||||
-f installation/docker-compose-erpnext.yml \
|
||||
-f installation/erpnext-publish.yml \
|
||||
ps -q erpnext-python)
|
||||
|
||||
DOCKER_LOG=$(docker logs ${CONTAINER_ID} 2>&1 | grep "Starting gunicorn")
|
||||
INCREMENT=0
|
||||
while [[ ${DOCKER_LOG} != *"Starting gunicorn"* && ${INCREMENT} -lt 60 ]]; do
|
||||
sleep 3
|
||||
echo "Wait for migration to complete ..."
|
||||
((INCREMENT = INCREMENT + 1))
|
||||
DOCKER_LOG=$(docker logs ${CONTAINER_ID} 2>&1 | grep "Starting gunicorn")
|
||||
if [[ ${DOCKER_LOG} != *"Starting gunicorn"* && ${INCREMENT} -eq 60 ]]; then
|
||||
docker logs ${CONTAINER_ID}
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "${ULINE}Migration Log${ENDULINE}"
|
||||
docker logs ${CONTAINER_ID}
|
||||
}
|
||||
|
||||
function loopHealthCheck() {
|
||||
echo "Create Container to Check MariaDB"
|
||||
docker run --name frappe_doctor \
|
||||
-v frappebench00_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network frappebench00_default \
|
||||
frappe/erpnext-worker:edge doctor || true
|
||||
|
||||
echo "Loop Health Check"
|
||||
FRAPPE_LOG=$(docker logs frappe_doctor | grep "Health check successful" || echo "")
|
||||
while [[ -z "${FRAPPE_LOG}" ]]; do
|
||||
sleep 1
|
||||
CONTAINER=$(docker start frappe_doctor)
|
||||
echo "Restarting ${CONTAINER} ..."
|
||||
FRAPPE_LOG=$(docker logs frappe_doctor | grep "Health check successful" || echo "")
|
||||
done
|
||||
echo "Health check successful"
|
||||
}
|
||||
|
||||
echo -e "${ULINE}Copy env-example file${ENDULINE}"
|
||||
cp env-example .env
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Set version to v13${ENDULINE}"
|
||||
sed -i -e "s/edge/v13/g" .env
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Start Services${ENDULINE}"
|
||||
docker-compose \
|
||||
--project-name frappebench00 \
|
||||
-f installation/docker-compose-common.yml \
|
||||
-f installation/docker-compose-erpnext.yml \
|
||||
-f installation/erpnext-publish.yml \
|
||||
pull
|
||||
docker pull postgres:11.8
|
||||
docker-compose \
|
||||
--project-name frappebench00 \
|
||||
-f installation/docker-compose-common.yml \
|
||||
-f installation/docker-compose-erpnext.yml \
|
||||
-f installation/erpnext-publish.yml \
|
||||
up -d
|
||||
# Start postgres
|
||||
docker run --name postgresql -d \
|
||||
-e "POSTGRES_PASSWORD=admin" \
|
||||
-v frappebench00_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network frappebench00_default \
|
||||
postgres:11.8
|
||||
|
||||
loopHealthCheck
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Create new site (v13)${ENDULINE}"
|
||||
docker run -it \
|
||||
-e "SITE_NAME=test.localhost" \
|
||||
-e "INSTALL_APPS=erpnext" \
|
||||
-v frappebench00_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network frappebench00_default \
|
||||
frappe/erpnext-worker:v13 new
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Ping created site${ENDULINE}"
|
||||
curl -sS http://test.localhost/api/method/version
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Check Created Site Index Page${ENDULINE}"
|
||||
curl -s http://test.localhost | w3m -T text/html -dump
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Set version to edge${ENDULINE}"
|
||||
sed -i -e "s/v13/edge/g" .env
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Restart containers with edge image${ENDULINE}"
|
||||
docker-compose \
|
||||
--project-name frappebench00 \
|
||||
-f installation/docker-compose-common.yml \
|
||||
-f installation/docker-compose-erpnext.yml \
|
||||
-f installation/erpnext-publish.yml \
|
||||
stop
|
||||
docker-compose \
|
||||
--project-name frappebench00 \
|
||||
-f installation/docker-compose-common.yml \
|
||||
-f installation/docker-compose-erpnext.yml \
|
||||
-f installation/erpnext-publish.yml \
|
||||
up -d
|
||||
|
||||
checkMigrationComplete
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Ping migrated site${ENDULINE}"
|
||||
sleep 3
|
||||
curl -sS http://test.localhost/api/method/version
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Check Migrated Site Index Page${ENDULINE}"
|
||||
curl -s http://test.localhost | w3m -T text/html -dump
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Create new site (pgsql)${ENDULINE}"
|
||||
docker run -it \
|
||||
-e "SITE_NAME=pgsql.localhost" \
|
||||
-e "POSTGRES_HOST=postgresql" \
|
||||
-e "DB_ROOT_USER=postgres" \
|
||||
-e "POSTGRES_PASSWORD=admin" \
|
||||
-v frappebench00_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network frappebench00_default \
|
||||
frappe/erpnext-worker:edge new
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Check New PGSQL Site${ENDULINE}"
|
||||
sleep 3
|
||||
RESTORE_STATUS=$(curl -sS http://pgsql.localhost/api/method/version || echo "")
|
||||
INCREMENT=0
|
||||
while [[ -z "${RESTORE_STATUS}" && ${INCREMENT} -lt 60 ]]; do
|
||||
sleep 1
|
||||
echo -e "${ULINE}Wait for restoration to complete ..."
|
||||
RESTORE_STATUS=$(curl -sS http://pgsql.localhost/api/method/version || echo "")
|
||||
((INCREMENT = INCREMENT + 1))
|
||||
if [[ -z "${RESTORE_STATUS}" && ${INCREMENT} -eq 60 ]]; then
|
||||
CONTAINER_ID=$(docker-compose \
|
||||
--project-name frappebench00 \
|
||||
-f installation/docker-compose-common.yml \
|
||||
-f installation/docker-compose-erpnext.yml \
|
||||
-f installation/erpnext-publish.yml \
|
||||
ps -q erpnext-python)
|
||||
docker logs ${CONTAINER_ID}
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Ping new pgsql site${ENDULINE}"
|
||||
echo $RESTORE_STATUS
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Check New PGSQL Index Page${ENDULINE}"
|
||||
curl -s http://pgsql.localhost | w3m -T text/html -dump
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Backup site${ENDULINE}"
|
||||
docker run -it \
|
||||
-e "WITH_FILES=1" \
|
||||
-v frappebench00_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network frappebench00_default \
|
||||
frappe/erpnext-worker:edge backup
|
||||
|
||||
MINIO_ACCESS_KEY="AKIAIOSFODNN7EXAMPLE"
|
||||
MINIO_SECRET_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
||||
|
||||
echo -e "${ULINE}Start MinIO container for s3 compatible storage${ENDULINE}"
|
||||
docker run -d --name minio \
|
||||
-e "MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}" \
|
||||
-e "MINIO_SECRET_KEY=${MINIO_SECRET_KEY}" \
|
||||
--network frappebench00_default \
|
||||
minio/minio server /data
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Create bucket named erpnext${ENDULINE}"
|
||||
docker run \
|
||||
--network frappebench00_default \
|
||||
vltgroup/s3cmd:latest s3cmd --access_key=${MINIO_ACCESS_KEY} \
|
||||
--secret_key=${MINIO_SECRET_KEY} \
|
||||
--region=us-east-1 \
|
||||
--no-ssl \
|
||||
--host=minio:9000 \
|
||||
--host-bucket=minio:9000 \
|
||||
mb s3://erpnext
|
||||
|
||||
echo -e "${NEWLINE}${NEWLINE}${ULINE}Push backup to MinIO s3${ENDULINE}"
|
||||
docker run \
|
||||
-e BUCKET_NAME=erpnext \
|
||||
-e REGION=us-east-1 \
|
||||
-e BUCKET_DIR=local \
|
||||
-e ACCESS_KEY_ID=${MINIO_ACCESS_KEY} \
|
||||
-e SECRET_ACCESS_KEY=${MINIO_SECRET_KEY} \
|
||||
-e ENDPOINT_URL=http://minio:9000 \
|
||||
-v frappebench00_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network frappebench00_default \
|
||||
frappe/erpnext-worker:edge push-backup
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Stop Services${ENDULINE}"
|
||||
docker-compose \
|
||||
--project-name frappebench00 \
|
||||
-f installation/docker-compose-common.yml \
|
||||
-f installation/docker-compose-erpnext.yml \
|
||||
-f installation/erpnext-publish.yml \
|
||||
stop
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Prune Containers${ENDULINE}"
|
||||
docker container prune -f && docker volume prune -f
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Start Services${ENDULINE}"
|
||||
docker-compose \
|
||||
--project-name frappebench00 \
|
||||
-f installation/docker-compose-common.yml \
|
||||
-f installation/docker-compose-erpnext.yml \
|
||||
-f installation/erpnext-publish.yml \
|
||||
up -d
|
||||
|
||||
loopHealthCheck
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Restore backup from MinIO / S3${ENDULINE}"
|
||||
docker run \
|
||||
-e MYSQL_ROOT_PASSWORD=admin \
|
||||
-e BUCKET_NAME=erpnext \
|
||||
-e BUCKET_DIR=local \
|
||||
-e ACCESS_KEY_ID=${MINIO_ACCESS_KEY} \
|
||||
-e SECRET_ACCESS_KEY=${MINIO_SECRET_KEY} \
|
||||
-e ENDPOINT_URL=http://minio:9000 \
|
||||
-e REGION=us-east-1 \
|
||||
-v frappebench00_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network frappebench00_default \
|
||||
frappe/erpnext-worker:edge restore-backup
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Check Restored Site (test)${ENDULINE}"
|
||||
sleep 3
|
||||
RESTORE_STATUS=$(curl -sS http://test.localhost/api/method/version || echo "")
|
||||
INCREMENT=0
|
||||
while [[ -z "${RESTORE_STATUS}" && ${INCREMENT} -lt 60 ]]; do
|
||||
sleep 1
|
||||
echo "Wait for restoration to complete ..."
|
||||
RESTORE_STATUS=$(curl -sS http://test.localhost/api/method/version || echo "")
|
||||
((INCREMENT = INCREMENT + 1))
|
||||
if [[ -z "${RESTORE_STATUS}" && ${INCREMENT} -eq 60 ]]; then
|
||||
CONTAINER_ID=$(docker-compose \
|
||||
--project-name frappebench00 \
|
||||
-f installation/docker-compose-common.yml \
|
||||
-f installation/docker-compose-erpnext.yml \
|
||||
-f installation/erpnext-publish.yml \
|
||||
ps -q erpnext-python)
|
||||
docker logs ${CONTAINER_ID}
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "${ULINE}Ping restored site (test)${ENDULINE}"
|
||||
echo ${RESTORE_STATUS}
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Check Restored Site Index Page (test)${ENDULINE}"
|
||||
curl -s http://test.localhost | w3m -T text/html -dump
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Check Restored Site (pgsql)${ENDULINE}"
|
||||
sleep 3
|
||||
RESTORE_STATUS=$(curl -sS http://pgsql.localhost/api/method/version || echo "")
|
||||
INCREMENT=0
|
||||
while [[ -z "${RESTORE_STATUS}" && ${INCREMENT} -lt 60 ]]; do
|
||||
sleep 1
|
||||
echo "Wait for restoration to complete ..."
|
||||
RESTORE_STATUS=$(curl -sS http://pgsql.localhost/api/method/version || echo "")
|
||||
((INCREMENT = INCREMENT + 1))
|
||||
if [[ -z "${RESTORE_STATUS}" && ${INCREMENT} -eq 60 ]]; then
|
||||
CONTAINER_ID=$(docker-compose \
|
||||
--project-name frappebench00 \
|
||||
-f installation/docker-compose-common.yml \
|
||||
-f installation/docker-compose-erpnext.yml \
|
||||
-f installation/erpnext-publish.yml \
|
||||
ps -q erpnext-python)
|
||||
docker logs ${CONTAINER_ID}
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "${ULINE}Ping restored site (pgsql)${ENDULINE}"
|
||||
echo ${RESTORE_STATUS}
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Check Restored Site Index Page (pgsql)${ENDULINE}"
|
||||
curl -s http://pgsql.localhost | w3m -T text/html -dump
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Create new site (edge)${ENDULINE}"
|
||||
docker run -it \
|
||||
-e "SITE_NAME=edge.localhost" \
|
||||
-e "INSTALL_APPS=erpnext" \
|
||||
-v frappebench00_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network frappebench00_default \
|
||||
frappe/erpnext-worker:edge new
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Check New Edge Site${ENDULINE}"
|
||||
sleep 3
|
||||
RESTORE_STATUS=$(curl -sS http://edge.localhost/api/method/version || echo "")
|
||||
INCREMENT=0
|
||||
while [[ -z "${RESTORE_STATUS}" && ${INCREMENT} -lt 60 ]]; do
|
||||
sleep 1
|
||||
echo -e "${ULINE}Wait for restoration to complete ...${ENDULINE}"
|
||||
RESTORE_STATUS=$(curl -sS http://edge.localhost/api/method/version || echo "")
|
||||
((INCREMENT = INCREMENT + 1))
|
||||
if [[ -z "${RESTORE_STATUS}" && ${INCREMENT} -eq 60 ]]; then
|
||||
CONTAINER_ID=$(docker-compose \
|
||||
--project-name frappebench00 \
|
||||
-f installation/docker-compose-common.yml \
|
||||
-f installation/docker-compose-erpnext.yml \
|
||||
-f installation/erpnext-publish.yml \
|
||||
ps -q erpnext-python)
|
||||
docker logs ${CONTAINER_ID}
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Ping new edge site${ENDULINE}"
|
||||
echo ${RESTORE_STATUS}
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Check New Edge Index Page${ENDULINE}"
|
||||
curl -s http://edge.localhost | w3m -T text/html -dump
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Migrate command in edge container${ENDULINE}"
|
||||
docker run -it \
|
||||
-e "MAINTENANCE_MODE=1" \
|
||||
-v frappebench00_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
-v frappebench00_assets-vol:/home/frappe/frappe-bench/sites/assets \
|
||||
--network frappebench00_default \
|
||||
frappe/erpnext-worker:edge migrate
|
||||
|
||||
checkMigrationComplete
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Restore backup from MinIO / S3 (Overwrite)${ENDULINE}"
|
||||
docker run \
|
||||
-e MYSQL_ROOT_PASSWORD=admin \
|
||||
-e BUCKET_NAME=erpnext \
|
||||
-e BUCKET_DIR=local \
|
||||
-e ACCESS_KEY_ID=${MINIO_ACCESS_KEY} \
|
||||
-e SECRET_ACCESS_KEY=${MINIO_SECRET_KEY} \
|
||||
-e ENDPOINT_URL=http://minio:9000 \
|
||||
-e REGION=us-east-1 \
|
||||
-v frappebench00_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network frappebench00_default \
|
||||
frappe/erpnext-worker:edge restore-backup
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Check Overwritten Site${ENDULINE}"
|
||||
sleep 3
|
||||
RESTORE_STATUS=$(curl -sS http://test.localhost/api/method/version || echo "")
|
||||
INCREMENT=0
|
||||
while [[ -z "${RESTORE_STATUS}" && ${INCREMENT} -lt 60 ]]; do
|
||||
sleep 1
|
||||
echo -e "${ULINE}Wait for restoration to complete ..."
|
||||
RESTORE_STATUS=$(curl -sS http://test.localhost/api/method/version || echo "")
|
||||
((INCREMENT = INCREMENT + 1))
|
||||
if [[ -z "${RESTORE_STATUS}" && ${INCREMENT} -eq 60 ]]; then
|
||||
CONTAINER_ID=$(docker-compose \
|
||||
--project-name frappebench00 \
|
||||
-f installation/docker-compose-common.yml \
|
||||
-f installation/docker-compose-erpnext.yml \
|
||||
-f installation/erpnext-publish.yml \
|
||||
ps -q erpnext-python)
|
||||
docker logs ${CONTAINER_ID}
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Ping overwritten site${ENDULINE}"
|
||||
echo ${RESTORE_STATUS}
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Check Overwritten Index Page${ENDULINE}"
|
||||
curl -s http://test.localhost | w3m -T text/html -dump
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Check console command for site test.localhost${ENDULINE}"
|
||||
docker run \
|
||||
-v frappebench00_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network frappebench00_default \
|
||||
frappe/erpnext-worker:edge console test.localhost
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Check console command for site pgsql.localhost${ENDULINE}"
|
||||
docker run \
|
||||
-v frappebench00_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network frappebench00_default \
|
||||
frappe/erpnext-worker:edge console pgsql.localhost
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Check drop site: test.localhost (mariadb)${ENDULINE}"
|
||||
docker run \
|
||||
-e SITE_NAME=test.localhost \
|
||||
-v frappebench00_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network frappebench00_default \
|
||||
frappe/erpnext-worker:edge drop
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Check drop site: pgsql.localhost (pgsql)${ENDULINE}"
|
||||
docker run \
|
||||
-e SITE_NAME=pgsql.localhost \
|
||||
-v frappebench00_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network frappebench00_default \
|
||||
frappe/erpnext-worker:edge drop
|
||||
|
||||
echo -e "${NEWLINE}${ULINE}Check bench --help${ENDULINE}"
|
||||
docker run \
|
||||
-v frappebench00_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network frappebench00_default \
|
||||
--user frappe \
|
||||
frappe/erpnext-worker:edge bench --help
|
||||
26
tests/functions.sh
Normal file
26
tests/functions.sh
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
print_group() {
|
||||
echo ::endgroup::
|
||||
echo "::group::$*"
|
||||
}
|
||||
|
||||
ping_site() {
|
||||
print_group "Ping site $SITE_NAME"
|
||||
|
||||
echo Ping version
|
||||
ping_res=$(curl -sS "http://$SITE_NAME/api/method/version")
|
||||
echo "$ping_res"
|
||||
if [[ -z $(echo "$ping_res" | grep "message" || echo "") ]]; then
|
||||
echo "Ping failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo Check index
|
||||
index_res=$(curl -sS "http://$SITE_NAME")
|
||||
if [[ -n $(echo "$index_res" | grep "Internal Server Error" || echo "") ]]; then
|
||||
echo "Index check failed"
|
||||
echo "$index_res"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
283
tests/integration-test.sh
Executable file
283
tests/integration-test.sh
Executable file
@@ -0,0 +1,283 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
source tests/functions.sh
|
||||
|
||||
project_name=frappe_bench_00
|
||||
|
||||
docker_compose_with_args() {
|
||||
# shellcheck disable=SC2068
|
||||
docker-compose \
|
||||
-p $project_name \
|
||||
-f installation/docker-compose-common.yml \
|
||||
-f installation/docker-compose-frappe.yml \
|
||||
-f installation/frappe-publish.yml \
|
||||
$@
|
||||
}
|
||||
|
||||
check_migration_complete() {
|
||||
print_group Check migration
|
||||
|
||||
container_id=$(docker_compose_with_args ps -q frappe-python)
|
||||
cmd="docker logs ${container_id} 2>&1 | grep 'Starting gunicorn' || echo ''"
|
||||
worker_log=$(eval "$cmd")
|
||||
INCREMENT=0
|
||||
|
||||
while [[ ${worker_log} != *"Starting gunicorn"* && ${INCREMENT} -lt 120 ]]; do
|
||||
sleep 3
|
||||
((INCREMENT = INCREMENT + 1))
|
||||
echo "Wait for migration to complete..."
|
||||
worker_log=$(eval "$cmd")
|
||||
if [[ ${worker_log} != *"Starting gunicorn"* && ${INCREMENT} -eq 120 ]]; then
|
||||
echo Migration timeout
|
||||
docker logs "${container_id}"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo Migration Log
|
||||
docker logs "${container_id}"
|
||||
}
|
||||
|
||||
check_health() {
|
||||
print_group Loop health check
|
||||
|
||||
docker run --name frappe_doctor \
|
||||
-v "${project_name}_sites-vol:/home/frappe/frappe-bench/sites" \
|
||||
--network "${project_name}_default" \
|
||||
frappe/frappe-worker:edge doctor || true
|
||||
|
||||
cmd='docker logs frappe_doctor | grep "Health check successful" || echo ""'
|
||||
doctor_log=$(eval "$cmd")
|
||||
INCREMENT=0
|
||||
|
||||
while [[ -z "${doctor_log}" && ${INCREMENT} -lt 60 ]]; do
|
||||
sleep 1
|
||||
((INCREMENT = INCREMENT + 1))
|
||||
container=$(docker start frappe_doctor)
|
||||
echo "Restarting ${container}..."
|
||||
doctor_log=$(eval "$cmd")
|
||||
|
||||
if [[ ${INCREMENT} -eq 60 ]]; then
|
||||
docker logs "${container}"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Initial group
|
||||
echo ::group::Setup .env
|
||||
cp env-example .env
|
||||
sed -i -e "s/edge/v13/g" .env
|
||||
cat .env
|
||||
# shellcheck disable=SC2046
|
||||
export $(cat .env)
|
||||
|
||||
print_group Start services
|
||||
echo Start main services
|
||||
docker_compose_with_args up -d --quiet-pull
|
||||
|
||||
echo Start postgres
|
||||
docker pull postgres:11.8 -q
|
||||
docker run \
|
||||
--name postgresql \
|
||||
-d \
|
||||
-e POSTGRES_PASSWORD=admin \
|
||||
-v ${project_name}_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network ${project_name}_default \
|
||||
postgres:11.8
|
||||
|
||||
check_health
|
||||
|
||||
print_group "Create new site "
|
||||
SITE_NAME=test.localhost
|
||||
docker run \
|
||||
--rm \
|
||||
-e SITE_NAME=$SITE_NAME \
|
||||
-v ${project_name}_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network ${project_name}_default \
|
||||
frappe/frappe-worker:v13 new
|
||||
|
||||
ping_site
|
||||
|
||||
print_group "Update .env (v13 -> edge)"
|
||||
sed -i -e "s/v13/edge/g" .env
|
||||
cat .env
|
||||
# shellcheck disable=SC2046
|
||||
export $(cat .env)
|
||||
|
||||
print_group Restart containers
|
||||
docker_compose_with_args stop
|
||||
docker_compose_with_args up -d
|
||||
|
||||
check_migration_complete
|
||||
sleep 5
|
||||
ping_site
|
||||
|
||||
PG_SITE_NAME=pgsql.localhost
|
||||
print_group "Create new site (Postgres)"
|
||||
docker run \
|
||||
--rm \
|
||||
-e SITE_NAME=$PG_SITE_NAME \
|
||||
-e POSTGRES_HOST=postgresql \
|
||||
-e DB_ROOT_USER=postgres \
|
||||
-e POSTGRES_PASSWORD=admin \
|
||||
-v ${project_name}_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network ${project_name}_default \
|
||||
frappe/frappe-worker:edge new
|
||||
|
||||
check_migration_complete
|
||||
SITE_NAME=$PG_SITE_NAME ping_site
|
||||
|
||||
print_group Backup site
|
||||
docker run \
|
||||
--rm \
|
||||
-e WITH_FILES=1 \
|
||||
-v ${project_name}_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network ${project_name}_default \
|
||||
frappe/frappe-worker:edge backup
|
||||
|
||||
MINIO_ACCESS_KEY="AKIAIOSFODNN7EXAMPLE"
|
||||
MINIO_SECRET_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
||||
|
||||
print_group Prepare S3 server
|
||||
echo Start S3 server
|
||||
docker run \
|
||||
--name minio \
|
||||
-d \
|
||||
-e "MINIO_ACCESS_KEY=$MINIO_ACCESS_KEY" \
|
||||
-e "MINIO_SECRET_KEY=$MINIO_SECRET_KEY" \
|
||||
--network ${project_name}_default \
|
||||
minio/minio server /data
|
||||
|
||||
echo Create bucket
|
||||
docker run \
|
||||
--rm \
|
||||
--network ${project_name}_default \
|
||||
vltgroup/s3cmd:latest \
|
||||
s3cmd \
|
||||
--access_key=$MINIO_ACCESS_KEY \
|
||||
--secret_key=$MINIO_SECRET_KEY \
|
||||
--region=us-east-1 \
|
||||
--no-ssl \
|
||||
--host=minio:9000 \
|
||||
--host-bucket=minio:9000 \
|
||||
mb s3://frappe
|
||||
|
||||
print_group Push backup
|
||||
docker run \
|
||||
--rm \
|
||||
-e BUCKET_NAME=frappe \
|
||||
-e REGION=us-east-1 \
|
||||
-e BUCKET_DIR=local \
|
||||
-e ACCESS_KEY_ID=$MINIO_ACCESS_KEY \
|
||||
-e SECRET_ACCESS_KEY=$MINIO_SECRET_KEY \
|
||||
-e ENDPOINT_URL=http://minio:9000 \
|
||||
-v ${project_name}_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network ${project_name}_default \
|
||||
frappe/frappe-worker:edge push-backup
|
||||
|
||||
print_group Prune and restart services
|
||||
docker_compose_with_args stop
|
||||
docker container prune -f && docker volume prune -f
|
||||
docker_compose_with_args up -d
|
||||
|
||||
check_health
|
||||
|
||||
print_group Restore backup from S3
|
||||
docker run \
|
||||
--rm \
|
||||
-e MYSQL_ROOT_PASSWORD=admin \
|
||||
-e BUCKET_NAME=frappe \
|
||||
-e BUCKET_DIR=local \
|
||||
-e ACCESS_KEY_ID=$MINIO_ACCESS_KEY \
|
||||
-e SECRET_ACCESS_KEY=$MINIO_SECRET_KEY \
|
||||
-e ENDPOINT_URL=http://minio:9000 \
|
||||
-e REGION=us-east-1 \
|
||||
-v ${project_name}_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network ${project_name}_default \
|
||||
frappe/frappe-worker:edge restore-backup
|
||||
|
||||
check_health
|
||||
ping_site
|
||||
SITE_NAME=$PG_SITE_NAME ping_site
|
||||
|
||||
EDGE_SITE_NAME=edge.localhost
|
||||
print_group "Create new site (edge)"
|
||||
docker run \
|
||||
--rm \
|
||||
-e SITE_NAME=$EDGE_SITE_NAME \
|
||||
-e INSTALL_APPS=frappe \
|
||||
-v ${project_name}_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network ${project_name}_default \
|
||||
frappe/frappe-worker:edge new
|
||||
|
||||
check_health
|
||||
SITE_NAME=$EDGE_SITE_NAME ping_site
|
||||
|
||||
print_group Migrate edge site
|
||||
docker run \
|
||||
--rm \
|
||||
-e MAINTENANCE_MODE=1 \
|
||||
-v ${project_name}_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
-v ${project_name}_assets-vol:/home/frappe/frappe-bench/sites/assets \
|
||||
--network ${project_name}_default \
|
||||
frappe/frappe-worker:edge migrate
|
||||
|
||||
check_migration_complete
|
||||
|
||||
print_group "Restore backup S3 (overwrite)"
|
||||
docker run \
|
||||
--rm \
|
||||
-e MYSQL_ROOT_PASSWORD=admin \
|
||||
-e BUCKET_NAME=frappe \
|
||||
-e BUCKET_DIR=local \
|
||||
-e ACCESS_KEY_ID=$MINIO_ACCESS_KEY \
|
||||
-e SECRET_ACCESS_KEY=$MINIO_SECRET_KEY \
|
||||
-e ENDPOINT_URL=http://minio:9000 \
|
||||
-e REGION=us-east-1 \
|
||||
-v ${project_name}_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network ${project_name}_default \
|
||||
frappe/frappe-worker:edge restore-backup
|
||||
|
||||
check_migration_complete
|
||||
ping_site
|
||||
|
||||
print_group "Check console for $SITE_NAME"
|
||||
docker run \
|
||||
--rm \
|
||||
-v ${project_name}_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network ${project_name}_default \
|
||||
frappe/frappe-worker:edge console $SITE_NAME
|
||||
|
||||
print_group "Check console for $PG_SITE_NAME"
|
||||
docker run \
|
||||
--rm \
|
||||
-v ${project_name}_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network ${project_name}_default \
|
||||
frappe/frappe-worker:edge console $PG_SITE_NAME
|
||||
|
||||
print_group "Check drop site for $SITE_NAME (MariaDB)"
|
||||
docker run \
|
||||
--rm \
|
||||
-e SITE_NAME=$SITE_NAME \
|
||||
-v ${project_name}_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network ${project_name}_default \
|
||||
frappe/frappe-worker:edge drop
|
||||
|
||||
print_group "Check drop site for $PG_SITE_NAME (Postgres)"
|
||||
docker run \
|
||||
--rm \
|
||||
-e SITE_NAME=$PG_SITE_NAME \
|
||||
-v ${project_name}_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network ${project_name}_default \
|
||||
frappe/frappe-worker:edge drop
|
||||
|
||||
print_group Check bench --help
|
||||
docker run \
|
||||
--rm \
|
||||
-v ${project_name}_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network ${project_name}_default \
|
||||
--user frappe \
|
||||
frappe/frappe-worker:edge bench --help
|
||||
44
tests/test-erpnext.sh
Executable file
44
tests/test-erpnext.sh
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
source tests/functions.sh
|
||||
|
||||
project_name="test_erpnext"
|
||||
SITE_NAME="test_erpnext.localhost"
|
||||
|
||||
echo ::group::Setup env
|
||||
cp env-example .env
|
||||
sed -i -e "s/edge/test/g" .env
|
||||
# shellcheck disable=SC2046
|
||||
export $(cat .env)
|
||||
cat .env
|
||||
|
||||
print_group Start services
|
||||
docker-compose \
|
||||
-p $project_name \
|
||||
-f installation/docker-compose-common.yml \
|
||||
-f installation/docker-compose-erpnext.yml \
|
||||
-f installation/erpnext-publish.yml \
|
||||
up -d
|
||||
|
||||
print_group Fix permissions
|
||||
docker run \
|
||||
--rm \
|
||||
--user root \
|
||||
-v ${project_name}_sites-vol:/sites \
|
||||
-v ${project_name}_assets-vol:/assets \
|
||||
-v ${project_name}_logs-vol:/logs \
|
||||
frappe/erpnext-worker:test chown -R 1000:1000 /logs /sites /assets
|
||||
|
||||
print_group Create site
|
||||
docker run \
|
||||
--rm \
|
||||
-e "SITE_NAME=$SITE_NAME" \
|
||||
-e "INSTALL_APPS=erpnext" \
|
||||
-v ${project_name}_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network ${project_name}_default \
|
||||
frappe/erpnext-worker:test new
|
||||
|
||||
ping_site
|
||||
rm .env
|
||||
43
tests/test-frappe.sh
Executable file
43
tests/test-frappe.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
source tests/functions.sh
|
||||
|
||||
project_name="test_frappe"
|
||||
SITE_NAME="test_frappe.localhost"
|
||||
|
||||
docker_compose_with_args() {
|
||||
# shellcheck disable=SC2068
|
||||
docker-compose \
|
||||
-p $project_name \
|
||||
-f installation/docker-compose-common.yml \
|
||||
-f installation/docker-compose-frappe.yml \
|
||||
-f installation/frappe-publish.yml \
|
||||
$@
|
||||
}
|
||||
|
||||
echo ::group::Setup env
|
||||
cp env-example .env
|
||||
sed -i -e "s/edge/test/g" .env
|
||||
# shellcheck disable=SC2046
|
||||
export $(cat .env)
|
||||
cat .env
|
||||
|
||||
print_group Start services
|
||||
docker_compose_with_args up -d
|
||||
|
||||
print_group Create site
|
||||
docker run \
|
||||
--rm \
|
||||
-e "SITE_NAME=$SITE_NAME" \
|
||||
-v ${project_name}_sites-vol:/home/frappe/frappe-bench/sites \
|
||||
--network ${project_name}_default \
|
||||
frappe/frappe-worker:test new
|
||||
|
||||
ping_site
|
||||
|
||||
print_group Stop and remove containers
|
||||
docker_compose_with_args down
|
||||
|
||||
rm .env
|
||||
150
travis.py
150
travis.py
@@ -1,150 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="frappe_docker common CI elements", add_help=True
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"service",
|
||||
action="store",
|
||||
type=str,
|
||||
help='Name of the service to build: "erpnext" or "frappe"',
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--tag-only",
|
||||
required=False,
|
||||
action="store_true",
|
||||
dest="tag_only",
|
||||
help="Only tag an image and push it.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-b",
|
||||
"--is-beta",
|
||||
required=False,
|
||||
default=False,
|
||||
action="store_true",
|
||||
dest="is_beta",
|
||||
help="Specify if tag is beta",
|
||||
)
|
||||
|
||||
image_type = parser.add_mutually_exclusive_group(required=True)
|
||||
image_type.add_argument(
|
||||
"-a",
|
||||
"--nginx",
|
||||
action="store_const",
|
||||
dest="image_type",
|
||||
const="nginx",
|
||||
help="Build the nginx + static assets image",
|
||||
)
|
||||
image_type.add_argument(
|
||||
"-s",
|
||||
"--socketio",
|
||||
action="store_const",
|
||||
dest="image_type",
|
||||
const="socketio",
|
||||
help="Build the frappe-socketio image",
|
||||
)
|
||||
image_type.add_argument(
|
||||
"-w",
|
||||
"--worker",
|
||||
action="store_const",
|
||||
dest="image_type",
|
||||
const="worker",
|
||||
help="Build the python environment image",
|
||||
)
|
||||
|
||||
tag_type = parser.add_mutually_exclusive_group(required=True)
|
||||
tag_type.add_argument(
|
||||
"-g",
|
||||
"--git-version",
|
||||
action="store",
|
||||
type=str,
|
||||
dest="version",
|
||||
help='The version number of service (i.e. "11", "12", etc.)',
|
||||
)
|
||||
tag_type.add_argument(
|
||||
"-t",
|
||||
"--tag",
|
||||
action="store",
|
||||
type=str,
|
||||
dest="tag",
|
||||
help="The image tag (i.e. erpnext-worker:$TAG )",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def git_version(service, version, branch, is_beta=False):
|
||||
print(f"Pulling {service} v{version}")
|
||||
subprocess.run(
|
||||
f"git clone https://github.com/frappe/{service} --branch {branch}", shell=True
|
||||
)
|
||||
cd = os.getcwd()
|
||||
os.chdir(os.getcwd() + f"/{service}")
|
||||
subprocess.run("git fetch --tags", shell=True)
|
||||
|
||||
# XX-beta becomes XX for tags search
|
||||
version = version.split("-")[0]
|
||||
|
||||
version_tag = (
|
||||
subprocess.check_output(
|
||||
f"git tag --list --sort=-version:refname \"v{version}*\" | sed -n 1p | sed -e 's#.*@\(\)#\\1#'",
|
||||
shell=True,
|
||||
)
|
||||
.strip()
|
||||
.decode("ascii")
|
||||
)
|
||||
|
||||
if not is_beta:
|
||||
version_tag = version_tag.split("-")[0]
|
||||
|
||||
os.chdir(cd)
|
||||
return version_tag
|
||||
|
||||
|
||||
def build(service, tag, image, branch):
|
||||
build_args = f"--build-arg GIT_BRANCH={branch}"
|
||||
if service == "erpnext":
|
||||
build_args += f" --build-arg IMAGE_TAG={branch}"
|
||||
if image == "nginx" and branch == "version-11":
|
||||
build_args += f" --build-arg NODE_IMAGE_TAG=10-prod"
|
||||
|
||||
print(f"Building {service} {image} image")
|
||||
subprocess.run(
|
||||
f"docker build {build_args} -t {service}-{image} -f build/{service}-{image}/Dockerfile .",
|
||||
shell=True,
|
||||
)
|
||||
tag_and_push(f"{service}-{image}", tag)
|
||||
|
||||
|
||||
def tag_and_push(image_name, tag):
|
||||
print(f'Tagging {image_name} as "{tag}" and pushing')
|
||||
subprocess.run(f"docker tag {image_name} frappe/{image_name}:{tag}", shell=True)
|
||||
subprocess.run(f"docker push frappe/{image_name}:{tag}", shell=True)
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
tag = args.tag
|
||||
branch = "develop"
|
||||
|
||||
if args.version:
|
||||
branch = "version-" + args.version
|
||||
tag = git_version(args.service, args.version, branch, args.is_beta)
|
||||
|
||||
if args.tag_only:
|
||||
tag_and_push(f"{args.service}-{args.image_type}", tag)
|
||||
else:
|
||||
build(args.service, tag, args.image_type, branch)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user