132 Commits

Author SHA1 Message Date
Revant Nandgaonkar
09060341c7 fix: make changes for backward compat
make changes for refactor to work on existing setups
2022-03-07 12:30:23 +05:30
dependabot[bot]
e1f32b7476 chore(deps): bump actions/checkout from 2 to 3 (#673)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-04 22:35:27 +03:00
dependabot[bot]
cc5c0b6485 chore(deps): bump actions/setup-python from 2 to 3 (#672)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 3.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-02 10:12:53 +03:00
dependabot[bot]
66fca59f74 chore(deps): bump docker/bake-action from 1.6.0 to 1.7.0 (#665)
Bumps [docker/bake-action](https://github.com/docker/bake-action) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/docker/bake-action/releases)
- [Commits](https://github.com/docker/bake-action/compare/v1.6.0...v1.7.0)

---
updated-dependencies:
- dependency-name: docker/bake-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-16 09:05:23 +03:00
Babuuu
3ba01027de Change mysql.user to mysql.global_priv -> for MariaDB 10.4+ (#650)
Co-authored-by: meradoch <meradoch@notdeadeyes.notdeadeyes>
2022-02-12 17:39:15 +05:30
Lev
8e34d092f8 Fix pre-commit config (#657)
* Fix pre-commit config

* Lint settings.json
2022-02-08 12:27:54 +03:00
Lev
05853825d8 build: Change bench target names and move CI to separate workflow (#655)
* Change bench target names and move CI to separate workflow

* Fix file detection
2022-02-08 11:35:52 +03:00
github-actions[bot]
b02191f3f8 chore(deps): Update pre-commit hooks (#652)
Co-authored-by: vrslev <vrslev@users.noreply.github.com>
2022-02-06 21:43:22 +05:30
Vaibhav Chopra
55a4dc9269 Update settings.json (#648)
removes a squiggly line whenever doing imports like 
```
from frappe.model.document import Document
```
2022-02-02 17:25:19 +05:30
github-actions[bot]
7404bcadee chore(deps): Update pre-commit hooks (#644)
Co-authored-by: vrslev <vrslev@users.noreply.github.com>
2022-01-31 12:13:43 +03:00
Nicolás Bottini
18a16372b2 Update references to $ERPNEXT_VERSION (#631)
* Update references to $ERPNEXT_VERSION

Documentation is making references to an unexisting $VERSION variable. I'm updating it to $ERPNEXT_VERSION.

* Update site-operations.md to use $FRAPPE_VERSION 

Fix references to $ERPNEXT_VERSION when using frappe/frappe-worker so now it uses $FRAPPE_VERSION
2022-01-12 12:41:50 +05:30
Robproject
42bfd485f8 Change erpnext-python volumes description (#628) 2022-01-07 14:05:04 +05:30
github-actions[bot]
52f91b318c chore(deps): Update pre-commit hooks (#625)
Co-authored-by: vrslev <vrslev@users.noreply.github.com>
2022-01-05 18:45:56 +03:00
github-actions[bot]
a46a97dc81 chore(deps): Update pre-commit hooks (#623)
Co-authored-by: vrslev <vrslev@users.noreply.github.com>
2021-12-30 09:59:56 +03:00
vrslev
d573ba8769 chore(deps): Update pre-commit hooks 2021-12-26 13:15:30 +03:00
vrslev
4daa31e1cf chore(deps): Update pre-commit hooks 2021-12-24 13:45:18 +03:00
github-actions[bot]
c2b1543128 chore(deps): Update pre-commit hooks (#618)
Co-authored-by: vrslev <vrslev@users.noreply.github.com>
2021-12-19 14:01:18 +05:30
Lev
adaa753d89 fix: Bench initialization with ERPNext v12 on frappe/bench (#613) 2021-12-11 13:57:10 +05:30
vrslev
e665b014ca chore(deps): Update pre-commit hooks 2021-12-11 10:44:51 +03:00
Lev
6b93efdf3c chore: Rename LICENSE.md to LICENSE (#608) 2021-12-10 16:25:57 +05:30
Lev
407ac2f836 ci: Remove greetings workflow (#610)
It is quite annoying, not very useful and takes 30–50 seconds to finish
2021-12-10 16:24:48 +05:30
Lev
db7823a2e2 chore(lint): Take hooks configs out so linting works without pre-commit too (#609) 2021-12-10 12:31:44 +03:00
Lev
3171f212ab Merge pull request #607 from vrslev/run-pre-commit
chore: Run pre-commit
2021-12-10 12:26:45 +03:00
Lev Vereshchagin
0b861de117 chore(lint): Run shfmt 2021-12-10 11:56:18 +03:00
Lev Vereshchagin
be7c6254f8 chore(lint): Run codespell 2021-12-10 11:54:51 +03:00
Lev Vereshchagin
1c9c4bd780 chore(lint): Change exclude in codespell hook 2021-12-10 11:54:27 +03:00
Lev Vereshchagin
fb07581595 chore(lint): Run prettier 2021-12-10 11:53:43 +03:00
Lev Vereshchagin
8b4dfce4f0 chore(lint): Run isort 2021-12-10 11:53:00 +03:00
Lev Vereshchagin
f8b7b7af05 chore(lint): Run black 2021-12-10 11:52:40 +03:00
Lev Vereshchagin
c4772bc5b5 chore(lint): Run pyupgrade 2021-12-10 11:51:47 +03:00
Lev Vereshchagin
3e4e66769b chore(lint): Run end-of-file-fixer 2021-12-10 11:51:11 +03:00
Lev Vereshchagin
9f0d602c72 chore(lint): Fix trailing-whitespace 2021-12-10 11:50:39 +03:00
Lev
2ac2b7ce43 chore: Use pre-commit framework (#604)
* Add pre-commit, prettier configs and editorconfig

* Remove `check-toml` hook

* Add isort config

* Add codespell config

* Update hooks

* Remove prettierignore

* Add shellcheck config

* Add lint workflow

* Add pre-commit autoupdate workflow

* Remove path constraints from lint workflow

* Fix CI

* Add guide to CONTRIBUTING.md

* Fix (probably) shfmt

* Fix (probably) shfmt

* Fix (probably) shfmt
2021-12-10 11:48:00 +03:00
Revant Nandgaonkar
65f6228be8 fix: update devcontainer mariadb (#605) 2021-12-06 17:55:24 +05:30
Lev
e629dd313e chore: Fix pwd url and move pwd.yml away from tests directory (#603) 2021-12-05 18:56:21 +03:00
Lev
399414bcab fix: Remove --log.level=DEBUG from traefik command to reduce log verbosity (#600) 2021-12-05 16:49:02 +05:30
Lev
6378473c43 fix(bench): Set BENCH_DEVELOPER env var in bashrc instead of Dockerfile (#598) 2021-12-05 16:45:50 +05:30
Lev
442c767338 ci(lint): Specify paths that trigger workflow (#601) 2021-12-05 12:44:34 +03:00
Lev
a48ae2d12e ci: Move greetings.yml to appropriate folder (#599) 2021-12-05 12:24:09 +03:00
Lev
81ec8da94f feat: Upgrade mariadb and use command instead of copying config file (#597) 2021-12-05 14:32:08 +05:30
Lev
180b9ababc docs: Update CONTRIBUTING.md (#594) 2021-12-03 05:06:58 +05:30
Lev
9b49391dbb feat(bench): Don't show editable-bench warnings (#593) 2021-12-02 20:36:07 +05:30
Chinmay Pai
c87ee80a22 Merge pull request #592 from revant/feat-allow-devcont-config 2021-11-30 23:28:16 +05:30
Revant Nandgaonkar
aadb0a8139 feat: allow devcontainer vscode configuration 2021-11-30 17:27:25 +05:30
Revant Nandgaonkar
0af1302485 fix: install py 3.7 using pyenv (#590)
* fix: install py 3.7 using pyenv

* fix: set pyenv global versions

* refactor: cleaup code
2021-11-30 16:03:01 +05:30
Lev
9a986575f6 ci: Fix paths that trigger workflows (#587) 2021-11-26 19:52:17 +05:30
Lev
5aae67117c Build ERPNext images on top fresh Frappe images (#582)
Update build_stable.yml
2021-11-24 05:10:27 +05:30
Revant Nandgaonkar
d049e33b40 Merge pull request #579 from frappe/dependabot/github_actions/webfactory/ssh-agent-0.5.4
chore(deps): bump webfactory/ssh-agent from 0.5.3 to 0.5.4
2021-11-22 19:13:42 +05:30
dependabot[bot]
a9c3f6e9b9 chore(deps): bump webfactory/ssh-agent from 0.5.3 to 0.5.4
Bumps [webfactory/ssh-agent](https://github.com/webfactory/ssh-agent) from 0.5.3 to 0.5.4.
- [Release notes](https://github.com/webfactory/ssh-agent/releases)
- [Changelog](https://github.com/webfactory/ssh-agent/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webfactory/ssh-agent/compare/v0.5.3...v0.5.4)

---
updated-dependencies:
- dependency-name: webfactory/ssh-agent
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-22 10:06:05 +00:00
Revant Nandgaonkar
e4ab6945a4 Merge pull request #576 from revant/change-nginx-port
fix: change unpriviliged nginx port from 80 to 8080
2021-11-22 15:18:57 +05:30
Revant Nandgaonkar
1427907a82 ci: revert shell script functions 2021-11-22 13:22:09 +05:30
Revant Nandgaonkar
91648299ba ci: fix test and revert yamls 2021-11-22 13:17:17 +05:30
Revant Nandgaonkar
d68bcdc8bd ci: output erpnext-nginx container logs 2021-11-22 06:14:30 +05:30
Revant Nandgaonkar
8fc3fbb27e ci: fix lint 2021-11-22 05:36:59 +05:30
Revant Nandgaonkar
4f7108c3a8 ci: check connections before curl 2021-11-22 05:22:12 +05:30
Revant Nandgaonkar
cb2943bdf7 ci: reload volume permissions 2021-11-21 22:37:22 +05:30
Revant Nandgaonkar
318f65170a ci: fix test volume permissions 2021-11-21 21:39:19 +05:30
Revant Nandgaonkar
3295c8070e fix: change unpriviliged nginx port from 80 to 8080 2021-11-21 16:23:32 +05:30
Revant Nandgaonkar
6b6a1b9ad8 Merge pull request #572 from revant/fix-erpnext-build
ci: fix erpnext build
2021-11-19 22:46:58 +05:30
Revant Nandgaonkar
9bfb3abde4 ci: fix erpnext nginx build permissions 2021-11-19 22:02:50 +05:30
Revant Nandgaonkar
593dfe444a ci: fix erpnext build 2021-11-19 20:41:23 +05:30
Chinmay Pai
abe6d670c4 Merge pull request #570 from revant/unprivileged-nginx 2021-11-19 09:05:57 +05:30
Revant Nandgaonkar
56c3341d74 Merge pull request #569 from Meradoch/patch-1
Fix permission denied error
2021-11-19 04:06:08 +05:30
Revant Nandgaonkar
c04edf8b6b fix(frappe-nginx): install python2 for v12 builds 2021-11-18 23:55:02 +05:30
Revant Nandgaonkar
8634460315 fix(frappe-nginx): create /assets dir 2021-11-18 22:48:22 +05:30
Revant Nandgaonkar
ef31d8e025 feat(frappe-nginx): use nginxinc/nginx-unprivileged image 2021-11-18 22:23:54 +05:30
Meradoch
2fa61e9868 Proposed changes to fix permission errors with mkdir 2021-11-18 18:40:30 +03:00
Meradoch
ccc3fc2707 Fix permission denied error 2021-11-18 14:58:25 +03:00
Revant Nandgaonkar
b7d990b2a5 Merge pull request #566 from revant/gha-creds
ci: use git command to config git creds
2021-11-11 09:20:53 +05:30
Revant Nandgaonkar
d320db2d04 ci: use git command to config git creds 2021-11-11 06:30:33 +05:30
Revant Nandgaonkar
02d8ce6d21 Merge pull request #564 from revant/gha-creds
ci: fix steps for helm release
2021-11-11 05:21:45 +05:30
Revant Nandgaonkar
cbf646b326 ci: fix steps for helm release 2021-11-11 05:06:57 +05:30
Revant Nandgaonkar
a355d8a48f Merge pull request #563 from revant/gha-creds
ci: change git creds action for helm release
2021-11-11 04:20:49 +05:30
Revant Nandgaonkar
6a8440be34 ci: change git creds action for helm release 2021-11-10 22:10:51 +05:30
Revant Nandgaonkar
c0a4079cdf ci: fix git creds for helm release (#561) 2021-11-10 21:42:19 +05:30
Lev
2c97ffd579 fix: ERPNext build (#558)
* fix(erpnext-nginx): Install recommended apt packages, set DEBIAN_FRONTEND=noninteractive

* Revert "fix(erpnext-nginx): Install recommended apt packages, set DEBIAN_FRONTEND=noninteractive"

This reverts commit 652481175d.

* fix(erpnext-worker): Install gcc
2021-11-10 11:24:47 +03:00
Lev
7c157aa487 refactor: Dockerfiles (#555)
* refactor(frappe-worker): Dockerfile

* fix(frappe-worker): Cloning frappe repo

* refactor(frappe-nginx): Dockerfile

* refactor(erpnext-nginx): Dockerfile

* chore(frappe-worker): Add space in Dockerfile

* refactor(bench): Dockerfile

* refactor(frappe-socketio): Dockerfile

* ci(Test): Add `workflow_dispatch` trigger

* fix(bench): Uncomment missing package

* fix(bench): Replace legacy `libmariadbclient-dev` with `libmariadb-dev`

* refactor(erpnext-nginx): Install script

- Remove repetitive code
- Don't explicitly install production deps (they are installed via plain `yarn` with dev deps)
- Don't write to lockfiles

* fix(erpnext-nginx): use -e flag, fix installation

* fix(erpnext-nginx): Don't install unused wget

* fix(frappe-nginx): Get newest certificates

* fix: Install ca-certificates on erpnext-nginx instead of frappe-nginx

* trigger ci

* ci(test): Fix trigger duplicate

* chore: Fix linting
2021-11-10 04:43:46 +05:30
Revant Nandgaonkar
f0997dbf23 Merge pull request #556 from vrslev/fix-drop-flags
fix(frappe-worker): Drop flags
2021-11-09 22:20:20 +05:30
Lev
5a38d66a70 chore: Fix linting 2021-11-09 16:54:02 +03:00
Lev
a2db23e68e fix(frappe-worker): Drop flags 2021-11-09 16:49:40 +03:00
Lev
5254e2aad3 fix: Improve CI, fix builds (#554)
* ci(Test): Add `workflow_dispatch` trigger

* ci(Test): Build and run on push and pr

* test: Set failfast, remove ERPNext reference

* test: Reveal .env file for logs

* ci(Test): Rename step

* ci(Test): Fix git tag

* test: Add -x flag for debugging

* test: Fix failing test

* ci(Test): Check if built images are used

* ci(Test): Configure trigger paths, don't build on schedule

* test: Remove bug

* ci: Build and test stable images on PR

* fix(frappe-nginx): Get back to `ls` from `find`

* check if test passes with erpnext images

* fix(frappe-worker): Default sites in backup command

* get integration test changes back

* chore: Fix linting
2021-11-09 15:15:33 +03:00
Lev
b57371edf3 chore: Add version constraint for lint action 2021-11-09 14:52:44 +03:00
Lev
fe47a4b165 ci: Fix docker dependabot config 2021-11-09 14:29:06 +03:00
Lev
f74f730b5e chore: Fix lint issues (#553)
* ci(Lint): Temporarily disable branch contraint

* chore(erpnext-nginx): Fix linting

* chore: Fix linting issues

* ci(Lint): Add -x flag

* ci(Lint): Fix push trigger

* chore: Fix linting and formatting

* fix: Gunicorn server start

* chore(frappe-worker): Fix linting

* chore(frappe-nginx): Fix linting

* fix(erpnext-nginx): App installation

* fix(erpnext-nginx): App installation

* refactor(frappe-nginx): Use `find` instead of `ls`
2021-11-09 13:05:29 +03:00
Revant Nandgaonkar
358293afc9 Merge pull request #549 from vrslev/refactor-worker-commands
refactor(frappe-worker): Replace basic commands with bench
2021-11-09 04:12:58 +05:30
Lev
d1ed880c78 ci(Dependabot): Add config for docker and npm 2021-11-08 19:44:10 +03:00
Revant Nandgaonkar
5aa6e4ec4a Merge pull request #548 from vrslev/fix-socketio-cmd
fix(frappe-socketio): Remove dead `doctor` command
2021-11-08 21:21:18 +05:30
Lev
e4df6e5aec refactor(worker): Replace basic commands with bench 2021-11-08 16:10:26 +03:00
Lev
457ecc513b fix(frappe-socketio): Remove dead doctor command 2021-11-08 14:51:21 +03:00
Lev
9d41a6d24e Fix Python version for v12 2021-11-06 21:26:15 +03:00
Lev
de04eb032c build: Fix develop build 2021-11-06 21:11:29 +03:00
Lev
089a2dc206 build: Fix stable builds (#546) 2021-11-06 21:07:39 +03:00
Lev
daa1926f03 Revert "update"
This reverts commit ce1ed546ab.
2021-11-06 20:02:42 +03:00
Lev
ce1ed546ab update 2021-11-06 20:01:55 +03:00
Revant Nandgaonkar
8ad84719ea Merge pull request #544 from vrslev/improve-tests-and-fix-builds
Fix builds and tests
2021-11-06 17:10:33 +05:30
Revant Nandgaonkar
a8ab724212 Merge pull request #545 from vrslev/build-structure
chore: Move files from build/common
2021-11-06 11:22:47 +05:30
Lev
c808ad1767 chore: Move files from build/common 2021-11-05 20:00:11 +03:00
Lev
9a4aa8d226 ci: Lint on PR 2021-11-05 19:36:47 +03:00
Lev
9fa3e1b0e0 Merge branch 'main' into improve-tests-and-fix-builds 2021-11-05 19:01:18 +03:00
Lev
d2b18a3b48 update 2021-11-05 18:58:02 +03:00
Lev
38c7f1e269 Merge pull request #543 from vrslev/fix-build
ci: Fix stable build
2021-11-04 19:02:28 +03:00
Lev
de255d8052 ci: Fix stable build 2021-11-04 19:00:16 +03:00
Revant Nandgaonkar
915885bb05 Merge pull request #542 from vrslev/push-images-instantly
ci: Push images right after build
2021-11-04 21:27:45 +05:30
Lev
3c0f08652e ci: Push images right after build
This fixes the bug when ERPNext images are built upon old Frappe images and test are ran on old images
2021-11-04 15:24:08 +03:00
Revant Nandgaonkar
e685dc5ebc Merge pull request #540 from vrslev/update-base-images
build: Update base images
2021-11-04 14:02:28 +05:30
Lev
a4c115f96d build: Update Python from 3.7 to 3.9; Debian from 10 to 11 2021-11-03 16:48:01 +03:00
Revant Nandgaonkar
33ec8cafc3 Merge pull request #537 from dtwm/develop
fix: Add missing packages for frappe:develop
2021-11-01 20:49:07 +05:30
dt
63da01c702 fix: Add missing packages for frappe:develop 2021-11-01 10:49:45 +03:00
Lev
2da9c02d58 ci: Build fixes
- Simplify builds by separating dev/stable workflows
- Fix Helm deploy key
- Remove deploy_key.env (already using deploy key in secrets)
- Fix paths matching on push and pull_request triggers
- Fix possible issues with tag difference between ERPNext and Frappe (add tag resolving step before pushing)
- Don't login, push Docker images and release Helm chart on forks
- Don't test on version 12 (there's no test for this version)
- Remove frappe-installer (from chore: use github actions #525)
- Fix badges in readme
2021-10-31 11:02:58 +05:30
Revant Nandgaonkar
37b8ccece1 Merge pull request #528 from frappe/dependabot/github_actions/actions/stale-4
chore(deps): bump actions/stale from 1 to 4
2021-10-30 18:28:17 +05:30
Revant Nandgaonkar
08a542978f Merge pull request #533 from revant/ci-fix-workflows
ci: fixes ci steps
2021-10-30 16:26:07 +05:30
Revant Nandgaonkar
7d36be56f4 ci: fixes ci steps
helm release deploy key uses webfactory/ssh-agent@v0.5.3
test only on PR
docker login only if not PR
2021-10-30 11:47:12 +05:30
Revant Nandgaonkar
4c79d6d7c6 Merge pull request #531 from revant/fix-b64decode
ci: fix base64 decode of env variable
2021-10-29 22:48:56 +05:30
Revant Nandgaonkar
107f393277 ci: fix base64 decode of env variable 2021-10-29 22:44:30 +05:30
Revant Nandgaonkar
47b2d34dd1 Merge pull request #530 from vrslev/fix-typo-in-gha-workflow
ci(Build): Fix paths for PRs and branches
2021-10-29 16:49:23 +05:30
Lev
1daec15268 ci(Build): Fix paths for PRs and branches 2021-10-29 13:44:25 +03:00
dependabot[bot]
7518c2d374 chore(deps): bump actions/stale from 1 to 4
Bumps [actions/stale](https://github.com/actions/stale) from 1 to 4.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v1...v4)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-29 06:04:50 +00:00
Revant Nandgaonkar
20a119d7aa Merge pull request #527 from vrslev/develop
ci: Use Docker Buildx Bake and GitHub Actions
2021-10-29 11:34:32 +05:30
Lev
b18119574b ci: Use base64 encoded helm deploy key 2021-10-28 10:39:24 +03:00
Lev
a3cf159439 ci: Uncomment for-production code 2021-10-27 16:27:46 +03:00
Lev
078a7f5602 chore: Rename badge 2021-10-27 16:10:14 +03:00
Lev
1cbb845af1 ci: Use GitHub Actions to build images 2021-10-27 16:07:43 +03:00
Revant Nandgaonkar
c149a93f31 Merge pull request #517 from revant/dev-docs
dev updates
2021-09-15 15:12:37 +05:30
Revant Nandgaonkar
90e973090e chore: devcontainer update
specify shell for devcontainer

[skip travis]
2021-09-15 15:11:36 +05:30
Revant Nandgaonkar
401613900f docs: update dev README for v13 2021-09-15 15:10:19 +05:30
Revant Nandgaonkar
9d3e66528d Merge pull request #509 from PeterDaveHello/clean-up-apt-cache-in-dockerfile
Make sure apt lists cleaned up in Dockerfile
2021-08-11 06:32:27 +05:30
Revant Nandgaonkar
48e9947580 fix: empty continuation line 2021-08-10 21:33:08 +05:30
Peter Dave Hello
428b8abffe Make sure apt lists cleaned up in Dockerfile 2021-08-10 19:16:46 +08:00
Revant Nandgaonkar
7fc0f06610 Merge pull request #508 from PeterDaveHello/patch-1
Set git clone depth to 1 in build/bench/Dockerfile
2021-08-09 21:09:01 +05:30
Peter Dave Hello
08442253c8 Set git clone depth to 1 in build/bench/Dockerfile
Looks like it's done in the other Dockerfiles
2021-08-09 16:13:05 +08:00
Revant Nandgaonkar
911c4ca6ce Merge pull request #499 from iboen/patch-1
fix: rename volume for creating job
2021-07-11 19:46:49 +05:30
Ibnu Sina Wardy
f5d9832ae1 fix: rename volume for creating job 2021-07-10 20:51:49 +07:00
87 changed files with 2451 additions and 2068 deletions

19
.editorconfig Normal file
View 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

View File

@@ -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
View 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
View 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
View 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
View 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
View 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
View 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

View 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

View File

@@ -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
View 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
View 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
View File

@@ -0,0 +1 @@
installation/docker-compose-custom.yml

1
.shellcheckrc Normal file
View File

@@ -0,0 +1 @@
external-sources=true

View File

@@ -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

View File

@@ -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": []
}

View File

@@ -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

View File

@@ -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.

View File

View File

@@ -1,12 +1,11 @@
| Develop | [![Build Status](https://travis-ci.com/frappe/frappe_docker.svg?branch=develop)](https://travis-ci.com/frappe/frappe_docker) |
|---------|-----------------------------------------------------------------------------------------------------------------------------|
| Master | [![Build Status](https://travis-ci.com/frappe/frappe_docker.svg?branch=master)](https://travis-ci.com/frappe/frappe_docker) |
[![Build Stable](https://github.com/frappe/frappe_docker/actions/workflows/build_stable.yml/badge.svg)](https://github.com/frappe/frappe_docker/actions/workflows/build_stable.yml)
[![Build Develop](https://github.com/frappe/frappe_docker/actions/workflows/build_develop.yml/badge.svg)](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)

View File

@@ -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"

View File

@@ -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()

View File

@@ -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()

View File

@@ -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'

View File

@@ -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()

View File

@@ -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()

View File

@@ -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:],
)

View File

@@ -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

View File

@@ -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}

View File

@@ -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" ]

View File

@@ -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

View File

@@ -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}

View File

@@ -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;"]

View File

@@ -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}"

View File

@@ -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;

View File

@@ -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"]

View File

@@ -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

View File

@@ -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
View 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:],
)

View File

@@ -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)

View 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()

View File

@@ -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__":

View 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"

View File

@@ -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)

View File

@@ -1,2 +1,3 @@
import gevent.monkey
gevent.monkey.patch_all()

View File

@@ -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()

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View 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

View File

@@ -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}"

View 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}"

Binary file not shown.

View File

@@ -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

View File

@@ -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"
]
}
]
}

View File

@@ -1,4 +0,0 @@
{
"python.pythonPath": "frappe-bench/env/bin/python",
"debug.node.autoAttach": "disabled"
}

View File

@@ -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
```

View 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"
]
}
]
}

View 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
View 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"]
}

View File

@@ -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:

View File

@@ -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

View File

@@ -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
```
```

View File

@@ -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

View File

@@ -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:

View File

@@ -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)

View File

@@ -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

View File

@@ -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.

View File

@@ -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:

View File

@@ -1,4 +1,4 @@
version: '3'
version: "3"
services:
erpnext-nginx:

View File

@@ -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:

View File

@@ -1,4 +1,4 @@
version: '3'
version: "3"
networks:
default:

View File

@@ -3,4 +3,4 @@ version: "3"
services:
erpnext-nginx:
ports:
- "80:80"
- "80:8080"

View File

@@ -1,7 +0,0 @@
[mysqld]
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
[mysql]
default-character-set = utf8mb4

View File

@@ -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

View File

@@ -3,4 +3,4 @@ version: "3"
services:
frappe-nginx:
ports:
- "80:80"
- "80:8080"

View File

@@ -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
View 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

View File

@@ -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")

View File

@@ -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
View 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
View 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
View 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
View 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
View File

@@ -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()