├── .drone.yml ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature-request.md └── stale.yml ├── .gitignore ├── LICENSE ├── Makefile ├── common.Makefile ├── docker.Makefile ├── hack ├── bats │ ├── .appveyor.yml │ ├── .editorconfig │ ├── .gitattributes │ ├── .travis.yml │ ├── AUTHORS │ ├── Dockerfile │ ├── LICENSE.md │ ├── README.md │ ├── bin │ │ └── bats │ ├── contrib │ │ └── rpm │ │ │ └── bats.spec │ ├── docs │ │ ├── CHANGELOG.md │ │ ├── CODEOWNERS │ │ ├── CODE_OF_CONDUCT.md │ │ ├── CONTRIBUTING.md │ │ ├── PULL_REQUEST_TEMPLATE.md │ │ ├── releasing.md │ │ └── usage.md │ ├── install.sh │ ├── libexec │ │ └── bats-core │ │ │ ├── bats │ │ │ ├── bats-exec-suite │ │ │ ├── bats-exec-test │ │ │ ├── bats-format-tap-stream │ │ │ └── bats-preprocess │ ├── man │ │ ├── Makefile │ │ ├── README.md │ │ ├── bats.1 │ │ ├── bats.1.ronn │ │ ├── bats.7 │ │ └── bats.7.ronn │ ├── package.json │ └── test │ │ ├── bats.bats │ │ ├── fixtures │ │ ├── bats │ │ │ ├── cmd_using_stdin.bash │ │ │ ├── dos_line.bats │ │ │ ├── duplicate-tests.bats │ │ │ ├── empty.bats │ │ │ ├── environment.bats │ │ │ ├── expand_var_in_test_name.bats │ │ │ ├── exported_function.bats │ │ │ ├── external_function_calls.bats │ │ │ ├── failing.bats │ │ │ ├── failing_and_passing.bats │ │ │ ├── failing_helper.bats │ │ │ ├── failing_setup.bats │ │ │ ├── failing_teardown.bats │ │ │ ├── intact.bats │ │ │ ├── load.bats │ │ │ ├── loop_keep_IFS.bats │ │ │ ├── no-final-newline.bats │ │ │ ├── output.bats │ │ │ ├── parallel.bats │ │ │ ├── passing.bats │ │ │ ├── passing_and_failing.bats │ │ │ ├── passing_and_skipping.bats │ │ │ ├── passing_failing_and_skipping.bats │ │ │ ├── quoted_and_unquoted_test_names.bats │ │ │ ├── read_from_stdin.bats │ │ │ ├── reference_unset_parameter.bats │ │ │ ├── reference_unset_parameter_in_setup.bats │ │ │ ├── reference_unset_parameter_in_teardown.bats │ │ │ ├── setup.bats │ │ │ ├── single_line.bats │ │ │ ├── skipped.bats │ │ │ ├── skipped_with_parens.bats │ │ │ ├── source_nonexistent_file.bats │ │ │ ├── source_nonexistent_file_in_setup.bats │ │ │ ├── source_nonexistent_file_in_teardown.bats │ │ │ ├── teardown.bats │ │ │ ├── test_helper.bash │ │ │ ├── unbound_variable.bats │ │ │ ├── unofficial_bash_strict_mode.bash │ │ │ ├── unofficial_bash_strict_mode.bats │ │ │ ├── whitespace.bats │ │ │ └── without_trailing_newline.bats │ │ └── suite │ │ │ ├── empty │ │ │ └── .gitkeep │ │ │ ├── filter │ │ │ ├── a.bats │ │ │ ├── b.bats │ │ │ └── c.bats │ │ │ ├── multiple │ │ │ ├── a.bats │ │ │ └── b.bats │ │ │ ├── parallel │ │ │ ├── parallel1.bats │ │ │ ├── parallel2.bats │ │ │ ├── parallel3.bats │ │ │ └── parallel4.bats │ │ │ ├── recursive │ │ │ ├── subsuite │ │ │ │ └── test2.bats │ │ │ └── test.bats │ │ │ └── single │ │ │ └── test.bats │ │ ├── install.bats │ │ ├── root.bats │ │ ├── suite.bats │ │ └── test_helper.bash ├── container-structure-test ├── php-series └── wordpress-php-series ├── php ├── Dockerfile-7.4 ├── Dockerfile-8.1 ├── Dockerfile-8.2 ├── Dockerfile-8.3 ├── Makefile ├── README.md ├── docker │ ├── bin │ │ ├── docker-entrypoint │ │ └── wp-install │ ├── build-scripts │ │ ├── install-composer │ │ ├── install-dockerize │ │ ├── install-php-extensions │ │ ├── install-supervisord │ │ ├── php-extensions.minimal.debug.yaml │ │ └── php-extensions.minimal.yaml │ ├── etc │ │ ├── .gitkeep │ │ └── README.md │ ├── lib │ │ └── php │ │ │ └── phpinfo.php │ └── templates │ │ ├── msmtp.conf │ │ ├── nginx │ │ ├── conf.d │ │ │ ├── 10-client-body-path.conf │ │ │ ├── 10-logging.conf │ │ │ ├── 10-lua-init.conf │ │ │ ├── 10-net.conf │ │ │ ├── 10-real-ip.conf │ │ │ ├── 10-temp-paths.conf │ │ │ ├── 20-resolver.conf │ │ │ ├── 40-gzip.conf │ │ │ ├── 40-mime-types.conf │ │ │ ├── 40-php-upstreams.conf │ │ │ ├── 40-server-tokens.conf │ │ │ ├── 90-custom.conf │ │ │ ├── 90-metrics.conf │ │ │ ├── 90-php-whitelist-map.conf │ │ │ └── 99-default-server.conf │ │ ├── fastcgi.conf │ │ ├── fastcgi_params │ │ ├── gcs-proxy.conf │ │ ├── nginx.conf │ │ ├── stats.htpasswd │ │ └── vhost-conf.d │ │ │ ├── 00-custom-pre.conf │ │ │ ├── 10-upstreams.conf │ │ │ ├── 20-client.conf │ │ │ ├── 30-buffering.conf │ │ │ ├── 30-cors-headers.conf │ │ │ ├── 30-ping.conf │ │ │ ├── 30-well-known-locations.conf │ │ │ ├── 40-gcloud-access-token.conf │ │ │ ├── 75-page-cache-locations.conf │ │ │ ├── 80-index.conf │ │ │ ├── 99-custom-post.conf │ │ │ └── page-cache.d │ │ │ └── 10-index.conf │ │ ├── php-fpm.conf │ │ ├── php-fpm.d │ │ └── www.conf │ │ ├── php.ini │ │ ├── start-nginx.sh │ │ ├── start-php-fpm.sh │ │ └── supervisor.conf ├── nginx-lua │ ├── resty_modules │ │ ├── lualib │ │ │ ├── prometheus.lua │ │ │ ├── prometheus_keys.lua │ │ │ ├── prometheus_resty_counter.lua │ │ │ └── resty │ │ │ │ ├── aes.lua │ │ │ │ ├── evp.lua │ │ │ │ ├── hmac.lua │ │ │ │ ├── http.lua │ │ │ │ ├── http_connect.lua │ │ │ │ ├── http_headers.lua │ │ │ │ ├── jwt-validators.lua │ │ │ │ ├── jwt.lua │ │ │ │ ├── md5.lua │ │ │ │ ├── mlcache.lua │ │ │ │ ├── mlcache │ │ │ │ └── ipc.lua │ │ │ │ ├── random.lua │ │ │ │ ├── sha.lua │ │ │ │ ├── sha1.lua │ │ │ │ ├── sha224.lua │ │ │ │ ├── sha256.lua │ │ │ │ ├── sha384.lua │ │ │ │ ├── sha512.lua │ │ │ │ └── string.lua │ │ ├── manifest │ │ │ ├── lua-resty-hmac.list │ │ │ ├── lua-resty-hmac.meta │ │ │ ├── lua-resty-http.list │ │ │ ├── lua-resty-http.meta │ │ │ ├── lua-resty-jwt.list │ │ │ ├── lua-resty-jwt.meta │ │ │ ├── lua-resty-mlcache.list │ │ │ ├── lua-resty-mlcache.meta │ │ │ ├── lua-resty-string.list │ │ │ ├── lua-resty-string.meta │ │ │ ├── nginx-lua-prometheus.list │ │ │ └── nginx-lua-prometheus.meta │ │ ├── pod │ │ │ ├── lua-resty-hmac-0.06 │ │ │ │ └── lua-resty-hmac-0.06.pod │ │ │ ├── lua-resty-http-0.16.1 │ │ │ │ └── lua-resty-http-0.16.1.pod │ │ │ ├── lua-resty-jwt-0.2.0 │ │ │ │ └── lua-resty-jwt-0.2.0.pod │ │ │ ├── lua-resty-mlcache-2.6.0 │ │ │ │ └── lua-resty-mlcache-2.6.0.pod │ │ │ ├── lua-resty-string-0.11 │ │ │ │ └── lua-resty-string-0.11.pod │ │ │ └── nginx-lua-prometheus-0.20221218 │ │ │ │ ├── changelog.pod │ │ │ │ ├── integration.readme.pod │ │ │ │ └── nginx-lua-prometheus-0.20221218.pod │ │ └── resty.index │ └── src │ │ ├── .luarc.json │ │ └── bitpoke │ │ ├── cors.lua │ │ ├── gcs.lua │ │ └── mime-types.lua ├── tag.sh └── test │ ├── container-structure-test.yaml │ └── test.sh ├── project.Makefile ├── var.Makefile └── wordpress ├── Dockerfile-6.7 ├── Dockerfile-6.7-php-7.4 ├── Dockerfile-6.7-php-8.1 ├── Dockerfile-6.7-php-8.3 ├── Dockerfile-6.8 ├── Dockerfile-6.8-php-8.3 ├── Dockerfile-bedrock ├── Dockerfile-bedrock-build ├── Dockerfile-bedrock-build-php-7.4 ├── Dockerfile-bedrock-build-php-8.1 ├── Dockerfile-bedrock-build-php-8.3 ├── Dockerfile-bedrock-php-7.4 ├── Dockerfile-bedrock-php-8.1 ├── Dockerfile-bedrock-php-8.3 ├── Makefile ├── docker ├── bin │ └── wp-install ├── build-scripts │ └── install-wp-cli ├── templates │ ├── nginx │ │ └── vhost-conf.d │ │ │ ├── 50-gcs-media.conf │ │ │ ├── 50-upstreams.conf │ │ │ ├── 60-metrics.conf │ │ │ └── 99-subpath-rewrites.conf │ └── wp-cli.yaml └── webroot │ ├── index.php │ └── wp-config.php ├── tag.sh └── test ├── bedrock-php-7.4 ├── .env.example ├── .gitignore ├── CHANGELOG.md ├── Dockerfile ├── LICENSE.md ├── README.md ├── composer.json ├── composer.lock ├── config │ ├── application.php │ └── environments │ │ ├── development.php │ │ └── staging.php ├── web │ ├── app │ │ ├── mu-plugins │ │ │ ├── bedrock-autoloader.php │ │ │ ├── disallow-indexing.php │ │ │ └── register-theme-directory.php │ │ ├── plugins │ │ │ └── .gitkeep │ │ ├── themes │ │ │ ├── .gitkeep │ │ │ └── default-test-theme │ │ │ │ ├── index.php │ │ │ │ └── style.css │ │ └── uploads │ │ │ └── .gitkeep │ ├── index.php │ └── wp-config.php └── wp-cli.yml ├── bedrock ├── .env.example ├── .gitignore ├── CHANGELOG.md ├── Dockerfile ├── LICENSE.md ├── README.md ├── composer.json ├── composer.lock ├── config │ ├── application.php │ └── environments │ │ ├── development.php │ │ └── staging.php ├── phpcs.xml ├── web │ ├── app │ │ ├── mu-plugins │ │ │ ├── bedrock-autoloader.php │ │ │ └── register-theme-directory.php │ │ ├── plugins │ │ │ └── .gitkeep │ │ ├── themes │ │ │ └── .gitkeep │ │ └── uploads │ │ │ └── .gitkeep │ ├── index.php │ └── wp-config.php └── wp-cli.yml ├── classic ├── Dockerfile ├── config │ └── .gitkeep └── wp-content │ └── themes │ └── default-test-theme │ ├── index.php │ └── style.css ├── container-structure-test.yaml ├── docker-compose.yml ├── e2e.bats └── test.sh /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a bug or a defect 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **What happened**: 11 | 12 | **What you expected to happen**: 13 | 14 | **How to reproduce it (as minimally and precisely as possible)**: 15 | 16 | **Anything else?**: 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Request a new feature or an enhancement 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 15 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - security 8 | - lifecycle/frozen 9 | # Label to use when marking an issue as stale 10 | staleLabel: lifecycle/stale 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | 17 | You can add lifecycle/frozen label to indicate that this issue or PR should 18 | not be auto-closed due to inactivity. 19 | # Comment to post when closing a stale issue. Set to `false` to disable 20 | closeComment: > 21 | This issue has been closed due to lack of activity. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | docker-compose/html 2 | .build 3 | 4 | .#* 5 | -------------------------------------------------------------------------------- /common.Makefile: -------------------------------------------------------------------------------- 1 | ifndef __COMMON_MAKEFILE__ 2 | 3 | __COMMON_MAKEFILE__ := included 4 | 5 | define print_target 6 | @$(call print_notice,Building $@...) 7 | endef 8 | 9 | define print_notice 10 | printf "\n\033[93m\033[1m$(1)\033[0m\n" 11 | endef 12 | 13 | define print_error 14 | printf "\n\033[93m\033[1m$(1)\033[0m\n" 15 | endef 16 | 17 | .build: 18 | mkdir -p "$@" 19 | 20 | .build/tmp: | .build 21 | mkdir -p "$@" 22 | 23 | .PHONY: clean 24 | clean:: 25 | rm -Rf .build 26 | 27 | 28 | endif 29 | -------------------------------------------------------------------------------- /docker.Makefile: -------------------------------------------------------------------------------- 1 | ifndef __DOCKER_MAKEFILE__ 2 | 3 | __DOCKER_MAKEFILE__ := included 4 | 5 | makefile_dir := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) 6 | include $(makefile_dir)/common.Makefile 7 | include $(makefile_dir)/var.Makefile 8 | 9 | # this dance is required since .build/var/VARNAME requires that VARNAME to be defined 10 | # and TAG_SUFFIX is not always defined 11 | ifdef TAG_SUFFIX 12 | TAG_SUFFIX_SLUG := -$(TAG_SUFFIX) 13 | TAG_SUFFIX_DEP := .build/var/TAG_SUFFIX 14 | else 15 | TAG_SUFFIX_DEP := .build/.TAG_SUFFIX 16 | .build/.TAG_SUFFIX: | .build 17 | @rm -f .build/var/TAG_SUFFIX 18 | endif 19 | .PRECIOUS: .build/var/TAG_SUFFIX 20 | 21 | define build_targets_for 22 | $(patsubst Dockerfile-%,$(1)-%,$(DOCKERFILES)) 23 | endef 24 | 25 | DOCKER_BUILD ?= docker build --pull 26 | 27 | .PHONY: images 28 | $(call build_targets_for, $(RUNTIME)): 29 | images: $(call build_targets_for, $(RUNTIME)) 30 | $(RUNTIME)-%: .build/$(RUNTIME)-% ; 31 | 32 | .PHONY: test 33 | $(call build_targets_for, test-$(RUNTIME)): 34 | test: $(call build_targets_for, test-$(RUNTIME)) 35 | test-$(RUNTIME)-%: .build/test-$(RUNTIME)-% ; 36 | 37 | .PHONY: pull-cache 38 | $(call build_targets_for, pull-$(RUNTIME)): 39 | pull-cache: $(call build_targets_for, pull-$(RUNTIME)) 40 | pull-$(RUNTIME)-%: 41 | docker pull $(REGISTRY):$(@:pull-$(RUNTIME)-%=%)$(TAG_SUFFIX_SLUG) || true 42 | 43 | .PHONY: tags 44 | $(call build_targets_for, tag-$(RUNTIME)): 45 | tags: $(call build_targets_for, tag-$(RUNTIME)) 46 | tag-$(RUNTIME)-%: .build/tag-$(RUNTIME)-% ; 47 | 48 | .PHONY: push-images 49 | $(call build_targets_for, push-$(RUNTIME)): 50 | push-images: $(call build_targets_for, push-$(RUNTIME)) 51 | push-$(RUNTIME)-%: .build/tag-$(RUNTIME)-% 52 | @for tag in $$(cat $<); do \ 53 | echo docker push $(REGISTRY):$${tag} ; \ 54 | docker push $(REGISTRY):$${tag} ; \ 55 | done 56 | 57 | .PRECIOUS: .build/$(RUNTIME)-% 58 | .build/$(RUNTIME)-%: Dockerfile-% $(SRCS) | .build 59 | $(DOCKER_BUILD) \ 60 | $(patsubst %,--build-arg BASE_IMAGE=%,$(BASE_IMAGE)) \ 61 | $(patsubst %,--build-arg PHP_BASE_IMAGE=%,$(PHP_BASE_IMAGE)) \ 62 | -t local$@ \ 63 | --cache-from $(REGISTRY):$(@:.build/$(RUNTIME)-%=%) \ 64 | --cache-from $(REGISTRY):$(@:.build/$(RUNTIME)-%=%)$(TAG_SUFFIX_SLUG) \ 65 | -f $(@:.build/$(RUNTIME)-%=Dockerfile-%) . 66 | touch "$@" 67 | 68 | .build/test-$(RUNTIME)-%: .build/$(RUNTIME)-% 69 | $(CURDIR)/test/test.sh local$< 70 | 71 | .PRECIOUS: .build/tag-$(RUNTIME)-% 72 | .build/tag-$(RUNTIME)-%: .build/$(RUNTIME)-% $(TAG_SUFFIX_DEP) 73 | @TAG_SUFFIX="$(TAG_SUFFIX)" ./tag.sh $(@:.build/tag-$(RUNTIME)-%=%) local$(@:.build/tag-$(RUNTIME)-%=.build/$(RUNTIME)-%) | sort | uniq > $@ 74 | @for tag in $$(cat $@); do \ 75 | echo docker tag local$(@:.build/tag-$(RUNTIME)-%=.build/$(RUNTIME)-%) $(REGISTRY):$${tag} ; \ 76 | docker tag local$(@:.build/tag-$(RUNTIME)-%=.build/$(RUNTIME)-%) $(REGISTRY):$${tag} ; \ 77 | done 78 | 79 | endif 80 | -------------------------------------------------------------------------------- /hack/bats/.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 'v1.1.0+appveyor.{build}' 2 | 3 | build: off 4 | 5 | # This presumes that Git bash is installed at `C:\Program Files\Git` and the 6 | # bash we're using is `C:\Program Files\Git\bin\bash.exe`. 7 | # 8 | # If instead it finds the Windows Subsystem for Linux bash at 9 | # `C:\Windows\System32\bash.exe`, it will fail with an error like: 10 | # /mnt/c/.../bats-core/test/test_helper.bash: line 1: 11 | # syntax error near unexpected token `$'{\r'' 12 | test_script: 13 | - where bash 14 | - bash --version 15 | - bash -c 'export' 16 | - bash -c 'time PATH="/usr/bin:${PATH}" bin/bats test' 17 | -------------------------------------------------------------------------------- /hack/bats/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | indent_style = space 6 | indent_size = 2 7 | insert_final_newline = true 8 | max_line_length = 80 9 | trim_trailing_whitespace = true 10 | 11 | # The JSON files contain newlines inconsistently 12 | [*.json] 13 | indent_size = 2 14 | insert_final_newline = ignore 15 | 16 | # YAML 17 | [*.{yml,yaml}] 18 | indent_style = space 19 | indent_size = 2 20 | 21 | # Makefiles always use tabs for recipe indentation 22 | [{Makefile,*.mak}] 23 | indent_style = tab 24 | 25 | # Markdown 26 | [*.{md,rmd,mkd,mkdn,mdwn,mdown,markdown,litcoffee}] 27 | max_line_length = 80 28 | # tabs behave as if they were replaced by spaces with a tab stop of 4 characters 29 | tab_width = 4 30 | # trailing spaces indicates word wrap 31 | trim_trailing_spaces = false 32 | trim_trailing_whitespace = false 33 | -------------------------------------------------------------------------------- /hack/bats/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.sh eol=lf 3 | libexec/* eol=lf 4 | -------------------------------------------------------------------------------- /hack/bats/.travis.yml: -------------------------------------------------------------------------------- 1 | language: bash 2 | 3 | os: 4 | - linux 5 | 6 | env: 7 | - BASHVER= 8 | - BASHVER=3.2 9 | - BASHVER=4.0 10 | - BASHVER=4.1 11 | - BASHVER=4.2 12 | - BASHVER=4.3 13 | - BASHVER=4.4 14 | - BASHVER=5 15 | 16 | matrix: 17 | include: 18 | - os: osx 19 | 20 | services: 21 | - docker 22 | 23 | script: 24 | - | 25 | if [[ "${TRAVIS_OS_NAME:-}" == 'linux' && -n "${BASHVER}" ]]; then 26 | docker build --build-arg bashver="${BASHVER}" --tag "bats/bats:bash-${BASHVER}" . && 27 | docker run -it "bash:${BASHVER}" --version && 28 | time docker run -it "bats/bats:bash-${BASHVER}" --tap /opt/bats/test 29 | else 30 | time bin/bats --tap test 31 | fi 32 | 33 | notifications: 34 | email: 35 | on_success: never 36 | -------------------------------------------------------------------------------- /hack/bats/AUTHORS: -------------------------------------------------------------------------------- 1 | Andrew Martin (https://control-plane.io/) 2 | Bianca Tamayo (https://biancatamayo.me/) 3 | Jason Karns (http://jasonkarns.com/) 4 | Mike Bland (https://mike-bland.com/) 5 | -------------------------------------------------------------------------------- /hack/bats/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG bashver=latest 2 | 3 | FROM bash:${bashver} 4 | 5 | # Install parallel and accept the citation notice (we aren't using this in a 6 | # context where it make sense to cite GNU Parallel). 7 | RUN apk add --no-cache parallel && \ 8 | mkdir -p ~/.parallel && touch ~/.parallel/will-cite 9 | 10 | RUN ln -s /opt/bats/bin/bats /usr/sbin/bats 11 | COPY . /opt/bats/ 12 | 13 | ENTRYPOINT ["bash", "/usr/sbin/bats"] 14 | -------------------------------------------------------------------------------- /hack/bats/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 bats-core contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | --- 23 | 24 | * [bats-core] is a continuation of [bats]. Copyright for portions of the 25 | bats-core project are held by Sam Stephenson, 2014 as part of the project 26 | [bats], licensed under MIT: 27 | 28 | Copyright (c) 2014 Sam Stephenson 29 | 30 | Permission is hereby granted, free of charge, to any person obtaining 31 | a copy of this software and associated documentation files (the 32 | "Software"), to deal in the Software without restriction, including 33 | without limitation the rights to use, copy, modify, merge, publish, 34 | distribute, sublicense, and/or sell copies of the Software, and to 35 | permit persons to whom the Software is furnished to do so, subject to 36 | the following conditions: 37 | 38 | The above copyright notice and this permission notice shall be 39 | included in all copies or substantial portions of the Software. 40 | 41 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 42 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 43 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 44 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 45 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 46 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 47 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 48 | 49 | For details, please see the [version control history][commits]. 50 | 51 | [bats-core]: https://github.com/bats-core/bats-core 52 | [bats]:https://github.com/sstephenson/bats 53 | [commits]:https://github.com/bats-core/bats-core/commits/master 54 | -------------------------------------------------------------------------------- /hack/bats/bin/bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | BATS_READLINK='true' 6 | if command -v 'greadlink' >/dev/null; then 7 | BATS_READLINK='greadlink' 8 | elif command -v 'readlink' >/dev/null; then 9 | BATS_READLINK='readlink' 10 | fi 11 | 12 | bats_resolve_absolute_root_dir() { 13 | local cwd="$PWD" 14 | local path="$1" 15 | local result="$2" 16 | local target_dir 17 | local target_name 18 | local original_shell_options="$-" 19 | 20 | # Resolve the parent directory, e.g. /bin => /usr/bin on CentOS (#113). 21 | set -P 22 | 23 | while true; do 24 | target_dir="${path%/*}" 25 | target_name="${path##*/}" 26 | 27 | if [[ "$target_dir" != "$path" ]]; then 28 | cd "$target_dir" 29 | fi 30 | 31 | if [[ -L "$target_name" ]]; then 32 | path="$("$BATS_READLINK" "$target_name")" 33 | else 34 | printf -v "$result" -- '%s' "${PWD%/*}" 35 | set +P "-$original_shell_options" 36 | cd "$cwd" 37 | return 38 | fi 39 | done 40 | } 41 | 42 | export BATS_ROOT 43 | bats_resolve_absolute_root_dir "$0" 'BATS_ROOT' 44 | exec "$BATS_ROOT/libexec/bats-core/bats" "$@" 45 | -------------------------------------------------------------------------------- /hack/bats/contrib/rpm/bats.spec: -------------------------------------------------------------------------------- 1 | %global provider github.com 2 | %global project bats-core 3 | %global repo bats-core 4 | 5 | Name: bats 6 | Version: 1.1.0 7 | Release: 1%{?dist} 8 | Summary: Bash Automated Testing System 9 | 10 | Group: Development/Libraries 11 | License: MIT 12 | URL: https://%{provider}/%{project}/%{repo} 13 | Source0: https://%{provider}/%{project}/%{repo}/archive/v%{version}.tar.gz 14 | 15 | BuildArch: noarch 16 | 17 | Requires: bash 18 | 19 | %description 20 | Bats is a TAP-compliant testing framework for Bash. 21 | It provides a simple way to verify that the UNIX programs you write behave as expected. 22 | Bats is most useful when testing software written in Bash, but you can use it to test any UNIX program. 23 | 24 | %prep 25 | %setup -q -n %{repo}-%{version} 26 | 27 | %install 28 | mkdir -p ${RPM_BUILD_ROOT}%{_prefix} ${RPM_BUILD_ROOT}%{_libexecdir} ${RPM_BUILD_ROOT}%{_mandir} 29 | ./install.sh ${RPM_BUILD_ROOT}%{_prefix} 30 | 31 | %clean 32 | rm -rf $RPM_BUILD_ROOT 33 | 34 | %check 35 | 36 | %files 37 | %doc README.md LICENSE.md 38 | %{_bindir}/%{name} 39 | %{_libexecdir}/%{repo} 40 | %{_mandir}/man1/%{name}.1.gz 41 | %{_mandir}/man7/%{name}.7.gz 42 | 43 | %changelog 44 | * Tue Jul 08 2018 mbland - 1.1.0-1 45 | - Increase version to match upstream release 46 | 47 | * Mon Jun 18 2018 pixdrift - 1.0.2-1 48 | - Increase version to match upstream release 49 | - Relocate libraries to bats-core subdirectory 50 | 51 | * Sat Jun 09 2018 pixdrift - 1.0.1-1 52 | - Increase version to match upstream release 53 | 54 | * Fri Jun 08 2018 pixdrift - 1.0.0-1 55 | - Initial package build of forked (bats-core) github project 56 | -------------------------------------------------------------------------------- /hack/bats/docs/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This enables automatic code review requests per: 2 | # - https://help.github.com/articles/about-codeowners/ 3 | # - https://help.github.com/articles/enabling-required-reviews-for-pull-requests/ 4 | * @bats-core/bats-core 5 | -------------------------------------------------------------------------------- /hack/bats/docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting one of the current [project maintainers](#project-maintainers) listed below. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Project Maintainers 69 | 70 | ### Current Maintainers 71 | 72 | * [Bianca Tamayo][bt-gh] 73 | * [Mike Bland][mb-gh] 74 | * [Jason Karns][jk-gh] 75 | * [Andrew Martin][am-gh] 76 | 77 | ### Past Maintainers 78 | 79 | * Sam Stephenson <> (Original author) 80 | 81 | ## Attribution 82 | 83 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 84 | available at [http://contributor-covenant.org/version/1/4][version] 85 | 86 | [bt-gh]: https://github.com/btamayo 87 | [mb-gh]: https://github.com/mbland 88 | [jk-gh]: https://github.com/jasonkarns 89 | [am-gh]: https://github.com/sublimino 90 | 91 | [homepage]: https://contributor-covenant.org 92 | [version]: https://contributor-covenant.org/version/1/4/ 93 | -------------------------------------------------------------------------------- /hack/bats/docs/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | - [ ] I have reviewed the [Contributor Guidelines][contributor]. 2 | - [ ] I have reviewed the [Code of Conduct][coc] and agree to abide by it 3 | 4 | [contributor]: https://github.com/bats-core/bats-core/blob/master/docs/CONTRIBUTING.md 5 | [coc]: https://github.com/bats-core/bats-core/blob/master/docs/CODE_OF_CONDUCT.md 6 | -------------------------------------------------------------------------------- /hack/bats/docs/usage.md: -------------------------------------------------------------------------------- 1 | # Docker Usage Guide 2 | 3 | - [Docker Usage Guide](#docker-usage-guide) 4 | * [Basic Usage](#basic-usage) 5 | * [Docker Gotchas](#docker-gotchas) 6 | * [Extending from the base image](#extending-from-the-base-image) 7 | 8 | ## Basic Usage 9 | 10 | To build and run `bats`' own tests: 11 | ```bash 12 | $ git clone https://github.com/bats-core/bats-core.git 13 | Cloning into 'bats-core'... 14 | remote: Counting objects: 1222, done. 15 | remote: Compressing objects: 100% (53/53), done. 16 | remote: Total 1222 (delta 34), reused 55 (delta 21), pack-reused 1146 17 | Receiving objects: 100% (1222/1222), 327.28 KiB | 1.70 MiB/s, done. 18 | Resolving deltas: 100% (661/661), done. 19 | 20 | $ cd bats-core/ 21 | $ docker build --tag bats:latest . 22 | ... 23 | $ docker run -it bats:latest --tap /opt/bats/test 24 | ``` 25 | 26 | To mount your tests into the container, first build the image as above. Then, for example with `bats`: 27 | ```bash 28 | $ docker run -it -v "$PWD:/opt/bats" bats:latest /opt/bats/test 29 | ``` 30 | This runs the `test/` directory from the bats-core repository inside the bats Docker container. 31 | 32 | For test suites that are intended to run in isolation from the project (i.e. the tests do not depend on project files outside of the test directory), you can mount the test directory by itself and execute the tests like so: 33 | 34 | ```bash 35 | $ docker run -it -v "$PWD/test:/test" bats:latest /test 36 | ``` 37 | 38 | ## Docker Gotchas 39 | 40 | Relying on functionality provided by your environment (ssh keys or agent, installed binaries, fixtures outside the mounted test directory) will fail when running inside Docker. 41 | 42 | `--interactive`/`-i` attaches an interactive terminal and is useful to kill hanging processes (otherwise has to be done via docker stop command). `--tty`/`-t` simulates a tty (often not used, but most similar to test runs from a Bash prompt). Interactivity is important to a user, but not a build, and TTYs are probably more important to a headless build. Everything's least-surprising to a new Docker use if both are used. 43 | 44 | ## Extending from the base image 45 | 46 | Docker operates on a principle of isolation, and bundles all dependencies required into the Docker image. These can be mounted in at runtime (for test files, configuration, etc). For binary dependencies it may be better to extend the base Docker image with further tools and files. 47 | 48 | ```dockerfile 49 | FROM bats 50 | 51 | RUN \ 52 | apk \ 53 | --no-cache \ 54 | --update \ 55 | add \ 56 | openssh 57 | 58 | ``` 59 | -------------------------------------------------------------------------------- /hack/bats/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | BATS_ROOT="${0%/*}" 6 | PREFIX="$1" 7 | 8 | if [[ -z "$PREFIX" ]]; then 9 | printf '%s\n' \ 10 | "usage: $0 " \ 11 | " e.g. $0 /usr/local" >&2 12 | exit 1 13 | fi 14 | 15 | install -d -m 755 "$PREFIX"/{bin,libexec/bats-core,share/man/man{1,7}} 16 | install -m 755 "$BATS_ROOT/bin"/* "$PREFIX/bin" 17 | install -m 755 "$BATS_ROOT/libexec/bats-core"/* "$PREFIX/libexec/bats-core" 18 | install -m 644 "$BATS_ROOT/man/bats.1" "$PREFIX/share/man/man1" 19 | install -m 644 "$BATS_ROOT/man/bats.7" "$PREFIX/share/man/man7" 20 | 21 | echo "Installed Bats to $PREFIX/bin/bats" 22 | -------------------------------------------------------------------------------- /hack/bats/libexec/bats-core/bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | export BATS_VERSION='1.2.0-dev' 5 | 6 | version() { 7 | printf 'Bats %s\n' "$BATS_VERSION" 8 | } 9 | 10 | abort() { 11 | printf 'Error: %s\n' "$1" >&2 12 | usage >&2 13 | exit 1 14 | } 15 | 16 | usage() { 17 | local cmd="${0##*/}" 18 | local line 19 | 20 | while IFS= read -r line; do 21 | printf '%s\n' "$line" 22 | done <] [-j ] [-p | -t] ... 24 | $cmd [-h | -v] 25 | 26 | is the path to a Bats test file, or the path to a directory 27 | containing Bats test files (ending with ".bats"). 28 | 29 | -c, --count Count the number of test cases without running any tests 30 | -f, --filter Filter test cases by names matching the regular expression 31 | -h, --help Display this help message 32 | -j, --jobs Number of parallel jobs to run (requires GNU parallel) 33 | -p, --pretty Show results in pretty format (default for terminals) 34 | -r, --recursive Include tests in subdirectories 35 | -t, --tap Show results in TAP format 36 | -v, --version Display the version number 37 | 38 | For more information, see https://github.com/bats-core/bats-core 39 | 40 | END_OF_HELP_TEXT 41 | } 42 | 43 | expand_link() { 44 | readlink="$(type -p greadlink readlink | head -1)" 45 | "$readlink" -f "$1" 46 | } 47 | 48 | expand_path() { 49 | local path="${1%/}" 50 | local dirname="${path%/*}" 51 | local result="$2" 52 | 53 | if [[ "$dirname" == "$path" ]]; then 54 | dirname="$PWD" 55 | else 56 | cd "$dirname" 57 | dirname="$PWD" 58 | cd "$OLDPWD" 59 | fi 60 | printf -v "$result" '%s/%s' "$dirname" "${path##*/}" 61 | } 62 | 63 | BATS_LIBEXEC="$(dirname "$(expand_link "$BASH_SOURCE")")" 64 | export BATS_CWD="$PWD" 65 | export BATS_TEST_PATTERN="^[[:blank:]]*@test[[:blank:]]+(.*[^[:blank:]])[[:blank:]]+\{(.*)\$" 66 | export BATS_TEST_FILTER= 67 | export PATH="$BATS_LIBEXEC:$PATH" 68 | 69 | arguments=() 70 | 71 | # Unpack single-character options bundled together, e.g. -cr, -pr. 72 | for arg in "$@"; do 73 | if [[ "$arg" =~ ^-[^-]. ]]; then 74 | index=1 75 | while option="${arg:$((index++)):1}"; do 76 | if [[ -z "$option" ]]; then 77 | break 78 | fi 79 | arguments+=("-$option") 80 | done 81 | else 82 | arguments+=("$arg") 83 | fi 84 | shift 85 | done 86 | 87 | set -- "${arguments[@]}" 88 | arguments=() 89 | 90 | unset flags pretty recursive 91 | flags=() 92 | pretty= 93 | recursive= 94 | if [[ -z "${CI:-}" && -t 0 && -t 1 ]] && command -v tput >/dev/null; then 95 | pretty=1 96 | fi 97 | 98 | while [[ "$#" -ne 0 ]]; do 99 | case "$1" in 100 | -h|--help) 101 | version 102 | usage 103 | exit 0 104 | ;; 105 | -v|--version) 106 | version 107 | exit 0 108 | ;; 109 | -c|--count) 110 | flags+=('-c') 111 | ;; 112 | -f|--filter) 113 | shift 114 | flags+=('-f' "$1") 115 | ;; 116 | -j|--jobs) 117 | shift 118 | flags+=('-j' "$1") 119 | ;; 120 | -r|--recursive) 121 | recursive=1 122 | ;; 123 | -t|--tap) 124 | pretty= 125 | ;; 126 | -p|--pretty) 127 | pretty=1 128 | ;; 129 | -*) 130 | abort "Bad command line option '$1'" 131 | ;; 132 | *) 133 | arguments+=("$1") 134 | ;; 135 | esac 136 | shift 137 | done 138 | 139 | if [[ "${#arguments[@]}" -eq 0 ]]; then 140 | abort 'Must specify at least one ' 141 | fi 142 | 143 | filenames=() 144 | for filename in "${arguments[@]}"; do 145 | expand_path "$filename" 'filename' 146 | 147 | if [[ -d "$filename" ]]; then 148 | shopt -s nullglob 149 | if [[ "$recursive" -eq 1 ]]; then 150 | while IFS= read -r -d $'\0' file; do 151 | filenames+=("$file") 152 | done < <(find "$filename" -type f -name '*.bats' -print0 | sort -z) 153 | else 154 | for suite_filename in "$filename"/*.bats; do 155 | filenames+=("$suite_filename") 156 | done 157 | fi 158 | shopt -u nullglob 159 | else 160 | filenames+=("$filename") 161 | fi 162 | done 163 | 164 | formatter="cat" 165 | if [[ -n "$pretty" ]]; then 166 | flags+=("-x") 167 | formatter="bats-format-tap-stream" 168 | fi 169 | 170 | set -o pipefail execfail 171 | exec bats-exec-suite "${flags[@]}" "${filenames[@]}" | "$formatter" 172 | -------------------------------------------------------------------------------- /hack/bats/libexec/bats-core/bats-exec-suite: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | count_only_flag='' 5 | extended_syntax_flag='' 6 | filter='' 7 | num_jobs=1 8 | have_gnu_parallel= 9 | flags=() 10 | 11 | while [[ "$#" -ne 0 ]]; do 12 | case "$1" in 13 | -c) 14 | count_only_flag=1 15 | ;; 16 | -f) 17 | shift 18 | filter="$1" 19 | flags+=('-f' "$filter") 20 | ;; 21 | -j) 22 | shift 23 | num_jobs="$1" 24 | ;; 25 | -x) 26 | extended_syntax_flag='-x' 27 | flags+=('-x') 28 | ;; 29 | *) 30 | break 31 | ;; 32 | esac 33 | shift 34 | done 35 | 36 | if ( type -p parallel &>/dev/null ); then 37 | have_gnu_parallel=1 38 | elif [[ "$num_jobs" != 1 ]]; then 39 | printf 'bats: cannot execute "%s" jobs without GNU parallel\n' "$num_jobs" >&2 40 | exit 1 41 | fi 42 | 43 | trap 'kill 0; exit 1' INT 44 | 45 | all_tests=() 46 | for filename in "$@"; do 47 | if [[ ! -f "$filename" ]]; then 48 | printf 'bats: %s does not exist\n' "$filename" >&2 49 | exit 1 50 | fi 51 | 52 | test_names=() 53 | test_dupes=() 54 | while read -r line; do 55 | if [[ ! "$line" =~ ^bats_test_function\ ]]; then 56 | continue 57 | fi 58 | line="${line%$'\r'}" 59 | line="${line#* }" 60 | 61 | all_tests+=( "$(printf "%s\t%s" "$filename" "$line")" ) 62 | if [[ " ${test_names[*]} " == *" $line "* ]]; then 63 | test_dupes+=("$line") 64 | continue 65 | fi 66 | test_names+=("$line") 67 | done < <(BATS_TEST_FILTER="$filter" bats-preprocess "$filename") 68 | 69 | if [[ "${#test_dupes[@]}" -ne 0 ]]; then 70 | printf 'bats warning: duplicate test name(s) in %s: %s\n' "$filename" "${test_dupes[*]}" >&2 71 | fi 72 | done 73 | 74 | test_count="${#all_tests[@]}" 75 | 76 | if [[ -n "$count_only_flag" ]]; then 77 | printf '%d\n' "${test_count}" 78 | exit 79 | fi 80 | 81 | status=0 82 | printf '1..%d\n' "${test_count}" 83 | 84 | # No point on continuing if there's no tests. 85 | if [[ "${test_count}" == 0 ]]; then 86 | exit 87 | fi 88 | 89 | if [[ "$num_jobs" != 1 ]]; then 90 | # Only use GNU parallel when we want parallel execution -- there is a small 91 | # amount of overhead using it over a simple loop in the serial case. 92 | set -o pipefail 93 | printf '%s\n' "${all_tests[@]}" | grep -v '^$' | \ 94 | parallel -qk -j "$num_jobs" --colsep="\t" -- bats-exec-test "${flags[@]}" '{1}' '{2}' '{#}' || status=1 95 | else 96 | # Just do it serially. 97 | test_number=0 98 | for test_line in "${all_tests[@]}"; do 99 | # Only handle non-empty lines 100 | if [[ $test_line ]]; then 101 | filename="${test_line%%$'\t'*}" 102 | test_name="${test_line##*$'\t'}" 103 | ((++test_number)) 104 | bats-exec-test "${flags[@]}" "$filename" "$test_name" "$test_number" || status=1 105 | fi 106 | done 107 | if [[ "${test_number}" != "${test_count}" ]]; then 108 | printf '# bats warning: Only executed %s of %s tests\n' "$test_number" "$test_count" 109 | status=1 110 | fi 111 | fi 112 | exit "$status" 113 | -------------------------------------------------------------------------------- /hack/bats/libexec/bats-core/bats-format-tap-stream: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | header_pattern='[0-9]+\.\.[0-9]+' 5 | IFS= read -r header 6 | 7 | if [[ "$header" =~ $header_pattern ]]; then 8 | count="${header:3}" 9 | index=0 10 | passed=0 11 | failures=0 12 | skipped=0 13 | name= 14 | count_column_width=$(( ${#count} * 2 + 2 )) 15 | else 16 | # If the first line isn't a TAP plan, print it and pass the rest through 17 | printf '%s\n' "$header" 18 | exec cat 19 | fi 20 | 21 | update_screen_width() { 22 | screen_width="$(tput cols)" 23 | count_column_left=$(( $screen_width - $count_column_width )) 24 | } 25 | 26 | trap update_screen_width WINCH 27 | update_screen_width 28 | 29 | begin() { 30 | go_to_column 0 31 | buffer_with_truncation $(( $count_column_left - 1 )) ' %s' "$name" 32 | clear_to_end_of_line 33 | go_to_column $count_column_left 34 | buffer "%${#count}s/${count}" "$index" 35 | go_to_column 1 36 | } 37 | 38 | pass() { 39 | go_to_column 0 40 | buffer ' ✓ %s' "$name" 41 | advance 42 | } 43 | 44 | skip() { 45 | local reason="$1" 46 | if [[ -n "$reason" ]]; then 47 | reason=": $reason" 48 | fi 49 | go_to_column 0 50 | buffer ' - %s (skipped%s)' "$name" "$reason" 51 | advance 52 | } 53 | 54 | fail() { 55 | go_to_column 0 56 | set_color 1 bold 57 | buffer ' ✗ %s' "$name" 58 | advance 59 | } 60 | 61 | log() { 62 | set_color 1 63 | buffer ' %s\n' "$1" 64 | clear_color 65 | } 66 | 67 | summary() { 68 | buffer '\n%d test' "$count" 69 | if [[ "$count" -ne 1 ]]; then 70 | buffer 's' 71 | fi 72 | 73 | buffer ', %d failure' "$failures" 74 | if [[ "$failures" -ne 1 ]]; then 75 | buffer 's' 76 | fi 77 | 78 | if [[ "$skipped" -gt 0 ]]; then 79 | buffer ', %d skipped' "$skipped" 80 | fi 81 | 82 | not_run=$((count - passed - failures - skipped)) 83 | if [[ "$not_run" -gt 0 ]]; then 84 | buffer ', %d not run' "$not_run" 85 | fi 86 | 87 | buffer '\n' 88 | } 89 | 90 | buffer_with_truncation() { 91 | local width="$1" 92 | shift 93 | local string 94 | 95 | printf -v 'string' -- "$@" 96 | 97 | if [[ "${#string}" -gt "$width" ]]; then 98 | buffer '%s...' "${string:0:$(( $width - 4 ))}" 99 | else 100 | buffer '%s' "$string" 101 | fi 102 | } 103 | 104 | go_to_column() { 105 | local column="$1" 106 | buffer '\x1B[%dG' $(( $column + 1 )) 107 | } 108 | 109 | clear_to_end_of_line() { 110 | buffer '\x1B[K' 111 | } 112 | 113 | advance() { 114 | clear_to_end_of_line 115 | buffer '\n' 116 | clear_color 117 | } 118 | 119 | set_color() { 120 | local color="$1" 121 | local weight=22 122 | 123 | if [[ "$2" == 'bold' ]]; then 124 | weight=1 125 | fi 126 | buffer '\x1B[%d;%dm' "$(( 30 + $color ))" "$weight" 127 | } 128 | 129 | clear_color() { 130 | buffer '\x1B[0m' 131 | } 132 | 133 | _buffer= 134 | 135 | buffer() { 136 | local content 137 | printf -v content -- "$@" 138 | _buffer+="$content" 139 | } 140 | 141 | flush() { 142 | printf '%s' "$_buffer" 143 | _buffer= 144 | } 145 | 146 | finish() { 147 | flush 148 | printf '\n' 149 | } 150 | 151 | trap finish EXIT 152 | 153 | while IFS= read -r line; do 154 | case "$line" in 155 | 'begin '* ) 156 | ((++index)) 157 | name="${line#* $index }" 158 | begin 159 | flush 160 | ;; 161 | 'ok '* ) 162 | skip_expr="ok $index (.*) # skip ?(([[:print:]]*))?" 163 | if [[ "$line" =~ $skip_expr ]]; then 164 | ((++skipped)) 165 | skip "${BASH_REMATCH[2]}" 166 | else 167 | ((++passed)) 168 | pass 169 | fi 170 | ;; 171 | 'not ok '* ) 172 | ((++failures)) 173 | fail 174 | ;; 175 | '# '* ) 176 | log "${line:2}" 177 | ;; 178 | esac 179 | done 180 | 181 | summary 182 | -------------------------------------------------------------------------------- /hack/bats/libexec/bats-core/bats-preprocess: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | bats_encode_test_name() { 5 | local name="$1" 6 | local result='test_' 7 | local hex_code 8 | 9 | if [[ ! "$name" =~ [^[:alnum:]\ _-] ]]; then 10 | name="${name//_/-5f}" 11 | name="${name//-/-2d}" 12 | name="${name// /_}" 13 | result+="$name" 14 | else 15 | local length="${#name}" 16 | local char i 17 | 18 | for ((i=0; i] [-p | -t] ...
9 | bats [-h | -v] 10 | 11 | is the path to a Bats test file, or the path to a directory containing 12 | Bats test files (ending with ".bats"). 13 | 14 | 15 | DESCRIPTION 16 | ----------- 17 | 18 | Bats is a TAP-compliant testing framework for Bash. It provides a simple 19 | way to verify that the UNIX programs you write behave as expected. 20 | 21 | A Bats test file is a Bash script with special syntax for defining 22 | test cases. Under the hood, each test case is just a function with a 23 | description. 24 | 25 | Test cases consist of standard shell commands. Bats makes use of 26 | Bash's `errexit` (`set -e`) option when running test cases. If every 27 | command in the test case exits with a `0` status code (success), the 28 | test passes. In this way, each line is an assertion of truth. 29 | 30 | See `bats`(7) for more information on writing Bats tests. 31 | 32 | 33 | RUNNING TESTS 34 | ------------- 35 | 36 | To run your tests, invoke the `bats` interpreter with a path to a test 37 | file. The file's test cases are run sequentially and in isolation. If 38 | all the test cases pass, `bats` exits with a `0` status code. If there 39 | are any failures, `bats` exits with a `1` status code. 40 | 41 | You can invoke the `bats` interpreter with multiple test file arguments, 42 | or with a path to a directory containing multiple `.bats` files. Bats 43 | will run each test file individually and aggregate the results. If any 44 | test case fails, `bats` exits with a `1` status code. 45 | 46 | 47 | OPTIONS 48 | ------- 49 | 50 | * `-c`, `--count`: 51 | Count the number of test cases without running any tests 52 | * `-f`, `--filter`: 53 | Filter test cases by names matching the regular expression 54 | * `-h`, `--help`: 55 | Display help message 56 | * `-p`, `--pretty`: 57 | Show results in pretty format (default for terminals) 58 | * `-r`, `--recursive`: 59 | Include tests in subdirectories 60 | * `-t`, `--tap`: 61 | Show results in TAP format 62 | * `-v`, `--version`: 63 | Display the version number 64 | 65 | 66 | OUTPUT 67 | ------ 68 | 69 | When you run Bats from a terminal, you'll see output as each test is 70 | performed, with a check-mark next to the test's name if it passes or 71 | an "X" if it fails. 72 | 73 | $ bats addition.bats 74 | ✓ addition using bc 75 | ✓ addition using dc 76 | 77 | 2 tests, 0 failures 78 | 79 | If Bats is not connected to a terminal--in other words, if you run it 80 | from a continuous integration system or redirect its output to a 81 | file--the results are displayed in human-readable, machine-parsable 82 | TAP format. You can force TAP output from a terminal by invoking Bats 83 | with the `--tap` option. 84 | 85 | $ bats --tap addition.bats 86 | 1..2 87 | ok 1 addition using bc 88 | ok 2 addition using dc 89 | 90 | 91 | EXIT STATUS 92 | ----------- 93 | 94 | The `bats` interpreter exits with a value of `0` if all test cases pass, 95 | or `1` if one or more test cases fail. 96 | 97 | 98 | SEE ALSO 99 | -------- 100 | 101 | Bats wiki: _https://github.com/bats\-core/bats\-core/wiki/_ 102 | 103 | `bash`(1), `bats`(7) 104 | 105 | 106 | COPYRIGHT 107 | --------- 108 | 109 | (c) 2017-2018 bats-core organization
110 | (c) 2011-2016 Sam Stephenson 111 | 112 | Bats is released under the terms of an MIT-style license. 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /hack/bats/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bats", 3 | "version": "1.2.0-dev", 4 | "description": "Bash Automated Testing System", 5 | "homepage": "https://github.com/bats-core/bats-core#readme", 6 | "license": "MIT", 7 | "author": "Sam Stephenson (http://sstephenson.us/)", 8 | "repository": "github:bats-core/bats-core", 9 | "bugs": "https://github.com/bats-core/bats-core/issues", 10 | "files": [ 11 | "bin", 12 | "libexec", 13 | "man" 14 | ], 15 | "directories": { 16 | "bin": "bin", 17 | "doc": "docs", 18 | "man": "man", 19 | "test": "test" 20 | }, 21 | "scripts": { 22 | "test": "bin/bats test" 23 | }, 24 | "keywords": [ 25 | "bats", 26 | "bash", 27 | "shell", 28 | "test", 29 | "unit" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/cmd_using_stdin.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Fractional timeout supported in bash 4+ 4 | if [ "${BASH_VERSINFO[0]}" -lt 4 ]; then 5 | timeout=1 6 | else 7 | timeout=0.01 8 | fi 9 | 10 | # Just reading from stdin 11 | while read -r -t $timeout foo; do 12 | if [ "$foo" == "EXIT" ]; then 13 | echo "Found" 14 | exit 0 15 | fi 16 | done 17 | 18 | echo "Not found" 19 | exit 1 20 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/dos_line.bats: -------------------------------------------------------------------------------- 1 | @test "foo" { 2 | echo "foo" 3 | } 4 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/duplicate-tests.bats: -------------------------------------------------------------------------------- 1 | # This does not fail as expected 2 | @test "gizmo test" { 3 | false 4 | } 5 | 6 | @test "gizmo test" "this does fail, as expected" { 7 | false 8 | } 9 | 10 | # This overrides any previous test from the suite with the same description 11 | @test "gizmo test" { 12 | true 13 | } 14 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/empty.bats: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpoke/stack-runtimes/9b76589c28c02a16ce66ef332fe362887465400f/hack/bats/test/fixtures/bats/empty.bats -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/environment.bats: -------------------------------------------------------------------------------- 1 | @test "setting a variable" { 2 | variable=1 3 | [ $variable -eq 1 ] 4 | } 5 | 6 | @test "variables do not persist across tests" { 7 | [ -z "$variable" ] 8 | } 9 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/expand_var_in_test_name.bats: -------------------------------------------------------------------------------- 1 | @test "$SUITE: test with variable in name" { 2 | true 3 | } 4 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/exported_function.bats: -------------------------------------------------------------------------------- 1 | if exported_function; then 2 | a='exported_function' 3 | fi 4 | 5 | @test "failing test" { 6 | echo "a='$a'" 7 | false 8 | } 9 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/external_function_calls.bats: -------------------------------------------------------------------------------- 1 | load test_helper 2 | 3 | # Test various combinations that may fail line number detection in stack trace 4 | # Tests are designed so the first statement succeeds and 2nd fails 5 | # All tests fail on the same line so checking can be automated 6 | 7 | @test "Call true function && false" { 8 | help_me 9 | help_me && false 10 | } 11 | 12 | @test "Call true function && return 1" { 13 | help_me 14 | help_me && return 1 15 | } 16 | 17 | @test "Call true function and invert" { 18 | help_me 19 | ! help_me 20 | } 21 | 22 | @test "Call false function || false" { 23 | ! failing_helper 24 | failing_helper || false 25 | } 26 | 27 | @test "Call false function && return 1" { 28 | ! failing_helper 29 | failing_helper || return 1 30 | } 31 | 32 | @test "Call false function" { 33 | ! failing_helper 34 | failing_helper 35 | } 36 | 37 | @test "Call return_0 function && false" { 38 | return_0 39 | return_0 && false 40 | } 41 | 42 | @test "Call return_0 function && return 1" { 43 | return_0 44 | return_0 && return 1 45 | } 46 | 47 | @test "Call return_0 function and invert" { 48 | return_0 49 | ! return_0 50 | } 51 | 52 | @test "Call return_1 function || false" { 53 | ! return_1 54 | return_1 || false 55 | } 56 | 57 | @test "Call return_1 function && return 1" { 58 | ! return_1 59 | return_1 || return 1 60 | } 61 | 62 | @test "Call return_1 function" { 63 | ! return_1 64 | return_1 65 | } 66 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/failing.bats: -------------------------------------------------------------------------------- 1 | @test "a failing test" { 2 | true 3 | true 4 | eval "( exit ${STATUS:-1} )" 5 | } 6 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/failing_and_passing.bats: -------------------------------------------------------------------------------- 1 | @test "a failing test" { 2 | false 3 | } 4 | 5 | @test "a passing test" { 6 | true 7 | } 8 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/failing_helper.bats: -------------------------------------------------------------------------------- 1 | load "test_helper" 2 | 3 | @test "failing helper function" { 4 | true 5 | failing_helper 6 | } 7 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/failing_setup.bats: -------------------------------------------------------------------------------- 1 | setup() { 2 | false 3 | } 4 | 5 | @test "truth" { 6 | true 7 | } 8 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/failing_teardown.bats: -------------------------------------------------------------------------------- 1 | teardown() { 2 | eval "( exit ${STATUS:-1} )" 3 | } 4 | 5 | @test "truth" { 6 | [ "$PASS" = 1 ] 7 | } 8 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/intact.bats: -------------------------------------------------------------------------------- 1 | @test "dash-e on beginning of line" { 2 | run cat - <&2 && return 1 3 | } 4 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/output.bats: -------------------------------------------------------------------------------- 1 | @test "success writing to stdout" { 2 | echo "success stdout 1" 3 | echo "success stdout 2" 4 | } 5 | 6 | @test "success writing to stderr" { 7 | echo "success stderr" >&2 8 | } 9 | 10 | @test "failure writing to stdout" { 11 | echo "failure stdout 1" 12 | echo "failure stdout 2" 13 | false 14 | } 15 | 16 | @test "failure writing to stderr" { 17 | echo "failure stderr" >&2 18 | false 19 | } 20 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/parallel.bats: -------------------------------------------------------------------------------- 1 | @test "slow test 1" { 2 | sleep 3s 3 | } 4 | 5 | @test "slow test 2" { 6 | sleep 3s 7 | } 8 | 9 | @test "slow test 3" { 10 | sleep 3s 11 | } 12 | 13 | @test "slow test 4" { 14 | sleep 3s 15 | } 16 | 17 | @test "slow test 5" { 18 | sleep 3s 19 | } 20 | 21 | @test "slow test 6" { 22 | sleep 3s 23 | } 24 | 25 | @test "slow test 7" { 26 | sleep 3s 27 | } 28 | 29 | @test "slow test 8" { 30 | sleep 3s 31 | } 32 | 33 | @test "slow test 9" { 34 | sleep 3s 35 | } 36 | 37 | @test "slow test 10" { 38 | sleep 3s 39 | } 40 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/passing.bats: -------------------------------------------------------------------------------- 1 | @test "a passing test" { 2 | true 3 | } 4 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/passing_and_failing.bats: -------------------------------------------------------------------------------- 1 | @test "a passing test" { 2 | true 3 | } 4 | 5 | @test "a failing test" { 6 | false 7 | } 8 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/passing_and_skipping.bats: -------------------------------------------------------------------------------- 1 | @test "a passing test" { 2 | true 3 | } 4 | 5 | @test "a skipped test with no reason" { 6 | skip 7 | } 8 | 9 | @test "a skipped test with a reason" { 10 | skip "for a really good reason" 11 | } 12 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/passing_failing_and_skipping.bats: -------------------------------------------------------------------------------- 1 | @test "a passing test" { 2 | true 3 | } 4 | 5 | @test "a skipping test" { 6 | skip 7 | } 8 | 9 | @test "a failing test" { 10 | false 11 | } 12 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/quoted_and_unquoted_test_names.bats: -------------------------------------------------------------------------------- 1 | @test 'single-quoted name' { 2 | true 3 | } 4 | 5 | @test "double-quoted name" { 6 | true 7 | } 8 | 9 | @test unquoted name { 10 | true 11 | } 12 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/read_from_stdin.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | @test "test 1" { 4 | # Don't print anything 5 | run bash -c "$BATS_TEST_DIRNAME/cmd_using_stdin.bash" 6 | [ "$status" -eq 1 ] 7 | [ "$output" = "Not found" ] 8 | } 9 | 10 | @test "test 2 with TAB in name" { 11 | run bash -c "echo EXIT | $BATS_TEST_DIRNAME/cmd_using_stdin.bash" 12 | [ "$status" -eq 0 ] 13 | [ "$output" = "Found" ] 14 | } 15 | 16 | @test "test 3" { 17 | run bash -c "echo EXIT | $BATS_TEST_DIRNAME/cmd_using_stdin.bash" 18 | [ "$status" -eq 0 ] 19 | [ "$output" = "Found" ] 20 | } 21 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/reference_unset_parameter.bats: -------------------------------------------------------------------------------- 1 | @test "referencing unset parameter fails" { 2 | set -u 3 | echo "$unset_parameter" 4 | } 5 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/reference_unset_parameter_in_setup.bats: -------------------------------------------------------------------------------- 1 | setup() { 2 | set -u 3 | echo "$unset_parameter" 4 | } 5 | 6 | teardown() { 7 | echo "should not capture the next line" 8 | [ 1 -eq 2 ] 9 | } 10 | 11 | @test "referencing unset parameter fails in setup" { 12 | : 13 | } 14 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/reference_unset_parameter_in_teardown.bats: -------------------------------------------------------------------------------- 1 | teardown() { 2 | set -u 3 | echo "$unset_parameter" 4 | } 5 | 6 | @test "referencing unset parameter fails in teardown" { 7 | : 8 | } 9 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/setup.bats: -------------------------------------------------------------------------------- 1 | LOG="$BATS_TEST_SUITE_TMPDIR/setup.log" 2 | 3 | setup() { 4 | echo "$BATS_TEST_NAME" >> "$LOG" 5 | } 6 | 7 | @test "one" { 8 | [ "$(tail -n 1 "$LOG")" = "test_one" ] 9 | } 10 | 11 | @test "two" { 12 | [ "$(tail -n 1 "$LOG")" = "test_two" ] 13 | } 14 | 15 | @test "three" { 16 | [ "$(tail -n 1 "$LOG")" = "test_three" ] 17 | } 18 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/single_line.bats: -------------------------------------------------------------------------------- 1 | @test "empty" { } 2 | 3 | @test "passing" { true; } 4 | 5 | @test "input redirection" { diff - <( echo hello ); } <> "$LOG" 5 | } 6 | 7 | @test "one" { 8 | true 9 | } 10 | 11 | @test "two" { 12 | false 13 | } 14 | 15 | @test "three" { 16 | true 17 | } 18 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/test_helper.bash: -------------------------------------------------------------------------------- 1 | help_me() { 2 | true 3 | } 4 | 5 | failing_helper() { 6 | false 7 | } 8 | 9 | return_0() { 10 | # Just return 0. Intentional assignment to boost line numbers 11 | result=0 12 | return $result 13 | } 14 | 15 | return_1() { 16 | # Just return 0. Intentional assignment to boost line numbers 17 | result=1 18 | return $result 19 | } 20 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/unbound_variable.bats: -------------------------------------------------------------------------------- 1 | set -u 2 | 3 | # This file is used to test line number offsets. Any changes to lines will affect tests 4 | 5 | @test "access unbound variable" { 6 | unset unset_variable 7 | # Add a line for checking line number 8 | foo=$unset_variable 9 | } 10 | 11 | @test "access second unbound variable" { 12 | unset second_unset_variable 13 | foo=$second_unset_variable 14 | } 15 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/unofficial_bash_strict_mode.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/unofficial_bash_strict_mode.bats: -------------------------------------------------------------------------------- 1 | load unofficial_bash_strict_mode 2 | @test "unofficial Bash strict mode conditions met" { 3 | : 4 | } 5 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/whitespace.bats: -------------------------------------------------------------------------------- 1 | @test "no extra whitespace" { 2 | : 3 | } 4 | 5 | @test "tab at beginning of line" { 6 | : 7 | } 8 | 9 | @test "tab before description" { 10 | : 11 | } 12 | 13 | @test "tab before opening brace" { 14 | : 15 | } 16 | 17 | @test "tabs at beginning of line and before description" { 18 | : 19 | } 20 | 21 | @test "tabs at beginning, before description, before brace" { 22 | : 23 | } 24 | 25 | @test "extra whitespace around single-line test" { :; } 26 | 27 | @test "no extra whitespace around single-line test" {:;} 28 | 29 | @test parse unquoted name between extra whitespace {:;} 30 | 31 | @test { {:;} # unquote single brace is a valid description 32 | 33 | @test ' {:;} # empty name from single quote 34 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/bats/without_trailing_newline.bats: -------------------------------------------------------------------------------- 1 | @test "truth" { 2 | true 3 | } -------------------------------------------------------------------------------- /hack/bats/test/fixtures/suite/empty/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpoke/stack-runtimes/9b76589c28c02a16ce66ef332fe362887465400f/hack/bats/test/fixtures/suite/empty/.gitkeep -------------------------------------------------------------------------------- /hack/bats/test/fixtures/suite/filter/a.bats: -------------------------------------------------------------------------------- 1 | @test 'foo in a' { } 2 | @test '--bar in a' { } 3 | @test 'baz in a' { } 4 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/suite/filter/b.bats: -------------------------------------------------------------------------------- 1 | @test 'bar_in_b' { } 2 | @test '--baz_in_b' { } 3 | @test 'quux_in_b' { } 4 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/suite/filter/c.bats: -------------------------------------------------------------------------------- 1 | @test 'quux_in c' { } 2 | @test 'xyzzy in c' { } 3 | @test 'plugh_in c' { } 4 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/suite/multiple/a.bats: -------------------------------------------------------------------------------- 1 | @test "truth" { 2 | true 3 | } 4 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/suite/multiple/b.bats: -------------------------------------------------------------------------------- 1 | @test "more truth" { 2 | true 3 | } 4 | 5 | @test "quasi-truth" { 6 | [ -z "$FLUNK" ] 7 | } 8 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/suite/parallel/parallel1.bats: -------------------------------------------------------------------------------- 1 | ../../bats/parallel.bats -------------------------------------------------------------------------------- /hack/bats/test/fixtures/suite/parallel/parallel2.bats: -------------------------------------------------------------------------------- 1 | ../../bats/parallel.bats -------------------------------------------------------------------------------- /hack/bats/test/fixtures/suite/parallel/parallel3.bats: -------------------------------------------------------------------------------- 1 | ../../bats/parallel.bats -------------------------------------------------------------------------------- /hack/bats/test/fixtures/suite/parallel/parallel4.bats: -------------------------------------------------------------------------------- 1 | ../../bats/parallel.bats -------------------------------------------------------------------------------- /hack/bats/test/fixtures/suite/recursive/subsuite/test2.bats: -------------------------------------------------------------------------------- 1 | @test "another passing test" { 2 | true 3 | } 4 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/suite/recursive/test.bats: -------------------------------------------------------------------------------- 1 | @test "a passing test" { 2 | true 3 | } 4 | -------------------------------------------------------------------------------- /hack/bats/test/fixtures/suite/single/test.bats: -------------------------------------------------------------------------------- 1 | @test "a passing test" { 2 | true 3 | } 4 | -------------------------------------------------------------------------------- /hack/bats/test/install.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | INSTALL_DIR= 6 | BATS_ROOT= 7 | 8 | setup() { 9 | make_bats_test_suite_tmpdir 10 | INSTALL_DIR="$BATS_TEST_SUITE_TMPDIR/bats-core" 11 | BATS_ROOT="${BATS_TEST_DIRNAME%/*}" 12 | } 13 | 14 | @test "install.sh creates a valid installation" { 15 | run "$BATS_ROOT/install.sh" "$INSTALL_DIR" 16 | [ "$status" -eq 0 ] 17 | [ "$output" == "Installed Bats to $INSTALL_DIR/bin/bats" ] 18 | [ -x "$INSTALL_DIR/bin/bats" ] 19 | [ -x "$INSTALL_DIR/libexec/bats-core/bats" ] 20 | [ -x "$INSTALL_DIR/libexec/bats-core/bats-exec-suite" ] 21 | [ -x "$INSTALL_DIR/libexec/bats-core/bats-exec-test" ] 22 | [ -x "$INSTALL_DIR/libexec/bats-core/bats-format-tap-stream" ] 23 | [ -x "$INSTALL_DIR/libexec/bats-core/bats-preprocess" ] 24 | [ -f "$INSTALL_DIR/share/man/man1/bats.1" ] 25 | [ -f "$INSTALL_DIR/share/man/man7/bats.7" ] 26 | 27 | run "$INSTALL_DIR/bin/bats" -v 28 | [ "$status" -eq 0 ] 29 | [ "${output%% *}" == 'Bats' ] 30 | } 31 | 32 | @test "install.sh only updates permissions for Bats files" { 33 | mkdir -p "$INSTALL_DIR"/{bin,libexec/bats-core} 34 | 35 | local dummy_bin="$INSTALL_DIR/bin/dummy" 36 | printf 'dummy' >"$dummy_bin" 37 | 38 | local dummy_libexec="$INSTALL_DIR/libexec/bats-core/dummy" 39 | printf 'dummy' >"$dummy_libexec" 40 | 41 | run "$BATS_ROOT/install.sh" "$INSTALL_DIR" 42 | [ "$status" -eq 0 ] 43 | [ -f "$dummy_bin" ] 44 | [ ! -x "$dummy_bin" ] 45 | [ -f "$dummy_libexec" ] 46 | [ ! -x "$dummy_libexec" ] 47 | } 48 | 49 | @test "bin/bats is resilient to symbolic links" { 50 | run "$BATS_ROOT/install.sh" "$INSTALL_DIR" 51 | [ "$status" -eq 0 ] 52 | 53 | # Simulate a symlink to bin/bats (without using a symlink, for Windows sake) 54 | # by creating a wrapper script that executes bin/bats via a relative path. 55 | # 56 | # root.bats contains tests that use real symlinks on platforms that support 57 | # them, as does the .travis.yml script that exercises the Dockerfile. 58 | local bats_symlink="$INSTALL_DIR/bin/bats-link" 59 | printf '%s\n' '#! /usr/bin/env bash' \ 60 | "cd '$INSTALL_DIR/bin'" \ 61 | './bats "$@"' >"$bats_symlink" 62 | chmod 700 "$bats_symlink" 63 | 64 | run "$bats_symlink" -v 65 | [ "$status" -eq 0 ] 66 | [ "${output%% *}" == 'Bats' ] 67 | } 68 | -------------------------------------------------------------------------------- /hack/bats/test/root.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | # 3 | # This suite is dedicated to calculating BATS_ROOT when going through various 4 | # permutations of symlinks. It was inspired by the report in issue #113 that the 5 | # calculation was broken on CentOS, where /bin is symlinked to /usr/bin. 6 | # 7 | # The basic test environment is (all paths relative to BATS_TEST_SUITE_TMPDIR): 8 | # 9 | # - /bin is a relative symlink to /usr/bin, exercising the symlink resolution of 10 | # the `bats` parent directory (i.e. "${0%/*}") 11 | # - /usr/bin/bats is an absolute symlink to /opt/bats-core/bin/bats, exercising 12 | # the symlink resolution of the `bats` executable itself (i.e. "${0##*/}") 13 | 14 | load test_helper 15 | 16 | # This would make a good candidate for a one-time setup/teardown per #39. 17 | setup() { 18 | make_bats_test_suite_tmpdir 19 | cd "$BATS_TEST_SUITE_TMPDIR" 20 | mkdir -p {usr/bin,opt/bats-core} 21 | "$BATS_ROOT/install.sh" "opt/bats-core" 22 | 23 | ln -s "usr/bin" "bin" 24 | 25 | if [[ ! -L "bin" ]]; then 26 | cd - >/dev/null 27 | skip "symbolic links aren't functional on OSTYPE=$OSTYPE" 28 | fi 29 | 30 | ln -s "$BATS_TEST_SUITE_TMPDIR/opt/bats-core/bin/bats" \ 31 | "$BATS_TEST_SUITE_TMPDIR/usr/bin/bats" 32 | cd - >/dev/null 33 | } 34 | 35 | @test "#113: set BATS_ROOT when /bin is a symlink to /usr/bin" { 36 | run "$BATS_TEST_SUITE_TMPDIR/bin/bats" -v 37 | [ "$status" -eq 0 ] 38 | [ "${output%% *}" == 'Bats' ] 39 | } 40 | 41 | # The resolution scheme here is: 42 | # 43 | # - /bin => /usr/bin (relative directory) 44 | # - /usr/bin/foo => /usr/bin/bar (relative executable) 45 | # - /usr/bin/bar => /opt/bats/bin0/bar (absolute executable) 46 | # - /opt/bats/bin0 => /opt/bats/bin1 (relative directory) 47 | # - /opt/bats/bin1 => /opt/bats/bin2 (absolute directory) 48 | # - /opt/bats/bin2/bar => /opt/bats-core/bin/bar (absolute executable) 49 | # - /opt/bats-core/bin/bar => /opt/bats-core/bin/baz (relative executable) 50 | # - /opt/bats-core/bin/baz => /opt/bats-core/bin/bats (relative executable) 51 | @test "set BATS_ROOT with extreme symlink resolution" { 52 | cd "$BATS_TEST_SUITE_TMPDIR" 53 | mkdir -p "opt/bats/bin2" 54 | 55 | ln -s bar usr/bin/foo 56 | ln -s "$BATS_TEST_SUITE_TMPDIR/opt/bats/bin0/bar" usr/bin/bar 57 | ln -s bin1 opt/bats/bin0 58 | ln -s "$BATS_TEST_SUITE_TMPDIR/opt/bats/bin2" opt/bats/bin1 59 | ln -s "$BATS_TEST_SUITE_TMPDIR/opt/bats-core/bin/bar" opt/bats/bin2/bar 60 | ln -s baz opt/bats-core/bin/bar 61 | ln -s bats opt/bats-core/bin/baz 62 | 63 | cd - >/dev/null 64 | run "$BATS_TEST_SUITE_TMPDIR/bin/foo" -v 65 | [ "$status" -eq 0 ] 66 | [ "${output%% *}" == 'Bats' ] 67 | } 68 | -------------------------------------------------------------------------------- /hack/bats/test/test_helper.bash: -------------------------------------------------------------------------------- 1 | emulate_bats_env() { 2 | export BATS_CWD="$PWD" 3 | export BATS_TEST_PATTERN="^[[:blank:]]*@test[[:blank:]]+(.*[^[:blank:]])[[:blank:]]+\{(.*)\$" 4 | export BATS_TEST_FILTER= 5 | } 6 | 7 | fixtures() { 8 | FIXTURE_ROOT="$BATS_TEST_DIRNAME/fixtures/$1" 9 | RELATIVE_FIXTURE_ROOT="${FIXTURE_ROOT#$BATS_CWD/}" 10 | } 11 | 12 | make_bats_test_suite_tmpdir() { 13 | export BATS_TEST_SUITE_TMPDIR="$BATS_TMPDIR/bats-test-tmp" 14 | mkdir -p "$BATS_TEST_SUITE_TMPDIR" 15 | } 16 | 17 | filter_control_sequences() { 18 | "$@" | sed $'s,\x1b\\[[0-9;]*[a-zA-Z],,g' 19 | } 20 | 21 | if ! command -v tput >/dev/null; then 22 | tput() { 23 | printf '1000\n' 24 | } 25 | export -f tput 26 | fi 27 | 28 | emit_debug_output() { 29 | printf '%s\n' 'output:' "$output" >&2 30 | } 31 | 32 | teardown() { 33 | if [[ -n "$BATS_TEST_SUITE_TMPDIR" ]]; then 34 | rm -rf "$BATS_TEST_SUITE_TMPDIR" 35 | fi 36 | } 37 | -------------------------------------------------------------------------------- /hack/container-structure-test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2019 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o pipefail 19 | set -o nounset 20 | 21 | PROJECT_ROOT=$(dirname "${BASH_SOURCE}")/.. 22 | 23 | # Install tools we need, but only from vendor/... 24 | cd ${PROJECT_ROOT} 25 | 26 | exec docker run --rm -w /workspace \ 27 | -e DOCKER_HOST \ 28 | -v $(pwd):/workspace \ 29 | gcr.io/gcp-runtimes/container-structure-test:v1.8.0 \ 30 | "$@" 31 | -------------------------------------------------------------------------------- /hack/php-series: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2019 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o pipefail 19 | 20 | PHP_VERSION="$1" 21 | 22 | set -o nounset 23 | 24 | if [ -z "${PHP_VERSION}" ] ; then 25 | echo "Usage: php-version PHP_VERSION" >&2 26 | exit 1 27 | fi 28 | 29 | IFS='.' read -ra VERSION_PARTS <<< "$PHP_VERSION" 30 | 31 | if [ "${#VERSION_PARTS[@]}" -lt 3 ] ; then 32 | echo "Invalid version string ${PHP_VERSION}" >&2 33 | exit 2 34 | fi 35 | 36 | echo "${VERSION_PARTS[0]}.${VERSION_PARTS[1]}" 37 | -------------------------------------------------------------------------------- /hack/wordpress-php-series: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | '7.3', 21 | '5.1' => '7.2', 22 | '5.0' => '7.2', 23 | '4.9' => '7.2', 24 | '4.8' => '7.2', 25 | ] ); 26 | 27 | if ( count( $argv ) != 2 ) { 28 | fwrite(STDERR, 'Usage: wordpress-php-series WORDPRESS_VERSION\n'); 29 | exit(1); 30 | } 31 | 32 | $wp_version = trim($argv[1]); 33 | 34 | @list( $major, $minor, $patch, $tag ) = explode( '.', $wp_version, 4 ); 35 | 36 | echo @WORDPRESS_PHP_SERIES["$major.$minor"] ?: DEFAULT_SERIES; 37 | -------------------------------------------------------------------------------- /php/Dockerfile-7.4: -------------------------------------------------------------------------------- 1 | ARG PHP_VERSION=7.4.33 2 | FROM php:${PHP_VERSION}-fpm-buster as php 3 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 4 | ENV PATH="/usr/local/docker/bin:${PATH}" 5 | ENV PHP_VERSION=${PHP_VERSION} 6 | ENV PORT=8080 7 | ENV DOCUMENT_ROOT=/app/html 8 | 9 | ENV COMPOSER_VERSION=1.10.26 10 | ENV SUPERVISORD_VERSION=0.6.3 11 | ENV DOCKERIZE_VERSION=2.2.0 12 | # keep wp-cli happy 13 | ENV PAGER=cat 14 | 15 | # workaround for https://github.com/docker-library/php/issues/924 16 | ENV PHP_OPENSSL=yes 17 | 18 | # configure composer 19 | ENV COMPOSER_CACHE_DIR=/var/lib/composer/cache 20 | ENV COMPOSER_HOME=/var/lib/composer 21 | ENV COMPOSER_NO_INTERACTION=1 22 | 23 | # install openresty and minimal requirements 24 | RUN set -ex \ 25 | && apt-get update \ 26 | && apt-get install --no-install-recommends -y gnupg \ 27 | && curl -s https://openresty.org/package/pubkey.gpg | apt-key add - \ 28 | && echo "deb http://openresty.org/package/debian buster openresty" > /etc/apt/sources.list.d/openresty.list \ 29 | && apt-get update \ 30 | && apt-get install --no-install-recommends -y \ 31 | msmtp unzip openresty=1.21* openresty-debug=1.21* libyaml-0-2 libyaml-dev \ 32 | less git openssh-client procps \ 33 | # we need yaml support for installing extensions 34 | && pecl install yaml \ 35 | && docker-php-ext-enable --ini-name 50-docker-php-ext-yaml.ini yaml \ 36 | && apt-get autoremove --purge -y libyaml-dev \ 37 | && rm -rf /var/lib/apt/lists/* 38 | 39 | COPY docker/build-scripts /usr/local/docker/build-scripts/ 40 | 41 | RUN set -ex \ 42 | && apt-get update \ 43 | && mkdir -p ${COMPOSER_HOME} ${COMPOSER_CACHE_DIR} \ 44 | && chown www-data:www-data ${COMPOSER_HOME} ${COMPOSER_CACHE_DIR} \ 45 | && chmod 777 ${COMPOSER_HOME} ${COMPOSER_CACHE_DIR} \ 46 | && /usr/local/docker/build-scripts/install-composer \ 47 | && composer global require --prefer-dist hirak/prestissimo 0.3.10 \ 48 | && /usr/local/docker/build-scripts/install-dockerize \ 49 | && /usr/local/docker/build-scripts/install-supervisord \ 50 | && /usr/local/docker/build-scripts/install-php-extensions /usr/local/docker/build-scripts/php-extensions.minimal.yaml \ 51 | && rm -rf /var/lib/apt/lists/* /tmp/pear/* ${COMPOSER_CACHE_DIR:?}/* 52 | 53 | WORKDIR /app 54 | # prepare rootfs 55 | RUN set -ex \ 56 | # symlink generated php.ini 57 | && ln -sf /usr/local/docker/etc/php.ini /usr/local/etc/php/conf.d/zz-01-custom.ini \ 58 | # symlink php.ini from /app/config/php.ini 59 | && ln -sf /app/config/php.ini /usr/local/etc/php/conf.d/zz-90-app.ini \ 60 | # our dummy index 61 | && mkdir /app/html \ 62 | && { \ 63 | echo "&2 \ 65 | && mkdir -p /var/lib/nginx/logs \ 66 | && ln -sf /dev/null /var/lib/nginx/logs/error.log \ 67 | && chown -R www-data:www-data /app \ 68 | && chown -R www-data:www-data /run \ 69 | && chown -R www-data:www-data /var/lib/nginx \ 70 | && chown -R www-data:www-data /var/www 71 | 72 | COPY --chown=www-data:www-data ./docker /usr/local/docker 73 | COPY --chown=www-data:www-data ./nginx-lua /usr/local/docker/lib/nginx/lua/ 74 | USER www-data:www-data 75 | 76 | STOPSIGNAL SIGTERM 77 | EXPOSE 8080 78 | ENTRYPOINT ["/usr/local/docker/bin/docker-entrypoint"] 79 | CMD ["supervisord", "-c", "/usr/local/docker/etc/supervisor.conf"] 80 | -------------------------------------------------------------------------------- /php/Dockerfile-8.1: -------------------------------------------------------------------------------- 1 | ARG PHP_VERSION=8.1.31 2 | FROM php:${PHP_VERSION}-fpm-bullseye as php 3 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 4 | ENV PATH="/usr/local/docker/bin:${PATH}" 5 | ENV PHP_VERSION=${PHP_VERSION} 6 | ENV PORT=8080 7 | ENV DOCUMENT_ROOT=/app/html 8 | 9 | ENV COMPOSER_VERSION=2.2.23 10 | ENV SUPERVISORD_VERSION=0.6.3 11 | ENV DOCKERIZE_VERSION=2.2.0 12 | # keep wp-cli happy 13 | ENV PAGER=cat 14 | 15 | # workaround for https://github.com/docker-library/php/issues/924 16 | ENV PHP_OPENSSL=yes 17 | 18 | # configure composer 19 | ENV COMPOSER_CACHE_DIR=/var/lib/composer/cache 20 | ENV COMPOSER_HOME=/var/lib/composer 21 | ENV COMPOSER_NO_INTERACTION=1 22 | 23 | # install openresty and minimal requirements 24 | RUN set -ex \ 25 | && apt-get update \ 26 | && apt-get install --no-install-recommends -y gnupg \ 27 | && curl -s https://openresty.org/package/pubkey.gpg | apt-key add - \ 28 | && echo "deb http://openresty.org/package/debian bullseye openresty" > /etc/apt/sources.list.d/openresty.list \ 29 | && apt-get update \ 30 | && apt-get install --no-install-recommends -y \ 31 | msmtp unzip openresty=1.21* openresty-debug=1.21* libyaml-0-2 libyaml-dev \ 32 | less git openssh-client procps libmagickwand-dev \ 33 | # we need yaml support for installing extensions 34 | && pecl install yaml \ 35 | && docker-php-ext-enable --ini-name 50-docker-php-ext-yaml.ini yaml \ 36 | && apt-get autoremove --purge -y libyaml-dev \ 37 | && rm -rf /var/lib/apt/lists/* 38 | 39 | COPY docker/build-scripts /usr/local/docker/build-scripts/ 40 | 41 | RUN set -ex \ 42 | && apt-get update \ 43 | && mkdir -p ${COMPOSER_HOME} ${COMPOSER_CACHE_DIR} \ 44 | && chown www-data:www-data ${COMPOSER_HOME} ${COMPOSER_CACHE_DIR} \ 45 | && chmod 777 ${COMPOSER_HOME} ${COMPOSER_CACHE_DIR} \ 46 | && /usr/local/docker/build-scripts/install-composer \ 47 | && /usr/local/docker/build-scripts/install-dockerize \ 48 | && /usr/local/docker/build-scripts/install-supervisord \ 49 | && /usr/local/docker/build-scripts/install-php-extensions /usr/local/docker/build-scripts/php-extensions.minimal.yaml \ 50 | && rm -rf /var/lib/apt/lists/* /tmp/pear/* ${COMPOSER_CACHE_DIR:?}/* 51 | 52 | WORKDIR /app 53 | # prepare rootfs 54 | RUN set -ex \ 55 | # symlink generated php.ini 56 | && ln -sf /usr/local/docker/etc/php.ini /usr/local/etc/php/conf.d/zz-01-custom.ini \ 57 | # symlink php.ini from /app/config/php.ini 58 | && ln -sf /app/config/php.ini /usr/local/etc/php/conf.d/zz-90-app.ini \ 59 | # our dummy index 60 | && mkdir /app/html \ 61 | && { \ 62 | echo "&2 \ 64 | && mkdir -p /var/lib/nginx/logs \ 65 | && ln -sf /dev/null /var/lib/nginx/logs/error.log \ 66 | && chown -R www-data:www-data /app \ 67 | && chown -R www-data:www-data /run \ 68 | && chown -R www-data:www-data /var/lib/nginx \ 69 | && chown -R www-data:www-data /var/www 70 | 71 | COPY --chown=www-data:www-data ./docker /usr/local/docker 72 | COPY --chown=www-data:www-data ./nginx-lua /usr/local/docker/lib/nginx/lua/ 73 | USER www-data:www-data 74 | 75 | STOPSIGNAL SIGTERM 76 | EXPOSE 8080 77 | ENTRYPOINT ["/usr/local/docker/bin/docker-entrypoint"] 78 | CMD ["supervisord", "-c", "/usr/local/docker/etc/supervisor.conf"] 79 | -------------------------------------------------------------------------------- /php/Dockerfile-8.2: -------------------------------------------------------------------------------- 1 | ARG PHP_VERSION=8.2.26 2 | FROM php:${PHP_VERSION}-fpm-bookworm as php 3 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 4 | ENV PATH="/usr/local/docker/bin:${PATH}" 5 | ENV PHP_VERSION=${PHP_VERSION} 6 | ENV PORT=8080 7 | ENV DOCUMENT_ROOT=/app/html 8 | 9 | ENV COMPOSER_VERSION=2.5.8 10 | ENV SUPERVISORD_VERSION=0.6.3 11 | ENV DOCKERIZE_VERSION=2.2.0 12 | # keep wp-cli happy 13 | ENV PAGER=cat 14 | 15 | # workaround for https://github.com/docker-library/php/issues/924 16 | ENV PHP_OPENSSL=yes 17 | 18 | # configure composer 19 | ENV COMPOSER_CACHE_DIR=/var/lib/composer/cache 20 | ENV COMPOSER_HOME=/var/lib/composer 21 | ENV COMPOSER_NO_INTERACTION=1 22 | 23 | # install openresty and minimal requirements 24 | RUN set -ex \ 25 | && apt-get update \ 26 | && apt-get install --no-install-recommends -y gnupg \ 27 | && curl -s https://openresty.org/package/pubkey.gpg | apt-key add - \ 28 | && echo "deb http://openresty.org/package/debian bullseye openresty" > /etc/apt/sources.list.d/openresty.list \ 29 | && apt-get update \ 30 | && apt-get install --no-install-recommends -y \ 31 | msmtp unzip openresty=1.21* openresty-debug=1.21* libyaml-0-2 libyaml-dev \ 32 | less git openssh-client procps libmagickwand-dev \ 33 | # we need yaml support for installing extensions 34 | && pecl install yaml \ 35 | && docker-php-ext-enable --ini-name 50-docker-php-ext-yaml.ini yaml \ 36 | && apt-get autoremove --purge -y libyaml-dev \ 37 | && rm -rf /var/lib/apt/lists/* 38 | 39 | COPY docker/build-scripts /usr/local/docker/build-scripts/ 40 | 41 | RUN set -ex \ 42 | && apt-get update \ 43 | && mkdir -p ${COMPOSER_HOME} ${COMPOSER_CACHE_DIR} \ 44 | && chown www-data:www-data ${COMPOSER_HOME} ${COMPOSER_CACHE_DIR} \ 45 | && chmod 777 ${COMPOSER_HOME} ${COMPOSER_CACHE_DIR} \ 46 | && /usr/local/docker/build-scripts/install-composer \ 47 | && /usr/local/docker/build-scripts/install-dockerize \ 48 | && /usr/local/docker/build-scripts/install-supervisord \ 49 | && /usr/local/docker/build-scripts/install-php-extensions /usr/local/docker/build-scripts/php-extensions.minimal.yaml \ 50 | && rm -rf /var/lib/apt/lists/* /tmp/pear/* ${COMPOSER_CACHE_DIR:?}/* 51 | 52 | WORKDIR /app 53 | # prepare rootfs 54 | RUN set -ex \ 55 | # symlink generated php.ini 56 | && ln -sf /usr/local/docker/etc/php.ini /usr/local/etc/php/conf.d/zz-01-custom.ini \ 57 | # symlink php.ini from /app/config/php.ini 58 | && ln -sf /app/config/php.ini /usr/local/etc/php/conf.d/zz-90-app.ini \ 59 | # our dummy index 60 | && mkdir /app/html \ 61 | && { \ 62 | echo "&2 \ 64 | && mkdir -p /var/lib/nginx/logs \ 65 | && ln -sf /dev/null /var/lib/nginx/logs/error.log \ 66 | && chown -R www-data:www-data /app \ 67 | && chown -R www-data:www-data /run \ 68 | && chown -R www-data:www-data /var/lib/nginx \ 69 | && chown -R www-data:www-data /var/www 70 | 71 | COPY --chown=www-data:www-data ./docker /usr/local/docker 72 | COPY --chown=www-data:www-data ./nginx-lua /usr/local/docker/lib/nginx/lua/ 73 | USER www-data:www-data 74 | 75 | STOPSIGNAL SIGTERM 76 | EXPOSE 8080 77 | ENTRYPOINT ["/usr/local/docker/bin/docker-entrypoint"] 78 | CMD ["supervisord", "-c", "/usr/local/docker/etc/supervisor.conf"] 79 | -------------------------------------------------------------------------------- /php/Dockerfile-8.3: -------------------------------------------------------------------------------- 1 | ARG PHP_VERSION=8.3.14 2 | FROM php:${PHP_VERSION}-fpm-bookworm as php 3 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 4 | ENV PATH="/usr/local/docker/bin:${PATH}" 5 | ENV PHP_VERSION=${PHP_VERSION} 6 | ENV PORT=8080 7 | ENV DOCUMENT_ROOT=/app/html 8 | 9 | ENV COMPOSER_VERSION=2.7.2 10 | ENV SUPERVISORD_VERSION=0.6.3 11 | ENV DOCKERIZE_VERSION=2.2.0 12 | # keep wp-cli happy 13 | ENV PAGER=cat 14 | 15 | # workaround for https://github.com/docker-library/php/issues/924 16 | ENV PHP_OPENSSL=yes 17 | 18 | # configure composer 19 | ENV COMPOSER_CACHE_DIR=/var/lib/composer/cache 20 | ENV COMPOSER_HOME=/var/lib/composer 21 | ENV COMPOSER_NO_INTERACTION=1 22 | 23 | # install openresty and minimal requirements 24 | RUN set -ex \ 25 | && apt-get update \ 26 | && apt-get install --no-install-recommends -y gnupg \ 27 | && curl -s https://openresty.org/package/pubkey.gpg | apt-key add - \ 28 | && echo "deb http://openresty.org/package/debian buster openresty" > /etc/apt/sources.list.d/openresty.list \ 29 | && apt-get update \ 30 | && apt-get install --no-install-recommends -y \ 31 | msmtp unzip openresty=1.21* openresty-debug=1.21* libyaml-0-2 libyaml-dev \ 32 | less git openssh-client procps libmagickwand-dev \ 33 | # we need yaml support for installing extensions 34 | && pecl install yaml \ 35 | && docker-php-ext-enable --ini-name 50-docker-php-ext-yaml.ini yaml \ 36 | && apt-get autoremove --purge -y libyaml-dev \ 37 | && rm -rf /var/lib/apt/lists/* 38 | 39 | COPY docker/build-scripts /usr/local/docker/build-scripts/ 40 | 41 | RUN set -ex \ 42 | && apt-get update \ 43 | && mkdir -p ${COMPOSER_HOME} ${COMPOSER_CACHE_DIR} \ 44 | && chown www-data:www-data ${COMPOSER_HOME} ${COMPOSER_CACHE_DIR} \ 45 | && chmod 777 ${COMPOSER_HOME} ${COMPOSER_CACHE_DIR} \ 46 | && /usr/local/docker/build-scripts/install-composer \ 47 | && /usr/local/docker/build-scripts/install-dockerize \ 48 | && /usr/local/docker/build-scripts/install-supervisord \ 49 | && /usr/local/docker/build-scripts/install-php-extensions /usr/local/docker/build-scripts/php-extensions.minimal.yaml \ 50 | && rm -rf /var/lib/apt/lists/* /tmp/pear/* ${COMPOSER_CACHE_DIR:?}/* 51 | 52 | WORKDIR /app 53 | # prepare rootfs 54 | RUN set -ex \ 55 | # symlink generated php.ini 56 | && ln -sf /usr/local/docker/etc/php.ini /usr/local/etc/php/conf.d/zz-01-custom.ini \ 57 | # symlink php.ini from /app/config/php.ini 58 | && ln -sf /app/config/php.ini /usr/local/etc/php/conf.d/zz-90-app.ini \ 59 | # our dummy index 60 | && mkdir /app/html \ 61 | && { \ 62 | echo "&2 \ 64 | && mkdir -p /var/lib/nginx/logs \ 65 | && ln -sf /dev/null /var/lib/nginx/logs/error.log \ 66 | && chown -R www-data:www-data /app \ 67 | && chown -R www-data:www-data /run \ 68 | && chown -R www-data:www-data /var/lib/nginx \ 69 | && chown -R www-data:www-data /var/www 70 | 71 | COPY --chown=www-data:www-data ./docker /usr/local/docker 72 | COPY --chown=www-data:www-data ./nginx-lua /usr/local/docker/lib/nginx/lua/ 73 | USER www-data:www-data 74 | 75 | STOPSIGNAL SIGTERM 76 | EXPOSE 8080 77 | ENTRYPOINT ["/usr/local/docker/bin/docker-entrypoint"] 78 | CMD ["supervisord", "-c", "/usr/local/docker/etc/supervisor.conf"] 79 | -------------------------------------------------------------------------------- /php/Makefile: -------------------------------------------------------------------------------- 1 | DOCKERFILES = $(wildcard Dockerfile*) 2 | SRCS := $(shell find docker nginx-lua -type f) 3 | .PHONY: all 4 | all: images 5 | 6 | include ../project.Makefile 7 | RUNTIME := php-runtime 8 | REGISTRY := $(REGISTRY)/$(RUNTIME) 9 | include ../docker.Makefile 10 | -------------------------------------------------------------------------------- /php/docker/bin/docker-entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2018 Pressinfra SRL. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | set -e 16 | set -o pipefail 17 | 18 | dockerize -template /usr/local/docker/templates:/usr/local/docker/etc -no-overwrite 19 | if [ -f /usr/local/docker/etc/msmtp.conf ] ; then 20 | chmod 0600 /usr/local/docker/etc/msmtp.conf 21 | fi 22 | 23 | if [ -d /usr/local/docker/entrypoint.d ] ; then 24 | run-parts --exit-on-error /usr/local/docker/entrypoint.d 25 | fi 26 | 27 | exec "$@" 28 | -------------------------------------------------------------------------------- /php/docker/bin/wp-install: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2019 Pressinfra SRL. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | set -e 16 | set -o pipefail 17 | 18 | title="$1" 19 | url="$2" 20 | user="$3" 21 | # escape quotes 22 | # due to: https://github.com/wp-cli/wp-cli/issues/5089 23 | pass=$(echo $4 | sed -e "s/'/\\\\\'/g" -e 's/"/\\\\"/g') 24 | email="$5" 25 | 26 | wp core install \ 27 | --title="$title" \ 28 | --url="$url" \ 29 | --admin_user="$user" \ 30 | --admin_password="$pass" \ 31 | --admin_email="$email" \ 32 | --skip-email \ 33 | "${@:6}" 34 | -------------------------------------------------------------------------------- /php/docker/build-scripts/install-composer: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2017 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | 18 | # A shell script for installing composer 19 | set -xe 20 | 21 | EXPECTED_SIGNATURE=$(curl -f https://composer.github.io/installer.sig) 22 | php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" 23 | ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA384', 'composer-setup.php');") 24 | 25 | if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ] 26 | then 27 | >&2 echo 'ERROR: Invalid composer installer signature' 28 | rm composer-setup.php 29 | exit 1 30 | fi 31 | 32 | version_arg="" 33 | if [ -n "$COMPOSER_VERSION" ] ; then 34 | version_arg="--version $COMPOSER_VERSION" 35 | fi 36 | 37 | php composer-setup.php \ 38 | --quiet $version_arg \ 39 | --install-dir=/usr/local/bin \ 40 | --filename=composer 41 | 42 | rm composer-setup.php 43 | -------------------------------------------------------------------------------- /php/docker/build-scripts/install-dockerize: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # A shell script for installing dockerize 4 | set -eo pipefail 5 | 6 | TMP="$(mktemp -d)" 7 | cleanup() { 8 | rm -rf "${TMP}" 9 | } 10 | trap cleanup EXIT 11 | 12 | RELEASE_URL="https://github.com/bitpoke/dockerize/releases/download/v${DOCKERIZE_VERSION}" 13 | ARCHIVE_FILE="dockerize-linux-amd64-v${DOCKERIZE_VERSION}.tar.gz" 14 | 15 | set -x 16 | curl -sL "${RELEASE_URL}/${ARCHIVE_FILE}" -o "${TMP}/${ARCHIVE_FILE}" 17 | tar -C "${TMP}" -xzvf "${TMP}/${ARCHIVE_FILE}" 18 | chmod +x "${TMP}/dockerize" 19 | mv "${TMP}/dockerize" /usr/local/bin/dockerize 20 | -------------------------------------------------------------------------------- /php/docker/build-scripts/install-supervisord: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # A shell script for installing supervisord 4 | set -eo pipefail 5 | 6 | TMP="$(mktemp -d)" 7 | cleanup() { 8 | rm -rf "${TMP}" 9 | } 10 | trap cleanup EXIT 11 | 12 | RELEASE_URL="https://github.com/ochinchina/supervisord/releases/download/v${SUPERVISORD_VERSION}" 13 | BIN_FILE="supervisord_${SUPERVISORD_VERSION}_linux_amd64" 14 | CHECKSUMS_FILE="supervisord_${SUPERVISORD_VERSION}_checksums.txt" 15 | 16 | set -x 17 | curl -sL "${RELEASE_URL}/${CHECKSUMS_FILE}" -o "${TMP}/${CHECKSUMS_FILE}" 18 | curl -sL "${RELEASE_URL}/${BIN_FILE}" -o "${TMP}/${BIN_FILE}" 19 | cd "${TMP}" 20 | sha256sum --ignore-missing -c "${CHECKSUMS_FILE}" 21 | chmod +x "${TMP}/${BIN_FILE}" 22 | mv "${TMP}/${BIN_FILE}" /usr/local/bin/supervisord 23 | -------------------------------------------------------------------------------- /php/docker/build-scripts/php-extensions.minimal.debug.yaml: -------------------------------------------------------------------------------- 1 | - name: opencensus 2 | type: pecl 3 | version: 0.3.0 4 | -------------------------------------------------------------------------------- /php/docker/build-scripts/php-extensions.minimal.yaml: -------------------------------------------------------------------------------- 1 | - name: pdo 2 | - name: "bcmath" 3 | - name: "exif" 4 | - name: "gd" 5 | phpMaxVersion: 7.4 6 | build: 7 | args: 8 | - "--with-jpeg-dir=/usr/include/" 9 | - "--with-png-dir=/usr/include/" 10 | - "--with-webp-dir=/usr/include/" 11 | depends: 12 | - libjpeg62-turbo-dev 13 | - libpng-dev 14 | - libwebp-dev 15 | - name: "gd" 16 | phpMinVersion: 7.4 17 | build: 18 | args: 19 | - "--with-jpeg=/usr/include/" 20 | - "--with-webp=/usr/include/" 21 | - "--with-freetype=/usr/include/" 22 | depends: 23 | - libjpeg62-turbo-dev 24 | - libpng-dev 25 | - libwebp-dev 26 | - libfreetype6-dev 27 | - name: imap 28 | build: 29 | depends: 30 | - "libc-client-dev" 31 | - "libkrb5-dev" 32 | - "libssl-dev" 33 | args: 34 | - "--with-kerberos" 35 | - "--with-imap-ssl" 36 | - name: intl 37 | build: 38 | depends: 39 | - "libicu-dev" 40 | - "zlib1g-dev" 41 | - name: gettext 42 | - name: mysqli 43 | - name: pdo_mysql 44 | - name: opcache 45 | - name: pcntl 46 | - name: shmop 47 | - name: simplexml 48 | - name: soap 49 | - name: xsl 50 | build: 51 | depends: 52 | - "libxslt-dev" 53 | - name: zip 54 | build: 55 | depends: 56 | - "libzip-dev" 57 | - "zlib1g-dev" 58 | 59 | # 60 | # PECL extensions 61 | # 62 | 63 | # Install mcrypt for backward compatibility 64 | - name: mcrypt 65 | phpMaxVersion: 8.2 66 | version: 1.0.6 67 | type: pecl 68 | build: 69 | depends: 70 | - "libmcrypt-dev" 71 | 72 | # install apcu before serializers so that they detect and enable support for it 73 | - name: apcu 74 | type: pecl 75 | version: 5.1.22 76 | priority: 30 77 | - name: apc 78 | phpMaxVersion: 8.0 79 | pecl_name: apcu_bc 80 | type: pecl 81 | version: 1.0.5 82 | 83 | # install serializers to make them available for the rest of the extensions 84 | - name: igbinary 85 | type: pecl 86 | version: 3.2.14 87 | 88 | - name: msgpack 89 | phpMinVersion: 7.4 90 | phpMaxVersion: 8.1 91 | type: pecl 92 | version: 2.1.2 93 | 94 | - name: msgpack 95 | phpMinVersion: 8.1 96 | type: pecl 97 | version: 2.2.0 98 | 99 | - name: zstd 100 | type: pecl 101 | version: 0.12.3 102 | build: 103 | depends: 104 | - "libzstd-dev" 105 | 106 | - name: mailparse 107 | type: pecl 108 | version: 3.1.5 109 | 110 | - name: memcache 111 | phpMaxVersion: 8.0 112 | type: pecl 113 | version: 4.0.5.2 114 | build: 115 | depends: 116 | - "zlib1g-dev" 117 | 118 | - name: memcache 119 | phpMinVersion: 8.0 120 | type: pecl 121 | version: 8.2 122 | build: 123 | depends: 124 | - "zlib1g-dev" 125 | 126 | - name: memcached 127 | type: pecl 128 | version: 3.2.0 129 | build: 130 | depends: 131 | - "libmemcached-dev" 132 | - "zlib1g-dev" 133 | args: 134 | - "--enable-memcached-igbinary" 135 | - "--enable-memcached-msgpack" 136 | - "--enable-memcached-json" 137 | 138 | - name: imagick 139 | type: pecl 140 | version: 3.7.0 141 | phpMaxVersion: 8.3 142 | build: 143 | depends: 144 | - "libmagickwand-dev" 145 | 146 | - name: imagick 147 | type: pecl 148 | version: 3.7.0 149 | src: https://github.com/Imagick/imagick/archive/7088edc353f53c4bc644573a79cdcd67a726ae16.tar.gz 150 | phpMinVersion: 8.3 151 | build: 152 | depends: 153 | - "libmagickwand-dev" 154 | 155 | - name: redis 156 | type: pecl 157 | version: 5.3.7 158 | build: 159 | args: 160 | - "--enable-redis-igbinary" 161 | - "--enable-redis-msgpack" 162 | - "--enable-redis-lzf" 163 | - "--enable-redis-zstd" 164 | -------------------------------------------------------------------------------- /php/docker/etc/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpoke/stack-runtimes/9b76589c28c02a16ce66ef332fe362887465400f/php/docker/etc/.gitkeep -------------------------------------------------------------------------------- /php/docker/etc/README.md: -------------------------------------------------------------------------------- 1 | **Note** make sure that `/usr/local/docker/etc` is writable, so the application 2 | is able to generate it's config even if the container is read-only 3 | -------------------------------------------------------------------------------- /php/docker/lib/php/phpinfo.php: -------------------------------------------------------------------------------- 1 | = 7.3.0" .Env.PHP_VERSION }} 7 | log_limit = 8192 8 | {{- end }} 9 | -------------------------------------------------------------------------------- /php/docker/templates/php.ini: -------------------------------------------------------------------------------- 1 | ; disable memory limit globally. 2 | ; for FastCGI workers it's set by php-fpm 3 | memory_limit=-1 4 | 5 | expose_php=off 6 | 7 | sendmail_path=/usr/bin/msmtp -C /usr/local/docker/etc/msmtp.conf --read-envelope-from -t 8 | log_errors=on 9 | display_errors=off 10 | 11 | {{- if .Env.PHP_MAX_INPUT_VARS }} 12 | max_input_vars={{ .Env.PHP_MAX_INPUT_VARS }} 13 | {{- end }} 14 | 15 | apc.serializer=igbinary 16 | 17 | session.serialize_handler=igbinary 18 | 19 | {{- if (semverCompare ">= 8.0.0" .Env.PHP_VERSION) }} 20 | {{- $jit_buffer_size := default "32" .Env.PHP_JIT_BUFFER_SIZE | atoi }} 21 | {{- $jit := default "off" .Env.PHP_JIT }} 22 | {{- if (and (ne "disable" $jit) $jit_buffer_size) }} 23 | opcache.jit = {{ default "off" .Env.PHP_JIT }} 24 | opcache.jit_buffer_size = {{ $jit_buffer_size}}M 25 | {{- else }} 26 | opcache.jit = "disable" 27 | opcache.jit_buffer_size = 0 28 | {{- end }} 29 | {{- end }} 30 | -------------------------------------------------------------------------------- /php/docker/templates/start-nginx.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | SCRIPT_TAG="[start-nginx]" 4 | STOPPED_FILE="/tmp/pod/nginx-stopped" 5 | 6 | >&2 echo "$SCRIPT_TAG Initiated start-nginx.sh script." 7 | 8 | interceptQuitSignal() { 9 | >&2 echo "$SCRIPT_TAG Stopping nginx." 10 | 11 | # Send QUIT signal to subprocesses of this script (nginx master process in this case) 12 | PID=$$ 13 | >&2 echo "$SCRIPT_TAG Killing subprocesses of process $PID." 14 | pkill --parent $PID --signal 3 15 | 16 | # Wait for the master subprocesses to end; should be good enough as long as they wait recursively 17 | while [ -n "$(pgrep --parent $PID)" ]; do sleep 0.1; done 18 | 19 | mkdir -p "$(dirname $STOPPED_FILE)" && touch $STOPPED_FILE 20 | 21 | >&2 echo "$SCRIPT_TAG Stopped nginx." 22 | 23 | # Deactivate the trap. 24 | trap - QUIT 25 | } 26 | 27 | # Cleanup stopped file in case of restart 28 | rm -f $STOPPED_FILE 29 | 30 | # Trap SIGQUIT signal to create the nginx-stopped file after shutting down 31 | trap interceptQuitSignal QUIT 32 | 33 | # Wait for php-fpm to get online 34 | /usr/local/bin/dockerize -wait unix:///var/run/php-www.sock 35 | 36 | # Finally start nginx in background so that the trap may activate 37 | {{- if eq "debug" (default "warn" .Env.NGINX_ERROR_LOG_LEVEL) }} 38 | /usr/bin/openresty-debug -p /var/lib/nginx -g 'daemon off;' -c /usr/local/docker/etc/nginx/nginx.conf & 39 | {{- else }} 40 | /usr/bin/openresty -p /var/lib/nginx -g 'daemon off;' -c /usr/local/docker/etc/nginx/nginx.conf & 41 | {{- end }} 42 | 43 | wait 44 | 45 | sleep 0.2 46 | -------------------------------------------------------------------------------- /php/docker/templates/start-php-fpm.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | SCRIPT_TAG="[start-php-fpm]" 4 | NGINX_STOPPED_FILE="/tmp/pod/nginx-stopped" 5 | 6 | interceptQuitSignal() { 7 | >&2 echo "$SCRIPT_TAG Waiting for nginx to stop." 8 | 9 | until [ -f $NGINX_STOPPED_FILE ] 10 | do sleep 0.1 11 | done 12 | 13 | >&2 echo "$SCRIPT_TAG Stopping php-fpm." 14 | 15 | # Send QUIT signal to subprocesses of this script (php-fpm master process in this case) 16 | PID=$$ 17 | pkill --parent $PID --signal 3 18 | 19 | # Wait for the master subprocesses to end; should be good enough as long as they wait recursively 20 | while [ -n "$(pgrep --parent $PID)" ]; do sleep 0.1; done 21 | 22 | >&2 echo "$SCRIPT_TAG Stopped php-fpm." 23 | 24 | # Deactivate the trap. 25 | trap - QUIT 26 | } 27 | 28 | # Trap SIGQUIT signal to sleep until nginx has been stopped, before killing php-fpm 29 | trap interceptQuitSignal QUIT 30 | 31 | # Finally start php-fpm in background so that the trap may activate 32 | /usr/local/sbin/php-fpm -y /usr/local/docker/etc/php-fpm.conf & 33 | 34 | wait 35 | 36 | sleep 0.2 37 | -------------------------------------------------------------------------------- /php/docker/templates/supervisor.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon = true 3 | loglevel = debug 4 | logfile = /dev/stderr 5 | logfile_maxbytes = 0 6 | pidfile = /var/run/supervisord.pid 7 | 8 | [inet_http_server] 9 | port = 127.0.0.1:9001 10 | 11 | [program:nginx] 12 | command = /bin/sh /usr/local/docker/etc/start-nginx.sh 13 | # for graceful shutdown 14 | stopsignal = QUIT 15 | stopwaitsecs = 20 16 | process_name = nginx 17 | autorestart = true 18 | stdout_logfile = /dev/stdout 19 | stdout_logfile_maxbytes = 0 20 | stderr_logfile = /dev/stderr 21 | stderr_logfile_maxbytes = 0 22 | 23 | [program:php-fpm] 24 | command = /bin/sh /usr/local/docker/etc/start-php-fpm.sh 25 | # https://github.com/docker-library/php/blob/04c0ee7a0277e0ebc3fcdc46620cf6c1f6273100/7.4/buster/fpm/Dockerfile#L257-L259 26 | stopsignal = QUIT 27 | stopwaitsecs = 20 28 | autorestart = true 29 | stdout_logfile = /dev/stdout 30 | stdout_logfile_maxbytes = 0 31 | stderr_logfile = /dev/stderr 32 | stderr_logfile_maxbytes = 0 33 | 34 | [group:web] 35 | programs = nginx,php-fpm 36 | 37 | [include] 38 | files = /usr/local/docker/etc/supervisor.d/*.conf 39 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/lualib/prometheus_keys.lua: -------------------------------------------------------------------------------- 1 | -- Storage to keep track of used keys. Allows to atomically create, delete 2 | -- and list keys. The keys are synchronized between nginx workers 3 | -- using ngx.shared.dict. The whole purpose of this module is to avoid 4 | -- using ngx.shared.dict:get_keys (see https://github.com/openresty/lua-nginx-module#ngxshareddictget_keys), 5 | -- which blocks all workers and therefore it shouldn't be used with large 6 | -- amounts of keys. 7 | 8 | local KeyIndex = {} 9 | KeyIndex.__index = KeyIndex 10 | 11 | function KeyIndex.new(shared_dict, prefix) 12 | local self = setmetatable({}, KeyIndex) 13 | self.dict = shared_dict 14 | self.key_prefix = prefix .. "key_" 15 | self.delete_count = prefix .. "delete_count" 16 | self.key_count = prefix .. "key_count" 17 | self.last = 0 18 | self.deleted = 0 19 | self.keys = {} 20 | self.index = {} 21 | return self 22 | end 23 | 24 | -- Loads new keys that might have been added by other workers since last sync. 25 | function KeyIndex:sync() 26 | local delete_count = self.dict:get(self.delete_count) or 0 27 | local N = self.dict:get(self.key_count) or 0 28 | if self.deleted ~= delete_count then 29 | -- Some other worker deleted something, lets do a full sync. 30 | self:sync_range(0, N) 31 | self.deleted = delete_count 32 | elseif N ~= self.last then 33 | -- Sync only new keys, if there are any. 34 | self:sync_range(self.last, N) 35 | end 36 | return N 37 | end 38 | 39 | -- Iterates keys from first to last, adds new items and removes deleted items. 40 | function KeyIndex:sync_range(first, last) 41 | for i = first, last do 42 | -- Read i-th key. If it is nil, it means it was deleted by some other thread. 43 | local key = self.dict:get(self.key_prefix .. i) 44 | if key then 45 | self.keys[i] = key 46 | self.index[key] = i 47 | elseif self.keys[i] then 48 | self.index[self.keys[i]] = nil 49 | self.keys[i] = nil 50 | end 51 | end 52 | self.last = last 53 | end 54 | 55 | -- Returns array of all keys. 56 | function KeyIndex:list() 57 | self:sync() 58 | local copy = {} 59 | local i = 1 60 | for _, v in pairs(self.keys) do 61 | copy[i] = v 62 | i = i + 1 63 | end 64 | return copy 65 | end 66 | 67 | -- Atomically adds one or more keys to the index. 68 | -- 69 | -- Args: 70 | -- key_or_keys: Single string or a list of strings containing keys to add. 71 | -- 72 | -- Returns: 73 | -- nil on success, string with error message otherwise 74 | function KeyIndex:add(key_or_keys, err_msg_lru_eviction) 75 | local keys = key_or_keys 76 | if type(key_or_keys) == "string" then 77 | keys = { key_or_keys } 78 | end 79 | 80 | for _, key in pairs(keys) do 81 | while true do 82 | local N = self:sync() 83 | if self.index[key] ~= nil then 84 | -- key already exists, we can skip it 85 | break 86 | end 87 | N = N+1 88 | local ok, err, forcible = self.dict:add(self.key_prefix .. N, key) 89 | if ok then 90 | local _, _, forcible2 = self.dict:incr(self.key_count, 1, 0) 91 | self.keys[N] = key 92 | self.index[key] = N 93 | if forcible or forcible2 then 94 | return (err_msg_lru_eviction .. "; key index: add key: idx=" .. 95 | self.key_prefix .. N .. ", key=" .. key) 96 | end 97 | break 98 | elseif err ~= "exists" then 99 | return "Unexpected error adding a key: " .. err 100 | end 101 | end 102 | end 103 | end 104 | 105 | -- Removes a key based on its value. 106 | -- 107 | -- Args: 108 | -- key: String value of the key, must exists in this index. 109 | function KeyIndex:remove(key, err_msg_lru_eviction) 110 | local i = self.index[key] 111 | if i then 112 | self.index[key] = nil 113 | self.keys[i] = nil 114 | self.dict:set(self.key_prefix .. i, nil) 115 | self.deleted = self.deleted + 1 116 | 117 | -- increment delete_count to signalize other workers that they should do a full sync 118 | local _, err, forcible = self.dict:incr(self.delete_count, 1, 0) 119 | if err or forcible then 120 | return err or err_msg_lru_eviction 121 | end 122 | else 123 | ngx.log(ngx.ERR, "Trying to remove non-existent key: ", key) 124 | end 125 | end 126 | 127 | return KeyIndex 128 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/lualib/prometheus_resty_counter.lua: -------------------------------------------------------------------------------- 1 | local ngx_shared = ngx.shared 2 | local pairs = pairs 3 | local ngx = ngx 4 | local error = error 5 | local setmetatable = setmetatable 6 | local tonumber = tonumber 7 | 8 | local clear_tab 9 | do 10 | local ok 11 | ok, clear_tab = pcall(require, "table.clear") 12 | if not ok then 13 | clear_tab = function(tab) 14 | for k in pairs(tab) do 15 | tab[k] = nil 16 | end 17 | end 18 | end 19 | end 20 | 21 | local _M = { 22 | _VERSION = '0.2.1' 23 | } 24 | local mt = { __index = _M } 25 | 26 | -- local cache of counters increments 27 | local increments = {} 28 | -- boolean flags of per worker sync timers 29 | local timer_started = {} 30 | 31 | local id 32 | 33 | local function sync(_, self) 34 | local err, _, forcible 35 | local ok = true 36 | for k, v in pairs(self.increments) do 37 | _, err, forcible = self.dict:incr(k, v, 0) 38 | if forcible then 39 | ngx.log(ngx.ERR, "increasing counter in shdict: lru eviction: key=", k) 40 | ok = false 41 | end 42 | if err then 43 | ngx.log(ngx.ERR, "error increasing counter in shdict key: ", k, ", err: ", err) 44 | ok = false 45 | end 46 | end 47 | 48 | clear_tab(self.increments) 49 | if ok == false then 50 | self.dict:incr(self.error_metric_name, 1, 0) 51 | end 52 | 53 | return ok 54 | end 55 | 56 | function _M.new(shdict_name, sync_interval, error_metric_name) 57 | id = ngx.worker.id() 58 | 59 | if not ngx_shared[shdict_name] then 60 | error("shared dict \"" .. (shdict_name or "nil") .. "\" not defined", 2) 61 | end 62 | 63 | if not increments[shdict_name] then 64 | increments[shdict_name] = {} 65 | end 66 | 67 | local self = setmetatable({ 68 | dict = ngx_shared[shdict_name], 69 | increments = increments[shdict_name], 70 | error_metric_name = error_metric_name, 71 | }, mt) 72 | 73 | if sync_interval then 74 | sync_interval = tonumber(sync_interval) 75 | if not sync_interval or sync_interval < 0 then 76 | error("expect sync_interval to be a positive number", 2) 77 | end 78 | if not timer_started[shdict_name] then 79 | ngx.log(ngx.DEBUG, "start timer for shdict ", shdict_name, " on worker ", id) 80 | ngx.timer.every(sync_interval, sync, self) 81 | timer_started[shdict_name] = true 82 | end 83 | end 84 | 85 | return self 86 | end 87 | 88 | function _M:sync() 89 | return sync(false, self) 90 | end 91 | 92 | function _M:incr(key, step) 93 | step = step or 1 94 | local v = self.increments[key] 95 | if v then 96 | step = step + v 97 | end 98 | 99 | self.increments[key] = step 100 | return true 101 | end 102 | 103 | function _M:reset(key, number) 104 | if not number then 105 | return nil, "expect a number at #2" 106 | end 107 | return self.dict:incr(key, -number, number) 108 | end 109 | 110 | function _M:get(key) 111 | return self.dict:get(key) 112 | end 113 | 114 | function _M:get_keys(max_count) 115 | return self.dict:get_keys(max_count) 116 | end 117 | 118 | return _M 119 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/lualib/resty/http_headers.lua: -------------------------------------------------------------------------------- 1 | local rawget, rawset, setmetatable = 2 | rawget, rawset, setmetatable 3 | 4 | local str_lower = string.lower 5 | 6 | local _M = { 7 | _VERSION = '0.16.1', 8 | } 9 | 10 | 11 | -- Returns an empty headers table with internalised case normalisation. 12 | function _M.new() 13 | local mt = { 14 | normalised = {}, 15 | } 16 | 17 | mt.__index = function(t, k) 18 | return rawget(t, mt.normalised[str_lower(k)]) 19 | end 20 | 21 | mt.__newindex = function(t, k, v) 22 | local k_normalised = str_lower(k) 23 | 24 | -- First time seeing this header field? 25 | if not mt.normalised[k_normalised] then 26 | -- Create a lowercased entry in the metatable proxy, with the value 27 | -- of the given field case 28 | mt.normalised[k_normalised] = k 29 | 30 | -- Set the header using the given field case 31 | rawset(t, k, v) 32 | else 33 | -- We're being updated just with a different field case. Use the 34 | -- normalised metatable proxy to give us the original key case, and 35 | -- perorm a rawset() to update the value. 36 | rawset(t, mt.normalised[k_normalised], v) 37 | end 38 | end 39 | 40 | return setmetatable({}, mt) 41 | end 42 | 43 | 44 | return _M 45 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/lualib/resty/md5.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) by Yichun Zhang (agentzh) 2 | 3 | 4 | local ffi = require "ffi" 5 | local ffi_new = ffi.new 6 | local ffi_str = ffi.string 7 | local C = ffi.C 8 | local setmetatable = setmetatable 9 | --local error = error 10 | 11 | 12 | local _M = { _VERSION = '0.11' } 13 | 14 | local mt = { __index = _M } 15 | 16 | 17 | ffi.cdef[[ 18 | typedef unsigned long MD5_LONG ; 19 | 20 | enum { 21 | MD5_CBLOCK = 64, 22 | MD5_LBLOCK = MD5_CBLOCK/4 23 | }; 24 | 25 | typedef struct MD5state_st 26 | { 27 | MD5_LONG A,B,C,D; 28 | MD5_LONG Nl,Nh; 29 | MD5_LONG data[MD5_LBLOCK]; 30 | unsigned int num; 31 | } MD5_CTX; 32 | 33 | int MD5_Init(MD5_CTX *c); 34 | int MD5_Update(MD5_CTX *c, const void *data, size_t len); 35 | int MD5_Final(unsigned char *md, MD5_CTX *c); 36 | ]] 37 | 38 | local buf = ffi_new("char[16]") 39 | local ctx_ptr_type = ffi.typeof("MD5_CTX[1]") 40 | 41 | 42 | function _M.new(self) 43 | local ctx = ffi_new(ctx_ptr_type) 44 | if C.MD5_Init(ctx) == 0 then 45 | return nil 46 | end 47 | 48 | return setmetatable({ _ctx = ctx }, mt) 49 | end 50 | 51 | 52 | function _M.update(self, s) 53 | return C.MD5_Update(self._ctx, s, #s) == 1 54 | end 55 | 56 | 57 | function _M.final(self) 58 | if C.MD5_Final(buf, self._ctx) == 1 then 59 | return ffi_str(buf, 16) 60 | end 61 | 62 | return nil 63 | end 64 | 65 | 66 | function _M.reset(self) 67 | return C.MD5_Init(self._ctx) == 1 68 | end 69 | 70 | 71 | return _M 72 | 73 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/lualib/resty/random.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) by Yichun Zhang (agentzh) 2 | 3 | 4 | local ffi = require "ffi" 5 | local ffi_new = ffi.new 6 | local ffi_str = ffi.string 7 | local C = ffi.C 8 | --local setmetatable = setmetatable 9 | --local error = error 10 | 11 | 12 | local _M = { _VERSION = '0.11' } 13 | 14 | 15 | ffi.cdef[[ 16 | int RAND_bytes(unsigned char *buf, int num); 17 | int RAND_pseudo_bytes(unsigned char *buf, int num); 18 | ]] 19 | 20 | 21 | function _M.bytes(len, strong) 22 | local buf = ffi_new("char[?]", len) 23 | if strong then 24 | if C.RAND_bytes(buf, len) == 0 then 25 | return nil 26 | end 27 | else 28 | C.RAND_pseudo_bytes(buf,len) 29 | end 30 | 31 | return ffi_str(buf, len) 32 | end 33 | 34 | 35 | return _M 36 | 37 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/lualib/resty/sha.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) by Yichun Zhang (agentzh) 2 | 3 | 4 | local ffi = require "ffi" 5 | 6 | 7 | local _M = { _VERSION = '0.11' } 8 | 9 | 10 | ffi.cdef[[ 11 | typedef unsigned long SHA_LONG; 12 | typedef unsigned long long SHA_LONG64; 13 | 14 | enum { 15 | SHA_LBLOCK = 16 16 | }; 17 | ]]; 18 | 19 | return _M 20 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/lualib/resty/sha1.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) by Yichun Zhang (agentzh) 2 | 3 | 4 | require "resty.sha" 5 | local ffi = require "ffi" 6 | local ffi_new = ffi.new 7 | local ffi_str = ffi.string 8 | local C = ffi.C 9 | local setmetatable = setmetatable 10 | --local error = error 11 | 12 | 13 | local _M = { _VERSION = '0.11' } 14 | 15 | 16 | local mt = { __index = _M } 17 | 18 | 19 | ffi.cdef[[ 20 | typedef struct SHAstate_st 21 | { 22 | SHA_LONG h0,h1,h2,h3,h4; 23 | SHA_LONG Nl,Nh; 24 | SHA_LONG data[SHA_LBLOCK]; 25 | unsigned int num; 26 | } SHA_CTX; 27 | 28 | int SHA1_Init(SHA_CTX *c); 29 | int SHA1_Update(SHA_CTX *c, const void *data, size_t len); 30 | int SHA1_Final(unsigned char *md, SHA_CTX *c); 31 | ]] 32 | 33 | local digest_len = 20 34 | 35 | local buf = ffi_new("char[?]", digest_len) 36 | local ctx_ptr_type = ffi.typeof("SHA_CTX[1]") 37 | 38 | 39 | function _M.new(self) 40 | local ctx = ffi_new(ctx_ptr_type) 41 | if C.SHA1_Init(ctx) == 0 then 42 | return nil 43 | end 44 | 45 | return setmetatable({ _ctx = ctx }, mt) 46 | end 47 | 48 | 49 | function _M.update(self, s) 50 | return C.SHA1_Update(self._ctx, s, #s) == 1 51 | end 52 | 53 | 54 | function _M.final(self) 55 | if C.SHA1_Final(buf, self._ctx) == 1 then 56 | return ffi_str(buf, digest_len) 57 | end 58 | 59 | return nil 60 | end 61 | 62 | 63 | function _M.reset(self) 64 | return C.SHA1_Init(self._ctx) == 1 65 | end 66 | 67 | 68 | return _M 69 | 70 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/lualib/resty/sha224.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) by Yichun Zhang (agentzh) 2 | 3 | 4 | require "resty.sha256" 5 | local ffi = require "ffi" 6 | local ffi_new = ffi.new 7 | local ffi_str = ffi.string 8 | local C = ffi.C 9 | local setmetatable = setmetatable 10 | --local error = error 11 | 12 | 13 | local _M = { _VERSION = '0.11' } 14 | 15 | 16 | local mt = { __index = _M } 17 | 18 | 19 | ffi.cdef[[ 20 | int SHA224_Init(SHA256_CTX *c); 21 | int SHA224_Update(SHA256_CTX *c, const void *data, size_t len); 22 | int SHA224_Final(unsigned char *md, SHA256_CTX *c); 23 | ]] 24 | 25 | local digest_len = 28 26 | 27 | local buf = ffi_new("char[?]", digest_len) 28 | local ctx_ptr_type = ffi.typeof("SHA256_CTX[1]") 29 | 30 | 31 | function _M.new(self) 32 | local ctx = ffi_new(ctx_ptr_type) 33 | if C.SHA224_Init(ctx) == 0 then 34 | return nil 35 | end 36 | 37 | return setmetatable({ _ctx = ctx }, mt) 38 | end 39 | 40 | 41 | function _M.update(self, s) 42 | return C.SHA224_Update(self._ctx, s, #s) == 1 43 | end 44 | 45 | 46 | function _M.final(self) 47 | if C.SHA224_Final(buf, self._ctx) == 1 then 48 | return ffi_str(buf, digest_len) 49 | end 50 | 51 | return nil 52 | end 53 | 54 | 55 | function _M.reset(self) 56 | return C.SHA224_Init(self._ctx) == 1 57 | end 58 | 59 | 60 | return _M 61 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/lualib/resty/sha256.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) by Yichun Zhang (agentzh) 2 | 3 | 4 | require "resty.sha" 5 | local ffi = require "ffi" 6 | local ffi_new = ffi.new 7 | local ffi_str = ffi.string 8 | local C = ffi.C 9 | local setmetatable = setmetatable 10 | --local error = error 11 | 12 | 13 | local _M = { _VERSION = '0.11' } 14 | 15 | 16 | local mt = { __index = _M } 17 | 18 | 19 | ffi.cdef[[ 20 | typedef struct SHA256state_st 21 | { 22 | SHA_LONG h[8]; 23 | SHA_LONG Nl,Nh; 24 | SHA_LONG data[SHA_LBLOCK]; 25 | unsigned int num,md_len; 26 | } SHA256_CTX; 27 | 28 | int SHA256_Init(SHA256_CTX *c); 29 | int SHA256_Update(SHA256_CTX *c, const void *data, size_t len); 30 | int SHA256_Final(unsigned char *md, SHA256_CTX *c); 31 | ]] 32 | 33 | local digest_len = 32 34 | 35 | local buf = ffi_new("char[?]", digest_len) 36 | local ctx_ptr_type = ffi.typeof("SHA256_CTX[1]") 37 | 38 | 39 | function _M.new(self) 40 | local ctx = ffi_new(ctx_ptr_type) 41 | if C.SHA256_Init(ctx) == 0 then 42 | return nil 43 | end 44 | 45 | return setmetatable({ _ctx = ctx }, mt) 46 | end 47 | 48 | 49 | function _M.update(self, s) 50 | return C.SHA256_Update(self._ctx, s, #s) == 1 51 | end 52 | 53 | 54 | function _M.final(self) 55 | if C.SHA256_Final(buf, self._ctx) == 1 then 56 | return ffi_str(buf, digest_len) 57 | end 58 | 59 | return nil 60 | end 61 | 62 | 63 | function _M.reset(self) 64 | return C.SHA256_Init(self._ctx) == 1 65 | end 66 | 67 | 68 | return _M 69 | 70 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/lualib/resty/sha384.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) by Yichun Zhang (agentzh) 2 | 3 | 4 | require "resty.sha512" 5 | local ffi = require "ffi" 6 | local ffi_new = ffi.new 7 | local ffi_str = ffi.string 8 | local C = ffi.C 9 | local setmetatable = setmetatable 10 | --local error = error 11 | 12 | 13 | local _M = { _VERSION = '0.11' } 14 | 15 | 16 | local mt = { __index = _M } 17 | 18 | 19 | ffi.cdef[[ 20 | int SHA384_Init(SHA512_CTX *c); 21 | int SHA384_Update(SHA512_CTX *c, const void *data, size_t len); 22 | int SHA384_Final(unsigned char *md, SHA512_CTX *c); 23 | ]] 24 | 25 | local digest_len = 48 26 | 27 | local buf = ffi_new("char[?]", digest_len) 28 | local ctx_ptr_type = ffi.typeof("SHA512_CTX[1]") 29 | 30 | 31 | function _M.new(self) 32 | local ctx = ffi_new(ctx_ptr_type) 33 | if C.SHA384_Init(ctx) == 0 then 34 | return nil 35 | end 36 | 37 | return setmetatable({ _ctx = ctx }, mt) 38 | end 39 | 40 | 41 | function _M.update(self, s) 42 | return C.SHA384_Update(self._ctx, s, #s) == 1 43 | end 44 | 45 | 46 | function _M.final(self) 47 | if C.SHA384_Final(buf, self._ctx) == 1 then 48 | return ffi_str(buf, digest_len) 49 | end 50 | 51 | return nil 52 | end 53 | 54 | 55 | function _M.reset(self) 56 | return C.SHA384_Init(self._ctx) == 1 57 | end 58 | 59 | return _M 60 | 61 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/lualib/resty/sha512.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) by Yichun Zhang (agentzh) 2 | 3 | 4 | require "resty.sha" 5 | local ffi = require "ffi" 6 | local ffi_new = ffi.new 7 | local ffi_str = ffi.string 8 | local C = ffi.C 9 | local setmetatable = setmetatable 10 | --local error = error 11 | 12 | 13 | local _M = { _VERSION = '0.11' } 14 | 15 | 16 | local mt = { __index = _M } 17 | 18 | 19 | ffi.cdef[[ 20 | enum { 21 | SHA512_CBLOCK = SHA_LBLOCK*8 22 | }; 23 | 24 | typedef struct SHA512state_st 25 | { 26 | SHA_LONG64 h[8]; 27 | SHA_LONG64 Nl,Nh; 28 | union { 29 | SHA_LONG64 d[SHA_LBLOCK]; 30 | unsigned char p[SHA512_CBLOCK]; 31 | } u; 32 | unsigned int num,md_len; 33 | } SHA512_CTX; 34 | 35 | int SHA512_Init(SHA512_CTX *c); 36 | int SHA512_Update(SHA512_CTX *c, const void *data, size_t len); 37 | int SHA512_Final(unsigned char *md, SHA512_CTX *c); 38 | ]] 39 | 40 | local digest_len = 64 41 | 42 | local buf = ffi_new("char[?]", digest_len) 43 | local ctx_ptr_type = ffi.typeof("SHA512_CTX[1]") 44 | 45 | 46 | function _M.new(self) 47 | local ctx = ffi_new(ctx_ptr_type) 48 | if C.SHA512_Init(ctx) == 0 then 49 | return nil 50 | end 51 | 52 | return setmetatable({ _ctx = ctx }, mt) 53 | end 54 | 55 | 56 | function _M.update(self, s) 57 | return C.SHA512_Update(self._ctx, s, #s) == 1 58 | end 59 | 60 | 61 | function _M.final(self) 62 | if C.SHA512_Final(buf, self._ctx) == 1 then 63 | return ffi_str(buf, digest_len) 64 | end 65 | 66 | return nil 67 | end 68 | 69 | 70 | function _M.reset(self) 71 | return C.SHA512_Init(self._ctx) == 1 72 | end 73 | 74 | 75 | return _M 76 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/lualib/resty/string.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) by Yichun Zhang (agentzh) 2 | 3 | 4 | local ffi = require "ffi" 5 | local ffi_new = ffi.new 6 | local ffi_str = ffi.string 7 | local C = ffi.C 8 | --local setmetatable = setmetatable 9 | --local error = error 10 | local tonumber = tonumber 11 | 12 | 13 | local _M = { _VERSION = '0.11' } 14 | 15 | 16 | ffi.cdef[[ 17 | typedef unsigned char u_char; 18 | 19 | u_char * ngx_hex_dump(u_char *dst, const u_char *src, size_t len); 20 | 21 | intptr_t ngx_atoi(const unsigned char *line, size_t n); 22 | ]] 23 | 24 | local str_type = ffi.typeof("uint8_t[?]") 25 | 26 | 27 | function _M.to_hex(s) 28 | local len = #s * 2 29 | local buf = ffi_new(str_type, len) 30 | C.ngx_hex_dump(buf, s, #s) 31 | return ffi_str(buf, len) 32 | end 33 | 34 | 35 | function _M.atoi(s) 36 | return tonumber(C.ngx_atoi(s, #s)) 37 | end 38 | 39 | 40 | return _M 41 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/manifest/lua-resty-hmac.list: -------------------------------------------------------------------------------- 1 | lualib/resty/hmac.lua 2 | pod/lua-resty-hmac-0.06/lua-resty-hmac-0.06.pod 3 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/manifest/lua-resty-hmac.meta: -------------------------------------------------------------------------------- 1 | account = jkeys089 2 | name = lua-resty-hmac 3 | abstract = HMAC functions for ngx_lua and LuaJIT 4 | author = Jon Keys 5 | is_original = yes 6 | license = 2bsd 7 | repo_link = https://github.com/jkeys089/lua-resty-hmac 8 | lib_dir = lib 9 | doc_dir = lib 10 | version = 0.06 11 | main_module = lib/resty/hmac.lua 12 | requires = luajit >= 2.0.0, openresty/lua-resty-string >= 0.08 13 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/manifest/lua-resty-http.list: -------------------------------------------------------------------------------- 1 | lualib/resty/http_headers.lua 2 | lualib/resty/http_connect.lua 3 | lualib/resty/http.lua 4 | pod/lua-resty-http-0.16.1/lua-resty-http-0.16.1.pod 5 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/manifest/lua-resty-http.meta: -------------------------------------------------------------------------------- 1 | account = ledgetech 2 | name = lua-resty-http 3 | abstract = Lua HTTP client cosocket driver for OpenResty/ngx_lua 4 | author = James Hurst 5 | is_original = yes 6 | license = 2bsd 7 | repo_link = https://github.com/ledgetech/lua-resty-http 8 | lib_dir = lib 9 | doc_dir = lib 10 | version = 0.16.1 11 | main_module = lib/resty/http.lua 12 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/manifest/lua-resty-jwt.list: -------------------------------------------------------------------------------- 1 | lualib/resty/evp.lua 2 | lualib/resty/jwt-validators.lua 3 | lualib/resty/jwt.lua 4 | pod/lua-resty-jwt-0.2.0/lua-resty-jwt-0.2.0.pod 5 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/manifest/lua-resty-jwt.meta: -------------------------------------------------------------------------------- 1 | account = cdbattags 2 | name = lua-resty-jwt 3 | abstract = JWT For The Great Openresty 4 | author = Christian Battaglia (cdbattags) 5 | is_original = yes 6 | license = apache2 7 | repo_link = https://github.com/cdbattags/lua-resty-jwt 8 | lib_dir = lib 9 | doc_dir = lib 10 | version = 0.2.0 11 | main_module = lib/resty/jwt.lua 12 | requires = luajit, jkeys089/lua-resty-hmac >= 0.02 13 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/manifest/lua-resty-mlcache.list: -------------------------------------------------------------------------------- 1 | lualib/resty/mlcache.lua 2 | lualib/resty/mlcache/ipc.lua 3 | pod/lua-resty-mlcache-2.6.0/lua-resty-mlcache-2.6.0.pod 4 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/manifest/lua-resty-mlcache.meta: -------------------------------------------------------------------------------- 1 | account = thibaultcha 2 | name = lua-resty-mlcache 3 | abstract = Layered caching library for OpenResty 4 | author = Thibault Charbonnier (thibaultcha) 5 | is_original = yes 6 | license = mit 7 | repo_link = https://github.com/thibaultcha/lua-resty-mlcache 8 | lib_dir = lib 9 | doc_dir = lib 10 | version = 2.6.0 11 | main_module = lib/resty/mlcache.lua 12 | requires = openresty 13 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/manifest/lua-resty-string.list: -------------------------------------------------------------------------------- 1 | lualib/resty/sha224.lua 2 | lualib/resty/sha384.lua 3 | lualib/resty/aes.lua 4 | lualib/resty/sha256.lua 5 | lualib/resty/random.lua 6 | lualib/resty/sha512.lua 7 | lualib/resty/sha.lua 8 | lualib/resty/string.lua 9 | lualib/resty/sha1.lua 10 | lualib/resty/md5.lua 11 | pod/lua-resty-string-0.11/lua-resty-string-0.11.pod 12 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/manifest/lua-resty-string.meta: -------------------------------------------------------------------------------- 1 | account = openresty 2 | name = lua-resty-string 3 | abstract = String utilities and common hash functions for ngx_lua and LuaJIT 4 | author = Yichun "agentzh" Zhang (agentzh) 5 | is_original = yes 6 | license = 2bsd 7 | repo_link = https://github.com/openresty/lua-resty-string 8 | lib_dir = lib 9 | doc_dir = lib 10 | version = 0.11 11 | main_module = lib/resty/string.lua 12 | requires = luajit >= 2.1.0 13 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/manifest/nginx-lua-prometheus.list: -------------------------------------------------------------------------------- 1 | lualib/prometheus_keys.lua 2 | lualib/prometheus.lua 3 | lualib/prometheus_resty_counter.lua 4 | pod/nginx-lua-prometheus-0.20221218/changelog.pod 5 | pod/nginx-lua-prometheus-0.20221218/integration.readme.pod 6 | pod/nginx-lua-prometheus-0.20221218/nginx-lua-prometheus-0.20221218.pod 7 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/manifest/nginx-lua-prometheus.meta: -------------------------------------------------------------------------------- 1 | account = knyar 2 | name = nginx-lua-prometheus 3 | abstract = Prometheus metric library for Nginx 4 | author = Anton Tolchanov 5 | is_original = yes 6 | license = mit 7 | repo_link = https://github.com/knyar/nginx-lua-prometheus 8 | lib_dir = lib 9 | doc_dir = lib 10 | version = 0.20221218 11 | main_module = lib/prometheus.lua 12 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/pod/nginx-lua-prometheus-0.20221218/changelog.pod: -------------------------------------------------------------------------------- 1 | =encoding utf-8 2 | 3 | 4 | =head1 Changelog 5 | 6 | 7 | This file only calls out major changes. Please see [the list of Git commits]( 8 | https://github.com/knyar/nginx-lua-prometheus/commits/master) for the full list 9 | of changes. 10 | 11 | 12 | =head2 0.20221218 13 | 14 | 15 | 16 | =over 17 | 18 | 19 | =item * 20 | 21 | Added escaping of newline characters in label values (#145). 22 | 23 | =item * 24 | 25 | Improved detection of LRU evictions (#147, #148). 26 | 27 | =item * 28 | 29 | Per-worker metric name lookup tables now have a bounded size aimed at preventing 30 | memory leaking in environments with high metric churn (#151). 31 | 32 | 33 | =back 34 | 35 | 36 | =head2 0.20220527 37 | 38 | 39 | Performance optimization aimed at decreasing impact that metric collection has 40 | on other requests (#139). 41 | 42 | 43 | =head2 0.20220127 44 | 45 | 46 | Performance optimization of metric collection (#131). 47 | 48 | 49 | =head2 0.20210206 50 | 51 | 52 | Bucket label values no longer have leading and trailing zeroes (#119). 53 | 54 | 55 | =head2 0.20201218 56 | 57 | 58 | Histogram metrics can now be reset (#112). 59 | 60 | 61 | =head2 0.20201118 62 | 63 | 64 | Allow utf8 label values (#110). 65 | 66 | 67 | =head2 0.20200523 68 | 69 | 70 | 71 | =over 72 | 73 | 74 | =item * 75 | 76 | Scalability improvements that allow tracking a larger number of metrics 77 | without impacting nginx performance (#82). 78 | 79 | =item * 80 | 81 | Simplified library initialization, moving all of it to C. 82 | 83 | =item * 84 | 85 | Error metric name is now configurable (#91). 86 | 87 | 88 | =back 89 | 90 | 91 | =head2 0.20200420 92 | 93 | 94 | This is a significant release that includes counter performance improvements. 95 | 96 | B: this release requires additional per-worker initialization 97 | in the C section of nginx configuration. 98 | 99 | 100 | =over 101 | 102 | 103 | =item * 104 | 105 | Added support for incrementing and decrementing gauges (#52). 106 | 107 | =item * 108 | 109 | Added del and reset for gauge and counter metrics (#56). 110 | 111 | =item * 112 | 113 | Added per-worker lua counters that allow incrementing counter metrics 114 | without locking the dictionary (#75). 115 | 116 | 117 | =back 118 | 119 | 120 | =head2 0.20181120 121 | 122 | 123 | Added stream module support (#42). 124 | 125 | 126 | =head2 0.20171117 127 | 128 | 129 | Improved performance of metric collection (#25). 130 | 131 | 132 | =head2 0.1-20170610 133 | 134 | 135 | Initial version of the library. 136 | -------------------------------------------------------------------------------- /php/nginx-lua/resty_modules/pod/nginx-lua-prometheus-0.20221218/integration.readme.pod: -------------------------------------------------------------------------------- 1 | =encoding utf-8 2 | 3 | This is a simple integration test for nginx-lua-prometheus. 4 | 5 | The test builds and starts an nginx container with nginx-lua-prometheus. 6 | Then a small Go program starts several concurrent HTTP clients sending 7 | requests to nginx in a loop for a predefined amount of time. After all requests 8 | are sent, the test collects metrics and compares request counters with the 9 | total number of requests sent by clients. A few other metric checks are 10 | performed as well. 11 | -------------------------------------------------------------------------------- /php/nginx-lua/src/.luarc.json: -------------------------------------------------------------------------------- 1 | { 2 | "diagnostics.globals": [ 3 | "ngx", "google_credentials" 4 | ], 5 | "workspace.library": [ 6 | "${3rd}/OpenResty/library" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /php/nginx-lua/src/bitpoke/cors.lua: -------------------------------------------------------------------------------- 1 | -- Copyright 2023 Bitpoke Soft SRL. 2 | -- Copyright 2018 Pressinfra SRL. 3 | -- 4 | -- Licensed under the Apache License, Version 2.0 (the "License"); 5 | -- you may not use this file except in compliance with the License. 6 | -- You may obtain a copy of the License at 7 | -- 8 | -- http://www.apache.org/licenses/LICENSE-2.0 9 | -- 10 | -- Unless required by applicable law or agreed to in writing, software 11 | -- distributed under the License is distributed on an "AS IS" BASIS, 12 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | -- See the License for the specific language governing permissions and 14 | -- limitations under the License. 15 | 16 | local _M = { _VERSION = '0.1' } 17 | 18 | local cors = {} 19 | 20 | function _M.init() 21 | cors.allow_origins = (os.getenv("CORS_ALLOW_ORIGINS") or ""):gsub("%s+", ""):lower() 22 | cors.allow_paths = (os.getenv("CORS_ALLOW_PATHS") or "\\.(css|ttf|otf|eot|woff|woff2)$") 23 | 24 | cors.allow_methods = (os.getenv("CORS_ALLOW_METHODS") or "GET,POST,PUT,PATCH,DELETE,OPTIONS"):gsub("%s+", ""):upper() 25 | cors.allow_headers = (os.getenv("CORS_ALLOW_HEADERS") or "accept,accept-encoding,authorization,content-type,dnt,origin,user-agent,x-requested-with"):gsub("%s+", ""):lower() 26 | 27 | cors.allow_credentials = "true" == (os.getenv("CORS_ALLOW_CREDENTIALS") or "false"):lower() 28 | cors.preflight_max_age = tonumber(os.getenv("CORS_PREFLIGHT_MAX_AGE")) or 86400 29 | cors.expose_headers = (os.getenv("CORS_EXPOSE_HEADERS") or "") 30 | end 31 | 32 | function _M.set_headers() 33 | local req_method = ngx.req.get_method() 34 | local req_origin = (ngx.var.http_origin or ''):lower() 35 | local headers = ngx.header 36 | 37 | -- set only if CORS is enabled 38 | if '' == cors.allow_origins then 39 | return 40 | end 41 | 42 | -- do not override existing headers 43 | if '' ~= (ngx.var.upstrea_http_access_control_allow_origin or '') then 44 | return 45 | end 46 | 47 | -- restrict CORS to specific paths 48 | if '' ~= cors.allow_paths then 49 | local from, to, err = ngx.re.find(ngx.var.request_uri or '/', "^[^?]+", "jio") 50 | if err ~= nil then 51 | ngx.log(ngx.ERR, "failed to match request uri: ", err) 52 | return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) 53 | end 54 | local req_path = string.sub(ngx.var.request_uri, from or 1, to) 55 | 56 | if not ngx.re.find(req_path, cors.allow_paths, 'jio') then 57 | return 58 | end 59 | end 60 | 61 | if '*' == cors.allow_origins then 62 | headers["Access-Control-Allow-Origin"] = "*" 63 | else 64 | for origin in cors.allow_origins:gmatch("([^,]+)") do 65 | if origin == req_origin then 66 | headers["Access-Control-Allow-Origin"] = req_origin 67 | break 68 | end 69 | end 70 | end 71 | 72 | if cors.allow_credentials then 73 | headers["Access-Control-Allow-Credentials"] = "true" 74 | end 75 | 76 | if req_method == "OPTIONS" then 77 | headers["Access-Control-Allow-Methods"] = cors.allow_methods 78 | headers["Access-Control-Allow-Headers"] = cors.allow_headers 79 | headers["Access-Control-Max-Age"] = tostring(cors.preflight_max_age) 80 | 81 | headers['Content-Type'] = 'text/plain; charset=utf-8' 82 | headers['Content-Length'] = '0' 83 | return ngx.exit(ngx.HTTP_NO_CONTENT) 84 | elseif "" ~= cors.expose_headers then 85 | headers["Access-Control-Expose-Headers"] = cors.expose_headers 86 | end 87 | end 88 | 89 | return _M 90 | -------------------------------------------------------------------------------- /php/tag.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2019 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o pipefail 19 | DEFAULT_TAG="$1" 20 | IMAGE="$2" 21 | 22 | TAG_SUFFIX_SLUG="" 23 | if [ -n "${TAG_SUFFIX}" ]; then 24 | TAG_SUFFIX_SLUG="-${TAG_SUFFIX}" 25 | fi 26 | 27 | set -o nounset 28 | if [ -z "${DEFAULT_TAG}" ]; then 29 | echo "Usage: tags.sh DEFAULT_TAG [IMAGE]" >&2 30 | exit 1 31 | fi 32 | 33 | PROJECT_ROOT=$(dirname "${BASH_SOURCE[0]}")/../.. 34 | cd "${PROJECT_ROOT}" 35 | 36 | echo "${DEFAULT_TAG}${TAG_SUFFIX_SLUG}" 37 | docker run --rm "${IMAGE}" php -r "echo PHP_VERSION . \"${TAG_SUFFIX_SLUG}\n\";" 38 | -------------------------------------------------------------------------------- /php/test/container-structure-test.yaml: -------------------------------------------------------------------------------- 1 | schemaVersion: 2.0.0 2 | 3 | commandTests: 4 | - name: check php is installed 5 | command: "which" 6 | args: ["php"] 7 | expectedOutput: ["/usr/local/bin/php\n"] 8 | 9 | - name: check php-fpm is installed 10 | command: "which" 11 | args: ["php-fpm"] 12 | expectedOutput: ["/usr/local/sbin/php-fpm\n"] 13 | 14 | - name: check openresty is installed 15 | command: "which" 16 | args: ["openresty"] 17 | expectedOutput: ["/usr/bin/openresty\n"] 18 | 19 | - name: validate default openresty config 20 | setup: 21 | - ["/usr/local/docker/bin/docker-entrypoint", "/bin/true"] 22 | - ["mkdir", "-p", "/var/www/.config/gcloud/"] 23 | - [ 24 | "/bin/bash", 25 | "-c", 26 | "echo {} > /var/www/.config/gcloud/google_application_credentials.json", 27 | ] 28 | command: "/usr/bin/openresty" 29 | args: 30 | [ 31 | "-t", 32 | "-p", 33 | "/var/lib/nginx", 34 | "-g", 35 | "daemon off;", 36 | "-c", 37 | "/usr/local/docker/etc/nginx/nginx.conf", 38 | ] 39 | exitCode: 0 40 | 41 | - name: validate default php-fpm config 42 | setup: 43 | - ["/usr/local/docker/bin/docker-entrypoint", "/bin/true"] 44 | command: "/usr/local/sbin/php-fpm" 45 | args: ["-t", "-y", "/usr/local/docker/etc/php-fpm.conf"] 46 | exitCode: 0 47 | 48 | fileExistenceTests: 49 | - &www-data 50 | name: "app" 51 | path: "/app" 52 | shouldExist: true 53 | permissions: "drwxr-xr-x" 54 | uid: 33 55 | gid: 33 56 | - <<: *www-data 57 | name: "/app/html" 58 | path: "/app/html" 59 | - <<: *www-data 60 | name: "/var/lib/nginx" 61 | path: "/var/lib/nginx" 62 | - <<: *www-data 63 | name: "/var/lib/composer" 64 | path: "/var/lib/composer" 65 | permissions: "drwxrwxrwx" 66 | - <<: *www-data 67 | name: "/var/lib/composer/cache" 68 | path: "/var/lib/composer/cache" 69 | permissions: "drwxrwxrwx" 70 | 71 | metadataTest: 72 | env: 73 | - key: PORT 74 | value: 8080 75 | - key: DOCUMENT_ROOT 76 | value: /app/html 77 | exposedPorts: ["8080"] 78 | -------------------------------------------------------------------------------- /php/test/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2019 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o pipefail 19 | IMAGE="$1" 20 | 21 | set -o nounset 22 | if [ -z "${IMAGE}" ] ; then 23 | echo "Usage: test.sh IMAGE" >&2 24 | exit 1 25 | fi 26 | 27 | export PROJECT_ROOT=$(dirname "${BASH_SOURCE}")/../.. 28 | cd "${PROJECT_ROOT}" 29 | 30 | hack/container-structure-test test --config php/test/container-structure-test.yaml --image "$IMAGE" 31 | -------------------------------------------------------------------------------- /project.Makefile: -------------------------------------------------------------------------------- 1 | ifndef __PROJECT_MAKEFILE__ 2 | 3 | __PROJECT_MAKEFILE__ := included 4 | 5 | REGISTRY ?= docker.io/bitpoke 6 | 7 | 8 | ifndef CI 9 | TAG_SUFFIX ?= canary 10 | endif 11 | 12 | GIT_COMMIT = $(shell git describe --always --abbrev=40 --dirty) 13 | 14 | endif 15 | -------------------------------------------------------------------------------- /var.Makefile: -------------------------------------------------------------------------------- 1 | ifndef __VAR_MAKEFILE__ 2 | 3 | __VAR_MAKEFILE__ := included 4 | 5 | # Provides a class of targets that ensures variables are 6 | # defined and trigger rebuilds when variable values change. 7 | # 8 | # Usage: 9 | # 10 | # my_target: .build/var/REGISTRY 11 | # 12 | # my_target rebuilds when the value of $(REGISTRY) changes. 13 | # .build/var/REGISTRY also ensures that $(REGISTRY) value 14 | # is non-empty. 15 | 16 | 17 | # The main target that this Makefile offers. 18 | # This is a real file that gets updated when a variable value 19 | # change is detected. This rule does not have a recipe. It 20 | # relies on the %-phony prerequisite to detect the change and 21 | # update the file. 22 | .build/var/%: .build/var/%-required .build/var/%-phony ; 23 | 24 | 25 | .build/var: 26 | mkdir -p "$@" 27 | 28 | 29 | # Since we can't make pattern targets phony, we make them 30 | # effectively phony by depending on this phony target. 31 | .PHONY: var/phony 32 | var/phony: ; 33 | 34 | 35 | # An effectively phony target that always runs to compare the current 36 | # value of the variable (say VARNAME) with the content of the 37 | # corresponding .build/var/VARNAME file. If the contents differ, 38 | # the recipe updates .build/var/VARNAME, which effectively trigger 39 | # rebuilding of targets depending on .build/var.VARNAME. 40 | .build/var/%-phony: var/phony | .build/var 41 | @ \ 42 | var_key="$*" ; \ 43 | var_val="${$*}" ; \ 44 | var_val_old=$$(cat ".build/var/$$var_key" 2> /dev/null) ; \ 45 | if [ "$$var_val" != "$$var_val_old" ]; then \ 46 | $(call print_notice,$$var_key has been updated) ; \ 47 | printf " old value: $$var_val_old\n" ; \ 48 | printf " new value: $$var_val\n" ; \ 49 | printf "$$var_val" > .build/var/$$var_key ; \ 50 | fi 51 | 52 | 53 | # An effectively phony target that verifies that the variable 54 | # has a non-empty value. 55 | .build/var/%-required: var/phony 56 | @ \ 57 | var_key="$*" ; \ 58 | var_val="${$*}" ; \ 59 | if [ "$$var_val" = "" ]; then \ 60 | $(call print_notice,Make variable '$*' is required); \ 61 | exit 1; \ 62 | fi 63 | 64 | 65 | endif 66 | -------------------------------------------------------------------------------- /wordpress/Dockerfile-6.7: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE=docker.io/bitpoke/wordpress-runtime:bedrock-php-8.2 2 | FROM ${BASE_IMAGE} as bedrock 3 | ENV WORDPRESS_VERSION=6.7.2 4 | ENV WP_CONTENT_DIR=${DOCUMENT_ROOT}/wp-content 5 | ENV STACK_MEDIA_PATH=/wp-content/uploads 6 | RUN set -ex \ 7 | && wp core download --skip-content --path=web/wp --version=${WORDPRESS_VERSION} \ 8 | && cp /usr/local/docker/webroot/* /app/web/ 9 | ONBUILD COPY --chown=www-data:www-data config /app/config 10 | ONBUILD COPY --chown=www-data:www-data wp-content /app/web/wp-content 11 | -------------------------------------------------------------------------------- /wordpress/Dockerfile-6.7-php-7.4: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE=docker.io/bitpoke/wordpress-runtime:bedrock-php-7.4 2 | FROM ${BASE_IMAGE} as bedrock 3 | ENV WORDPRESS_VERSION=6.7.2 4 | ENV WP_CONTENT_DIR=${DOCUMENT_ROOT}/wp-content 5 | ENV STACK_MEDIA_PATH=/wp-content/uploads 6 | RUN set -ex \ 7 | && wp core download --skip-content --path=web/wp --version=${WORDPRESS_VERSION} \ 8 | && cp /usr/local/docker/webroot/* /app/web/ 9 | ONBUILD COPY --chown=www-data:www-data config /app/config 10 | ONBUILD COPY --chown=www-data:www-data wp-content /app/web/wp-content 11 | -------------------------------------------------------------------------------- /wordpress/Dockerfile-6.7-php-8.1: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE=docker.io/bitpoke/wordpress-runtime:bedrock-php-8.1 2 | FROM ${BASE_IMAGE} as bedrock 3 | ENV WORDPRESS_VERSION=6.7.2 4 | ENV WP_CONTENT_DIR=${DOCUMENT_ROOT}/wp-content 5 | ENV STACK_MEDIA_PATH=/wp-content/uploads 6 | RUN set -ex \ 7 | && wp core download --skip-content --path=web/wp --version=${WORDPRESS_VERSION} \ 8 | && cp /usr/local/docker/webroot/* /app/web/ 9 | ONBUILD COPY --chown=www-data:www-data config /app/config 10 | ONBUILD COPY --chown=www-data:www-data wp-content /app/web/wp-content 11 | -------------------------------------------------------------------------------- /wordpress/Dockerfile-6.7-php-8.3: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE=docker.io/bitpoke/wordpress-runtime:bedrock-php-8.3 2 | FROM ${BASE_IMAGE} as bedrock 3 | ENV WORDPRESS_VERSION=6.7.2 4 | ENV WP_CONTENT_DIR=${DOCUMENT_ROOT}/wp-content 5 | ENV STACK_MEDIA_PATH=/wp-content/uploads 6 | RUN set -ex \ 7 | && wp core download --skip-content --path=web/wp --version=${WORDPRESS_VERSION} \ 8 | && cp /usr/local/docker/webroot/* /app/web/ 9 | ONBUILD COPY --chown=www-data:www-data config /app/config 10 | ONBUILD COPY --chown=www-data:www-data wp-content /app/web/wp-content 11 | -------------------------------------------------------------------------------- /wordpress/Dockerfile-6.8: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE=docker.io/bitpoke/wordpress-runtime:bedrock-php-8.2 2 | FROM ${BASE_IMAGE} as bedrock 3 | ENV WORDPRESS_VERSION=6.8.1 4 | ENV WP_CONTENT_DIR=${DOCUMENT_ROOT}/wp-content 5 | ENV STACK_MEDIA_PATH=/wp-content/uploads 6 | RUN set -ex \ 7 | && wp core download --skip-content --path=web/wp --version=${WORDPRESS_VERSION} \ 8 | && cp /usr/local/docker/webroot/* /app/web/ 9 | ONBUILD COPY --chown=www-data:www-data config /app/config 10 | ONBUILD COPY --chown=www-data:www-data wp-content /app/web/wp-content 11 | -------------------------------------------------------------------------------- /wordpress/Dockerfile-6.8-php-8.3: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE=docker.io/bitpoke/wordpress-runtime:bedrock-php-8.3 2 | FROM ${BASE_IMAGE} as bedrock 3 | ENV WORDPRESS_VERSION=6.8.1 4 | ENV WP_CONTENT_DIR=${DOCUMENT_ROOT}/wp-content 5 | ENV STACK_MEDIA_PATH=/wp-content/uploads 6 | RUN set -ex \ 7 | && wp core download --skip-content --path=web/wp --version=${WORDPRESS_VERSION} \ 8 | && cp /usr/local/docker/webroot/* /app/web/ 9 | ONBUILD COPY --chown=www-data:www-data config /app/config 10 | ONBUILD COPY --chown=www-data:www-data wp-content /app/web/wp-content 11 | -------------------------------------------------------------------------------- /wordpress/Dockerfile-bedrock: -------------------------------------------------------------------------------- 1 | ARG PHP_BASE_IMAGE=docker.io/bitpoke/php-runtime:8.2 2 | FROM ${PHP_BASE_IMAGE} as bedrock 3 | 4 | ENV WP_CLI_VERSION=2.9.0 5 | ENV WP_CLI_CONFIG_PATH=/app/wp-cli.yml 6 | ENV DOCUMENT_ROOT=/app/web 7 | ENV WP_CONTENT_DIR=${DOCUMENT_ROOT}/app 8 | ENV STACK_MEDIA_PATH=/app/uploads 9 | USER root 10 | COPY docker/build-scripts /usr/local/docker/build-scripts/ 11 | RUN set -ex \ 12 | && /usr/local/docker/build-scripts/install-wp-cli \ 13 | && rm -rf /app \ 14 | && mkdir -p /app/web /src \ 15 | && { \ 16 | echo "path: $DOCUMENT_ROOT/wp"; \ 17 | } | tee /app/wp-cli.yml >&2 \ 18 | && chown -R www-data:www-data /app /src 19 | 20 | COPY --chown=www-data:www-data ./docker /usr/local/docker 21 | USER www-data 22 | -------------------------------------------------------------------------------- /wordpress/Dockerfile-bedrock-build: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE=docker.io/bitpoke/wordpress-runtime:bedrock 2 | FROM ${BASE_IMAGE} as bedrock 3 | WORKDIR /src 4 | # Install project dependencies as first build step for child images so that we 5 | # heat up composer cache 6 | ONBUILD COPY --chown=www-data:www-data composer.json composer.lock /src/ 7 | ONBUILD RUN composer install --no-dev --no-interaction --no-progress --no-ansi --no-scripts 8 | 9 | ONBUILD COPY --chown=www-data:www-data . /src 10 | ONBUILD RUN composer install --no-dev --no-interaction --no-progress --no-ansi --no-scripts 11 | ONBUILD RUN cp -a /src/. /app 12 | -------------------------------------------------------------------------------- /wordpress/Dockerfile-bedrock-build-php-7.4: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE=docker.io/bitpoke/wordpress-runtime:bedrock-php-7.4 2 | FROM ${BASE_IMAGE} as bedrock 3 | WORKDIR /src 4 | # Install project dependencies as first build step for child images so that we 5 | # heat up composer cache 6 | ONBUILD COPY --chown=www-data:www-data composer.json composer.lock /src/ 7 | ONBUILD RUN composer install --no-dev --no-interaction --no-progress --no-ansi --no-scripts 8 | 9 | ONBUILD COPY --chown=www-data:www-data . /src 10 | ONBUILD RUN composer install --no-dev --no-interaction --no-progress --no-ansi --no-scripts 11 | ONBUILD RUN cp -a /src/. /app 12 | -------------------------------------------------------------------------------- /wordpress/Dockerfile-bedrock-build-php-8.1: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE=docker.io/bitpoke/wordpress-runtime:bedrock-php-8.1 2 | FROM ${BASE_IMAGE} as bedrock 3 | WORKDIR /src 4 | # Install project dependencies as first build step for child images so that we 5 | # heat up composer cache 6 | ONBUILD COPY --chown=www-data:www-data composer.json composer.lock /src/ 7 | ONBUILD RUN composer install --no-dev --no-interaction --no-progress --no-ansi --no-scripts 8 | 9 | ONBUILD COPY --chown=www-data:www-data . /src 10 | ONBUILD RUN composer install --no-dev --no-interaction --no-progress --no-ansi --no-scripts 11 | ONBUILD RUN cp -a /src/. /app 12 | -------------------------------------------------------------------------------- /wordpress/Dockerfile-bedrock-build-php-8.3: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE=docker.io/bitpoke/wordpress-runtime:bedrock-php-8.3 2 | FROM ${BASE_IMAGE} as bedrock 3 | WORKDIR /src 4 | # Install project dependencies as first build step for child images so that we 5 | # heat up composer cache 6 | ONBUILD COPY --chown=www-data:www-data composer.json composer.lock /src/ 7 | ONBUILD RUN composer install --no-dev --no-interaction --no-progress --no-ansi --no-scripts 8 | 9 | ONBUILD COPY --chown=www-data:www-data . /src 10 | ONBUILD RUN composer install --no-dev --no-interaction --no-progress --no-ansi --no-scripts 11 | ONBUILD RUN cp -a /src/. /app 12 | -------------------------------------------------------------------------------- /wordpress/Dockerfile-bedrock-php-7.4: -------------------------------------------------------------------------------- 1 | ARG PHP_BASE_IMAGE=docker.io/bitpoke/php-runtime:7.4 2 | FROM ${PHP_BASE_IMAGE} as bedrock 3 | 4 | ENV WP_CLI_VERSION=2.9.0 5 | ENV WP_CLI_CONFIG_PATH=/app/wp-cli.yml 6 | ENV DOCUMENT_ROOT=/app/web 7 | ENV WP_CONTENT_DIR=${DOCUMENT_ROOT}/app 8 | ENV STACK_MEDIA_PATH=/app/uploads 9 | USER root 10 | COPY docker/build-scripts /usr/local/docker/build-scripts/ 11 | RUN set -ex \ 12 | && /usr/local/docker/build-scripts/install-wp-cli \ 13 | && rm -rf /app \ 14 | && mkdir -p /app/web /src \ 15 | && { \ 16 | echo "path: $DOCUMENT_ROOT/wp"; \ 17 | } | tee /app/wp-cli.yml >&2 \ 18 | && chown -R www-data:www-data /app /src 19 | 20 | COPY --chown=www-data:www-data ./docker /usr/local/docker 21 | USER www-data 22 | -------------------------------------------------------------------------------- /wordpress/Dockerfile-bedrock-php-8.1: -------------------------------------------------------------------------------- 1 | ARG PHP_BASE_IMAGE=docker.io/bitpoke/php-runtime:8.1 2 | FROM ${PHP_BASE_IMAGE} as bedrock 3 | 4 | ENV WP_CLI_VERSION=2.9.0 5 | ENV WP_CLI_CONFIG_PATH=/app/wp-cli.yml 6 | ENV DOCUMENT_ROOT=/app/web 7 | ENV WP_CONTENT_DIR=${DOCUMENT_ROOT}/app 8 | ENV STACK_MEDIA_PATH=/app/uploads 9 | USER root 10 | COPY docker/build-scripts /usr/local/docker/build-scripts/ 11 | RUN set -ex \ 12 | && /usr/local/docker/build-scripts/install-wp-cli \ 13 | && rm -rf /app \ 14 | && mkdir -p /app/web /src \ 15 | && { \ 16 | echo "path: $DOCUMENT_ROOT/wp"; \ 17 | } | tee /app/wp-cli.yml >&2 \ 18 | && chown -R www-data:www-data /app /src 19 | 20 | COPY --chown=www-data:www-data ./docker /usr/local/docker 21 | USER www-data 22 | -------------------------------------------------------------------------------- /wordpress/Dockerfile-bedrock-php-8.3: -------------------------------------------------------------------------------- 1 | ARG PHP_BASE_IMAGE=docker.io/bitpoke/php-runtime:8.3 2 | FROM ${PHP_BASE_IMAGE} as bedrock 3 | 4 | ENV WP_CLI_VERSION=2.9.0 5 | ENV WP_CLI_CONFIG_PATH=/app/wp-cli.yml 6 | ENV DOCUMENT_ROOT=/app/web 7 | ENV WP_CONTENT_DIR=${DOCUMENT_ROOT}/app 8 | ENV STACK_MEDIA_PATH=/app/uploads 9 | USER root 10 | COPY docker/build-scripts /usr/local/docker/build-scripts/ 11 | RUN set -ex \ 12 | && /usr/local/docker/build-scripts/install-wp-cli \ 13 | && rm -rf /app \ 14 | && mkdir -p /app/web /src \ 15 | && { \ 16 | echo "path: $DOCUMENT_ROOT/wp"; \ 17 | } | tee /app/wp-cli.yml >&2 \ 18 | && chown -R www-data:www-data /app /src 19 | 20 | COPY --chown=www-data:www-data ./docker /usr/local/docker 21 | USER www-data 22 | -------------------------------------------------------------------------------- /wordpress/Makefile: -------------------------------------------------------------------------------- 1 | DOCKERFILES = $(wildcard Dockerfile*) 2 | SRCS := $(shell find docker -type f) 3 | .PHONY: all 4 | all: images 5 | 6 | include ../project.Makefile 7 | RUNTIME := wordpress-runtime 8 | REGISTRY := $(REGISTRY)/$(RUNTIME) 9 | include ../docker.Makefile 10 | 11 | # Modify build targets for bedrock so that we build, test and publish bedrock and bedrock-build variants 12 | .build/wordpress-runtime-bedrock-build: .build/wordpress-runtime-bedrock 13 | .build/wordpress-runtime-bedrock-build: BASE_IMAGE := local.build/wordpress-runtime-bedrock 14 | .build/wordpress-runtime-bedrock-build: DOCKER_BUILD := docker build 15 | 16 | .build/tag-wordpress-runtime-bedrock-build: .build/tag-wordpress-runtime-bedrock 17 | 18 | .build/test-wordpress-runtime-bedrock: .build/test-wordpress-runtime-bedrock-build ; 19 | 20 | wordpress-runtime-bedrock: wordpress-runtime-bedrock-build 21 | push-wordpress-runtime-bedrock: push-wordpress-runtime-bedrock-build 22 | 23 | # Modify build targets for bedrock-php-7.4 so that we build, test and publish bedrock-php-7.4 and bedrock-build-php-7.4 variants 24 | .build/wordpress-runtime-bedrock-build-php-7.4: .build/wordpress-runtime-bedrock-php-7.4 25 | .build/wordpress-runtime-bedrock-build-php-7.4: BASE_IMAGE := local.build/wordpress-runtime-bedrock-php-7.4 26 | .build/wordpress-runtime-bedrock-build-php-7.4: DOCKER_BUILD := docker build 27 | 28 | .build/tag-wordpress-runtime-bedrock-build-php-7.4: .build/tag-wordpress-runtime-bedrock-php-7.4 29 | 30 | .build/test-wordpress-runtime-bedrock-build-php-7.4: .build/test-wordpress-runtime-bedrock-build-php-7.4 ; 31 | 32 | wordpress-runtime-bedrock-php-7.4: wordpress-runtime-bedrock-build-php-7.4 33 | push-wordpress-runtime-bedrock-php-7.4: push-wordpress-runtime-bedrock-build-php-7.4 34 | 35 | # Modify build targets for bedrock-php-8.1 so that we build, test and publish bedrock-php-8.1 and bedrock-build-php-8.1 variants 36 | .build/wordpress-runtime-bedrock-build-php-8.1: .build/wordpress-runtime-bedrock-php-8.1 37 | .build/wordpress-runtime-bedrock-build-php-8.1: BASE_IMAGE := local.build/wordpress-runtime-bedrock-php-8.1 38 | .build/wordpress-runtime-bedrock-build-php-8.1: DOCKER_BUILD := docker build 39 | 40 | .build/tag-wordpress-runtime-bedrock-build-php-8.1: .build/tag-wordpress-runtime-bedrock-php-8.1 41 | 42 | .build/test-wordpress-runtime-bedrock-build-php-8.1: .build/test-wordpress-runtime-bedrock-build-php-8.1 ; 43 | 44 | wordpress-runtime-bedrock-php-8.1: wordpress-runtime-bedrock-build-php-8.1 45 | push-wordpress-runtime-bedrock-php-8.1: push-wordpress-runtime-bedrock-build-php-8.1 46 | 47 | # Modify build targets for bedrock-php-8.3 so that we build, test and publish bedrock-php-8.3 and bedrock-build-php-8.3 variants 48 | .build/wordpress-runtime-bedrock-build-php-8.3: .build/wordpress-runtime-bedrock-php-8.3 49 | .build/wordpress-runtime-bedrock-build-php-8.3: BASE_IMAGE := local.build/wordpress-runtime-bedrock-php-8.3 50 | .build/wordpress-runtime-bedrock-build-php-8.3: DOCKER_BUILD := docker build 51 | 52 | .build/tag-wordpress-runtime-bedrock-build-php-8.3: .build/tag-wordpress-runtime-bedrock-php-8.3 53 | 54 | .build/test-wordpress-runtime-bedrock-build-php-8.3: .build/test-wordpress-runtime-bedrock-build-php-8.3 ; 55 | 56 | wordpress-runtime-bedrock-php-8.3: wordpress-runtime-bedrock-build-php-8.3 57 | push-wordpress-runtime-bedrock-php-8.3: push-wordpress-runtime-bedrock-build-php-8.3 58 | 59 | 60 | -------------------------------------------------------------------------------- /wordpress/docker/bin/wp-install: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2019 Pressinfra SRL. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | set -e 16 | set -o pipefail 17 | 18 | title="$1" 19 | url="$2" 20 | user="$3" 21 | # escape quotes 22 | # due to: https://github.com/wp-cli/wp-cli/issues/5089 23 | pass=$(echo $4 | sed -e "s/'/\\\\\'/g" -e 's/"/\\\\"/g') 24 | email="$5" 25 | 26 | wp core install \ 27 | --title="$title" \ 28 | --url="$url" \ 29 | --admin_user="$user" \ 30 | --admin_password="$pass" \ 31 | --admin_email="$email" \ 32 | --skip-email \ 33 | "${@:6}" 34 | -------------------------------------------------------------------------------- /wordpress/docker/build-scripts/install-wp-cli: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # A shell script for installing wp-cli 4 | set -eo pipefail 5 | 6 | TMP="$(mktemp -d)" 7 | cleanup() { 8 | rm -rf "${TMP}" 9 | } 10 | trap cleanup EXIT 11 | 12 | RELEASE_URL="https://github.com/wp-cli/wp-cli/releases/download/v${WP_CLI_VERSION}" 13 | BIN_FILE="wp-cli-${WP_CLI_VERSION}.phar" 14 | CHECKSUMS_FILE="wp-cli-${WP_CLI_VERSION}.phar.sha512" 15 | 16 | set -x 17 | curl -sL "${RELEASE_URL}/${BIN_FILE}" -o "${TMP}/${BIN_FILE}" 18 | 19 | EXPECTED_SIGNATURE=$(curl -sL "${RELEASE_URL}/${CHECKSUMS_FILE}") 20 | ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA512', '${TMP}/${BIN_FILE}');") 21 | 22 | if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ] 23 | then 24 | >&2 echo 'ERROR: Invalid wp-cli installer signature' 25 | exit 1 26 | fi 27 | 28 | cd "${TMP}" 29 | chmod +x "${TMP}/${BIN_FILE}" 30 | mv "${TMP}/${BIN_FILE}" /usr/local/bin/wp 31 | -------------------------------------------------------------------------------- /wordpress/docker/templates/nginx/vhost-conf.d/50-gcs-media.conf: -------------------------------------------------------------------------------- 1 | # vim: set ft=nginx: 2 | 3 | {{- if or (hasPrefix "gs://" (default "" .Env.STACK_MEDIA_BUCKET)) (hasPrefix "gcs://" (default "" .Env.STACK_MEDIA_BUCKET)) }} 4 | location ^~ {{ default "/media" .Env.STACK_MEDIA_PATH }} { 5 | location ~ /$ { 6 | return 404; 7 | } 8 | 9 | location {{ default "/media" .Env.STACK_MEDIA_PATH }} { 10 | include "/usr/local/docker/etc/nginx/gcs-proxy.conf"; 11 | 12 | proxy_hide_header cache-control; # let nginx manage cache control 13 | expires 1y; 14 | } 15 | } 16 | {{- end }} 17 | -------------------------------------------------------------------------------- /wordpress/docker/templates/nginx/vhost-conf.d/50-upstreams.conf: -------------------------------------------------------------------------------- 1 | # vim: set ft=nginx: 2 | 3 | if ( $http_cookie ~ "wordpress_logged_in" ) { set $upstream 'php-critical'; } 4 | if ( $uri ~ "/wp-login.php$" ) { set $upstream 'php-critical'; } 5 | if ( $uri ~ "/wp-cron.php$" ) { set $upstream 'php-async'; } 6 | -------------------------------------------------------------------------------- /wordpress/docker/templates/nginx/vhost-conf.d/60-metrics.conf: -------------------------------------------------------------------------------- 1 | location ~ {{ default "/wp-json/stack/v1/metrics" .Env.STACK_METRICS_WPAPI_ENDPOINT }}/? { 2 | allow 127.0.0.1; 3 | deny all; 4 | 5 | try_files $uri $uri/ /index.php$is_args$args; 6 | } 7 | -------------------------------------------------------------------------------- /wordpress/docker/templates/nginx/vhost-conf.d/99-subpath-rewrites.conf: -------------------------------------------------------------------------------- 1 | # vim: set ft=nginx: 2 | {{- $routes := splitList "," (default "" .Env.STACK_ROUTES) -}} 3 | {{- $wpContent := trimAll "/" (trimPrefix (default "/app/web" .Env.DOCUMENT_ROOT) (default "/app/web/wp-content" .Env.WP_CONTENT_DIR)) -}} 4 | {{- $wp := trimAll "/" (default "wp" .Env.WP_CORE_DIRECTORY) }} 5 | {{- $generateMultisiteSubfolderRules := eq "subfolder" (default "off" .Env.STACK_MULTISITE_RULES | lower) }} 6 | 7 | {{- define "rewrite" -}} 8 | {{- /* $pathPrefix contains the normalized pathPrefix, containing trailing and leading slashes */ -}} 9 | {{- $pathPrefix := empty (trimAll "/" .url.path) | ternary "/" (printf "/%s/" (trimAll "/" .url.path)) -}} 10 | 11 | {{- if .multisiteSubfolder }} 12 | if ( $http_host = "{{ .url.host }}" ) { 13 | rewrite ^{{ $pathPrefix }}([_0-9a-zA-Z-]+)(/{{ .wpContent }}/.*) $2 last; 14 | rewrite ^{{ $pathPrefix }}([_0-9a-zA-Z-]+)/((wp-includes|wp-admin|wp-content)/.*) /{{ .wp }}/$2 last; 15 | rewrite ^{{ $pathPrefix }}([_0-9a-zA-Z-]+)/([^/]+\.php)$ /{{ .wp }}/$2 last; 16 | } 17 | {{- end }} 18 | 19 | {{- /* we only generate rewrite rules if the path is not root */ -}} 20 | {{- if (ne "/" $pathPrefix) }} 21 | if ( $http_host = "{{ .url.host }}" ) { 22 | rewrite ^{{ $pathPrefix }}({{ .wpContent }}/.*) /$1 last; 23 | rewrite ^{{ $pathPrefix }}{{ .wp }}/((wp-includes|wp-admin|wp-content)/.*) /{{ .wp }}/$1 last; 24 | rewrite ^{{ $pathPrefix }}{{ .wp }}/([^/]+\.php)$ /{{ .wp }}/$1 last; 25 | } 26 | {{- end }} 27 | {{- end }} 28 | 29 | # add /wp-admin trailing slash 30 | rewrite ^[^\s]*/wp-admin$ $scheme://$host$uri/ permanent; 31 | {{ range $route := $routes }} 32 | # routing for {{ $route }} 33 | {{ template "rewrite" (dict "url" (urlParse (printf "http://%s" $route)) "wp" $wp "wpContent" $wpContent "multisiteSubfolder" $generateMultisiteSubfolderRules) }} 34 | {{ end }} 35 | -------------------------------------------------------------------------------- /wordpress/docker/templates/wp-cli.yaml: -------------------------------------------------------------------------------- 1 | path: {{ default "/var/www/html" .Env.DOCUMENT_ROOT }}/wp 2 | -------------------------------------------------------------------------------- /wordpress/docker/webroot/index.php: -------------------------------------------------------------------------------- 1 | &2 30 | exit 1 31 | fi 32 | 33 | PROJECT_ROOT=$(dirname "${BASH_SOURCE[0]}")/../.. 34 | cd "${PROJECT_ROOT}" 35 | 36 | echo "${DEFAULT_TAG}${TAG_SUFFIX_SLUG}" 37 | cat <= 7.1 21 | * Composer - [Install](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx) 22 | 23 | ## Installation 24 | 25 | 1. Create a new project: 26 | ```sh 27 | $ composer create-project roots/bedrock 28 | ``` 29 | 2. Update environment variables in the `.env` file: 30 | * Database variables 31 | * `DB_NAME` - Database name 32 | * `DB_USER` - Database user 33 | * `DB_PASSWORD` - Database password 34 | * `DB_HOST` - Database host 35 | * Optionally, you can define `DATABASE_URL` for using a DSN instead of using the variables above (e.g. `mysql://user:password@127.0.0.1:3306/db_name`) 36 | * `WP_ENV` - Set to environment (`development`, `staging`, `production`) 37 | * `WP_HOME` - Full URL to WordPress home (https://example.com) 38 | * `WP_SITEURL` - Full URL to WordPress including subdirectory (https://example.com/wp) 39 | * `AUTH_KEY`, `SECURE_AUTH_KEY`, `LOGGED_IN_KEY`, `NONCE_KEY`, `AUTH_SALT`, `SECURE_AUTH_SALT`, `LOGGED_IN_SALT`, `NONCE_SALT` 40 | * Generate with [wp-cli-dotenv-command](https://github.com/aaemnnosttv/wp-cli-dotenv-command) 41 | * Generate with [our WordPress salts generator](https://roots.io/salts.html) 42 | 3. Add theme(s) in `web/app/themes/` as you would for a normal WordPress site 43 | 4. Set the document root on your webserver to Bedrock's `web` folder: `/path/to/site/web/` 44 | 5. Access WordPress admin at `https://example.com/wp/wp-admin/` 45 | 46 | ## Documentation 47 | 48 | Bedrock documentation is available at [https://roots.io/bedrock/docs/](https://roots.io/bedrock/docs/). 49 | 50 | ## Contributing 51 | 52 | Contributions are welcome from everyone. We have [contributing guidelines](https://github.com/roots/guidelines/blob/master/CONTRIBUTING.md) to help you get started. 53 | 54 | ## Bedrock sponsors 55 | 56 | Help support our open-source development efforts by [becoming a patron](https://www.patreon.com/rootsdev). 57 | 58 | Kinsta KM Digital itineris 59 | 60 | ## Community 61 | 62 | Keep track of development and community news. 63 | 64 | * Participate on the [Roots Discourse](https://discourse.roots.io/) 65 | * Follow [@rootswp on Twitter](https://twitter.com/rootswp) 66 | * Read and subscribe to the [Roots Blog](https://roots.io/blog/) 67 | * Subscribe to the [Roots Newsletter](https://roots.io/subscribe/) 68 | * Listen to the [Roots Radio podcast](https://roots.io/podcast/) 69 | -------------------------------------------------------------------------------- /wordpress/test/bedrock-php-7.4/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "roots/bedrock", 3 | "type": "project", 4 | "license": "MIT", 5 | "description": "WordPress boilerplate with modern development tools, easier configuration, and an improved folder structure", 6 | "homepage": "https://roots.io/bedrock/", 7 | "authors": [ 8 | { 9 | "name": "Scott Walkinshaw", 10 | "email": "scott.walkinshaw@gmail.com", 11 | "homepage": "https://github.com/swalkinshaw" 12 | }, 13 | { 14 | "name": "Ben Word", 15 | "email": "ben@benword.com", 16 | "homepage": "https://github.com/retlehs" 17 | } 18 | ], 19 | "keywords": [ 20 | "bedrock", "composer", "roots", "wordpress", "wp", "wp-config" 21 | ], 22 | "support": { 23 | "issues": "https://github.com/roots/bedrock/issues", 24 | "forum": "https://discourse.roots.io/category/bedrock" 25 | }, 26 | "config": { 27 | "preferred-install": "dist" 28 | }, 29 | "repositories": [ 30 | { 31 | "type": "composer", 32 | "url": "https://wpackagist.org" 33 | } 34 | ], 35 | "require": { 36 | "php": ">=7.1", 37 | "composer/installers": "^1.4", 38 | "vlucas/phpdotenv": "^3.4.0", 39 | "oscarotero/env": "^1.2.0", 40 | "roots/wordpress": "5.2.2", 41 | "roots/wp-config": "1.0.0", 42 | "roots/wp-password-bcrypt": "1.0.0" 43 | }, 44 | "require-dev": { 45 | "squizlabs/php_codesniffer": "^3.4.2", 46 | "roave/security-advisories": "dev-master" 47 | }, 48 | "extra": { 49 | "installer-paths": { 50 | "web/app/mu-plugins/{$name}/": ["type:wordpress-muplugin"], 51 | "web/app/plugins/{$name}/": ["type:wordpress-plugin"], 52 | "web/app/themes/{$name}/": ["type:wordpress-theme"] 53 | }, 54 | "wordpress-install-dir": "web/wp" 55 | }, 56 | "scripts": { 57 | "post-root-package-install": [ 58 | "php -r \"copy('.env.example', '.env');\"" 59 | ], 60 | "test": [ 61 | "phpcs" 62 | ] 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /wordpress/test/bedrock-php-7.4/config/application.php: -------------------------------------------------------------------------------- 1 | load(); 30 | $dotenv->required(['WP_HOME', 'WP_SITEURL']); 31 | if (!env('DATABASE_URL')) { 32 | $dotenv->required(['DB_NAME', 'DB_USER', 'DB_PASSWORD']); 33 | } 34 | } 35 | 36 | /** 37 | * Set up our global environment constant and load its config first 38 | * Default: production 39 | */ 40 | define('WP_ENV', env('WP_ENV') ?: 'production'); 41 | 42 | /** 43 | * URLs 44 | */ 45 | Config::define('WP_HOME', env('WP_HOME')); 46 | Config::define('WP_SITEURL', env('WP_SITEURL')); 47 | 48 | /** 49 | * Custom Content Directory 50 | */ 51 | Config::define('CONTENT_DIR', '/app'); 52 | Config::define('WP_CONTENT_DIR', $webroot_dir . Config::get('CONTENT_DIR')); 53 | Config::define('WP_CONTENT_URL', Config::get('WP_HOME') . Config::get('CONTENT_DIR')); 54 | 55 | /** 56 | * DB settings 57 | */ 58 | Config::define('DB_NAME', env('DB_NAME')); 59 | Config::define('DB_USER', env('DB_USER')); 60 | Config::define('DB_PASSWORD', env('DB_PASSWORD')); 61 | Config::define('DB_HOST', env('DB_HOST') ?: 'localhost'); 62 | Config::define('DB_CHARSET', 'utf8mb4'); 63 | Config::define('DB_COLLATE', ''); 64 | $table_prefix = env('DB_PREFIX') ?: 'wp_'; 65 | 66 | if (env('DATABASE_URL')) { 67 | $dsn = (object) parse_url(env('DATABASE_URL')); 68 | 69 | Config::define('DB_NAME', substr($dsn->path, 1)); 70 | Config::define('DB_USER', $dsn->user); 71 | Config::define('DB_PASSWORD', isset($dsn->pass) ? $dsn->pass : null); 72 | Config::define('DB_HOST', isset($dsn->port) ? "{$dsn->host}:{$dsn->port}" : $dsn->host); 73 | } 74 | 75 | /** 76 | * Authentication Unique Keys and Salts 77 | */ 78 | Config::define('AUTH_KEY', env('AUTH_KEY')); 79 | Config::define('SECURE_AUTH_KEY', env('SECURE_AUTH_KEY')); 80 | Config::define('LOGGED_IN_KEY', env('LOGGED_IN_KEY')); 81 | Config::define('NONCE_KEY', env('NONCE_KEY')); 82 | Config::define('AUTH_SALT', env('AUTH_SALT')); 83 | Config::define('SECURE_AUTH_SALT', env('SECURE_AUTH_SALT')); 84 | Config::define('LOGGED_IN_SALT', env('LOGGED_IN_SALT')); 85 | Config::define('NONCE_SALT', env('NONCE_SALT')); 86 | 87 | /** 88 | * Custom Settings 89 | */ 90 | Config::define('AUTOMATIC_UPDATER_DISABLED', true); 91 | Config::define('DISABLE_WP_CRON', env('DISABLE_WP_CRON') ?: false); 92 | // Disable the plugin and theme file editor in the admin 93 | Config::define('DISALLOW_FILE_EDIT', true); 94 | // Disable plugin and theme updates and installation from the admin 95 | Config::define('DISALLOW_FILE_MODS', true); 96 | 97 | /** 98 | * Debugging Settings 99 | */ 100 | Config::define('WP_DEBUG_DISPLAY', false); 101 | Config::define('SCRIPT_DEBUG', false); 102 | ini_set('display_errors', 0); 103 | 104 | /** 105 | * Allow WordPress to detect HTTPS when used behind a reverse proxy or a load balancer 106 | * See https://codex.wordpress.org/Function_Reference/is_ssl#Notes 107 | */ 108 | if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') { 109 | $_SERVER['HTTPS'] = 'on'; 110 | } 111 | 112 | $env_config = __DIR__ . '/environments/' . WP_ENV . '.php'; 113 | 114 | if (file_exists($env_config)) { 115 | require_once $env_config; 116 | } 117 | 118 | Config::apply(); 119 | 120 | /** 121 | * Bootstrap WordPress 122 | */ 123 | if (!defined('ABSPATH')) { 124 | define('ABSPATH', $webroot_dir . '/wp/'); 125 | } 126 | -------------------------------------------------------------------------------- /wordpress/test/bedrock-php-7.4/config/environments/development.php: -------------------------------------------------------------------------------- 1 | =7.4", 35 | "composer/installers": "^2.0", 36 | "vlucas/phpdotenv": "^5.4", 37 | "oscarotero/env": "^2.1", 38 | "roots/bedrock-autoloader": "^1.0", 39 | "roots/bedrock-disallow-indexing": "^2.0", 40 | "roots/wordpress": "5.9", 41 | "roots/wp-config": "1.0.0", 42 | "roots/wp-password-bcrypt": "1.1.0" 43 | }, 44 | "require-dev": { 45 | "squizlabs/php_codesniffer": "^3.6.2", 46 | "roave/security-advisories": "dev-latest" 47 | }, 48 | "config": { 49 | "optimize-autoloader": true, 50 | "preferred-install": "dist", 51 | "allow-plugins": { 52 | "composer/installers": true, 53 | "roots/wordpress-core-installer": true 54 | } 55 | }, 56 | "minimum-stability": "dev", 57 | "prefer-stable": true, 58 | "extra": { 59 | "installer-paths": { 60 | "web/app/mu-plugins/{$name}/": ["type:wordpress-muplugin"], 61 | "web/app/plugins/{$name}/": ["type:wordpress-plugin"], 62 | "web/app/themes/{$name}/": ["type:wordpress-theme"] 63 | }, 64 | "wordpress-install-dir": "web/wp" 65 | }, 66 | "scripts": { 67 | "post-root-package-install": [ 68 | "php -r \"copy('.env.example', '.env');\"" 69 | ], 70 | "test": [ 71 | "phpcs" 72 | ] 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /wordpress/test/bedrock/config/environments/development.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | Roots Coding Standards 4 | 5 | 6 | . 7 | 8 | 9 | 10 | 11 | 12 | web/wp 13 | vendor/ 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /wordpress/test/bedrock/web/app/mu-plugins/bedrock-autoloader.php: -------------------------------------------------------------------------------- 1 | >> Request: ${url}" 29 | read http_status_code http_redirect_url <<< "$(curl -sv -o /dev/stderr -w "%{http_code} %{redirect_url}" "${url}")" 30 | echo ">>> Response Code: ${http_status_code}" 31 | echo ">>> Redirect URL: ${http_redirect_url}" 32 | } 33 | 34 | setup() { 35 | [ -n "$TEST_IMAGE" ] 36 | TEST_TMP_DIR=$(mktemp -d) 37 | cp "$BATS_TEST_DIRNAME/docker-compose.yml" "$TEST_TMP_DIR" 38 | TEST_PORT="$((30000 + RANDOM % 1000))" 39 | export TEST_PORT 40 | export TEST_HOSTNAME 41 | docker-compose up -d 42 | docker-compose exec -T wordpress dockerize -wait tcp://mysql:3306 -timeout 30s 43 | http_status_code=0 44 | http_redirect_url="" 45 | } 46 | 47 | teardown() { 48 | docker-compose logs --no-color wordpress 49 | docker-compose rm -fs 50 | [ -d "$TEST_TMP_DIR" ] && rm -rf "$TEST_TMP_DIR" 51 | } 52 | 53 | @test "serves WordPress on the chosen PORT" { 54 | install-wordpress 55 | local url="http://${TEST_HOSTNAME}:${TEST_PORT}" 56 | request "$url" 57 | [ "$http_status_code" -eq 200 ] 58 | [ -z "$http_redirect_url" ] 59 | } 60 | 61 | @test "WordPress is installed as subdirectory" { 62 | install-wordpress 63 | local url="http://${TEST_HOSTNAME}:${TEST_PORT}/wp-admin/" 64 | request "$url" 65 | [ "$http_status_code" -eq 302 ] 66 | [ "$http_redirect_url" == "http://${TEST_HOSTNAME}:${TEST_PORT}/wp/wp-admin/" ] 67 | } 68 | -------------------------------------------------------------------------------- /wordpress/test/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2019 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o pipefail 19 | export IMAGE="$1" 20 | 21 | set -o nounset 22 | if [ -z "${IMAGE}" ] ; then 23 | echo "Usage: test.sh TEST_IMAGE" >&2 24 | exit 1 25 | fi 26 | 27 | export PROJECT_ROOT=$(dirname "${BASH_SOURCE}")/../.. 28 | cd "${PROJECT_ROOT}" 29 | 30 | TEST_IMAGE="${IMAGE}-test" 31 | 32 | TEST_CONTEXT="wordpress/test/classic" 33 | 34 | RUNTIME_IMAGE="${IMAGE}" 35 | BUILDER_IMAGE="${IMAGE/bedrock/bedrock-build}" 36 | 37 | if [[ $IMAGE == *"bedrock"* ]] ; then 38 | TEST_CONTEXT="wordpress/test/bedrock" 39 | if [[ $IMAGE == *"bedrock-build"* ]] ; then 40 | RUNTIME_IMAGE="${IMAGE/bedrock-build/bedrock}" 41 | BUILDER_IMAGE="${IMAGE}" 42 | fi 43 | fi 44 | if [[ $IMAGE == *"bedrock-php-7.4"* ]] ; then 45 | TEST_CONTEXT="wordpress/test/bedrock-php-7.4" 46 | fi 47 | 48 | export TEST_IMAGE 49 | 50 | set -x 51 | 52 | docker build -t "${TEST_IMAGE}" \ 53 | --build-arg "BUILDER_IMAGE=${BUILDER_IMAGE}" \ 54 | --build-arg "RUNTIME_IMAGE=${RUNTIME_IMAGE}" \ 55 | -f "${TEST_CONTEXT}/Dockerfile" "${TEST_CONTEXT}" 56 | hack/container-structure-test test --config wordpress/test/container-structure-test.yaml --image "$TEST_IMAGE" 57 | hack/bats/bin/bats wordpress/test/e2e.bats 58 | --------------------------------------------------------------------------------