diff --git a/.github/workflows/check_changelog.yml b/.github/workflows/check_changelog.yml new file mode 100644 index 00000000..d9ee3731 --- /dev/null +++ b/.github/workflows/check_changelog.yml @@ -0,0 +1,24 @@ +name: Check Changelog + +on: + pull_request: + types: [opened, reopened, edited, labeled, unlabeled, synchronize] + +permissions: + contents: read + +jobs: + check-changelog: + runs-on: ubuntu-latest + if: | + !contains(github.event.pull_request.body, '[skip changelog]') && + !contains(github.event.pull_request.body, '[changelog skip]') && + !contains(github.event.pull_request.body, '[skip ci]') && + !contains(github.event.pull_request.labels.*.name, 'skip changelog') && + !contains(github.event.pull_request.labels.*.name, 'dependencies') + steps: + - uses: actions/checkout@v3 + - name: Check that CHANGELOG is touched + run: | + git fetch origin ${{ github.base_ref }} --depth 1 && \ + git diff remotes/origin/${{ github.base_ref }} --name-only | grep CHANGELOG.md diff --git a/.gitignore b/.gitignore index 7b5c283e..97fe443a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +archives/ vendor/ .bundle/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a251fa3..f4e24329 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,47 @@ -## v3 (3/30/2017) +# Static Buildpack Changelog + +## Unreleased + +## v9 (2022-06-09) + +* [#243](https://github.com/heroku/heroku-buildpack-static/pull/243) Buildpack is officially deprecated + +## v8 (2022-05-19) + +* [#240](https://github.com/heroku/heroku-buildpack-static/pull/240) Update ngx_mruby from 2.2.3 to 2.2.4. +* [#240](https://github.com/heroku/heroku-buildpack-static/pull/240) Update nginx from 1.19.0 to 1.21.3. +* [#238](https://github.com/heroku/heroku-buildpack-static/pull/238) Drop support for Cedar-14 and Heroku-16. + +## v7 (2021-03-09) + +* [#202](https://github.com/heroku/heroku-buildpack-static/pull/202) Output a helpful error message when no `static.json` is found + +## v6 (2020-12-09) + +* [#190](https://github.com/heroku/heroku-buildpack-static/pull/190) Update nginx for Heroku-16 and Heroku-18 to 1.19.0 +* [#186](https://github.com/heroku/heroku-buildpack-static/pull/186) Ensure the SSL module is enabled + +## v5 (2020-11-11) + +* [#182](https://github.com/heroku/heroku-buildpack-static/pull/182) Add support for Heroku-20 +* [#181](https://github.com/heroku/heroku-buildpack-static/pull/181) Fix compatibility with ngx_mruby 1.18.4+ +* [#178](https://github.com/heroku/heroku-buildpack-static/pull/178) Exclude unnecessary files when publishing the buildpack +* [#177](https://github.com/heroku/heroku-buildpack-static/pull/177) Make curl retry in case of a failed download +* [#177](https://github.com/heroku/heroku-buildpack-static/pull/177) Fix the printing of the installed nginx version +* [#177](https://github.com/heroku/heroku-buildpack-static/pull/177) Switch to the recommended S3 URL format +* [#177](https://github.com/heroku/heroku-buildpack-static/pull/177) Remove unused archive caching +* [#177](https://github.com/heroku/heroku-buildpack-static/pull/177) Fail the build early on unsupported stacks + +## v4 (2019-09-18) + +* [#136](https://github.com/heroku/heroku-buildpack-static/pull/136) Add support for canonical host +* [#45](https://github.com/heroku/heroku-buildpack-static/pull/45) Add Basic Auth Configuration +* [#78](https://github.com/heroku/heroku-buildpack-static/pull/78) Add json mime type +* [#70](https://github.com/heroku/heroku-buildpack-static/pull/70) Make config copying idempotent +* [#68](https://github.com/heroku/heroku-buildpack-static/pull/68) Disable access logs + +## v3 (2017-03-30) + * [#32](https://github.com/heroku/heroku-buildpack-static/pull/32) proxies set ssl server name extension for SNI * [#37](https://github.com/heroku/heroku-buildpack-static/pull/47) fallback proxies set ssl server name extension for SNI * [#61](https://github.com/heroku/heroku-buildpack-static/pull/61) proxy redirects work even when the scheme does not match @@ -6,11 +49,11 @@ * [#63](https://github.com/heroku/heroku-buildpack-static/pull/63) proxies respect DNS TTL * [#65](https://github.com/heroku/heroku-buildpack-static/pull/65) https redirects happen over proxies -## v2 (7/13/2016) +## v2 (2016-07-13) * [#36](https://github.com/heroku/heroku-buildpack-static/pull/36) env interpolation available when doing `redirects` * [#40](https://github.com/heroku/heroku-buildpack-static/pull/40) mitigate CRLF HTTP Header Injection when using `https_only` -## v1 (3/27/2016) +## v1 (2016-03-27) * Initial Release! diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000..522fa4a0 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,2 @@ +# Comment line immediately above ownership line is reserved for related gus information. Please be careful while editing. +#ECCN:Open Source diff --git a/Gemfile.lock b/Gemfile.lock index b8099032..bd63e355 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,9 +7,9 @@ GEM docker-api (1.33.1) excon (>= 0.38.0) json - excon (0.54.0) - json (2.0.2) - rake (10.4.0) + excon (0.78.0) + json (2.3.1) + rake (12.3.3) ref (1.0.5) rspec (3.2.0) rspec-core (~> 3.2.0) diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..35a19b91 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Heroku + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..eab3d8f4 --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +S3_BUCKET ?= heroku-buildpack-static + +.PHONY: build build-heroku-18 build-heroku-20 sync + +build: build-heroku-18 build-heroku-20 + +build-heroku-18: + @docker pull heroku/heroku:18-build + @docker run -v "$(shell pwd)":/buildpack --rm -it -e "STACK=heroku-18" heroku/heroku:18-build /buildpack/scripts/build_ngx_mruby.sh + +build-heroku-20: + @docker pull heroku/heroku:20-build + @docker run -v "$(shell pwd)":/buildpack --rm -it -e "STACK=heroku-20" heroku/heroku:20-build /buildpack/scripts/build_ngx_mruby.sh + +sync: + @echo "Performing dry run of sync to $(S3_BUCKET)..." + @echo + @aws s3 sync archives/ s3://$(S3_BUCKET) --dryrun + @echo + @read -p "Continue with sync? [y/N]? " answer && [ "$$answer" = "y" ] + @echo + @aws s3 sync archives/ s3://$(S3_BUCKET) diff --git a/README.md b/README.md index b4f880ac..2b40e705 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,35 @@ # heroku-buildpack-static + **NOTE**: This buildpack is in an experimental OSS project. This is a buildpack for handling static sites and single page web apps. For a guide, read the [Getting Started with Single Page Apps on Heroku](https://gist.github.com/hone/24b06869b4c1eca701f9). +## WARNING: `heroku-buildpack-static` is deprecated + +This buildpack is deprecated and is no longer being maintained. +If you are using this project, you can transition over to NGINX via an NGINX buildpack. +Use your project's existing configuration. +To find the NGINX configuration generated by the heroku-buildpack-static you can run: + +``` +$ heroku run bash +~ $ bin/config/make-config +~ $ cat config/nginx.conf +``` + +These commands will output your current NGINX config generated from your `static.json` contents. + +- Write these contents to your local repo at `config/nginx.conf.erb`, commit them to git. +- Replace path logic that previously used `mruby` with static logic. +- Configure your app to use the NGINX buildpack via `heroku buildpacks:add heroku-community/nginx`. +- Remove this buildpack via `heroku buildpacks:remove heroku-community/static` (or `heroku buildpacks:remove https://github.com/heroku/heroku-buildpack-static`). + +## Deprecation PRs + +If you have tips or tricks for migrating off of this buildpack and want to add them to the instructions above please send a PR. + ## Features * serving static assets * gzip on by default @@ -14,14 +39,14 @@ For a guide, read the [Getting Started with Single Page Apps on Heroku](https:// ## Deploying The `static.json` file is required to use this buildpack. This file handles all the configuration described below. -1. Set the app to this buildpack: `$ heroku buildpacks:set https://github.com/heroku/heroku-buildpack-static.git`. +1. Set the app to this buildpack: `$ heroku buildpacks:set heroku-community/static`. 2. Deploy: `$ git push heroku master` ### Configuration You can configure different options for your static application by writing a `static.json` in the root folder of your application. #### Root -This allows you to specify a different asset root for the directory of your application. For instance, if you're using ember-cli, it naturally builds a `dist/` directory, so you might want to use that intsead. +This allows you to specify a different asset root for the directory of your application. For instance, if you're using ember-cli, it naturally builds a `dist/` directory, so you might want to use that instead. ```json { @@ -278,6 +303,7 @@ when accessing `/foo`, `X-Foo` will have the value `"foo"` and `X-Bar` will not In case you have multiple buildpacks for the application you can ensure static rendering in `Procfile` with `web: bin/boot`. ## Testing + For testing we use Docker to replicate Heroku locally. You'll need to have [it setup locally](https://docs.docker.com/installation/). We're also using rspec for testing with Ruby. You'll need to have those setup and install those deps: ```sh @@ -303,3 +329,17 @@ You need to forward the docker's port 3000 to the virtual machine's port though. ``` VBoxManage modifyvm "boot2docker-vm" --natpf1 "tcp-port3000,tcp,,3000,,3000"; ``` + +## Releasing new binaries + +The steps buildpack maintainers need to perform when releasing new nginx +binaries (either for a new stack or `ngx_mruby` version), are: + +1. Update the stacks list in `Makefile` and/or the ngx_mruby version + in `scripts/build_ngx_mruby.sh`. +2. Run `make build` to build all stacks or `make build-heroku-NN` to build just one stack. +3. Ensure the AWS CLI is installed (eg `brew install awscli`). +4. Authenticate with the relevant AWS account (typically by setting the environment variables from PCSK). +5. Run `make sync` (or if using a custom S3 bucket, `S3_BUCKET=... make sync`). +6. Update `bin/compile` to reference the new stacks and/or nginx version URLs. +7. Open a PR with the changes from (1) and (6). diff --git a/bin/compile b/bin/compile index fc0f36f7..7094b3d4 100755 --- a/bin/compile +++ b/bin/compile @@ -1,36 +1,53 @@ #!/usr/bin/env bash # bin/compile -set -e +set -euo pipefail build_dir=$1 cache_dir=$2 env_dir=$3 bp_dir=$(dirname $(dirname $0)) -fetch_nginx_tarball() { - local version="1.9.7" - local tarball_file="nginx-$version.tgz" - local stack="cedar-14" - local nginx_tarball_url="https://s3-external-1.amazonaws.com/heroku-buildpack-ruby/nginx/$stack/nginx-$version-ngx_mruby.tgz" - local dest_path="$cache_dir/$stack/$tarball_file" +cat <<'EOF' + ## WARNING: `heroku-buildpack-static` is deprecated - if [ -f "$dest_path" ]; then - echo -n "cat $dest_path" - else - echo -n "curl -L $nginx_tarball_url" - fi -} + This buildpack is deprecated and is no longer being maintained. + If you are using this project, you can transition over to NGINX via an NGINX buildpack. + Use your project's existing configuration. + To find the NGINX configuration generated by the heroku-buildpack-static you can run: + + ``` + $ heroku run bash + ~ $ bin/config/make-config + ~ $ cat config/nginx.conf + ``` + + These commands will output your current NGINX config generated from your `static.json` contents. + + - Write these contents to your local repo at `config/nginx.conf.erb`, commit them to git. + - Replace path logic that previously used `mruby` with static logic. + - Configure your app to use the NGINX buildpack via `heroku buildpacks:add heroku-community/nginx`. + - Remove this buildpack via `heroku buildpacks:remove heroku-community/static` (or `heroku buildpacks:remove https://github.com/heroku/heroku-buildpack-static`). + +EOF + +case "${STACK}" in + heroku-18|heroku-20) + nginx_archive_url="https://heroku-buildpack-static.s3.amazonaws.com/${STACK}/nginx-1.21.3-ngx_mruby-2.2.4.tgz" + ;; + *) + echo "Stack ${STACK} is not supported!" + exit 1 + ;; +esac mkdir -p $build_dir/bin -$(fetch_nginx_tarball) | tar xzC $build_dir/bin -nginx_version=$($build_dir/bin/nginx-$STACK -V 2>&1 | head -1 | awk '{ print $NF }') +curl -sSf --retry 3 --connect-timeout 3 "${nginx_archive_url}" | tar -xzC "${build_dir}/bin" +nginx_version=$("${build_dir}/bin/nginx" -v |& cut -d '/' -f 2-) cp -a $bp_dir/scripts/{boot,config} -t $build_dir/bin/ -echo "-----> Installed ${nginx_version} to /app/bin" +echo "-----> Installed nginx ${nginx_version} to /app/bin" mkdir -p $build_dir/config cp $bp_dir/scripts/config/templates/mime.types $build_dir/config mkdir -p $build_dir/logs - -exit 0 diff --git a/bin/detect b/bin/detect index d604f47d..fb9d3ce8 100755 --- a/bin/detect +++ b/bin/detect @@ -4,6 +4,7 @@ build_dir=$1 if [ ! -f "$build_dir/static.json" ]; then + echo "Could not find 'static.json'! Please ensure it exists and is checked into Git." >&2 exit 1 fi diff --git a/buildpack.toml b/buildpack.toml new file mode 100644 index 00000000..d19cba94 --- /dev/null +++ b/buildpack.toml @@ -0,0 +1,15 @@ +[buildpack] +name = "Static HTML" + + [publish.Ignore] + files = [ + ".github/", + "spec/", + ".gitignore", + ".rspec", + "circle.yml", + "Gemfile", + "Gemfile.lock", + "Makefile", + "Rakefile" + ] diff --git a/scripts/build_ngx_mruby.sh b/scripts/build_ngx_mruby.sh new file mode 100755 index 00000000..7d274e7a --- /dev/null +++ b/scripts/build_ngx_mruby.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +set -euo pipefail + +NGX_MRUBY_VERSION='2.2.4' +NGX_MRUBY_URL="https://github.com/matsumotory/ngx_mruby/archive/v${NGX_MRUBY_VERSION}.tar.gz" + +echo "Building ngx_mruby v${NGX_MRUBY_VERSION} for ${STACK}" + +BUILD_DIR=$(mktemp -d /tmp/ngx_mruby.XXXX) +SCRIPTS_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +OUTPUT_DIR="$(dirname "${SCRIPTS_DIR}")/archives/${STACK}" + +mkdir -p "${OUTPUT_DIR}" +cd "${BUILD_DIR}" + +echo "Downloading ngx_mruby from ${NGX_MRUBY_URL}" +curl -sSfL "${NGX_MRUBY_URL}" | tar -xz --strip-components 1 + +# Taken from the defaults: +# https://github.com/matsumotory/ngx_mruby/blob/v2.2.4/build.sh#L23-L40 +BUILD_OPTS="--prefix=${PWD}/build/nginx" +BUILD_OPTS+=' --with-http_stub_status_module --with-stream --without-stream_access_module --with-cc-opt=-fno-common' +# Our custom addition, to enable the SSL module. +BUILD_OPTS+=' --with-http_ssl_module' + +NGINX_CONFIG_OPT_ENV="${BUILD_OPTS}" ./build.sh +make install + +echo 'nginx build complete!' + +NGINX_BIN_DIR="${BUILD_DIR}/build/nginx/sbin" +cd "${NGINX_BIN_DIR}" + +# Check that nginx can start +./nginx -V + +# Check that OpenSSL support was enabled +./nginx -V |& grep 'built with OpenSSL' || { echo 'Missing OpenSSL support!'; exit 1; } + +NGINX_VERSION=$(./nginx -v |& cut -d '/' -f 2-) +ARCHIVE_PATH="${OUTPUT_DIR}/nginx-${NGINX_VERSION}-ngx_mruby-${NGX_MRUBY_VERSION}.tgz" + +tar -czf "${ARCHIVE_PATH}" nginx + +echo "Archive saved to: ${ARCHIVE_PATH}" diff --git a/scripts/config/lib/ngx_mruby/routes_path.rb b/scripts/config/lib/ngx_mruby/routes_path.rb index d9fa8963..f5958c0b 100644 --- a/scripts/config/lib/ngx_mruby/routes_path.rb +++ b/scripts/config/lib/ngx_mruby/routes_path.rb @@ -6,7 +6,7 @@ config = {} config = JSON.parse(File.read(USER_CONFIG)) if File.exist?(USER_CONFIG) -req = Nginx::Request.new +req = Nginx::Request.new # defined by https://github.com/matsumotory/ngx_mruby/blob/c7682cfb4c0984a41f1a447b71ae01e1f4fcc6bf/docs/class_and_method/README.md#nginxrequest-class uri = req.var.uri nginx_route = req.var.route routes = {} diff --git a/scripts/config/templates/nginx.conf.erb b/scripts/config/templates/nginx.conf.erb index e973c726..624f52c6 100644 --- a/scripts/config/templates/nginx.conf.erb +++ b/scripts/config/templates/nginx.conf.erb @@ -58,8 +58,9 @@ http { auth_basic_user_file <%= basic_auth_htpasswd_path %>; <% end %> + mruby_post_read_handler /app/bin/config/lib/ngx_mruby/headers.rb cache; + location / { - mruby_post_read_handler /app/bin/config/lib/ngx_mruby/headers.rb cache; mruby_set $fallback /app/bin/config/lib/ngx_mruby/routes_fallback.rb cache; <% if clean_urls %> try_files $uri.html $uri $uri/ $fallback; diff --git a/spec/support/docker/proxy/Gemfile.lock b/spec/support/docker/proxy/Gemfile.lock index a481a29b..f3152fe7 100644 --- a/spec/support/docker/proxy/Gemfile.lock +++ b/spec/support/docker/proxy/Gemfile.lock @@ -1,14 +1,18 @@ GEM remote: https://rubygems.org/ specs: - rack (1.6.11) - rack-protection (1.5.5) + mustermann (1.1.1) + ruby2_keywords (~> 0.0.1) + rack (2.2.3.1) + rack-protection (2.2.0) rack - sinatra (1.4.7) - rack (~> 1.5) - rack-protection (~> 1.4) - tilt (>= 1.3, < 3) - tilt (2.0.5) + ruby2_keywords (0.0.5) + sinatra (2.2.0) + mustermann (~> 1.0) + rack (~> 2.2) + rack-protection (= 2.2.0) + tilt (~> 2.0) + tilt (2.0.10) PLATFORMS ruby